From 808f439f73b947eb1b376e2c3d5c576e445de543 Mon Sep 17 00:00:00 2001 From: Till Kamppeter Date: Thu, 17 Nov 2022 13:58:03 +0100 Subject: [PATCH] Removed everything which got split out In the New Architecture for printing we switch to an all-IPP workflow with PPD files being abolished and classic CUPS printer drivers replaced by Printer Applications (software emulation of driverless IPP printers). To conserve the functionality of the CUPS filters which got developed over the last 20+ years into a PPD-less, IPP-driven world without having to maintain and include the legacy PPD support in OS distributions and other system environments, the original cups-filters package got split into 5 separate packages: libcupsfilters, libppd, cups-filters, braille-printer-app, and cups-browsed, with libcupsfilters and braille-printer-app not containing PPD file support code any more and cups-browsed being planned to drop explicit use of PPD files. This commit drops everything from the cups-filters repository which got split out into the other 4, new repositories. What remains are the CUPS filter executables, from which most are wrappers around filter functions implemented in the libcupsfilters and libppd libraries, backends, the "driverless" utility for setting up driverless printers with classic printer setup tools, and auxiliary files (PPD files, *.drv files, MIME rules, ...). This package is needed for using CUPS 2.x with filters from libcupsfilters (2.x) and libppd. It is not needed/does not make sense to be used with CUPS 3.x or the CUPS Snap (or any other containerized CUPS implementation). If you need printer drivers for such CUPS versions, use Printer Applications. --- CHANGES.md | 370 +- Makefile.am | 1018 +- backend/cups-brf.c | 171 - backend/implicitclass.c | 436 - banners/classified | 4 - banners/confidential | 4 - banners/form | 4 - banners/secret | 4 - banners/standard | 4 - banners/topsecret | 4 - banners/unclassified | 4 - charset/pdf.utf-8.heavy | 40 - charset/pdf.utf-8.simple | 33 - configure.ac | 771 +- cupsfilters/bannertopdf.c | 988 -- cupsfilters/bitmap.c | 494 - cupsfilters/bitmap.h | 38 - cupsfilters/catalog.c | 671 - cupsfilters/catalog.h | 121 - cupsfilters/check.c | 101 - cupsfilters/cmyk.c | 1392 -- cupsfilters/colord.c | 504 - cupsfilters/colord.h | 37 - cupsfilters/colormanager.c | 229 - cupsfilters/colormanager.h | 67 - cupsfilters/debug-internal.h | 56 - cupsfilters/debug.c | 45 - cupsfilters/dither.c | 296 - cupsfilters/driver.h | 227 - cupsfilters/filter.c | 1686 -- cupsfilters/filter.h | 449 - cupsfilters/fontembed-private.h | 373 - cupsfilters/fontembed/README | 37 - cupsfilters/fontembed/aglfn13.c | 233 - cupsfilters/fontembed/dynstring-private.h | 25 - cupsfilters/fontembed/dynstring.c | 125 - cupsfilters/fontembed/embed-pdf-private.h | 21 - cupsfilters/fontembed/embed-pdf.c | 749 - cupsfilters/fontembed/embed-sfnt-private.h | 37 - cupsfilters/fontembed/embed-sfnt.c | 876 - cupsfilters/fontembed/embed.c | 344 - cupsfilters/fontembed/fontfile.c | 67 - cupsfilters/fontembed/frequent-private.h | 27 - cupsfilters/fontembed/frequent.c | 114 - cupsfilters/fontembed/macroman-private.h | 49 - cupsfilters/fontembed/sfnt-private.h | 162 - cupsfilters/fontembed/sfnt-subset.c | 450 - cupsfilters/fontembed/sfnt.c | 1216 -- cupsfilters/fontembed/test-analyze.c | 287 - cupsfilters/fontembed/test-pdf.c | 245 - cupsfilters/fontembed/test-ps.c | 118 - cupsfilters/ghostscript.c | 1686 -- cupsfilters/ieee1284.c | 1346 -- cupsfilters/ieee1284.h | 129 - cupsfilters/image-colorspace.c | 1556 -- cupsfilters/image-jpeg.c | 326 - cupsfilters/image-png.c | 318 - cupsfilters/image-private.h | 180 - cupsfilters/image-tiff.c | 1722 -- cupsfilters/image-zoom.c | 349 - cupsfilters/image.c | 1004 -- cupsfilters/image.h | 126 - cupsfilters/image.pgm | Bin 152380 -> 0 bytes cupsfilters/image.ppm | Bin 457020 -> 0 bytes cupsfilters/imagetopdf.c | 2022 --- cupsfilters/imagetoraster.c | 4802 ------ cupsfilters/ipp.c | 2717 --- cupsfilters/ipp.h | 206 - cupsfilters/log.h | 40 - cupsfilters/lut.c | 143 - cupsfilters/mupdftopwg.c | 621 - cupsfilters/pack.c | 299 - cupsfilters/pclmtoraster.cxx | 1206 -- cupsfilters/pdf.cxx | 477 - cupsfilters/pdf.h | 50 - cupsfilters/pdftopdf/intervalset-private.h | 41 - cupsfilters/pdftopdf/intervalset.cxx | 149 - cupsfilters/pdftopdf/nup-private.h | 108 - cupsfilters/pdftopdf/nup.cxx | 312 - cupsfilters/pdftopdf/pdftopdf-private.h | 24 - .../pdftopdf/pdftopdf-processor-private.h | 239 - cupsfilters/pdftopdf/pdftopdf-processor.cxx | 521 - cupsfilters/pdftopdf/pdftopdf.cxx | 1020 -- cupsfilters/pdftopdf/pptypes-private.h | 73 - cupsfilters/pdftopdf/pptypes.cxx | 238 - cupsfilters/pdftopdf/qpdf-cm-private.h | 17 - cupsfilters/pdftopdf/qpdf-cm.cxx | 167 - cupsfilters/pdftopdf/qpdf-pdftopdf-private.h | 47 - .../qpdf-pdftopdf-processor-private.h | 99 - .../pdftopdf/qpdf-pdftopdf-processor.cxx | 920 - cupsfilters/pdftopdf/qpdf-pdftopdf.cxx | 248 - cupsfilters/pdftopdf/qpdf-tools-private.h | 26 - cupsfilters/pdftopdf/qpdf-tools.cxx | 83 - cupsfilters/pdftopdf/qpdf-xobject-private.h | 13 - cupsfilters/pdftopdf/qpdf-xobject.cxx | 199 - cupsfilters/pdftoraster.cxx | 2200 --- cupsfilters/pdfutils.c | 550 - cupsfilters/pdfutils.h | 111 - cupsfilters/pwgtopdf.cxx | 1826 -- cupsfilters/pwgtoraster.c | 2657 --- cupsfilters/raster.c | 1841 -- cupsfilters/raster.h | 62 - cupsfilters/rastertopwg.c | 607 - cupsfilters/rgb.c | 428 - cupsfilters/srgb.c | 69 - cupsfilters/test1284.c | 75 - cupsfilters/testcmyk.c | 431 - cupsfilters/testdither.c | 186 - cupsfilters/testimage.c | 87 - cupsfilters/testpdf1.c | 64 - cupsfilters/testpdf2.c | 131 - cupsfilters/testrgb.c | 340 - cupsfilters/texttopdf.c | 2548 --- cupsfilters/texttotext.c | 1246 -- cupsfilters/universal.c | 342 - data/banner-instr-append.txt | 2 - data/classified.pdf | Bin 1191 -> 0 bytes data/classified.ps | 22 - data/confidential.pdf | Bin 1193 -> 0 bytes data/confidential.ps | 22 - data/default-testpage.pdf | Bin 40141 -> 0 bytes data/default.pdf | Bin 1057 -> 0 bytes data/form-instr-append.txt | 3 - data/form_english.pdf | Bin 276117 -> 0 bytes data/form_english_in.odt | Bin 13661 -> 0 bytes data/form_russian.pdf | Bin 270308 -> 0 bytes data/form_russian_in.odt | Bin 13866 -> 0 bytes data/makePDFfromPS.sh | 7 - data/secret.pdf | Bin 1187 -> 0 bytes data/secret.ps | 22 - data/standard.pdf | Bin 1191 -> 0 bytes data/standard.ps | 22 - data/testprint | 4 - data/testprint-instr-append.txt | 2 - data/topsecret.pdf | Bin 1191 -> 0 bytes data/topsecret.ps | 22 - data/unclassified.pdf | Bin 1193 -> 0 bytes data/unclassified.ps | 22 - drv/generic-brf.drv | 51 - drv/generic-ubrl.drv | 43 - drv/indexv3.drv | 136 - drv/indexv4.drv | 141 - filter/braille/drivers/common/fr-braille.po | 243 - .../braille/drivers/common/media-braille.defs | 13 - .../braille/drivers/generic/brftoembosser.in | 55 - .../drivers/index/imageubrltoindexv3.in | 298 - .../drivers/index/imageubrltoindexv4.in | 299 - filter/braille/drivers/index/index.defs | 36 - filter/braille/drivers/index/index.sh.in | 126 - filter/braille/drivers/index/indexv3.sh.in | 88 - filter/braille/drivers/index/indexv4.sh.in | 58 - .../braille/drivers/index/textbrftoindexv3.in | 127 - filter/braille/drivers/index/ubrlto4dot.c | 28 - filter/braille/filters/TODO.txt | 3 - filter/braille/filters/braille.defs | 115 - filter/braille/filters/brftopagedbrf.in | 140 - filter/braille/filters/cups-braille.sh.in | 481 - filter/braille/filters/imagemagick.defs | 109 - filter/braille/filters/imagetobrf.in | 108 - filter/braille/filters/liblouis.defs | 11 - filter/braille/filters/liblouis1.defs.gen.in | 277 - filter/braille/filters/musicxmltobrf.in | 39 - filter/braille/filters/texttobrf.in | 278 - filter/braille/filters/vectortobrf.in | 55 - filter/braille/filters/vectortopdf.in | 65 - libcupsfilters.pc.in | 12 - libppd.pc.in | 12 - mime/braille.convs | 94 - mime/braille.types | 51 - ppd/README.md | 22 - ppd/array-private.h | 40 - ppd/array.c | 138 - ppd/debug-internal.h | 87 - ppd/debug-private.h | 57 - ppd/debug.c | 627 - ppd/encode.c | 411 - ppd/epson.h | 17 - ppd/file-private.h | 77 - ppd/file.c | 259 - ppd/font.defs | 45 - ppd/genstrings.cxx | 197 - ppd/hp.h | 14 - ppd/imagetops-pstops.c | 5307 ------ ppd/ipp-private.h | 62 - ppd/label.h | 19 - ppd/language-private.h | 74 - ppd/language.c | 721 - ppd/media.defs | 198 - ppd/pdftops.c | 1867 -- ppd/ppd-attr.c | 315 - ppd/ppd-cache.c | 4721 ------ ppd/ppd-collection.cxx | 3166 ---- ppd/ppd-conflicts.c | 1196 -- ppd/ppd-custom.c | 98 - ppd/ppd-emit.c | 1283 -- ppd/ppd-filter.c | 2047 --- ppd/ppd-filter.h | 205 - ppd/ppd-generator.c | 2609 --- ppd/ppd-ipp.c | 1569 -- ppd/ppd-load-profile.c | 894 - ppd/ppd-localize.c | 737 - ppd/ppd-mark.c | 1102 -- ppd/ppd-page.c | 378 - ppd/ppd.c | 3519 ---- ppd/ppd.h | 889 - ppd/ppdc-array.cxx | 149 - ppd/ppdc-attr.cxx | 51 - ppd/ppdc-catalog.cxx | 873 - ppd/ppdc-choice.cxx | 46 - ppd/ppdc-constraint.cxx | 49 - ppd/ppdc-driver.cxx | 1324 -- ppd/ppdc-file.cxx | 93 - ppd/ppdc-filter.cxx | 45 - ppd/ppdc-font.cxx | 51 - ppd/ppdc-group.cxx | 87 - ppd/ppdc-import.cxx | 333 - ppd/ppdc-mediasize.cxx | 70 - ppd/ppdc-message.cxx | 43 - ppd/ppdc-option.cxx | 112 - ppd/ppdc-private.h | 46 - ppd/ppdc-profile.cxx | 50 - ppd/ppdc-shared.cxx | 67 - ppd/ppdc-source.cxx | 3813 ----- ppd/ppdc-string.cxx | 49 - ppd/ppdc-variable.cxx | 55 - ppd/ppdc.cxx | 465 - ppd/ppdc.h | 524 - ppd/ppdhtml.cxx | 178 - ppd/ppdi.cxx | 124 - ppd/ppdmerge.cxx | 359 - ppd/ppdpo.cxx | 249 - ppd/raster-error.c | 131 - ppd/raster-interpret.c | 1876 -- ppd/raster-private.h | 46 - ppd/raster.defs | 84 - ppd/rastertops.c | 540 - ppd/snprintf.c | 343 - ppd/string-private.h | 212 - ppd/string.c | 742 - ppd/test.ppd | 263 - ppd/test2.ppd | 244 - ppd/testdriver.c | 170 - ppd/testppd.c | 1967 --- ppd/thread-private.h | 74 - ppd/thread.c | 141 - utils/cups-browsed-upstart.conf | 20 - utils/cups-browsed.8 | 103 - utils/cups-browsed.c | 14147 ---------------- utils/cups-browsed.conf.5 | 1000 -- utils/cups-browsed.conf.in | 760 - utils/cups-browsed.in | 156 - utils/cups-browsed.service | 11 - utils/org.cups.cupsd.Notifier.xml | 147 - 253 files changed, 244 insertions(+), 128440 deletions(-) delete mode 100644 backend/cups-brf.c delete mode 100644 backend/implicitclass.c delete mode 100644 banners/classified delete mode 100644 banners/confidential delete mode 100644 banners/form delete mode 100644 banners/secret delete mode 100644 banners/standard delete mode 100644 banners/topsecret delete mode 100644 banners/unclassified delete mode 100644 charset/pdf.utf-8.heavy delete mode 100644 charset/pdf.utf-8.simple delete mode 100644 cupsfilters/bannertopdf.c delete mode 100644 cupsfilters/bitmap.c delete mode 100644 cupsfilters/bitmap.h delete mode 100644 cupsfilters/catalog.c delete mode 100644 cupsfilters/catalog.h delete mode 100644 cupsfilters/check.c delete mode 100644 cupsfilters/cmyk.c delete mode 100644 cupsfilters/colord.c delete mode 100644 cupsfilters/colord.h delete mode 100644 cupsfilters/colormanager.c delete mode 100644 cupsfilters/colormanager.h delete mode 100644 cupsfilters/debug-internal.h delete mode 100644 cupsfilters/debug.c delete mode 100644 cupsfilters/dither.c delete mode 100644 cupsfilters/driver.h delete mode 100644 cupsfilters/filter.c delete mode 100644 cupsfilters/filter.h delete mode 100644 cupsfilters/fontembed-private.h delete mode 100644 cupsfilters/fontembed/README delete mode 100644 cupsfilters/fontembed/aglfn13.c delete mode 100644 cupsfilters/fontembed/dynstring-private.h delete mode 100644 cupsfilters/fontembed/dynstring.c delete mode 100644 cupsfilters/fontembed/embed-pdf-private.h delete mode 100644 cupsfilters/fontembed/embed-pdf.c delete mode 100644 cupsfilters/fontembed/embed-sfnt-private.h delete mode 100644 cupsfilters/fontembed/embed-sfnt.c delete mode 100644 cupsfilters/fontembed/embed.c delete mode 100644 cupsfilters/fontembed/fontfile.c delete mode 100644 cupsfilters/fontembed/frequent-private.h delete mode 100644 cupsfilters/fontembed/frequent.c delete mode 100644 cupsfilters/fontembed/macroman-private.h delete mode 100644 cupsfilters/fontembed/sfnt-private.h delete mode 100644 cupsfilters/fontembed/sfnt-subset.c delete mode 100644 cupsfilters/fontembed/sfnt.c delete mode 100644 cupsfilters/fontembed/test-analyze.c delete mode 100644 cupsfilters/fontembed/test-pdf.c delete mode 100644 cupsfilters/fontembed/test-ps.c delete mode 100644 cupsfilters/ghostscript.c delete mode 100644 cupsfilters/ieee1284.c delete mode 100644 cupsfilters/ieee1284.h delete mode 100644 cupsfilters/image-colorspace.c delete mode 100644 cupsfilters/image-jpeg.c delete mode 100644 cupsfilters/image-png.c delete mode 100644 cupsfilters/image-private.h delete mode 100644 cupsfilters/image-tiff.c delete mode 100644 cupsfilters/image-zoom.c delete mode 100644 cupsfilters/image.c delete mode 100644 cupsfilters/image.h delete mode 100644 cupsfilters/image.pgm delete mode 100644 cupsfilters/image.ppm delete mode 100644 cupsfilters/imagetopdf.c delete mode 100644 cupsfilters/imagetoraster.c delete mode 100644 cupsfilters/ipp.c delete mode 100644 cupsfilters/ipp.h delete mode 100644 cupsfilters/log.h delete mode 100644 cupsfilters/lut.c delete mode 100644 cupsfilters/mupdftopwg.c delete mode 100644 cupsfilters/pack.c delete mode 100644 cupsfilters/pclmtoraster.cxx delete mode 100644 cupsfilters/pdf.cxx delete mode 100644 cupsfilters/pdf.h delete mode 100644 cupsfilters/pdftopdf/intervalset-private.h delete mode 100644 cupsfilters/pdftopdf/intervalset.cxx delete mode 100644 cupsfilters/pdftopdf/nup-private.h delete mode 100644 cupsfilters/pdftopdf/nup.cxx delete mode 100644 cupsfilters/pdftopdf/pdftopdf-private.h delete mode 100644 cupsfilters/pdftopdf/pdftopdf-processor-private.h delete mode 100644 cupsfilters/pdftopdf/pdftopdf-processor.cxx delete mode 100644 cupsfilters/pdftopdf/pdftopdf.cxx delete mode 100644 cupsfilters/pdftopdf/pptypes-private.h delete mode 100644 cupsfilters/pdftopdf/pptypes.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-cm-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-cm.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-tools-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-tools.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-xobject-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-xobject.cxx delete mode 100644 cupsfilters/pdftoraster.cxx delete mode 100644 cupsfilters/pdfutils.c delete mode 100644 cupsfilters/pdfutils.h delete mode 100644 cupsfilters/pwgtopdf.cxx delete mode 100644 cupsfilters/pwgtoraster.c delete mode 100644 cupsfilters/raster.c delete mode 100644 cupsfilters/raster.h delete mode 100644 cupsfilters/rastertopwg.c delete mode 100644 cupsfilters/rgb.c delete mode 100644 cupsfilters/srgb.c delete mode 100644 cupsfilters/test1284.c delete mode 100644 cupsfilters/testcmyk.c delete mode 100644 cupsfilters/testdither.c delete mode 100644 cupsfilters/testimage.c delete mode 100644 cupsfilters/testpdf1.c delete mode 100644 cupsfilters/testpdf2.c delete mode 100644 cupsfilters/testrgb.c delete mode 100644 cupsfilters/texttopdf.c delete mode 100644 cupsfilters/texttotext.c delete mode 100644 cupsfilters/universal.c delete mode 100644 data/banner-instr-append.txt delete mode 100644 data/classified.pdf delete mode 100644 data/classified.ps delete mode 100644 data/confidential.pdf delete mode 100644 data/confidential.ps delete mode 100644 data/default-testpage.pdf delete mode 100644 data/default.pdf delete mode 100644 data/form-instr-append.txt delete mode 100644 data/form_english.pdf delete mode 100644 data/form_english_in.odt delete mode 100644 data/form_russian.pdf delete mode 100644 data/form_russian_in.odt delete mode 100755 data/makePDFfromPS.sh delete mode 100644 data/secret.pdf delete mode 100644 data/secret.ps delete mode 100644 data/standard.pdf delete mode 100644 data/standard.ps delete mode 100644 data/testprint delete mode 100644 data/testprint-instr-append.txt delete mode 100644 data/topsecret.pdf delete mode 100644 data/topsecret.ps delete mode 100644 data/unclassified.pdf delete mode 100644 data/unclassified.ps delete mode 100644 drv/generic-brf.drv delete mode 100644 drv/generic-ubrl.drv delete mode 100644 drv/indexv3.drv delete mode 100644 drv/indexv4.drv delete mode 100644 filter/braille/drivers/common/fr-braille.po delete mode 100644 filter/braille/drivers/common/media-braille.defs delete mode 100755 filter/braille/drivers/generic/brftoembosser.in delete mode 100755 filter/braille/drivers/index/imageubrltoindexv3.in delete mode 100755 filter/braille/drivers/index/imageubrltoindexv4.in delete mode 100644 filter/braille/drivers/index/index.defs delete mode 100644 filter/braille/drivers/index/index.sh.in delete mode 100644 filter/braille/drivers/index/indexv3.sh.in delete mode 100644 filter/braille/drivers/index/indexv4.sh.in delete mode 100755 filter/braille/drivers/index/textbrftoindexv3.in delete mode 100644 filter/braille/drivers/index/ubrlto4dot.c delete mode 100644 filter/braille/filters/TODO.txt delete mode 100644 filter/braille/filters/braille.defs delete mode 100755 filter/braille/filters/brftopagedbrf.in delete mode 100644 filter/braille/filters/cups-braille.sh.in delete mode 100644 filter/braille/filters/imagemagick.defs delete mode 100755 filter/braille/filters/imagetobrf.in delete mode 100644 filter/braille/filters/liblouis.defs delete mode 100644 filter/braille/filters/liblouis1.defs.gen.in delete mode 100644 filter/braille/filters/musicxmltobrf.in delete mode 100755 filter/braille/filters/texttobrf.in delete mode 100644 filter/braille/filters/vectortobrf.in delete mode 100644 filter/braille/filters/vectortopdf.in delete mode 100644 libcupsfilters.pc.in delete mode 100644 libppd.pc.in delete mode 100644 mime/braille.convs delete mode 100644 mime/braille.types delete mode 100644 ppd/README.md delete mode 100644 ppd/array-private.h delete mode 100644 ppd/array.c delete mode 100644 ppd/debug-internal.h delete mode 100644 ppd/debug-private.h delete mode 100644 ppd/debug.c delete mode 100644 ppd/encode.c delete mode 100644 ppd/epson.h delete mode 100644 ppd/file-private.h delete mode 100644 ppd/file.c delete mode 100644 ppd/font.defs delete mode 100644 ppd/genstrings.cxx delete mode 100644 ppd/hp.h delete mode 100644 ppd/imagetops-pstops.c delete mode 100644 ppd/ipp-private.h delete mode 100644 ppd/label.h delete mode 100644 ppd/language-private.h delete mode 100644 ppd/language.c delete mode 100644 ppd/media.defs delete mode 100644 ppd/pdftops.c delete mode 100644 ppd/ppd-attr.c delete mode 100644 ppd/ppd-cache.c delete mode 100644 ppd/ppd-collection.cxx delete mode 100644 ppd/ppd-conflicts.c delete mode 100644 ppd/ppd-custom.c delete mode 100644 ppd/ppd-emit.c delete mode 100644 ppd/ppd-filter.c delete mode 100644 ppd/ppd-filter.h delete mode 100644 ppd/ppd-generator.c delete mode 100644 ppd/ppd-ipp.c delete mode 100644 ppd/ppd-load-profile.c delete mode 100644 ppd/ppd-localize.c delete mode 100644 ppd/ppd-mark.c delete mode 100644 ppd/ppd-page.c delete mode 100644 ppd/ppd.c delete mode 100644 ppd/ppd.h delete mode 100644 ppd/ppdc-array.cxx delete mode 100644 ppd/ppdc-attr.cxx delete mode 100644 ppd/ppdc-catalog.cxx delete mode 100644 ppd/ppdc-choice.cxx delete mode 100644 ppd/ppdc-constraint.cxx delete mode 100644 ppd/ppdc-driver.cxx delete mode 100644 ppd/ppdc-file.cxx delete mode 100644 ppd/ppdc-filter.cxx delete mode 100644 ppd/ppdc-font.cxx delete mode 100644 ppd/ppdc-group.cxx delete mode 100644 ppd/ppdc-import.cxx delete mode 100644 ppd/ppdc-mediasize.cxx delete mode 100644 ppd/ppdc-message.cxx delete mode 100644 ppd/ppdc-option.cxx delete mode 100644 ppd/ppdc-private.h delete mode 100644 ppd/ppdc-profile.cxx delete mode 100644 ppd/ppdc-shared.cxx delete mode 100644 ppd/ppdc-source.cxx delete mode 100644 ppd/ppdc-string.cxx delete mode 100644 ppd/ppdc-variable.cxx delete mode 100644 ppd/ppdc.cxx delete mode 100644 ppd/ppdc.h delete mode 100644 ppd/ppdhtml.cxx delete mode 100644 ppd/ppdi.cxx delete mode 100644 ppd/ppdmerge.cxx delete mode 100644 ppd/ppdpo.cxx delete mode 100644 ppd/raster-error.c delete mode 100644 ppd/raster-interpret.c delete mode 100644 ppd/raster-private.h delete mode 100644 ppd/raster.defs delete mode 100644 ppd/rastertops.c delete mode 100644 ppd/snprintf.c delete mode 100644 ppd/string-private.h delete mode 100644 ppd/string.c delete mode 100644 ppd/test.ppd delete mode 100644 ppd/test2.ppd delete mode 100644 ppd/testdriver.c delete mode 100644 ppd/testppd.c delete mode 100644 ppd/thread-private.h delete mode 100644 ppd/thread.c delete mode 100644 utils/cups-browsed-upstart.conf delete mode 100644 utils/cups-browsed.8 delete mode 100644 utils/cups-browsed.c delete mode 100644 utils/cups-browsed.conf.5 delete mode 100644 utils/cups-browsed.conf.in delete mode 100644 utils/cups-browsed.in delete mode 100644 utils/cups-browsed.service delete mode 100644 utils/org.cups.cupsd.Notifier.xml diff --git a/CHANGES.md b/CHANGES.md index cf78c52a4..b3af4ff71 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,310 +2,16 @@ ## CHANGES IN V2.0b1 (TBA) -### libcupsfilters - -- Introduced the filter functions concept converting filter - executables into library functions with common call scheme, moving - their core functionality into libcupsfilters and allowing easier use - by Printer Applications. Common data about the printer and the job - are supplied via a data structure, which is the same for each filter - in a chain of filters. The data structure can be extended via named - extensions. - - Converted nearly all filters to filter functions, only exceptions are rastertoescpx, rastertopclx, commandtoescpx, commandtopclx, and foomatic-rip. The latter is deeply involved with Foomatic PPDs and - the others are legacy printer drivers. Filter functions which - output PostScript are implemented in libppd. - -- Converted CUPS' rastertopwg filter into the cfFilterRasterToPWG() - filter function. - -- Created new cfFilterPWGToRaster() filter function primarily to print - raster input (PWG Raster, Apple Raster, images) to CUPS Raster - drivers in driver-retro-fitting Printer Applications. - -- Converted all filter functions to completely work without PPD files, - using only printer and job IPP attributes and an option list for - options not mappable to IPP attributes. For some filter functions - there are also wrappers for a more comprehensive PPD file support in + the others are legacy printer drivers. The filter functions are + mainly in libcupsfilters, the ones which generate PostScript are in libppd. -- Added concept of callback functions for logging, for the filter - functions not spitting their log to stderr. - -- Added concept of callback functions for telling that a job is - cancelled so that filter functions can return early. This change is - to get more flexibility and especially to support the - papplJobIsCanceled() of PAPPL. - -- Added new streaming mode triggered by the boolean - "filter-streaming-mode" option. In this mode a filter (function) is - supposed to avoid everything which prevents the job data from - streaming, as loading the whole job (or good part of it) into a - temporary file or into memory, interpreting PDF, pre-checking input - file type or zero-page jobs, ... This is mainly to be used by - Printer Applications when they do raster printing in streaming mode, - to run with lowest resources possible. Currently - cfFilterGhostscript() and cfFilterPDFToPDF() got a streaming - mode. In streaming mode PostScript (not PDF) is assumed as input and - no zero-page-job check is done, and all QPDF processing (page - management, page size adjustment, ...) is skipped and only JCL - according to the PPD added. - -- Added raster-only PDF and PCLm output support to the ghostscript() - filter function. Note that PCLm does not support back side - orientation for duplex. - -- cfFilterPDFToPDF(): Introduced new "input-page-ranges" attribute - (Issue #365, Pull request #444, #445). - -- Added cfFilterChain() filter function to run several filter - functions in a chain. - -- Added filterPOpen() and filterPClose() functions which similar to - popen() and pclose() create a file descriptor which either takes - data to feed into a filter function or provides data coming out of a - filter function. - -- Added new cfFilterExternal() filter function which calls an external - CUPS filter (or backend) executable specified in the parameters, for - example a legacy or proprietary printer driver which cannot be - converted into a filter function. Backends can be run both for job - execution or in their discovery mode. The environment in which the - external executable is running is resembling CUPS as best as - possible. - -- Added support for the back and side channels which CUPS uses for - additional communication between the filters and the backend into - the filter function infrastructure. Now filter functions can use - these channels and also CUPS filters or backends called via the - cfFilterExternal() function. Printer Applications can easily create - the needed pipes via the new function cfFilterOpenBackAndSidePipes() - and close them via cfFilterCloseBackAndSidePipes() and filter - functions used as classic CUPS filters get the appropriate file - descriptors supplied by ihe ppdFilterCUPSWrapper() function of - libppd. - -- Added the cfFilterUniversal() filter function which allows a single - CUPS filter executable which auto-creates chains of filter function - calls to convert any input data format into any other output data - format. So CUPS can call a single filter for any conversion, taking - up less resources. Thanks to Pranshu Kharkwal for this excellent - GSoC project (Pull request #421). - -- Added functions to read out IPP attributes from the job and check - them against the IPP attributes (capabilities) of the printer: - cfIPPAttrEnumValForPrinter(), cfIPPAttrIntValForPrinter(), - cfIPPAttrResolutionForPrinter() - -- Added functions cfGenerateSizes() and cfGetPageDimensions() to match - input page sizes with the page sizes available on the printer - according to printer IPP attributes. - -- Moved IEEE1284-device-ID-related functions into the public API of - libcupsfilters, also made the internal functions public and renamed - them all to cfIEEE1284...(), moved test1284 to cupsfilters/. - -- Extended cfIEEE1284NormalizeMakeAndModel() to a universal function - to clean up and normalize make/model strings (also device IDs) to - get human-readable, machine-readable, or easily sortable make/model - strings. Also allow supplying a regular expression to extract driver - information when the input string is a PPD's *NickName. - -- When calling filters without having printer attributes, improved - understanding of color mode options/attributes. Options - "output-mode", "OutputMode", "print-color-mode", and choices "auto", - "color", "auto-monochrome", "process-monochrome", and "bi-level" are - supported now and default color mode is RGB 8-bit color and not - 1-bit monochrome. - -- When parsing IPP attributes/options map the color spaces the same - way as in the PPD generator (Issue #326, Pull request #327). - -- Added new oneBitToGrayLine() API function which converts a line of - 1-bit monochrome pixels into 8-bit grayscale format - (cupsfilters/bitmap.[ch]). - -- Removed support for asymmetric image resolutions ("ppi=XXXxYYY") in - cfFilterImageToPDF() and cfFilterImageToRaster() as CUPS does not - support this (Issue #347, Pull request #361, OpenPrinting CUPS issue - #115). - -- Removed now obsolete apply_filters() API function to call a chain of - external CUPS filter executables, as we have filter functions now - and even can call one or another filter executable (or even backend) - via cfFilterExternal(). - -- Build system, README: Require CUPS 2.2.2+ and QPDF 10.3.2+. Removed - now unneeded ./configure switches for PCLm support in QPDF and for - use of the urftopdf filter for old CUPS versions. - -- Renamed function/data type/constant names to get a consistent API: - Functions start with "cf" and the name is in camel-case, data types - start with "cf_" and are all-lowercase with "_" separators, - constants start with "CF_" and are all-uppercase, also with "_" - separators. - -- Bumped soname to 2, as we have a new API now. - -- Build system: Remove '-D_PPD_DEPRECATED=""' from the compiling - command lines of the source files which use libcups. The flag is not - supported any more for longer times already and all the PPD-related - functions deprecated by CUPS have moved into libppd now. - -- Older versions of libcups (< 2.3.1) had the enum name for - fold-accordion finishings mistyped. Added a workaround. - -- Added support for Sharp-proprietary "ARDuplex" PPD option name for - double-sided printing. - -- Build system: Add files in gitignore that are generated by - "autogen.sh", "configure", and "make" (Pull request #336). - -- Fixed possible crash bug in oneBitLine() function. - -- In ghostscript() pass on LD_LIBRARY_PATH to Ghostscript - - -### libppd - -- Added the new libppd library overtaking all the PPD handling - functions from libcups, CUPS' ppdc utility (PPD compiler using *.drv - files), and the PPD file collection handling functionality from - cups-driverd, as they are deprecated there and will probably get - removed with the next CUPS version. This form of conservation is - mainly intended for converting classic printer drivers which use - PPDs into Printer Applications without completely rewriting them. - -- Added functions ppdLoadAttributes(), ppdFilterLoadPPD(), - ppdFilterLoadPPDFile(), ppdFilterFreePPD(), and - ppdFilterFreePPDFile() to convert all PPD file options and settings - relevant for the filter functions in libcupsfilters into printer IPP - attributes and options. Also create a named ("libppd") extension in - the filter data structure, for filter functions explicitly - supporting PPD files. - -- Added ppdFilterCUPSWrapper() convenience function to easily create a - classic CUPS filter from a filter function. - -- Converted the ...tops CUPS filters into the filter functions - ppdFilterPDFToPS(), and ppdFilterRasterToPS(). As PostScript as - print output format is as deprecated as PPD gfiles and all - PostScript printers use PPD files, we move PostScript output - generation to libppd, too. - -- Converted CUPS' pstops filter into the ppdFilterPSToPS() filter - functions. - -- Introduced ppdFilterImageToPS() filter function for Printer - Applications to print on PostScript printers. It is used to print - images (IPP standard requires at least JPEG to be accepted) without - need of a PDF renderer (cfFilterImageToPDF -> cfFilterPDFToPS) and - without need to convert to Raster (cfFilterImageToRaster -> - cfFilterRasterToPS). - -- Created wrappers to add PPD-defined JCL code to jobs for native PDF - printers, ppdFilterImageToPDF() and ppdFilterPDFToPDF() filter - functions with the underlying ppdFilterEmitJCL() function. - -- Created ppdFilterUniversal() wrapper function for - cfFilterUniversal() to add PPD file support, especially for PPD - files which define driver filters. - -- ppdFilterExternalCUPS() wrapper function for cfFilterExternal() to - add PPD file support. - -- Auto-pre-fill the presets with most suitable options from the PPD - files with new ppdCacheAssignPresets() function (Only if not filled - via "APPrinterPreset" in the PPD). This way we can use the IPP - options print-color-mode, print-quality, and print-content-optimize - in a PPD/CUPS-driver-retro-fitting Printer Application with most - PPDs. - -- In ppdCacheCreateWithPPD() also support presets for - print-ontent-optimize, not only for print-color-mode and - print-quality - -- In the ppdCacheGetPageSize() function do not only check the fit of - the margins with the page size matching the job's page size (like - "A4") but also the margins of the variants (like "A4.Borderless"), - even if the variant's size dimensions differ from the base size (and - the physical paper size), for example for overspray in borderless - printing mode (HPLIP does this). Also select a borderless variant - only if the job requests borderless (all margins zero). - -- Move the functions ppdPwgUnppdizeName(), ppdPwgPpdizeName(), and - ppdPwgPpdizeResolution() into the public API. - -- In the ppdPwgUnppdizeName() allow to supply NULL as the list of - characters to replace by dashes. Then all non-alpha-numeric - characters get replaced, to result in IPP-conforming keyword - strings. - -- In the ppdPwgUnppdizeName() function support negative numbers - -- ppdFilterPSToPS(): Introduced new "input-page-ranges" attribute - (Issue #365, Pull request #444, #445). - -- Changed "ColorModel" option in the PPDs from the PPD generator to - mirror the print-color-mode IPP attribute instead of providing all - color space/depth combos for manual selection. Color space and depth - are now auto-selected by the urf-supported and - pwg-raster-document-type-supported printer IPP attributes and the - settings of print-color-mode and print-quality. This is now - implemented in the ghostscript() filter function both for use of the - auto-generated PPD file for driverless iPP printers and use without - PPD, based on IPP attributes. For this the new library functions - cupsRasterPrepareHeader() to create a header for Raster output and - cupsRasterSetColorSpace() to auto-select color space and depth were - created. - -- Clean-up of human-readable string handling in the PPD generator. - -- Removed support for asymmetric image resolutions ("ppi=XXXxYYY") in - cfFilterImageToPS() as CUPS does not support this (Issue #347, Pull - request #361, OpenPrinting CUPS issue #115). - -- Removed versioning.h and the macros defined in this file (Issue - #334). - -- Removed ppdCreateFromIPPCUPS(), we have a better one in - libcupsfilters. Also removed corresponding test in testppd. - -- Build system, README: Require CUPS 2.2.2+ and QPDF 10.3.2+. Removed - now unneeded ./configure switches for PCLm support in QPDF and for - use of the urftopdf filter for old CUPS versions. - -- Build system: Remove '-D_PPD_DEPRECATED=""' from the compiling - command lines of the source files which use libcups. The flag is not - supported any more for longer times already and all the PPD-related - functions deprecated by CUPS have moved into libppd now. - -- Older versions of libcups (< 2.3.1) had the enum name for - fold-accordion finishings mistyped. Added a workaround. - -- Added support for Sharp-proprietary "ARDuplex" PPD option name for - double-sided printing. - -- Build system: Add files in gitignore that are generated by - "autogen.sh", "configure", and "make" (Pull request #336). - -- Fixed PPD memory leak caused by "emulators" field not freed - (OpenPrinting CUPS issue #124). - Make "True" in boolean options - case-insensitive (OpenPrinting CUPS pull request #106). - - -### cups-filters - -- Converted nearly all filters to filter functions, only exceptions - are rastertoescpx, rastertopclx, commandtoescpx, commandtopclx, and - foomatic-rip. The latter is deeply involved with Foomatic PPDs and - the others are legacy printer drivers. - - Replaced all the filters converted to filter functions by a simple - wrapper executable using ppdFilterCUPSWrapper() for backward - compatibility with CUPS 2.x. + wrapper executable using ppdFilterCUPSWrapper() of libppd for + backward compatibility with CUPS 2.x. - Added new streaming mode triggered by the boolean "filter-streaming-mode" option. In this mode a filter (function) is @@ -321,80 +27,32 @@ size adjustment, ...) is skipped and only JCL according to the PPD added. -- The CUPS filter imagetops uses the cfFilterImageToPS() filter - function now. +- The CUPS filter imagetops uses the ppdFilterImageToPS() filter + function of libppd now. -- driverless, driverless-fax, Added IPP Fax Out support. Now printer - setup tools list an additional fax "driver". A fax queue is created +- driverless, driverless-fax: Added IPP Fax Out support. Now printer + setup tools list an additional fax "driver". A fax queue is created by selecting this driver. Jobs have to be sent with "-o phone=12345" to supply the destination phone number (Pull request #280, #293, #296, #302, #304, #305, #306, #309, Issue #298, #308). -- sys5ippprinter, cups-browsed: Removed sys5ippprinter, as CUPS does - not support System V interface scripts any more. This first approach - of PPD-less printing was also not actually made use of. +- sys5ippprinter: Removed sys5ippprinter, as CUPS does not support + System V interface scripts any more. This first approach of PPD-less + printing was also not actually made use of. - urftopdf: Removed as we require CUPS 2.2.2+ now which supports Apple Raster by itself. -- Build system, README: Require CUPS 2.2.2+ and QPDF 10.3.2+. Removed - now unneeded ./configure switches for PCLm support in QPDF and for - use of the urftopdf filter for old CUPS versions. - -- Sample PPDs: Renamed source directory from "ppd/" to "ppdfiles/" - -- Build system: Added missing library dependencies to the filters to - make parallel builds work (Issue #319). - -- Build system: Remove '-D_PPD_DEPRECATED=""' from the compiling - command lines of the source files which use libcups. The flag is not - supported any more for longer times already and all the PPD-related - functions deprecated by CUPS have moved into libppd now. - -- Build system: Add files in gitignore that are generated by - "autogen.sh", "configure", and "make" (Pull request #336). - - -### braille-printer-app - -- textbrftoindex: Fix control character filtering (Pull - request #409) - -- Build system: Remove '-D_PPD_DEPRECATED=""' from the - compiling command lines of the source files which use - libcups. The flag is not supported any more for longer times - already and all the PPD-related functions deprecated by CUPS - have moved into libppd now. - -- Build system: Add files in gitignore that are generated by - "autogen.sh", "configure", and "make" (Pull request #336). - - -### cups-browsed - -- Added multi-threaded operation, the Avahi resolver callback (which - examines the remote printer, registers it, checks whether we want a - local queue for it, adds it to a cluster, ...) and the - creation/modification of a local CUPS queue is now done in separate - threads, so that these processes can get executed in parallel to - keep the local queues up-to-date more timely and to not overload the - system's resources. Thanks a lot to Mohit Mohan who did this work - as Google Summer of Code 2020 project - (https://github.com/mohitmo/GSoC-2020-Documentation). - -- Let the implicitclass backend use filter functions instead of - calling filter executables. - - Build system, README.md: Require CUPS 2.2.2+ and QPDF 10.3.2+. Removed now unneeded ./configure switches for PCLm support in QPDF and for use of the urftopdf filter for old CUPS versions. +- Sample PPDs: Renamed source directory from "ppd/" to "ppdfiles/". + - Build system: Remove '-D_PPD_DEPRECATED=""' from the compiling command lines of the source files which use libcups. The flag is not supported any more for longer times already and all the PPD-related functions deprecated by CUPS have moved into libppd now. -- Build system: Add files in gitignore that are generated by +- Build system: Add files in .gitignore that are generated by "autogen.sh", "configure", and "make" (Pull request #336). - -- implicitclass: Added "#include " (Issue #335). diff --git a/Makefile.am b/Makefile.am index ba357d1cf..992877737 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -pkgconfdir = $(libdir)/pkgconfig -pkgconf_DATA = \ - libcupsfilters.pc \ - libppd.pc - doc_DATA = \ ABOUT-NLS \ AUTHORS \ @@ -21,91 +16,49 @@ EXTRA_DIST = \ autogen.sh \ config.rpath \ ln-srf \ - libcupsfilters.pc.in \ - libppd.pc.in \ - utils/cups-browsed.service \ - utils/cups-browsed-upstart.conf \ - utils/driverless-fax.in \ - filter/braille/drivers/index/ubrlto4dot.c \ - filter/braille/filters/TODO.txt + utils/driverless-fax.in EXTRA_DIST += \ - ppd/testdriver.c \ - data/makePDFfromPS.sh \ - data/classified.ps \ - data/confidential.ps \ - data/secret.ps \ - data/standard.ps \ - data/topsecret.ps \ - data/unclassified.ps \ - data/banner-instr-append.txt \ - data/form-instr-append.txt \ - data/testprint-instr-append.txt \ drv/custom-media-lines -# ========= -# utilities -# ========= - -pkgutilsdir = $(bindir) -pkgutils_PROGRAMS = - # ======== # Backends # ======== pkgbackenddir = $(CUPS_SERVERBIN)/backend -pkgbackend_PROGRAMS = parallel serial beh implicitclass +pkgbackend_PROGRAMS = parallel serial beh parallel_SOURCES = \ backend/parallel.c parallel_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) parallel_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) serial_SOURCES = \ backend/serial.c serial_LDADD = \ - libppd.la \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) serial_CFLAGS = \ - -I$(srcdir)/ppd/ \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) beh_SOURCES = \ backend/beh.c beh_LDADD = \ - libppd.la \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) beh_CFLAGS = \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) - -implicitclass_SOURCES = \ - backend/implicitclass.c -implicitclass_LDADD = \ - libcupsfilters.la \ - libppd.la \ - $(CUPS_LIBS) -implicitclass_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) -if ENABLE_BRAILLE -pkgbackend_PROGRAMS += cups-brf -endif - -cups_brf_SOURCES = \ - backend/cups-brf.c - -# ============== -# PPD Generators -# ============== +# ==================== +# "driverless" utility +# ==================== utils/driverless-fax: utils/driverless-fax.in Makefile sed \ -e "s|\@CUPS_SERVERBIN\@|$(CUPS_SERVERBIN)|" \ @@ -123,495 +76,14 @@ endif driverless_SOURCES = \ utils/driverless.c driverless_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) driverless_CXXFLAGS = $(driverless_CFLAGS) driverless_LDADD = \ - libcupsfilters.la \ - libppd.la \ - $(CUPS_LIBS) - - -# ======= -# Banners -# ======= -pkgbannerdir = $(CUPS_DATADIR)/banners -pkgbanner_DATA = - -bannerfiles = \ - banners/classified \ - banners/confidential \ - banners/secret \ - banners/standard \ - banners/form \ - banners/topsecret \ - banners/unclassified - -pkgbanner_DATA += $(bannerfiles) - -EXTRA_DIST += $(bannerfiles) - -# ======== -# Charsets -# ======== -pkgcharsetdir = $(CUPS_DATADIR)/charsets -pkgcharset_DATA = \ - charset/pdf.utf-8.heavy \ - charset/pdf.utf-8.simple - -EXTRA_DIST += $(pkgcharset_DATA) - -# ======= -# Filters -# ======= - -pkgfilter_PROGRAMS = -pkgfilterdir = $(CUPS_SERVERBIN)/filter - -# ==================== -# CUPS Filters library -# ==================== -pkgfiltersincludedir = $(includedir)/cupsfilters -pkgfiltersinclude_DATA = \ - cupsfilters/bitmap.h \ - cupsfilters/catalog.h \ - cupsfilters/colord.h \ - cupsfilters/colormanager.h \ - cupsfilters/driver.h \ - cupsfilters/filter.h \ - cupsfilters/ieee1284.h \ - cupsfilters/image.h \ - cupsfilters/ipp.h \ - cupsfilters/log.h \ - cupsfilters/pdf.h \ - cupsfilters/pdfutils.h \ - cupsfilters/raster.h - -lib_LTLIBRARIES = libcupsfilters.la - -check_PROGRAMS = \ - testcmyk \ - testdither \ - testimage \ - testrgb \ - test1284 \ - testpdf1 \ - testpdf2 \ - test-analyze \ - test-pdf \ - test-ps -TESTS = \ - testdither \ - testpdf1 \ - testpdf2 \ - test-analyze \ - test-pdf \ - test-ps - -# testcmyk # fails as it opens some image.ppm which is nowerhe to be found. -# testimage # requires also some ppm file as argument -# testrgb # same error -# FIXME: run old testdither -# ./testdither > test/0-255.pgm 2>test/0-255.log -# ./testdither 0 127 255 > test/0-127-255.pgm 2>test/0-127-255.log -# ./testdither 0 85 170 255 > test/0-85-170-255.pgm 2>test/0-85-170-255.log -# ./testdither 0 63 127 170 198 227 255 > test/0-63-127-170-198-227-255.pgm 2>test/0-63-127-170-198-227-255.log -# ./testdither 0 210 383 > test/0-210-383.pgm 2>test/0-210-383.log -# ./testdither 0 82 255 > test/0-82-255.pgm 2>test/0-82-255.log -# ./testdither 0 510 > test/0-510.pgm 2>test/0-510.log -# ./testdither 0 1020 > test/0-1020.pgm 2>test/0-1020.log - -# We need ieee1284 up and running. -# Leave it to the user to run if they have the bus. -#TESTS += test1284 - -libcupsfilters_la_SOURCES = \ - cupsfilters/bannertopdf.c \ - cupsfilters/bitmap.c \ - cupsfilters/catalog.c \ - cupsfilters/check.c \ - cupsfilters/cmyk.c \ - cupsfilters/colord.c \ - cupsfilters/colormanager.c \ - cupsfilters/debug.c \ - cupsfilters/debug-internal.h \ - cupsfilters/dither.c \ - cupsfilters/filter.c \ - cupsfilters/fontembed-private.h \ - cupsfilters/fontembed/aglfn13.c \ - cupsfilters/fontembed/dynstring.c \ - cupsfilters/fontembed/dynstring-private.h \ - cupsfilters/fontembed/embed.c \ - cupsfilters/fontembed/embed-sfnt.c \ - cupsfilters/fontembed/embed-sfnt-private.h \ - cupsfilters/fontembed/embed-pdf.c \ - cupsfilters/fontembed/embed-pdf-private.h \ - cupsfilters/fontembed/fontfile.c \ - cupsfilters/fontembed/frequent.c \ - cupsfilters/fontembed/frequent-private.h \ - cupsfilters/fontembed/macroman-private.h \ - cupsfilters/fontembed/sfnt.c \ - cupsfilters/fontembed/sfnt-private.h \ - cupsfilters/fontembed/sfnt-subset.c \ - cupsfilters/ghostscript.c \ - cupsfilters/ieee1284.c \ - cupsfilters/image.c \ - cupsfilters/image-colorspace.c \ - cupsfilters/image-jpeg.c \ - cupsfilters/image-png.c \ - cupsfilters/image-private.h \ - cupsfilters/image-tiff.c \ - cupsfilters/image-zoom.c \ - cupsfilters/imagetopdf.c \ - cupsfilters/imagetoraster.c \ - cupsfilters/ipp.c \ - cupsfilters/lut.c \ - cupsfilters/mupdftopwg.c \ - cupsfilters/pack.c \ - cupsfilters/pclmtoraster.cxx \ - cupsfilters/pdf.cxx \ - cupsfilters/pdftopdf/pdftopdf.cxx \ - cupsfilters/pdftopdf/pdftopdf-private.h \ - cupsfilters/pdftopdf/pdftopdf-processor.cxx \ - cupsfilters/pdftopdf/pdftopdf-processor-private.h \ - cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx \ - cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h \ - cupsfilters/pdftopdf/pptypes.cxx \ - cupsfilters/pdftopdf/pptypes-private.h \ - cupsfilters/pdftopdf/nup.cxx \ - cupsfilters/pdftopdf/nup-private.h \ - cupsfilters/pdftopdf/intervalset.cxx \ - cupsfilters/pdftopdf/intervalset-private.h \ - cupsfilters/pdftopdf/qpdf-tools.cxx \ - cupsfilters/pdftopdf/qpdf-tools-private.h \ - cupsfilters/pdftopdf/qpdf-xobject.cxx \ - cupsfilters/pdftopdf/qpdf-xobject-private.h \ - cupsfilters/pdftopdf/qpdf-pdftopdf.cxx \ - cupsfilters/pdftopdf/qpdf-pdftopdf-private.h \ - cupsfilters/pdftopdf/qpdf-cm.cxx \ - cupsfilters/pdftopdf/qpdf-cm-private.h \ - cupsfilters/pdftoraster.cxx \ - cupsfilters/pdfutils.c \ - cupsfilters/pwgtopdf.cxx \ - cupsfilters/pwgtoraster.c \ - cupsfilters/raster.c \ - cupsfilters/rastertopwg.c \ - cupsfilters/rgb.c \ - cupsfilters/srgb.c \ - cupsfilters/texttopdf.c \ - cupsfilters/texttotext.c \ - cupsfilters/universal.c \ - $(pkgfiltersinclude_DATA) -libcupsfilters_la_LIBADD = \ - $(FONTCONFIG_LIBS) \ - $(CUPS_LIBS) \ - $(LCMS_LIBS) \ - $(LIBQPDF_LIBS) \ - $(LIBJPEG_LIBS) \ - $(EXIF_LIBS) \ - $(LIBPNG_LIBS) \ - $(TIFF_LIBS) \ - $(POPPLER_LIBS) \ - -lm -libcupsfilters_la_CFLAGS = \ - -I$(srcdir)/cupsfilters/fontembed/ \ - $(FONTCONFIG_CFLAGS) \ - $(CUPS_CFLAGS) \ - $(LCMS_CFLAGS) \ - $(LIBQPDF_CFLAGS) \ - $(LIBJPEG_CFLAGS) \ - $(EXIF_CFLAGS) \ - $(LIBPNG_CFLAGS) \ - $(TIFF_CFLAGS) -libcupsfilters_la_LDFLAGS = \ - -no-undefined \ - -version-info 2 -if BUILD_DBUS -libcupsfilters_la_CFLAGS += $(DBUS_CFLAGS) -DHAVE_DBUS -libcupsfilters_CXXFLAGS = -std=c++0x $(libcupsfilters_CFLAGS) # -std=c++11 -libcupsfilters_la_LIBADD += $(DBUS_LIBS) -endif - -testcmyk_SOURCES = \ - cupsfilters/testcmyk.c \ - $(pkgfiltersinclude_DATA) -testcmyk_LDADD = \ - libcupsfilters.la \ - -lm - -testdither_SOURCES = \ - cupsfilters/testdither.c \ - $(pkgfiltersinclude_DATA) -testdither_LDADD = \ - libcupsfilters.la \ - -lm - -testimage_SOURCES = \ - cupsfilters/testimage.c \ - $(pkgfiltersinclude_DATA) -testimage_LDADD = \ - $(LIBJPEG_LIBS) \ - $(LIBPNG_LIBS) \ - $(TIFF_LIBS) \ - libcupsfilters.la \ - -lm -testimage_CFLAGS = \ - $(LIBJPEG_CFLAGS) \ - $(LIBPNG_CFLAGS) \ - $(TIFF_CFLAGS) - -testrgb_SOURCES = \ - cupsfilters/testrgb.c \ - $(pkgfiltersinclude_DATA) -testrgb_LDADD = \ - libcupsfilters.la \ - -lm - -test1284_SOURCES = \ - cupsfilters/test1284.c -test1284_LDADD = \ - libcupsfilters.la \ - $(CUPS_LIBS) -test1284_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - $(CUPS_CFLAGS) - -testpdf1_SOURCES = \ - cupsfilters/testpdf1.c \ - cupsfilters/fontembed-private.h -testpdf1_CFLAGS = \ - -I$(srcdir)/cupsfilters/fontembed/ \ - -I$(srcdir)/cupsfilters/ -testpdf1_LDADD = \ - libcupsfilters.la - -testpdf2_SOURCES = \ - cupsfilters/testpdf2.c \ - cupsfilters/fontembed-private.h -testpdf2_CFLAGS = \ - -I$(srcdir)/cupsfilters/fontembed/ \ - -I$(srcdir)/cupsfilters/ -testpdf2_LDADD = \ - libcupsfilters.la - -test_analyze_SOURCES = cupsfilters/fontembed/test-analyze.c -test_analyze_LDADD = libcupsfilters.la - -test_pdf_SOURCES = cupsfilters/fontembed/test-pdf.c -test_pdf_LDADD = libcupsfilters.la - -test_ps_SOURCES = cupsfilters/fontembed/test-ps.c -test_ps_LDADD = libcupsfilters.la - -EXTRA_DIST += \ - $(pkgfiltersinclude_DATA) \ - cupsfilters/image.pgm \ - cupsfilters/image.ppm \ - cupsfilters/fontembed/README - -# ========= -# CUPS Data -# ========= -pkgcupsdatadir = $(CUPS_DATADIR)/data -pkgcupsdata_DATA = \ - data/default.pdf \ - data/form_russian.pdf \ - data/form_english.pdf \ - data/form_english_in.odt \ - data/form_russian_in.odt \ - data/default-testpage.pdf \ - data/testprint \ - data/classified.pdf \ - data/confidential.pdf \ - data/secret.pdf \ - data/standard.pdf \ - data/topsecret.pdf \ - data/unclassified.pdf - -EXTRA_DIST += $(pkgcupsdata_DATA) - -# =========== -# CUPS Config -# =========== -pkgcupsserverrootdir = $(CUPS_SERVERROOT) -pkgcupsserverroot_DATA = \ - utils/cups-browsed.conf - -# ========================== -# PPD legacy support library -# ========================== -pkgppdincludedir = $(includedir)/ppd -pkgppdinclude_DATA = \ - ppd/ppd.h \ - ppd/ppdc.h \ - ppd/ppd-filter.h - -pkgppddefsdir = $(datadir)/ppdc -pkgppddefs_DATA = \ - ppd/epson.h \ - ppd/hp.h \ - ppd/label.h \ - ppd/font.defs \ - ppd/media.defs \ - ppd/raster.defs - -lib_LTLIBRARIES += libppd.la - -check_PROGRAMS += \ - testppd -TESTS += \ - testppd - -libppd_la_SOURCES = \ - ppd/ppd-attr.c \ - ppd/ppd.c \ - ppd/ppd-cache.c \ - ppd/ppd-collection.cxx \ - ppd/ppd-conflicts.c \ - ppd/ppd-custom.c \ - ppd/ppd-emit.c \ - ppd/ppd-filter.c \ - ppd/ppd-generator.c \ - ppd/ppd-load-profile.c \ - ppd/ppd-localize.c \ - ppd/ppd-mark.c \ - ppd/ppd-page.c \ - ppd/ppd-ipp.c \ - ppd/array.c \ - ppd/array-private.h \ - ppd/debug.c \ - ppd/debug-internal.h \ - ppd/debug-private.h \ - ppd/encode.c \ - ppd/file.c \ - ppd/file-private.h \ - ppd/imagetops-pstops.c \ - ppd/ipp-private.h \ - ppd/language.c \ - ppd/language-private.h \ - ppd/pdftops.c \ - ppd/raster-interpret.c \ - ppd/raster-error.c \ - ppd/raster-private.h \ - ppd/rastertops.c \ - ppd/string.c \ - ppd/snprintf.c \ - ppd/string-private.h \ - ppd/thread.c \ - ppd/thread-private.h \ - ppd/ppdc-array.cxx \ - ppd/ppdc-attr.cxx \ - ppd/ppdc-catalog.cxx \ - ppd/ppdc-choice.cxx \ - ppd/ppdc-constraint.cxx \ - ppd/ppdc-driver.cxx \ - ppd/ppdc-file.cxx \ - ppd/ppdc-filter.cxx \ - ppd/ppdc-font.cxx \ - ppd/ppdc-group.cxx \ - ppd/ppdc-import.cxx \ - ppd/ppdc-mediasize.cxx \ - ppd/ppdc-message.cxx \ - ppd/ppdc-option.cxx \ - ppd/ppdc-private.h \ - ppd/ppdc-profile.cxx \ - ppd/ppdc-shared.cxx \ - ppd/ppdc-source.cxx \ - ppd/ppdc-string.cxx \ - ppd/ppdc-variable.cxx \ - $(pkgppdinclude_DATA) \ - $(pkgppddefs_DATA) -libppd_la_LIBADD = \ - libcupsfilters.la \ - $(CUPS_LIBS) -libppd_la_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - $(CUPS_CFLAGS) -libppd_la_LDFLAGS = \ - -no-undefined \ - -version-info 1 -libppd_la_DEPENDENCIES = \ - libcupsfilters.la - -testppd_SOURCES = ppd/testppd.c -testppd_LDADD = libppd.la \ - -lcups - -EXTRA_DIST += \ - $(pkgppdinclude_DATA) \ - $(pkgppddefs_DATA) \ - ppd/test.ppd \ - ppd/test2.ppd \ - ppd/README.md - -# =========================== -# ppdc PPD compiler utilities -# =========================== - -if ENABLE_PPDC_UTILS -pkgutils_PROGRAMS += \ - genstrings \ - ppdc \ - ppdhtml \ - ppdi \ - ppdmerge \ - ppdpo -endif - -genstrings_SOURCES = \ - ppd/genstrings.cxx -genstrings_LDADD = \ - libppd.la \ - $(CUPS_LIBS) -genstrings_CFLAGS = \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) - -ppdc_SOURCES = \ - ppd/ppdc.cxx -ppdc_LDADD = \ - libppd.la \ - $(CUPS_LIBS) -ppdc_CFLAGS = \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) - -ppdhtml_SOURCES = \ - ppd/ppdhtml.cxx -ppdhtml_LDADD = \ - libppd.la \ - $(CUPS_LIBS) -ppdhtml_CFLAGS = \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) - -ppdi_SOURCES = \ - ppd/ppdi.cxx -ppdi_LDADD = \ - libppd.la \ - $(CUPS_LIBS) -ppdi_CFLAGS = \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) - -ppdmerge_SOURCES = \ - ppd/ppdmerge.cxx -ppdmerge_LDADD = \ - libppd.la \ - $(CUPS_LIBS) -ppdmerge_CFLAGS = \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) - -ppdpo_SOURCES = \ - ppd/ppdpo.cxx -ppdpo_LDADD = \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) -ppdpo_CFLAGS = \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) # ======= # Drivers @@ -622,20 +94,11 @@ gendrvfiles = \ drv/cupsfilters.drv pkgdriver_DATA = $(gendrvfiles) -brldrvfiles = \ - drv/generic-brf.drv \ - drv/generic-ubrl.drv \ - drv/indexv3.drv \ - drv/indexv4.drv -if ENABLE_BRAILLE -pkgdriver_DATA += $(brldrvfiles) -endif - -EXTRA_DIST += $(gendrvfiles) $(brldrvfiles) +EXTRA_DIST += $(gendrvfiles) -# ======= +# ======================= # Definitions for drivers -# ======= +# ======================= pkgppdcdir = $(CUPS_DATADIR)/ppdc genppdcfiles = \ @@ -643,49 +106,8 @@ genppdcfiles = \ filter/escp.h pkgppdc_DATA = $(genppdcfiles) -GENERATED_LIBLOUIS = \ - filter/braille/filters/liblouis3.defs \ - filter/braille/filters/liblouis4.defs -GENERATED_DEFS = \ - filter/braille/filters/liblouis1.defs \ - filter/braille/filters/liblouis2.defs \ - $(GENERATED_LIBLOUIS) - -filter/braille/filters/liblouis1.defs: filter/braille/filters/liblouis1.defs.gen - $< > $@ - -filter/braille/filters/liblouis2.defs: filter/braille/filters/liblouis1.defs - sed -e "s/Braille transcription/Additional Braille transcription (2)/" \ - -e "s/^ \\*Choice / Choice /" \ - -e "s/^ Choice \"HyphLocale\// *Choice \"HyphLocale\//" \ - -e s/LibLouis/LibLouis2/ \ - < $< > $@ - -$(GENERATED_LIBLOUIS): filter/braille/filters/liblouis%.defs: filter/braille/filters/liblouis1.defs - sed -e "s/Braille transcription/Additional Braille transcription ($*)/" \ - -e "s/^ \\*Choice / Choice /" \ - -e "s/^ Choice \"None\// *Choice \"None\//" \ - -e s/LibLouis/LibLouis$*/ \ - < $< > $@ - -brlppdcfiles = \ - filter/braille/drivers/common/media-braille.defs \ - filter/braille/drivers/index/index.defs \ - filter/braille/filters/braille.defs \ - filter/braille/filters/imagemagick.defs \ - filter/braille/filters/liblouis.defs \ - filter/braille/drivers/common/fr-braille.po - -if ENABLE_BRAILLE -pkgppdc_DATA += $(brlppdcfiles) -nodist_pkgppdc_DATA = \ - $(GENERATED_DEFS) -endif - EXTRA_DIST += \ - filter/braille/filters/liblouis1.defs.gen.in \ - $(genppdcfiles) \ - $(brlppdcfiles) + $(genppdcfiles) # ===== # MIMEs @@ -731,13 +153,6 @@ pkgmime_DATA += $(mutoolmimefiles) endif endif -brlmimefiles = \ - mime/braille.convs \ - mime/braille.types -if ENABLE_BRAILLE -pkgmime_DATA += $(brlmimefiles) -endif - EXTRA_DIST += \ $(genmimefiles) \ $(universalmimefiles) \ @@ -745,63 +160,25 @@ EXTRA_DIST += \ $(individualmimefiles) \ $(popplermimefiles) \ $(gsmimefiles) \ - $(mutoolmimefiles) \ - $(brlmimefiles) - -# ================= -# Braille aux files -# ================= -if ENABLE_BRAILLE -pkgbrailledir = $(CUPS_DATADIR)/braille -nodist_pkgbraille_SCRIPTS = \ - filter/braille/drivers/index/indexv4.sh \ - filter/braille/drivers/index/indexv3.sh \ - filter/braille/drivers/index/index.sh \ - filter/braille/filters/cups-braille.sh -endif - -# ========== -# PDF to PDF -# ========== -pkgfilter_PROGRAMS += pdftopdf + $(mutoolmimefiles) -pdftopdf_SOURCES = \ - filter/pdftopdf.c -pdftopdf_CFLAGS = \ - -I$(srcdir)/ppd/ \ - -I$(srcdir)/cupsfilters/ \ - $(CUPS_CFLAGS) -pdftopdf_LDADD = \ - libppd.la \ - libcupsfilters.la \ - $(CUPS_LIBS) +# ======= +# Filters +# ======= +pkgfilter_PROGRAMS = +pkgfilterdir = $(CUPS_SERVERBIN)/filter -# ====================== -# Simple filter binaries -# ====================== genfilterscripts = \ filter/texttops pkgfilter_SCRIPTS = $(genfilterscripts) -if ENABLE_BRAILLE -nodist_pkgfilter_SCRIPTS = \ - filter/braille/drivers/generic/brftoembosser \ - filter/braille/drivers/index/imageubrltoindexv3 \ - filter/braille/drivers/index/imageubrltoindexv4 \ - filter/braille/drivers/index/textbrftoindexv3 \ - filter/braille/filters/imagetobrf \ - filter/braille/filters/vectortopdf \ - filter/braille/filters/vectortobrf \ - filter/braille/filters/texttobrf \ - filter/braille/filters/brftopagedbrf \ - filter/braille/filters/musicxmltobrf -endif pkgfilter_PROGRAMS += \ - commandtoescpx \ - commandtopclx \ + pdftopdf \ texttotext \ rastertoescpx \ - rastertopclx + rastertopclx \ + commandtoescpx \ + commandtopclx if ENABLE_GHOSTSCRIPT pkgfilter_PROGRAMS += \ gstopxl @@ -851,7 +228,7 @@ pkgfilter_PROGRAMS += \ imagetoraster endif -check_PROGRAMS += \ +check_PROGRAMS = \ test-external # Not reliable bash script @@ -865,37 +242,35 @@ bannertopdf_SOURCES = \ filter/bannertopdf.c bannertopdf_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) bannertopdf_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) commandtoescpx_SOURCES = \ - cupsfilters/driver.h \ filter/commandtoescpx.c \ filter/pcl.h commandtoescpx_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) commandtoescpx_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) commandtopclx_SOURCES = \ - cupsfilters/driver.h \ filter/commandtopclx.c \ filter/pcl.h commandtopclx_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) commandtopclx_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) foomatic_rip_SOURCES = \ @@ -914,232 +289,240 @@ foomatic_rip_SOURCES = \ filter/foomatic-rip/spooler.c \ filter/foomatic-rip/spooler.h \ filter/foomatic-rip/util.c \ - filter/foomatic-rip/util.h \ - cupsfilters/colord.h + filter/foomatic-rip/util.h foomatic_rip_CFLAGS = \ -DCONFIG_PATH='"$(sysconfdir)/foomatic"' \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) foomatic_rip_LDADD = \ $(CUPS_LIBS) \ -lm \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) gstoraster_SOURCES = \ filter/gstoraster.c gstoraster_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) gstoraster_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) gstopdf_SOURCES = \ filter/gstopdf.c gstopdf_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) gstopdf_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) gstopxl_SOURCES = \ filter/gstopxl.c gstopxl_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) gstopxl_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) imagetopdf_SOURCES = \ filter/imagetopdf.c imagetopdf_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) imagetopdf_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) imagetops_SOURCES = \ filter/imagetops.c imagetops_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) imagetops_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) imagetoraster_SOURCES = \ filter/imagetoraster.c imagetoraster_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) imagetoraster_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) pclmtoraster_SOURCES = \ filter/pclmtoraster.c pclmtoraster_CXXFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) pclmtoraster_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ + $(CUPS_LIBS) + +pdftopdf_SOURCES = \ + filter/pdftopdf.c +pdftopdf_CFLAGS = \ + $(LIBPPD_CFLAGS) \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(CUPS_CFLAGS) +pdftopdf_LDADD = \ + $(LIBPPD_LIBS) \ + $(LIBCUPSFILTERS_LIBS) \ $(CUPS_LIBS) pwgtopclm_SOURCES = \ filter/pwgtopclm.c pwgtopclm_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) pwgtopclm_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) pwgtopdf_SOURCES = \ filter/pwgtopdf.c pwgtopdf_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) pwgtopdf_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) mupdftopwg_SOURCES = \ filter/mupdftopwg.c mupdftopwg_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) mupdftopwg_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) pwgtoraster_SOURCES = \ filter/pwgtoraster.c pwgtoraster_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) pwgtoraster_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) rastertops_SOURCES = \ filter/rastertops.c rastertops_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) rastertops_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) rastertopwg_SOURCES = \ filter/rastertopwg.c rastertopwg_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) rastertopwg_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) texttotext_SOURCES = \ filter/texttotext.c texttotext_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) texttotext_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) texttopdf_SOURCES = \ filter/texttopdf.c texttopdf_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) texttopdf_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) pdftops_SOURCES = \ filter/pdftops.c pdftops_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) pdftops_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) pstops_SOURCES = \ filter/pstops.c pstops_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) pstops_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) pdftoraster_SOURCES = \ filter/pdftoraster.c pdftoraster_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) pdftoraster_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) rastertoescpx_SOURCES = \ - cupsfilters/driver.h \ filter/escp.h \ filter/rastertoescpx.c rastertoescpx_CFLAGS = \ $(CUPS_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) rastertoescpx_LDADD = \ $(CUPS_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) rastertopclx_SOURCES = \ - cupsfilters/driver.h \ filter/pcl.h \ filter/pcl-common.c \ filter/pcl-common.h \ @@ -1147,99 +530,55 @@ rastertopclx_SOURCES = \ rastertopclx_CFLAGS = \ $(CUPS_CFLAGS) \ $(LIBPNG_CFLAGS) \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) rastertopclx_LDADD = \ $(CUPS_LIBS) \ $(LIBPNG_LIBS) \ - libcupsfilters.la \ - libppd.la + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) universal_SOURCES = \ filter/universal.c universal_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ + $(LIBCUPSFILTERS_CFLAGS) \ $(CUPS_CFLAGS) universal_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) test_external_SOURCES = \ filter/test-external.c test_external_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ + $(LIBCUPSFILTERS_CFLAGS) \ + $(LIBPPD_CFLAGS) \ $(CUPS_CFLAGS) test_external_LDADD = \ - libcupsfilters.la \ - libppd.la \ + $(LIBCUPSFILTERS_LIBS) \ + $(LIBPPD_LIBS) \ $(CUPS_LIBS) -# ===== -# UTILS -# ===== +# ========= +# Man pages +# ========= +man_MANS = -cups_notifier_sources = \ - cups-notifier.c \ - cups-notifier.h - -$(cups_notifier_sources): utils/org.cups.cupsd.Notifier.xml - gdbus-codegen \ - --interface-prefix org.cups.cupsd \ - --c-namespace Cups \ - --generate-c-code cups-notifier \ - utils/org.cups.cupsd.Notifier.xml - -sbin_PROGRAMS = \ - cups-browsed -cups_browsed_SOURCES = \ - utils/cups-browsed.c -nodist_cups_browsed_SOURCES = \ - $(cups_notifier_sources) -cups_browsed_CFLAGS = \ - -I$(srcdir)/cupsfilters/ \ - -I$(srcdir)/ppd/ \ - $(CUPS_CFLAGS) \ - $(AVAHI_CFLAGS) \ - $(AVAHI_GLIB_CFLAGS) \ - $(GLIB_CFLAGS) \ - $(GIO_CFLAGS) \ - $(GIO_UNIX_CFLAGS) -cups_browsed_CXXFLAGS = $(cups_browsed_CFLAGS) -cups_browsed_LDADD = \ - libcupsfilters.la \ - libppd.la \ - $(CUPS_LIBS) \ - $(AVAHI_LIBS) \ - $(AVAHI_GLIB_LIBS) \ - $(GLIB_LIBS) \ - $(GIO_LIBS) \ - $(GIO_UNIX_LIBS) -initrcdir = $(INITDDIR) -initrc_SCRIPTS = utils/cups-browsed - -cupsbrowsedmanpages = \ - utils/cups-browsed.8 \ - utils/cups-browsed.conf.5 -man_MANS = $(cupsbrowsedmanpages) driverlessmanpages = \ utils/driverless.1 if ENABLE_DRIVERLESS man_MANS += $(driverlessmanpages) endif + foomaticmanpages = \ filter/foomatic-rip/foomatic-rip.1 if ENABLE_FOOMATIC man_MANS += $(foomaticmanpages) endif -EXTRA_DIST += utils/cups-browsed.in \ - $(cupsbrowsedmanpages) \ + +EXTRA_DIST += \ $(driverlessmanpages) \ - filter/foomatic-rip/foomatic-rip.1.in \ - utils/org.cups.cupsd.Notifier.xml -BUILT_SOURCES = $(cups_notifier_sources) -CLEANFILES = $(BUILT_SOURCES) $(GENERATED_DEFS) + filter/foomatic-rip/foomatic-rip.1.in # === # PPD @@ -1278,44 +617,8 @@ if ENABLE_DRIVERLESS $(LN_SRF) $(DESTDIR)$(pkgppdgendir)/driverless-fax $(DESTDIR)$(bindir) $(LN_SRF) $(DESTDIR)$(pkgppdgendir)/driverless-fax $(DESTDIR)$(pkgbackenddir) endif -if ENABLE_BRAILLE - $(LN_S) -f imagetobrf $(DESTDIR)$(pkgfilterdir)/imagetoubrl - $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/svgtopdf - $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/xfigtopdf - $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/wmftopdf - $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/emftopdf - $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/cgmtopdf - $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/cmxtopdf - $(LN_S) -f vectortobrf $(DESTDIR)$(pkgfilterdir)/vectortoubrl - $(LN_S) -f textbrftoindexv3 $(DESTDIR)$(pkgfilterdir)/textbrftoindexv4 -endif - -install-data-hook: -if RCLINKS - for level in $(RCLEVELS); do \ - $(INSTALL) -d -m 755 $(DESTDIR)$(INITDIR)/rc$${level}.d; \ - $(LN_S) -f ../init.d/cups-browsed $(DESTDIR)$(INITDIR)/rc$${level}.d/S$(RCSTART)cups-browsed; \ - $(LN_S) -f ../init.d/cups-browsed $(DESTDIR)$(INITDIR)/rc$${level}.d/K$(RCSTOP)cups-browsed; \ - done; \ - $(INSTALL) -d -m 755 $(DESTDIR)$(INITDIR)/rc0.d; \ - $(LN_S) -f ../init.d/cups-browsed $(DESTDIR)$(INITDIR)/rc0.d/K$(RCSTOP)cups-browsed; -endif - $(LN_S) -f pdf.utf-8.simple \ - $(DESTDIR)$(pkgcharsetdir)/pdf.utf-8 - chmod 700 $(DESTDIR)/$(pkgbackenddir)/implicitclass -if ENABLE_BRAILLE - chmod 700 $(DESTDIR)/$(pkgbackenddir)/cups-brf -endif - uninstall-hook: -if RCLINKS - if test "x$(INITDIR)" != x; then \ - $(RM) $(DESTDIR)$(BUILDROOT)$(INITDIR)/rc?.d/[SK]??cups-browsed || :; \ - rmdir $(DESTDIR)$(BUILDROOT)$(INITDIR)/rc?.d || :;\ - fi -endif - $(RM) $(DESTDIR)$(pkgcharsetdir)/pdf.utf-8 if ENABLE_FOOMATIC $(RM) $(DESTDIR)$(bindir)/foomatic-rip endif @@ -1325,16 +628,5 @@ if ENABLE_DRIVERLESS $(RM) $(DESTDIR)$(bindir)/driverless-fax $(RM) $(DESTDIR)$(pkgbackenddir)/driverless-fax endif -if ENABLE_BRAILLE - $(RM) $(DESTDIR)$(pkgfilterdir)/imagetoubrl - $(RM) $(DESTDIR)$(pkgfilterdir)/svgtopdf - $(RM) $(DESTDIR)$(pkgfilterdir)/xfigtopdf - $(RM) $(DESTDIR)$(pkgfilterdir)/wmftopdf - $(RM) $(DESTDIR)$(pkgfilterdir)/emftopdf - $(RM) $(DESTDIR)$(pkgfilterdir)/cgmtopdf - $(RM) $(DESTDIR)$(pkgfilterdir)/cmxtopdf - $(RM) $(DESTDIR)$(pkgfilterdir)/vectortoubrl - $(RM) $(DESTDIR)$(pkgfilterdir)/textbrftoindexv4 -endif SUBDIRS = diff --git a/backend/cups-brf.c b/backend/cups-brf.c deleted file mode 100644 index eb7f7d17c..000000000 --- a/backend/cups-brf.c +++ /dev/null @@ -1,171 +0,0 @@ -// -// BRF (Braille-Ready Format) virtual backend -// -// Copyright (c) 2017 by Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -int -main(int argc, - char *argv[]) -{ - char *user; - char *dir; - char *title; - char *outfile; - char *c; - char buffer[4096]; - ssize_t sizein, sizeout, done; - struct passwd *pw; - int ret; - int fd; - - if (setuid(0)) - { - // We need to be root to be able to turn into another user. - fprintf(stderr,"ERROR: cups-brf must be called as root\n"); - return (CUPS_BACKEND_FAILED); - } - - if (argc == 1) - { - // This is just discovery. - printf("file cups-brf:/ \"Virtual Braille BRF Printer\" \"CUPS-BRF\" \"MFG:Generic;MDL:CUPS-BRF Printer;DES:Generic CUPS-BRF Printer;CLS:PRINTER;CMD:BRF;\"\n"); - return (CUPS_BACKEND_OK); - } - - if (argc < 6) - { - // Invalid number of parameters. - fprintf(stderr, "ERROR: cups-brf jobid user name nb options [filename]\n"); - return (CUPS_BACKEND_FAILED); - } - - if (argc == 7) - { - // Explicit file name, open it. - char *filename = argv[6]; - fd = open(filename, O_RDONLY); - if (dup2(fd, STDIN_FILENO) < 0) { - fprintf(stderr, "ERROR: opening file \"%s\"\n", filename); - return (CUPS_BACKEND_FAILED); - } - } - - // Now we have everything, turn into the user - user = argv[2]; - pw = getpwnam(user); - if (pw == NULL) - { - fprintf(stderr, "ERROR: getting user \"%s\" information\n", user); - return (CUPS_BACKEND_FAILED); - } - if (setgid(pw->pw_gid)) - { - fprintf(stderr, "ERROR: turning gid into %u\n", pw->pw_gid); - return (CUPS_BACKEND_FAILED); - } - if (setuid(pw->pw_uid)) - { - fprintf(stderr, "ERROR: turning uid into %u\n", pw->pw_uid); - return (CUPS_BACKEND_FAILED); - } - - // Now we act as user - umask(0077); - - // Create BRF directory in $HOME - if (asprintf(&dir, "%s/BRF", pw->pw_dir) < 0) - { - fprintf(stderr, "ERROR: could not allocate memory\n"); - return (CUPS_BACKEND_FAILED); - } - fprintf(stderr, "DEBUG: creating directory \"%s\n", dir); - ret = mkdir(dir, 0700); - if (ret == -1 && errno != EEXIST) - { - fprintf(stderr, "ERROR: could not create directory \"%s\": %s\n", - dir, strerror(errno)); - return (CUPS_BACKEND_FAILED); - } - - // Avoid escaping from the directory - title = argv[3]; - for (c = title; *c; c++) - { - if (*c == '/') - *c = '_'; - } - // Avoid hiding the file - while (*title == '.') - title++; - - // Avoid empty title - if (!*title) - title = "unknown"; - - // generate mask - if (asprintf(&outfile, "%s/%s.XXXXXX.brf", dir, title) < 0) - { - fprintf(stderr, "ERROR: could not allocate memory\n"); - return (CUPS_BACKEND_FAILED); - } - - // Create file - fprintf(stderr, "DEBUG: creating file \"%s\n", outfile); - fd = mkstemps(outfile, 4); - if (fd < 0) - { - fprintf(stderr, "ERROR: could not create file \"%s\": %s\n", - outfile, strerror(errno)); - return (CUPS_BACKEND_FAILED); - } - - // We are all set, copy data. - while (1) - { - // Read some. - sizein = read(STDIN_FILENO, buffer, sizeof(buffer)); - if (sizein < 0) - { - fprintf(stderr, "ERROR: while reading input: %s\n", strerror(errno)); - return (CUPS_BACKEND_FAILED); - } - if (sizein == 0) - // We are done! - break; - - // Write it. - for (done = 0; done < sizein; done += sizeout) - { - sizeout = write(fd, buffer + done, sizein - done); - if (sizeout < 0) - { - fprintf(stderr, "ERROR: while writing to \"%s\": %s\n", - outfile, strerror(errno)); - return (CUPS_BACKEND_FAILED); - } - } - } - if (close(fd) < 0) - { - fprintf(stderr, "ERROR: while closing \"%s\": %s\n", - outfile, strerror(errno)); - return (CUPS_BACKEND_FAILED); - } - - return (CUPS_BACKEND_OK); -} diff --git a/backend/implicitclass.c b/backend/implicitclass.c deleted file mode 100644 index 757be56d8..000000000 --- a/backend/implicitclass.c +++ /dev/null @@ -1,436 +0,0 @@ -// -// implicitclass backend for implementing an implicit-class-like behavior -// of redundant print servers managed by cups-browsed. -// -// Copyright 2015-2019 by Till Kamppeter -// Copyright 2018-2019 by Deepak Patankar -// -// This is based on dnssd.c of CUPS -// dnssd.c copyright notice is follows: -// -// Copyright 2008-2015 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers. -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// -// Local globals... -// - -// IPP Attribute which cups-browsed uses to tell us the destination queue for -// the current job -#define CUPS_BROWSED_DEST_PRINTER "cups-browsed-dest-printer" - -static int job_canceled = 0; // Set to 1 on SIGTERM - - -// -// Local functions... -// - -static void sigterm_handler(int sig); - - -// -// 'main()' - Browse for printers. -// - -int // O - Exit status -main(int argc, // I - Number of command-line args - char *argv[]) // I - Command-line arguments -{ - const char *device_uri; // URI with which we were called - char scheme[64], username[32], queue_name[1024], resource[32], - printer_uri[1024], document_format[256], resolution[16]; - int port, status; - const char *ptr1 = NULL; - char *ptr2, *ptr3, *ptr4; - const char *job_id; - int i; - char dest_host[1024]; // Destination host - ipp_t *request, *response; - ipp_attribute_t *attr; - char uri[HTTP_MAX_URI]; - static const char *pattrs[] = - { - "printer-defaults" - }; -#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) - struct sigaction action; // Actions for POSIX signals -#endif // HAVE_SIGACTION && !HAVE_SIGSET - - // - // Don't buffer stderr, and catch SIGTERM... - // - - setbuf(stderr, NULL); - -#ifdef HAVE_SIGSET // Use System V signals over POSIX to avoid bugs - sigset(SIGTERM, sigterm_handler); -#elif defined(HAVE_SIGACTION) - memset(&action, 0, sizeof(action)); - - sigemptyset(&action.sa_mask); - action.sa_handler = sigterm_handler; - sigaction(SIGTERM, &action, NULL); -#else - signal(SIGTERM, sigterm_handler); -#endif // HAVE_SIGSET - - // - // Check command-line... - // - - if (argc >= 6) - { - if ((device_uri = getenv("DEVICE_URI")) == NULL) - { - if (!argv || !argv[0] || !strchr(argv[0], ':')) - return (-1); - - device_uri = argv[0]; - } - status = httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, - scheme, sizeof(scheme), - username, sizeof(username), - queue_name, sizeof(queue_name), - &port, - resource, sizeof(resource)); - if (status != HTTP_URI_STATUS_OK && - status != HTTP_URI_STATUS_UNKNOWN_SCHEME) - { - fprintf(stderr, "ERROR: Incorrect device URI syntax: %s\n", - device_uri); - return (CUPS_BACKEND_STOP); - } - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", ippPort(), "/printers/%s", queue_name); - job_id = argv[1]; - for (i = 0; i < 120; i++) - { - // Wait up to 60 sec for cups-browsed to supply the destination host - // Try reading the option in which cups-browsed has deposited the - // destination host - request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, - uri); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", - sizeof(pattrs) / sizeof(pattrs[0]), - NULL, pattrs); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", - NULL, cupsUser()); - if ((response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) == - NULL) - goto failed; - for (attr = ippFirstAttribute(response); attr != NULL; - attr = ippNextAttribute(response)) - { - while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER) - attr = ippNextAttribute(response); - if (attr == NULL) - break; - ptr1 = NULL; - while (attr != NULL && ippGetGroupTag(attr) == - IPP_TAG_PRINTER) - { - if (!strcmp(ippGetName(attr), - CUPS_BROWSED_DEST_PRINTER "-default")) - ptr1 = ippGetString(attr, 0, NULL); - if (ptr1 != NULL) - break; - attr = ippNextAttribute(response); - } - if (ptr1 != NULL) - break; - } - fprintf(stderr, "DEBUG: Read " CUPS_BROWSED_DEST_PRINTER " option: %s\n", - (ptr1 ? ptr1 : "Option not found")); - if (ptr1 == NULL) - goto failed; - // Destination host is between double quotes, as double quotes are - // illegal in host names one easily recognizes whether the option is - // complete and avoids accepting a partially written host name - if (*ptr1 != '"') - goto failed; - ptr1 ++; - // Check whether option was set for this job, if not, keep waiting - if (strncmp(ptr1, job_id, strlen(job_id)) != 0) - goto failed; - ptr1 += strlen(job_id); - if (*ptr1 != ' ') - goto failed; - ptr1 ++; - // Read destination host name (or message) and check whether it is - // complete (second double quote) - if ((ptr2 = strchr(ptr1, '"')) != NULL) - { - *ptr2 = '\0'; - break; - } - failed: - // Pause half a second before next attempt - usleep(500000); - } - - if (i >= 120) - { - // Timeout, no useful data from cups-browsed received - fprintf(stderr, "ERROR: No destination host name supplied by cups-browsed for printer \"%s\", is cups-browsed running?\n", - queue_name); - return (CUPS_BACKEND_STOP); - } - strncpy(dest_host, ptr1, sizeof(dest_host) - 1); - if (!strcmp(dest_host, "NO_DEST_FOUND")) - { - // All remote queues are either disabled or not accepting jobs, let - // CUPS retry after the usual interval - fprintf(stderr, - "ERROR: No suitable destination host found by cups-browsed.\n"); - return (CUPS_BACKEND_RETRY); - } - else if (!strcmp(dest_host, "ALL_DESTS_BUSY")) - { - // We queue on the client and all remote queues are busy, so we wait - // 5 sec and check again then - fprintf(stderr, - "DEBUG: No free destination host found by cups-browsed, retrying in 5 sec.\n"); - sleep(5); - return (CUPS_BACKEND_RETRY_CURRENT); - } - else - { - // We have the destination host name now, do the job - char *title; - int num_options = 0; - cups_option_t *options = NULL; - int fd, nullfd; - cf_filter_data_t filter_data; - cf_filter_universal_parameter_t universal_parameters; - cf_filter_external_t ipp_backend_params; - cf_filter_filter_in_chain_t universal_in_chain, - ipp_in_chain; - cups_array_t *filter_chain; - int retval; - - fprintf(stderr, - "DEBUG: Received destination host name from cups-browsed: printer-uri %s\n", - ptr1); - - // Parse the command line options and prepare them for the new print - // job - cupsSetUser(argv[2]); - title = argv[3]; - if (title == NULL) - { - if (argc == 7) - { - if ((title = strrchr(argv[6], '/')) != NULL) - title ++; - else - title = argv[6]; - } - else - title = "(stdin)"; - } - num_options = cupsAddOption("copies", argv[4], num_options, &options); - num_options = cupsParseOptions(argv[5], num_options, &options); - if (argc == 7) - fd = open(argv[6], O_RDONLY); - else - fd = 0; // stdin - - // Finding the document format in which the pdftoippprinter will - // convert the pdf file - if ((ptr3 = strchr(ptr1, ' ')) != NULL) - { - *ptr3 = '\0'; - ptr3++; - } - - // Finding the resolution requested for the job - if ((ptr4 = strchr(ptr3, ' ')) != NULL) - { - *ptr4 = '\0'; - ptr4++; - } - - strncpy(printer_uri, ptr1, sizeof(printer_uri) - 1); - strncpy(document_format, ptr3, sizeof(document_format) - 1); - strncpy(resolution, ptr4, sizeof(resolution) - 1); - - fprintf(stderr, - "DEBUG: Received job for the printer with the destination uri - %s, Final-document format for the printer - %s and requested resolution - %s\n", - printer_uri, document_format, resolution); - - // Adjust option list for the cfFilterUniversal() filter function call - num_options = cupsAddOption("Resolution", resolution, - num_options, &options); - num_options = cupsRemoveOption("cups-browsed-dest-printer", - num_options, &options); - num_options = cupsRemoveOption("cups-browsed", - num_options, &options); - - // Set up filter data record to be used by the filter functions to - // process the job - filter_data.printer = printer_uri; - filter_data.job_id = atoi(argv[1]); - filter_data.job_user = argv[2]; - filter_data.job_title = title; - filter_data.copies = atoi(argv[4]); - filter_data.content_type = "application/vnd.cups-pdf"; - filter_data.final_content_type = document_format; - filter_data.job_attrs = NULL; // We use command line options - filter_data.printer_attrs = - cfGetPrinterAttributes4(printer_uri, NULL, 0, NULL, 0, 1, 0); - // Poll the printer attributes from - // the printer - filter_data.num_options = num_options; - filter_data.options = options; // Command line options from 5th - // arg - filter_data.extension = NULL; - filter_data.back_pipe[0] = -1; - filter_data.back_pipe[1] = -1; - filter_data.side_pipe[0] = -1; - filter_data.side_pipe[1] = -1; - filter_data.logfunc = cfCUPSLogFunc; // Logging scheme of CUPS - filter_data.logdata = NULL; - filter_data.iscanceledfunc = cfCUPSIsCanceledFunc; // Job-is-canceled - // function - filter_data.iscanceleddata = &job_canceled; - - // If the polling of the printer's IPP attributes has failed, it - // means most probably that it is not a driverless IPP printer - // (IPP 2.x) but a legacy IPP printer (IPP 1.x) which usually - // has unsufficient capability info. Therefore we fall back to - // the PPD file here which contains some info from the printer's - // DNS-SD record. - // - // If we have successfully polled the IPP attributes from the - // printer, these attributes are the most precise printer - // capability info and as the queue's PPD is only for the - // cluster we prefer the IPP attributes. - if (filter_data.printer_attrs == NULL && - ppdFilterLoadPPDFile(&filter_data, getenv("PPD")) < 0) - { - ippDelete(response); - fprintf(stderr, - "ERROR: Unable to get sufficient capability info of the destination printer.\n"); - return (CUPS_BACKEND_FAILED); - } - - cfFilterOpenBackAndSidePipes(&filter_data); - - // Parameters for cfFilterUniversal() call - universal_parameters.actual_output_type = NULL; - memset(&(universal_parameters.texttopdf_params), 0, - sizeof(cf_filter_texttopdf_parameter_t)); - - // Parameters for cfFilterExternalCUPS() call for IPP backend - ipp_backend_params.filter = "ipp"; - ipp_backend_params.exec_mode = 1; - ipp_backend_params.num_options = 0; - ipp_backend_params.options = NULL; - ipp_backend_params.envp = NULL; - cfFilterAddEnvVar("DEVICE_URI", printer_uri, &ipp_backend_params.envp); - - // Filter chain entry for the ppdFilterUniversal() filter function call - universal_in_chain.function = ppdFilterUniversal; - universal_in_chain.parameters = &universal_parameters; - universal_in_chain.name = "Filters"; - - // Filter chain entry for the IPP CUPS backend call - ipp_in_chain.function = ppdFilterExternalCUPS; - ipp_in_chain.parameters = &ipp_backend_params; - ipp_in_chain.name = "Backend"; - - filter_chain = cupsArrayNew(NULL, NULL); - cupsArrayAdd(filter_chain, &universal_in_chain); - cupsArrayAdd(filter_chain, &ipp_in_chain); - - // DEVICE_URI environment variable - setenv("DEVICE_URI", printer_uri, 1); - - // FINAL_CONTENT_TYPE environment variable - setenv("FINAL_CONTENT_TYPE", document_format, 1); - - // We call the IPP CUPS backend at the end of the chain, so we have - // no output - nullfd = open("/dev/null", O_WRONLY); - - // Call the filter chain to run the needed filters and the backend - retval = cfFilterChain(fd, nullfd, fd != 0 ? 1 : 0, &filter_data, - filter_chain); - - cfFilterCloseBackAndSidePipes(&filter_data); - - // Clean up - if (ipp_backend_params.envp) - { - for (i = 0; ipp_backend_params.envp[i]; i ++) - free(ipp_backend_params.envp[i]); - free(ipp_backend_params.envp); - } - cupsArrayDelete(filter_chain); - ippDelete(response); - - ppdFilterFreePPDFile(&filter_data); - - if (retval) - { - fprintf(stderr, "ERROR: Job processing failed.\n"); - return (CUPS_BACKEND_FAILED); - } - } - } - else if (argc != 1) - { - fprintf(stderr, - "Usage: %s job-id user title copies options [file]\n", - argv[0]); - return (CUPS_BACKEND_FAILED); - } - - // - // No discovery mode at all for this backend - // - - return (CUPS_BACKEND_OK); -} - - -// -// 'sigterm_handler()' - Handle termination signals. -// - -static void -sigterm_handler(int sig) // I - Signal number (unused) -{ - (void)sig; - - if (job_canceled) - _exit(CUPS_BACKEND_OK); - else - job_canceled = 1; -} diff --git a/banners/classified b/banners/classified deleted file mode 100644 index 2de5c1c38..000000000 --- a/banners/classified +++ /dev/null @@ -1,4 +0,0 @@ -#PDF-BANNER -Template classified.pdf -Show printer-name printer-info printer-location printer-make-and-model printer-driver-name printer-driver-version paper-size imageable-area job-id options time-at-creation time-at-processing - diff --git a/banners/confidential b/banners/confidential deleted file mode 100644 index 5d404ab0e..000000000 --- a/banners/confidential +++ /dev/null @@ -1,4 +0,0 @@ -#PDF-BANNER -Template confidential.pdf -Show printer-name printer-info printer-location printer-make-and-model printer-driver-name printer-driver-version paper-size imageable-area job-id options time-at-creation time-at-processing - diff --git a/banners/form b/banners/form deleted file mode 100644 index b1e862cd6..000000000 --- a/banners/form +++ /dev/null @@ -1,4 +0,0 @@ -#PDF-BANNER -Template form_english.pdf -Font UseDefault -Font-size 11 diff --git a/banners/secret b/banners/secret deleted file mode 100644 index 579d68a14..000000000 --- a/banners/secret +++ /dev/null @@ -1,4 +0,0 @@ -#PDF-BANNER -Template secret.pdf -Show printer-name printer-info printer-location printer-make-and-model printer-driver-name printer-driver-version paper-size imageable-area job-id options time-at-creation time-at-processing - diff --git a/banners/standard b/banners/standard deleted file mode 100644 index 788dc8d5d..000000000 --- a/banners/standard +++ /dev/null @@ -1,4 +0,0 @@ -#PDF-BANNER -Template standard.pdf -Show printer-name printer-info printer-location printer-make-and-model printer-driver-name printer-driver-version paper-size imageable-area job-id options time-at-creation time-at-processing - diff --git a/banners/topsecret b/banners/topsecret deleted file mode 100644 index 76aefac23..000000000 --- a/banners/topsecret +++ /dev/null @@ -1,4 +0,0 @@ -#PDF-BANNER -Template topsecret.pdf -Show printer-name printer-info printer-location printer-make-and-model printer-driver-name printer-driver-version paper-size imageable-area job-id options time-at-creation time-at-processing - diff --git a/banners/unclassified b/banners/unclassified deleted file mode 100644 index 696c100fb..000000000 --- a/banners/unclassified +++ /dev/null @@ -1,4 +0,0 @@ -#PDF-BANNER -Template unclassified.pdf -Show printer-name printer-info printer-location printer-make-and-model printer-driver-name printer-driver-version paper-size imageable-area job-id options time-at-creation time-at-processing - diff --git a/charset/pdf.utf-8.heavy b/charset/pdf.utf-8.heavy deleted file mode 100644 index a610af01f..000000000 --- a/charset/pdf.utf-8.heavy +++ /dev/null @@ -1,40 +0,0 @@ -charset utf8 - -# -# This file defines the font mappings used for Unicode/UTF-8 text printing -# through PDF. -# -# 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; contrary to PostScript printing -# they only select the font. To find the glyph the complete unicode -# character will be looked up in the (3,1) resp. (3,0) cmap of the -# TrueType font. 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 CourierNew CourierNew:bold CourierNew:italic CourierNew:bold:italic -0100 02FF ltor single DejaVuSansMono DejaVuSansMono:bold DejaVuSansMono:oblique DejaVuSansMono:bold:oblique -0300 03FF ltor single DejaVuSansMono -0400 04FF ltor single DejaVuSansMono DejaVuSansMono:bold DejaVuSansMono:oblique DejaVuSansMono:bold:oblique -0500 05FF rtol single FreeMono -1E00 1EFF ltor single CourierNew CourierNew:bold CourierNew:italic CourierNew:bold:italic -2000 21FF ltor single DejaVuSansMono DejaVuSansMono:bold DejaVuSansMono:oblique DejaVuSansMono:bold:oblique -2200 23FF ltor single Symbol -3000 9FFF ltor double ARPLUMingCN -#0400 04FF ltor single FreeMono FreeMono:bold FreeMono:oblique FreeMono:bold:oblique diff --git a/charset/pdf.utf-8.simple b/charset/pdf.utf-8.simple deleted file mode 100644 index f7d3adf71..000000000 --- a/charset/pdf.utf-8.simple +++ /dev/null @@ -1,33 +0,0 @@ -charset utf8 - -# -# This file defines the font mappings used for Unicode/UTF-8 text printing -# through PDF. -# -# 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; contrary to PostScript printing -# they only select the font. To find the glyph the complete unicode -# character will be looked up in the (3,1) resp. (3,0) cmap of the -# TrueType font. 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 04FF ltor single monospace monospace:bold monospace:oblique monospace:bold:oblique -0500 05FF rtol single monospace -3000 9FFF ltor double ARPLUmingCN diff --git a/configure.ac b/configure.ac index c4775bea6..29c80ba58 100644 --- a/configure.ac +++ b/configure.ac @@ -115,110 +115,15 @@ CUPS_SERVERBIN="`$CUPSCONFIG --serverbin`" AC_DEFINE_UNQUOTED(CUPS_SERVERBIN, "$CUPS_SERVERBIN", [Path to CUPS binaries dir]) AC_SUBST(CUPS_SERVERBIN) -AX_COMPARE_VERSION([$CUPS_VERSION],[gt],[1.4], [ - AC_DEFINE(CUPS_1_4, 1, [CUPS Version is 1.4 or newer]) -]) - -AC_ARG_ENABLE([driverless], [AS_HELP_STRING([--enable-driverless], [enable PPD generator for driverless printing in /usr/lib/cups/driver/, for manual setup of driverless printers with printer setup tool.])], - [enable_driverless="$enableval"], - [enable_driverless=yes] -) -AX_COMPARE_VERSION([$CUPS_VERSION],[lt],[1.6], [ - enable_driverless=no -]) -AM_CONDITIONAL([ENABLE_DRIVERLESS], -[test "x$enable_driverless" != "xno"]) - -AC_DEFINE(PDFTOPDF, [], [Needed for pdftopdf filter compilation]) -AC_DEFINE_DIR(BANNERTOPDF_DATADIR, "{CUPS_DATADIR}/data", [Directory where bannertopdf finds its data files (PDF templates)]) - -AC_ARG_ENABLE([auto-setup-local-only], [AS_HELP_STRING([--enable-auto-setup-local-only], [enable automatic setup of only local IPP printers.])], - [enable_auto_setup_local_only="$enableval"], - [enable_auto_setup_local_only=no] -) -if test "x$enable_auto_setup_local_only" != "xno"; then - AC_DEFINE([ONLY_LOCAL_IPP_PRINTERS_AUTO_SETUP], [], [Auto-setup only local IPP network printers?]) -fi - -AC_ARG_ENABLE([auto-setup-driverless-only], [AS_HELP_STRING([--enable-auto-setup-driverless-only], [enable automatic setup of only IPP network printers with driverless printing support.])], - [enable_auto_setup_driverless_only="$enableval"], - [enable_auto_setup_driverless_only=no] -) -if test "x$enable_auto_setup_driverless_only" != "xno"; then - AC_DEFINE([ONLY_DRIVERLESS_IPP_PRINTERS_AUTO_SETUP], [], [Auto-setup only driverless IPP network printers?]) -fi - -if test "x$enable_auto_setup_local_only" != "xno"; then - enable_auto_setup_driverless_only=no - enable_auto_setup_all=no -else - if test "x$enable_auto_setup_driverless_only" != "xno"; then - enable_auto_setup_all=no - else - enable_auto_setup_all=yes - fi -fi - -AC_ARG_ENABLE([ppdc-utils], [AS_HELP_STRING([--enable-ppdc-utils], [enable ppdc utilities, to build PPD files from driver information files (*.drv).])], - [enable_ppdc_utils="$enableval"], - [enable_ppdc_utils="no"]) -AM_CONDITIONAL([ENABLE_PPDC_UTILS], -[test "x$enable_ppdc_utils" != "xno"]) - -PPDC_DATADIR="$datadir/ppdc" -AC_DEFINE_UNQUOTED(PPDC_DATADIR, "$PPDC_DATADIR", [ppdc include dir]) -AC_SUBST(PPDC_DATADIR) - -AC_SEARCH_LIBS([dlopen], - [dl], - [AS_IF([test "$ac_cv_search_dlopen" != "none required"], [ - DLOPEN_LIBS="$ac_cv_search_dlopen" - ])], - AC_MSG_ERROR([unable to find the dlopen() function]) -) -AC_SUBST(DLOPEN_LIBS) - -# Transient run-time state dir of CUPS -CUPS_STATEDIR="" -AC_ARG_WITH(cups-rundir, [ --with-cups-rundir set transient run-time state directory of CUPS],CUPS_STATEDIR="$withval",[ - case "$uname" in - Darwin*) - # Darwin (OS X) - CUPS_STATEDIR="$CUPS_SERVERROOT" - ;; - *) - # All others - CUPS_STATEDIR="$localstatedir/run/cups" - ;; - esac]) -AC_DEFINE_UNQUOTED(CUPS_STATEDIR, "$CUPS_STATEDIR", [Transient run-time state dir of CUPS]) -AC_SUBST(CUPS_STATEDIR) - -# Domain socket of CUPS... -CUPS_DEFAULT_DOMAINSOCKET="" -AC_ARG_WITH(cups-domainsocket, [ --with-cups-domainsocket set unix domain socket name used by CUPS -], - default_domainsocket="$withval", - default_domainsocket="") +# ======================== +# Check for libcupsfilters +# ======================== +PKG_CHECK_MODULES([LIBCUPSFILTERS], [libcupsfilters]) -if test x$enable_domainsocket != xno -a x$default_domainsocket != xno; then - if test "x$default_domainsocket" = x; then - case "$uname" in - Darwin*) - # Darwin and MaxOS X do their own thing... - CUPS_DEFAULT_DOMAINSOCKET="$localstatedir/run/cupsd" - ;; - *) - # All others use FHS standard... - CUPS_DEFAULT_DOMAINSOCKET="$CUPS_STATEDIR/cups.sock" - ;; - esac - else - CUPS_DEFAULT_DOMAINSOCKET="$default_domainsocket" - fi -fi -AC_DEFINE_UNQUOTED(CUPS_DEFAULT_DOMAINSOCKET, "$CUPS_DEFAULT_DOMAINSOCKET", "Domain socket of CUPS") -AC_SUBST(CUPS_DEFAULT_DOMAINSOCKET) +# ================ +# Check for libppd +# ================ +PKG_CHECK_MODULES([LIBPPD], [libppd]) # ====================== # Check system functions @@ -252,9 +157,9 @@ AC_CHECK_HEADERS([sys/ioctl.h]) AC_CHECK_HEADER(string.h,AC_DEFINE(HAVE_STRING_H)) AC_CHECK_HEADER(strings.h,AC_DEFINE(HAVE_STRINGS_H)) -# ============= -# Image options -# ============= +# ======================= +# Check for image filters +# ======================= AC_ARG_ENABLE([imagefilters], [AS_HELP_STRING([--disable-imagefilters], [Build the image filters.])], [enable_imagefilters="$enableval"], @@ -262,258 +167,6 @@ AC_ARG_ENABLE([imagefilters], ) AM_CONDITIONAL([ENABLE_IMAGEFILTERS], [test "x$enable_imagefilters" != "xno"]) -# Libraries -AC_ARG_WITH([jpeg], - [AS_HELP_STRING([--without-jpeg], [Disable jpeg support.])], - [with_jpeg="$withval"], - [with_jpeg=yes] -) -AS_IF([test x"$with_jpeg" != "xno"], [ - AC_DEFINE([HAVE_LIBJPEG], [], [Defines if we provide jpeg library.]) - AC_CHECK_HEADERS([jpeglib.h]) - AC_SEARCH_LIBS([jpeg_destroy_decompress], - [jpeg], - LIBJPEG_LIBS="-ljpeg", - AC_MSG_ERROR([jpeg libraries not found.]) - ) - AC_SUBST(LIBJPEG_LIBS) -]) - -AC_ARG_WITH([png], - [AS_HELP_STRING([--without-png], [Disable png support.])], - [with_png="$withval"], - [with_png=yes] -) -AS_IF([test x"$with_png" != "xno"], [ - PKG_CHECK_MODULES([LIBPNG], [libpng]) - AC_DEFINE([HAVE_LIBPNG], [], [Defines if we provide png library.]) -]) - -AC_ARG_WITH([tiff], - [AS_HELP_STRING([--without-tiff], [Disable tiff support.])], - [with_tiff="$withval"], - [with_tiff=yes] -) -AS_IF([test x"$with_tiff" != "xno"], [ - AC_DEFINE([HAVE_LIBTIFF], [], [Defines if we provide tiff library.]) - AC_CHECK_HEADERS([tiff.h]) - AC_SEARCH_LIBS([TIFFReadScanline], - [tiff], - LIBJPEG_LIBS="-ltiff", - AC_MSG_ERROR([tiff libraries not found.]) - ) - AC_SUBST(LIBTIFF_LIBS) -]) - -# ================================== -# Check for modules needed by utils/ -# ================================== - -dnl including EXIF library -EXIF_LIBS="" -EXIF_CFLAGS="" - -AC_ARG_ENABLE([exif], - [AS_HELP_STRING([--disable-exif], [Disable exif metadata readout from images])], - [enable_exif="$enableval"], - [enable_exif=yes] -) -AM_CONDITIONAL([ENABLE_EXIF], [test "x$enable_exif" != "xno"]) - -AC_ARG_WITH(exif-libs, - [AS_HELP_STRING([--with-exif-libs], [Set directory for exif library.])], - EXIF_LIBS="-L$withval $EXIF_LIBS",) -AC_ARG_WITH(exif-includes, - [AS_HELP_STRING([--with-exif-includes], [Set directory for exif includes.])], - EXIF_CFLAGS="-I$withval $EXIF_CFLAGS",) - -if test "x$enable_exif" != xno; then - PKG_CHECK_MODULES(EXIF, libexif, - [AC_DEFINE(HAVE_EXIF, [], [Define if you have the libexif library])]) -fi - -AC_SUBST(EXIF_LIBS) -AC_SUBST(EXIF_CFLAGS) - -dnl Avahi for cups-browsed -AVAHI_LIBS="" -AVAHI_CFLAGS="" -AVAHI_GLIB_CFLAGS="" -AVAHI_GLIB_LIBS="" - -AC_ARG_ENABLE([avahi], - [AS_HELP_STRING([--disable-avahi], [Disable DNS Service Discovery support using Avahi.])], - [enable_avahi="$enableval"], - [enable_avahi=yes] -) -AM_CONDITIONAL([ENABLE_AVAHI], [test "x$enable_avahi" != "xno"]) - -AC_ARG_WITH(avahi-libs, - [AS_HELP_STRING([--with-avahi-libs], [Set directory for Avahi library.])], - AVAHI_LIBS="-L$withval $AVAHI_LIBS",) -AC_ARG_WITH(avahi-includes, - [AS_HELP_STRING([--with-avahi-includes], [Set directory for Avahi includes])], - AVAHI_CFLAGS="-I$withval $AVAHI_CFLAGS",) - -if test "x$enable_avahi" != xno; then - PKG_CHECK_MODULES(AVAHI, avahi-client, - [AC_DEFINE(HAVE_AVAHI, [], [Define if you have the avahi library])]) -fi - -AC_SUBST(AVAHI_LIBS) -AC_SUBST(AVAHI_CFLAGS) - -dnl -dnl LDAP configuration stuff for CUPS. -dnl -dnl Copyright 2007-2011 by Apple Inc. -dnl Copyright 2003-2006 by Easy Software Products, all rights reserved. -dnl -dnl These coded instructions, statements, and computer programs are the -dnl property of Apple Inc. and are protected by Federal copyright -dnl law. Distribution and use rights are outlined in the file "COPYING" -dnl which should have been included with this file. -dnl - -AC_ARG_ENABLE([ldap], [AS_HELP_STRING([--disable-ldap], [disable LDAP support.])], - [enable_ldap="$enableval"], - [enable_ldap=yes] -) -AC_ARG_WITH([ldap-libs], [AS_HELP_STRING([--with-ldap-libs], [set directory for LDAP library.])], - LDFLAGS="-L$withval $LDFLAGS" - DSOFLAGS="-L$withval $DSOFLAGS",) -AC_ARG_WITH([ldap-includes], [AS_HELP_STRING([--with-ldap-includes], [set directory for LDAP includes.])], - CFLAGS="-I$withval $CFLAGS" - CPPFLAGS="-I$withval $CPPFLAGS",) - -if test x$enable_ldap != xno; then - - AC_CHECK_HEADER([ldap.h], [ - AC_SEARCH_LIBS([ldap_initialize], [ldap], [ - AC_DEFINE([HAVE_LDAP], [], [Define if LDAP support should be enabled]) - AC_DEFINE([HAVE_OPENLDAP], [], [If LDAP support is that of OpenLDAP]) - AC_CHECK_LIB([ldap], [ldap_start_tls], - AC_DEFINE([HAVE_LDAP_SSL], [], [If LDAP has SSL/TLS support enabled]))],[ - - AC_CHECK_LIB([ldap], [ldap_init], [ - AC_DEFINE([HAVE_LDAP], [], [Define if LDAP support should be enabled]) - AC_DEFINE([HAVE_MOZILLA_LDAP], [], [If LDAP support is that of Mozilla]) - AC_CHECK_HEADERS([ldap_ssl.h], [], [], [#include ]) - AC_CHECK_LIB([ldap], [ldapssl_init], - AC_DEFINE([HAVE_LDAP_SSL], [], [If LDAP has SSL/TLS support enabled]))])] - ) - AC_CHECK_LIB([ldap], [ldap_set_rebind_proc], AC_DEFINE([HAVE_LDAP_REBIND_PROC], [], [If libldap implements ldap_set_rebind_proc])) - ]) - -fi - -PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.30.2]) -AC_SUBST(GLIB_CFLAGS) -AC_SUBST(GLIB_LIBS) - -if test x$enable_avahi != xno; then - PKG_CHECK_MODULES(AVAHI_GLIB, [avahi-glib]) -fi - -AC_SUBST(AVAHI_GLIB_CFLAGS) -AC_SUBST(AVAHI_GLIB_LIBS) - -PKG_CHECK_MODULES(GIO, [gio-2.0]) -AC_SUBST(GIO_CFLAGS) -AC_SUBST(GIO_LIBS) - -PKG_CHECK_MODULES(GIO_UNIX, [gio-unix-2.0]) -AC_SUBST(GIO_UNIX_CFLAGS) -AC_SUBST(GIO_UNIX_LIBS) - -AC_ARG_WITH([browseremoteprotocols], - [AS_HELP_STRING([--with-browseremoteprotocols=value], [Set which protocols to listen for in cups-browsed (default: dnssd cups)])], - [with_browseremoteprotocols="$withval"], - [with_browseremoteprotocols="dnssd cups"] -) -BROWSEREMOTEPROTOCOLS="$with_browseremoteprotocols" -AC_SUBST(BROWSEREMOTEPROTOCOLS) - -dnl Setup init.d locations... -AC_ARG_WITH(rcdir, [AS_HELP_STRING([--with-rcdir], [Set path for rc scripts])],rcdir="$withval",rcdir="") -AC_ARG_WITH(rclevels, [AS_HELP_STRING([--with-rclevels], [Set run levels for rc scripts])],rclevels="$withval",rclevels="2 3 5") -AC_ARG_WITH(rcstart, [AS_HELP_STRING([--with-rcstart], [Set start number for rc scripts])],rcstart="$withval",rcstart="99") -AC_ARG_WITH(rcstop, [AS_HELP_STRING([--with-rcstop], [Set stop number for rc scripts])],rcstop="$withval",rcstop="00") - -INITDIR="" -INITDDIR="" -RCLEVELS="$rclevels" -RCSTART="$rcstart" -RCSTOP="$rcstop" - -if test x$rcdir = x; then - case "`uname`" in - FreeBSD* | OpenBSD* | MirBSD* | ekkoBSD*) - # FreeBSD and OpenBSD - ;; - - Linux | GNU | GNU/k*BSD*) - # Linux/HURD seems to choose an init.d directory at random... - if test -d /sbin/init.d; then - # SuSE - INITDIR="/sbin/init.d" - else - if test -d /etc/init.d; then - # Others - INITDIR="/etc" - else - # RedHat - INITDIR="/etc/rc.d" - fi - fi - RCSTART="82" - RCSTOP="35" - ;; - - NetBSD*) - # NetBSD - INITDDIR="/etc/rc.d" - ;; - - *) - INITDIR="/etc" - ;; - - esac -elif test "x$rcdir" != xno; then - if test "x$rclevels" = x; then - INITDDIR="$rcdir" - else - INITDIR="$rcdir" - fi -fi - -AM_CONDITIONAL([RCLINKS], [test "x$INITDIR" != "x"]) - -if test "x${INITDIR}" != "x" -a "x${INITDDIR}" = "x"; then - INITDDIR="${INITDIR}/init.d" -fi - -AC_SUBST(INITDIR) -AC_SUBST(INITDDIR) -AC_SUBST(RCLEVELS) -AC_SUBST(RCSTART) -AC_SUBST(RCSTOP) - -# ====================================== -# Check for various pdf required modules -# ====================================== -PKG_CHECK_MODULES([LCMS], [lcms2], [lcms2=yes], [lcms2=no]) -AS_IF([test x"$lcms2" = "xno"], [ - PKG_CHECK_MODULES([LCMS], [lcms]) - AC_DEFINE([USE_LCMS1], [1], [Defines if use lcms1]) -]) -PKG_CHECK_MODULES([FREETYPE], [freetype2], [AC_DEFINE([HAVE_FREETYPE_H], [1], [Have FreeType2 include files])]) -PKG_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.0.0]) -PKG_CHECK_MODULES([ZLIB], [zlib]) -AC_DEFINE([HAVE_LIBZ], [], [Define that we use zlib]) -PKG_CHECK_MODULES([LIBQPDF], [libqpdf >= 10.3.2]) - # ================= # Check for Poppler # ================= @@ -522,101 +175,21 @@ AC_ARG_ENABLE(poppler, AS_HELP_STRING([--enable-poppler],[enable Poppler-based f AM_CONDITIONAL(ENABLE_POPPLER, test x$enable_poppler = xyes) if test x$enable_poppler = xyes; then PKG_CHECK_MODULES([POPPLER], [poppler-cpp >= 0.19]) - AC_CHECK_HEADER([poppler/cpp/poppler-version.h], [AC_DEFINE([HAVE_CPP_POPPLER_VERSION_H],,[Define if you have Poppler's "cpp/poppler-version.h" header file.])], []) fi -# =============== -# Check for D-Bus -# =============== -AC_ARG_ENABLE(dbus, AS_HELP_STRING([--enable-dbus],[enable DBus CMS code]), - enable_dbus=$enableval,enable_dbus=yes) -AM_CONDITIONAL(BUILD_DBUS, test x$enable_dbus = xyes) -if test x$enable_dbus = xyes; then - PKG_CHECK_MODULES(DBUS, dbus-1) -fi - -# =================================== -# Check for large files and long long -# =================================== -AC_SYS_LARGEFILE -LARGEFILE="" -AS_IF([test x"$enable_largefile" != "xno"], [ - LARGEFILE="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE" - AS_IF([test x"$ac_cv_sys_large_files" = "x1"], [LARGEFILE="$LARGEFILE -D_LARGE_FILES"]) - AS_IF([test x"$ac_cv_sys_file_offset_bits" = "x64"], [LARGEFILE="$LARGEFILE -D_FILE_OFFSET_BITS=64"]) -]) -AC_SUBST(LARGEFILE) - -AC_CHECK_TYPE(long long, [long_long_found=yes], [long_long_found=no]) -AS_IF([test x"$long_long_found" = "xyes"], [ - AC_DEFINE([HAVE_LONG_LONG], [], [Platform supports long long type]) -]) - -# ================ -# Check for pdf2ps -# ================ +# ===================== +# Check for Ghostscript +# ===================== AC_ARG_ENABLE([ghostscript], [AS_HELP_STRING([--disable-ghostscript], [Disable filters using Ghostscript.])], [enable_ghostscript="$enableval"], [enable_ghostscript=yes] ) -AC_ARG_ENABLE([pdftops], - [AS_HELP_STRING([--disable-pdftops], [Disable filters using Poppler's pdftops.])], - [enable_pdftops="$enableval"], - [enable_pdftops=yes] -) -AC_ARG_WITH([pdftops], - [AS_HELP_STRING([--with-pdftops=value], [Set which pdftops to use (gs,pdftops,pdftocairo,acroread,mupdf,hybrid).])], - [with_pdftops="$withval"], - [with_pdftops=hybrid] -) -AS_CASE([x$with_pdftops], - [xgs|xpdftops|xpdftocairo|xacroread|xmupdf|xhybrid], [], - [AC_MSG_ERROR([Unknown value of with-pdftops provided: $with_pdftops])] -) AC_ARG_WITH([gs-path], [AS_HELP_STRING([--with-gs-path=value], [Set path to ghostcript binary (default: system).])], [with_gs_path="$withval"], [with_gs_path=system] ) -AC_ARG_WITH([pdftops-path], - [AS_HELP_STRING([--with-pdftops-path=value], [Set path to pdftops/ghostscript binary (default: system).])], - [with_pdftops_path="$withval"], - [with_pdftops_path=system] -) -AC_ARG_WITH([pdftocairo-path], - [AS_HELP_STRING([--with-pdftocairo-path=value], [Set path to pdftocairo binary (default: system).])], - [with_pdftocairo_path="$withval"], - [with_pdftocairo_path=system] -) -AC_ARG_WITH([acroread-path], - [AS_HELP_STRING([--with-acroread-path=value], [Set path to acroread binary (default: system).])], - [with_acroread_path="$withval"], - [with_acroread_path=system] -) -AC_ARG_WITH([ippfind-path], - [AS_HELP_STRING([--with-ippfind-path=value], [Set path to ippfind binary (default: system).])], - [with_ippfind_path="$withval"], - [with_ippfind_path=system] -) -AC_ARG_WITH([pdftops-maxres], - [AS_HELP_STRING([--with-pdftops-maxres=value], [Set maximum image rendering resolution for pdftops filter (0, 75, 150, 300, 600, 1200, 2400, 4800, 90, 180, 360, 720, 1440, 2880, 5760, unlimited). Default: 1440])], - [with_pdftops_maxres="$withval"], - [with_pdftops_maxres=1440] -) -AS_CASE([x$with_pdftops_maxres], - [x0|x75|x150|x300|x600|x1200|x2400|x4800|x90|x180|x360|x720|x1440|x2880|x5760], [CUPS_PDFTOPS_MAXRES=$with_pdftops_maxres], - [xunlimited], [CUPS_PDFTOPS_MAXRES=0], - [AC_MSG_ERROR([Unknown value of with-pdftops-maxres provided: $with_pdftops])] -) -AC_ARG_ENABLE([gs-ps2write], - [AS_HELP_STRING([--disable-gs-ps2write], [Ghostscript doesn't support ps2write device.])], - [enable_gs_ps2write="$enableval"], - [enable_gs_ps2write=yes] -) - -CUPS_PDFTOPS_RENDERER="" - CUPS_GHOSTSCRIPT="" AS_IF([test "x$enable_ghostscript" != "xyes"], [ with_gs_path="" @@ -634,61 +207,10 @@ AS_IF([test "x$enable_ghostscript" != "xyes"], [ AC_MSG_ERROR([Required gs binary is missing. Please install ghostscript package.]) ]) AC_DEFINE([HAVE_GHOSTSCRIPT], [], [Define that we provide ghostscript binary]) - AS_IF([test x"$with_pdftops" = xgs -o x"$CUPS_PDFTOPS_RENDERER" = x], [CUPS_PDFTOPS_RENDERER=GS]) - - AS_IF([test "x$cross_compiling" != "xyes"], [ - AC_MSG_CHECKING(whether gs supports the ps2write device) - AS_IF([`$CUPS_GHOSTSCRIPT -h 2>&1 | grep -q ps2write`], [ - AC_MSG_RESULT([yes]) - ], [ - AC_MSG_RESULT([no]) - enable_gs_ps2write=no - ]) - ]) - AS_IF([test x"$enable_gs_ps2write" = "xyes"], [ - AC_DEFINE([HAVE_GHOSTSCRIPT_PS2WRITE], [], [gs supports ps2write]) - ]) ]) AM_CONDITIONAL(ENABLE_GHOSTSCRIPT, test "x$enable_ghostscript" = xyes) AC_SUBST(CUPS_GHOSTSCRIPT) - -AS_IF([test "x$enable_pdftops" != "xyes"], [ - with_pdftops_path="" -], [ - AS_IF([test "x$with_pdftops_path" != "xsystem"], [ - CUPS_PDFTOPS="$with_pdftops_path" - ], [ - AS_IF([test "x$cross_compiling" = "xyes"], [ - CUPS_PDFTOPS="/usr/bin/pdftops" - ], [ - AC_CHECK_PROG(CUPS_PDFTOPS, pdftops, /usr/bin/pdftops) - ]) - AS_IF([test "x$CUPS_PDFTOPS" = "x"], [ - AC_MSG_ERROR([Required pdftops is missing. Please install the pdftops utility of Poppler.]) - ]) - ]) - AS_IF([test "x$CUPS_PDFTOPS" != "x"], [ - AC_DEFINE([HAVE_POPPLER_PDFTOPS], [], [Define that we provide poppler pdftops.]) - AS_IF([test x"$with_pdftops" = xpdftops -o x"$CUPS_PDFTOPS_RENDERER" = x], [CUPS_PDFTOPS_RENDERER=PDFTOPS]) - - AS_IF([test "x$cross_compiling" != "xyes"], [ - AC_MSG_CHECKING([whether pdftops supports -origpagesizes]) - AS_IF([`$CUPS_PDFTOPS -h 2>&1 | grep -q -- -origpagesizes`], [ - AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES], [] , [pdftops supports -origpagesizes.]) - ], [ - AC_MSG_RESULT([no]) - ]) - AC_MSG_CHECKING([whether pdftops supports -r]) - AS_IF([`$CUPS_PDFTOPS -h 2>&1 | grep -q -- '-r '`], [ - AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_POPPLER_PDFTOPS_WITH_RESOLUTION], [] , [pdftops supports -r argument.]) - ], [ - AC_MSG_RESULT([no]) - ]) - ]) - ]) -]) +AC_DEFINE_UNQUOTED([CUPS_GHOSTSCRIPT], "$CUPS_GHOSTSCRIPT", [gs binary to use]) # ================ # Check for Mutool @@ -722,73 +244,80 @@ AS_IF([test "x$enable_mutool" != "xyes"], [ AC_MSG_ERROR([Required mutool binary is missing. Please install mutool.]) ]) AC_DEFINE([HAVE_MUTOOL], [], [Define that we provide mutool binary]) - AS_IF([test x"$with_pdftops" = xmupdf -o x"$CUPS_PDFTOPS_RENDERER" = x], [CUPS_PDFTOPS_RENDERER=MUPDF]) ]) AM_CONDITIONAL(ENABLE_MUTOOL, test "x$enable_mutool" = xyes) AC_SUBST(CUPS_MUTOOL) +AC_DEFINE_UNQUOTED([CUPS_MUTOOL],"$CUPS_MUTOOL",[mutool binary to use]) + +# ============================== +# Check for "driverless" utility +# ============================== -AC_ARG_ENABLE([pdftocairo], - [AS_HELP_STRING([--disable-pdftocairo], [Disable filters using pdftocairo.])], - [enable_pdftocairo="$enableval"], - [enable_pdftocairo=yes] +AC_ARG_ENABLE([driverless], [AS_HELP_STRING([--enable-driverless], [enable PPD generator for driverless printing in /usr/lib/cups/driver/, for manual setup of driverless printers with printer setup tool.])], + [enable_driverless="$enableval"], + [enable_driverless=yes] ) +AM_CONDITIONAL([ENABLE_DRIVERLESS], +[test "x$enable_driverless" != "xno"]) -CUPS_PDFTOCAIRO="" -AS_IF([test "x$enable_pdftocairo" != "xyes"], [ - with_pdftocairo_path="" +dnl ippfind +AC_ARG_WITH([ippfind-path], + [AS_HELP_STRING([--with-ippfind-path=value], [Set path to ippfind binary (default: system).])], + [with_ippfind_path="$withval"], + [with_ippfind_path=system] +) +AS_IF([test "x$with_ippfind_path" != "xsystem"], [ + CUPS_IPPFIND="$with_ippfind_path" ], [ - AS_IF([test "x$with_pdftocairo_path" != "xsystem"], [ - CUPS_PDFTOCAIRO="$with_pdftocairo_path" - ], [ - CUPS_PDFTOCAIRO="pdftocairo" - ]) - AS_IF([test "x$CUPS_PDFTOCAIRO" != "x"], [ - AC_DEFINE([HAVE_PDFTOCAIRO], [], [Define that we provide pdftocairo binary]) - AS_IF([test x"$with_pdftops" = xpdftocairo -o x"$CUPS_PDFTOPS_RENDERER" = x], [CUPS_PDFTOPS_RENDERER=PDFTOCAIRO]) - ], [ - AC_MSG_ERROR([Required pdftocairo is missing. Please install Poppler developer packages.]) - ]) + CUPS_IPPFIND="ippfind" ]) +AC_DEFINE_UNQUOTED([CUPS_IPPFIND], "$CUPS_IPPFIND", [ippfind binary to use.]) + + +dnl Avahi +AVAHI_LIBS="" +AVAHI_CFLAGS="" +AVAHI_GLIB_CFLAGS="" +AVAHI_GLIB_LIBS="" -AC_ARG_ENABLE([acroread], - [AS_HELP_STRING([--disable-acroread], [Disable filters using acroread.])], - [enable_acroread="$enableval"], - [enable_acroread=yes] +AC_ARG_ENABLE([avahi], + [AS_HELP_STRING([--disable-avahi], [Disable DNS Service Discovery support using Avahi.])], + [enable_avahi="$enableval"], + [enable_avahi=yes] ) +AM_CONDITIONAL([ENABLE_AVAHI], [test "x$enable_avahi" != "xno"]) -CUPS_ACROREAD="" -AS_IF([test "x$enable_acroread" != "xyes"], [ - with_acroread_path="" -], [ - AS_IF([test "x$with_acroread_path" != "xsystem"], [ - CUPS_ACROREAD="$with_acroread_path" - ], [ - CUPS_ACROREAD="acroread" - ]) - AS_IF([test "x$CUPS_ACROREAD" != "x"], [ - AC_DEFINE([HAVE_ACROREAD], [], [Define that we provide acroread binary]) - AS_IF([test x"$with_pdftops" = xacroread -o x"$CUPS_PDFTOPS_RENDERER" = x], [CUPS_PDFTOPS_RENDERER=ACROREAD]) - ]) -]) +AC_ARG_WITH(avahi-libs, + [AS_HELP_STRING([--with-avahi-libs], [Set directory for Avahi library.])], + AVAHI_LIBS="-L$withval $AVAHI_LIBS",) +AC_ARG_WITH(avahi-includes, + [AS_HELP_STRING([--with-avahi-includes], [Set directory for Avahi includes])], + AVAHI_CFLAGS="-I$withval $AVAHI_CFLAGS",) -AS_IF([test "x$CUPS_GHOSTSCRIPT" != "x" -a "x$CUPS_PDFTOPS" != "x"], [ - AS_IF([test x"$with_pdftops" = xhybrid -o x"$CUPS_PDFTOPS_RENDERER" = x], [CUPS_PDFTOPS_RENDERER=HYBRID]) -]) +if test "x$enable_avahi" != xno; then + PKG_CHECK_MODULES(AVAHI, avahi-client, + [AC_DEFINE(HAVE_AVAHI, [], [Define if you have the avahi library])]) +fi -AS_IF([test "x$with_ippfind_path" != "xsystem"], [ - CUPS_IPPFIND="$with_ippfind_path" -], [ - CUPS_IPPFIND="ippfind" +AC_SUBST(AVAHI_LIBS) +AC_SUBST(AVAHI_CFLAGS) + +# =================================== +# Check for large files and long long +# =================================== +AC_SYS_LARGEFILE +LARGEFILE="" +AS_IF([test x"$enable_largefile" != "xno"], [ + LARGEFILE="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE" + AS_IF([test x"$ac_cv_sys_large_files" = "x1"], [LARGEFILE="$LARGEFILE -D_LARGE_FILES"]) + AS_IF([test x"$ac_cv_sys_file_offset_bits" = "x64"], [LARGEFILE="$LARGEFILE -D_FILE_OFFSET_BITS=64"]) ]) +AC_SUBST(LARGEFILE) -AC_DEFINE_UNQUOTED([CUPS_GHOSTSCRIPT], "$CUPS_GHOSTSCRIPT", [gs binary to use]) -AC_DEFINE_UNQUOTED([CUPS_MUTOOL],"$CUPS_MUTOOL",[mutool binary to use]) -AC_DEFINE_UNQUOTED([CUPS_POPPLER_PDFTOPS], "$CUPS_PDFTOPS", [pdftops binary to use.]) -AC_DEFINE_UNQUOTED([CUPS_POPPLER_PDFTOCAIRO], "$CUPS_PDFTOCAIRO", [pdftocairo binary to use.]) -AC_DEFINE_UNQUOTED([CUPS_ACROREAD], "$CUPS_ACROREAD", [acroread binary to use.]) -AC_DEFINE_UNQUOTED([CUPS_IPPFIND], "$CUPS_IPPFIND", [ippfind binary to use.]) -AC_DEFINE_UNQUOTED([CUPS_PDFTOPS_MAX_RESOLUTION], [$CUPS_PDFTOPS_MAXRES], [max resolution used for pdftops when converting images]) -AC_DEFINE_UNQUOTED([CUPS_PDFTOPS_RENDERER], [$CUPS_PDFTOPS_RENDERER], [Define default renderer]) +AC_CHECK_TYPE(long long, [long_long_found=yes], [long_long_found=no]) +AS_IF([test x"$long_long_found" = "xyes"], [ + AC_DEFINE([HAVE_LONG_LONG], [], [Platform supports long long type]) +]) # ================== # Check for foomatic @@ -834,7 +363,6 @@ AS_IF([test "x$CUPS_GHOSTSCRIPT" == "x" -a "x$CUPS_PDFTOPS" == "x"], [ AM_CONDITIONAL([ENABLE_UNIVERSAL_CUPS_FILTER], [test "x$enable_universal_cups_filter" != "xno"]) - # ================================= # Check for individual CUPS filters # ================================= @@ -846,20 +374,6 @@ AC_ARG_ENABLE([individual-cups-filters], AM_CONDITIONAL([ENABLE_INDIVIDUAL_CUPS_FILTERS], [test "x$enable_individual_cups_filters" != "xno"]) -# ========= -# Test ARGS -# ========= -AC_ARG_WITH([test-font-path], - [AS_HELP_STRING([--with-test-font-path=value], [Set path to font used for tests (default: /usr/share/fonts/dejavu/DejaVuSans.ttf).])], - [with_test_font_path="$withval"], - [with_test_font_path=`( find /usr/share/fonts -name DejaVuSans.ttf; echo /usr/share/fonts/dejavu/DejaVuSans.ttf ) | head -1`] -) - -AS_IF([test "x$cross_compiling" != "xyes" && ! test -f "$with_test_font_path"], - [AC_MSG_WARN(DejaVuSans.ttf font file is missing. Please install a package providing it.) && [with_test_font_path=no]] -) -AC_DEFINE_UNQUOTED([TESTFONT], ["$with_test_font_path"], [Path to font used in tests]) - # ================ # Check for cflags # ================ @@ -879,59 +393,6 @@ AS_IF([test x"$GCC" = "xyes"], [ CFLAGS="$CFLAGS -D_GNU_SOURCE" CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE" -# ========================== -# Braille embossing/liblouis -# ========================== -AC_ARG_ENABLE(braille, AS_HELP_STRING([--enable-braille],[enable Braille embosing filters, requires liblouis]), - enable_braille=$enableval,enable_braille=yes) -AC_MSG_CHECKING(for liblouis) -PKG_CHECK_EXISTS([liblouis], [ - AC_MSG_RESULT(yes) - if test "x$enable_braille" = xyes; then - TABLESDIR=`$PKG_CONFIG --variable=tablesdir liblouis` - else - TABLESDIR=/usr/share/liblouis/tables - fi -], [ - AC_MSG_RESULT(no) - TABLESDIR=/usr/share/liblouis/tables -]) -AM_CONDITIONAL(ENABLE_BRAILLE, test "x$enable_braille" = xyes) -AC_SUBST(TABLESDIR) - -# =============================================== -# Should we keep generated queues after shutdown? -# =============================================== -AC_ARG_ENABLE(saving-created-queues, AS_HELP_STRING([--enable-saving-created-queues], [enable saving created queues during shutdown]), - [SAVING_CREATED_QUEUES=$enableval],[SAVING_CREATED_QUEUES="no"]) - -AS_IF([test "x$SAVING_CREATED_QUEUES" != "xno"], - [AC_DEFINE([SAVING_CREATED_QUEUES], [1], [Define whether we save queues during shutdown])] -) - -# ========================================= -# Local queue naming for remote CUPS queues -# ========================================= -AC_ARG_WITH([remote-cups-local-queue-naming], - [AS_HELP_STRING([--with-remote-cups-local-queue-naming=DNS-SD|MakeModel|RemoteName], [Choose the origin of local queue naming for remote CUPS queues, default based on DNS-SD ID])], - [case "x$withval" in - "xMakeModel") - REMOTE_CUPS_LOCAL_QUEUE_NAMING="MakeModel" - AC_DEFINE([NAMING_MAKE_MODEL], [1], [Define that we create local queues for remote CUPS queues based on printer Make-Model]) - ;; - "xRemoteName") - REMOTE_CUPS_LOCAL_QUEUE_NAMING="RemoteName" - AC_DEFINE([NAMING_REMOTE_NAME], [1], [Define that we create local queues for remote CUPS queues based on their print queue name on the server]) - ;; - *) - REMOTE_CUPS_LOCAL_QUEUE_NAMING="DNS-SD" - AC_DEFINE([NAMING_DNSSD], [1], [Define that we create local queues for remote CUPS queues based on DNS-SD name]) - ;; - esac], - [REMOTE_CUPS_LOCAL_QUEUE_NAMING="DNS-SD" - AC_DEFINE([NAMING_DNSSD], [1], [Define that we create local queues for remote CUPS queues based on DNS-SD name])] -) - # ========================================================= # Select a different shell instead of the default /bin/bash # ========================================================= @@ -942,67 +403,12 @@ AC_ARG_WITH([shell], ) AC_DEFINE_UNQUOTED([SHELL], "$with_shell", [Path for a modern shell]) -# ============================ -# Debugging options for libppd -# ============================ -AC_ARG_ENABLE(debug_guards, [ --enable-debug-guards build libppd with memory allocat -ion guards]) -AC_ARG_ENABLE(debug_printfs, [ --enable-debug-printfs build libppd with CUPS_DEBUG_LOG support]) - -dnl Debug printfs can slow things down, so provide a separate option for that -if test x$enable_debug_printfs = xyes; then - CFLAGS="$CFLAGS -DDEBUG" - CXXFLAGS="$CXXFLAGS -DDEBUG" -fi - -dnl Debug guards use an extra 4 bytes for some structures like strings in the -dnl string pool, so provide a separate option for that -if test x$enable_debug_guards = xyes; then - CFLAGS="$CFLAGS -DDEBUG_GUARDS" - CXXFLAGS="$CXXFLAGS -DDEBUG_GUARDS" -fi - -# ========================================================= -# Turn on/off network interface updates for each found entry -# ========================================================== -AC_ARG_ENABLE([frequent_netif_update], - [AS_HELP_STRING([--enable-frequent-netif-update], [Enable network interface update after each found entry to prevent network issues])], - [FREQUENT_NETIF_UPDATE=$enableval], - [FREQUENT_NETIF_UPDATE=yes] -) - -AS_IF([test "x$FREQUENT_NETIF_UPDATE" != "xno"], - [AC_DEFINE([FREQUENT_NETIF_UPDATE], [1], [Define whether we want network interface update after each found entry])] -) - # ===================== # Prepare all .in files # ===================== AC_CONFIG_FILES([ - libcupsfilters.pc - libppd.pc Makefile - utils/cups-browsed - utils/cups-browsed.conf filter/foomatic-rip/foomatic-rip.1 - filter/braille/drivers/index/indexv4.sh - filter/braille/drivers/index/indexv3.sh - filter/braille/drivers/index/index.sh - filter/braille/drivers/index/textbrftoindexv3 - filter/braille/drivers/index/imageubrltoindexv3 - filter/braille/drivers/index/imageubrltoindexv4 - filter/braille/drivers/generic/brftoembosser - filter/braille/filters/cups-braille.sh - filter/braille/filters/imagetobrf - filter/braille/filters/texttobrf - filter/braille/filters/brftopagedbrf - filter/braille/filters/vectortopdf - filter/braille/filters/vectortobrf - filter/braille/filters/musicxmltobrf - filter/braille/filters/liblouis1.defs.gen -]) -AC_CONFIG_COMMANDS([executable-scripts], [ - chmod +x filter/braille/filters/liblouis1.defs.gen ]) AC_OUTPUT @@ -1017,48 +423,21 @@ Environment settings: LDFLAGS: ${LDFLAGS} Build configuration: cups-config: ${with_cups_config} - font directory: ${sysconfdir}/${FONTDIR} foomatic: ${enable_foomatic} - init directory: ${INITDDIR} - cups dom socket: ${CUPS_DEFAULT_DOMAINSOCKET} poppler: ${enable_poppler} ghostscript: ${enable_ghostscript} gs-path: ${with_gs_path} - gs ps2write: ${enable_gs_ps2write} mutool: ${enable_mutool} mutool-path: ${with_mutool_path} ippfind-path: ${with_ippfind_path} imagefilters: ${enable_imagefilters} - jpeg: ${with_jpeg} - exif: ${enable_exif} - pdftocairo: ${enable_pdftocairo} - pdftocairo-path: ${with_pdftocairo_path} - acroread: ${enable_acroread} - acroread-path: ${with_acroread_path} - pdftops: ${CUPS_PDFTOPS_RENDERER} - Poppler's pdftops: ${enable_pdftops} - pdftops-path: ${with_pdftops_path} - png: ${with_png} pstops: ${enable_pstops} rastertopwg: ${enable_rastertopwg} shell: ${with_shell} - test-font: ${with_test_font_path} - tiff: ${with_tiff} avahi: ${enable_avahi} - dbus: ${enable_dbus} - browsing: ${with_browseremoteprotocols} - werror: ${enable_werror} - braille: ${enable_braille} - braille tables: ${TABLESDIR} universal CUPS filter: ${enable_universal_cups_filter} individual CUPS filters: ${enable_individual_cups_filters} driverless: ${enable_driverless} - ppdc utilities: ${enable_ppdc_utils} - local queue naming for remote CUPS queues: ${REMOTE_CUPS_LOCAL_QUEUE_NAMING} - keep generated queues during shutdown: ${SAVING_CREATED_QUEUES} - update network interfaces after each found entry: ${FREQUENT_NETIF_UPDATE} - all ipp printer auto-setup: ${enable_auto_setup_all} - only driverless auto-setup: ${enable_auto_setup_driverless_only} - only local auto-setup: ${enable_auto_setup_local_only} + werror: ${enable_werror} ============================================================================== ]) diff --git a/cupsfilters/bannertopdf.c b/cupsfilters/bannertopdf.c deleted file mode 100644 index 5a73af40e..000000000 --- a/cupsfilters/bannertopdf.c +++ /dev/null @@ -1,988 +0,0 @@ -// -// Copyright 2012 Canonical Ltd. -// Copyright 2013 ALT Linux, Andrew V. Stepanov -// Copyright 2018 Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include -#include -#include "ipp.h" -#include "ipp.h" -#include "raster.h" - -#ifndef HAVE_OPEN_MEMSTREAM -#include -#include -#include -#endif - -#include -#include - -#include "filter.h" -#include "pdf.h" - -typedef enum banner_info_e -{ - INFO_IMAGEABLE_AREA = 1, - INFO_JOB_BILLING = 1 << 1, - INFO_JOB_ID = 1 << 2, - INFO_JOB_NAME = 1 << 3, - INFO_JOB_ORIGINATING_HOST_NAME = 1 << 4, - INFO_JOB_ORIGINATING_USER_NAME = 1 << 5, - INFO_JOB_UUID = 1 << 6, - INFO_OPTIONS = 1 << 7, - INFO_PAPER_NAME = 1 << 8, - INFO_PAPER_SIZE = 1 << 9, - INFO_PRINTER_DRIVER_NAME = 1 << 10, - INFO_PRINTER_DRIVER_VERSION = 1 << 11, - INFO_PRINTER_INFO = 1 << 12, - INFO_PRINTER_LOCATION = 1 << 13, - INFO_PRINTER_MAKE_AND_MODEL = 1 << 14, - INFO_PRINTER_NAME = 1 << 15, - INFO_TIME_AT_CREATION = 1 << 16, - INFO_TIME_AT_PROCESSING = 1 << 17 -} banner_info_t; - -typedef struct banner_s -{ - char *template_file; - char *header, *footer; - unsigned infos; -} banner_t; - -static void -banner_free(banner_t *banner) -{ - if (banner) - { - free(banner->template_file); - free(banner->header); - free(banner->footer); - free(banner); - } -} - -static int -parse_line(char *line, - char **key, - char **value) -{ - char *p = line; - - *key = *value = NULL; - - while (isspace(*p)) - p++; - if (!*p || *p == '#') - return (0); - - *key = p; - while (*p && !isspace(*p)) - p++; - if (!*p) - return (1); - - *p++ = '\0'; - - while (isspace(*p)) - p++; - if (!*p) - return (1); - - *value = p; - - // remove trailing space - while (*p) - p++; - while (isspace(*--p)) - *p = '\0'; - - return (1); -} - -static unsigned -parse_show(char *s, - cf_logfunc_t log, - void *ld) -{ - unsigned info = 0; - char *tok; - - for (tok = strtok(s, " \t"); tok; tok = strtok(NULL, " \t")) - { - if (!strcasecmp(tok, "imageable-area")) - info |= INFO_IMAGEABLE_AREA; - else if (!strcasecmp(tok, "job-billing")) - info |= INFO_JOB_BILLING; - else if (!strcasecmp(tok, "job-id")) - info |= INFO_JOB_ID; - else if (!strcasecmp(tok, "job-name")) - info |= INFO_JOB_NAME; - else if (!strcasecmp(tok, "job-originating-host-name")) - info |= INFO_JOB_ORIGINATING_HOST_NAME; - else if (!strcasecmp(tok, "job-originating-user-name")) - info |= INFO_JOB_ORIGINATING_USER_NAME; - else if (!strcasecmp(tok, "job-uuid")) - info |= INFO_JOB_UUID; - else if (!strcasecmp(tok, "options")) - info |= INFO_OPTIONS; - else if (!strcasecmp(tok, "paper-name")) - info |= INFO_PAPER_NAME; - else if (!strcasecmp(tok, "paper-size")) - info |= INFO_PAPER_SIZE; - else if (!strcasecmp(tok, "printer-driver-name")) - info |= INFO_PRINTER_DRIVER_NAME; - else if (!strcasecmp(tok, "printer-driver-version")) - info |= INFO_PRINTER_DRIVER_VERSION; - else if (!strcasecmp(tok, "printer-info")) - info |= INFO_PRINTER_INFO; - else if (!strcasecmp(tok, "printer-location")) - info |= INFO_PRINTER_LOCATION; - else if (!strcasecmp(tok, "printer-make-and-model")) - info |= INFO_PRINTER_MAKE_AND_MODEL; - else if (!strcasecmp(tok, "printer-name")) - info |= INFO_PRINTER_NAME; - else if (!strcasecmp(tok, "time-at-creation")) - info |= INFO_TIME_AT_CREATION; - else if (!strcasecmp(tok, "time-at-processing")) - info |= INFO_TIME_AT_PROCESSING; - else if (log) - log(ld, CF_LOGLEVEL_ERROR, "cfFilterBannerToPDF: error: unknown value for 'Show': %s\n", tok); - } - - return (info); -} - -static char * -template_path(const char *name, - const char *datadir) -{ - char *result; - - if (name[0] == '/') - return (strdup(name)); - - result = malloc(strlen(datadir) + strlen(name) + 2); - sprintf(result, "%s/%s", datadir, name); - - return (result); -} - -static banner_t * -banner_new_from_file(const char *filename, - int *num_options, - cups_option_t **options, - const char *datadir, - cf_logfunc_t log, - void *ld) -{ - FILE *f; - char *line = NULL; - size_t len = 0, bytes_read; - int linenr = 0; - int ispdf = 0; - int gotinfos = 0; - banner_t *banner = NULL; - - if (!(f = fopen(filename, "r"))) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterBannerToPDF: Error opening temporary file with input stream"); - goto out; - } - - while ((bytes_read = getline(&line, &len, f)) != -1) - { - char *start = line; - - linenr ++; - - if (bytes_read == -1) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterBannerToPDF: No banner instructions found in input stream"); - goto out; - } - - if (strncmp(line, "%PDF-", 5) == 0) - ispdf = 1; - while(start < line + len && - ((ispdf && *start == '%') || isspace(*start))) - start ++; - if (strncasecmp(start, "#PDF-BANNER", 11) == 0 || - strncasecmp(start, "PDF-BANNER", 10) == 0) - break; - } - - banner = calloc(1, sizeof *banner); - - while (getline(&line, &len, f) != -1) - { - char *key, *value; - - linenr++; - if (!parse_line(line, &key, &value)) - continue; - - if (!value) - { - if (ispdf) - break; - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterBannerToPDF: Line %d is missing a value", linenr); - continue; - } - - if (ispdf) - while (*key == '%') - key ++; - - if (!strcasecmp(key, "template")) - banner->template_file = template_path(value, datadir); - else if (!strcasecmp(key, "header")) - banner->header = strdup(value); - else if (!strcasecmp(key, "footer")) - banner->header = strdup(value); - else if (!strcasecmp(key, "font")) - *num_options = cupsAddOption("banner-font", - strdup(value), *num_options, options); - else if (!strcasecmp(key, "font-size")) - *num_options = cupsAddOption("banner-font-size", - strdup(value), *num_options, options); - else if (!strcasecmp(key, "show")) - { - banner->infos = parse_show(value, log, ld); - gotinfos = 1; - } - else if (!strcasecmp(key, "image") || - !strcasecmp(key, "notice")) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterBannerToPDF: Note: %d: bannertopdf does not support '%s'", - linenr, key); - } - else - { - if (ispdf) - break; - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterBannerToPDF: Error: %d: unknown keyword '%s'", - linenr, key); - } - } - - // load default template if none was specified - if (!banner->template_file) - { - if (ispdf) - banner->template_file = strdup(filename); - else - banner->template_file = template_path("default.pdf", datadir); - } - if (!gotinfos) - { - char *value = strdup("printer-name printer-info printer-location printer-make-and-model printer-driver-name printer-driver-version paper-size imageable-area job-id options time-at-creation time-at-processing"); - banner->infos = parse_show(value, log, ld); - free(value); - } - - out: - free(line); - if (f) - fclose(f); - - return (banner); -} - -static int -get_int_option(const char *name, - int num_options, - cups_option_t *options, - int def) -{ - const char *value = cupsGetOption(name, num_options, options); - return (value ? atoi(value) : def); -} - -static int -duplex_marked(cf_filter_data_t *data) -{ - const char *val; // Pointer into value - if ((val = cupsGetOption("Duplex", - data->num_options, data->options)) != NULL) - return (strncasecmp(val, "Duplex", 6) == 0); - else if ((val = cupsGetOption("sides", - data->num_options, data->options)) != NULL) - return (strncasecmp(val, "two-sided-", 10) == 0); - else if ((val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "sides")) != NULL) - return (strncasecmp(val, "two-sided-", 10) == 0); - else if (data->header) - return (data->header->Duplex); - return (0); -} - -static void -info_linef(FILE *s, - const char *key, - const char *valuefmt, ...) -{ - va_list ap; - - va_start(ap, valuefmt); - fprintf(s, "(%s: ", key); - vfprintf(s, valuefmt, ap); - fprintf(s, ") Tj T*\n"); - va_end(ap); -} - -static void -info_line(FILE *s, - const char *key, - const char *value) -{ - info_linef(s, key, "%s", value); -} - -static void -info_line_time(FILE *s, - const char *key, - const char *timestamp) -{ - char buf[40]; - time_t time; - - if (timestamp) - { - time = (time_t)atoll(timestamp); - strftime(buf, sizeof buf, "%c", localtime(&time)); - info_line(s, key, buf); - } - else - info_line(s, key, "unknown"); -} - -static const char * -human_time(const char *timestamp) -{ - time_t time; - int size = sizeof(char) * 40; - char *buf = malloc(size); - strcpy(buf, "unknown"); - - if (timestamp) - { - time = (time_t)atoll(timestamp); - strftime(buf, size, "%c", localtime(&time)); - } - - return (buf); -} - -// -// Add new key & value. -// - -static cf_opt_t * -add_opt(cf_opt_t *in_opt, - const char *key, - const char *val) -{ - if (!key || !val) - return (in_opt); - - if (!strlen(key) || !strlen(val)) - return (in_opt); - - cf_opt_t *entry = malloc(sizeof(cf_opt_t)); - if (!entry) - return (in_opt); - - entry->key = key; - entry->val = val; - entry->next = in_opt; - - return (entry); -} - -// -// Collect all known info about current task. -// Bond PDF form field name with collected info. -// -// Create PDF form's field names according above. -// - -static -cf_opt_t *get_known_opts(cf_filter_data_t *data, - const char *jobid, - const char *user, - const char *jobtitle, - int num_options, - cups_option_t *options) -{ - - cf_opt_t *opt = NULL; - ipp_t *printer_attrs = data->printer_attrs; - ipp_attribute_t *ipp_attr; - char buf[1024]; - const char *value = NULL; - - // Job ID - opt = add_opt(opt, "job-id", jobid); - - // Job title - opt = add_opt(opt, "job-title", jobtitle); - - // Printed by - opt = add_opt(opt, "user", user); - - // Printer name - opt = add_opt(opt, "printer-name", data->printer); - - // Printer info - if ((value = cupsGetOption("printer-info", - num_options, options)) == NULL || !value[0]) - value = getenv("PRINTER_INFO"); - opt = add_opt(opt, "printer-info", value); - - // Time at creation - opt = add_opt(opt, "time-at-creation", - human_time(cupsGetOption("time-at-creation", num_options, - options))); - - // Processing time - opt = add_opt(opt, "time-at-processing", - human_time(cupsGetOption("time-at-processing", num_options, - options))); - - // Billing information - opt = add_opt(opt, "job-billing", - cupsGetOption("job-billing", num_options, options)); - - // Source hostname - opt = add_opt(opt, "job-originating-host-name", - cupsGetOption("job-originating-host-name", num_options, - options)); - - // Banner font - opt = add_opt(opt, "banner-font", - cupsGetOption("banner-font", num_options, options)); - - // Banner font size - opt = add_opt(opt, "banner-font-size", - cupsGetOption("banner-font-size", num_options, options)); - - // Job UUID - opt = add_opt(opt, "job-uuid", - cupsGetOption("job-uuid", num_options, options)); - - // Security context - opt = add_opt(opt, "security-context", - cupsGetOption("security-context", num_options, options)); - - // Security context range part - opt = add_opt(opt, "security-context-range", - cupsGetOption("security-context-range", num_options, options)); - - // Security context current range part - const char *full_range = cupsGetOption("security-context-range", num_options, - options); - if (full_range) - { - size_t cur_size = strcspn(full_range, "-"); - char *cur_range = strndup(full_range, cur_size); - opt = add_opt(opt, "security-context-range-cur", cur_range); - } - - // Security context type part - opt = add_opt(opt, "security-context-type", - cupsGetOption("security-context-type", num_options, options)); - - // Security context role part - opt = add_opt(opt, "security-context-role", - cupsGetOption("security-context-role", num_options, options)); - - // Security context user part - opt = add_opt(opt, "security-context-user", - cupsGetOption("security-context-user", num_options, options)); - -#if 0 - // Driver - opt = add_opt(opt, "driver", "driverless"); - - // Driver version - opt = add_opt(opt, "driver-version", VERSION); -#endif - - // Make and model - int is_fax = 0; - char make[256]; - char *model; - if ((ipp_attr = ippFindAttribute(printer_attrs, "ipp-features-supported", - IPP_TAG_ZERO)) != NULL && - ippContainsString(ipp_attr, "faxout")) - { - ipp_attr = ippFindAttribute(printer_attrs, "printer-uri-supported", - IPP_TAG_ZERO); - if (ipp_attr) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (strcasestr(buf, "faxout")) - is_fax = 1; - } - } - - if ((ipp_attr = ippFindAttribute(printer_attrs, "printer-info", - IPP_TAG_ZERO)) != NULL || - (ipp_attr = ippFindAttribute(printer_attrs, "printer-make-and-model", - IPP_TAG_ZERO)) != NULL) - snprintf(make, sizeof(make), "%s", ippGetString(ipp_attr, 0, NULL)); - else - snprintf(make, sizeof(make), "%s", "Unknown Printer"); - if (!strncasecmp(make, "Hewlett Packard ", 16) || - !strncasecmp(make, "Hewlett-Packard ", 16)) - { - model = make + 16; - snprintf(make, sizeof(make), "%s", "HP"); - } - else if ((model = strchr(make, ' ')) != NULL) - *model++ = '\0'; - else - model = make; - snprintf(buf, sizeof(buf), "%s %s%s", - make, model, (is_fax ? ", Fax" : "")); - char *nickname = buf; - opt = add_opt(opt, "make-and-model", nickname); - - return (opt); -} - -static int -generate_banner_pdf(banner_t *banner, - cf_filter_data_t *data, - const char *jobid, - const char *user, - const char *jobtitle, - int num_options, - cups_option_t *options, - cf_logfunc_t log, - void *ld, - FILE *outputfp) -{ - char *buf; - size_t len; - FILE *s; - cf_pdf_t *doc; - float page_width = 0.0, page_length = 0.0; - float media_limits[4]; - float page_scale; - unsigned copies; - ipp_t *printer_attrs = data->printer_attrs; - ipp_attribute_t *ipp_attr; - char buf2[1024]; - const char *value; -#ifndef HAVE_OPEN_MEMSTREAM - struct stat st; -#endif - - if (!(doc = cfPDFLoadTemplate(banner->template_file))) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "PDF template must exist and contain exactly 1 page: %s", - banner->template_file); - return (1); - } - - memset(media_limits, 0, sizeof(media_limits)); - if (data != NULL && (data->printer_attrs) != NULL) - { - cfGetPageDimensions(data->printer_attrs, data->job_attrs, - num_options, options, - data->header, 0, - &(page_width), &(page_length), - &(media_limits[0]), &(media_limits[1]), - &(media_limits[2]), &(media_limits[3]), - NULL, NULL); - media_limits[2] = page_width - media_limits[2]; - media_limits[3] = page_length - media_limits[3]; - } - - if (cfPDFResizePage(doc, 1, page_width, page_length, &page_scale) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Unable to resize requested PDF page"); - cfPDFFree(doc); - return (1); - } - - if (cfPDFAddType1Font(doc, 1, "Courier") != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Unable to add type1 font to requested PDF page"); - cfPDFFree(doc); - return (1); - } - -#ifdef HAVE_OPEN_MEMSTREAM - s = open_memstream(&buf, &len); -#else - if ((s = tmpfile()) == NULL) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, "cfFilterBannerToPDF: Cannot create temp file: %s\n", strerror(errno)); - cfPDFFree(doc); - return (1); - } -#endif - - if (banner->infos & INFO_IMAGEABLE_AREA) - { - fprintf(s, "q\n"); - fprintf(s, "0 0 0 RG\n"); - fprintf(s, "%f %f %f %f re S\n", media_limits[0] + 1.0, - media_limits[1] + 1.0, - media_limits[2] - media_limits[0] - 2.0, - media_limits[3] - media_limits[1] - 2.0); - fprintf(s, "Q\n"); - } - - fprintf(s, "%f 0 0 %f 0 0 cm\n", page_scale, page_scale); - - fprintf(s, "0 0 0 rg\n"); - fprintf(s, "BT\n"); - fprintf(s, "/bannertopdf-font 14 Tf\n"); - fprintf(s, "83.662 335.0 Td\n"); - fprintf(s, "17 TL\n"); - - if ((banner->infos & INFO_PRINTER_NAME) && - data->printer && data->printer[0] && data->printer[0] != '/') - info_line(s, "Printer", data->printer); - - if ((banner->infos & INFO_PRINTER_INFO) && - (((value = cupsGetOption("printer-info", - num_options, options)) != NULL && value[0]) || - ((value = getenv("PRINTER_INFO")) != NULL && value[0]))) - info_line(s, "Description", value); - - if ((banner->infos & INFO_PRINTER_LOCATION) && - (((value = cupsGetOption("printer-location", - num_options, options)) != NULL && value[0]) || - ((value = getenv("PRINTER_LOCATION")) != NULL && value[0]))) - info_line(s, "Location", value); - - if ((banner->infos & INFO_JOB_ID) && - data->printer && data->printer[0] && data->printer[0] != '/' && - jobid && jobid[0]) - info_linef(s, "Job ID", "%s-%s", data->printer, jobid); - - if ((banner->infos & INFO_JOB_NAME) && jobtitle && jobtitle[0]) - info_line(s, "Job Title", jobtitle); - - if ((banner->infos & INFO_JOB_ORIGINATING_HOST_NAME) && - (value = cupsGetOption("job-originating-host-name", - num_options, options)) != NULL && value[0]) - info_line(s, "Printed from", value); - - if ((banner->infos & INFO_JOB_ORIGINATING_USER_NAME) && user && user[0]) - info_line(s, "Printed by", user); - - if ((banner->infos & INFO_TIME_AT_CREATION) && - (value = - cupsGetOption("time-at-creation", num_options, options)) != NULL && - value[0]) - info_line_time(s, "Created at", value); - - if ((banner->infos & INFO_TIME_AT_PROCESSING) && - (value = - cupsGetOption("time-at-processing", num_options, options)) != NULL && - value[0]) - info_line_time(s, "Printed at", value); - - if ((banner->infos & INFO_JOB_BILLING) && - (value = cupsGetOption("job-billing", num_options, options)) != NULL && - value[0]) - info_line(s, "Billing Information\n", value); - - if ((banner->infos & INFO_JOB_UUID) && - (value = cupsGetOption("job-uuid", num_options, options)) != NULL && - value[0]) - info_line(s, "Job UUID", value); - - if ((banner->infos & INFO_PRINTER_DRIVER_NAME) || - (banner->infos & INFO_PRINTER_MAKE_AND_MODEL)) - { - int is_fax = 0; - char make[256]; - char *model; - if ((ipp_attr = ippFindAttribute(printer_attrs, - "ipp-features-supported", - IPP_TAG_ZERO)) != NULL && - ippContainsString(ipp_attr, "faxout")) - { - ipp_attr = ippFindAttribute(printer_attrs, "printer-uri-supported", - IPP_TAG_ZERO); - if (ipp_attr) - { - ippAttributeString(ipp_attr, buf2, sizeof(buf2)); - if (strcasestr(buf2, "faxout")) - is_fax = 1; - } - } - - if ((ipp_attr = ippFindAttribute(printer_attrs, - "printer-info", - IPP_TAG_ZERO)) != NULL || - (ipp_attr = ippFindAttribute(printer_attrs, - "printer-make-and-model", - IPP_TAG_ZERO)) != NULL) - { - snprintf(make, sizeof(make), "%s", ippGetString(ipp_attr, 0, NULL)); - - if (!strncasecmp(make, "Hewlett Packard ", 16) || - !strncasecmp(make, "Hewlett-Packard ", 16)) - { - model = make + 16; - snprintf(make, sizeof(make), "%s", "HP"); - } - else if ((model = strchr(make, ' ')) != NULL) - *model++ = '\0'; - else - model = make; - snprintf(buf2, sizeof(buf2), "%s %s%s", - make, model, (is_fax ? " (Fax)" : "")); - char *nickname = buf2; - info_line(s, "Make and Model", nickname); - } - } - - if (banner->infos & INFO_IMAGEABLE_AREA) - { - info_linef(s, "Media Limits", "%.2f x %.2f to %.2f x %.2f inches", - media_limits[0] / 72.0, - media_limits[1] / 72.0, - media_limits[2] / 72.0, - media_limits[3] / 72.0); - info_linef(s, "Media Limits", "%.2f x %.2f to %.2f x %.2f cm", - media_limits[0] / 72.0 * 2.54, - media_limits[1] / 72.0 * 2.54, - media_limits[2] / 72.0 * 2.54, - media_limits[3] / 72.0 * 2.54); - } - - fprintf(s, "ET\n"); -#ifndef HAVE_OPEN_MEMSTREAM - fflush(s); - if (fstat(fileno(s), &st) < 0) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, "cfFilterBannerToPDF: Cannot fstat(): %s\n", , strerror(errno)); - return (1); - } - fseek(s, 0L, SEEK_SET); - if ((buf = malloc(st.st_size + 1)) == NULL) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, "cfFilterBannerToPDF: Cannot malloc(): %s\n", , strerror(errno)); - return (1); - } - size_t nbytes = fread(buf, 1, st.st_size, s); - buf[st.st_size] = '\0'; - len = strlen(buf); -#endif // !HAVE_OPEN_MEMSTREAM - fclose(s); - - cf_opt_t *known_opts = get_known_opts(data, - jobid, - user, - jobtitle, - num_options, - options); - - // - // Try to find a PDF form in PDF template and fill it. - // - - if (cfPDFFillForm(doc, known_opts) != 0) - { - // - // Could we fill a PDF form? If no, just add PDF stream. - // - - if (cfPDFPrependStream(doc, 1, buf, len) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Unable to prepend stream to requested PDF page"); - } - } - - copies = get_int_option("number-up", num_options, options, 1); - - if (duplex_marked(data)) - copies *= 2; - - if (copies > 1) - { - if (cfPDFDuplicatePage(doc, 1, copies - 1) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Unable to duplicate requested PDF page"); - } - } - - cfPDFWrite(doc, outputfp); - - cf_opt_t *opt_current = known_opts; - cf_opt_t *opt_next = NULL; - while (opt_current != NULL) - { - opt_next = opt_current->next; - free(opt_current); - opt_current = opt_next; - } - - free(buf); - cfPDFFree(doc); - return (0); -} - -int -cfFilterBannerToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - - // Template/Banner data directory -{ - banner_t *banner; - int num_options = 0; - int ret; - FILE *inputfp; - FILE *outputfp; - int tempfd; - cups_option_t *options = NULL; - char tempfile[1024], buffer[1024]; - size_t bytes; - const char *datadir = (const char *)parameters; - - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - char jobid[50]; - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - // - // Open the input data stream specified by the inputfd... - // - - if ((inputfp = fdopen(inputfd, "rb")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterBannerToPDF: Unable to open input data stream."); - } - return (1); - } - - // - // Copy the input data stream into a temporary file... - // - - if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterBannerToPDF: Unable to copy input file: %s", - strerror(errno)); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterBannerToPDF: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) - bytes = write(tempfd, buffer, bytes); - - if (inputfd) - { - fclose(inputfp); - close(inputfd); - } - close(tempfd); - - // - // Open the output data stream specified by the outputfd... - // - - if ((outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterBannerToPDF: Unable to open output data stream."); - } - - fclose(inputfp); - return (1); - } - - // - // Parse the instructions... - // - - banner = banner_new_from_file(tempfile, &num_options, &options, datadir, - log, ld); - - if (!banner) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, "cfFilterBannerToPDF: Could not read banner file"); - return (1); - } - - sprintf(jobid, "%d", data->job_id); - - // - // Create the banner/test page - // - - ret = generate_banner_pdf(banner, - data, - jobid, - data->job_user, - data->job_title, - num_options, - options, - log, - ld, - outputfp); - - // - // Clean up... - // - - banner_free(banner); - if (options) cupsFreeOptions(num_options, options); - unlink(tempfile); - - return (ret); -} diff --git a/cupsfilters/bitmap.c b/cupsfilters/bitmap.c deleted file mode 100644 index 9c9d5ac7b..000000000 --- a/cupsfilters/bitmap.c +++ /dev/null @@ -1,494 +0,0 @@ -// -// Copyright (c) 2020, Vikrant Malik -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "image.h" -#include -#include - -unsigned int dither1[16][16] = { - { 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}, - {255, 127, 223, 95, 247, 119, 215, 87, 253, 125, 221, 93, 245, 117, 213, 85} -}; - -unsigned int dither2[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} -}; - -unsigned int dither4[4][4] = { - { 0, 8, 2, 10}, - {12, 4, 14, 6}, - { 3, 11, 1, 9}, - {15, 7, 13, 5} -}; - -unsigned char revTable[256] = { - 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, - 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, - 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, - 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, - 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, - 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, - 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, - 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, - 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, - 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, - 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, - 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, - 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, - 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, - 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, - 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff -}; - - -// -// 'cfConvertBits()' - Convert 8 bit raster data to bitspercolor -// raster data using ordered dithering. -// - -unsigned char * // O - Output string -cfConvertBits(unsigned char *src, // I - Input string - unsigned char *dst, // I - Destination string - unsigned int x, // I - Column - unsigned int y, // I - Row - unsigned int cupsNumColors,// I - Number of color components of - // output data - unsigned int bitspercolor) // I - Bitspercolor of output data -{ - // assumed that max number of colors is 4 - unsigned char c = 0; - unsigned short s = 0; - switch (bitspercolor) - { - case 1: - if (cupsNumColors != 1) - { - for (unsigned int i = 0; i < cupsNumColors; i++) - { - c <<= 1; - // ordered dithering - if (src[i] > dither1[y & 0xf][x & 0xf]) - c |= 0x1; - } - *dst = c; - } - else - return (src); // Do not convert bits if both bitspercolor and - // numcolors are 1 - break; - case 2: - for (unsigned int i = 0; i < cupsNumColors; i++) - { - unsigned int d; - c <<= 2; - // ordered dithering - d = src[i] + dither2[y & 0x7][x & 0x7]; - if (d > 255) - d = 255; - c |= d >> 6; - } - *dst = c; - break; - case 4: - for (unsigned int i = 0; i < cupsNumColors; i++) - { - unsigned int d; - s <<= 4; - // ordered dithering - d = src[i] + dither4[y & 0x3][x & 0x3]; - if (d > 255) - d = 255; - s |= d >> 4; - } - if (cupsNumColors < 3) - dst[0] = s; - else - { - dst[0] = s >> 8; - dst[1] = s; - } - break; - case 16: - for (unsigned int i = 0; i < cupsNumColors; i++) - { - dst[i * 2] = src[i]; - dst[i * 2 + 1] = src[i]; - } - break; - case 8: - case 0: - default: - return (src); - break; - } - - return (dst); -} - - -// -// 'cfWritePixel()' - Write a pixel from pixelBuf to dst based on -// color order. -// - -void // O - Exit status -cfWritePixel(unsigned char *dst, // I - Destination string - unsigned int plane, // I - Plane/Band - unsigned int pixeli, // I - Pixel - unsigned char *pixelBuf, // I - Input string - unsigned int cupsNumColors,// I - Number of color components of - // output data - unsigned int bitspercolor, // I - Bitspercolor of output data - cups_order_t colororder) // I - Color Order of output data -{ - unsigned int bo; - unsigned char so; - switch (colororder) - { - case CUPS_ORDER_PLANAR: - case CUPS_ORDER_BANDED: - if (cupsNumColors != 1) - { - switch (bitspercolor) - { - case 1: - bo = pixeli & 0x7; - so = cupsNumColors - plane - 1; - if ((pixeli & 7) == 0) - dst[pixeli / 8] = 0; - dst[pixeli / 8] |= ((*pixelBuf >> so) & 1) << (7 - bo); - break; - case 2: - bo = (pixeli & 0x3) * 2; - so = (cupsNumColors - plane - 1) * 2; - if ((pixeli & 3) == 0) - dst[pixeli / 4] = 0; - dst[pixeli / 4] |= ((*pixelBuf >> so) & 3) << (6 - bo); - break; - case 4: - { - unsigned short c = (pixelBuf[0] << 8) | pixelBuf[1]; - bo = (pixeli & 0x1) * 4; - so = (cupsNumColors - plane - 1) * 4; - if ((pixeli & 1) == 0) - dst[pixeli / 2] = 0; - dst[pixeli / 2] |= ((c >> so) & 0xf) << (4 - bo); - } - break; - case 8: - dst[pixeli] = pixelBuf[plane]; - break; - case 16: - default: - dst[pixeli * 2] = pixelBuf[plane * 2]; - dst[pixeli * 2 +1] = pixelBuf[plane * 2 + 1]; - break; - } - break; - } - case CUPS_ORDER_CHUNKED: - default: - switch (bitspercolor) - { - case 1: - switch (cupsNumColors) - { - case 1: - { - unsigned int bo = pixeli & 0x7; - if ((pixeli & 7) == 0) - dst[pixeli / 8] = 0; - dst[pixeli / 8] |= *pixelBuf << (7 - bo); - } - break; - case 6: - dst[pixeli] = *pixelBuf; - break; - case 3: - case 4: - default: - { - unsigned int qo = (pixeli & 0x1) * 4; - if ((pixeli & 1) == 0) - dst[pixeli / 2] = 0; - dst[pixeli / 2] |= *pixelBuf << (4 - qo); - } - break; - } - break; - case 2: - switch (cupsNumColors) - { - case 1: - { - unsigned int bo = (pixeli & 0x3) * 2; - if ((pixeli & 3) == 0) - dst[pixeli / 4] = 0; - dst[pixeli / 4] |= *pixelBuf << (6 - bo); - } - break; - case 3: - case 4: - default: - dst[pixeli] = *pixelBuf; - break; - } - break; - case 4: - switch (cupsNumColors) - { - case 1: - { - unsigned int bo = (pixeli & 0x1) * 4; - if ((pixeli & 1) == 0) - dst[pixeli / 2] = 0; - dst[pixeli / 2] |= *pixelBuf << (4 - bo); - } - break; - case 3: - case 4: - default: - dst[pixeli * 2] = pixelBuf[0]; - dst[pixeli * 2 + 1] = pixelBuf[1]; - break; - } - break; - case 8: - { - unsigned char *dp = dst + pixeli * cupsNumColors; - for (unsigned int i = 0; i < cupsNumColors; i++) - dp[i] = pixelBuf[i]; - } - break; - case 16: - default: - { - unsigned char *dp = dst + pixeli * cupsNumColors * 2; - for (unsigned int i = 0; i < cupsNumColors * 2; i++) - dp[i] = pixelBuf[i]; - } - break; - } - break; - } -} - - -// -// 'cfReverseOneBitLine()' - Reverse the order of pixels in one line -// of 1-bit raster data. -// - -unsigned char * // O - Output string -cfReverseOneBitLine(unsigned char *src, // I - Input line - unsigned char *dst, // I - Destination string - unsigned int pixels,// I - Number of pixels - unsigned int size) // I - Bytesperline -{ - unsigned char *bp; - unsigned char *dp; - unsigned int npadbits = (size * 8) - pixels; - - if (npadbits == 0) - { - bp = src + size - 1; - dp = dst; - for (unsigned int j = 0; j < size; j++, bp--, dp++) - *dp = revTable[*bp]; - } - else - { - unsigned int pd, d; - unsigned int sw; - - size = (pixels + 7) / 8; - sw = (size * 8) - pixels; - bp = src + size - 1; - dp = dst; - - pd = *bp--; - for (unsigned int j = 1; j < size; j++, bp--, dp++) - { - d = *bp; - *dp = revTable[(((d << 8) | pd) >> sw) & 0xff]; - pd = d; - } - *dp = revTable[(pd >> sw) & 0xff]; - } - return (dst); -} - - -// -// 'cfReverseOneBitLineSwap()' - Reverse the order of pixels in one -// line of 1-bit raster data and invert -// the colors. -// - -unsigned char * // O - Output string -cfReverseOneBitLineSwap(unsigned char *src, // I - Input line - unsigned char *dst, // I - Destination string - unsigned int pixels,// I - Number of pixels - unsigned int size) // I - Bytesperline -{ - unsigned char *bp; - unsigned char *dp; - unsigned int npadbits = (size * 8) - pixels; - - if (npadbits == 0) - { - bp = src + size - 1; - dp = dst; - for (unsigned int j = 0; j < size; j++, bp--, dp++) - *dp = revTable[(unsigned char)(~*bp)]; - } - else - { - unsigned int pd, d; - unsigned int sw; - - size = (pixels + 7) / 8; - sw = (size * 8) - pixels; - bp = src + size - 1; - dp = dst; - - pd = *bp--; - for (unsigned int j = 1; j < size; j++, bp--, dp++) - { - d = *bp; - *dp = ~revTable[(((d << 8) | pd) >> sw) & 0xff]; - pd = d; - } - *dp = ~revTable[(pd >> sw) & 0xff]; - } - return (dst); -} - - -// -// 'cfOneBitLine()' - Convert one line of 8-bit raster data to 1-bit -// raster data using ordered dithering. -// - -void // O - Output line -cfOneBitLine(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination line - unsigned int width, // I - Width of raster image in pixels - unsigned int row, // I - Current Row - int bi_level) // I - Bi-level option -{ - // If bi_level is true, do threshold dithering to produce black and - // white output else, do ordered dithering. - unsigned char t = 0; - unsigned int threshold = 0; - for (unsigned int w = 0; w < width; w += 8) - { - t = 0; - for (int k = 0; k < 8; k++) - { - t <<= 1; - if (w + k < width) - { - if (bi_level) - threshold = 128; - else - threshold = dither1[row & 0xf][(w + k) & 0xf]; - if (*src > threshold) - t |= 0x1; - src += 1; - } - } - *dst = t; - dst += 1; - } -} - - -// -// 'cfOneBitToGrayLine()' - Convert one line of 1-bit raster data to -// 8-bit raster data. -// - -void // O - Output line -cfOneBitToGrayLine(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination line - unsigned int width) // I - Width of raster image in - // pixels -{ - unsigned char mask = 0x80; - for (unsigned int w = 0; w < width; w += 1) - { - if (mask == 0) - { - mask = 0x80; - src ++; - } - *dst = (*src & mask) ? 0xff : 0; - mask >>= 1; - dst ++; - } -} - - -// -// 'cfRGB8toKCMYcm()' - Convert one pixel of 8-bit RGB data to KCMYcm -// raster data. -// - -unsigned char -*cfRGB8toKCMYcm(unsigned char *src, - unsigned char *dst, - unsigned int x, - unsigned int y) -{ - unsigned char cmyk[4]; - unsigned char c; - unsigned char d; - - cfImageRGBToCMYK(src, cmyk, 1); - c = 0; - d = dither1[y & 0xf][x & 0xf]; - // K - if (cmyk[3] > d) - c |= 0x20; - // C - if (cmyk[0] > d) - c |= 0x10; - // M - if (cmyk[1] > d) - c |= 0x08; - // Y - if (cmyk[2] > d) - c |= 0x04; - if (c == 0x18) // Blue - c = 0x11; // cyan + light magenta - else if (c == 0x14) // Green - c = 0x06; // light cyan + yellow - *dst = c; - return (dst); -} diff --git a/cupsfilters/bitmap.h b/cupsfilters/bitmap.h deleted file mode 100644 index 85c0789dd..000000000 --- a/cupsfilters/bitmap.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) 2020, Vikrant Malik -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_BITMAP_H_ -# define _CUPS_FILTERS_BITMAP_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - -#include - -unsigned char *cfConvertBits(unsigned char *src, unsigned char *dst, - unsigned int x, unsigned int y, - unsigned int cupsNumColors,unsigned int bits); -void cfWritePixel(unsigned char *dst, unsigned int plane, unsigned int pixeli, - unsigned char *pixelBuf, unsigned int cupsNumColors, - unsigned int bits, cups_order_t colororder); -unsigned char *cfReverseOneBitLine(unsigned char *src, unsigned char *dst, - unsigned int pixels, unsigned int size); -unsigned char *cfReverseOneBitLineSwap(unsigned char *src, unsigned char *dst, - unsigned int pixels, unsigned int size); -void *cfOneBitLine(unsigned char *src, unsigned char *dst, unsigned int width, - unsigned int row, int bi_level); -void *cfOneBitToGrayLine(unsigned char *src, unsigned char *dst, - unsigned int width); -unsigned char *cfRGB8toKCMYcm(unsigned char *src, unsigned char *dst, - unsigned int x, unsigned int y); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_BITMAP_H_ diff --git a/cupsfilters/catalog.c b/cupsfilters/catalog.c deleted file mode 100644 index 0af0daf25..000000000 --- a/cupsfilters/catalog.c +++ /dev/null @@ -1,671 +0,0 @@ -// -// IPP attribute/option string catalog manager for libcupsfilters. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers. -// - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -int // O - 1 on success, 0 on failure -cfGetURI(const char *url, // I - URL to get - char *name, // I - Temporary filename - size_t namesize) // I - Size of temporary filename - // buffer -{ - http_t *http = NULL; - char scheme[32], // URL scheme - userpass[256], // URL username:password - host[256], // URL host - resource[256]; // URL resource - int port; // URL port - http_encryption_t encryption; // Type of encryption to use - http_status_t status; // Status of GET request - int fd; // Temporary file - - - if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), - userpass, sizeof(userpass), host, sizeof(host), &port, - resource, sizeof(resource)) < HTTP_URI_STATUS_OK) - return (0); - - if (port == 443 || !strcmp(scheme, "https")) - encryption = HTTP_ENCRYPTION_ALWAYS; - else - encryption = HTTP_ENCRYPTION_IF_REQUESTED; - - http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL); - - if (!http) - return (0); - - if ((fd = cupsTempFd(name, (int)namesize)) < 0) - return (0); - - status = cupsGetFd(http, resource, fd); - - close(fd); - httpClose(http); - - if (status != HTTP_STATUS_OK) - { - unlink(name); - *name = '\0'; - return (0); - } - - return (1); -} - - -// -// 'cfCatalogFind()' - Find a CUPS message catalog file containing -// human-readable standard option and choice names -// for IPP printers -// - -const char * -cfCatalogSearchDir(const char *dirname) -{ - const char *catalog = NULL, *c1, *c2; - cups_dir_t *dir = NULL, *subdir; - cups_dentry_t *subdirentry, *catalogentry; - char subdirpath[1024], catalogpath[2048], lang[8]; - int i; - - if (dirname == NULL) - return (NULL); - - // Check first whether we have an English file and prefer this - snprintf(catalogpath, sizeof(catalogpath), "%s/en/cups_en.po", dirname); - if (access(catalogpath, R_OK) == 0) - { - // Found - catalog = strdup(catalogpath); - return (catalog); - } - - if ((dir = cupsDirOpen(dirname)) == NULL) - return (NULL); - - while ((subdirentry = cupsDirRead(dir)) != NULL) - { - // Do we actually have a subdir? - if (!S_ISDIR(subdirentry->fileinfo.st_mode)) - continue; - // Check format of subdir name - c1 = subdirentry->filename; - if (c1[0] < 'a' || c1[0] > 'z' || c1[1] < 'a' || c1[1] > 'z') - continue; - if (c1[2] >= 'a' && c1[2] <= 'z') - i = 3; - else - i = 2; - if (c1[i] == '_') - { - i ++; - if (c1[i] < 'A' || c1[i] > 'Z' || c1[i + 1] < 'A' || c1[i + 1] > 'Z') - continue; - i += 2; - if (c1[i] >= 'A' && c1[i] <= 'Z') - i ++; - } - if (c1[i] != '\0' && c1[i] != '@') - continue; - strncpy(lang, c1, i); - lang[i] = '\0'; - snprintf(subdirpath, sizeof(subdirpath), "%s/%s", dirname, c1); - if ((subdir = cupsDirOpen(subdirpath)) != NULL) - { - while ((catalogentry = cupsDirRead(subdir)) != NULL) - { - // Do we actually have a regular file? - if (!S_ISREG(catalogentry->fileinfo.st_mode)) - continue; - // Check format of catalog name - c2 = catalogentry->filename; - if (strlen(c2) < 10 || strncmp(c2, "cups_", 5) != 0 || - strncmp(c2 + 5, lang, i) != 0 || - strcmp(c2 + strlen(c2) - 3, ".po")) - continue; - // Is catalog readable ? - snprintf(catalogpath, sizeof(catalogpath), "%s/%s", subdirpath, c2); - if (access(catalogpath, R_OK) != 0) - continue; - // Found - catalog = strdup(catalogpath); - break; - } - cupsDirClose(subdir); - subdir = NULL; - if (catalog != NULL) - break; - } - } - - cupsDirClose(dir); - return (catalog); -} - - -const char * -cfCatalogFind(const char *preferreddir) -{ - const char *catalog = NULL, *c; - char buf[1024]; - - // Directory supplied by calling program, from config file, - // environment variable, ... - if ((catalog = cfCatalogSearchDir(preferreddir)) != NULL) - goto found; - - // Directory supplied by environment variable CUPS_LOCALEDIR - if ((catalog = cfCatalogSearchDir(getenv("CUPS_LOCALEDIR"))) != NULL) - goto found; - - // Determine CUPS datadir (usually /usr/share/cups) - if ((c = getenv("CUPS_DATADIR")) == NULL) - c = CUPS_DATADIR; - - // Search /usr/share/cups/locale/ (location which - // Debian/Ubuntu package of CUPS is using) - snprintf(buf, sizeof(buf), "%s/locale", c); - if ((catalog = cfCatalogSearchDir(buf)) != NULL) - goto found; - - // Search /usr/(local/)share/locale/ (standard location - // which CUPS is using on Linux) - snprintf(buf, sizeof(buf), "%s/../locale", c); - if ((catalog = cfCatalogSearchDir(buf)) != NULL) - goto found; - - // Search /usr/(local/)lib/locale/ (standard location - // which CUPS is using on many non-Linux systems) - snprintf(buf, sizeof(buf), "%s/../../lib/locale", c); - if ((catalog = cfCatalogSearchDir(buf)) != NULL) - goto found; - - found: - return (catalog); -} - - -static int -compare_choices(void *a, - void *b, - void *user_data) -{ - return (strcasecmp(((catalog_choice_strings_t *)a)->name, - ((catalog_choice_strings_t *)b)->name)); -} - - -static int -compare_options(void *a, - void *b, - void *user_data) -{ - return (strcasecmp(((catalog_opt_strings_t *)a)->name, - ((catalog_opt_strings_t *)b)->name)); -} - - -void -cfCatalogFreeChoiceStrings(void* entry, - void* user_data) -{ - catalog_choice_strings_t *entry_rec = (catalog_choice_strings_t *)entry; - - if (entry_rec) - { - if (entry_rec->name) free(entry_rec->name); - if (entry_rec->human_readable) free(entry_rec->human_readable); - free(entry_rec); - } -} - - -void -cfCatalogFreeOptionStrings(void* entry, - void* user_data) -{ - catalog_opt_strings_t *entry_rec = (catalog_opt_strings_t *)entry; - - if (entry_rec) - { - if (entry_rec->name) free(entry_rec->name); - if (entry_rec->human_readable) free(entry_rec->human_readable); - if (entry_rec->choices) cupsArrayDelete(entry_rec->choices); - free(entry_rec); - } -} - - -cups_array_t * -cfCatalogOptionArrayNew() -{ - return (cupsArrayNew3(compare_options, NULL, NULL, 0, - NULL, cfCatalogFreeOptionStrings)); -} - - -catalog_opt_strings_t * -cfCatalogFindOption(cups_array_t *options, - char *name) -{ - catalog_opt_strings_t opt; - - if (!name || !options) - return (NULL); - - opt.name = name; - return (cupsArrayFind(options, &opt)); -} - - -catalog_choice_strings_t * -cfCatalogFindChoice(cups_array_t *choices, - char *name) -{ - catalog_choice_strings_t choice; - - if (!name || !choices) - return (NULL); - - choice.name = name; - return (cupsArrayFind(choices, &choice)); -} - - -catalog_opt_strings_t * -cfCatalogAddOption(char *name, - char *human_readable, - cups_array_t *options) -{ - catalog_opt_strings_t *opt = NULL; - - if (!name || !options) - return (NULL); - - if ((opt = cfCatalogFindOption(options, name)) == NULL) - { - opt = calloc(1, sizeof(catalog_opt_strings_t)); - if (!opt) - return (NULL); - opt->human_readable = NULL; - opt->choices = cupsArrayNew3(compare_choices, NULL, NULL, 0, - NULL, cfCatalogFreeChoiceStrings); - if (!opt->choices) - { - free(opt); - return (NULL); - } - opt->name = strdup(name); - if (!cupsArrayAdd(options, opt)) - { - cfCatalogFreeOptionStrings(opt, NULL); - return (NULL); - } - } - - if (human_readable) - opt->human_readable = strdup(human_readable); - - return (opt); -} - - -catalog_choice_strings_t * -cfCatalogAddChoice(char *name, - char *human_readable, - char *opt_name, - cups_array_t *options) -{ - catalog_choice_strings_t *choice = NULL; - catalog_opt_strings_t *opt; - - if (!name || !human_readable || !opt_name || !options) - return (NULL); - - opt = cfCatalogAddOption(opt_name, NULL, options); - if (!opt) - return (NULL); - - if ((choice = cfCatalogFindChoice(opt->choices, name)) == NULL) - { - choice = calloc(1, sizeof(catalog_choice_strings_t)); - if (!choice) - return (NULL); - choice->human_readable = NULL; - choice->name = strdup(name); - if (!cupsArrayAdd(opt->choices, choice)) - { - cfCatalogFreeChoiceStrings(choice, NULL); - return (NULL); - } - } - - if (human_readable) - choice->human_readable = strdup(human_readable); - - return (choice); - -} - - -char * -cfCatalogLookUpOption(char *name, - cups_array_t *options, - cups_array_t *printer_options) -{ - catalog_opt_strings_t *opt = NULL; - - if (!name || !options) - return (NULL); - - if (printer_options && - (opt = cfCatalogFindOption(printer_options, name)) != NULL) - return (opt->human_readable); - if ((opt = cfCatalogFindOption(options, name)) != NULL) - return (opt->human_readable); - else - return (NULL); -} - - -char * -cfCatalogLookUpChoice(char *name, - char *opt_name, - cups_array_t *options, - cups_array_t *printer_options) -{ - catalog_opt_strings_t *opt = NULL; - catalog_choice_strings_t *choice = NULL; - - if (!name || !opt_name || !options) - return (NULL); - - if (printer_options && - (opt = cfCatalogFindOption(printer_options, opt_name)) != NULL && - (choice = cfCatalogFindChoice(opt->choices, name)) != NULL) - return (choice->human_readable); - else if ((opt = cfCatalogFindOption(options, opt_name)) != NULL && - (choice = cfCatalogFindChoice(opt->choices, name)) != NULL) - return (choice->human_readable); - else - return (NULL); -} - - -void -cfCatalogLoad(const char *location, - cups_array_t *options) -{ - char tmpfile[1024]; - const char *filename = NULL; - struct stat statbuf; - cups_file_t *fp; - char line[65536]; - char *ptr, *start, *start2, *end, *end2, *sep; - char *opt_name = NULL, *choice_name = NULL, - *human_readable = NULL; - int part = -1; // -1: before first "msgid" or invalid - // line - // 0: "msgid" - // 1: "msgstr" - // 2: "..." = "..." - // 10: EOF, save last entry - int digit; - int found_in_catalog = 0; - - if (location == NULL || (strncasecmp(location, "http:", 5) && - strncasecmp(location, "https:", 6))) - { - if (location == NULL || - (stat(location, &statbuf) == 0 && - S_ISDIR(statbuf.st_mode))) // directory? - { - filename = cfCatalogFind(location); - if (filename) - found_in_catalog = 1; - } - else - filename = location; - } - else - { - if (cfGetURI(location, tmpfile, sizeof(tmpfile))) - filename = tmpfile; - } - if (!filename) - return; - - if ((fp = cupsFileOpen(filename, "r")) == NULL) - { - if (filename == tmpfile) - unlink(filename); - return; - } - - while (cupsFileGets(fp, line, sizeof(line)) || (part = 10)) - { - // Find a pair of quotes delimiting a string in each line - // and optional "msgid" or "msgstr" keywords, or a - // "..." = "..." pair. Skip comments ('#') and empty lines. - if (part < 10) - { - ptr = line; - while (isspace(*ptr)) ptr ++; - if (*ptr == '#' || *ptr == '\0') continue; - if ((start = strchr(ptr, '\"')) == NULL) continue; - if ((end = strrchr(ptr, '\"')) == start) continue; - if (*(end - 1) == '\\') continue; - start2 = NULL; - end2 = NULL; - if (start > ptr) - { - if (*(start - 1) == '\\') continue; - if (strncasecmp(ptr, "msgid", 5) == 0) part = 0; - if (strncasecmp(ptr, "msgstr", 6) == 0) part = 1; - } - else - { - start2 = ptr; - while ((start2 = strchr(start2 + 1, '\"')) < end && - *(start2 - 1) == '\\'); - if (start2 < end) - { - // Line with "..." = "..." of text/strings format - end2 = end; - end = start2; - start2 ++; - while (isspace(*start2)) start2 ++; - if (*start2 != '=') continue; - start2 ++; - while (isspace(*start2)) start2 ++; - if (*start2 != '\"') continue; - start2 ++; - *end2 = '\0'; - part = 2; - } - else - // Continuation line in message catalog file - start2 = NULL; - } - start ++; - *end = '\0'; - } - // Read out the strings between the quotes and save entries - if (part == 0 || part == 2 || part == 10) - { - // Save previous attribute - if (human_readable) - { - if (opt_name) - { - if (choice_name) - { - cfCatalogAddChoice(choice_name, human_readable, - opt_name, options); - free(choice_name); - } - else - cfCatalogAddOption(opt_name, human_readable, options); - free(opt_name); - } - free(human_readable); - opt_name = NULL; - choice_name = NULL; - human_readable = NULL; - } - // Stop the loop after saving the last entry - if (part == 10) - break; - // IPP attribute has to be defined with a single msgid line, - // no continuation lines - if (opt_name) - { - free (opt_name); - opt_name = NULL; - if (choice_name) - { - free (choice_name); - choice_name = NULL; - } - part = -1; - continue; - } - // No continuation line in text/strings format - if (part == 2 && (start2 == NULL || end2 == NULL)) - { - part = -1; - continue; - } - // Check line if it is a valid IPP attribute: - // No spaces, only lowercase letters, digits, '-', '_', - // "option" or "option.choice" - for (ptr = start, sep = NULL; ptr < end; ptr ++) - if (*ptr == '.') // Separator between option and choice - { - if (!sep) // Only the first '.' counts - { - sep = ptr + 1; - *ptr = '\0'; - } - } - else if (!((*ptr >= 'a' && *ptr <= 'z') || - (*ptr >= '0' && *ptr <= '9') || - *ptr == '-' || *ptr == '_')) - break; - if (ptr < end) // Illegal character found - { - part = -1; - continue; - } - if (strlen(start) > 0) // Option name found - opt_name = strdup(start); - else // Empty option name - { - part = -1; - continue; - } - if (sep && strlen(sep) > 0) // Choice name found - choice_name = strdup(sep); - else // Empty choice name - choice_name = NULL; - if (part == 2) // Human-readable string in the same line - { - start = start2; - end = end2; - } - } - if (part == 1 || part == 2) - { - // msgid was not for an IPP attribute, ignore this msgstr - if (!opt_name) continue; - // Empty string - if (start == end) continue; - // Unquote string - ptr = start; - end = start; - while (*ptr) - { - if (*ptr == '\\') - { - ptr ++; - if (isdigit(*ptr)) - { - digit = 0; - *end = 0; - while (isdigit(*ptr) && digit < 3) - { - *end = *end * 8 + *ptr - '0'; - digit ++; - ptr ++; - } - end ++; - } - else - { - if (*ptr == 'n') - *end ++ = '\n'; - else if (*ptr == 'r') - *end ++ = '\r'; - else if (*ptr == 't') - *end ++ = '\t'; - else - *end ++ = *ptr; - ptr ++; - } - } - else - *end ++ = *ptr ++; - } - *end = '\0'; - // Did the unquoting make the string empty? - if (strlen(start) == 0) continue; - // Add the string to our human-readable string - if (human_readable) // Continuation line - { - human_readable = realloc(human_readable, - sizeof(char) * - (strlen(human_readable) + - strlen(start) + 2)); - ptr = human_readable + strlen(human_readable); - *ptr = ' '; - strncpy(ptr + 1, start, - sizeof(human_readable) - (ptr - human_readable) - 1); - } - else // First line - human_readable = strdup(start); - } - } - cupsFileClose(fp); - if (choice_name != NULL) - free(choice_name); - if (opt_name != NULL) - free(opt_name); - if (filename == tmpfile) - unlink(filename); - if (found_in_catalog) - free((char *)filename); -} diff --git a/cupsfilters/catalog.h b/cupsfilters/catalog.h deleted file mode 100644 index 5c3f0ddf9..000000000 --- a/cupsfilters/catalog.h +++ /dev/null @@ -1,121 +0,0 @@ -// -// IPP attribute/option string catalog manager for libcupsfilters. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Human-readable strings for printer or scanner option/attribute -// names and also the names for the possible choices for each option -// are essentially important for user-friendly print dialogs and other -// user interfaces. Also translations into the user-chosen user -// interface language should be used. -// -// To allow for human-readable names and translations/localizations we -// use the standard set of human-readable strings from the PWG: -// -// https://ftp.pwg.org/pub/pwg/ipp/examples/ipp.pot -// https://ftp.pwg.org/pub/pwg/ipp/examples/ipp.strings -// -// Translations are supposed to go here (but none are available yet): -// -// https://github.com/istopwg/ippregistry/tree/master/localizations -// -// These standard strings are also part of CUPS' translation files: -// -// https://github.com/OpenPrinting/cups/tree/master/locale -// -// Here translations take actually place as part of the translations -// of CUPS itself. -// -// We take the files from the CUPS installed into the system. It is -// not complicated to check the user language in the printer's IPP -// response via the attributes-natural-language attribute and then -// request an appropriate language version of the files if -// available. The printer-specific strings are downloaded from the -// printer following the URI in the printer-strings-uri attribute and -// are in the selected language. -// -// See also: -// -// https://lists.linuxfoundation.org/pipermail/printing-architecture/2021/003992.html -// https://lists.linuxfoundation.org/pipermail/printing-architecture/2021/003995.html -// - - -#ifndef _CUPS_FILTERS_CATALOG_H_ -# define _CUPS_FILTERS_CATALOG_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - -// -// Include necessary headers... -// - -#include - -#include -#include -#include -#include -#include - -#if defined(WIN32) || defined(__EMX__) -# include -#else -# include -# include -#endif // WIN32 || __EMX__ - -#include -#include -#include - -// Data structure for IPP choice name and human-readable string -typedef struct catalog_choice_strings_s { - char *name, - *human_readable; -} catalog_choice_strings_t; - -// Data structure for IPP option name, human-readable string, and choice list -typedef struct catalog_opt_strings_s { - char *name, - *human_readable; - cups_array_t *choices; -} catalog_opt_strings_t; - - -// -// Prototypes... -// - -int cfGetURI(const char *url, char *name, size_t namesize); -const char *cfCatalogSearchDir(const char *dirname); -const char *cfCatalogFind(const char *preferreddir); -void cfCatalogFreeChoiceStrings(void* entry, void* user_data); -void cfCatalogFreeOptionStrings(void* entry, void* user_data); -cups_array_t *cfCatalogOptionArrayNew(); -catalog_opt_strings_t *cfCatalogFindOption(cups_array_t *options, char *name); -catalog_choice_strings_t *cfCatalogFindChoice(cups_array_t *choices, - char *name); -catalog_opt_strings_t *cfCatalogAddOption(char *name, char *human_readable, - cups_array_t *options); -catalog_choice_strings_t *cfCatalogAddChoice(char *name, char *human_readable, - char *opt_name, - cups_array_t *options); -char *cfCatalogLookUpOption(char *name, cups_array_t *options, - cups_array_t *printer_options); -char *cfCatalogLookUpChoice(char *name, char *opt_name, - cups_array_t *options, - cups_array_t *printer_options); -void cfCatalogLoad(const char *location, cups_array_t *options); - - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_CATALOG_H_ diff --git a/cupsfilters/check.c b/cupsfilters/check.c deleted file mode 100644 index 72322462f..000000000 --- a/cupsfilters/check.c +++ /dev/null @@ -1,101 +0,0 @@ -// -// Byte checking routines for libcupsfilters. -// -// Copyright 2007 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfCheckBytes() - Check to see if all bytes are zero. -// cfCheckValue() - Check to see if all bytes match the given value. - - -// -// Include necessary headers. -// - -#include "driver.h" - - -// -// 'cfCheckBytes()' - Check to see if all bytes are zero. -// - -int // O - 1 if they match -cfCheckBytes(const unsigned char *bytes, // I - Bytes to check - int length) // I - Number of bytes to check -{ - while (length > 7) - { - if (*bytes++) - return (0); - if (*bytes++) - return (0); - if (*bytes++) - return (0); - if (*bytes++) - return (0); - if (*bytes++) - return (0); - if (*bytes++) - return (0); - if (*bytes++) - return (0); - if (*bytes++) - return (0); - - length -= 8; - } - - while (length > 0) - if (*bytes++) - return (0); - else - length --; - - return (1); -} - - -// -// 'cfCheckValue()' - Check to see if all bytes match the given value. -// - -int // O - 1 if they match -cfCheckValue(const unsigned char *bytes, // I - Bytes to check - int length, // I - Number of bytes to check - const unsigned char value) // I - Value to check -{ - while (length > 7) - { - if (*bytes++ != value) - return (0); - if (*bytes++ != value) - return (0); - if (*bytes++ != value) - return (0); - if (*bytes++ != value) - return (0); - if (*bytes++ != value) - return (0); - if (*bytes++ != value) - return (0); - if (*bytes++ != value) - return (0); - if (*bytes++ != value) - return (0); - - length -= 8; - } - - while (length > 0) - if (*bytes++ != value) - return (0); - else - length --; - - return (1); -} diff --git a/cupsfilters/cmyk.c b/cupsfilters/cmyk.c deleted file mode 100644 index f9da7ecd3..000000000 --- a/cupsfilters/cmyk.c +++ /dev/null @@ -1,1392 +0,0 @@ -// -// CMYK color separation code for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfCMYKDelete() - Delete a color separation. -// cfCMYKDoBlack() - Do a black separation... -// cfCMYKDoCMYK() - Do a CMYK separation... -// cfCMYKDoGray() - Do a grayscale separation... -// cfCMYKDoRGB() - Do an sRGB separation... -// cfCMYKNew() - Create a new CMYK color separation. -// cfCMYKSetBlack() - Set the transition range for CMY to black. -// cfCMYKSetCurve() - Set a color transform curve using points. -// cfCMYKSetGamma() - Set a color transform curve using gamma and -// density. -// cfCMYKSetInkLimit() - Set the limit on the amount of ink. -// cfCMYKSetLtDk() - Set light/dark ink transforms. -// - -// -// Include necessary headers. -// - -#include -#include "driver.h" -#include -#include - - -// -// 'cfCMYKDelete()' - Delete a color separation. -// - -void -cfCMYKDelete(cf_cmyk_t *cmyk) // I - Color separation -{ - // - // Range check input... - // - - if (cmyk == NULL) - return; - - // - // Free memory used... - // - - free(cmyk->channels[0]); - free(cmyk); -} - - -// -// 'cfCMYKDoBlack()' - Do a black separation... -// - -void -cfCMYKDoBlack(const cf_cmyk_t *cmyk, - // I - Color separation - const unsigned char *input, - // I - Input grayscale pixels - short *output, - // O - Output Device-N pixels - int num_pixels) - // I - Number of pixels -{ - int k; // Current black value - const short **channels; // Copy of channel LUTs - int ink, // Amount of ink - ink_limit; // Ink limit from separation - - - // - // Range check input... - // - - if (cmyk == NULL || input == NULL || output == NULL || num_pixels <= 0) - return; - - // - // Loop through it all... - // - - channels = (const short **)cmyk->channels; - ink_limit = cmyk->ink_limit; - - switch (cmyk->num_channels) - { - case 1 : // Black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = *input++; - *output++ = channels[0][k]; - - num_pixels --; - } - break; - - case 2 : // Black, light black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = *input++; - output[0] = channels[0][k]; - output[1] = channels[1][k]; - - if (ink_limit) - { - ink = output[0] + output[1]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - } - } - - output += 2; - num_pixels --; - } - break; - - case 3 : // CMY - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = *input++; - output[0] = channels[0][k]; - output[1] = channels[1][k]; - output[2] = channels[2][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - } - } - - output += 3; - num_pixels --; - } - break; - - case 4 : // CMYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = *input++; - *output++ = 0; - *output++ = 0; - *output++ = 0; - *output++ = channels[3][k]; - - num_pixels --; - } - break; - - case 6 : // CcMmYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = *input++; - *output++ = 0; - *output++ = 0; - *output++ = 0; - *output++ = 0; - *output++ = 0; - *output++ = channels[5][k]; - - num_pixels --; - } - break; - - case 7 : // CcMmYKk - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = *input++; - output[0] = 0; - output[1] = 0; - output[2] = 0; - output[3] = 0; - output[4] = 0; - output[5] = channels[5][k]; - output[6] = channels[6][k]; - - if (ink_limit) - { - ink = output[5] + output[6]; - - if (ink > ink_limit) - { - output[5] = ink_limit * output[5] / ink; - output[6] = ink_limit * output[6] / ink; - } - } - - output += 7; - num_pixels --; - } - break; - } -} - - -// -// 'cfCMYKDoCMYK()' - Do a CMYK separation... -// - -void -cfCMYKDoCMYK(const cf_cmyk_t *cmyk, - // I - Color separation - const unsigned char *input, - // I - Input grayscale pixels - short *output, - // O - Output Device-N pixels - int num_pixels) - // I - Number of pixels -{ - int c, // Current cyan value - m, // Current magenta value - y, // Current yellow value - k; // Current black value - const short **channels; // Copy of channel LUTs - int ink, // Amount of ink - ink_limit; // Ink limit from separation - - - // - // Range check input... - // - - if (cmyk == NULL || input == NULL || output == NULL || num_pixels <= 0) - return; - - // - // Loop through it all... - // - - channels = (const short **)cmyk->channels; - ink_limit = cmyk->ink_limit; - - switch (cmyk->num_channels) - { - case 1 : // Black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = *input++; - m = *input++; - y = *input++; - k = *input++ + (c * 31 + m * 61 + y * 8) / 100; - - if (k < 255) - *output++ = channels[0][k]; - else - *output++ = channels[0][255]; - - num_pixels --; - } - break; - - case 2 : // Black, light black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = *input++; - m = *input++; - y = *input++; - k = *input++ + (c * 31 + m * 61 + y * 8) / 100; - - if (k < 255) - { - output[0] = channels[0][k]; - output[1] = channels[1][k]; - } - else - { - output[0] = channels[0][255]; - output[1] = channels[1][255]; - } - - if (ink_limit) - { - ink = output[0] + output[1]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - } - } - - output += 2; - num_pixels --; - } - break; - - case 3 : // CMY - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = *input++; - m = *input++; - y = *input++; - k = *input++; - c += k; - m += k; - y += k; - - if (c < 255) - output[0] = channels[0][c]; - else - output[0] = channels[0][255]; - - if (m < 255) - output[1] = channels[1][m]; - else - output[1] = channels[1][255]; - - if (y < 255) - output[2] = channels[2][y]; - else - output[2] = channels[2][255]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - } - } - - output += 3; - num_pixels --; - } - break; - - case 4 : // CMYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = *input++; - m = *input++; - y = *input++; - k = *input++; - - output[0] = channels[0][c]; - output[1] = channels[1][m]; - output[2] = channels[2][y]; - output[3] = channels[3][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - } - } - - output += 4; - num_pixels --; - } - break; - - case 6 : // CcMmYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = *input++; - m = *input++; - y = *input++; - k = *input++; - - output[0] = channels[0][c]; - output[1] = channels[1][c]; - output[2] = channels[2][m]; - output[3] = channels[3][m]; - output[4] = channels[4][y]; - output[5] = channels[5][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3] + - output[4] + output[5]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - output[4] = ink_limit * output[4] / ink; - output[5] = ink_limit * output[5] / ink; - } - } - - output += 6; - num_pixels --; - } - break; - - case 7 : // CcMmYKk - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = *input++; - m = *input++; - y = *input++; - k = *input++; - - output[0] = channels[0][c]; - output[1] = channels[1][c]; - output[2] = channels[2][m]; - output[3] = channels[3][m]; - output[4] = channels[4][y]; - output[5] = channels[5][k]; - output[6] = channels[6][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3] + - output[4] + output[5] + output[6]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - output[4] = ink_limit * output[4] / ink; - output[5] = ink_limit * output[5] / ink; - output[6] = ink_limit * output[6] / ink; - } - } - - output += 7; - num_pixels --; - } - break; - } -} - - -// -// 'cfCMYKDoGray()' - Do a grayscale separation... -// - -void -cfCMYKDoGray(const cf_cmyk_t *cmyk, - // I - Color separation - const unsigned char *input, - // I - Input grayscale pixels - short *output, - // O - Output Device-N pixels - int num_pixels) - // I - Number of pixels -{ - int k, // Current black value - kc; // Current black color value - const short **channels; // Copy of channel LUTs - int ink, // Amount of ink - ink_limit; // Ink limit from separation - - - // - // Range check input... - // - - if (cmyk == NULL || input == NULL || output == NULL || num_pixels <= 0) - return; - - // - // Loop through it all... - // - - channels = (const short **)cmyk->channels; - ink_limit = cmyk->ink_limit; - - switch (cmyk->num_channels) - { - case 1 : // Black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = cf_scmy_lut[*input++]; - *output++ = channels[0][k]; - - num_pixels --; - } - break; - - case 2 : // Black, light black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = cf_scmy_lut[*input++]; - output[0] = channels[0][k]; - output[1] = channels[1][k]; - - if (ink_limit) - { - ink = output[0] + output[1]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - } - } - - output += 2; - num_pixels --; - } - break; - - case 3 : // CMY - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = cf_scmy_lut[*input++]; - output[0] = channels[0][k]; - output[1] = channels[1][k]; - output[2] = channels[2][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - } - } - - output += 3; - num_pixels --; - } - break; - - case 4 : // CMYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = cf_scmy_lut[*input++]; - kc = cmyk->color_lut[k]; - k = cmyk->black_lut[k]; - output[0] = channels[0][kc]; - output[1] = channels[1][kc]; - output[2] = channels[2][kc]; - output[3] = channels[3][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - } - } - - output += 4; - num_pixels --; - } - break; - - case 6 : // CcMmYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = cf_scmy_lut[*input++]; - kc = cmyk->color_lut[k]; - k = cmyk->black_lut[k]; - output[0] = channels[0][kc]; - output[1] = channels[1][kc]; - output[2] = channels[2][kc]; - output[3] = channels[3][kc]; - output[4] = channels[4][kc]; - output[5] = channels[5][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3] + - output[4] + output[5]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - output[4] = ink_limit * output[4] / ink; - output[5] = ink_limit * output[5] / ink; - } - } - - output += 6; - num_pixels --; - } - break; - - case 7 : // CcMmYKk - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - k = cf_scmy_lut[*input++]; - kc = cmyk->color_lut[k]; - k = cmyk->black_lut[k]; - output[0] = channels[0][kc]; - output[1] = channels[1][kc]; - output[2] = channels[2][kc]; - output[3] = channels[3][kc]; - output[4] = channels[4][kc]; - output[5] = channels[5][k]; - output[6] = channels[6][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3] + - output[4] + output[5] + output[6]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - output[4] = ink_limit * output[4] / ink; - output[5] = ink_limit * output[5] / ink; - output[6] = ink_limit * output[6] / ink; - } - } - - output += 7; - num_pixels --; - } - break; - } -} - - -// -// 'cfCMYKDoRGB()' - Do an sRGB separation... -// - -void -cfCMYKDoRGB(const cf_cmyk_t *cmyk, - // I - Color separation - const unsigned char *input, - // I - Input grayscale pixels - short *output, - // O - Output Device-N pixels - int num_pixels) - // I - Number of pixels -{ - int c, // Current cyan value - m, // Current magenta value - y, // Current yellow value - k, // Current black value - kc, // Current black color value - km; // Maximum black value - const short **channels; // Copy of channel LUTs - int ink, // Amount of ink - ink_limit; // Ink limit from separation - - - // - // Range check input... - // - - if (cmyk == NULL || input == NULL || output == NULL || num_pixels <= 0) - return; - - // - // Loop through it all... - // - - channels = (const short **)cmyk->channels; - ink_limit = cmyk->ink_limit; - - switch (cmyk->num_channels) - { - case 1 : // Black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = cf_scmy_lut[*input++]; - m = cf_scmy_lut[*input++]; - y = cf_scmy_lut[*input++]; - k = (c * 31 + m * 61 + y * 8) / 100; - - *output++ = channels[0][k]; - - num_pixels --; - } - break; - - case 2 : // Black, light black - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = cf_scmy_lut[*input++]; - m = cf_scmy_lut[*input++]; - y = cf_scmy_lut[*input++]; - k = (c * 31 + m * 61 + y * 8) / 100; - - output[0] = channels[0][k]; - output[1] = channels[1][k]; - - if (ink_limit) - { - ink = output[0] + output[1]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - } - } - - output += 2; - num_pixels --; - } - break; - - case 3 : // CMY - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = cf_scmy_lut[*input++]; - m = cf_scmy_lut[*input++]; - y = cf_scmy_lut[*input++]; - - output[0] = channels[0][c]; - output[1] = channels[1][m]; - output[2] = channels[2][y]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - } - } - - output += 3; - num_pixels --; - } - break; - - case 4 : // CMYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = cf_scmy_lut[*input++]; - m = cf_scmy_lut[*input++]; - y = cf_scmy_lut[*input++]; - k = min(c, min(m, y)); - - if ((km = max(c, max(m, y))) > k) - k = k * k * k / (km * km); - - kc = cmyk->color_lut[k] - k; - k = cmyk->black_lut[k]; - c += kc; - m += kc; - y += kc; - - output[0] = channels[0][c]; - output[1] = channels[1][m]; - output[2] = channels[2][y]; - output[3] = channels[3][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - } - } - - output += 4; - num_pixels --; - } - break; - - case 6 : // CcMmYK - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = cf_scmy_lut[*input++]; - m = cf_scmy_lut[*input++]; - y = cf_scmy_lut[*input++]; - k = min(c, min(m, y)); - - if ((km = max(c, max(m, y))) > k) - k = k * k * k / (km * km); - - kc = cmyk->color_lut[k] - k; - k = cmyk->black_lut[k]; - c += kc; - m += kc; - y += kc; - - output[0] = channels[0][c]; - output[1] = channels[1][c]; - output[2] = channels[2][m]; - output[3] = channels[3][m]; - output[4] = channels[4][y]; - output[5] = channels[5][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3] + - output[4] + output[5]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - output[4] = ink_limit * output[4] / ink; - output[5] = ink_limit * output[5] / ink; - } - } - - output += 6; - num_pixels --; - } - break; - - case 7 : // CcMmYKk - while (num_pixels > 0) - { - // - // Get the input black value and then set the corresponding color - // channel values... - // - - c = cf_scmy_lut[*input++]; - m = cf_scmy_lut[*input++]; - y = cf_scmy_lut[*input++]; - k = min(c, min(m, y)); - - if ((km = max(c, max(m, y))) > k) - k = k * k * k / (km * km); - - kc = cmyk->color_lut[k] - k; - k = cmyk->black_lut[k]; - c += kc; - m += kc; - y += kc; - - output[0] = channels[0][c]; - output[1] = channels[1][c]; - output[2] = channels[2][m]; - output[3] = channels[3][m]; - output[4] = channels[4][y]; - output[5] = channels[5][k]; - output[6] = channels[6][k]; - - if (ink_limit) - { - ink = output[0] + output[1] + output[2] + output[3] + - output[4] + output[5] + output[6]; - - if (ink > ink_limit) - { - output[0] = ink_limit * output[0] / ink; - output[1] = ink_limit * output[1] / ink; - output[2] = ink_limit * output[2] / ink; - output[3] = ink_limit * output[3] / ink; - output[4] = ink_limit * output[4] / ink; - output[5] = ink_limit * output[5] / ink; - output[6] = ink_limit * output[6] / ink; - } - } - - output += 7; - num_pixels --; - } - break; - } -} - - -// -// 'cfCMYKNew()' - Create a new CMYK color separation. -// - -cf_cmyk_t * // O - New CMYK separation or NULL -cfCMYKNew(int num_channels) // I - Number of color components -{ - cf_cmyk_t *cmyk; // New color separation - int i; // Looping var - - - // - // Range-check the input... - // - - if (num_channels < 1) - return (NULL); - - // - // Allocate memory for the separation... - // - - if ((cmyk = calloc(1, sizeof(cf_cmyk_t))) == NULL) - return (NULL); - - // - // Allocate memory for the LUTs... - // - - cmyk->num_channels = num_channels; - - if ((cmyk->channels[0] = calloc(num_channels * 256, sizeof(short))) == NULL) - { - free(cmyk); - return (NULL); - } - - for (i = 1; i < num_channels; i ++) - cmyk->channels[i] = cmyk->channels[0] + i * 256; - - // - // Fill in the LUTs with unity transitions... - // - - for (i = 0; i < 256; i ++) - cmyk->black_lut[i] = i; - - switch (num_channels) - { - case 1 : // K - case 2 : // Kk - for (i = 0; i < 256; i ++) - { - cmyk->channels[0][i] = CF_MAX_LUT * i / 255; - } - break; - case 3 : // CMY - for (i = 0; i < 256; i ++) - { - cmyk->channels[0][i] = CF_MAX_LUT * i / 255; - cmyk->channels[1][i] = CF_MAX_LUT * i / 255; - cmyk->channels[2][i] = CF_MAX_LUT * i / 255; - } - break; - case 4 : // CMYK - for (i = 0; i < 256; i ++) - { - cmyk->channels[0][i] = CF_MAX_LUT * i / 255; - cmyk->channels[1][i] = CF_MAX_LUT * i / 255; - cmyk->channels[2][i] = CF_MAX_LUT * i / 255; - cmyk->channels[3][i] = CF_MAX_LUT * i / 255; - } - break; - case 6 : // CcMmYK - case 7 : // CcMmYKk - for (i = 0; i < 256; i ++) - { - cmyk->channels[0][i] = CF_MAX_LUT * i / 255; - cmyk->channels[2][i] = CF_MAX_LUT * i / 255; - cmyk->channels[4][i] = CF_MAX_LUT * i / 255; - cmyk->channels[5][i] = CF_MAX_LUT * i / 255; - } - break; - } - - // - // Return the separation... - // - - return (cmyk); -} - - -// -// 'cfCMYKSetBlack()' - Set the transition range for CMY to black. -// - -void -cfCMYKSetBlack(cf_cmyk_t *cmyk, // I - CMYK color separation - float lower, // I - No black ink - float upper, // I - Only black ink - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - int i, // Looping var - delta, // Difference between lower and upper - ilower, // Lower level from 0 to 255 - iupper; // Upper level from 0 to 255 - - - // - // Range check input... - // - - if (cmyk == NULL || lower < 0.0 || lower > 1.0 || upper < 0.0 || upper > 1.0 || - lower > upper) - return; - - // - // Convert lower and upper to integers from 0 to 255... - // - - ilower = (int)(255.0 * lower + 0.5); - iupper = (int)(255.0 * upper + 0.5); - delta = iupper - ilower; - - // - // Generate the CMY-only data... - // - - for (i = 0; i < ilower; i ++) - { - cmyk->black_lut[i] = 0; - cmyk->color_lut[i] = i; - } - - // - // Then the transition data... - // - - for (; i < iupper; i ++) - { - cmyk->black_lut[i] = iupper * (i - ilower) / delta; - cmyk->color_lut[i] = ilower - ilower * (i - ilower) / delta; - } - - // - // Then the K-only data... - // - - for (; i < 256; i ++) - { - cmyk->black_lut[i] = i; - cmyk->color_lut[i] = 0; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfCMYKSetBlack(cmyk, lower=%.3f, upper=%.3f)", - lower, upper); - - if (log) - for (i = 0; i < 256; i += 17) - log(ld, CF_LOGLEVEL_DEBUG, - " %3d = %3dk + %3dc", i, - cmyk->black_lut[i], cmyk->color_lut[i]); -} - - -// -// 'cfCMYKSetCurve()' - Set a color transform curve using points. -// - -void -cfCMYKSetCurve(cf_cmyk_t *cmyk, // I - CMYK color separation - int channel, // I - Color channel - int num_xypoints, - // I - Number of X,Y points - const float *xypoints, // I - X,Y points - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - int i; // Looping var - int xstart; // Start position - int xend; // End position - int xdelta; // Difference in position - int ystart; // Start value - int yend; // End value - int ydelta; // Difference in value - - - // - // Range check input... - // - - if (cmyk == NULL || channel < 0 || channel >= cmyk->num_channels || - num_xypoints < 1 || xypoints == NULL) - return; - - // - // Initialize the lookup table for the specified channel... - // - - for (xstart = xend = 0, ystart = yend = 0; - num_xypoints > 0; - num_xypoints --, xypoints += 2, xstart = xend, ystart = yend) - { - xend = (int)(255.0 * xypoints[1] + 0.5); - yend = (int)(CF_MAX_LUT * xypoints[0] + 0.5); - xdelta = xend - xstart; - ydelta = yend - ystart; - - for (i = xstart; i < xend; i ++) - cmyk->channels[channel][i] = ystart + ydelta * (i - xstart) / xdelta; - } - - // - // Initialize any trailing values to the maximum of the last data point... - // - - for (i = xend; i < 256; i ++) - cmyk->channels[channel][i] = yend; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cupsCMYKSetXY(cmyk, channel=%d, num_xypoints=%d, " - "xypoints=[%.3f %.3f %.3f %.3f ...])", channel, - num_xypoints, xypoints[0], xypoints[1], xypoints[2], - xypoints[3]); - - if (log) - for (i = 0; i < 256; i += 17) - log(ld, CF_LOGLEVEL_DEBUG, - " %3d = %4d", i, - cmyk->channels[channel + 0][i]); -} - - -// -// 'cfCMYKSetGamma()' - Set a color transform curve using gamma and density. -// - -void -cfCMYKSetGamma(cf_cmyk_t *cmyk, // I - CMYK color separation - int channel, // I - Ink channel - float gamval, // I - Gamma correction - float density, // I - Maximum density - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - int i; // Looping var - - - // - // Range check input... - // - - if (cmyk == NULL || channel < 0 || channel >= cmyk->num_channels || - gamval <= 0.0 || density <= 0.0 || density > 1.0) - return; - - // - // Initialize the lookup table for the specified channel... - // - - for (i = 0; i < 256; i ++) - cmyk->channels[channel][i] = (int)(density * CF_MAX_LUT * - pow((float)i / 255.0, gamval) + 0.5); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfCMYKSetGamma(cmyk, channel=%d, gamval=%.3f, " - "density=%.3f)", channel, gamval, density); - - if (log) - for (i = 0; i < 256; i += 17) - log(ld, CF_LOGLEVEL_DEBUG, - " %3d = %4d", i, - cmyk->channels[channel + 0][i]); -} - - -// -// 'cfCMYKSetInkLimit()' - Set the limit on the amount of ink. -// - -void -cfCMYKSetInkLimit(cf_cmyk_t *cmyk, // I - CMYK color separation - float limit) // I - Limit of ink -{ - if (!cmyk || limit < 0.0) - return; - - cmyk->ink_limit = limit * CF_MAX_LUT; -} - - -// -// 'cfCMYKSetLtDk()' - Set light/dark ink transforms. -// - -void -cfCMYKSetLtDk(cf_cmyk_t *cmyk, // I - CMYK color separation - int channel, // I - Dark ink channel (+1 for light) - float light, // I - Light ink only level - float dark, // I - Dark ink only level - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - int i, // Looping var - delta, // Difference between lower and upper - ilight, // Light level from 0 to 255 - idark; // Dark level from 0 to 255 - short lut[256]; // Original LUT data - - - // - // Range check input... - // - - if (cmyk == NULL || light < 0.0 || light > 1.0 || dark < 0.0 || dark > 1.0 || - light > dark || channel < 0 || channel > (cmyk->num_channels - 2)) - return; - - // - // Convert lower and upper to integers from 0 to 255... - // - - ilight = (int)(255.0 * light + 0.5); - idark = (int)(255.0 * dark + 0.5); - delta = idark - ilight; - - // - // Copy the dark ink LUT... - // - - memcpy(lut, cmyk->channels[channel], sizeof(lut)); - - // - // Generate the light-only data... - // - - for (i = 0; i < ilight; i ++) - { - cmyk->channels[channel + 0][i] = 0; - cmyk->channels[channel + 1][i] = CF_MAX_LUT * i / ilight; - } - - // - // Then the transition data... - // - - for (; i < idark; i ++) - { - cmyk->channels[channel + 0][i] = CF_MAX_LUT * idark * (i - ilight) / - delta / 255; - cmyk->channels[channel + 1][i] = CF_MAX_LUT - CF_MAX_LUT * - (i - ilight) / delta; - } - - // - // Then the K-only data... - // - - for (; i < 256; i ++) - { - cmyk->channels[channel + 0][i] = CF_MAX_LUT * i / 255; - cmyk->channels[channel + 1][i] = 0; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfCMYKSetLtDk(cmyk, channel=%d, light=%.3f, " - "dark=%.3f)", channel, light, dark); - - if (log) - for (i = 0; i < 256; i += 17) - log(ld, CF_LOGLEVEL_DEBUG, - " %3d = %4dlt + %4ddk", i, - cmyk->channels[channel + 0][i], cmyk->channels[channel + 1][i]); -} diff --git a/cupsfilters/colord.c b/cupsfilters/colord.c deleted file mode 100644 index cca1ac57d..000000000 --- a/cupsfilters/colord.c +++ /dev/null @@ -1,504 +0,0 @@ -// -// Common routines to access the colord CMS framework for libcupsfilter. -// -// Copyright (c) 2011, Tim Waugh -// Copyright (c) 2011-2013, Richard Hughes -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include -#ifdef HAVE_DBUS - #include -#endif - -#include "colord.h" - -#define QUAL_COLORSPACE 0 -#define QUAL_MEDIA 1 -#define QUAL_RESOLUTION 2 -#define QUAL_SIZE 3 - -char ** -cfColordGetQualifier(cf_filter_data_t *data, - const char *color_space, - const char *media_type, - int x_res, - int y_res) -{ - int i, len; - const char *val; - const char *ptr1, *ptr2; - char buf[64]; - char **tuple = NULL; - int num_options = 0; - cups_option_t *options = NULL; - - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - // Get data from "cm-profile-qualifier" option - if ((val = - cupsGetOption("cm-profile-qualifier", - data->num_options, data->options)) != NULL && - val[0] != '\0') - { - tuple = calloc(QUAL_SIZE + 1, sizeof(char*)); - ptr1 = ptr2 = val; - for (i = 0; i < QUAL_SIZE; i ++) - { - while (*ptr2 && *ptr2 != '.') ptr2 ++; - len = ptr2 - ptr1; - tuple[i] = malloc((len + 1) * sizeof(char)); - memcpy(tuple[i], ptr1, len); - tuple[i][len] = '\0'; - if (*ptr2) - ptr2 ++; - ptr1 = ptr2; - } - } - else - { - // String for resolution - if (x_res <= 0) - buf[0] = '\0'; - else if (y_res <= 0 || y_res == x_res) - snprintf(buf, sizeof(buf), "%ddpi", x_res); - else - snprintf(buf, sizeof(buf), "%dx%ddpi", x_res, y_res); - - // return a NULL terminated array so we don't have to break it up later - tuple = calloc(QUAL_SIZE + 1, sizeof(char*)); - tuple[QUAL_COLORSPACE] = strdup(color_space ? color_space : ""); - tuple[QUAL_MEDIA] = strdup(media_type ? media_type : ""); - tuple[QUAL_RESOLUTION] = strdup(buf); - } - - cupsFreeOptions(num_options, options); - - return (tuple); -} - -#ifdef HAVE_DBUS - -static char * -get_filename_for_profile_path(cf_filter_data_t *data, - DBusConnection *con, - const char *object_path) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - char *filename = NULL; - const char *interface = "org.freedesktop.ColorManager.Profile"; - const char *property = "Filename"; - const char *tmp; - DBusError error; - DBusMessageIter args; - DBusMessage *message = NULL; - DBusMessage *reply = NULL; - DBusMessageIter sub; - - message = dbus_message_new_method_call("org.freedesktop.ColorManager", - object_path, - "org.freedesktop.DBus.Properties", - "Get"); - - dbus_message_iter_init_append(message, &args); - dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &interface); - dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &property); - - // send syncronous - dbus_error_init(&error); - if (log) log(ld, CF_LOGLEVEL_DEBUG, "Calling %s.Get(%s)", interface, property); - - reply = dbus_connection_send_with_reply_and_block(con, - message, - -1, - &error); - if (reply == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "DEBUG: Failed to send: %s:%s", - error.name, error.message); - dbus_error_free(&error); - goto out; - } - - // get reply data - dbus_message_iter_init(reply, &args); - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Incorrect reply type"); - goto out; - } - - dbus_message_iter_recurse(&args, &sub); - dbus_message_iter_get_basic(&sub, &tmp); - filename = strdup(tmp); - out: - if (message != NULL) - dbus_message_unref(message); - if (reply != NULL) - dbus_message_unref(reply); - return (filename); -} - -static char * -get_profile_for_device_path(cf_filter_data_t *data, - DBusConnection *con, - const char *object_path, - const char **split) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - char **key = NULL; - char *profile = NULL; - char str[256]; - const char *tmp; - DBusError error; - DBusMessageIter args; - DBusMessageIter entry; - DBusMessage *message = NULL; - DBusMessage *reply = NULL; - int i = 0; - const int max_keys = 7; - - message = dbus_message_new_method_call("org.freedesktop.ColorManager", - object_path, - "org.freedesktop.ColorManager.Device", - "GetProfileForQualifiers"); - dbus_message_iter_init_append(message, &args); - - // create the fallbacks - key = calloc(max_keys + 1, sizeof(char*)); - - // exact match - i = 0; - snprintf(str, sizeof(str), "%s.%s.%s", - split[QUAL_COLORSPACE], - split[QUAL_MEDIA], - split[QUAL_RESOLUTION]); - key[i++] = strdup(str); - snprintf(str, sizeof(str), "%s.%s.*", - split[QUAL_COLORSPACE], - split[QUAL_MEDIA]); - key[i++] = strdup(str); - snprintf(str, sizeof(str), "%s.*.%s", - split[QUAL_COLORSPACE], - split[QUAL_RESOLUTION]); - key[i++] = strdup(str); - snprintf(str, sizeof(str), "%s.*.*", - split[QUAL_COLORSPACE]); - key[i++] = strdup(str); - key[i++] = strdup("*"); - dbus_message_iter_open_container(&args, - DBUS_TYPE_ARRAY, - "s", - &entry); - for (i = 0; key[i] != NULL; i ++) - { - dbus_message_iter_append_basic(&entry, - DBUS_TYPE_STRING, - &key[i]); - } - dbus_message_iter_close_container(&args, &entry); - - // send syncronous - dbus_error_init(&error); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Calling GetProfileForQualifiers(%s...)", key[0]); - reply = dbus_connection_send_with_reply_and_block(con, - message, - -1, - &error); - if (reply == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, "Failed to send: %s:%s", - error.name, error.message); - dbus_error_free(&error); - goto out; - } - - // get reply data - dbus_message_iter_init(reply, &args); - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Incorrect reply type"); - goto out; - } - dbus_message_iter_get_basic(&args, &tmp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, "Found profile %s", tmp); - - // get filename - profile = get_filename_for_profile_path(data, con, tmp); - - out: - if (message != NULL) - dbus_message_unref(message); - if (reply != NULL) - dbus_message_unref(reply); - if (key != NULL) - { - for (i = 0; i < max_keys; i ++) - free(key[i]); - free(key); - } - return (profile); -} - -static char * -get_device_path_for_device_id(cf_filter_data_t *data, - DBusConnection *con, - const char *device_id) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - char *device_path = NULL; - const char *device_path_tmp; - DBusError error; - DBusMessageIter args; - DBusMessage *message = NULL; - DBusMessage *reply = NULL; - - message = dbus_message_new_method_call("org.freedesktop.ColorManager", - "/org/freedesktop/ColorManager", - "org.freedesktop.ColorManager", - "FindDeviceById"); - dbus_message_iter_init_append(message, &args); - dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id); - - // send syncronous - dbus_error_init(&error); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Calling FindDeviceById(%s)", device_id); - reply = dbus_connection_send_with_reply_and_block(con, - message, - -1, - &error); - if (reply == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Failed to send: %s:%s", - error.name, error.message); - dbus_error_free(&error); - goto out; - } - - // get reply data - dbus_message_iter_init(reply, &args); - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Incorrect reply type"); - goto out; - } - dbus_message_iter_get_basic(&args, &device_path_tmp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Found device %s", device_path_tmp); - device_path = strdup(device_path_tmp); - out: - if (message != NULL) - dbus_message_unref(message); - if (reply != NULL) - dbus_message_unref(reply); - return (device_path); -} - -char * -cfColordGetProfileForDeviceID(cf_filter_data_t *data, - const char *device_id, - const char **qualifier_tuple) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - DBusConnection *con = NULL; - char *device_path = NULL; - char *filename = NULL; - - if (device_id == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "No colord device ID available"); - goto out; - } - - // connect to system bus - con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - if (con == NULL) - { - // If D-Bus is not reachable, gracefully leave and ignore error - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Failed to connect to system bus"); - goto out; - } - - // find the device - device_path = get_device_path_for_device_id (data, con, device_id); - if (device_path == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Failed to get device %s", device_id); - goto out; - } - - // get the best profile for the device - filename = get_profile_for_device_path(data, con, device_path, - qualifier_tuple); - if (filename == NULL || !filename[0]) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Failed to get profile filename for %s", device_id); - goto out; - } - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Use profile filename: '%s'", filename); - out: - free(device_path); - if (con != NULL) - dbus_connection_unref(con); - return (filename); -} - -static int -get_profile_inhibitors(cf_filter_data_t *data, - DBusConnection *con, - const char *object_path) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - char *tmp; - const char *interface = "org.freedesktop.ColorManager.Device"; - const char *property = "ProfilingInhibitors"; - DBusError error; - DBusMessageIter args; - DBusMessageIter sub; - DBusMessageIter sub2; - DBusMessage *message = NULL; - DBusMessage *reply = NULL; - int inhibitors = 0; - - message = dbus_message_new_method_call("org.freedesktop.ColorManager", - object_path, - "org.freedesktop.DBus.Properties", - "Get"); - - dbus_message_iter_init_append(message, &args); - dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &interface); - dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &property); - - // send syncronous - dbus_error_init(&error); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Calling %s.Get(%s)", interface, property); - reply = dbus_connection_send_with_reply_and_block(con, - message, - -1, - &error); - if (reply == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Failed to send: %s:%s", - error.name, error.message); - dbus_error_free(&error); - goto out; - } - - // get reply data - dbus_message_iter_init(reply, &args); - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Incorrect reply type"); - goto out; - } - - // count the size of the array - dbus_message_iter_recurse(&args, &sub2); - dbus_message_iter_recurse(&sub2, &sub); - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) - { - dbus_message_iter_get_basic(&sub, &tmp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Inhibitor %s exists", tmp); - dbus_message_iter_next(&sub); - inhibitors++; - } - out: - if (message != NULL) - dbus_message_unref(message); - if (reply != NULL) - dbus_message_unref(reply); - return (inhibitors); -} - -int -cfColordGetInhibitForDeviceID(cf_filter_data_t *data, - const char *device_id) -{ - cf_logfunc_t log = data->logfunc; - void* ld = data->logdata; - DBusConnection *con; - char *device_path = NULL; - int has_inhibitors = FALSE; - - // connect to system bus - con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - if (con == NULL) - { - // If D-Bus is not reachable, gracefully leave and ignore error - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Failed to connect to system bus"); - goto out; - } - - // find the device - device_path = get_device_path_for_device_id (data, con, device_id); - if (device_path == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Failed to get find device %s", device_id); - goto out; - } - - // get the best profile for the device - has_inhibitors = get_profile_inhibitors(data, con, device_path); - out: - free(device_path); - if (con != NULL) - dbus_connection_unref(con); - return (has_inhibitors); -} - -#else - -char * -cfColordGetProfileForDeviceID(cf_filter_data_t *data, - const char *device_id, - const char **qualifier_tuple) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - if (log) log(ld, CF_LOGLEVEL_WARN, - "not compiled with DBus support"); - return (NULL); -} - -int -cfColordGetInhibitForDeviceID(cf_filter_data_t *data, - const char *device_id) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - if (log) log(ld, CF_LOGLEVEL_WARN, - "not compiled with DBus support"); - return (0); -} - -#endif diff --git a/cupsfilters/colord.h b/cupsfilters/colord.h deleted file mode 100644 index 5b5bc0301..000000000 --- a/cupsfilters/colord.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Common routines to access the colord CMS framework for libcupsfilter. -// -// Copyright (c) 2011-2013, Richard Hughes -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#ifndef _CUPS_FILTERS_COLORD_H_ -# define _CUPS_FILTERS_COLORD_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -#include -#include - -char **cfColordGetQualifier(cf_filter_data_t *data, - const char *color_space, - const char *media_type, - int x_res, - int y_res); -char *cfColordGetProfileForDeviceID(cf_filter_data_t *data, - const char *device_id, - const char **qualifier_tuple); -int cfColordGetInhibitForDeviceID(cf_filter_data_t *data, - const char *device_id); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_COLORD_H_ diff --git a/cupsfilters/colormanager.c b/cupsfilters/colormanager.c deleted file mode 100644 index f25788c68..000000000 --- a/cupsfilters/colormanager.c +++ /dev/null @@ -1,229 +0,0 @@ -// -// "Color Manager" - Color management interface for libcupsfilters. -// -// Copyright (c) 2011-2013, Richard Hughes -// Copyright (c) 2014, Joseph Simon -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#include "colormanager.h" -#include -#include -#include - - -#define CM_MAX_FILE_LENGTH 1024 - - -// -// Commonly-used calibration numbers -// - -double adobergb_wp[3] = {0.95045471, 1.0, 1.08905029}; -double sgray_wp[3] = {0.9505, 1, 1.0890}; -double adobergb_gamma[3] = {2.2, 2.2, 2.2}; -double sgray_gamma[1] = {2.2}; -double adobergb_matrix[9] = {0.60974121, 0.20527649, 0.14918518, - 0.31111145, 0.62567139, 0.06321716, - 0.01947021, 0.06086731, 0.74456787}; -double blackpoint_default[3] = {0.0, 0.0, 0.0}; - - -// -// Public functions -// - -// -// Get printer color management status from the system's color manager -// - -int -cfCmIsPrinterCmDisabled(cf_filter_data_t *data) -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - int is_printer_cm_disabled = 0; // color management status flag - char printer_id[CM_MAX_FILE_LENGTH] = ""; // colord printer id - - - // If invalid input, we leave color management alone - if (data->printer == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Color Manager: Invalid printer name."); - return (0); - } - - // Create printer id string for colord - snprintf(printer_id, CM_MAX_FILE_LENGTH, "cups-%s", data->printer); - - // Check if device is inhibited/disabled in colord - is_printer_cm_disabled = cfColordGetInhibitForDeviceID (data, printer_id); - - if (is_printer_cm_disabled) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Color Manager: Color management disabled by OS."); - - return (is_printer_cm_disabled); -} - - -// -// Get printer ICC profile from the system's color manager -// - -int -cfCmGetPrinterIccProfile(cf_filter_data_t *data, - const char *color_space, - const char *media_type, - int x_res, - int y_res, - char **profile) // ICC Profile Path -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - const char *val; - int is_profile_set = 0; // profile-is-found flag - char **qualifier = NULL; // color qualifier strings - char *icc_profile = NULL; // icc profile path - char printer_id[CM_MAX_FILE_LENGTH] = ""; // colord printer id - - if (data->printer == NULL || profile == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Color Manager: Invalid input - Unable to find profile."); - return (-1); - } - - // Get color qualifier triple - qualifier = cfColordGetQualifier(data, color_space, media_type, - x_res, y_res); - - if (qualifier != NULL) - { - // Create printer id string for colord - snprintf(printer_id, CM_MAX_FILE_LENGTH, "cups-%s", data->printer); - - // Get profile from colord using qualifiers - icc_profile = cfColordGetProfileForDeviceID(data, - (const char *)printer_id, - (const char **)qualifier); - } - - // Do we have a profile? - if (icc_profile) - is_profile_set = 1; - // If not, get fallback profile from option - else if ((val = cupsGetOption("cm-fallback-profile", - data->num_options, data->options)) != NULL && - val[0] != '\0') - { - is_profile_set = 1; - icc_profile = strdup(val); - } - else - icc_profile = NULL; - - // If a profile is found, we give it to the caller - if (is_profile_set) - *profile = strdup(icc_profile); - else - *profile = NULL; - - if (qualifier != NULL) - { - for (int i= 0; qualifier[i] != NULL; i++) - free(qualifier[i]); - free(qualifier); - } - - if (icc_profile != NULL) - free(icc_profile); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Color Manager: ICC Profile: %s", - *profile ? *profile : "None"); - - return (is_profile_set); -} - - -// -// Find the "cm-calibration" CUPS option -// - -cf_cm_calibration_t -cfCmGetCupsColorCalibrateMode(cf_filter_data_t *data) -{ - int num_options = 0; - cups_option_t *options = NULL; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_cm_calibration_t status; // color management status - - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - // Find the string in CUPS options and - if (cupsGetOption(CF_CM_CALIBRATION_STRING, num_options, options) != NULL) - status = CF_CM_CALIBRATION_ENABLED; - else - status = CF_CM_CALIBRATION_DISABLED; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Color Manager: %s", status ? - "Calibration Mode/Enabled" : "Calibration Mode/Off"); - - cupsFreeOptions(num_options, options); - - return (status); -} - - -// -// Accessor functions to return specific calibration data -// - -// Gamma values - -double *cfCmGammaAdobeRGB(void) -{ - return (adobergb_gamma); -} - -double *cfCmGammaSGray(void) -{ - return (sgray_gamma); -} - - -// Whitepoint values - -double *cfCmWhitePointAdobeRGB(void) -{ - return (adobergb_wp); -} - -double *cfCmWhitePointSGray(void) -{ - return (sgray_wp); -} - - -// Adapted primaries matrix - -double *cfCmMatrixAdobeRGB(void) -{ - return (adobergb_matrix); -} - - -// Blackpoint value - -double *cfCmBlackPointDefault(void) -{ - return (blackpoint_default); -} diff --git a/cupsfilters/colormanager.h b/cupsfilters/colormanager.h deleted file mode 100644 index 2d0e9b5e0..000000000 --- a/cupsfilters/colormanager.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// "Color Manager" - Color management interface for libcupsfilters. -// -// Copyright (c) 2014, Joseph Simon -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#ifndef _CUPS_FILTERS_COLORMANAGER_H_ -# define _CUPS_FILTERS_COLORMANAGER_H_ - - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -#include -#include - - -#define CF_CM_CALIBRATION_STRING "cm-calibration" // String for "Color - // Calibration Mode" - - -// Enum for status of CUPS color calibration -typedef enum cf_cm_calibration_e -{ - CF_CM_CALIBRATION_DISABLED = 0, // "cm-calibration" option - // not found - CF_CM_CALIBRATION_ENABLED = 1 // "cm-calibration" found -} cf_cm_calibration_t; - - -// -// Prototypes -// - -extern -cf_cm_calibration_t cfCmGetCupsColorCalibrateMode(cf_filter_data_t *data); - -extern int cfCmGetPrinterIccProfile(cf_filter_data_t *data, - const char *color_space, - const char *media_type, - int x_res, - int y_res, - char **icc_profile); - -extern int cfCmIsPrinterCmDisabled(cf_filter_data_t *data); - -extern double* cfCmGammaAdobeRGB(void); -extern double* cfCmGammaSGray(void); - -extern double* cfCmWhitePointAdobeRGB(void); -extern double* cfCmWhitePointSGray(void); - -extern double* cfCmMatrixAdobeRGB(void); -extern double* cfCmBlackPointDefault(void); - - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_COLORMANAGER_H_ diff --git a/cupsfilters/debug-internal.h b/cupsfilters/debug-internal.h deleted file mode 100644 index 98ae63775..000000000 --- a/cupsfilters/debug-internal.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// Internal debugging macros for libcupsfilters. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_DEBUG_INTERNAL_H_ -# define _CUPS_FILTERS_DEBUG_INTERNAL_H_ - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// The debug macros are used if you compile with DEBUG defined. -// -// Usage: -// -// DEBUG_puts("string"); -// DEBUG_printf(("format string", arg, arg, ...)); -// DEBUG_assert(boolean expression); -// -// Note the extra parenthesis around the DEBUG_printf macro... -// - -# ifdef DEBUG -# include -# define DEBUG_puts(x) _cf_debug_puts(x) -# define DEBUG_printf(x) _cf_debug_printf x -# define DEBUG_assert(x) assert(x) -# else -# define DEBUG_puts(x) -# define DEBUG_printf(x) -# define DEBUG_assert(x) -# endif // DEBUG - -# ifdef DEBUG -extern void _cf_debug_printf(const char *format, ...); -extern void _cf_debug_puts(const char *s); -# endif // DEBUG - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_DEBUG_INTERNAL_H_ diff --git a/cupsfilters/debug.c b/cupsfilters/debug.c deleted file mode 100644 index 43eb556dc..000000000 --- a/cupsfilters/debug.c +++ /dev/null @@ -1,45 +0,0 @@ -// -// Debugging functions for cupsfilters. -// -// Copyright © 2008-2018 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -// -// Include necessary headers... -// - -#include -#include - - -// -// '_cf_debug_printf()' - Write a formatted line to the log. -// - -void -_cf_debug_printf(const char *format, // I - Printf-style format string - ...) // I - Additional arguments as needed -{ - va_list ap; // Pointer to arguments - - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - fflush(stderr); -} - - -// -// '_cf_debug_puts()' - Write a single line to the log. -// - -void -_cf_debug_puts(const char *s) // I - String to output -{ - fputs(s, stderr); - fflush(stderr); -} diff --git a/cupsfilters/dither.c b/cupsfilters/dither.c deleted file mode 100644 index 018fe21bb..000000000 --- a/cupsfilters/dither.c +++ /dev/null @@ -1,296 +0,0 @@ -// -// Dithering routines for libcupsfilters. -// -// Copyright 2007 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfDitherDelete() - Free a dithering buffer. -// cfDitherLine() - Dither a line of pixels... -// cfDitherNew() - Create a dithering buffer. -// - -// -// Include necessary headers. -// - -#include -#include "driver.h" - - -// -// 'cfDitherDelete()' - Free a dithering buffer. -// -// Returns 0 on success, -1 on failure. -// - -void -cfDitherDelete(cf_dither_t *d) // I - Dithering buffer -{ - if (d != NULL) - free(d); -} - - -// -// 'cfDitherLine()' - Dither a line of pixels... -// - -void -cfDitherLine(cf_dither_t *d, // I - Dither data - const cf_lut_t *lut, // I - Lookup table - const short *data, // I - Separation data - int num_channels, - // I - Number of components - unsigned char *p) // O - Pixels -{ - register int x, // Horizontal position in line... - pixel, // Current adjusted pixel... - e, // Current error - e0, e1, e2; // Error values - register int errval0, // First half of error value - errval1, // Second half of error value - errbase, // Base multiplier - errbase0, // Base multiplier for large values - errbase1, // Base multiplier for small values - errrange; // Range of random multiplier - register int *p0, // Error buffer pointers... - *p1; - static char logtable[16384]; // Error magnitude for randomness - static char loginit = 0; // Has the table been initialized? - - - if (!loginit) - { - // - // Initialize a logarithmic table for the magnitude of randomness - // that is introduced. - // - - loginit = 1; - - logtable[0] = 0; - for (x = 1; x < 2049; x ++) - logtable[x] = (int)(log(x / 16.0) / log(2.0) + 1.0); - for (; x < 16384; x ++) - logtable[x] = logtable[2049]; - } - - if (d->row == 0) - { - // - // Dither from left to right: - // - // e0 == p0[0] - // e1 e2 == p1[-1] p1[0] - // - - p0 = d->errors + 2; - p1 = d->errors + 2 + d->width + 4; - e0 = p0[0]; - e1 = 0; - e2 = 0; - - // - // Error diffuse each output pixel... - // - - for (x = d->width; - x > 0; - x --, p0 ++, p1 ++, p ++, data += num_channels) - { - // - // Skip blank pixels... - // - - if (*data == 0) - { - *p = 0; - e0 = p0[1]; - p1[-1] = e1; - e1 = e2; - e2 = 0; - continue; - } - - // - // Compute the net pixel brightness and brightness error. Set a dot - // if necessary... - // - - pixel = lut[*data].intensity + e0 / 128; - - if (pixel > CF_MAX_LUT) - pixel = CF_MAX_LUT; - else if (pixel < 0) - pixel = 0; - - *p = lut[pixel].pixel; - e = lut[pixel].error; - - // - // Set the randomness factor... - // - - if (e > 0) - errrange = logtable[e]; - else - errrange = logtable[-e]; - - errbase = 8 - errrange; - errrange = errrange * 2 + 1; - - // - // Randomize the error value. - // - - if (errrange > 1) - { - errbase0 = errbase + (CUPS_RAND() % errrange); - errbase1 = errbase + (CUPS_RAND() % errrange); - } - else - errbase0 = errbase1 = errbase; - - // - // X 7/16 = X e0 - // 3/16 5/16 1/16 = e1 e2 - // - - errval0 = errbase0 * e; - errval1 = (16 - errbase0) * e; - e0 = p0[1] + 7 * errval0; - e1 = e2 + 5 * errval1; - - errval0 = errbase1 * e; - errval1 = (16 - errbase1) * e; - e2 = errval0; - p1[-1] = e1 + 3 * errval1; - } - } - else - { - // - // Dither from right to left: - // - // e0 == p0[0] - // e2 e1 == p1[0] p1[1] - // - - p0 = d->errors + d->width + 1 + d->width + 4; - p1 = d->errors + d->width + 1; - p += d->width - 1; - data += num_channels * (d->width - 1); - e0 = p0[0]; - e1 = 0; - e2 = 0; - - // - // Error diffuse each output pixel... - // - - for (x = d->width; - x > 0; - x --, p0 --, p1 --, p --, data -= num_channels) - { - // - // Skip blank pixels... - // - - if (*data == 0) - { - *p = 0; - e0 = p0[-1]; - p1[1] = e1; - e1 = e2; - e2 = 0; - continue; - } - - // - // Compute the net pixel brightness and brightness error. Set a dot - // if necessary... - // - - pixel = lut[*data].intensity + e0 / 128; - - if (pixel > CF_MAX_LUT) - pixel = CF_MAX_LUT; - else if (pixel < 0) - pixel = 0; - - *p = lut[pixel].pixel; - e = lut[pixel].error; - - // - // Set the randomness factor... - // - - if (e > 0) - errrange = logtable[e]; - else - errrange = logtable[-e]; - - errbase = 8 - errrange; - errrange = errrange * 2 + 1; - - // - // Randomize the error value. - // - - if (errrange > 1) - { - errbase0 = errbase + (CUPS_RAND() % errrange); - errbase1 = errbase + (CUPS_RAND() % errrange); - } - else - errbase0 = errbase1 = errbase; - - // - // X 7/16 = X e0 - // 3/16 5/16 1/16 = e1 e2 - // - - errval0 = errbase0 * e; - errval1 = (16 - errbase0) * e; - e0 = p0[-1] + 7 * errval0; - e1 = e2 + 5 * errval1; - - errval0 = errbase1 * e; - errval1 = (16 - errbase1) * e; - e2 = errval0; - p1[1] = e1 + 3 * errval1; - } - } - - // - // Update to the next row... - // - - d->row = 1 - d->row; -} - - -// -// 'cfDitherNew()' - Create an error-diffusion dithering buffer. -// - -cf_dither_t * // O - New state array -cfDitherNew(int width) // I - Width of output in pixels -{ - cf_dither_t *d; // New dithering buffer - - - if ((d = (cf_dither_t *)calloc(1, sizeof(cf_dither_t) + - 2 * (width + 4) * - sizeof(int))) == NULL) - return (NULL); - - d->width = width; - - return (d); -} diff --git a/cupsfilters/driver.h b/cupsfilters/driver.h deleted file mode 100644 index 40e5d2716..000000000 --- a/cupsfilters/driver.h +++ /dev/null @@ -1,227 +0,0 @@ -// -// Printer driver utilities header file for libcupsfilters. -// -// Copyright 2007 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#ifndef _CUPS_FILTERS_DRIVER_H_ -# define _CUPS_FILTERS_DRIVER_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Include necessary headers... -// - -# include -# include -# include -# include -# include "filter.h" - -# if defined(WIN32) || defined(__EMX__) -# include -# else -# include -# include -# endif // WIN32 || __EMX__ - -# include -# include - - -// -// Common macros... -// - -# ifndef min -# define min(a,b) ((a) < (b) ? (a) : (b)) -# define max(a,b) ((a) > (b) ? (a) : (b)) -# endif // !min - - -// -// Constants... -// - -#define CF_MAX_CHAN 15 // Maximum number of color components -#define CF_MAX_LUT 4095 // Maximum LUT value -#define CF_MAX_RGB 4 // Maximum number of sRGB components - - -// -// Types/structures for the various routines. -// - -typedef struct cf_lut_s // *** Lookup Table for Dithering *** -{ - short intensity; // Adjusted intensity - short pixel; // Output pixel value - int error; // Error from desired value -} cf_lut_t; - -typedef struct cf_dither_s // *** Dithering State *** -{ - int width; // Width of buffer - int row; // Current row - int errors[96]; // Error values -} cf_dither_t; - -typedef struct cf_sample_s // *** Color sample point *** -{ - unsigned char rgb[3]; // sRGB values - unsigned char colors[CF_MAX_RGB]; // Color values -} cf_sample_t; - -typedef struct cf_rgb_s // *** Color separation lookup table *** -{ - int cube_size; // Size of color cube (2-N) on a side - int num_channels; // Number of colors per sample - unsigned char ****colors; // 4-D array of sample values - int cube_index[256]; // Index into cube for a given sRGB - // value - int cube_mult[256]; // Multiplier value for a given sRGB - // value - int cache_init; // Are cached values initialized? - unsigned char black[CF_MAX_RGB]; // Cached black (sRGB = 0,0,0) - unsigned char white[CF_MAX_RGB]; // Cached white (sRGB = 255,255,255) -} cf_rgb_t; - -typedef struct cf_cmyk_s // *** Simple CMYK lookup table *** -{ - unsigned char black_lut[256]; // Black generation LUT - unsigned char color_lut[256]; // Color removal LUT - int ink_limit; // Ink limit - int num_channels; // Number of components - short *channels[CF_MAX_CHAN]; - // Lookup tables -} cf_cmyk_t; - - -// -// Globals... -// - -extern const unsigned char - cf_srgb_lut[256]; - // sRGB gamma lookup table -extern const unsigned char - cf_scmy_lut[256]; - // sRGB gamma lookup table (inverted) - - -// -// Prototypes... -// - -// -// Byte checking functions... -// - -extern int cfCheckBytes(const unsigned char *, int); -extern int cfCheckValue(const unsigned char *, int, - const unsigned char); - -// -// Dithering functions... -// - -extern void cfDitherLine(cf_dither_t *d, const cf_lut_t *lut, - const short *data, int num_channels, - unsigned char *p); -extern cf_dither_t *cfDitherNew(int width); -extern void cfDitherDelete(cf_dither_t *); - -// -// Lookup table functions for dithering... -// - -extern cf_lut_t *cfLutNew(int num_vals, const float *vals, - cf_logfunc_t log, void *ld); -extern void cfLutDelete(cf_lut_t *lut); - - -// -// Bit packing functions... -// - -extern void cfPackHorizontal(const unsigned char *, - unsigned char *, int, - const unsigned char, const int); -extern void cfPackHorizontal2(const unsigned char *, - unsigned char *, int, const int); -extern void cfPackHorizontalBit(const unsigned char *, - unsigned char *, int, - const unsigned char, - const unsigned char); -extern void cfPackVertical(const unsigned char *, unsigned char *, - int, const unsigned char, const int); - -// -// Color separation functions... -// - -extern void cfRGBDelete(cf_rgb_t *rgb); -extern void cfRGBDoGray(cf_rgb_t *rgb, - const unsigned char *input, - unsigned char *output, int num_pixels); -extern void cfRGBDoRGB(cf_rgb_t *rgb, - const unsigned char *input, - unsigned char *output, int num_pixels); -extern cf_rgb_t *cfRGBNew(int num_samples, cf_sample_t *samples, - int cube_size, int num_channels); - -// -// CMYK separation functions... -// - -extern cf_cmyk_t *cfCMYKNew(int num_channels); -extern void cfCMYKDelete(cf_cmyk_t *cmyk); -extern void cfCMYKDoBlack(const cf_cmyk_t *cmyk, - const unsigned char *input, - short *output, int num_pixels); -extern void cfCMYKDoCMYK(const cf_cmyk_t *cmyk, - const unsigned char *input, - short *output, int num_pixels); -extern void cfCMYKDoGray(const cf_cmyk_t *cmyk, - const unsigned char *input, - short *output, int num_pixels); -extern void cfCMYKDoRGB(const cf_cmyk_t *cmyk, - const unsigned char *input, - short *output, int num_pixels); -extern void cfCMYKSetBlack(cf_cmyk_t *cmyk, - float lower, float upper, - cf_logfunc_t log, void *ld); -extern void cfCMYKSetCurve(cf_cmyk_t *cmyk, int channel, - int num_xypoints, - const float *xypoints, - cf_logfunc_t log, void *ld); -extern void cfCMYKSetGamma(cf_cmyk_t *cmyk, int channel, - float gamval, float density, - cf_logfunc_t log, void *ld); -extern void cfCMYKSetInkLimit(cf_cmyk_t *cmyk, float limit); -extern void cfCMYKSetLtDk(cf_cmyk_t *cmyk, int channel, - float light, float dark, - cf_logfunc_t log, void *ld); - - -// -// Convenience macro for writing print data... -// - -# define cfWritePrintData(s,n) fwrite((s), 1, (n), stdout) - - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_DRIVER_H_ diff --git a/cupsfilters/filter.c b/cupsfilters/filter.c deleted file mode 100644 index 477bec72a..000000000 --- a/cupsfilters/filter.c +++ /dev/null @@ -1,1686 +0,0 @@ -// -// Filter functions support for libcupsfilters. -// -// Copyright © 2020-2022 by Till Kamppeter. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -// -// Include necessary headers... -// - -#include "config.h" -#include "filter.h" -#include -#include -#include -#include -#include -#include -#include - -extern char **environ; - - -// -// Type definitions -// - -typedef struct filter_function_pid_s // Filter in filter chain -{ - char *name; // Filter executable name - int pid; // PID of filter process -} filter_function_pid_t; - - -// -// 'fcntl_add_cloexec()' - Add FD_CLOEXEC flag to the flags -// of a given file descriptor. -// - -static int // Return value of fcntl() -fcntl_add_cloexec(int fd) // File descriptor to add FD_CLOEXEC to -{ - return (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)); -} - - -// -// 'fcntl_add_nonblock()' - Add O_NONBLOCK flag to the flags -// of a given file descriptor. -// - -static int // Return value of fcntl() -fcntl_add_nonblock(int fd) // File descriptor to add O_NONBLOCK to -{ - return (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)); -} - - -// -// 'cfCUPSLogFunc()' - Output log messages on stderr, compatible to -// CUPS, meaning that the debug level is -// represented by a prefix like "DEBUG: ", "INFO: -// ", ... -// - -void -cfCUPSLogFunc(void *data, - cf_loglevel_t level, - const char *message, - ...) -{ - va_list arglist; - - - (void)data; // No extra data needed - - switch(level) - { - case CF_LOGLEVEL_UNSPEC: - case CF_LOGLEVEL_DEBUG: - default: - fprintf(stderr, "DEBUG: "); - break; - case CF_LOGLEVEL_INFO: - fprintf(stderr, "INFO: "); - break; - case CF_LOGLEVEL_WARN: - fprintf(stderr, "WARN: "); - break; - case CF_LOGLEVEL_ERROR: - case CF_LOGLEVEL_FATAL: - fprintf(stderr, "ERROR: "); - break; - case CF_LOGLEVEL_CONTROL: - break; - } - va_start(arglist, message); - vfprintf(stderr, message, arglist); - va_end(arglist); - fputc('\n', stderr); - fflush(stderr); -} - - -// -// 'cfCUPSIsCanceledFunc()' - Return 1 if the job is canceled, which -// is the case when the integer pointed at -// by data is not zero. -// - -int -cfCUPSIsCanceledFunc(void *data) -{ - return (*((int *)data) != 0 ? 1 : 0); -} - - -static cf_filter_data_ext_t * -get_filter_data_ext_entry(cups_array_t *ext_array, - const char *name) -{ - cf_filter_data_ext_t *entry; - - if (!ext_array || !name) - return (NULL); - - for (entry = (cf_filter_data_ext_t *)cupsArrayFirst(ext_array); - entry; - entry = (cf_filter_data_ext_t *)cupsArrayNext(ext_array)) - if (strcmp(entry->name, name) == 0) - break; - - return (entry); -} - - -void * // O - Extension record which got - // replaced, NULL if there was - // no record under this name, - // the added record is the one - // there, or no record was - // added. If not NULL the - // returned record should usually - // be deleted or freed. -cfFilterDataAddExt(cf_filter_data_t *data, // I - Filter data record - const char *name, // I - Name of extension - void *ext) // I - Extension record to be added -{ - cf_filter_data_ext_t *entry; - void *old_ext = NULL; - - if (!data || !name || !ext) - return (NULL); - - if (data->extension == NULL) - data->extension = cupsArrayNew(NULL, NULL); - - if (data->extension == NULL) - return (NULL); - - if ((entry = get_filter_data_ext_entry(data->extension, name)) != NULL) - { - old_ext = entry->ext; - entry->ext = ext; - } - else - { - entry = (cf_filter_data_ext_t *)calloc(1, sizeof(cf_filter_data_ext_t)); - if (entry) - { - entry->name = strdup(name); - entry->ext = ext; - cupsArrayAdd(data->extension, entry); - } - } - - return (old_ext); -} - - -void * -cfFilterDataGetExt(cf_filter_data_t *data, - const char *name) -{ - cf_filter_data_ext_t *entry; - - if (!data || !name || data->extension == NULL) - return (NULL); - - if ((entry = get_filter_data_ext_entry(data->extension, name)) != NULL) - return (entry->ext); - else - return (NULL); -} - - -void * -cfFilterDataRemoveExt(cf_filter_data_t *data, - const char *name) -{ - cf_filter_data_ext_t *entry; - void *ext = NULL; - - if (!data || !name || data->extension == NULL) - return (NULL); - - if ((entry = get_filter_data_ext_entry(data->extension, name)) != NULL) - { - ext = entry->ext; - cupsArrayRemove(data->extension, entry); - free(entry->name); - free(entry); - if (cupsArrayCount(data->extension) == 0) - { - cupsArrayDelete(data->extension); - data->extension = NULL; - } - return (ext); - } - else - return (NULL); -} - - -// -// 'cfFilterGetEnvVar()' - Auxiliary function for cfFilterExternal(), -// gets value of an environment variable in a -// list of environment variables as used by -// the execve() function -// - -char * // O - The value, NULL if variable is not in - // list -cfFilterGetEnvVar(char *name, // I - Name of environment variable to read - char **env) // I - List of environment variable serttings -{ - int i = 0; - - - if (env) - for (i = 0; env[i]; i ++) - if (strncmp(env[i], name, strlen(name)) == 0 && - strlen(env[i]) > strlen(name) && - env[i][strlen(name)] == '=') - return (env[i] + strlen(name) + 1); - - return (NULL); -} - - -// -// 'cfFilterAddEnvVar()' - Auxiliary function for cfFilterExternal(), -// adds/sets an environment variable in a list of -// environment variables as used by the execve() -// function -// - -int // O - Index of where the new value got - // inserted in the list -cfFilterAddEnvVar(char *name, // I - Name of environment variable to set - char *value, // I - Value of environment variable to set - char ***env) // I - List of environment variable serttings -{ - char *p; - int i = 0, - name_len; - - - if (!name || !env || !name[0]) - return (-1); - - // Assemble a "VAR=VALUE" string and the string length of "VAR" - if ((p = strchr(name, '=')) != NULL) - { - // User supplied "VAR=VALUE" as name and NULL as value - if (value) - return (-1); - name_len = p - name; - p = strdup(name); - } - else - { - // User supplied variable name and value as the name and as the value - name_len = strlen(name); - p = (char *)calloc(strlen(name) + (value ? strlen(value) : 0) + 2, - sizeof(char)); - sprintf(p, "%s=%s", name, (value ? value : "")); - } - - // Check whether we already have this variable in the list and update its - // value if it is there - if (*env) - for (i = 0; (*env)[i]; i ++) - if (strncmp((*env)[i], p, name_len) == 0 && (*env)[i][name_len] == '=') - { - free((*env)[i]); - (*env)[i] = p; - return (i); - } - - // Add the variable as new item to the list - *env = (char **)realloc(*env, (i + 2) * sizeof(char *)); - (*env)[i] = p; - (*env)[i + 1] = NULL; - return (i); -} - - -// -// 'cfFilterTee()' - This filter function is mainly for debugging. it -// resembles the "tee" utility, passing through the -// data unfiltered and copying it to a file. The -// file name is simply given as parameter. This -// makes using the function easy (add it as item of -// a filter chain called via cfFilterChain()) and -// can even be used more than once in the same -// filter chain (using different file names). In -// case of write error to the copy file, copying is -// stopped but the rest of the job is passed on to -// the next filter. If NULL is supplied as file -// name, the data is simply passed through without -// getting copied. -// - -int // O - Error status -cfFilterTee(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters (File - // name) -{ - const char *filename = (const char *)parameters; - ssize_t bytes, total = 0; // Bytes read/written - char buffer[65536]; // Read/write buffer - cf_logfunc_t log = data->logfunc; // Log function - void *ld = data->logdata; // log function data - int teefd = -1; // File descriptor for "tee"ed - // copy - - - (void)inputseekable; - - // Open the "tee"ed copy file - if (filename) - teefd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); - - while ((bytes = read(inputfd, buffer, sizeof(buffer))) > 0) - { - total += bytes; - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTee (%s): Passing on%s %d bytes, total %d bytes.", - filename, teefd >= 0 ? " and copying" : "", bytes, total); - - if (teefd >= 0) - if (write(teefd, buffer, (size_t)bytes) != bytes) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTee (%s): Unable to write %d bytes to the copy, stopping copy, continuing job output.", - filename, (int)bytes); - close(teefd); - teefd = -1; - } - - if (write(outputfd, buffer, (size_t)bytes) != bytes) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTee (%s): Unable to pass on %d bytes.", - filename, (int)bytes); - if (teefd >= 0) - close(teefd); - close(inputfd); - close(outputfd); - return (1); - } - } - - if (teefd >= 0) - close(teefd); - close(inputfd); - close(outputfd); - return (0); -} - - -// -// 'cfFilterPOpen()' - Pipe a stream to or from a filter function Can -// be the input to or the output from the filter -// function. -// - -int // O - File decriptor -cfFilterPOpen(cf_filter_function_t filter_func, - // I - Filter function - int inputfd, // I - File descriptor input stream or -1 - int outputfd, // I - File descriptor output stream or -1 - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters, // I - Filter-specific parameters - int *filter_pid) // O - PID of forked filter process -{ - int pipefds[2], // Pipes for filters - pid, // Process ID of filter - ret, - infd, outfd; // Temporary file descriptors - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - - // - // Check file descriptors... - // - - if (inputfd < 0 && outputfd < 0) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPOpen: Either inputfd or outputfd must be < 0, not both"); - return (-1); - } - - if (inputfd > 0 && outputfd > 0) - { - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPOpen: One of inputfd or outputfd must be < 0"); - return (-1); - } - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Open a pipe ... - // - - if (pipe(pipefds) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPOpen: Could not create pipe for %s: %s", - inputfd < 0 ? "input" : "output", - strerror(errno)); - return (-1); - } - - if ((pid = fork()) == 0) - { - // - // Child process goes here... - // - // Update input and output FDs as needed... - // - - if (inputfd < 0) - { - inputseekable = 0; - infd = pipefds[0]; - outfd = outputfd; - close(pipefds[1]); - } - else - { - infd = inputfd; - outfd = pipefds[1]; - close(pipefds[0]); - } - - // - // Execute filter function... - // - - ret = (filter_func)(infd, outfd, inputseekable, data, parameters); - - // - // Close file descriptor and terminate the sub-process... - // - - close(infd); - close(outfd); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPOpen: Filter function completed with status %d.", - ret); - exit(ret); - - } - else if (pid > 0) - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPOpen: Filter function (PID %d) started.", pid); - - // - // Save PID for waiting for or terminating the sub-process - // - - *filter_pid = pid; - - // - // Return file descriptor to stream to or from - // - - if (inputfd < 0) - { - close(pipefds[0]); - return (pipefds[1]); - } - else - { - close(pipefds[1]); - return (pipefds[0]); - } - - } - else - { - - // - // fork() error - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPOpen: Could not fork to start filter function: %s", - strerror(errno)); - return (-1); - } -} - - -// -// 'cfFilterPClose()' - Close a piped stream created with -// cfFilterPOpen(). -// - -int // O - Error status -cfFilterPClose(int fd, // I - Pipe file descriptor - int filter_pid, // I - PID of forked filter process - cf_filter_data_t *data) -{ - int status, // Exit status - retval; // Return value - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - - // - // close the stream... - // - - close(fd); - - // - // Wait for the child process to exit... - // - - retval = 0; - - retry_wait: - if (waitpid (filter_pid, &status, 0) == -1) - { - if (errno == EINTR) - goto retry_wait; - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPClose: Filter function (PID %d) stopped with an error: %s!", - filter_pid, strerror(errno)); - goto out; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPClose: Filter function (PID %d) exited with no errors.", - filter_pid); - - // How did the filter function terminate - if (WIFEXITED(status)) - // Via exit() anywhere or return() in the main() function - retval = WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - // Via signal - retval = 256 * WTERMSIG(status); - - out: - return (retval); -} - - -// -// 'compare_filter_pids()' - Compare two filter PIDs... -// - -static int // O - Result of comparison -compare_filter_pids(filter_function_pid_t *a, // I - First filter - filter_function_pid_t *b) // I - Second filter -{ - return (a->pid - b->pid); -} - - -// -// 'cfFilterChain()' - Call filter functions in a chain to do a data -// format conversion which non of the individual -// filter functions does -// - -int // O - Error status -cfFilterChain(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, - // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - cups_array_t *filter_chain = (cups_array_t *)parameters; - cf_filter_filter_in_chain_t *filter, // Current filter - *next; // Next filter - int current, // Current filter - filterfds[2][2], // Pipes for filters - pid, // Process ID of filter - status, // Exit status - retval, // Return value - ret; - int infd, outfd; // Temporary file descriptors - char buf[4096]; - ssize_t bytes; - cups_array_t *pids; // Executed filters array - filter_function_pid_t *pid_entry, // Entry in executed filters array - key; // Search key for filters - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Remove NULL filters... - // - - for (filter = (cf_filter_filter_in_chain_t *)cupsArrayFirst(filter_chain); - filter; - filter = (cf_filter_filter_in_chain_t *)cupsArrayNext(filter_chain)) - { - if (!filter->function) - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterChain: Invalid filter: %s - Removing...", - filter->name ? filter->name : "Unspecified"); - cupsArrayRemove(filter_chain, filter); - } - else - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterChain: Running filter: %s", - filter->name ? filter->name : "Unspecified"); - } - - // - // Empty filter chain -> Pass through the data unchanged - // - - if (cupsArrayCount(filter_chain) == 0) - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterChain: No filter at all in chain, passing through the data."); - retval = 0; - while ((bytes = read(inputfd, buf, sizeof(buf))) > 0) - if (write(outputfd, buf, bytes) < bytes) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterChain: Data write error: %s", strerror(errno)); - retval = 1; - break; - } - if (bytes < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterChain: Data read error: %s", strerror(errno)); - retval = 1; - } - close(inputfd); - close(outputfd); - return (retval); - } - - // - // Execute all of the filters... - // - - pids = cupsArrayNew((cups_array_func_t)compare_filter_pids, NULL); - current = 0; - filterfds[0][0] = inputfd; - filterfds[0][1] = -1; - filterfds[1][0] = -1; - filterfds[1][1] = -1; - - for (filter = (cf_filter_filter_in_chain_t *)cupsArrayFirst(filter_chain); - filter; - filter = next, current = 1 - current) - { - next = (cf_filter_filter_in_chain_t *)cupsArrayNext(filter_chain); - - if (filterfds[1 - current][0] > 1) - { - close(filterfds[1 - current][0]); - filterfds[1 - current][0] = -1; - } - if (filterfds[1 - current][1] > 1) - { - close(filterfds[1 - current][1]); - filterfds[1 - current][1] = -1; - } - - if (next) - { - if (pipe(filterfds[1 - current]) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterChain: Could not create pipe for output of %s: %s", - filter->name ? filter->name : "Unspecified filter", - strerror(errno)); - return (1); - } - fcntl_add_cloexec(filterfds[1 - current][0]); - fcntl_add_cloexec(filterfds[1 - current][1]); - } - else - filterfds[1 - current][1] = outputfd; - - if ((pid = fork()) == 0) - { - // - // Child process goes here... - // - // Update input and output FDs as needed... - // - - infd = filterfds[current][0]; - outfd = filterfds[1 - current][1]; - if (filterfds[current][1] > 1) - close(filterfds[current][1]); - if (filterfds[1 - current][0] > 1) - close(filterfds[1 - current][0]); - - if (infd < 0) - infd = open("/dev/null", O_RDONLY); - - if (outfd < 0) - outfd = open("/dev/null", O_WRONLY); - - // - // Execute filter function... - // - - ret = (filter->function)(infd, outfd, inputseekable, data, - filter->parameters); - - close(infd); - close(outfd); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterChain: %s completed with status %d.", - filter->name ? filter->name : "Unspecified filter", ret); - exit(ret); - - } - else if (pid > 0) - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterChain: %s (PID %d) started.", - filter->name ? filter->name : "Unspecified filter", pid); - - pid_entry = malloc(sizeof(filter_function_pid_t)); - pid_entry->pid = pid; - pid_entry->name = filter->name ? filter->name : "Unspecified filter"; - cupsArrayAdd(pids, pid_entry); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterChain: Could not fork to start %s: %s", - filter->name ? filter->name : "Unspecified filter", - strerror(errno)); - break; - } - - inputseekable = 0; - } - - // - // Close remaining pipes... - // - - if (filterfds[0][0] > 1) - close(filterfds[0][0]); - if (filterfds[0][1] > 1) - close(filterfds[0][1]); - if (filterfds[1][0] > 1) - close(filterfds[1][0]); - if (filterfds[1][1] > 1) - close(filterfds[1][1]); - - // - // Wait for the children to exit... - // - - retval = 0; - - while (cupsArrayCount(pids) > 0) - { - if ((pid = wait(&status)) < 0) - { - if (errno == EINTR && iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterChain: Job canceled, killing filters ..."); - for (pid_entry = (filter_function_pid_t *)cupsArrayFirst(pids); - pid_entry; - pid_entry = (filter_function_pid_t *)cupsArrayNext(pids)) - { - kill(pid_entry->pid, SIGTERM); - free(pid_entry); - } - break; - } - else - continue; - } - - key.pid = pid; - if ((pid_entry = (filter_function_pid_t *)cupsArrayFind(pids, &key)) != - NULL) - { - cupsArrayRemove(pids, pid_entry); - - if (status) - { - if (WIFEXITED(status)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterChain: %s (PID %d) stopped with status %d", - pid_entry->name, pid, WEXITSTATUS(status)); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterChain: %s (PID %d) crashed on signal %d", - pid_entry->name, pid, WTERMSIG(status)); - } - retval = 1; - } - else - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterChain: %s (PID %d) exited with no errors.", - pid_entry->name, pid); - } - - free(pid_entry); - } - } - - cupsArrayDelete(pids); - - return (retval); -} - - -// -// 'sanitize_device_uri()' - Remove authentication info from a device URI -// - -static char * // O - Sanitized URI -sanitize_device_uri(const char *uri, // I - Device URI - char *buf, // I - Buffer for output - size_t bufsize) // I - Size of buffer -{ - char *start, // Start of data after scheme - *slash, // First slash after scheme:// - *ptr; // Pointer into user@host:port part - - - // URI not supplied - if (!uri) - return (NULL); - - // Copy the device URI to a temporary buffer so we can sanitize any auth - // info in it... - strncpy(buf, uri, bufsize); - - // Find the end of the scheme:// part... - if ((ptr = strchr(buf, ':')) != NULL) - { - for (start = ptr + 1; *start; start ++) - if (*start != '/') - break; - - // Find the next slash (/) in the URI... - if ((slash = strchr(start, '/')) == NULL) - slash = start + strlen(start); // No slash, point to the end - - // Check for an @ sign before the slash... - if ((ptr = strchr(start, '@')) != NULL && ptr < slash) - { - // Found an @ sign and it is before the resource part, so we have - // an authentication string. Copy the remaining URI over the - // authentication string... - memmove(start, ptr + 1, strlen(ptr + 1) + 1); - } - } - - // Return the sanitized URI... - return (buf); -} - - -// -// 'cfFilterExternal()' - Filter function which calls an external -// classic CUPS filter or System V interface -// script, for example a (proprietary) printer -// driver which cannot be converted to a filter -// function or if it is too awkward or risky to -// convert for example when the printer -// hardware is not available for testing -// - -int // O - Error status -cfFilterExternal(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - cf_filter_external_t *params = (cf_filter_external_t *)parameters; - int i; - int is_backend = 0; // Do we call a CUPS backend? - int pid, // Process ID of filter - stderrpid, // Process ID for stderr logging process - wpid; // PID reported as terminated - int fd; // Temporary file descriptor - int backfd, sidefd; // file descriptors for back and side - // channels - int stderrpipe[2]; // Pipe to log stderr - cups_file_t *fp; // File pointer to read log lines - char tmp_name[BUFSIZ] = ""; - char buf[2048]; // Log line buffer - cf_loglevel_t log_level; // Log level of filter's log message - char *ptr1, *ptr2, - *msg, // Filter log message - *filter_name; // Filter name for logging - char filter_path[1024]; // Full path of the filter - char **argv, // Command line args for filter - **envp = NULL; // Environment variables for filter - int num_all_options = 0; - cups_option_t *all_options = NULL; - char job_id_str[16], - copies_str[16], - *options_str = NULL; - cups_option_t *opt; - int status = 65536; - int wstatus; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - - if (!params->filter || !params->filter[0]) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal: Filter executable path/command not specified"); - return (1); - } - - // Check whether back/side channel FDs are valid and not all-zero - // from calloc'ed filter_data - if (data->back_pipe[0] == 0 && data->back_pipe[1] == 0) - data->back_pipe[0] = data->back_pipe[1] = -1; - if (data->side_pipe[0] == 0 && data->side_pipe[1] == 0) - data->side_pipe[0] = data->side_pipe[1] = -1; - - // Select the correct end of the back/side channel pipes: - // [0] for filters, [1] for backends - is_backend = (params->exec_mode > 0 ? 1 : 0); - backfd = data->back_pipe[is_backend]; - sidefd = data->side_pipe[is_backend]; - - // Filter name for logging - if ((filter_name = strrchr(params->filter, '/')) != NULL) - filter_name ++; - else - filter_name = (char *)params->filter; - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Copy the current environment variables and add the ones from the - // parameters - // - - // Copy the environment in which the caller got started - if (environ) - for (i = 0; environ[i]; i ++) - cfFilterAddEnvVar(environ[i], NULL, &envp); - - // Set the environment variables given by the parameters - if (params->envp) - for (i = 0; params->envp[i]; i ++) - cfFilterAddEnvVar(params->envp[i], NULL, &envp); - - // Add CUPS_SERVERBIN to the beginning of PATH - ptr1 = cfFilterGetEnvVar("PATH", envp); - ptr2 = cfFilterGetEnvVar("CUPS_SERVERBIN", envp); - if (ptr2 && ptr2[0]) - { - if (ptr1 && ptr1[0]) - { - snprintf(buf, sizeof(buf), "%s/%s:%s", - ptr2, params->exec_mode > 0 ? "backend" : "filter", ptr1); - ptr1 = buf; - } - else - ptr1 = ptr2; - cfFilterAddEnvVar("PATH", ptr1, &envp); - } - - // Determine full path for the filter - if (params->filter[0] == '/' || - (ptr1 = cfFilterGetEnvVar("CUPS_SERVERBIN", envp)) == NULL || !ptr1[0]) - strncpy(filter_path, params->filter, sizeof(filter_path) - 1); - else - snprintf(filter_path, sizeof(filter_path), "%s/%s/%s", ptr1, - params->exec_mode > 0 ? "backend" : "filter", params->filter); - - // Log the resulting list of environment variable settings - // (with any authentication info removed) - if (log) - { - for (i = 0; envp[i]; i ++) - if (!strncmp(envp[i], "AUTH_", 5)) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterExternal (%s): envp[%d]: AUTH_%c****", - filter_name, i, envp[i][5]); - else if (!strncmp(envp[i], "DEVICE_URI=", 11)) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterExternal (%s): envp[%d]: DEVICE_URI=%s", - filter_name, i, sanitize_device_uri(envp[i] + 11, - buf, sizeof(buf))); - else - log(ld, CF_LOGLEVEL_DEBUG, "cfFilterExternal (%s): envp[%d]: %s", - filter_name, i, envp[i]); - } - - if (params->exec_mode < 2) - { - // - // Filter or backend for job execution - // - - // - // Join the options from the filter data and from the parameters - // If an option is present in both filter data and parameters, the - // value in the filter data has priority - // - - for (i = 0, opt = params->options; i < params->num_options; i ++, opt ++) - num_all_options = cupsAddOption(opt->name, opt->value, num_all_options, - &all_options); - for (i = 0, opt = data->options; i < data->num_options; i ++, opt ++) - num_all_options = cupsAddOption(opt->name, opt->value, num_all_options, - &all_options); - - // - // Create command line arguments for the CUPS filter - // - - if (params->exec_mode >= 0) - // CUPS filter or backend - - // CUPS filter allow input via stdin, so no 6th command line - // argument needed - argv = (char **)calloc(7, sizeof(char *)); - else - { - // System V interface script - - // Needs input via file name as 6th command line - // argument, not via stdin - argv = (char **)calloc(8, sizeof(char *)); - - fd = cupsTempFd(tmp_name, sizeof(tmp_name)); - if (fd < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal: Can't create temporary file."); - goto out; - } - int bytes; - while ((bytes = read(inputfd, buf, sizeof(buf))) > 0) - { - if (write(fd, buf, bytes) != bytes) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal: Can't copy input data to temporary file."); - close(fd); - goto out; - } - } - close(fd); - close(inputfd); - inputfd = open("/dev/null", O_RDONLY); - } - - // Numeric parameters - snprintf(job_id_str, sizeof(job_id_str) - 1, "%d", - data->job_id > 0 ? data->job_id : 1); - snprintf(copies_str, sizeof(copies_str) - 1, "%d", - data->copies > 0 ? data->copies : 1); - - // Options, build string of "Name1=Value1 Name2=Value2 ..." but use - // "Name" and "noName" instead for boolean options - for (i = 0, opt = all_options; i < num_all_options; i ++, opt ++) - { - if (strcasecmp(opt->value, "true") == 0 || - strcasecmp(opt->value, "false") == 0) - { - options_str = - (char *)realloc(options_str, - ((options_str ? strlen(options_str) : 0) + - strlen(opt->name) + - (strcasecmp(opt->value, "false") == 0 ? 2 : 0) + 2) * - sizeof(char)); - if (i == 0) - options_str[0] = '\0'; - sprintf(options_str + strlen(options_str), " %s%s", - (strcasecmp(opt->value, "false") == 0 ? "no" : ""), opt->name); - } - else - { - options_str = - (char *)realloc(options_str, - ((options_str ? strlen(options_str) : 0) + - strlen(opt->name) + strlen(opt->value) + 3) * - sizeof(char)); - if (i == 0) - options_str[0] = '\0'; - sprintf(options_str + strlen(options_str), " %s=%s", opt->name, - opt->value); - } - } - - // Find DEVICE_URI environment variable - if (params->exec_mode > 0) - for (i = 0; envp[i]; i ++) - if (strncmp(envp[i], "DEVICE_URI=", 11) == 0) - break; - - // Add items to array - argv[0] = strdup((params->exec_mode > 0 && envp[i] ? - (char *)sanitize_device_uri(envp[i] + 11, - buf, sizeof(buf)) : - (data->printer ? data->printer : - (char *)params->filter))); - argv[1] = job_id_str; - argv[2] = data->job_user ? data->job_user : "Unknown"; - argv[3] = data->job_title ? data->job_title : "Untitled"; - argv[4] = copies_str; - argv[5] = options_str ? options_str + 1 : ""; - if (params->exec_mode >= 0) - // CUPS Filter/backend: Input from stdin - argv[6] = NULL; - else - { - // System V Interface: Input file from 6th argument - argv[6] = tmp_name; - argv[7] = NULL; - } - } - else - { - // - // Backend in device discovery mode - // - - argv = (char **)calloc(2, sizeof(char *)); - argv[0] = strdup((char *)params->filter); - argv[1] = NULL; - } - - // Log the arguments - if (log) - for (i = 0; argv[i]; i ++) - log(ld, CF_LOGLEVEL_DEBUG, "cfFilterExternal (%s): argv[%d]: %s", - filter_name, i, argv[i]); - - // - // Execute the filter - // - - if (pipe(stderrpipe) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): Could not create pipe for stderr: %s", - filter_name, strerror(errno)); - return (1); - } - - if ((pid = fork()) == 0) - { - // - // Child process goes here... - // - // Update stdin/stdout/stderr as needed... - // - - if (inputfd != 0) - { - if (inputfd < 0) - { - inputfd = open("/dev/null", O_RDONLY); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): No input file descriptor supplied for CUPS filter - %s", - filter_name, strerror(errno)); - } - - if (inputfd > 0) - { - fcntl_add_cloexec(inputfd); - if (dup2(inputfd, 0) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): Failed to connect input file descriptor with CUPS filter's stdin - %s", - filter_name, strerror(errno)); - goto fd_error; - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterExternal (%s): Connected input file descriptor %d to CUPS filter's stdin.", - filter_name, inputfd); - close(inputfd); - } - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterExternal (%s): Input file descriptor is stdin, no redirection needed.", - filter_name); - - if (tmp_name[0]) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterExternal (%s): Input comes from temporary file %s, supplied as 6th command line argument.", - filter_name, tmp_name); - - if (outputfd != 1) - { - if (outputfd < 0) - outputfd = open("/dev/null", O_WRONLY); - - if (outputfd > 1) - { - fcntl_add_cloexec(outputfd); - dup2(outputfd, 1); - close(outputfd); - } - } - - if (strcasestr(params->filter, "gziptoany")) - { - // Send stderr to the Nirwana if we are running gziptoany, as - // gziptoany emits a false "PAGE: 1 1" - if ((fd = open("/dev/null", O_RDWR)) > 2) - { - fcntl_add_cloexec(fd); - dup2(fd, 2); - close(fd); - } - else - close(fd); - } - else - { - // Send stderr into pipe for logging - fcntl_add_cloexec(stderrpipe[1]); - dup2(stderrpipe[1], 2); - fcntl_add_nonblock(2); - } - close(stderrpipe[0]); - close(stderrpipe[1]); - - if (params->exec_mode < 2) // Not needed in discovery mode of backend - { - // Back channel - if (backfd != 3 && backfd >= 0) - { - dup2(backfd, 3); - close(backfd); - fcntl_add_nonblock(3); - } - else if (backfd < 0) - { - if ((backfd = open("/dev/null", O_RDWR)) > 3) - { - dup2(backfd, 3); - close(backfd); - } - else - close(backfd); - fcntl_add_nonblock(3); - } - - // Side channel - if (sidefd != 4 && sidefd >= 0) - { - dup2(sidefd, 4); - close(sidefd); - fcntl_add_nonblock(4); - } - else if (sidefd < 0) - { - if ((sidefd = open("/dev/null", O_RDWR)) > 4) - { - dup2(sidefd, 4); - close(sidefd); - } - else - close(sidefd); - fcntl_add_nonblock(4); - } - } - - // - // Execute command... - // - - execve(filter_path, argv, envp); - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): Execution of %s %s failed - %s", - filter_name, params->exec_mode > 0 ? "backend" : "filter", - filter_path, strerror(errno)); - - fd_error: - exit(errno); - } - else if (pid > 0) - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterExternal (%s): %s (PID %d) started.", - filter_name, filter_path, pid); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): Unable to fork process for %s %s", - filter_name, params->exec_mode > 0 ? "backend" : "filter", - filter_path); - close(stderrpipe[0]); - close(stderrpipe[1]); - status = 1; - goto out; - } - if (inputfd >= 0) - close(inputfd); - if (outputfd >= 0) - close(outputfd); - - // - // Log the filter's stderr - // - - if ((stderrpid = fork()) == 0) - { - // - // Child process goes here... - // - - close(stderrpipe[1]); - fp = cupsFileOpenFd(stderrpipe[0], "r"); - while (cupsFileGets(fp, buf, sizeof(buf))) - if (log) - { - if (strncmp(buf, "DEBUG: ", 7) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 7; - } - else if (strncmp(buf, "DEBUG2: ", 8) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 8; - } - else if (strncmp(buf, "INFO: ", 6) == 0) - { - log_level = CF_LOGLEVEL_INFO; - msg = buf + 6; - } - else if (strncmp(buf, "WARNING: ", 9) == 0) - { - log_level = CF_LOGLEVEL_WARN; - msg = buf + 9; - } - else if (strncmp(buf, "ERROR: ", 7) == 0) - { - log_level = CF_LOGLEVEL_ERROR; - msg = buf + 7; - } - else if (strncmp(buf, "PAGE: ", 6) == 0 || - strncmp(buf, "ATTR: ", 6) == 0 || - strncmp(buf, "STATE: ", 7) == 0 || - strncmp(buf, "PPD: ", 5) == 0) - { - log_level = CF_LOGLEVEL_CONTROL; - msg = buf; - } - else - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf; - } - if (log_level == CF_LOGLEVEL_CONTROL) - log(ld, log_level, msg); - else - log(ld, log_level, "cfFilterExternal (%s): %s", - filter_name, msg); - } - cupsFileClose(fp); - // No need to close the fd stderrpipe[0], as cupsFileClose(fp) does this - // already - // Ignore errors of the logging process - exit(0); - } - else if (stderrpid > 0) - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterExternal (%s): Logging (PID %d) started.", - filter_name, stderrpid); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): Unable to fork process for logging", - filter_name); - close(stderrpipe[0]); - close(stderrpipe[1]); - status = 1; - goto out; - } - - close(stderrpipe[0]); - close(stderrpipe[1]); - - // - // Wait for filter and logging processes to finish - // - - status = 0; - - while (pid > 0 || stderrpid > 0) - { - if ((wpid = wait(&wstatus)) < 0) - { - if (errno == EINTR && iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterExternal (%s): Job canceled, killing %s ...", - filter_name, params->exec_mode > 0 ? "backend" : "filter"); - kill(pid, SIGTERM); - pid = -1; - kill(stderrpid, SIGTERM); - stderrpid = -1; - break; - } - else - continue; - } - - // How did the filter terminate - if (wstatus) - { - if (WIFEXITED(wstatus)) - { - // Via exit() anywhere or return() in the main() function - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): %s (PID %d) stopped with status %d", - filter_name, - (wpid == pid ? - (params->exec_mode > 0 ? "Backend" : "Filter") : - "Logging"), - wpid, WEXITSTATUS(wstatus)); - } - else - { - // Via signal - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterExternal (%s): %s (PID %d) crashed on signal %d", - filter_name, - (wpid == pid ? - (params->exec_mode > 0 ? "Backend" : "Filter") : - "Logging"), - wpid, WTERMSIG(wstatus)); - } - status = 1; - } - else - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterExternal (%s): %s (PID %d) exited with no errors.", - filter_name, - (wpid == pid ? - (params->exec_mode > 0 ? "Backend" : "Filter") : "Logging"), - wpid); - } - if (wpid == pid) - pid = -1; - else if (wpid == stderrpid) - stderrpid = -1; - } - - // - // Clean up - // - - out: - if (params->exec_mode < 0) - unlink(tmp_name); - cupsFreeOptions(num_all_options, all_options); - if (options_str) - free(options_str); - free(argv[0]); - free(argv); - for (i = 0; envp[i]; i ++) - free(envp[i]); - free(envp); - - return (status); -} - - -// -// 'cfFilterOpenBackAndSidePipes()' - Open the pipes for the back -// channel and the side channel, so -// that the filter functions can -// communicate with a backend. Only -// needed if a CUPS backend (either -// implemented as filter function -// or called via -// cfFilterExternal()) is called -// with the same filter_data record -// as the filters. Usually to be -// called when populating the -// filter_data record. -// - -int // O - 0 on success, - // -1 on error -cfFilterOpenBackAndSidePipes(cf_filter_data_t *data) // O - FDs in filter_data - // record -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - - // - // Initialize FDs... - // - - data->back_pipe[0] = -1; - data->back_pipe[1] = -1; - data->side_pipe[0] = -1; - data->side_pipe[1] = -1; - - // - // Create the back channel pipe... - // - - if (pipe(data->back_pipe)) - goto out; - - // - // Set the "close on exec" flag on each end of the pipe... - // - - if (fcntl_add_cloexec(data->back_pipe[0])) - goto out; - - if (fcntl_add_cloexec(data->back_pipe[1])) - goto out; - - // - // Create a socket pair as bi-directional pipe for the side channel... - // - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, data->side_pipe)) - goto out; - - // - // Make the side channel FDs non-blocking... - // - - if (fcntl_add_nonblock(data->side_pipe[0])) - goto out; - if (fcntl_add_nonblock(data->side_pipe[1])) - goto out; - - if (fcntl_add_cloexec(data->side_pipe[0])) - goto out; - if (fcntl_add_cloexec(data->side_pipe[1])) - goto out; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Pipes for back and side channels opened"); - - // - // Return 0 indicating success... - // - - return (0); - - out: - - // - // Clean up after failure... - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Unable to open pipes for back and side channels"); - - cfFilterCloseBackAndSidePipes(data); - - return (-1); -} - - -// -// 'cfFilterCloseBackAndSidePipes()' - Close the pipes for the back -// hannel and the side channel. -// sually to be called when done -// with the filter chain . -// - -void -cfFilterCloseBackAndSidePipes(cf_filter_data_t *data) // I - FDs in filter_data - // record -{ - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - - // - // close all valid FDs... - // - - if (data->back_pipe[0] >= 0) - close(data->back_pipe[0]); - if (data->back_pipe[1] >= 0) - close(data->back_pipe[1]); - if (data->side_pipe[0] >= 0) - close(data->side_pipe[0]); - if (data->side_pipe[1] >= 0) - close(data->side_pipe[1]); - - // - // ... and invalidate them - // - - data->back_pipe[0] = -1; - data->back_pipe[1] = -1; - data->side_pipe[0] = -1; - data->side_pipe[1] = -1; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Closed the pipes for back and side channels"); -} diff --git a/cupsfilters/filter.h b/cupsfilters/filter.h deleted file mode 100644 index 6476dc393..000000000 --- a/cupsfilters/filter.h +++ /dev/null @@ -1,449 +0,0 @@ -// -// Filter functions header file for libcupsfilters. -// -// Copyright © 2020-2022 by Till Kamppeter. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_FILTER_H_ -# define _CUPS_FILTERS_FILTER_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Include necessary headers... -// - -# include "log.h" - -# include -# include -# include -# include - -# if defined(WIN32) || defined(__EMX__) -# include -# else -# include -# include -# endif // WIN32 || __EMX__ - -# include -# include - - -// -// Types and structures... -// - -typedef int (*cf_filter_iscanceledfunc_t)(void *data); - -typedef struct cf_filter_data_s -{ - char *printer; // Print queue name or NULL - int job_id; // Job ID or 0 - char *job_user; // Job user or NULL - char *job_title; // Job title or NULL - int copies; // Number of copies - // (1 if filter(s) should not treat it) - char *content_type; // Input MIME type (CUPS env variable - // CONTENT_TYPE) or NULL - char *final_content_type; // Output MIME type (CUPS env variable - // FINAL_CONTENT_TYPE) or NULL - ipp_t *job_attrs; // IPP attributes passed along with the job - ipp_t *printer_attrs; // Printer capabilities in IPP format - // (what is answered to get-printer-attributes - cups_page_header2_t *header; - // CUPS/PWG Raster header (optional) - int num_options; - cups_option_t *options; // Job options as key/value pairs - int back_pipe[2]; // File descriptors of backchannel pipe - int side_pipe[2]; // File descriptors of sidechannel pipe - cups_array_t *extension; // Extension data - cf_logfunc_t logfunc; // Logging function, NULL for no logging - void *logdata; // User data for logging function, can be NULL - cf_filter_iscanceledfunc_t iscanceledfunc; - // Function returning 1 when job is - // canceled, NULL for not supporting stop - // on cancel - void *iscanceleddata; // User data for is-canceled function, can be - // NULL -} cf_filter_data_t; - -typedef struct cf_filter_data_ext_s -{ - char *name; - void *ext; -} cf_filter_data_ext_t; - -typedef int (*cf_filter_function_t)(int inputfd, int outputfd, - int inputseekable, cf_filter_data_t *data, - void *parameters); - -typedef enum cf_filter_out_format_e // Possible output formats for filter - // functions -{ - CF_FILTER_OUT_FORMAT_PDF, // PDF - CF_FILTER_OUT_FORMAT_PDF_IMAGE, // Raster-only PDF - CF_FILTER_OUT_FORMAT_PCLM, // PCLM - CF_FILTER_OUT_FORMAT_CUPS_RASTER, // CUPS Raster - CF_FILTER_OUT_FORMAT_PWG_RASTER, // PWG Raster - CF_FILTER_OUT_FORMAT_APPLE_RASTER, // Apple Raster - CF_FILTER_OUT_FORMAT_PXL // PCL-XL -} cf_filter_out_format_t; - -typedef struct cf_filter_filter_in_chain_s // filter entry for CUPS array to - // be supplied to cfFilterChain() - // filter function -{ - cf_filter_function_t function; // Filter function to be called - void *parameters; // Parameters for this filter function call - char *name; // Name/comment, only for logging -} cf_filter_filter_in_chain_t; - -typedef struct cf_filter_external_s // Parameters for the - // cfFilterExternal() filter - // function -{ - const char *filter; // Path/Name of the CUPS filter to be called by - // this filter function, required - int exec_mode; // 0 if we call a CUPS filter, -1 if we call - // a System V interface script, 1 if we call a CUPS - // backend, 2 if we call a CUPS backend in - // device discovery mode - int num_options; // Extra options for the 5th command line - cups_option_t *options; // argument, options of filter_data have - // priority, 0/NULL if none - char **envp; // Additional environment variables, the already - // defined ones stay valid but can be overwritten - // by these ones, NULL if none -} cf_filter_external_t; - -typedef struct cf_filter_texttopdf_parameter_s // parameters container of - // environemnt variables needed - // by texttopdf filter - // function -{ - char *data_dir; - char *char_set; - char *content_type; - char *classification; -} cf_filter_texttopdf_parameter_t; - -typedef struct cf_filter_universal_parameter_s // Contains input and output - // type to be supplied to the - // universal function, and also - // parameters for - // cfFilterTextToPDF() -{ - char *actual_output_type; - cf_filter_texttopdf_parameter_t texttopdf_params; - const char *bannertopdf_template_dir; -} cf_filter_universal_parameter_t; - - -// -// Prototypes... -// - -extern void cfCUPSLogFunc(void *data, - cf_loglevel_t level, - const char *message, - ...); - - -extern int cfCUPSIsCanceledFunc(void *data); - - -extern void *cfFilterDataAddExt(cf_filter_data_t *data, const char *name, - void *ext); - - -extern void *cfFilterDataGetExt(cf_filter_data_t *data, const char *name); - - -extern void *cfFilterDataRemoveExt(cf_filter_data_t *data, const char *name); - - -extern char *cfFilterGetEnvVar(char *name, char **env); - - -extern int cfFilterAddEnvVar(char *name, char *value, char ***env); - - -extern int cfFilterTee(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Parameters: Filename/path (const char *) to copy the data to - - -extern int cfFilterPOpen(cf_filter_function_t filter_func, // I - Filter - // function - int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters, - int *filter_pid); - - -extern int cfFilterPClose(int fd, - int filter_pid, - cf_filter_data_t *data); - - -extern int cfFilterChain(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Parameters: Unsorted (!) CUPS array of cf_filter_filter_in_chain_t* -// List of filters to execute in a chain, next filter takes output of -// previous filter as input, all get the same filter data, parameters -// are supplied individually in the array - - -extern int cfFilterExternal(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Parameters: cf_filter_external_t* -// -// Path/Name of the external CUPS/System V filter or backend to be -// called by this filter function, specification whether we call a -// filter or a backend, and in case of backend, whether in job -// processing or discovery mode, extra options for the 5th command -// line argument, and extra environment variables -// -// CUPS filter: -// See "man filter" -// -// CUPS Backend: -// See "man backend" -// -// System V interface script: -// https://www.ibm.com/docs/en/aix/7.2?topic=configuration-printer-interface-scripts - - -extern int cfFilterOpenBackAndSidePipes(cf_filter_data_t *data); - - -extern void cfFilterCloseBackAndSidePipes(cf_filter_data_t *data); - - -extern int cfFilterGhostscript(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of output format via data->final_content_type -// or alternatively as parameter of type cf_filter_out_format_t. -// -// Output formats: PDF, raster-only PDF, PCLm, PostScript, CUPS Raster, -// PWG Raster, Apple Raster, PCL-XL -// -// Note: With the Apple Raster selection and a Ghostscript version -// without "appleraster" output device (9.55.x and older) the output -// is actually CUPS Raster but information about available color -// spaces and depths is taken from the urf-supported printer IPP -// attribute. This mode is for further processing with -// rastertopwg. With Ghostscript supporting Apple Raster output -// (9.56.0 and newer), we actually produce Apple Raster and no further -// filter is required. - - -extern int cfFilterBannerToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Parameters: const char* -// Template directory: In this directory there are the PDF template files -// for the banners and test pages. CUPS uses /usr/share/cups/data/ for that. -// If you submit a PDF file with added banner instructions as input file -// the template directory is not needed as the PDF input file itself is used -// as template. - - -extern int cfFilterImageToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - - -extern int cfFilterImageToRaster(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of output format via data->final_content_type -// -// Output formats: CUPS Raster, PWG Raster, Apple Raster, PCLM -// -// Note: On the Apple Raster, PWG Raster, and PCLm selection the -// output is actually CUPS Raster but information about available -// color spaces and depths is taken from the urf-supported or -// pwg-raster-document-type-supported printer IPP attributes or from a -// supplied CUPS Raster sample header. This mode is for further -// processing with rastertopwg and/or pwgtopclm. This can change in the -// future when we add Apple Raster and PWG Raster output support to -// this filter function. - - -extern int cfFilterMuPDFToPWG(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of output format via data->final_content_type -// -// Output formats: CUPS Raster, PWG Raster, Apple Raster, PCLm -// -// Note: With CUPS Raster, Apple Raster, or PCLm selections the output -// is actually PWG Raster but information about available color spaces -// and depths is taken from the urf-supported printer IPP attribute, -// the pclm- attributes, or from a supplied CUPS Raster sample header -// (PCLM is always sGray/sRGB 8-bit). These modes are for further -// processing with pwgtoraster or pwgtopclm. This can change in the -// future when MuPDF adds further output formats. - - -extern int cfFilterPCLmToRaster(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of output format via data->final_content_type -// -// Output formats: CUPS Raster, Apple Raster, or PWG Raster - - -extern int cfFilterPDFToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// (Optional) Specification of output format via -// data->final_content_type is used for determining whether this -// filter function does page logging for CUPS (output of "PAGE: XX YY" -// log messages) or not and also to determine whether the printer or -// driver generates copies or whether we have to send the pages -// repeatedly. -// -// Alternatively, the options "pdf-filter-page-logging", -// "hardware-copies", and "hardware-collate" can be used to manually -// do these selections. - - -extern int cfFilterPDFToRaster(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void* parameters); - -// Requires specification of output format via data->final_content_type -// -// Output formats: CUPS Raster, PWG Raster, Apple Raster, PCLm -// -// Note: With PCLm selection the output is actually PWG Raster but -// color space and depth will be 8-bit sRGB or SGray, the only color -// spaces supported by PCLm. This mode is for further processing with -// pwgtopclm. - - -extern int cfFilterPWGToRaster(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of output format via data->final_content_type -// -// Output formats: CUPS Raster, PWG Raster, Apple Raster - - -extern int cfFilterPWGToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of output format via data->final_content_type -// or alternatively as parameter of type cf_filter_out_format_t. -// -// Output formats: PDF, PCLm - - -extern int cfFilterRasterToPWG(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of output format via data->final_content_type -// -// Output formats: Apple Raster or PWG Raster, if PCLM is specified -// PWG Raster is produced to feed into the cfFilterPWGToPDF() filter -// function. - - -extern int cfFilterTextToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Parameters: cf_filter_texttopdf_parameter_t* -// -// Data directory (fonts, charsets), charset, content type (for prettyprint), -// classification (for overprint/watermark) - - -extern int cfFilterTextToText(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - - -extern int cfFilterUniversal(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of input format via data->content_type and -// job's final output format via data->final_content_type -// -// Parameters: cf_filter_universal_parameter_t -// -// Contains: actual_output_type: Format which the filter should -// actually produce if different from job's final output -// format, otherwise NULL to produce the job's final output -// format -// texttopdf_params: parameters for texttopdf - - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_FILTER_H_ diff --git a/cupsfilters/fontembed-private.h b/cupsfilters/fontembed-private.h deleted file mode 100644 index f47f2e613..000000000 --- a/cupsfilters/fontembed-private.h +++ /dev/null @@ -1,373 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPSFILTERS_FONTEMBED_H_ -#define _CUPSFILTERS_FONTEMBED_H_ - - -// -// Include necessary headers... -// - -#include -#include - - -// -// Constants and macros... -// - -#define _CF_FONTEMBED_OTF_F_FMT_CFF 0x10000 -#define _CF_FONTEMBED_OTF_F_DO_CHECKSUM 0x40000 - -#define _CF_FONTEMBED_OTF_TAG(a, b, c, d) (unsigned int)(((a) << 24) | \ - ((b) << 16) | \ - ((c) << 8) | (d)) -#define _CF_FONTEMBED_OTF_UNTAG(a) (((unsigned int)(a) >> 24) & 0xff), \ - (((unsigned int)(a) >> 16) & 0xff), \ - (((unsigned int)(a) >> 8) & 0xff), \ - (((unsigned int)(a)) & 0xff) - - -// -// Types and structures... -// - -// OpenType Font (OTF) handling - -typedef struct -{ - unsigned int tag; - unsigned int checkSum; - unsigned int offset; - unsigned int length; -} _cf_fontembed_otf_dir_ent_t; - -typedef struct -{ - FILE *f; - unsigned int numTTC, useTTC; - unsigned int version; - - unsigned short numTables; - _cf_fontembed_otf_dir_ent_t *tables; - - int flags; - unsigned short unitsPerEm; - unsigned short indexToLocFormat; // 0=short, 1=long - unsigned short numGlyphs; - - // optionally loaded data - unsigned int *glyphOffsets; - unsigned short numberOfHMetrics; - char *hmtx, *name, *cmap; - const char *unimap; // ptr to (3,1) or (3,0) cmap start - - // single glyf buffer, allocated large enough by __cfFontEmbedOTFLoadMore() - char *gly; - _cf_fontembed_otf_dir_ent_t *glyfTable; - -} _cf_fontembed_otf_file_t; - -// SFNT Font files - -struct _cf_fontembed_fontfile_s -{ - _cf_fontembed_otf_file_t *sfnt; - // ??? *cff; - char *stdname; - union - { - int fobj; - void *user; - }; -}; - -typedef struct _cf_fontembed_fontfile_s _cf_fontembed_fontfile_t; - -// Output callback function type - -typedef void (*_cf_fontembed_output_fn_t)(const char *buf, int len, - void *context); - -// Bit manipulation - -typedef int* _cf_fontembed_bit_set_t; - -// General font embedding - -typedef enum -{ - _CF_FONTEMBED_EMB_FMT_T1, // type1, with AFM/PFM,PFA/PFB - _CF_FONTEMBED_EMB_FMT_TTF, // sfnt, for TTF(glyf) - _CF_FONTEMBED_EMB_FMT_OTF, // sfnt+cff, for OTF(cff) - _CF_FONTEMBED_EMB_FMT_CFF, // cff, for raw CFF - _CF_FONTEMBED_EMB_FMT_STDFONT // don't embed (already present) -} _cf_fontembed_emb_format_t; - -typedef enum -{ - _CF_FONTEMBED_EMB_DEST_NATIVE, // just subsetting/conversion - _CF_FONTEMBED_EMB_DEST_PS, -//_CF_FONTEMBED_EMB_DEST_PS2, -//_CF_FONTEMBED_EMB_DEST_PDF13, - _CF_FONTEMBED_EMB_DEST_PDF16 -} _cf_fontembed_emb_dest_t; - -typedef enum -{ - _CF_FONTEMBED_EMB_RIGHT_FULL = 0, - _CF_FONTEMBED_EMB_RIGHT_NONE = 0x02, - _CF_FONTEMBED_EMB_RIGHT_READONLY = 0x04, - _CF_FONTEMBED_EMB_RIGHT_NO_SUBSET = 0x0100, - _CF_FONTEMBED_EMB_RIGHT_BITMAPONLY = 0x0200 -} _cf_fontembed_emb_right_t; - -typedef enum -{ - _CF_FONTEMBED_EMB_A_MULTIBYTE = 0x01, // embedd as multibyte font? - _CF_FONTEMBED_EMB_A_SUBSET = 0x02, // do subsetting? - _CF_FONTEMBED_EMB_A_T1_TO_CFF = 0x04, // convert Type1 to CFF? - _CF_FONTEMBED_EMB_A_CFF_TO_OTF = 0x08, // wrap CFF(from input or - // T1+CONVERT_CFF) in sfnt? (OTF) - _CF_FONTEMBED_EMB_A_OTF_TO_CFF = 0x10, // unwrap CFF - - _CF_FONTEMBED_EMB_A_CLOSE_FONTFILE = 0x8000 -} _cf_fontembed_emb_action_t; - -typedef enum -{ - _CF_FONTEMBED_EMB_C_MUST_SUBSET = 0x01, // (fail, when not possible) - _CF_FONTEMBED_EMB_C_EDITABLE_SUBSET = 0x02, // (...) - _CF_FONTEMBED_EMB_C_NEVER_SUBSET = 0x04, // (...) - - _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE = 0x08, // always use multibyte fonts - - _CF_FONTEMBED_EMB_C_PDF_OT = 0x10, // output TTF/OTF (esp. CFF to - // OTF) - _CF_FONTEMBED_EMB_C_KEEP_T1 = 0x20, // don't convert T1 to CFF - - _CF_FONTEMBED_EMB_C_TAKE_FONTFILE = 0x8000 // take ownership of fontfile -} _cf_fontembed_emb_constraint_t; - -typedef struct _cf_fontembed_emb_params_s -{ - _cf_fontembed_emb_format_t intype; - _cf_fontembed_emb_format_t outtype; - _cf_fontembed_emb_dest_t dest; - - _cf_fontembed_emb_action_t plan; - - // font infos - _cf_fontembed_fontfile_t *font; - _cf_fontembed_emb_right_t rights; -// public: - _cf_fontembed_bit_set_t subset; -} _cf_fontembed_emb_params_t; - -// PDF file font embedding -typedef struct -{ - char *fontname; - unsigned int flags; - - // for the following: 0 = not set/invalid - int bbxmin, bbymin, bbxmax, bbymax; - int italicAngle; // >= 90: not set/invalid - int ascent; - int descent; - int capHeight; - int stemV; - // optional, default = 0: - int xHeight; - int avgWidth; - - // CID-additions: - char *panose; // 12 bytes - char *registry, *ordering; - int supplement; - - char data[1]; // used for storing e.g. > fontname -} _cf_fontembed_emb_pdf_font_descr_t; - -typedef struct -{ - // normal font - int first, last; - int *widths; - - // multibyte font - int default_width; - int *warray; // format: (len c w ... w)* - // if (len < 0) { c1 (c2 = c1 + (-len)) w } else { c w[len] }, - // terminated by len == 0 - - int data[1]; -} _cf_fontembed_emb_pdf_font_widths_t; - - -// -// Prototypes... -// - -// OpenType Font (OTF) handling - -// To load TTC collections: append e.g. "/3" for the third font in the file. -_cf_fontembed_otf_file_t *_cfFontEmbedOTFLoad(const char *file); -void _cfFontEmbedOTFClose(_cf_fontembed_otf_file_t *otf); - -char *_cfFontEmbedOTFGetTable(_cf_fontembed_otf_file_t *otf, unsigned int tag, - int *ret_len); - -int _cfFontEmbedOTFGetWidth(_cf_fontembed_otf_file_t *otf, unsigned short gid); -const char *_cfFontEmbedOTFGetName(_cf_fontembed_otf_file_t *otf, - int platformID, int encodingID, - int languageID, int nameID, int *ret_len); -int _cfFontEmbedOTFGetGlyph(_cf_fontembed_otf_file_t *otf, unsigned short gid); -unsigned short _cfFontEmbedOTFFromUnicode(_cf_fontembed_otf_file_t *otf, - int unicode); - -// TODO?! allow glyphs==NULL for non-subsetting table reduction? -int _cfFontEmbedOTFSubSet(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_bit_set_t glyphs, - _cf_fontembed_output_fn_t output, void *context); -int _cfFontEmbedOTFTTCExtract(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_output_fn_t output, void *context); -int _cfFontEmbedOTFSubSetCFF(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_bit_set_t glyphs, - _cf_fontembed_output_fn_t output, void *context); -int _cfFontEmbedOTFCFFExtract(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_output_fn_t output, void *context); - -// SFNT Font files - -_cf_fontembed_fontfile_t - *_cfFontEmbedFontFileOpenSFNT(_cf_fontembed_otf_file_t *otf); -_cf_fontembed_fontfile_t *_cfFontEmbedFontFileOpenStd(const char *name); -void _cfFontEmbedFontFileClose(_cf_fontembed_fontfile_t *ff); - -// General font embedding - -_cf_fontembed_emb_params_t - *_cfFontEmbedEmbNew(_cf_fontembed_fontfile_t *font, - _cf_fontembed_emb_dest_t dest, - _cf_fontembed_emb_constraint_t mode); -// _cfFontEmbedEmbEmbed does only the "binary" part -int _cfFontEmbedEmbEmbed(_cf_fontembed_emb_params_t *emb, - _cf_fontembed_output_fn_t output, void *context); - // returns number of bytes written -void _cfFontEmbedEmbClose(_cf_fontembed_emb_params_t *emb); - -// PDF file font embedding - -const char *_cfFontEmbedEmbPDFGetFontSubType(_cf_fontembed_emb_params_t *emb); -const char *_cfFontEmbedEmbPDFGetFontFileKey(_cf_fontembed_emb_params_t *emb); -const char - *_cfFontEmbedEmbPDFGetFontFileSubType(_cf_fontembed_emb_params_t *emb); - -_cf_fontembed_emb_pdf_font_descr_t - *_cfFontEmbedEmbPDFFontDescr(_cf_fontembed_emb_params_t *emb); -_cf_fontembed_emb_pdf_font_widths_t - *_cfFontEmbedEmbPDFFontWidths(_cf_fontembed_emb_params_t *emb); - -/** TODO elsewhere **/ -char *_cfFontEmbedEmbPDFSimpleFontDescr(_cf_fontembed_emb_params_t *emb, - _cf_fontembed_emb_pdf_font_descr_t *fdes, - int fontfile_obj_ref); -char *_cfFontEmbedEmbPDFSimpleFont(_cf_fontembed_emb_params_t *emb, - _cf_fontembed_emb_pdf_font_descr_t *fdes, - _cf_fontembed_emb_pdf_font_widths_t *fwid, - int fontdescr_obj_ref); -char *_cfFontEmbedEmbPDFSimpleCIDFont(_cf_fontembed_emb_params_t *emb, - const char *fontname, - int descendant_obj_ref); -char *_cfFontEmbedEmbPDFSimpleStdFont(_cf_fontembed_emb_params_t *emb); - - -// -// Inline functions... -// - -// Bit manipulation - -static inline void -_cfFontEmbedBitSet(_cf_fontembed_bit_set_t bs, - int num) -{ - bs[num / (8 * sizeof(int))] |= 1 << (num % (8 * sizeof(int))); -} - - -static inline int -_cfFontEmbedBitCheck(_cf_fontembed_bit_set_t bs, - int num) -{ - return bs [num / (8 * sizeof(int))] & 1 << (num % (8 * sizeof(int))); -} - - -// Use free() when done. returns NULL on bad_alloc -static inline _cf_fontembed_bit_set_t -_cfFontEmbedBitSetNew(int size) -{ - return (_cf_fontembed_bit_set_t)calloc(1, ((size + 8 * sizeof(int) - 1) & - ~(8 * sizeof(int) - 1)) / 8); -} - - -static inline int -_cfFontEmbedBitsUsed(_cf_fontembed_bit_set_t bits, - int size) // {{{ returns true if any bit is used -{ - size = (size + 8 * sizeof(int) - 1) / (8 * sizeof(int)); - while (size > 0) - { - if (*bits) - return (1); - bits ++; - size --; - } - return (0); -} -// }}} - -// General font embedding - -// TODO: encoding, TODO: ToUnicode -static inline void -_cfFontEmbedEmbSet(_cf_fontembed_emb_params_t *emb, - int unicode, - unsigned short gid) // {{{ -{ - if (emb->subset) - { - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) - { - _cfFontEmbedBitSet(emb->subset, gid); - // ToUnicode.add(gid, unicode); - } - else - { - // TODO ... encoding - } - } -} -// }}} - -// TODO: encoding?, TODO: non-sfnt -static inline unsigned short -_cfFontEmbedEmbGet(_cf_fontembed_emb_params_t *emb, int unicode) // {{{ gid -{ - const unsigned short gid = _cfFontEmbedOTFFromUnicode(emb->font->sfnt, - unicode); - _cfFontEmbedEmbSet(emb, unicode, gid); - return (gid); -} -// }}} - - -#endif // !_CUPSFILTERS_FONTEMBED_H_ diff --git a/cupsfilters/fontembed/README b/cupsfilters/fontembed/README deleted file mode 100644 index d5c53658c..000000000 --- a/cupsfilters/fontembed/README +++ /dev/null @@ -1,37 +0,0 @@ -fontembed/ - font embedding and subsetting functions in libcupsfilters ----------------------------------------------------------------------- - -Currently used for the cfFilterTextToPDF() filter function (files -cupsfilters/texttopdf.c, cupsfilters/pdfutils.c, -cupsfilters/pdfutils.h). - -These functions implement all the stuff required to -embed and subset TrueType fonts, as for example -required in PDF files. There are completely self-contained, -although a FreeType binding might come sometime in the future. - -Currently glyf-flavored TrueType is fully supported, -for OTF, i.e. CFF-flavored TrueType/OpenType, subsetting is not -done; but embedding does work. -And single-byte mode does needs work/thought wrt. to encodings. -Also reencoding and conversion of Type1 to CFF is planned. -PostScript embedding is another goal of the project. - -The most important issue that needs fixing is support for -text extraction in the PDF multibyte case (most common), -which requires ToUnicode support; some preparation is already done. - -Usage ------ -(TODO)... see cupsfilters/fontembed/test-pdf.c ... - - * for direct sfnt access and for embedding use - - - -License -------- -Copyright (c) 2008,2012 by Tobias Hoffmann. - -Licensed under Apache License v2.0. See the file "LICENSE" for more -information. diff --git a/cupsfilters/fontembed/aglfn13.c b/cupsfilters/fontembed/aglfn13.c deleted file mode 100644 index 4165734d8..000000000 --- a/cupsfilters/fontembed/aglfn13.c +++ /dev/null @@ -1,233 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include - -const char *aglfn13(unsigned short uni); - - -#ifdef WITH_AGLFN -static const char *agl_l207e[] = -{ - "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", - "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", "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"}; - -static const char *agl_la1ff[] = -{ - "exclamdown", "cent", "sterling", "currency", "yen", "brokenbar", "section", "dieresis", - "copyright", "ordfeminine", "guillemotleft", "logicalnot", "registered", "macron", "degree", "plusminus", - 0, 0, "acute", 0, "paragraph", "periodcentered", "cedilla", 0, - "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"}; - -static const char *agl_l1007f[] = -{ - "Amacron", "amacron", "Abreve", "abreve", "Aogonek", "aogonek", "Cacute", "cacute", - "Ccircumflex", "ccircumflex", "Cdotaccent", "cdotaccent", "Ccaron", "ccaron", "Dcaron", "dcaron", - "Dcroat", "dcroat", "Emacron", "emacron", "Ebreve", "ebreve", "Edotaccent", "edotaccent", - "Eogonek", "eogonek", "Ecaron", "ecaron", "Gcircumflex", "gcircumflex", "Gbreve", "gbreve", - "Gdotaccent", "gdotaccent", "Gcommaaccent", "gcommaaccent", "Hcircumflex", "hcircumflex", "Hbar", "hbar", - "Itilde", "itilde", "Imacron", "imacron", "Ibreve", "ibreve", "Iogonek", "iogonek", - "Idotaccent", "dotlessi", "IJ", "ij", "Jcircumflex", "jcircumflex", "Kcommaaccent", "kcommaaccent", - "kgreenlandic", "Lacute", "lacute", "Lcommaaccent", "lcommaaccent", "Lcaron", "lcaron", "Ldot", - "ldot", "Lslash", "lslash", "Nacute", "nacute", "Ncommaaccent", "ncommaaccent", "Ncaron", - "ncaron", "napostrophe", "Eng", "eng", "Omacron", "omacron", "Obreve", "obreve", - "Ohungarumlaut", "ohungarumlaut", "OE", "oe", "Racute", "racute", "Rcommaaccent", "rcommaaccent", - "Rcaron", "rcaron", "Sacute", "sacute", "Scircumflex", "scircumflex", "Scedilla", "scedilla", - "Scaron", "scaron", "Tcommaaccent", "tcommaaccent", "Tcaron", "tcaron", "Tbar", "tbar", - "Utilde", "utilde", "Umacron", "umacron", "Ubreve", "ubreve", "Uring", "uring", - "Uhungarumlaut", "uhungarumlaut", "Uogonek", "uogonek", "Wcircumflex", "wcircumflex", "Ycircumflex", "ycircumflex", - "Ydieresis", "Zacute", "zacute", "Zdotaccent", "zdotaccent", "Zcaron", "zcaron", "longs"}; - -struct agl_lt { unsigned short uid; const char *name; } agl_lxx[] = -{ - {0x0192, "florin"}, {0x01A0, "Ohorn"}, {0x01A1, "ohorn"}, {0x01AF, "Uhorn"}, - {0x01B0, "uhorn"}, {0x01E6, "Gcaron"}, {0x01E7, "gcaron"}, {0x01FA, "Aringacute"}, - {0x01FB, "aringacute"}, {0x01FC, "AEacute"}, {0x01FD, "aeacute"}, {0x01FE, "Oslashacute"}, - {0x01FF, "oslashacute"}, {0x0218, "Scommaaccent"}, {0x0219, "scommaaccent"}, {0x02BC, "afii57929"}, - {0x02BD, "afii64937"}, {0x02C6, "circumflex"}, {0x02C7, "caron"}, {0x02D8, "breve"}, - {0x02D9, "dotaccent"}, {0x02DA, "ring"}, {0x02DB, "ogonek"}, {0x02DC, "tilde"}, - {0x02DD, "hungarumlaut"}, {0x0300, "gravecomb"}, {0x0301, "acutecomb"}, {0x0303, "tildecomb"}, - {0x0309, "hookabovecomb"}, {0x0323, "dotbelowcomb"}, {0x0384, "tonos"}, {0x0385, "dieresistonos"}, - {0x0386, "Alphatonos"}, {0x0387, "anoteleia"}, {0x0388, "Epsilontonos"}, {0x0389, "Etatonos"}, - {0x038A, "Iotatonos"}, {0x038C, "Omicrontonos"}, {0x038E, "Upsilontonos"}, {0x038F, "Omegatonos"}, - {0x0390, "iotadieresistonos"}, {0x0391, "Alpha"}, {0x0392, "Beta"}, {0x0393, "Gamma"}, - {0x0394, "Delta"}, {0x0395, "Epsilon"}, {0x0396, "Zeta"}, {0x0397, "Eta"}, - {0x0398, "Theta"}, {0x0399, "Iota"}, {0x039A, "Kappa"}, {0x039B, "Lambda"}, - {0x039C, "Mu"}, {0x039D, "Nu"}, {0x039E, "Xi"}, {0x039F, "Omicron"}, - {0x03A0, "Pi"}, {0x03A1, "Rho"}, {0x03A3, "Sigma"}, {0x03A4, "Tau"}, - {0x03A5, "Upsilon"}, {0x03A6, "Phi"}, {0x03A7, "Chi"}, {0x03A8, "Psi"}, - {0x03A9, "Omega"}, {0x03AA, "Iotadieresis"}, {0x03AB, "Upsilondieresis"}, {0x03AC, "alphatonos"}, - {0x03AD, "epsilontonos"}, {0x03AE, "etatonos"}, {0x03AF, "iotatonos"}, {0x03B0, "upsilondieresistonos"}, - {0x03B1, "alpha"}, {0x03B2, "beta"}, {0x03B3, "gamma"}, {0x03B4, "delta"}, - {0x03B5, "epsilon"}, {0x03B6, "zeta"}, {0x03B7, "eta"}, {0x03B8, "theta"}, - {0x03B9, "iota"}, {0x03BA, "kappa"}, {0x03BB, "lambda"}, {0x03BC, "mu"}, - {0x03BD, "nu"}, {0x03BE, "xi"}, {0x03BF, "omicron"}, {0x03C0, "pi"}, - {0x03C1, "rho"}, {0x03C2, "sigma1"}, {0x03C3, "sigma"}, {0x03C4, "tau"}, - {0x03C5, "upsilon"}, {0x03C6, "phi"}, {0x03C7, "chi"}, {0x03C8, "psi"}, - {0x03C9, "omega"}, {0x03CA, "iotadieresis"}, {0x03CB, "upsilondieresis"}, - {0x03CC, "omicrontonos"}, {0x03CD, "upsilontonos"}, {0x03CE, "omegatonos"}, - {0x03D1, "theta1"}, {0x03D2, "Upsilon1"}, {0x03D5, "phi1"}, {0x03D6, "omega1"}, - {0x0401, "afii10023"}, {0x0402, "afii10051"}, {0x0403, "afii10052"}, {0x0404, "afii10053"}, - {0x0405, "afii10054"}, {0x0406, "afii10055"}, {0x0407, "afii10056"}, {0x0408, "afii10057"}, - {0x0409, "afii10058"}, {0x040A, "afii10059"}, {0x040B, "afii10060"}, {0x040C, "afii10061"}, - {0x040E, "afii10062"}, {0x040F, "afii10145"}, {0x0410, "afii10017"}, {0x0411, "afii10018"}, - {0x0412, "afii10019"}, {0x0413, "afii10020"}, {0x0414, "afii10021"}, {0x0415, "afii10022"}, - {0x0416, "afii10024"}, {0x0417, "afii10025"}, {0x0418, "afii10026"}, {0x0419, "afii10027"}, - {0x041A, "afii10028"}, {0x041B, "afii10029"}, {0x041C, "afii10030"}, {0x041D, "afii10031"}, - {0x041E, "afii10032"}, {0x041F, "afii10033"}, {0x0420, "afii10034"}, {0x0421, "afii10035"}, - {0x0422, "afii10036"}, {0x0423, "afii10037"}, {0x0424, "afii10038"}, {0x0425, "afii10039"}, - {0x0426, "afii10040"}, {0x0427, "afii10041"}, {0x0428, "afii10042"}, {0x0429, "afii10043"}, - {0x042A, "afii10044"}, {0x042B, "afii10045"}, {0x042C, "afii10046"}, {0x042D, "afii10047"}, - {0x042E, "afii10048"}, {0x042F, "afii10049"}, {0x0430, "afii10065"}, {0x0431, "afii10066"}, - {0x0432, "afii10067"}, {0x0433, "afii10068"}, {0x0434, "afii10069"}, {0x0435, "afii10070"}, - {0x0436, "afii10072"}, {0x0437, "afii10073"}, {0x0438, "afii10074"}, {0x0439, "afii10075"}, - {0x043A, "afii10076"}, {0x043B, "afii10077"}, {0x043C, "afii10078"}, {0x043D, "afii10079"}, - {0x043E, "afii10080"}, {0x043F, "afii10081"}, {0x0440, "afii10082"}, {0x0441, "afii10083"}, - {0x0442, "afii10084"}, {0x0443, "afii10085"}, {0x0444, "afii10086"}, {0x0445, "afii10087"}, - {0x0446, "afii10088"}, {0x0447, "afii10089"}, {0x0448, "afii10090"}, {0x0449, "afii10091"}, - {0x044A, "afii10092"}, {0x044B, "afii10093"}, {0x044C, "afii10094"}, {0x044D, "afii10095"}, - {0x044E, "afii10096"}, {0x044F, "afii10097"}, {0x0451, "afii10071"}, {0x0452, "afii10099"}, - {0x0453, "afii10100"}, {0x0454, "afii10101"}, {0x0455, "afii10102"}, {0x0456, "afii10103"}, - {0x0457, "afii10104"}, {0x0458, "afii10105"}, {0x0459, "afii10106"}, {0x045A, "afii10107"}, - {0x045B, "afii10108"}, {0x045C, "afii10109"}, {0x045E, "afii10110"}, {0x045F, "afii10193"}, - {0x0462, "afii10146"}, {0x0463, "afii10194"}, {0x0472, "afii10147"}, {0x0473, "afii10195"}, - {0x0474, "afii10148"}, {0x0475, "afii10196"}, {0x0490, "afii10050"}, {0x0491, "afii10098"}, - {0x04D9, "afii10846"}, {0x05B0, "afii57799"}, {0x05B1, "afii57801"}, {0x05B2, "afii57800"}, - {0x05B3, "afii57802"}, {0x05B4, "afii57793"}, {0x05B5, "afii57794"}, {0x05B6, "afii57795"}, - {0x05B7, "afii57798"}, {0x05B8, "afii57797"}, {0x05B9, "afii57806"}, {0x05BB, "afii57796"}, - {0x05BC, "afii57807"}, {0x05BD, "afii57839"}, {0x05BE, "afii57645"}, {0x05BF, "afii57841"}, - {0x05C0, "afii57842"}, {0x05C1, "afii57804"}, {0x05C2, "afii57803"}, {0x05C3, "afii57658"}, - {0x05D0, "afii57664"}, {0x05D1, "afii57665"}, {0x05D2, "afii57666"}, {0x05D3, "afii57667"}, - {0x05D4, "afii57668"}, {0x05D5, "afii57669"}, {0x05D6, "afii57670"}, {0x05D7, "afii57671"}, - {0x05D8, "afii57672"}, {0x05D9, "afii57673"}, {0x05DA, "afii57674"}, {0x05DB, "afii57675"}, - {0x05DC, "afii57676"}, {0x05DD, "afii57677"}, {0x05DE, "afii57678"}, {0x05DF, "afii57679"}, - {0x05E0, "afii57680"}, {0x05E1, "afii57681"}, {0x05E2, "afii57682"}, {0x05E3, "afii57683"}, - {0x05E4, "afii57684"}, {0x05E5, "afii57685"}, {0x05E6, "afii57686"}, {0x05E7, "afii57687"}, - {0x05E8, "afii57688"}, {0x05E9, "afii57689"}, {0x05EA, "afii57690"}, {0x05F0, "afii57716"}, - {0x05F1, "afii57717"}, {0x05F2, "afii57718"}, {0x060C, "afii57388"}, {0x061B, "afii57403"}, - {0x061F, "afii57407"}, {0x0621, "afii57409"}, {0x0622, "afii57410"}, {0x0623, "afii57411"}, - {0x0624, "afii57412"}, {0x0625, "afii57413"}, {0x0626, "afii57414"}, {0x0627, "afii57415"}, - {0x0628, "afii57416"}, {0x0629, "afii57417"}, {0x062A, "afii57418"}, {0x062B, "afii57419"}, - {0x062C, "afii57420"}, {0x062D, "afii57421"}, {0x062E, "afii57422"}, {0x062F, "afii57423"}, - {0x0630, "afii57424"}, {0x0631, "afii57425"}, {0x0632, "afii57426"}, {0x0633, "afii57427"}, - {0x0634, "afii57428"}, {0x0635, "afii57429"}, {0x0636, "afii57430"}, {0x0637, "afii57431"}, - {0x0638, "afii57432"}, {0x0639, "afii57433"}, {0x063A, "afii57434"}, {0x0640, "afii57440"}, - {0x0641, "afii57441"}, {0x0642, "afii57442"}, {0x0643, "afii57443"}, {0x0644, "afii57444"}, - {0x0645, "afii57445"}, {0x0646, "afii57446"}, {0x0647, "afii57470"}, {0x0648, "afii57448"}, - {0x0649, "afii57449"}, {0x064A, "afii57450"}, {0x064B, "afii57451"}, {0x064C, "afii57452"}, - {0x064D, "afii57453"}, {0x064E, "afii57454"}, {0x064F, "afii57455"}, {0x0650, "afii57456"}, - {0x0651, "afii57457"}, {0x0652, "afii57458"}, {0x0660, "afii57392"}, {0x0661, "afii57393"}, - {0x0662, "afii57394"}, {0x0663, "afii57395"}, {0x0664, "afii57396"}, {0x0665, "afii57397"}, - {0x0666, "afii57398"}, {0x0667, "afii57399"}, {0x0668, "afii57400"}, {0x0669, "afii57401"}, - {0x066A, "afii57381"}, {0x066D, "afii63167"}, {0x0679, "afii57511"}, {0x067E, "afii57506"}, - {0x0686, "afii57507"}, {0x0688, "afii57512"}, {0x0691, "afii57513"}, {0x0698, "afii57508"}, - {0x06A4, "afii57505"}, {0x06AF, "afii57509"}, {0x06BA, "afii57514"}, {0x06D2, "afii57519"}, - {0x06D5, "afii57534"}, {0x1E80, "Wgrave"}, {0x1E81, "wgrave"}, {0x1E82, "Wacute"}, - {0x1E83, "wacute"}, {0x1E84, "Wdieresis"}, {0x1E85, "wdieresis"}, {0x1EF2, "Ygrave"}, - {0x1EF3, "ygrave"}, {0x200C, "afii61664"}, {0x200D, "afii301"}, {0x200E, "afii299"}, - {0x200F, "afii300"}, {0x2012, "figuredash"}, {0x2013, "endash"}, {0x2014, "emdash"}, - {0x2015, "afii00208"}, {0x2017, "underscoredbl"}, {0x2018, "quoteleft"}, {0x2019, "quoteright"}, - {0x201A, "quotesinglbase"}, {0x201B, "quotereversed"}, {0x201C, "quotedblleft"}, {0x201D, "quotedblright"}, - {0x201E, "quotedblbase"}, {0x2020, "dagger"}, {0x2021, "daggerdbl"}, {0x2022, "bullet"}, - {0x2024, "onedotenleader"}, {0x2025, "twodotenleader"}, {0x2026, "ellipsis"}, {0x202C, "afii61573"}, - {0x202D, "afii61574"}, {0x202E, "afii61575"}, {0x2030, "perthousand"}, {0x2032, "minute"}, - {0x2033, "second"}, {0x2039, "guilsinglleft"}, {0x203A, "guilsinglright"}, {0x203C, "exclamdbl"}, - {0x2044, "fraction"}, {0x20A1, "colonmonetary"}, {0x20A3, "franc"}, {0x20A4, "lira"}, - {0x20A7, "peseta"}, {0x20AA, "afii57636"}, {0x20AB, "dong"}, {0x20AC, "Euro"}, - {0x2105, "afii61248"}, {0x2111, "Ifraktur"}, {0x2113, "afii61289"}, {0x2116, "afii61352"}, - {0x2118, "weierstrass"}, {0x211C, "Rfraktur"}, {0x211E, "prescription"}, {0x2122, "trademark"}, - {0x212E, "estimated"}, {0x2135, "aleph"}, {0x2153, "onethird"}, {0x2154, "twothirds"}, - {0x215B, "oneeighth"}, {0x215C, "threeeighths"}, {0x215D, "fiveeighths"}, {0x215E, "seveneighths"}, - {0x2190, "arrowleft"}, {0x2191, "arrowup"}, {0x2192, "arrowright"}, {0x2193, "arrowdown"}, - {0x2194, "arrowboth"}, {0x2195, "arrowupdn"}, {0x21A8, "arrowupdnbse"}, {0x21B5, "carriagereturn"}, - {0x21D0, "arrowdblleft"}, {0x21D1, "arrowdblup"}, {0x21D2, "arrowdblright"}, {0x21D3, "arrowdbldown"}, - {0x21D4, "arrowdblboth"}, {0x2200, "universal"}, {0x2202, "partialdiff"}, {0x2203, "existential"}, - {0x2205, "emptyset"}, {0x2207, "gradient"}, {0x2208, "element"}, {0x2209, "notelement"}, - {0x220B, "suchthat"}, {0x220F, "product"}, {0x2211, "summation"}, {0x2212, "minus"}, - {0x2217, "asteriskmath"}, {0x221A, "radical"}, {0x221D, "proportional"}, {0x221E, "infinity"}, - {0x221F, "orthogonal"}, {0x2220, "angle"}, {0x2227, "logicaland"}, {0x2228, "logicalor"}, - {0x2229, "intersection"}, {0x222A, "union"}, {0x222B, "integral"}, {0x2234, "therefore"}, - {0x223C, "similar"}, {0x2245, "congruent"}, {0x2248, "approxequal"}, {0x2260, "notequal"}, - {0x2261, "equivalence"}, {0x2264, "lessequal"}, {0x2265, "greaterequal"}, {0x2282, "propersubset"}, - {0x2283, "propersuperset"}, {0x2284, "notsubset"}, {0x2286, "reflexsubset"}, {0x2287, "reflexsuperset"}, - {0x2295, "circleplus"}, {0x2297, "circlemultiply"}, {0x22A5, "perpendicular"}, {0x22C5, "dotmath"}, - {0x2302, "house"}, {0x2310, "revlogicalnot"}, {0x2320, "integraltp"}, {0x2321, "integralbt"}, - {0x2329, "angleleft"}, {0x232A, "angleright"}, {0x2500, "SF100000"}, {0x2502, "SF110000"}, - {0x250C, "SF010000"}, {0x2510, "SF030000"}, {0x2514, "SF020000"}, {0x2518, "SF040000"}, - {0x251C, "SF080000"}, {0x2524, "SF090000"}, {0x252C, "SF060000"}, {0x2534, "SF070000"}, - {0x253C, "SF050000"}, {0x2550, "SF430000"}, {0x2551, "SF240000"}, {0x2552, "SF510000"}, - {0x2553, "SF520000"}, {0x2554, "SF390000"}, {0x2555, "SF220000"}, {0x2556, "SF210000"}, - {0x2557, "SF250000"}, {0x2558, "SF500000"}, {0x2559, "SF490000"}, {0x255A, "SF380000"}, - {0x255B, "SF280000"}, {0x255C, "SF270000"}, {0x255D, "SF260000"}, {0x255E, "SF360000"}, - {0x255F, "SF370000"}, {0x2560, "SF420000"}, {0x2561, "SF190000"}, {0x2562, "SF200000"}, - {0x2563, "SF230000"}, {0x2564, "SF470000"}, {0x2565, "SF480000"}, {0x2566, "SF410000"}, - {0x2567, "SF450000"}, {0x2568, "SF460000"}, {0x2569, "SF400000"}, {0x256A, "SF540000"}, - {0x256B, "SF530000"}, {0x256C, "SF440000"}, {0x2580, "upblock"}, {0x2584, "dnblock"}, - {0x2588, "block"}, {0x258C, "lfblock"}, {0x2590, "rtblock"}, {0x2591, "ltshade"}, - {0x2592, "shade"}, {0x2593, "dkshade"}, {0x25A0, "filledbox"}, {0x25A1, "H22073"}, - {0x25AA, "H18543"}, {0x25AB, "H18551"}, {0x25AC, "filledrect"}, {0x25B2, "triagup"}, - {0x25BA, "triagrt"}, {0x25BC, "triagdn"}, {0x25C4, "triaglf"}, {0x25CA, "lozenge"}, - {0x25CB, "circle"}, {0x25CF, "H18533"}, {0x25D8, "invbullet"}, {0x25D9, "invcircle"}, - {0x25E6, "openbullet"}, {0x263A, "smileface"}, {0x263B, "invsmileface"}, {0x263C, "sun"}, - {0x2640, "female"}, {0x2642, "male"}, {0x2660, "spade"}, {0x2663, "club"}, - {0x2665, "heart"}, {0x2666, "diamond"}, {0x266A, "musicalnote"}, {0x266B, "musicalnotedbl"}}; - -static int -agl_cmp(const void *a, - const void *b) -{ - const unsigned short aa = ((struct agl_lt *)a)->uid, - bb = ((struct agl_lt *)b)->uid; - if (aa < bb) - return (-1); - else if (aa > bb) - return (1); - return (0); -} - -const char * -aglfn13(unsigned short uni) -{ - if ((uni >= 0x0020) && (uni < 0x007f)) - return (agl_l207e[uni-0x0020]); - else if ((uni >= 0x00a1) && (uni <= 0x00ff)) - return (agl_la1ff[uni-0x00a1]); - else if ((uni >= 0x0100) && (uni <= 0x017f)) - return (agl_l1007f[uni-0x0100]); - else if (uni >= 0x0180) - { - struct agl_lt key, *res; - key.uid = uni; - res = bsearch(&key, agl_lxx, (sizeof(agl_lxx) / sizeof(struct agl_lt)), - sizeof(struct agl_lt), agl_cmp); - if (res) - return (res->name); - } - return (NULL); -} -#else - - -const char * -aglfn13(unsigned short uni) -{ - return (NULL); -} -#endif // WITH_AGLFN diff --git a/cupsfilters/fontembed/dynstring-private.h b/cupsfilters/fontembed/dynstring-private.h deleted file mode 100644 index e0ed3ad58..000000000 --- a/cupsfilters/fontembed/dynstring-private.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _FONTEMBED_DYNSTRING_H_ -#define _FONTEMBED_DYNSTRING_H_ - -typedef struct -{ - int len, alloc; - char *buf; -} __cf_fontembed_dyn_string_t; - -int __cfFontEmbedDynInit(__cf_fontembed_dyn_string_t *ds, - int reserve_size); // -1 on error -void __cfFontEmbedDynFree(__cf_fontembed_dyn_string_t *ds); -int __cfFontEmbedDynEnsure(__cf_fontembed_dyn_string_t *ds, int free_space); -int __cfFontEmbedDynPrintF(__cf_fontembed_dyn_string_t *ds, - const char *fmt, ...) // appends - __attribute__((format(printf, 2, 3))); - -#endif // !_FONTEMBED_DYNSTRING_H_ diff --git a/cupsfilters/fontembed/dynstring.c b/cupsfilters/fontembed/dynstring.c deleted file mode 100644 index 007ddcd6d..000000000 --- a/cupsfilters/fontembed/dynstring.c +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "dynstring-private.h" -#include -#include -#include -#include -#include -#include - - -int -__cfFontEmbedDynInit(__cf_fontembed_dyn_string_t *ds, - int reserve_size) // {{{ -{ - DEBUG_assert(ds); - DEBUG_assert(reserve_size > 0); - - ds->len = 0; - ds->alloc = reserve_size; - ds->buf = malloc(ds->alloc + 1); - if (!ds->buf) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - DEBUG_assert(0); - ds->len = -1; - return (-1); - } - return (0); -} -// }}} - - -void -__cfFontEmbedDynFree(__cf_fontembed_dyn_string_t *ds) // {{{ -{ - DEBUG_assert(ds); - - ds->len= -1; - ds->alloc = 0; - free(ds->buf); - ds->buf = NULL; -} -// }}} - - -int -__cfFontEmbedDynEnsure(__cf_fontembed_dyn_string_t *ds, - int free_space) // {{{ -{ - DEBUG_assert(ds); - DEBUG_assert(free_space); - - if (ds->len < 0) - return (-1); - if (ds->alloc - ds->len >= free_space) - return (0); // done - ds->alloc += free_space; - char *tmp = realloc(ds->buf, ds->alloc + 1); - if (!tmp) - { - ds->len = -1; - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - DEBUG_assert(0); - return (-1); - } - ds->buf = tmp; - return (0); -} -// }}} - - -static int -dyn_vprintf(__cf_fontembed_dyn_string_t *ds, - const char *fmt, - va_list ap) // {{{ -{ - DEBUG_assert(ds); - - int need, len = strlen(fmt) + 100; - va_list va; - - if (__cfFontEmbedDynEnsure(ds, len) == -1) - return (-1); - - while (1) - { - va_copy(va, ap); - need = vsnprintf(ds->buf + ds->len, ds->alloc-ds->len + 1, fmt, va); - va_end(va); - if (need == -1) - len += 100; - else if (need >= len) - len = need; - else { - ds->len += need; - break; - } - if (__cfFontEmbedDynEnsure(ds, len) == -1) - return (-1); - } - return (0); -} -// }}} - -int -__cfFontEmbedDynPrintF(__cf_fontembed_dyn_string_t *ds, - const char *fmt, - ...) // {{{ -{ - va_list va; - int ret; - - va_start(va, fmt); - ret = dyn_vprintf(ds, fmt, va); - va_end(va); - - return (ret); -} -// }}} diff --git a/cupsfilters/fontembed/embed-pdf-private.h b/cupsfilters/fontembed/embed-pdf-private.h deleted file mode 100644 index 87c71ee14..000000000 --- a/cupsfilters/fontembed/embed-pdf-private.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _FONTEMBED_EMBED_PDF_INT_H_ -#define _FONTEMBED_EMBED_PDF_INT_H_ - -_cf_fontembed_emb_pdf_font_widths_t *__cfFontEmbedEmbPDFFWNew(int datasize); - -// if default_width == -1: default_width will be estimated -// glyphs == NULL -> output all -_cf_fontembed_emb_pdf_font_widths_t - *__cfFontEmbedEmbPDFFWCIDWidths(const _cf_fontembed_bit_set_t glyphs, - int len, int default_width, - int (*getGlyphWidth)(void *context, int gid), - void *context); - -#endif // !_FONTEMBED_EMBED_PDF_INT_H_ diff --git a/cupsfilters/fontembed/embed-pdf.c b/cupsfilters/fontembed/embed-pdf.c deleted file mode 100644 index 59aea1072..000000000 --- a/cupsfilters/fontembed/embed-pdf.c +++ /dev/null @@ -1,749 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "embed-pdf-private.h" -#include "embed-sfnt-private.h" -#include "frequent-private.h" -#include -#include -#include - - -// NOTE: these must be in sync with the _cf_fontembed_emb_format_t enum -static const char *emb_pdf_font_subtype[][2] = -{ // {{{ (output_format, multibyte) - {"Type1", NULL}, - {"TrueType", "CIDFontType2"}, - {"Type1", "CIDFontType0"}, - {"Type1", "CIDFontType0"}, - {"Type1", NULL}}; -// }}} - -static const char *emb_pdf_fontfile_key[] = -{ // {{{ (output_format) - "FontFile", - "FontFile2", - "FontFile3", - "FontFile3", - NULL}; -// }}} - -// ... PDF1.6 here -static const char *emb_pdf_fontfile_subtype[][2] = -{ // {{{ (output_format,multibyte) - {NULL, NULL}, - {NULL, NULL}, - {"OpenType", "OpenType"}, - {"Type1C", "CIDFontType0C"}, - {NULL, NULL}}; -// }}} - - -static inline int -emb_multibyte(_cf_fontembed_emb_params_t *emb) // {{{ -{ - return ((emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) ? 1 : 0); -} -// }}} - - -static const char -*emb_pdf_escape_name(const char *name, - int len) // {{{ // - statically allocated buffer -{ - DEBUG_assert(name); - if (len == -1) - len = strlen(name); - DEBUG_assert(len <= 127); // PDF implementation limit - - static char buf[128 * 3]; - int iA, iB; - const char hex[]="0123456789abcdef"; - - for (iA = 0, iB = 0; iA < len; iA ++, iB ++) - { - if (((unsigned char)name[iA] < 33) || ((unsigned char)name[iA] > 126) || - (strchr("#()<>[]{}/%", name[iA]))) - { - buf[iB] = '#'; - buf[++iB] = hex[(name[iA] >> 4) & 0x0f]; - buf[++iB] = hex[name[iA] & 0xf]; - } - else - buf[iB] = name[iA]; - } - buf[iB] = 0; - return (buf); -} -// }}} - - -// this is in the font dict - -const char * -_cfFontEmbedEmbPDFGetFontSubType(_cf_fontembed_emb_params_t *emb) // {{{ -{ - DEBUG_assert(emb); - return (emb_pdf_font_subtype[emb->outtype][emb_multibyte(emb)]); -} -// }}} - - -// in font descriptor - -const char * -_cfFontEmbedEmbPDFGetFontFileKey(_cf_fontembed_emb_params_t *emb) // {{{ -{ - DEBUG_assert(emb); - return (emb_pdf_fontfile_key[emb->outtype]); -} -// }}} - - -// this is what to put in the font-stream dict - -const char * -_cfFontEmbedEmbPDFGetFontFileSubType(_cf_fontembed_emb_params_t *emb) // {{{ -{ - DEBUG_assert(emb); - return (emb_pdf_fontfile_subtype[emb->outtype][emb_multibyte(emb)]); -} -// }}} - - -// {{{ static _cf_fontembed_emb_pdf_font_descr_t * -// emb_pdf_fd_new(fontname, subset_tag, cid_registry, cid_ordering, -// cid_supplement, panose) - -static _cf_fontembed_emb_pdf_font_descr_t * -emb_pdf_fd_new(const char *fontname, - const char *subset_tag, - const char *cid_registry, // or supplement==-1 - const char *cid_ordering, // or supplement==-1 - int cid_supplement) // -1 for non-cid -{ - DEBUG_assert(fontname); - _cf_fontembed_emb_pdf_font_descr_t *ret; - - int len = sizeof(_cf_fontembed_emb_pdf_font_descr_t); - if (subset_tag) - { - DEBUG_assert(strlen(subset_tag) == 6); - len += 7; - } - len += strlen(fontname) + 1; - if (cid_supplement >= 0) - { // cid font - len += 12; // space for panose - DEBUG_assert(cid_registry); - DEBUG_assert(cid_ordering); - len += strlen(cid_registry) + 1; - len += strlen(cid_ordering) + 1; - } - ret = calloc(1, len); - if (!ret) - { - fprintf(stderr,"Bad alloc: %s\n", strerror(errno)); - DEBUG_assert(0); - return (NULL); - } - - // now fill the struct - len = 0; - if (cid_supplement >= 0) // free space for panose is at beginning - len += 12; - ret->fontname = ret->data + len; - len += strlen(fontname) + 1; - if (subset_tag) - { - snprintf(ret->fontname, 6, "%s", subset_tag); - ret->fontname[6] = '+'; - strcpy(ret->fontname + 7, fontname); - len += 7; - } - else - { - strcpy(ret->fontname, fontname); - } - ret->italicAngle = 90; - if (cid_supplement >= 0) - { - ret->registry = ret->data + len; - strcpy(ret->registry, cid_registry); - len += strlen(cid_registry) + 1; - - ret->ordering = ret->data + len; - strcpy(ret->ordering, cid_ordering); - len += strlen(cid_registry) + 1; - } - ret->supplement = cid_supplement; - - return (ret); -} -// }}} - - -_cf_fontembed_emb_pdf_font_descr_t * -_cfFontEmbedEmbPDFFontDescr(_cf_fontembed_emb_params_t *emb) // {{{ - // - to be freed by user -{ - DEBUG_assert(emb); - - const char *subset_tag = NULL; - // {{{ generate pdf subtag - static unsigned int rands = 0; - if (!rands) - rands = time(NULL); - - char subtag[7]; - subtag[6] = 0; - if (emb->plan & _CF_FONTEMBED_EMB_A_SUBSET) - { - int iA; - for (iA = 0; iA < 6; iA ++) - { - const int x = (int)(26.0 * (rand_r(&rands) / (RAND_MAX + 1.0))); - subtag[iA] = 'A' + x; - } - subset_tag = subtag; - } - // }}} - - const char *fontname = NULL; - if ((emb->intype == _CF_FONTEMBED_EMB_FMT_TTF) || - (emb->intype == _CF_FONTEMBED_EMB_FMT_OTF)) - { // TODO? use fontinfo from CFF when outtype==CFT, etc.? - DEBUG_assert(emb->font->sfnt); - fontname = __cfFontEmbedEmbOTFGetFontName(emb->font->sfnt); - } - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_STDFONT) - return (NULL); - else - { - fprintf(stderr, "NOT IMPLEMENTED\n"); - DEBUG_assert(0); - return (NULL); - } - - _cf_fontembed_emb_pdf_font_descr_t *ret; - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) // multibyte - ret = emb_pdf_fd_new(fontname, subset_tag, "Adobe", "Identity", 0); - // TODO other /ROS ? - else - ret = emb_pdf_fd_new(fontname, subset_tag, NULL, NULL, -1); - if (!ret) - return (NULL); - - if ((emb->intype == _CF_FONTEMBED_EMB_FMT_TTF) || - (emb->intype == _CF_FONTEMBED_EMB_FMT_OTF)) - __cfFontEmbedEmbOTFGetPDFFontDescr(emb->font->sfnt, ret); - else - DEBUG_assert(0); - return (ret); -} -// }}} - - -_cf_fontembed_emb_pdf_font_widths_t * -__cfFontEmbedEmbPDFFWNew(int datasize) // {{{ -{ - DEBUG_assert(datasize >= 0); - _cf_fontembed_emb_pdf_font_widths_t *ret = - calloc(1, sizeof(_cf_fontembed_emb_pdf_font_widths_t) + - datasize * sizeof(int)); - if (!ret) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - DEBUG_assert(0); - return (NULL); - } - return (ret); -} -// }}} - - -// if default_width == -1: default_width will be estimated - -_cf_fontembed_emb_pdf_font_widths_t * -__cfFontEmbedEmbPDFFWCIDWidths(const _cf_fontembed_bit_set_t glyphs, - int len, - int default_width, - int (*getGlyphWidth)(void *context, int gid), - void *context) // {{{ glyphs == NULL -> - // output all -{ - DEBUG_assert(getGlyphWidth); - - __cf_fontembed_frequent_t *freq = NULL; - if (default_width < 0) - freq = __cfFontEmbedFrequentNew(3); - - int iA, b, c; - int size = 0, - in_region = 0; // current number of elements in after region start - - // first pass: find continuous regions, calculate needed size, estimate dw - for (iA = 0, b = 0, c = 1; iA < len; iA ++, c <<= 1) - { - if (!c) - { - b ++; - c = 1; - } - if ((!glyphs) || (glyphs[b] & c)) - { - if (freq) - { - const int w = (*getGlyphWidth)(context, iA); - __cfFontEmbedFrequentAdd(freq, w); - } - if (in_region) - in_region ++; - else - { - // start new region - size += 2; // len c - in_region=1; - } - } - else - { - // region end - size += in_region; - in_region = 0; - } - } - size += in_region; - - if (freq) - { - default_width = __cfFontEmbedFrequentGet(freq, 0); - free(freq); - } - DEBUG_assert(default_width > 0); - - // now create the array - _cf_fontembed_emb_pdf_font_widths_t *ret = __cfFontEmbedEmbPDFFWNew(size + 1); - if (!ret) - return (NULL); - ret->default_width = default_width; - ret->warray = ret->data; - - // second pass - in_region = 0; - size = 0; - int *rlen = 0; // position of current len field (only valid if in_region != 0) - for (iA = 0, b = 0, c = 1; iA < len; iA ++, c <<= 1) - { - if (!c) - { - b ++; - c = 1; - } - if ((!glyphs) || (glyphs[b] & c)) - { - const int w = (*getGlyphWidth)(context, iA); - if (in_region > 0) - { - // in array region - if ((w == default_width) && (ret->warray[size - 1] == default_width)) - { - // omit this and prev entry - size --; - *rlen = in_region - 1; // !=0, as it does not start - // with > default_width - in_region = 0; // end region, immediate restart will take just the - // same amount of space - } - else if ((in_region >= 4) && - (ret->warray[size - 1] == w) && (ret->warray[size - 2] == w) && - (ret->warray[size - 3] == w) && (ret->warray[size - 4] == w)) - { - // five in a row. c1 c2 w [l c] is equally short and can be extended - // (-len c1 w) [w/ cost of array-region restart] - if (in_region == 4) // completely replace - size -= 6; - else - { - // first end previous region - size -= 4; - *rlen = in_region - 4; - } - in_region =- 4; // start range region instead - rlen = &ret->warray[size++]; - ret->warray[size ++] = iA - 4; - ret->warray[size ++] = w; - } - else - { - // just add - in_region ++; - ret->warray[size ++] = w; - } - continue; - } - else if (in_region < 0) - { - // in range region - if (ret->warray[size - 1] == w) - { - in_region --; // just add - continue; - } - *rlen = in_region; // end - in_region = 0; - } - if (w != default_width) - { - // start new array region - in_region = 1; - rlen = &ret->warray[size ++]; - ret->warray[size ++] = iA; // c - ret->warray[size ++] = w; - } - } - else if (in_region) - { - // TODO? no need to stop range region? - // } else if (in_region<0) { inregion--; } - *rlen = in_region; - in_region = 0; - } - } - if (in_region) - *rlen = in_region; - ret->warray[size] = 0; // terminator - return (ret); -} -// }}} - - -// TODO: Encoding into _cf_fontembed_emb_params_t (emb_new_enc(..., encoding, -// len, to_unicode)); -// -> will then change interpretation of _cf_fontembed_bit_set_t... -// (?really?); can we allow dynamic encoding map generation? -// -> encoding has a "len"; len < 256 - -_cf_fontembed_emb_pdf_font_widths_t * -_cfFontEmbedEmbPDFFontWidths(_cf_fontembed_emb_params_t *emb) // {{{ -{ - DEBUG_assert(emb); - - if ((emb->intype == _CF_FONTEMBED_EMB_FMT_TTF) || - (emb->intype == _CF_FONTEMBED_EMB_FMT_OTF)) - { - DEBUG_assert(emb->font->sfnt); - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) - return (__cfFontEmbedEmbOTFGetPDFCIDWidths(emb->font->sfnt, emb->subset)); - else - return (__cfFontEmbedEmbOTFGetPDFWidths(emb->font->sfnt, /*encoding*/NULL, - emb->font->sfnt->numGlyphs, - emb->subset)); // TODO: encoding - } - else - { - fprintf(stderr,"NOT IMPLEMENTED\n"); - DEBUG_assert(0); - return (NULL); - } -} -// }}} - - -// *** PDF out stuff *** -#include "dynstring-private.h" - -#define NEXT /* {{{ */ \ - if ((len < 0) || (len >= size)) \ - { \ - DEBUG_assert(0); \ - free(ret); \ - return (NULL); \ - } \ - pos += len; \ - size -= len; /* }}} */ - - -// TODO? /CIDSet TODO... /FontFamily /FontStretch /FontWeight (PDF1.5?) -// would be nice... - -char * -_cfFontEmbedEmbPDFSimpleFontDescr(_cf_fontembed_emb_params_t *emb, - _cf_fontembed_emb_pdf_font_descr_t *fdes, - int fontfile_obj_ref) // {{{ - to be freed - // by user -{ - DEBUG_assert(emb); - DEBUG_assert(fdes); - - char *ret = NULL, *pos; - int len, size; - - size = 300; - pos = ret = malloc(size); - if (!ret) - { - fprintf(stderr,"Bad alloc: %s\n", strerror(errno)); - return (NULL); - } - - len = snprintf(pos, size, - "<fontname, -1), - fdes->flags, - fdes->italicAngle); - NEXT; - - if (1) - { - // TODO type!=EMB_PDF_TYPE3 - len = snprintf(pos, size, - " /FontBBox [%d %d %d %d]\n" - " /Ascent %d\n" - " /Descent %d\n" - " /CapHeight %d\n" // if font has Latin chars - " /StemV %d\n", - fdes->bbxmin, fdes->bbymin, fdes->bbxmax, fdes->bbymax, - fdes->ascent, - fdes->descent, - fdes->capHeight, - fdes->stemV); - NEXT; - } - if (fdes->xHeight) - { - len = snprintf(pos, size, " /XHeight %d\n", fdes->xHeight); - NEXT; - } - if (fdes->avgWidth) - { - len = snprintf(pos, size, " /AvgWidth %d\n", fdes->avgWidth); - NEXT; - } - if (fdes->panose) - { - int iA; - len = snprintf(pos, size, " /Style << /Panose <"); - NEXT; - if (size < 30) - { - DEBUG_assert(0); - free(ret); - return (NULL); - } - for (iA = 0; iA < 12; iA ++) - snprintf(pos + iA * 2, size - iA * 2, "%02x", fdes->panose[iA]); - size -= 24; - pos += 24; - len = snprintf(pos, size, "> >>\n"); - NEXT; - } - // TODO (for Type0)? CIDSet -> simply our glyphs _cf_fontembed_bit_set_t - // (ok. endianess?) - len = snprintf(pos, size, - " /%s %d 0 R\n" - ">>\n", - _cfFontEmbedEmbPDFGetFontFileKey(emb), - fontfile_obj_ref); - NEXT; - - return (ret); -} -// }}} - - -char * -_cfFontEmbedEmbPDFSimpleFont(_cf_fontembed_emb_params_t *emb, - _cf_fontembed_emb_pdf_font_descr_t *fdes, - _cf_fontembed_emb_pdf_font_widths_t *fwid, - int fontdescr_obj_ref) // {{{ - to be freed by user -{ - DEBUG_assert(emb); - DEBUG_assert(fdes); - DEBUG_assert(fwid); - - int iA, iB; - __cf_fontembed_dyn_string_t ret; - - if (__cfFontEmbedDynInit(&ret, 500) == -1) - return (NULL); - - __cfFontEmbedDynPrintF(&ret, - "<fontname,-1), - fontdescr_obj_ref); - - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) - { - // multibyte - DEBUG_assert(fwid->warray); - __cfFontEmbedDynPrintF(&ret, - " /CIDSystemInfo <<\n" - " /Registry (%s)\n" - " /Ordering (%s)\n" - " /Supplement %d\n" - " >>\n" - " /DW %d\n", - //" /CIDToGIDMap /Id...\n" // TrueType only, default - // Identity [optional? which - // PDF version says what?] - fdes->registry, - fdes->ordering, - fdes->supplement, - fwid->default_width); - - if (fwid->warray[0]) - { - __cfFontEmbedDynPrintF(&ret, " /W ["); - for (iA = 0; fwid->warray[iA];) - { - if (fwid->warray[iA] < 0) - { - // c1 (c1-len) w - __cfFontEmbedDynPrintF(&ret, " %d %d %d", - fwid->warray[iA + 1], - fwid->warray[iA + 1] - fwid->warray[iA], - fwid->warray[iA + 2]); - iA += 3; - } - else - { - // c [w ... w] - iB = fwid->warray[iA ++]; // len - __cfFontEmbedDynPrintF(&ret, " %d [", fwid->warray[iA ++]); // c - for (; iB > 0; iB --) - __cfFontEmbedDynPrintF(&ret, " %d", fwid->warray[iA ++]); - __cfFontEmbedDynPrintF(&ret, "]"); - } - } - __cfFontEmbedDynPrintF(&ret, "]\n"); - } - } - else - { - // "not std14" - DEBUG_assert(fwid->widths); - __cfFontEmbedDynPrintF(&ret, - " /Encoding /MacRomanEncoding\n"// optional; TODO! - //" /ToUnicode ?\n" // optional - " /FirstChar %d\n" - " /LastChar %d\n" - " /Widths [", - fwid->first, - fwid->last); - for (iA = 0, iB = fwid->first; iB <= fwid->last; iA ++, iB ++) - __cfFontEmbedDynPrintF(&ret, " %d", fwid->widths[iA]); - __cfFontEmbedDynPrintF(&ret, "]\n"); - } - __cfFontEmbedDynPrintF(&ret, ">>\n"); - if (ret.len == -1) - { - __cfFontEmbedDynFree(&ret); - DEBUG_assert(0); - return (NULL); - } - - return (ret.buf); -} -// }}} - - -// TODO? + encoding as param? TODO + ToUnicode cmap -// => we need another struct EMB_PDF_FONTMAP -// (TODO?? fontname here without subset-tag [_some_ pdfs out there seem -// to be that way]) -// TODO? don't do the CidType0 check here? -// NOTE: this is _additionally_ to _cfFontEmbedEmbPDFSimpleFont()! - -char * -_cfFontEmbedEmbPDFSimpleCIDFont(_cf_fontembed_emb_params_t *emb, - const char *fontname, - int descendant_obj_ref) // {{{ - to be freed by user -{ - DEBUG_assert(emb); - DEBUG_assert(fontname); - - char *ret = NULL, *pos; - int len, size; - - size = 250; - pos = ret = malloc(size); - if (!ret) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - return (NULL); - } - // for CFF: one of: - // UniGB-UCS2-H, UniCNS-UCS2-H, UniJIS-UCS2-H, UniKS-UCS2-H - const char *encoding = "Identity-H", *addenc = "-"; - if (emb->outtype == _CF_FONTEMBED_EMB_FMT_TTF) - // !=CidType0 - addenc = ""; - - len = snprintf(pos, size, - "<>\n"); - NEXT; - - return (ret); -} -// }}} - - -char * -_cfFontEmbedEmbPDFSimpleStdFont(_cf_fontembed_emb_params_t *emb) - // {{{ - to be freed by user -{ - DEBUG_assert(emb); - DEBUG_assert(emb->font->stdname); - - char *ret = NULL, *pos; - int len, size; - - size=300; - pos = ret = malloc(size); - if (!ret) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - return (NULL); - } - - len = snprintf(pos, size, - "<>\n", -// _cfFontEmbedEmbPDFGetFontSubType(emb), - emb->font->stdname); - NEXT; - - return (ret); -} -// }}} - - -#undef NEXT diff --git a/cupsfilters/fontembed/embed-sfnt-private.h b/cupsfilters/fontembed/embed-sfnt-private.h deleted file mode 100644 index 3fb90c7d8..000000000 --- a/cupsfilters/fontembed/embed-sfnt-private.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _FONTEMBED_EMBED_SFNT_INT_H_ -#define _FONTEMBED_EMBED_SFNT_INT_H_ - -#include - - -_cf_fontembed_emb_right_t - __cfFontEmbedEmbOTFGetRights(_cf_fontembed_otf_file_t *otf); - -// NOTE: statically allocated buffer -const char *__cfFontEmbedEmbOTFGetFontName(_cf_fontembed_otf_file_t *otf); - -void - __cfFontEmbedEmbOTFGetPDFFontDescr(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_emb_pdf_font_descr_t *ret); -_cf_fontembed_emb_pdf_font_widths_t - *__cfFontEmbedEmbOTFGetPDFWidths(_cf_fontembed_otf_file_t *otf, - const unsigned short *encoding, - int len, - const _cf_fontembed_bit_set_t glyphs); -_cf_fontembed_emb_pdf_font_widths_t - *__cfFontEmbedEmbOTFGetPDFCIDWidths(_cf_fontembed_otf_file_t *otf, - const _cf_fontembed_bit_set_t glyph); - -int __cfFontEmbedEmbOTFPS(_cf_fontembed_otf_file_t *otf, - unsigned short *encoding, int len, - unsigned short *to_unicode, - _cf_fontembed_output_fn_t output, void *context); - -#endif // !_FONTEMBED_EMBED_SFNT_INT_H_ diff --git a/cupsfilters/fontembed/embed-sfnt.c b/cupsfilters/fontembed/embed-sfnt.c deleted file mode 100644 index c3c66a3e0..000000000 --- a/cupsfilters/fontembed/embed-sfnt.c +++ /dev/null @@ -1,876 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "embed-pdf-private.h" -#include "embed-sfnt-private.h" -#include "sfnt-private.h" -#include -#include -#include - - -_cf_fontembed_emb_right_t -__cfFontEmbedEmbOTFGetRights(_cf_fontembed_otf_file_t *otf) // {{{ -{ - _cf_fontembed_emb_right_t ret = _CF_FONTEMBED_EMB_RIGHT_FULL; - - int len; - char *os2 = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('O', 'S', '/', '2'), - &len); - if (os2) - { - const unsigned short os2_version = __cfFontEmbedGetUShort(os2); - // check len - DEBUG_assert((os2_version != 0x0000) || (len == 78)); - DEBUG_assert((os2_version != 0x0001) || (len == 86)); - DEBUG_assert((os2_version < 0x0002) || (os2_version > 0x0004) || - (len == 96)); - if (os2_version <= 0x0004) - { - // get rights - unsigned short fsType = __cfFontEmbedGetUShort(os2 + 8); - // from Adobe's Fontpolicies_v9.pdf, pg 13: - if (fsType == 0x0002) - ret = _CF_FONTEMBED_EMB_RIGHT_NONE; - else - { - ret = fsType & 0x0300; // _CF_FONTEMBED_EMB_RIGHT_BITMAPONLY, - // _CF_FONTEMBED_EMB_RIGHT_NO_SUBSET - if ((fsType & 0x000c) == 0x0004) - ret |= _CF_FONTEMBED_EMB_RIGHT_READONLY; - } - } - free(os2); - } - return (ret); -} -// }}} - - -// NOTE: statically allocated buffer - -const char * -__cfFontEmbedEmbOTFGetFontName(_cf_fontembed_otf_file_t *otf) // {{{ -{ - static char fontname[64]; - - int len; - const char *fname = _cfFontEmbedOTFGetName(otf, 3, 1, 0x409, 6, &len); - // Microsoft - if (fname) - { - int iA, iB = 0; - for (iA=0; (iA < 63) && (iA * 2 < len); iA ++) - { - if ((fname[2 * iA] == 0) && - (fname[2 * iA + 1] >= 33) && (fname[2 * iA + 1] <= 126) && - (!strchr("[](){}<>/%", fname[iA * 2 + 1]))) - fontname[iB ++] = fname[iA * 2 + 1]; - } - fontname[iB] = 0; - } - else if ((fname = _cfFontEmbedOTFGetName(otf, 1, 0, 0, 6, &len))) // Mac - { - int iA, iB = 0; - for (iA = 0; (iA < 63) && (iA < len); iA ++) - { - if ((fname[iA] >= 33) && (fname[iA] <= 126) && - (!strchr("[](){}<>/%", fname[iA]))) - fontname[iB ++] = fname[iA]; - } - fontname[iB] = 0; - } - else - fontname[0] = 0; - - if (!*fontname) - // TODO construct a fontname, eg from */*/*/4 - fprintf(stderr, "WARNING: no fontName\n"); - - return (fontname); -} -// }}} - - -// TODO? monospaced by actual glyph width? -// TODO? use PCLT table? (esp. CFF, as table dircouraged for glyf fonts) - -void -__cfFontEmbedEmbOTFGetPDFFontDescr(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_emb_pdf_font_descr_t *ret) - // {{{ -{ - int len; - -// TODO -// ... fill in struct - char *head = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd'), - &len); - DEBUG_assert(head); // version is 1.0 from _cfFontEmbedOTFLoad - ret->bbxmin = __cfFontEmbedGetShort(head + 36) * 1000 / otf->unitsPerEm; - ret->bbymin = __cfFontEmbedGetShort(head + 38) * 1000 / otf->unitsPerEm; - ret->bbxmax = __cfFontEmbedGetShort(head + 40) * 1000 / otf->unitsPerEm; - ret->bbymax = __cfFontEmbedGetShort(head + 42) * 1000 / otf->unitsPerEm; - const int macStyle = __cfFontEmbedGetUShort(head + 44); - free(head); - - char *post = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('p', 'o', 's', 't'), - &len); - DEBUG_assert(post); - const unsigned int post_version = __cfFontEmbedGetULong(post); - // check length - DEBUG_assert((post_version != 0x00010000) || (len == 32)); - DEBUG_assert((post_version != 0x00020000) || - (len >= 34 + 2 * otf->numGlyphs)); - DEBUG_assert((post_version != 0x00025000) || (len == 35 + otf->numGlyphs)); - DEBUG_assert((post_version != 0x00030000) || (len == 32)); - DEBUG_assert((post_version != 0x00020000) || - (__cfFontEmbedGetUShort(post + 32) == otf->numGlyphs)); // v4? - // DEBUG_assert((post_version == 0x00030000) == - // (!!(otf->flags&_CF_FONTEMBED_OTF_F_FMT_CFF))); - // // Ghostscript embedding does this.. - // TODO: v4 (apple) : uint16 reencoding[numGlyphs] - if ((post_version == 0x00010000) || - (post_version == 0x00020000) || - (post_version == 0x00025000) || - (post_version == 0x00030000) || - (post_version == 0x00040000)) - { - ret->italicAngle = __cfFontEmbedGetLong(post + 4) >> 16; - if (__cfFontEmbedGetULong(post + 12) > 0) // monospaced - ret->flags |= 1; - } - else - fprintf(stderr, "WARNING: no italicAngle, no monospaced flag\n"); - free(post); - - char *os2 = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('O', 'S', '/', '2'), - &len); - if (os2) - { - const unsigned short os2_version = __cfFontEmbedGetUShort(os2); - // check len - DEBUG_assert((os2_version != 0x0000) || (len == 78)); - DEBUG_assert((os2_version != 0x0001) || (len == 86)); - DEBUG_assert((os2_version < 0x0002) || (os2_version > 0x0004) || - (len == 96)); - if (os2_version <= 0x0004) - { - // from PDF14Deltas.pdf, pg 113 - const int weightClass = __cfFontEmbedGetUShort(os2 + 4); - ret->stemV = 50 + weightClass * weightClass / (65 * 65); - // TODO, really bad - //printf("a %d\n", weightClass); - if (ret->supplement >= 0) - { - // cid - ret->panose = ret->data; - memcpy(ret->panose, os2 + 30, 12); // sFamilyClass + panose - } - const unsigned short fsSelection = __cfFontEmbedGetUShort(os2 + 62); - if (fsSelection & 0x01) - // italic - ret->flags |= 0x0040; - if ((fsSelection & 0x10) && (weightClass > 600)) - // force bold - ret->flags |= 0x0400; - const unsigned char family_class = __cfFontEmbedGetUShort(os2 + 30) >> 8; - if (family_class == 10) - // script - ret->flags |= 0x0008; - if (family_class != 8) - // not sans-serif - ret->flags |= 0x0002; - - ret->avgWidth = __cfFontEmbedGetShort(os2 + 2) * 1000 / otf->unitsPerEm; - ret->ascent = __cfFontEmbedGetShort(os2 + 68) * 1000 / otf->unitsPerEm; - ret->descent = __cfFontEmbedGetShort(os2 + 70) * 1000 / otf->unitsPerEm; - if (os2_version >= 0x0002) - { - ret->xHeight = __cfFontEmbedGetShort(os2 + 86) * 1000 / otf->unitsPerEm; - ret->capHeight = __cfFontEmbedGetShort(os2 + 88) * 1000 / - otf->unitsPerEm; - } // else capHeight fixed later - } - else - { - free(os2); - os2 = NULL; - } - } - else - { - fprintf(stderr,"WARNING: no OS/2 table\n"); - // e.g. Subsetted font from Ghostscript // e.g. CFF - } - if (os2) - free(os2); - else - { - // TODO (if(CFF)) - fprintf(stderr, "WARNING: no ascent/descent, capHeight, stemV, flags\n"); - if (macStyle & 0x01) // force bold - just do it on bold - ret->flags |= 0x0400; - if (macStyle & 0x02) // italic - ret->flags |= 0x0004; - // ... flags TODO? (Serif, Script, Italic, AllCap,SmallCap, ForceBold) - } - - // ? maybe get ascent, descent, capHeight, xHeight, stemV directly from cff - // Fallbacks - if ((!ret->ascent) || (!ret->descent)) - { - char *hhea = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('h', 'h', 'e', 'a'), - &len); - if (hhea) - { - ret->ascent = __cfFontEmbedGetShort(hhea + 4) * 1000 / otf->unitsPerEm; - ret->descent = __cfFontEmbedGetShort(hhea + 6) * 1000 / otf->unitsPerEm; - } - free(hhea); - } - if (!ret->stemV) - { - // TODO? use name - const unsigned short d_gid = _cfFontEmbedOTFFromUnicode(otf, '.'); - if (d_gid) - { - // stemV=bbox['.'].width; - len = _cfFontEmbedOTFGetGlyph(otf, d_gid); - DEBUG_assert(len >= 10); - ret->stemV = (__cfFontEmbedGetShort(otf->gly + 6) - - __cfFontEmbedGetShort(otf->gly + 2)) * 1000 / - otf->unitsPerEm; - } - else - { - if (macStyle & 1) - // bold - ret->stemV = 165; - else - ret->stemV = 109; // TODO... unserious values... - } - } - if (!ret->capHeight) - // TODO? only reqd. for fonts with latin... - ret->capHeight = ret->ascent; - // TODO: OTF spec says: use metrics of 'H' (0 if not available) - if (0) // TODO? uses only adobe latin standard? ?? e.g. Type1 - ret->flags |= 0x0020; - else - ret->flags |= 0x0004; - // TODO SmallCap by font name(?) - -// TODO ; ? cid ? -} -// }}} - - -// TODO: split generic part and otf part -// TODO: FIXME: gid vs. char ... NOTE: not called in multi_byte mode... -// Adobe does: char --MacRoman/WinAnsi--> name --AGL--> -// unicode --cmap(3, 1) --> gid only avoidable by setting -// 'symbol' + custom(1, 0) / (3, 0) -// HINT: caller sets len == otf->numGlyphs (only when not using encoding...) - -_cf_fontembed_emb_pdf_font_widths_t * -__cfFontEmbedEmbOTFGetPDFWidths(_cf_fontembed_otf_file_t *otf, - const unsigned short *encoding, - int len, - const _cf_fontembed_bit_set_t glyphs) - // {{{ glyphs == NULL -> - // all from 0 to len -{ - DEBUG_assert(otf); - - int first = len, last = 0; - int iA; - - if (glyphs) - { - for (iA = 0; iA < len; iA ++) // iA is a "gid" when in multi_byte mode... - { - const int gid = (encoding) ? - encoding[iA] : _cfFontEmbedOTFFromUnicode(otf, iA); // TODO - if (_cfFontEmbedBitCheck(glyphs, gid)) - { - if (first > iA) - // first is a character index - first = iA; - if (last < iA) - last = iA; - } - } - } - else - { - first = 0; - last = len; - } - if (last < first) - { - // empty - fprintf(stderr, "WARNING: empty embedding range\n"); - return (NULL); - } - - // ensure hmtx is there - if (!otf -> hmtx) - { - if (__cfFontEmbedOTFLoadMore(otf) != 0) - { - fprintf(stderr, "Unsupported OTF font / cmap table \n"); - return (NULL); - } - } - - // now create the array - _cf_fontembed_emb_pdf_font_widths_t *ret = - __cfFontEmbedEmbPDFFWNew(last - first + 1); - if (!ret) - return (NULL); - ret->first = first; - ret->last = last; - ret->widths = ret->data; - for (iA = 0; first <= last; iA ++, first ++) - { - const int gid = (encoding) ? - encoding[first] : _cfFontEmbedOTFFromUnicode(otf, first); // TODO - if (gid >= otf->numGlyphs) - { - fprintf(stderr, "Bad glyphid\n"); - DEBUG_assert(0); - free(ret); - return (NULL); - } - if ((!glyphs) || (_cfFontEmbedBitCheck(glyphs, gid))) - ret->widths[iA] = - __cfFontEmbedGetWidthFast(otf, gid) * 1000 / otf->unitsPerEm; - // else 0 from calloc - } - - return (ret); -} -// }}} - - -// otf->hmtx must be there - -static int -emb_otf_pdf_glyphwidth(void *context, int gid) // {{{ -{ - _cf_fontembed_otf_file_t *otf = (_cf_fontembed_otf_file_t *)context; - return (__cfFontEmbedGetWidthFast(otf, gid) * 1000 / otf->unitsPerEm); -} -// }}} - - -_cf_fontembed_emb_pdf_font_widths_t * -__cfFontEmbedEmbOTFGetPDFCIDWidths(_cf_fontembed_otf_file_t *otf, - const _cf_fontembed_bit_set_t glyphs) // {{{ - // glyphs == NULL -> output all -{ - DEBUG_assert(otf); - - // ensure hmtx is there - if (!otf->hmtx) - { - if (__cfFontEmbedOTFLoadMore(otf) != 0) - { - fprintf(stderr, "Unsupported OTF font / cmap table \n"); - return NULL; - } - } - // int dw = emb_otf_pdf_glyphwidth(otf, 0); // e.g. - int dw = -1; // let them estimate - - return (__cfFontEmbedEmbPDFFWCIDWidths(glyphs, otf->numGlyphs, dw, - emb_otf_pdf_glyphwidth, otf)); -} -// }}} - - -/*** PS stuff ***/ - -#include "dynstring-private.h" - -const char *aglfn13(unsigned short uni); // aglfn13.c -#include "macroman-private.h" - - -// TODO? optimize pascal string skipping? (create index) -// NOTE: might return a statically allocated string - -static const char * -emb_otf_get_post_name(const char *post, - unsigned short gid) // {{{ -{ - if (!post) - return (NULL); - const unsigned int post_version = __cfFontEmbedGetULong(post); - if (post_version == 0x00010000) // font has only 258 chars... - // font cannot be used on windows - { - if (gid < sizeof(__cf_fontembed_mac_roman) / - sizeof(__cf_fontembed_mac_roman[0])) - return (__cf_fontembed_mac_roman[gid]); - } - else if (post_version == 0x00020000) - { - const unsigned short num_glyphs = __cfFontEmbedGetUShort(post + 32); - // DEBUG_assert(num_glyphs == otf->numGlyphs); - if (gid < num_glyphs) - { - unsigned short idx = __cfFontEmbedGetUShort(post + 34 + 2 * gid); - if (idx < 258) - { - if (idx < sizeof(__cf_fontembed_mac_roman) / - sizeof(__cf_fontembed_mac_roman[0])) - return (__cf_fontembed_mac_roman[idx]); - } - else if (idx < 32768) - { - const unsigned char *pos = (unsigned char *)post + 34 + 2 * num_glyphs; - for (idx -= 258; idx > 0; idx --) // this sucks... - pos += *pos + 1; // skip this string - // convert pascal string to asciiz - static char ret[256]; - const unsigned char len = *pos; - memcpy(ret, (const char *)pos + 1, len); - ret[len] = 0; - return (ret); - } - } - } - else if (post_version == 0x00025000) - { - // similiar to 0x00010000, deprecated - const unsigned short num_glyphs = __cfFontEmbedGetUShort(post + 32); - if (gid < num_glyphs) - { - const unsigned short idx = post[34 + gid] + gid; // post is signed char * - if (idx < sizeof(__cf_fontembed_mac_roman) / - sizeof(__cf_fontembed_mac_roman[0])) - return (__cf_fontembed_mac_roman[idx]); - } - } - else if (post_version == 0x00030000) - { - // no glyph names, sorry - // } else if (post_version == 0x00040000) { // apple AAT ?! - } - return (NULL); -} -// }}} - - -// TODO!? to_unicode should be able to represent more than one unicode -// character? -// NOTE: statically allocated string - -static const char * -get_glyphname(const char *post, - unsigned short *to_unicode, - int charcode, - unsigned short gid) // {{{ if charcode==0 -> force gid to be used -{ - if (gid == 0) - return (".notdef"); - - const char *postName = emb_otf_get_post_name(post, gid); - if (postName) - return (postName); - - static char ret[255]; - if (charcode) - { - if (to_unicode) // i.e. encoding was there - charcode = to_unicode[charcode]; - // TODO!? to_unicode should be able to represent more than one unicode - // character? - // TODO for additional credit: for ligatures, etc create - // /f_f /uni12341234 or the like - const char *aglname = aglfn13(charcode); // TODO? special case ZapfDingbats? - if (aglname) - return (aglname); - snprintf(ret, 250, "uni%04X", charcode); // allows extraction - } - else - snprintf(ret, 250, "c%d", gid); // last resort: only by gid - return (ret); -} -// }}} - - -struct OUTFILTER_PS -{ - _cf_fontembed_output_fn_t out; - void *ctx; - int len; -}; - - -// TODO: for maximum compatiblity (PS<2013 interpreter) split only on table -// or glyph boundary (needs lookup in loca table!) -// Note: table boundaries are at each call! - -static void -outfilter_ascii_ps(const char *buf, - int len, - void *context) // {{{ -{ - struct OUTFILTER_PS *of = context; - _cf_fontembed_output_fn_t out = of->out; - int iA; - - (*out)("<", 1, of->ctx); - of->len ++; - - const char *last = buf; - char tmp[256]; - while (len > 0) - { - for (iA = 0; (iA < 76) && (len > 0); iA += 2, len --) - { - const unsigned char ch = buf[iA >> 1]; - tmp[iA] = "0123456789abcdef"[ch >> 4]; - tmp[iA + 1] = "0123456789abcdef"[ch & 0x0f]; - } - buf += iA >> 1; - if (buf < last + 64000) - { - if (len > 0) - tmp[iA++] = '\n'; - (*out)(tmp, iA, of->ctx); - } - else - { - last = buf; - strcpy(tmp + iA, "00>\n<"); - iA += 5; - (*out)(tmp, iA, of->ctx); - } - of->len += iA; - } - - (*out)("00>\n",4,of->ctx); - of->len+=4; -} -// }}} - - -static void -outfilter_binary_ps(const char *buf, - int len, - void *context) // {{{ -{ - struct OUTFILTER_PS *of = context; - _cf_fontembed_output_fn_t out = of->out; - - char tmp[100]; - while (len > 0) - { - const int maxlen = (len > 64000) ? 64000 : len; - const int l = sprintf(tmp, "%d RD ", maxlen); - (*out)(tmp, l, of->ctx); - of->len += l; - - (*out)(buf, maxlen, of->ctx); - (*out)("\n", 1, of->ctx); - of->len += maxlen + 1; - len -= maxlen; - buf += maxlen; - } -} -// }}} - - -// -// encoding: character-code -> -// glyph id ["required", NULL: identity, i.e. from_unicode()] -// // TODO: respect subsetting -// to_unicode: character-code -> -// unicode [NULL: no char names] -// // kind-of "reverse" of encoding (to_unicode -// // does not make sense without >encoding) -// -// Status: -// - we need a 0..255 encoding to be used in the PS file -// - we want to allow the use of encoding[]; this should map from your -// desired PS-stream output character (0..255) directly to the gid -// - if encoding[] is not used, MacRoman/WinAnsi/latin1 is expected -// (easiest: latin1, as it is a subset of unicode) -// i.e. your want to output latin1 to the PS-stream -// - len is the length of >encoding, or the "last used latin1 character" -// - oh. in multibyte-mode no >encoding probably should mean -// identity(gid->gid) not (latin1->gid) -// - non-multibyte PDF -> only 255 chars ... not recommended (we can't -// just map to gids, but only to names, which acro will then cmap(3, 1) -// to gids) -// -// => problem with subsetting _cf_fontembed_bit_set_t (keyed by gid); we want -// _cf_fontembed_bit_set_t keyed by 0..255 (via encoding) -// -// TODO: a) multi font encoding -// TODO: b) cid/big font encoding (PS >= 2015) [/CIDFontType 2] : CMap does -// Charcode->CID, /CIDMap does CID->GID [e.g. Identity/delta value] -// (also needed [or a)] for loca>64000 if split, etc) -// e.g. /CIDMap 0 [requires PS >= 3011?] -// [Danger: do not split composites] -// TODO? incremental download [/GlyphDirectory array or dict] : -// /GlyphDirectory does GID-> mapping -// need 'fake' gdir table (size,offset=0) in sfnt; loca, glyf can be -// ommited; hmtx can be omitted for PS >= 3011 [/MetricsCount 2] -// idea is to fill initial null entries in the array/dict -// [Beware of save/restore!] -// NOTE: even when subsetting the font has to come first in the PS file -// -// ... special information: when multi-byte PDF encoding is used is -// output. -// therefore /Encoding /Identity-H + /CIDSystemInfo Adobe-Identity-0 will -// yield 1-1 mapping for font. -// problem is that text is not selectable. therefore there is the -// /ToUnicode CMap option -// - -int -__cfFontEmbedEmbOTFPS(_cf_fontembed_otf_file_t *otf, - unsigned short *encoding, - int len, - unsigned short *to_unicode, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - const int binary = 0; // binary format? // TODO - if (len > 256) - { - fprintf(stderr, "Encoding too big(%d) for Type42\n", len); - return (-1); - } - if (len < 1) - { - fprintf(stderr, "At least .notdef required in Type42\n"); - return (-1); - } - if (!encoding) - to_unicode = NULL; // does not make sense - - int iA, ret=0; - - __cf_fontembed_dyn_string_t ds; - if (__cfFontEmbedDynInit(&ds, 1024) == -1) - return (-1); - - int rlen = 0; - char *head = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd'), - &rlen); - if (!head) - { - free(ds.buf); - return (-1); - } - __cfFontEmbedDynPrintF(&ds, "%%!PS-TrueTypeFont-%d-%d\n", - otf->version, __cfFontEmbedGetULong(head + 4)); - const int bbxmin = __cfFontEmbedGetShort(head + 36) * 1000 / otf->unitsPerEm, - bbymin = __cfFontEmbedGetShort(head + 38) * 1000 / otf->unitsPerEm, - bbxmax = __cfFontEmbedGetShort(head + 40) * 1000 / otf->unitsPerEm, - bbymax = __cfFontEmbedGetShort(head + 42) * 1000 / otf->unitsPerEm; - free(head); - - char *post = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('p', 'o', 's', 't'), - &rlen); - if ((!post) && (rlen != -1)) // other error than "not found" - { - free(ds.buf); - return (-1); - } - if (post) - { - const unsigned int minMem = __cfFontEmbedGetULong(post + 16), - maxMem = __cfFontEmbedGetULong(post + 20); - if (minMem) - __cfFontEmbedDynPrintF(&ds, "%%VMusage: %d %d\n", minMem, maxMem); - } - - // don't forget the coordinate scaling... - __cfFontEmbedDynPrintF(&ds, "11 dict begin\n" - "/FontName /%s def\n" - "/FontType 42 def\n" - "/FontMatrix [1 0 0 1 0 0] def\n" - "/FontBBox [%f %f %f %f] def\n" - "/PaintType 0 def\n", - //"/XUID [42 16#%X 16#%X 16#%X 16#%X] def\n" - // // TODO?!? (md5 of font data) (16# means base16) - __cfFontEmbedEmbOTFGetFontName(otf), - bbxmin / 1000.0, bbymin / 1000.0, - bbxmax / 1000.0, bbymax / 1000.0); - if (post) - __cfFontEmbedDynPrintF(&ds,"/FontInfo 4 dict dup begin\n" - // TODO? [even non-post]: - // /version | /Notice | /Copyright | /FullName | - // /FamilyName| /Weight - // () readonly def\n from name table: 5 7 0 4 1 2 - // using: - // _cfFontEmbedOTFGetName(otf, 3, 1, 0x409, ?,&len) / - // _cfFontEmbedOTFGetName(otf, 1, 0, 0, ?, &len) + - // encoding - " /ItalicAngle %d def\n" - " /isFixedPitch %s def\n" - " /UnderlinePosition %f def\n" - " /UnderlineThickness %f def\n" - "end readonly def\n", - __cfFontEmbedGetLong(post + 4) >> 16, - (__cfFontEmbedGetULong(post + 12) ? - "true" : "false"), - (__cfFontEmbedGetShort(post + 8) - - __cfFontEmbedGetShort(post + 10) / 2) / - (float)otf->unitsPerEm, - __cfFontEmbedGetShort(post + 10) / - (float)otf->unitsPerEm); - __cfFontEmbedDynPrintF(&ds, "/Encoding 256 array\n" - "0 1 255 { 1 index exch /.notdef put } for\n"); - for (iA = 0; iA < len; iA ++) // encoding data: 0...255 -> /glyphname - { - const int gid = - (encoding) ? encoding[iA] : _cfFontEmbedOTFFromUnicode(otf, iA); - if (gid != 0) - __cfFontEmbedDynPrintF(&ds, "dup %d /%s put\n", - iA, get_glyphname(post, to_unicode, iA, gid)); - } - __cfFontEmbedDynPrintF(&ds, "readonly def\n"); - - if (binary) - __cfFontEmbedDynPrintF(&ds, - "/RD { string currentfile exch readstring pop } executeonly def\n"); - __cfFontEmbedDynPrintF(&ds, "/sfnts[\n"); - - if (ds.len < 0) - { - free(post); - free(ds.buf); - return (-1); - } - (*output)(ds.buf, ds.len, context); - ret += ds.len; - ds.len = 0; - - // TODO: only tables as in _cfFontEmbedOTFSubSet - // TODO: somehow communicate table boundaries: - // __cfFontEmbedOTFActionCopy does exactly one output call (per table) - // only __cfFontEmbedOTFActionReplace might do two (padding) - // {{{ copy tables verbatim (does not affect ds .len) - - struct __cf_fontembed_otf_write_s *otfree = NULL; -#if 0 - struct __cf_fontembed_otf_write_s *otw; - otwfree = otw = - malloc(sizeof(struct __cf_fontembed_otf_write_s) * otf->numTables); - if (!otw) - { - fprintf(stderr, "Bad alloc: %m\n"); - free(post); - free(ds.buf); - return (-1); - } - // just copy everything - for (iA = 0; iA < otf->numTables; iA ++) - { - otw[iA].tag = otf->tables[iA].tag; - otw[iA].action = __cfFontEmbedOTFActionCopy; - otw[iA].param = otf; - otw[iA].length = iA; - } - int numTables = otf->numTables; -#else - struct __cf_fontembed_otf_write_s otw[] = // sorted - { - {_CF_FONTEMBED_OTF_TAG('c', 'm', 'a', 'p'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('c', 'v', 't', ' '), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('f', 'p', 'g', 'm'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('g', 'l', 'y', 'f'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('h', 'h', 'e', 'a'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('h', 'm', 't', 'x'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('l', 'o', 'c', 'a'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('m', 'a', 'x', 'p'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('n', 'a', 'm', 'e'), - __cfFontEmbedOTFActionCopy, otf,}, - {_CF_FONTEMBED_OTF_TAG('p', 'r', 'e', 'p'), - __cfFontEmbedOTFActionCopy, otf,}, - // vhea vmtx (never used in PDF, but possible in PS >= 3011) - {0,0,0,0}}; - int numTables = __cfFontEmbedOTFIntersectTables(otf, otw); -#endif - - struct OUTFILTER_PS of; - of.out = output; - of.ctx = context; - of.len = 0; - if (binary) - iA = __cfFontEmbedOTFWriteSFNT(otw, otf->version, numTables, - outfilter_binary_ps, &of); - else - iA = __cfFontEmbedOTFWriteSFNT(otw, otf->version, numTables, - outfilter_ascii_ps, &of); - free(otfree); - if (iA == -1) - { - free(post); - free(ds.buf); - return (-1); - } - ret += of.len; - // }}} done copying - - __cfFontEmbedDynPrintF(&ds, "] def\n"); - - __cfFontEmbedDynPrintF(&ds, "/CharStrings %d dict dup begin\n" - "/.notdef 0 def\n", len); - for (iA = 0; iA < len; iA ++) // charstrings data: /glyphname -> gid - { - const int gid = - (encoding) ? encoding[iA] : _cfFontEmbedOTFFromUnicode(otf, iA); - if (gid) - __cfFontEmbedDynPrintF(&ds, "/%s %d def\n", - get_glyphname(post, to_unicode, iA, gid), gid); - // (respecting subsetting...) - } - __cfFontEmbedDynPrintF(&ds, "end readonly def\n"); - __cfFontEmbedDynPrintF(&ds, "FontName currentdict end definefont pop\n"); - free(post); - - if (ds.len < 0) - { - free(ds.buf); - return (-1); - } - (*output)(ds.buf, ds.len, context); - ret += ds.len; - ds.len = 0; - - free(ds.buf); - return (ret); -} -// }}} diff --git a/cupsfilters/fontembed/embed.c b/cupsfilters/fontembed/embed.c deleted file mode 100644 index 3082945f9..000000000 --- a/cupsfilters/fontembed/embed.c +++ /dev/null @@ -1,344 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include "embed-sfnt-private.h" -#include -#include -#include -#include -#include - - -static inline int -copy_file(FILE *f, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - DEBUG_assert(f); - DEBUG_assert(output); - - char buf[4096]; - int iA, ret = 0; - - ret = 0; - rewind(f); - do - { - iA = fread(buf, 1, 4096, f); - (*output)(buf, iA, context); - ret += iA; - } - while (iA > 0); - return (ret); -} -// }}} - - -// -// certain profiles: (=> constraints to be auto-applied in -// _cfFontEmbedEmbNew via >dest) -// PSold: T1->T1, TTF->T1, OTF->CFF->T1, STD->STD // output limit: T1 -// // (maybe length, -// // binary/text, ... limit) -// PS1: T1->T1, TTF->T1, OTF->CFF, STD->STD // output limit: T1,CFF -// // [does this really exists?] -// PS2: T1->T1, TTF->TTF, OTF->T1, STD->STD // output limit: T1, TTF -// PS3: T1->T1, TTF->TTF, OTF->CFF, STD->STD -// PDF12/13?: OTF->CFF -// PDF16: OTF->OTF (, T1->CFF?) -// --- rename KEEP_T1 to NEED_T1? NO_T42? -// -// converters: -// OTF->CFF, CFF->OTF (extract metrics, etc) -// (T1->CFF, CFF->T1) -// ((TTF->T1 ['good'; T1->TTF: not good])) -// [subsetTTF, subsetCFF, subsetT1] -// -// output modes: -// subset,CID(multibyte),(PS:)text/binary,(PS:)incremental -// -// TODO: remove dest from _cfFontEmbedEmbNew, replace with -// _cf_fontembed_emb_action_t constraints: -// - bitfield mask which ACTIONS are allowed. (problem: we want to force -// certain ones, e.g. MULTIBYTE) -// - e.g. currently _CF_FONTEMBED_EMB_C_PDF_OT has to functions -// - the only (other) 'difference' to now is the subsetting spec -// - another issue is, that emb_pdf_ might want some pdf version -// informatino (-> extra flag?) -// and funtion to determine appropriate mask for certain destination -// _cf_fontembed_emb_action_t emb_mask_for_dest(_cf_fontembed_emb_dest_t) -// TODO? determine viability before trying _cfFontEmbedEmbEmbed -// (idea: use _cfFontEmbedEmbEmbed(, NULL) -> will just return true/false -// [same codepath!]) -// -// TODO?! "always subset CJK" -// - - -_cf_fontembed_emb_params_t * -_cfFontEmbedEmbNew(_cf_fontembed_fontfile_t *font, - _cf_fontembed_emb_dest_t dest, - _cf_fontembed_emb_constraint_t mode) // {{{ -{ - DEBUG_assert(font); - - _cf_fontembed_emb_params_t *ret = - calloc(1, sizeof(_cf_fontembed_emb_params_t)); - if (!ret) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - if (mode & _CF_FONTEMBED_EMB_C_TAKE_FONTFILE) - _cfFontEmbedFontFileClose(font); - return (NULL); - } - ret->dest = dest; - ret->font = font; - if (mode & _CF_FONTEMBED_EMB_C_TAKE_FONTFILE) - ret->plan |= _CF_FONTEMBED_EMB_A_CLOSE_FONTFILE; - - // check parameters - if ((mode & _CF_FONTEMBED_EMB_C_KEEP_T1) && - (mode & _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE)) - { - fprintf(stderr, "Incompatible mode: KEEP_T1 and FORCE_MULTIBYTE\n"); - _cfFontEmbedEmbClose(ret); - return (NULL); - } - if ((mode & 0x07) > 5) - { - fprintf(stderr, "Bad subset specification\n"); - _cfFontEmbedEmbClose(ret); - return (NULL); - } - - // determine intype - int numGlyphs = 0; - if (font->sfnt) - { - if (font->sfnt->flags & _CF_FONTEMBED_OTF_F_FMT_CFF) - ret->intype = _CF_FONTEMBED_EMB_FMT_OTF; - else - ret->intype = _CF_FONTEMBED_EMB_FMT_TTF; - ret->rights = __cfFontEmbedEmbOTFGetRights(ret->font->sfnt); - numGlyphs = ret->font->sfnt->numGlyphs; // TODO - } - else if (font->stdname) - { - ret->intype = _CF_FONTEMBED_EMB_FMT_STDFONT; - ret->rights = _CF_FONTEMBED_EMB_RIGHT_NONE; - } else - DEBUG_assert(0); - -#if 0 - if ((ret->intype == _CF_FONTEMBED_EMB_FMT_CFF) && - (ret->cffFont.is_cid())) - { - ?= || ((ret->intype == _CF_FONTEMBED_EMB_FMT_OTF) && - (ret->sfnt->cffFont.is_cid())) // TODO? - ret->plan |= _CF_FONTEMBED_EMB_A_MULTIBYTE; - } -#endif // 0 - - // determine outtype - if (ret->intype == _CF_FONTEMBED_EMB_FMT_STDFONT) - { - ret->outtype = ret->intype; - if (mode & _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE) - { - fprintf(stderr, "Multibyte stdfonts are not possible\n"); - _cfFontEmbedEmbClose(ret); - return (NULL); - } - return (ret); // never subset, no multibyte - } - else if (ret->intype == _CF_FONTEMBED_EMB_FMT_T1) - { - if (mode & _CF_FONTEMBED_EMB_C_KEEP_T1) - ret->outtype = _CF_FONTEMBED_EMB_FMT_T1; - else { - ret->plan |= _CF_FONTEMBED_EMB_A_T1_TO_CFF; - ret->outtype = _CF_FONTEMBED_EMB_FMT_CFF; - } - } - else - ret->outtype = ret->intype; - if (ret->outtype == _CF_FONTEMBED_EMB_FMT_CFF) - { - if (mode & _CF_FONTEMBED_EMB_C_PDF_OT) - { - ret->outtype = _CF_FONTEMBED_EMB_FMT_OTF; - ret->plan |= _CF_FONTEMBED_EMB_A_CFF_TO_OTF; - } - } - else if (ret->outtype == _CF_FONTEMBED_EMB_FMT_OTF) - { - // TODO: no support yet; but we want to get the FontDescriptor/Name right - mode |= _CF_FONTEMBED_EMB_C_NEVER_SUBSET; - if (!(mode & _CF_FONTEMBED_EMB_C_PDF_OT)) - { // TODO!?! - ret->outtype = _CF_FONTEMBED_EMB_FMT_CFF; - ret->plan |= _CF_FONTEMBED_EMB_A_OTF_TO_CFF; - } - } - - if (mode & _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE) - ret->plan |= _CF_FONTEMBED_EMB_A_MULTIBYTE; - - // check rights (for subsetting) - if ((ret->rights & _CF_FONTEMBED_EMB_RIGHT_NONE) || - (ret->rights & _CF_FONTEMBED_EMB_RIGHT_BITMAPONLY) || - ((ret->rights & _CF_FONTEMBED_EMB_RIGHT_READONLY) && - (mode & _CF_FONTEMBED_EMB_C_EDITABLE_SUBSET)) || - ((ret->rights & _CF_FONTEMBED_EMB_RIGHT_NO_SUBSET) && - (mode & _CF_FONTEMBED_EMB_C_MUST_SUBSET))) - { - fprintf(stderr, "The font does not permit the requested embedding\n"); - _cfFontEmbedEmbClose(ret); - return (NULL); - } - else if ((!(ret->rights & _CF_FONTEMBED_EMB_RIGHT_NO_SUBSET)) && - (!(mode & _CF_FONTEMBED_EMB_C_NEVER_SUBSET))) - ret->plan |= _CF_FONTEMBED_EMB_A_SUBSET; - - // alloc subset - if (ret->plan & _CF_FONTEMBED_EMB_A_SUBSET) - { - ret->subset = _cfFontEmbedBitSetNew(numGlyphs); - if (!ret->subset) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - _cfFontEmbedEmbClose(ret); - return (NULL); - } - } - - return (ret); -} -// }}} - - -int -_cfFontEmbedEmbEmbed(_cf_fontembed_emb_params_t *emb, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - DEBUG_assert(emb); - - if (emb->dest == _CF_FONTEMBED_EMB_DEST_NATIVE) - { - } - else if (emb->dest <= _CF_FONTEMBED_EMB_DEST_PS) - { - int ret =- 2; - const char *fontname = - __cfFontEmbedEmbOTFGetFontName(emb->font->sfnt); // TODO!! - (*output)("%%BeginFont: ", 13, context); - (*output)(fontname, strlen(fontname), context); - (*output)("\n", 1, context); - if (emb->outtype == _CF_FONTEMBED_EMB_FMT_T1) - { - } - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_TTF) - { // emb->outtype==EMB_OUTPUT_OTF - // is stupid (?) - // do Type42 - ret = __cfFontEmbedEmbOTFPS(emb->font->sfnt, NULL, 256, NULL, output, - context); // TODO? - } - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_CFF) - { - } - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_STDFONT) - { - } - if (ret != -2) - { - if (ret != -1) - (*output)("%%EndFont\n", 10, context); - else - fprintf(stderr, "Failed\n"); - return (ret); - } - } - else if (emb->dest <= _CF_FONTEMBED_EMB_DEST_PDF16) - { - if (emb->outtype == _CF_FONTEMBED_EMB_FMT_TTF) - { - DEBUG_assert(emb->font->sfnt); - if (emb->plan & _CF_FONTEMBED_EMB_A_SUBSET) - return (_cfFontEmbedOTFSubSet(emb->font->sfnt, emb->subset, output, - context)); - else if (emb->font->sfnt->numTTC) - return (_cfFontEmbedOTFTTCExtract(emb->font->sfnt, output, context)); - else // copy verbatim - return (copy_file(emb->font->sfnt->f, output, context)); - } - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_OTF) - { - if (emb->plan & _CF_FONTEMBED_EMB_A_CFF_TO_OTF) - { - if (emb->plan & _CF_FONTEMBED_EMB_A_T1_TO_CFF) - { - // TODO - } - else - { - // DEBUG_assert(emb->font->cff); - // TODO - } - } - else - { - DEBUG_assert(emb->font->sfnt); - if (emb->plan & _CF_FONTEMBED_EMB_A_SUBSET) - return (_cfFontEmbedOTFSubSetCFF(emb->font->sfnt, emb->subset, output, - context)); - else - return (copy_file(emb->font->sfnt->f, output, context)); - } - } - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_CFF) - { - if (emb->plan & _CF_FONTEMBED_EMB_A_OTF_TO_CFF) - { - DEBUG_assert(emb->font->sfnt); - if (emb->plan & _CF_FONTEMBED_EMB_A_SUBSET) - { - // TODO - } - else - { - return (_cfFontEmbedOTFCFFExtract(emb->font->sfnt, output, context)); - } - } - else - { - // TODO - } - } - } - - fprintf(stderr, "NOT IMPLEMENTED\n"); - DEBUG_assert(0); - return -1; -} -// }}} - - -void -_cfFontEmbedEmbClose(_cf_fontembed_emb_params_t *emb) // {{{ -{ - if (emb) - { - free(emb->subset); - if (emb->plan & _CF_FONTEMBED_EMB_A_CLOSE_FONTFILE) - _cfFontEmbedFontFileClose(emb->font); - free(emb); - } -} -// }}} diff --git a/cupsfilters/fontembed/fontfile.c b/cupsfilters/fontembed/fontfile.c deleted file mode 100644 index 1ce29e541..000000000 --- a/cupsfilters/fontembed/fontfile.c +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include - - -//_cf_fontembed_fontfile_t *_cfFontEmbedFontFileOpen(const char *filename); - -#if 0 -_cf_fontembed_fontfile_t * -_cfFontEmbedFontFileOpen(const char *filename) -{ - // TODO? check magic - if (...) - { - } -} -#endif // 0 - - -_cf_fontembed_fontfile_t * -_cfFontEmbedFontFileOpenSFNT(_cf_fontembed_otf_file_t *otf) // {{{ -{ - if (!otf) - { - DEBUG_assert(0); - return (NULL); - } - _cf_fontembed_fontfile_t *ret = calloc(1, sizeof(_cf_fontembed_fontfile_t)); - - ret->sfnt = otf; - - return (ret); -} -// }}} - - -_cf_fontembed_fontfile_t * -_cfFontEmbedFontFileOpenStd(const char *name) // {{{ -{ - DEBUG_assert(name); - _cf_fontembed_fontfile_t *ret = calloc(1, sizeof(_cf_fontembed_fontfile_t)); - - ret->stdname = strdup(name); - - return (ret); -} -// }}} - - -void _cfFontEmbedFontFileClose(_cf_fontembed_fontfile_t *ff) // {{{ -{ - if (ff) - { - _cfFontEmbedOTFClose(ff->sfnt); - // ??? cff_close(ff->cff); - free(ff->stdname); - free(ff); - } -} -// }}} diff --git a/cupsfilters/fontembed/frequent-private.h b/cupsfilters/fontembed/frequent-private.h deleted file mode 100644 index 533df68bd..000000000 --- a/cupsfilters/fontembed/frequent-private.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _FONTEMBED_FREQUENT_H_ -#define _FONTEMBED_FREQUENT_H_ - -#include - -typedef struct __cf_fontembed_frequent_s __cf_fontembed_frequent_t; - -// size is the precision/return size: it will find at most >size -// elements (i.e. all, if there) with frequency > 1 / (size + 1) -__cf_fontembed_frequent_t *__cfFontEmbedFrequentNew(int size); - // - just free() it - -void __cfFontEmbedFrequentAdd(__cf_fontembed_frequent_t *freq, intptr_t key); - -// might return INTPTR_MIN, if not populated -// this is only an approximation! -intptr_t __cfFontEmbedFrequentGet(__cf_fontembed_frequent_t *freq, int pos); - // 0 is "most frequent" - -#endif // !_FONTEMBED_FREQUENT_H_ diff --git a/cupsfilters/fontembed/frequent.c b/cupsfilters/fontembed/frequent.c deleted file mode 100644 index 749176004..000000000 --- a/cupsfilters/fontembed/frequent.c +++ /dev/null @@ -1,114 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "frequent-private.h" -#include -#include - - -// misra-gries -// http://www2.research.att.com/~marioh/papers/vldb08-2.pdf - -struct __cf_fontembed_frequent_s -{ - int size, czero; - char sorted; - struct - { - intptr_t key; - int count, - zero; - } pair[]; -}; - - -// size is the precision/return size: in sequence with n _add(), -// it will find at most >size elements with occurence > n / (size + 1) times - -__cf_fontembed_frequent_t * -__cfFontEmbedFrequentNew(int size) // {{{ - just free() it -{ - DEBUG_assert(size>0); - __cf_fontembed_frequent_t *ret = - malloc(sizeof(ret[0]) + sizeof(ret->pair[0]) * size); - if (!ret) - return (NULL); - ret->size = size; - ret->czero = 0; - ret->sorted = 1; - int iA; - for (iA = 0; iA < size; iA ++) - { - ret->pair[iA].key = INTPTR_MIN; - ret->pair[iA].count = 0; - ret->pair[iA].zero = 0; - } - - return (ret); -} -// }}} - - -void -__cfFontEmbedFrequentAdd(__cf_fontembed_frequent_t *freq, - intptr_t key) // {{{ -{ - DEBUG_assert(freq); - int iA, zero = -1; - for (iA = freq->size - 1; iA >= 0; iA --) - { - if (freq->pair[iA].key == key) - { - freq->pair[iA].count ++; - freq->sorted = 0; - return; - } - else if (freq->pair[iA].count == freq->czero) - zero = iA; - } - if (zero >= 0) // insert into set - { - freq->pair[zero].key = key; - freq->pair[zero].count ++; // i.e. czero + 1 - freq->pair[zero].zero = freq->czero; - // if it was sorted, the free entries are at the end. zero points to the - // first free entry, because of the loop direction - } - else // out-of-set count - freq->czero ++; -} -// }}} - - -static int -frequent_cmp(const void *a, const void *b) // {{{ -{ - const typeof(((__cf_fontembed_frequent_t *)0)->pair[0]) *aa = a; - const typeof(((__cf_fontembed_frequent_t *)0)->pair[0]) *bb = b; - return ((bb->count - bb->zero) - (aa->count - aa->zero)); -} -// }}} - - -// true frequency is somewhere between (count-zero) and count - -intptr_t -__cfFontEmbedFrequentGet(__cf_fontembed_frequent_t *freq, - int pos) // {{{ -{ - DEBUG_assert(freq); - if (!freq->sorted) - { - // sort by (count-zero) - qsort(freq->pair, freq->size, sizeof(freq->pair[0]), frequent_cmp); - freq->sorted = 1; - } - if ((pos < 0) || (pos >= freq->size)) - return (INTPTR_MIN); - return (freq->pair[pos].key); -} -// }}} diff --git a/cupsfilters/fontembed/macroman-private.h b/cupsfilters/fontembed/macroman-private.h deleted file mode 100644 index be2b19e89..000000000 --- a/cupsfilters/fontembed/macroman-private.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// data header only. for inclusion in only one file -#ifndef _FONTEMBED_MACROMAN_PRIVATE_H_ -#define _FONTEMBED_MACROMAN_PRIVATE_H_ - -#ifdef WITH_MACROMAN -static const char *__cf_fontembed_mac_roman[] = -{ - ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", - "quotesingle", "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", "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", "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", - "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", - "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", - "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", - "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", - "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", - "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", - "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", - "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", - "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", - "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron", "scaron", - "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", - "onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", - "Scedilla", "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat" -}; -#else -static const char *__cf_fontembed_mac_roman[] = -{ - NULL -}; -#endif // WITH_MACROMAN - -#endif // !_FONTEMBED_MACROMAN_PRIVATE_H_ diff --git a/cupsfilters/fontembed/sfnt-private.h b/cupsfilters/fontembed/sfnt-private.h deleted file mode 100644 index 4bb24e5e7..000000000 --- a/cupsfilters/fontembed/sfnt-private.h +++ /dev/null @@ -1,162 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _FONTEMBED_SFNT_INT_H_ -#define _FONTEMBED_SFNT_INT_H_ - - -// -// Types and structures... -// - -struct __cf_fontembed_otf_write_s -{ - unsigned long tag; - int (*action)(void *param, int length, _cf_fontembed_output_fn_t output, - void *context); - // -1 on error, num_bytes_written on success; if >output == NULL - // return checksum in (unsigned int *)context instead. - void *param; - int length; -}; - - -// -// Inline functions... -// - -static inline unsigned short -__cfFontEmbedGetUShort(const char *buf) // {{{ -{ - return (((unsigned char)buf[0] << 8) | ((unsigned char)buf[1])); -} -// }}} - - -static inline short -__cfFontEmbedGetShort(const char *buf) // {{{ -{ - return ((buf[0] << 8) | ((unsigned char)buf[1])); -} -// }}} - - -static inline unsigned int -__cfFontEmbedGetUInt24(const char *buf) // {{{ -{ - return (((unsigned char)buf[0] << 16) | - ((unsigned char)buf[1] << 8 )| - ((unsigned char)buf[2])); -} -// }}} - - -static inline unsigned int -__cfFontEmbedGetULong(const char *buf) // {{{ -{ - return (((unsigned char)buf[0] << 24) | - ((unsigned char)buf[1] << 16) | - ((unsigned char)buf[2] << 8) | - ((unsigned char)buf[3])); -} -// }}} - - -static inline int -__cfFontEmbedGetLong(const char *buf) // {{{ -{ - return ((buf[0] << 24) | - ((unsigned char)buf[1] << 16) | - ((unsigned char)buf[2] << 8) | - ((unsigned char)buf[3])); -} -// }}} - - -static inline void -__cfFontEmbedSetUShort(char *buf, - unsigned short val) // {{{ -{ - buf[0] = val >> 8; - buf[1] = val & 0xff; -} - - -// }}} -static inline void -__cfFontEmbedSetULong(char *buf, - unsigned int val) // {{{ -{ - buf[0] = val >> 24; - buf[1] = (val >> 16) & 0xff; - buf[2] = (val >> 8) & 0xff; - buf[3] = val & 0xff; -} -// }}} - - -static inline unsigned int -__cfFontEmbedOTFCheckSum(const char *buf, - unsigned int len) // {{{ -{ - unsigned int ret = 0; - for (len = (len + 3) / 4; len > 0; len--, buf += 4) - ret += __cfFontEmbedGetULong(buf); - return (ret); -} -// }}} - - -static inline int -__cfFontEmbedGetWidthFast(_cf_fontembed_otf_file_t *otf, - int gid) // {{{ -{ - if (gid >= otf->numberOfHMetrics) - return (__cfFontEmbedGetUShort(otf->hmtx + - (otf->numberOfHMetrics - 1) * 4)); - else - return (__cfFontEmbedGetUShort(otf->hmtx + gid * 4)); -} -// }}} - - -// -// Prototypes... -// - -int __cfFontEmbedOTFLoadGlyf(_cf_fontembed_otf_file_t *otf); // - 0 on success -int __cfFontEmbedOTFLoadMore(_cf_fontembed_otf_file_t *otf); // - 0 on success - -int __cfFontEmbedOTFFindTable(_cf_fontembed_otf_file_t *otf, - unsigned int tag); // - table_index or - // -1 on error - -int __cfFontEmbedOTFActionCopy(void *param, int csum, - _cf_fontembed_output_fn_t output, void *context); -int __cfFontEmbedOTFActionReplace(void *param, int csum, - _cf_fontembed_output_fn_t output, - void *context); - -// Note: Don't use this directly. __cfFontEmbedOTFWriteSFNT will internally -// replace -// __cfFontEmbedOTFActionCopy for head with this -int __cfFontEmbedOTFActionCopyHead(void *param, int csum, - _cf_fontembed_output_fn_t output, - void *context); - -int __cfFontEmbedOTFWriteSFNT(struct __cf_fontembed_otf_write_s *otw, - unsigned int version, int numTables, - _cf_fontembed_output_fn_t output, void *context); - -/** from sfnt_subset.c: **/ - -// otw {0, }-terminated, will be modified; returns numTables for -// __cfFontEmbedOTFWriteSFNT -int __cfFontEmbedOTFIntersectTables(_cf_fontembed_otf_file_t *otf, - struct __cf_fontembed_otf_write_s *otw); - -#endif // !_FONTEMBED_SFNT_INT_H_ diff --git a/cupsfilters/fontembed/sfnt-subset.c b/cupsfilters/fontembed/sfnt-subset.c deleted file mode 100644 index f1803ed94..000000000 --- a/cupsfilters/fontembed/sfnt-subset.c +++ /dev/null @@ -1,450 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "sfnt-private.h" -#include -#include -#include -#include - - -int -_cfFontEmbedOTFTTCExtract(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - DEBUG_assert(otf); - DEBUG_assert(output); - DEBUG_assert(otf->numTTC); - - int iA; - - struct __cf_fontembed_otf_write_s *otw; - otw = malloc(sizeof(struct __cf_fontembed_otf_write_s) * otf->numTables); - if (!otw) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - return (-1); - } - - // just copy everything - for (iA = 0; iA < otf->numTables; iA ++) - { - otw[iA].tag = otf->tables[iA].tag; - otw[iA].action = __cfFontEmbedOTFActionCopy; - otw[iA].param = otf; - otw[iA].length = iA; - } - iA = __cfFontEmbedOTFWriteSFNT(otw, otf->version, otf->numTables, output, - context); - free(otw); - - return (iA); -} -// }}} - - -// otw {0, }-terminated, will be modified; returns numTables for -// __cfFontEmbedOTFWriteSFNT -int -__cfFontEmbedOTFIntersectTables(_cf_fontembed_otf_file_t *otf, - struct __cf_fontembed_otf_write_s *otw) // {{{ -{ - int iA, iB, numTables = 0; - - for (iA = 0, iB = 0; (iA < otf->numTables) && (otw[iB].tag);) - { - if (otf->tables[iA].tag == otw[iB].tag) - { - if (otw[iB].action == __cfFontEmbedOTFActionCopy) - otw[iB].length = iA; // original table location found. - if (iB != numTables) // >, actually - memmove(otw + numTables, otw + iB, - sizeof(struct __cf_fontembed_otf_write_s)); - iA ++; - iB ++; - numTables ++; - } - else if (otf->tables[iA].tag < otw[iB].tag) - iA ++; - else // not in otf->tables - { - if (otw[iB].action != __cfFontEmbedOTFActionCopy) // keep - { - if (iB != numTables) // >, actually - memmove(otw + numTables, otw + iB, - sizeof(struct __cf_fontembed_otf_write_s)); - numTables ++; - } - // else delete - iB ++; - } - } - return (numTables); -} -// }}} - - -// include components (set bit in >glyphs) of currently loaded compound glyph -// (with >curgid) -// returns additional space requirements (when bits below >donegid are touched) - -static int -otf_subset_glyf(_cf_fontembed_otf_file_t *otf, - int curgid, - int donegid, - _cf_fontembed_bit_set_t glyphs) // {{{ -{ - int ret = 0; - if (__cfFontEmbedGetShort(otf->gly) >= 0) // not composite - return (ret); // done - - char *cur = otf->gly + 10; - - unsigned short flags; - do - { - flags = __cfFontEmbedGetUShort(cur); - const unsigned short sub_gid = __cfFontEmbedGetUShort(cur + 2); - DEBUG_assert(sub_gid < otf->numGlyphs); - if (!_cfFontEmbedBitCheck(glyphs, sub_gid)) - { - // bad: temporarily load sub glyph - const int len = _cfFontEmbedOTFGetGlyph(otf, sub_gid); - DEBUG_assert(len > 0); - _cfFontEmbedBitSet(glyphs, sub_gid); - if (sub_gid < donegid) - { - ret += len; - ret += otf_subset_glyf(otf, sub_gid, donegid, glyphs); - // composite of composites?, e.g. in DejaVu - } -#ifdef DEBUG - const int res = -#endif - _cfFontEmbedOTFGetGlyph(otf, curgid); // reload current glyph - DEBUG_assert(res); - } - - // skip parameters - cur += 6; - if (flags & 0x01) - cur += 2; - if (flags & 0x08) - cur += 2; - else if (flags & 0x40) - cur += 4; - else if (flags & 0x80) - cur += 8; - } - while (flags & 0x20); // more components - - return (ret); -} -// }}} - - -// TODO: cmap only required in non-CID context - -int -_cfFontEmbedOTFSubSet(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_bit_set_t glyphs, - _cf_fontembed_output_fn_t output, - void *context) // {{{ - returns number of bytes written -{ - DEBUG_assert(otf); - DEBUG_assert(glyphs); - DEBUG_assert(output); - - int iA, b, c; - - // first pass: include all required glyphs - _cfFontEmbedBitSet(glyphs, 0); // .notdef always required - int glyfSize = 0; - for (iA = 0, b = 0, c = 1; iA < otf->numGlyphs; iA ++, c <<= 1) - { - if (!c) - { - b ++; - c = 1; - } - if (glyphs[b] & c) - { - int len = _cfFontEmbedOTFGetGlyph(otf, iA); - if (len < 0) - { - DEBUG_assert(0); - return (-1); - } - else if (len > 0) - { - glyfSize += len; - len = otf_subset_glyf(otf, iA, iA, glyphs); - if (len < 0) - { - DEBUG_assert(0); - return (-1); - } - glyfSize += len; - } - } - } - - // second pass: calculate new glyf and loca - int locaSize = (otf->numGlyphs + 1) * (otf->indexToLocFormat + 1) * 2; - - char *new_loca = malloc(locaSize); - char *new_glyf = malloc(glyfSize); - if ((!new_loca) || (!new_glyf)) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - DEBUG_assert(0); - free(new_loca); - free(new_glyf); - return (-1); - } - - int offset = 0; - for (iA = 0, b = 0, c = 1; iA < otf->numGlyphs; iA ++, c <<= 1) - { - if (!c) - { - b ++; - c = 1; - } - - DEBUG_assert(offset % 2 == 0); - // TODO? change format? if glyfSize < 0x20000 - if (otf->indexToLocFormat == 0) - __cfFontEmbedSetUShort(new_loca + iA * 2, offset / 2); - else // ==1 - __cfFontEmbedSetULong(new_loca + iA * 4, offset); - - if (glyphs[b] & c) - { - const int len = _cfFontEmbedOTFGetGlyph(otf, iA); - DEBUG_assert(len >= 0); - memcpy(new_glyf + offset, otf->gly, len); - offset += len; - } - } - // last entry - if (otf->indexToLocFormat == 0) - __cfFontEmbedSetUShort(new_loca + otf->numGlyphs * 2, offset / 2); - else // ==1 - __cfFontEmbedSetULong(new_loca + otf->numGlyphs * 4, offset); - DEBUG_assert(offset == glyfSize); - - // determine new tables. - struct __cf_fontembed_otf_write_s otw[] = // sorted - { - // TODO: cmap only required in non-CID context or always in CFF - {_CF_FONTEMBED_OTF_TAG('c', 'm', 'a', 'p'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('c', 'v', 't', ' '), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('f', 'p', 'g', 'm'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('g', 'l', 'y', 'f'), - __cfFontEmbedOTFActionReplace, new_glyf, glyfSize}, - {_CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd'), - __cfFontEmbedOTFActionCopy, otf, }, // _copy_head - {_CF_FONTEMBED_OTF_TAG('h', 'h', 'e', 'a'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('h', 'm', 't', 'x'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('l', 'o', 'c', 'a'), - __cfFontEmbedOTFActionReplace, new_loca, locaSize}, - {_CF_FONTEMBED_OTF_TAG('m', 'a', 'x', 'p'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('n', 'a', 'm', 'e'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('p', 'r', 'e', 'p'), - __cfFontEmbedOTFActionCopy, otf, }, - // vhea vmtx (never used in PDF, but possible in PS>=3011) - {0, 0, 0, 0} - }; - - // and write them - int numTables = __cfFontEmbedOTFIntersectTables(otf, otw); - int ret = __cfFontEmbedOTFWriteSFNT(otw, otf->version, numTables, output, - context); - - free(new_loca); - free(new_glyf); - return (ret); - - //TODO ? reduce cmap [to (1,0) ;-)] - //TODO (cmap for non-cid) -} -// }}} - - -// TODO no subsetting actually done (for now) - -int -_cfFontEmbedOTFSubSetCFF(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_bit_set_t glyphs, - _cf_fontembed_output_fn_t output, - void *context) // {{{ - returns number of bytes written -{ - DEBUG_assert(otf); - DEBUG_assert(output); - - // TODO char *new_cff = cff_subset(...); - - // determine new tables. - struct __cf_fontembed_otf_write_s otw[] = - { - {_CF_FONTEMBED_OTF_TAG('C', 'F', 'F', ' '), - __cfFontEmbedOTFActionCopy, otf, }, -// {_CF_FONTEMBED_OTF_TAG('C', 'F', 'F', ' '), -// __cfFontEmbedOTFActionReplace, new_glyf, glyfSize}, - {_CF_FONTEMBED_OTF_TAG('c', 'm', 'a', 'p'), - __cfFontEmbedOTFActionCopy, otf, }, -#if 0 // not actually needed! - {_CF_FONTEMBED_OTF_TAG('c', 'v', 't', ' '), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('f', 'p', 'g', 'm'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd'), - __cfFontEmbedOTFActionCopy, otf, }, // _copy_head - {_CF_FONTEMBED_OTF_TAG('h', 'h', 'e', 'a'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('h', 'm', 't', 'x'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('m', 'a', 'x', 'p'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('n', 'a', 'm', 'e'), - __cfFontEmbedOTFActionCopy, otf, }, - {_CF_FONTEMBED_OTF_TAG('p', 'r', 'e', 'p'), - __cfFontEmbedOTFActionCopy, otf, }, -#endif // 0 - {0, 0, 0, 0} - }; - - // and write them - int numTables = __cfFontEmbedOTFIntersectTables(otf, otw); - int ret = __cfFontEmbedOTFWriteSFNT(otw, otf->version, numTables, output, - context); - -// free(new_cff); - return (ret); -} -// }}} - - -//int copy_block(FILE *f, long pos, int length, -// _cf_fontembed_output_fn_t output, -// void *context); // copied bytes or -1 (also on premature EOF) - -static int -copy_block(FILE *f, - long pos, - int length, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - DEBUG_assert(f); - DEBUG_assert(output); - - char buf[4096]; - int iA, ret; - - ret = fseek(f, pos, SEEK_SET); - if (ret == -1) - { - fprintf(stderr, "Seek failed: %s\n", strerror(errno)); - return (-1); - } - ret = 0; - while (length > 4096) - { - iA = fread(buf, 1, 4096, f); - if (iA < 4096) - return (-1); - (*output)(buf, iA, context); - ret += iA; - length -= iA; - } - iA = fread(buf, 1, length, f); - if (iA < length) - return (-1); - (*output)(buf, iA, context); - ret += iA; - - return (ret); -} -// }}} - - -int -_cfFontEmbedOTFCFFExtract(_cf_fontembed_otf_file_t *otf, - _cf_fontembed_output_fn_t output, - void *context) // {{{ - returns number of bytes - // written -{ - DEBUG_assert(otf); - DEBUG_assert(output); - - int idx = - __cfFontEmbedOTFFindTable(otf, _CF_FONTEMBED_OTF_TAG('C', 'F', 'F', ' ')); - if (idx == -1) - return (-1); - - const _cf_fontembed_otf_dir_ent_t *table = otf->tables + idx; - - return (copy_block(otf->f, table->offset, table->length, output, context)); -} -// }}} - - -// CFF *otf_get_cff(); // not load, but create by "substream"-in ctor - -#if 0 // TODO elsewhere : char *cff_subset(...); - // first pass: include all required glyphs - _cfFontEmbedBitSet(glyphs, 0); // .notdef always required - int glyfSize = 0; - for (iA = 0, b = 0, c = 1; iA < otf->numGlyphs; iA ++, c <<= 1) - { - if (!c) - { - b ++; - c = 1; - } - if (glyphs[b] & c) - { - // TODO: cff_glyph - } - } - - // second pass: calculate new glyf and loca - char *new_cff = malloc(cffSize); - if (!new_cff) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - DEBUG_assert(0); - return (-1); - } - - int offset = 0; - for (iA = 0, b = 0, c = 1; iA < otf->numGlyphs; iA ++, c <<= 1) - { - if (!c) - { - b ++; - c = 1; - } - if (glyphs[b] & c) - { - //... - } - } - return (new_cff); -#endif // 0 diff --git a/cupsfilters/fontembed/sfnt.c b/cupsfilters/fontembed/sfnt.c deleted file mode 100644 index 88331587c..000000000 --- a/cupsfilters/fontembed/sfnt.c +++ /dev/null @@ -1,1216 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "sfnt-private.h" -#include -#include -#include -#include - - -// TODO? -// __cfFontEmbedGetShort(head + 48) // fontDirectionHint -// reqd. Tables: cmap, head, hhea, hmtx, maxp, name, OS/2, post -// OTF: glyf, loca [cvt, fpgm, prep] -// - -static void -otf_bsearch_params(int num, // {{{ - int recordSize, - int *searchRange, - int *entrySelector, - int *rangeShift) -{ - DEBUG_assert(num > 0); - DEBUG_assert(searchRange); - DEBUG_assert(entrySelector); - DEBUG_assert(rangeShift); - - int iA, iB; - for (iA = 1, iB = 0; iA <= num; iA <<= 1, iB ++); - - *searchRange = iA * recordSize / 2; - *entrySelector = iB - 1; - *rangeShift = num * recordSize - *searchRange; -} -// }}} - - -static char * -otf_bsearch(char *table, // {{{ - const char *target, - int len, - int searchRange, - int entrySelector, - int rangeShift, - int lower_bound) // return lower_bound, if !=0 -{ - char *ret = table + rangeShift; - if (memcmp(target, ret, len) < 0) - ret = table; - - for (; entrySelector > 0; entrySelector --) - { - searchRange >>= 1; - ret += searchRange; - if (memcmp(target, ret, len) < 0) - ret -= searchRange; - } - const int result = memcmp(target, ret, len); - if (result == 0) - return (ret); - else if (lower_bound) - { - if (result > 0) - return (ret + searchRange); - return (ret); - } - return (NULL); // not found; -} -// }}} - - -static _cf_fontembed_otf_file_t * -otf_new(FILE *f) // {{{ -{ - DEBUG_assert(f); - - _cf_fontembed_otf_file_t *ret; - ret = calloc(1, sizeof(_cf_fontembed_otf_file_t)); - if (ret) - { - ret->f = f; - ret->version = 0x00010000; - } - - return (ret); -} -// }}} - - -// will alloc, if >buf == NULL, returns >buf, or NULL on error -// NOTE: you probably want _cfFontEmbedOTFGetTable() - -static char * -otf_read(_cf_fontembed_otf_file_t *otf, - char *buf, - long pos, - int length) // {{{ -{ - char *ours = NULL; - - if (length == 0) - return (buf); - else if (length < 0) - { - DEBUG_assert(0); - return (NULL); - } - - int res = fseek(otf->f, pos, SEEK_SET); - if (res == -1) - { - fprintf(stderr, "Seek failed: %s\n", strerror(errno)); - return (NULL); - } - - // (+3) & ~3 for checksum... - const int pad_len = (length + 3) & ~3; - if (!buf) - { - ours = buf = malloc(sizeof(char) * pad_len); - if (!buf) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - return (NULL); - } - } - - res = fread(buf, 1, pad_len, otf->f); - if (res != pad_len) - { - if (res == length) // file size not multiple of 4, pad with zero - memset(buf + res, 0, pad_len - length); - else { - fprintf(stderr, "Short read\n"); - free(ours); - return (NULL); - } - } - - return (buf); -} -// }}} - - -static int -otf_get_ttc_start(_cf_fontembed_otf_file_t *otf, - int use_ttc) // {{{ -{ - char buf[4]; - - if (!otf->numTTC) // > 0 if TTC... - return (0); - - int pos = 0; - if ((use_ttc < 0) || (use_ttc >= otf->numTTC) || - (!otf_read(otf, buf, pos + 12 + 4 * use_ttc, 4))) - { - fprintf(stderr, "Bad TTC subfont number\n"); - return (-1); - } - return (__cfFontEmbedGetULong(buf)); -} -// }}} - - -static _cf_fontembed_otf_file_t * -otf_do_load(_cf_fontembed_otf_file_t *otf, - int pos) // {{{ -{ - int iA; - char buf[16]; - - // {{{ read offset table - if (otf_read(otf, buf, pos, 12)) - { - otf->version = __cfFontEmbedGetULong(buf); - if (otf->version == 0x00010000) // 1.0 truetype - { - } - else if (otf->version == - _CF_FONTEMBED_OTF_TAG('O', 'T', 'T', 'O')) // OTF(CFF) - otf->flags |= _CF_FONTEMBED_OTF_F_FMT_CFF; - else if (otf->version == - _CF_FONTEMBED_OTF_TAG('t', 'r', 'u', 'e')) // (old mac) - { - } - else if (otf->version == - _CF_FONTEMBED_OTF_TAG('t', 'y', 'p', '1')) // sfnt wrapped type1 - { - // TODO: unsupported - } - else - { - _cfFontEmbedOTFClose(otf); - otf = NULL; - } - pos += 12; - } - else - { - _cfFontEmbedOTFClose(otf); - otf = NULL; - } - if (!otf) - { - fprintf(stderr, "Not a ttf font\n"); - return (NULL); - } - otf->numTables = __cfFontEmbedGetUShort(buf + 4); - // }}} - - // {{{ read directory - otf->tables = malloc(sizeof(_cf_fontembed_otf_dir_ent_t) * otf->numTables); - if (!otf->tables) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - for (iA = 0; iA < otf->numTables; iA ++) - { - if (!otf_read(otf, buf, pos, 16)) - { - _cfFontEmbedOTFClose(otf); - return (NULL); - } - otf->tables[iA].tag = __cfFontEmbedGetULong(buf); - otf->tables[iA].checkSum = __cfFontEmbedGetULong(buf + 4); - otf->tables[iA].offset = __cfFontEmbedGetULong(buf + 8); - otf->tables[iA].length = __cfFontEmbedGetULong(buf + 12); - if ((otf->tables[iA].tag == _CF_FONTEMBED_OTF_TAG('C', 'F', 'F', ' ')) && - ((otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF) == 0)) - { - fprintf(stderr, "Wrong magic\n"); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - else if ((otf->tables[iA].tag == _CF_FONTEMBED_OTF_TAG('g', 'l', 'y', 'p')) && - (otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF)) - { - fprintf(stderr, "Wrong magic\n"); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - pos += 16; - } - // }}} - - //otf->flags |= _CF_FONTEMBED_OTF_F_DO_CHECKSUM; - // {{{ check head table - int len = 0; - char *head = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd'), - &len); - if ((!head) || - (__cfFontEmbedGetULong(head + 0) != 0x00010000) || // version - (len != 54) || - (__cfFontEmbedGetULong(head + 12) != 0x5F0F3CF5) || // magic - (__cfFontEmbedGetShort(head + 52) != 0x0000)) // glyphDataFormat - { - fprintf(stderr, "Unsupported OTF font / head table \n"); - free(head); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - // }}} - otf->unitsPerEm = __cfFontEmbedGetUShort(head + 18); - otf->indexToLocFormat = __cfFontEmbedGetShort(head + 50); - - // {{{ checksum whole file - if (otf->flags & _CF_FONTEMBED_OTF_F_DO_CHECKSUM) - { - unsigned int csum = 0; - char tmp[1024]; - rewind(otf->f); - while (!feof(otf->f)) - { - len = fread(tmp, 1, 1024, otf->f); - if (len & 3) // zero padding reqd. - memset(tmp + len, 0, 4 - (len & 3)); - csum += __cfFontEmbedOTFCheckSum(tmp, len); - } - if (csum != 0xb1b0afba) - { - fprintf(stderr, "Wrong global checksum\n"); - free(head); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - } - // }}} - free(head); - - // {{{ read maxp table / numGlyphs - char *maxp = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('m', 'a', 'x', 'p'), - &len); - if (maxp) - { - const unsigned int maxp_version = __cfFontEmbedGetULong(maxp); - if ((maxp_version == 0x00005000) && (len == 6)) // version 0.5 - { - otf->numGlyphs = __cfFontEmbedGetUShort(maxp + 4); - if ((otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF) == 0) // only CFF - { - free(maxp); - maxp = NULL; - } - } - else if ((maxp_version == 0x00010000) && (len == 32)) // version 1.0 - { - otf->numGlyphs = __cfFontEmbedGetUShort(maxp + 4); - if (otf->flags&_CF_FONTEMBED_OTF_F_FMT_CFF) // only TTF - { - free(maxp); - maxp = NULL; - } - } - else - { - free(maxp); - maxp = NULL; - } - } - if (!maxp) - { - fprintf(stderr, "Unsupported OTF font / maxp table \n"); - free(maxp); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - free(maxp); - // }}} - - return (otf); -} -// }}} - - -_cf_fontembed_otf_file_t * -_cfFontEmbedOTFLoad(const char *file) // {{{ -{ - FILE *f; - _cf_fontembed_otf_file_t *otf; - - int use_ttc =- 1; - if ((f = fopen(file, "rb")) == NULL) - { - // check for TTC - char *tmp = strrchr(file, '/'), *end; - if (tmp) - { - use_ttc = strtoul(tmp + 1, &end, 10); - if (!*end) - { - end = malloc((tmp - file + 1) * sizeof(char)); - if (!end) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - return (NULL); - } - strncpy(end, file, tmp - file); - end[tmp - file] = 0; - f = fopen(end, "rb"); - free(end); - } - } - if (!f) - { - fprintf(stderr, "Could not open \"%s\": %s\n", file, strerror(errno)); - return (NULL); - } - } - otf = otf_new(f); - if (!otf) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - fclose(f); - return (NULL); - } - - char buf[12]; - int pos = 0; - // {{{ check for TTC - if (otf_read(otf, buf, pos, 12)) - { - const unsigned int version = __cfFontEmbedGetULong(buf); - if (version == _CF_FONTEMBED_OTF_TAG('t', 't', 'c', 'f')) - { - const unsigned int ttc_version = __cfFontEmbedGetULong(buf + 4); - if ((ttc_version != 0x00010000) && (ttc_version != 0x00020000)) - { - fprintf(stderr, "Unsupported TTC version\n"); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - otf->numTTC = __cfFontEmbedGetULong(buf + 8); - otf->useTTC = use_ttc; - pos = otf_get_ttc_start(otf, use_ttc); - if (pos == -1) - { - _cfFontEmbedOTFClose(otf); - return (NULL); - } - } - } - else - { - fprintf(stderr, "Not a ttf font\n"); - _cfFontEmbedOTFClose(otf); - return (NULL); - } - // }}} - - return (otf_do_load(otf, pos)); -} -// }}} - - -void -_cfFontEmbedOTFClose(_cf_fontembed_otf_file_t *otf) // {{{ -{ - DEBUG_assert(otf); - if (otf) - { - free(otf->gly); - free(otf->cmap); - free(otf->name); - free(otf->hmtx); - free(otf->glyphOffsets); - fclose(otf->f); - free(otf->tables); - free(otf); - } -} -// }}} - - -static int -otf_dirent_compare(const void *a, const void *b) // {{{ -{ - const unsigned int aa = ((const _cf_fontembed_otf_dir_ent_t *)a)->tag; - const unsigned int bb = ((const _cf_fontembed_otf_dir_ent_t *)b)->tag; - if (aa < bb) - return (-1); - else if (aa > bb) - return (1); - return (0); -} -// }}} - - -int -__cfFontEmbedOTFFindTable(_cf_fontembed_otf_file_t *otf, - unsigned int tag) // {{{ - table_index or -1 on error -{ -#if 0 - // binary search would require raw table - int pos = 0; - char buf[12]; - if (!otf_read(otf, buf, pos, 12)) - return (-1); - pos = 12; - const unsigned int numTables = __cfFontEmbedGetUShort(buf + 4); - char *tables = malloc(16 * numTables); - if (!tables) - return (-1); - if (!otf_read(otf, tables, pos, 16 * numTables)) - { - free(tables); - return (-1); - } - char target[] = {(tag >> 24), (tag >> 16), (tag >> 8), tag}; - // DEBUG_assert(__cfFontEmbedGetUShort(buf + 6) + - // __cfFontEmbedGetUShort(buf + 10) == 16 * numTables); - char *result = otf_bsearch(tables, target, 4, - __cfFontEmbedGetUShort(buf + 6), - __cfFontEmbedGetUShort(buf + 8), - __cfFontEmbedGetUShort(buf + 10), 0); - free(tables); - if (result) - return (result - tables) / 16; -#elif 1 - _cf_fontembed_otf_dir_ent_t key = {.tag = tag}, *res; - res = bsearch(&key, otf->tables, otf->numTables, sizeof(otf->tables[0]), - otf_dirent_compare); - if (res) - return (res-otf->tables); -#else - int iA; - for (iA = 0; iA < otf->numTables; iA ++) - { - if (otf->tables[iA].tag == tag) - return (iA); - } -#endif // 1 - return (-1); -} -// }}} - - -char * -_cfFontEmbedOTFGetTable(_cf_fontembed_otf_file_t *otf, - unsigned int tag, - int *ret_len) // {{{ -{ - DEBUG_assert(otf); - DEBUG_assert(ret_len); - - const int idx = __cfFontEmbedOTFFindTable(otf, tag); - if (idx == -1) - { - *ret_len =- 1; - return (NULL); - } - const _cf_fontembed_otf_dir_ent_t *table = otf->tables + idx; - - char *ret = otf_read(otf, NULL, table->offset, table->length); - if (!ret) - return (NULL); - if (otf->flags & _CF_FONTEMBED_OTF_F_DO_CHECKSUM) - { - unsigned int csum = __cfFontEmbedOTFCheckSum(ret, table->length); - if (tag==_CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd')) // special case - csum -= __cfFontEmbedGetULong(ret + 8); - if (csum != table->checkSum) - { - fprintf(stderr, "Wrong checksum for %c%c%c%c\n", - _CF_FONTEMBED_OTF_UNTAG(tag)); - free(ret); - return (NULL); - } - } - *ret_len = table->length; - return (ret); -} -// }}} - - -int -__cfFontEmbedOTFLoadGlyf(_cf_fontembed_otf_file_t *otf) // {{{ - 0 on success -{ - DEBUG_assert((otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF) == 0); // not for CFF - int iA, len; - // {{{ find glyf table - iA = - __cfFontEmbedOTFFindTable(otf, _CF_FONTEMBED_OTF_TAG('g', 'l', 'y', 'f')); - if (iA == -1) - { - fprintf(stderr, "Unsupported OTF font / glyf table \n"); - return (-1); - } - otf->glyfTable = otf->tables + iA; - // }}} - - // {{{ read loca table - char *loca = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('l', 'o', 'c', 'a'), - &len); - if ((!loca) || - (otf->indexToLocFormat >= 2) || - (((len + 3) & ~3) != ((((otf->numGlyphs + 1) * - (otf->indexToLocFormat + 1) * 2) +3 ) & ~3))) - { - fprintf(stderr, "Unsupported OTF font / loca table \n"); - return (-1); - } - if (otf->glyphOffsets) - { - free(otf->glyphOffsets); - DEBUG_assert(0); - } - otf->glyphOffsets = malloc((otf->numGlyphs + 1) * sizeof(unsigned int)); - if (!otf->glyphOffsets) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - return (-1); - } - if (otf->indexToLocFormat == 0) - { - for (iA = 0; iA <= otf->numGlyphs; iA ++) - otf->glyphOffsets[iA] = __cfFontEmbedGetUShort(loca + iA * 2) * 2; - } - else // indexToLocFormat == 1 - { - for (iA = 0; iA <= otf->numGlyphs; iA ++) - otf->glyphOffsets[iA] = __cfFontEmbedGetULong(loca + iA * 4); - } - free(loca); - if (otf->glyphOffsets[otf->numGlyphs] > otf->glyfTable->length) - { - fprintf(stderr, "Bad loca table \n"); - return (-1); - } - // }}} - - // {{{ allocate otf->gly slot - int maxGlyfLen = 0; // no single glyf takes more space - for (iA = 1; iA <= otf->numGlyphs; iA ++) - { - const int glyfLen = otf->glyphOffsets[iA] - otf->glyphOffsets[iA - 1]; - if (glyfLen < 0) - { - fprintf(stderr, "Bad loca table: glyph len %d\n", glyfLen); - return (-1); - } - if (maxGlyfLen < glyfLen) - maxGlyfLen = glyfLen; - } - if (otf->gly) - { - free(otf->gly); - DEBUG_assert(0); - } - otf->gly=malloc(maxGlyfLen * sizeof(char)); - if (!otf->gly) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - return (-1); - } - // }}} - - return (0); -} -// }}} - -int -__cfFontEmbedOTFLoadMore(_cf_fontembed_otf_file_t *otf) - // {{{ - 0 on success => hhea, hmtx, name, [glyf] -{ - int iA; - int len; - - if ((otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF) == 0) // not for CFF - { - if (__cfFontEmbedOTFLoadGlyf(otf) == -1) - return (-1); - } - - // {{{ read hhea table - char *hhea = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('h', 'h', 'e', 'a'), - &len); - if ((!hhea) || - (__cfFontEmbedGetULong(hhea) != 0x00010000) || // version - (len != 36) || - (__cfFontEmbedGetShort(hhea + 32) != 0)) // metric format - { - fprintf(stderr, "Unsupported OTF font / hhea table \n"); - return (-1); - } - otf->numberOfHMetrics = __cfFontEmbedGetUShort(hhea + 34); - free(hhea); - // }}} - - // {{{ read hmtx table - char *hmtx = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('h', 'm', 't', 'x'), - &len); - if ((!hmtx) || - (len != otf->numberOfHMetrics * 2 + otf->numGlyphs * 2)) - { - fprintf(stderr, "Unsupported OTF font / hmtx table\n"); - return (-1); - } - if (otf->hmtx) - { - free(otf->hmtx); - DEBUG_assert(0); - } - otf->hmtx = hmtx; - // }}} - - // {{{ read name table - char *name = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('n', 'a', 'm', 'e'), - &len); - if ((!name) || - (__cfFontEmbedGetUShort(name) != 0x0000) || // version - (len < __cfFontEmbedGetUShort(name + 2) * 12 + 6) || - (len <= __cfFontEmbedGetUShort(name + 4))) - { - fprintf(stderr, "Unsupported OTF font / name table\n"); - return (-1); - } - - // check bounds - int name_count = __cfFontEmbedGetUShort(name + 2); - const char *nstore = name + __cfFontEmbedGetUShort(name + 4); - for (iA = 0; iA < name_count; iA ++) - { - const char *nrec = name + 6 + 12 * iA; - if (nstore-name + __cfFontEmbedGetUShort(nrec + 10) + - __cfFontEmbedGetUShort(nrec + 8) > len) - { - fprintf(stderr, "Bad name table\n"); - free(name); - return (-1); - } - } - if (otf->name) - { - free(otf->name); - DEBUG_assert(0); - } - otf->name = name; - // }}} - - return (0); -} -// }}} - - -static int -otf_load_cmap(_cf_fontembed_otf_file_t *otf) // {{{ - 0 on success -{ - int iA; - int len; - - char *cmap = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('c', 'm', 'a', 'p'), - &len); - if ((!cmap) || - (__cfFontEmbedGetUShort(cmap) != 0x0000) || // version - (len < __cfFontEmbedGetUShort(cmap + 2) * 8 + 4)) - { - fprintf(stderr, "Unsupported OTF font / cmap table\n"); - DEBUG_assert(0); - return (-1); - } - // check bounds, find (3, 0) or (3, 1) [TODO?] - const int numTables = __cfFontEmbedGetUShort(cmap + 2); - for (iA = 0; iA < numTables; iA ++) - { - const char *nrec = cmap + 4 + 8 * iA; - const unsigned int offset = __cfFontEmbedGetULong(nrec + 4); - const char *ndata = cmap + offset; - if ((ndata < cmap + 4 + 8 * numTables) || - (offset >= len) || - (offset + __cfFontEmbedGetUShort(ndata + 2) > len)) - { - fprintf(stderr, "Bad cmap table\n"); - free(cmap); - DEBUG_assert(0); - return (-1); - } - if ((__cfFontEmbedGetUShort(nrec) == 3) && - (__cfFontEmbedGetUShort(nrec + 2) <= 1) && - (__cfFontEmbedGetUShort(ndata) == 4) && - (__cfFontEmbedGetUShort(ndata + 4) == 0)) - otf->unimap = ndata; - } - if (otf->cmap) - { - free(otf->cmap); - DEBUG_assert(0); - } - otf->cmap = cmap; - - return (0); -} -// }}} - - -int -_cfFontEmbedOTFGetWidth(_cf_fontembed_otf_file_t *otf, - unsigned short gid) // {{{ -1 on error -{ - DEBUG_assert(otf); - - if (gid >= otf->numGlyphs) - return (-1); - - // ensure hmtx is there - if (!otf->hmtx) - { - if (__cfFontEmbedOTFLoadMore(otf) != 0) - { - fprintf(stderr, "Unsupported OTF font / cmap table\n"); - return (-1); - } - } - - return (__cfFontEmbedGetWidthFast(otf, gid)); -#if 0 - if (gid >= otf->numberOfHMetrics) - { - return (__cfFontEmbedGetUShort(otf->hmtx + - (otf->numberOfHMetrics - 1) * 2)); - // TODO? lsb = __cfFontEmbedGetShort(otf->hmtx + - // otf->numberOfHMetrics * 2 + gid * 2); - // lsb: left_side_bearing (also in table) - } - return (__cfFontEmbedGetUShort(otf->hmtx + gid * 4)); - // TODO? lsb = __cfFontEmbedGetShort(otf->hmtx + gid * 4 + 2); -#endif -} -// }}} - - -static int -otf_name_compare(const void *a, - const void *b) // {{{ -{ - return (memcmp(a, b, 8)); -} -// }}} - - -const char * -_cfFontEmbedOTFGetName(_cf_fontembed_otf_file_t *otf, - int platformID, - int encodingID, - int languageID, - int nameID, - int *ret_len) // {{{ -{ - DEBUG_assert(otf); - DEBUG_assert(ret_len); - - // ensure name is there - if (!otf->name) - { - if (__cfFontEmbedOTFLoadMore(otf) != 0) - { - *ret_len = -1; - DEBUG_assert(0); - return (NULL); - } - } - - char key[8]; - __cfFontEmbedSetUShort(key, platformID); - __cfFontEmbedSetUShort(key + 2, encodingID); - __cfFontEmbedSetUShort(key + 4, languageID); - __cfFontEmbedSetUShort(key + 6, nameID); - - char *res = bsearch(key, otf->name + 6, - __cfFontEmbedGetUShort(otf->name + 2), 12, - otf_name_compare); - if (res) - { - *ret_len = __cfFontEmbedGetUShort(res + 8); - int npos = __cfFontEmbedGetUShort(res + 10); - const char *nstore = otf->name + __cfFontEmbedGetUShort(otf->name + 4); - return (nstore + npos); - } - *ret_len = 0; - return (NULL); -} -// }}} - - -int -_cfFontEmbedOTFGetGlyph(_cf_fontembed_otf_file_t *otf, - unsigned short gid) // {{{ result in >otf->gly, - // returns length, -1 on error -{ - DEBUG_assert(otf); - DEBUG_assert((otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF) == 0); // not for CFF - - if (gid >= otf->numGlyphs) - return (-1); - - // ensure >glyphOffsets and >gly is there - if ((!otf->gly) || (!otf->glyphOffsets)) - { - if (__cfFontEmbedOTFLoadMore(otf) != 0) - { - DEBUG_assert(0); - return (-1); - } - } - - const int len = otf->glyphOffsets[gid + 1] - otf->glyphOffsets[gid]; - if (len == 0) - return (0); - - DEBUG_assert(otf->glyfTable->length >= otf->glyphOffsets[gid + 1]); - if (!otf_read(otf, otf->gly, - otf->glyfTable->offset + otf->glyphOffsets[gid], len)) - return (-1); - - return (len); -} -// }}} - - -unsigned short -_cfFontEmbedOTFFromUnicode(_cf_fontembed_otf_file_t *otf, - int unicode) // {{{ 0 = missing -{ - DEBUG_assert(otf); - DEBUG_assert((unicode >= 0) && (unicode < 65536)); - //DEBUG_assert((otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF) == 0); - // not for CFF, other method! - - // ensure >cmap and >unimap is there - if (!otf->cmap) - { - if (otf_load_cmap(otf) != 0) - { - DEBUG_assert(0); - return (0); // TODO? - } - } - if (!otf->unimap) - { - fprintf(stderr, "Unicode (3, 1) cmap in format 4 not found\n"); - return (0); - } - -#if 0 - // linear search is cache friendly and should be quite fast -#else - const unsigned short segCountX2 = __cfFontEmbedGetUShort(otf->unimap + 6); - char target[] = {unicode >> 8, unicode}; - // __cfFontEmbedSetUShort(target, unicode); - char *result = otf_bsearch((char *)otf->unimap + 14, target, 2, - __cfFontEmbedGetUShort(otf->unimap + 8), - __cfFontEmbedGetUShort(otf->unimap + 10), - __cfFontEmbedGetUShort(otf->unimap + 12), 1); - if (result >= otf->unimap + 14 + segCountX2) // outside of endCode[segCount] - { - DEBUG_assert(0); // bad font, no 0xffff sentinel - return (0); - } - - result += 2 + segCountX2; // jump over padding into startCode[segCount] - const unsigned short startCode = __cfFontEmbedGetUShort(result); - if (startCode > unicode) - return (0); - result += 2 * segCountX2; - const unsigned short rangeOffset = __cfFontEmbedGetUShort(result); - if (rangeOffset) - { - return (__cfFontEmbedGetUShort(result + rangeOffset + - 2 * (unicode-startCode))); - // the so called "obscure indexing trick" into glyphIdArray[] - // NOTE: this is according to apple spec; microsoft says we must add - // delta (probably incorrect; fonts probably have delta == 0) - } - else - { - const short delta = __cfFontEmbedGetShort(result - segCountX2); - return (delta + unicode) & 0xffff; - } -#endif // 0 -} -// }}} - - -/** output stuff **/ -int -__cfFontEmbedOTFActionCopy(void *param, - int table_no, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - _cf_fontembed_otf_file_t *otf = param; - const _cf_fontembed_otf_dir_ent_t *table = otf->tables + table_no; - - if (!output) // get checksum and unpadded length - { - *(unsigned int *)context = table->checkSum; - return (table->length); - } - - // TODO? copy_block(otf->f, table->offset, (table->length + 3) & ~3, output, - // context); - // problem: PS currently depends on single-output. Also checksum not possible - char *data = otf_read(otf, NULL, table->offset, table->length); - if (!data) - return (-1); - int ret = (table->length + 3) & ~3; - (*output)(data, ret, context); - free(data); - return (ret); // padded length -} -// }}} - - -// TODO? >modified time-stamp? -// Note: don't use this directly. __cfFontEmbedOTFWriteSFNT will internally -// replace -// __cfFontEmbedOTFActionCopy for head with this - -int -__cfFontEmbedOTFActionCopyHead(void *param, - int csum, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - _cf_fontembed_otf_file_t *otf = param; - const int table_no = - __cfFontEmbedOTFFindTable(otf, _CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd')); - // we can't have csum AND table_no ... never mind! - DEBUG_assert(table_no != -1); - const _cf_fontembed_otf_dir_ent_t *table = otf->tables + table_no; - - if (!output) // get checksum and unpadded length - { - *(unsigned int *)context = table->checkSum; - return (table->length); - } - - char *data = otf_read(otf, NULL, table->offset, table->length); - if (!data) - return (-1); - __cfFontEmbedSetULong(data + 8, 0xb1b0afba - csum); - // head. fix global checksum - int ret = (table->length + 3) & ~3; - (*output)(data, ret, context); - free(data); - return (ret); // padded length -} -// }}} - - -int -__cfFontEmbedOTFActionReplace(void *param, - int length, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - char *data = param; - char pad[4] = {0, 0, 0, 0}; - - int ret = (length + 3) & ~3; - if (!output) // get checksum and unpadded length - { - if (ret != length) - { - unsigned int csum = __cfFontEmbedOTFCheckSum(data, ret - 4); - memcpy(pad, data + ret - 4, ret - length); - csum += __cfFontEmbedGetULong(pad); - *(unsigned int *)context = csum; - } - else - *(unsigned int *)context = __cfFontEmbedOTFCheckSum(data, length); - return (length); - } - - (*output)(data, length, context); - if (ret != length) - (*output)(pad, ret - length, context); - - return (ret); // padded length -} -// }}} - -// -// windows "works best" with the following ordering: -// head, hhea, maxp, OS/2, hmtx, LTSH, VDMX, hdmx, cmap, fpgm, prep, cvt, -// loca, glyf, kern, name, post, gasp, PCLT, DSIG -// or for CFF: -// head, hhea, maxp, OS/2, name, cmap, post, CFF, (other tables, as -// convenient) -// - -#define NUM_PRIO 20 -static const struct -{ - int prio; - unsigned int tag; -} otf_tagorder_win[] = -{ // {{{ - {19, _CF_FONTEMBED_OTF_TAG('D', 'S', 'I', 'G')}, - { 5, _CF_FONTEMBED_OTF_TAG('L', 'T', 'S', 'H')}, - { 3, _CF_FONTEMBED_OTF_TAG('O', 'S', '/', '2')}, - {18, _CF_FONTEMBED_OTF_TAG('P', 'C', 'L', 'T')}, - { 6, _CF_FONTEMBED_OTF_TAG('V', 'D', 'M', 'X')}, - { 8, _CF_FONTEMBED_OTF_TAG('c', 'm', 'a', 'p')}, - {11, _CF_FONTEMBED_OTF_TAG('c', 'v', 't', ' ')}, - { 9, _CF_FONTEMBED_OTF_TAG('f', 'p', 'g', 'm')}, - {17, _CF_FONTEMBED_OTF_TAG('g', 'a', 's', 'p')}, - {13, _CF_FONTEMBED_OTF_TAG('g', 'l', 'y', 'f')}, - { 7, _CF_FONTEMBED_OTF_TAG('h', 'd', 'm', 'x')}, - { 0, _CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd')}, - { 1, _CF_FONTEMBED_OTF_TAG('h', 'h', 'e', 'a')}, - { 4, _CF_FONTEMBED_OTF_TAG('h', 'm', 't', 'x')}, - {14, _CF_FONTEMBED_OTF_TAG('k', 'e', 'r', 'n')}, - {12, _CF_FONTEMBED_OTF_TAG('l', 'o', 'c', 'a')}, - { 2, _CF_FONTEMBED_OTF_TAG('m', 'a', 'x', 'p')}, - {15, _CF_FONTEMBED_OTF_TAG('n', 'a', 'm', 'e')}, - {16, _CF_FONTEMBED_OTF_TAG('p', 'o', 's', 't')}, - {10, _CF_FONTEMBED_OTF_TAG('p', 'r', 'e', 'p')} -}; -// }}} - - -int -__cfFontEmbedOTFWriteSFNT(struct __cf_fontembed_otf_write_s *otw, - unsigned int version, - int numTables, - _cf_fontembed_output_fn_t output, - void *context) // {{{ -{ - int iA; - int ret; - - int *order = malloc(sizeof(int) * numTables); // temporary - char *start = malloc(12 + 16 * numTables); - if ((!order) || (!start)) - { - fprintf(stderr, "Bad alloc: %s\n", strerror(errno)); - free(order); - free(start); - return (-1); - } - - if (1) // sort tables - { - int priolist[NUM_PRIO] = {0, }; - - // reverse intersection of both sorted arrays - int iA = numTables - 1, - iB = sizeof(otf_tagorder_win) / sizeof(otf_tagorder_win[0]) - 1; - int ret = numTables - 1; - while ((iA >= 0) && (iB >= 0)) - { - if (otw[iA].tag == otf_tagorder_win[iB].tag) - priolist[otf_tagorder_win[iB--].prio] = 1 + iA--; - else if (otw[iA].tag > otf_tagorder_win[iB].tag) - // no order known: put unchanged at end of result - order[ret--] = iA--; - else // < - iB --; - } - for (iA = NUM_PRIO - 1; iA >= 0; iA --) - { - // pick the matched tables up in sorted order (bucketsort principle) - if (priolist[iA]) - order[ret--] = priolist[iA] - 1; - } - } - else - { - for (iA = 0; iA < numTables; iA ++) - order[iA] = iA; - } - - // the header - __cfFontEmbedSetULong(start, version); - __cfFontEmbedSetUShort(start + 4, numTables); - int a, b, c; - otf_bsearch_params(numTables, 16, &a, &b, &c); - __cfFontEmbedSetUShort(start + 6, a); - __cfFontEmbedSetUShort(start + 8, b); - __cfFontEmbedSetUShort(start + 10, c); - - // first pass: calculate table directory / offsets and checksums - unsigned int globalSum = 0, csum; - int offset = 12 + 16 * numTables; - int headAt = -1; - for (iA = 0; iA < numTables; iA ++) - { - char *entry = start + 12 + 16 * order[iA]; - const int res = (*otw[order[iA]].action)(otw[order[iA]].param, - otw[order[iA]].length, NULL, - &csum); - DEBUG_assert(res >= 0); - if (otw[order[iA]].tag == _CF_FONTEMBED_OTF_TAG('h', 'e', 'a', 'd')) - headAt = order[iA]; - __cfFontEmbedSetULong(entry, otw[order[iA]].tag); - __cfFontEmbedSetULong(entry + 4, csum); - __cfFontEmbedSetULong(entry + 8, offset); - __cfFontEmbedSetULong(entry + 12, res); - offset += (res + 3) & ~3; // padding - globalSum += csum; - } - - // second pass: write actual data - // write header + directory - ret= 12 + 16 * numTables; - (*output)(start, ret, context); - globalSum += __cfFontEmbedOTFCheckSum(start, ret); - - // change head - if ((headAt != -1) && (otw[headAt].action == - __cfFontEmbedOTFActionCopy)) // more needed? - { - otw[headAt].action = __cfFontEmbedOTFActionCopyHead; - otw[headAt].length = globalSum; - } - - // write tables - for (iA = 0; iA < numTables; iA ++) - { - const int res = (*otw[order[iA]].action)(otw[order[iA]].param, - otw[order[iA]].length, - output, context); - if (res < 0) - { - free(order); - free(start); - return (-1); - } - DEBUG_assert(((res + 3) & ~3) == res); - // correctly padded? (i.e. next line is just ret += res;) - ret += (res + 3) & ~3; - } - DEBUG_assert(offset == ret); - free(order); - free(start); - - return (ret); -} -// }}} diff --git a/cupsfilters/fontembed/test-analyze.c b/cupsfilters/fontembed/test-analyze.c deleted file mode 100644 index a3d138f4c..000000000 --- a/cupsfilters/fontembed/test-analyze.c +++ /dev/null @@ -1,287 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "embed-sfnt-private.h" -#include "sfnt-private.h" -#include "config.h" -#include -#include - - -enum { - WEIGHT_THIN = 100, - WEIGHT_EXTRALIGHT = 200, - WEIGHT_ULTRALIGHT = 200, - WEIGHT_LIGHT = 300, - WEIGHT_NORMAL = 400, - WEIGHT_REGULAR = 400, - WEIGHT_MEDIUM = 500, - WEIGHT_SEMIBOLD = 600, // DEMI - WEIGHT_BOLD = 700, - WEIGHT_EXTRABOLD = 800, - WEIGHT_ULTRABOLD = 800, - WEIGHT_BLACK = 900, - WEIGHT_HEAVY=900 -}; - - -void -show_post(_cf_fontembed_otf_file_t *otf) // {{{ -{ - DEBUG_assert(otf); - int len = 0; - char *buf; - - buf = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('p', 'o', 's', 't'), - &len); - if (!buf) - { - DEBUG_assert(len == -1); - printf("No post table\n"); - return; - } - // TODO: check len - printf("POST: (%d bytes)\n" - " version: %08x\n" - " italicAngle: %d.%d\n" - " underlinePosition: %d\n" - " underlineThickness: %d\n" - " isFixedPitch: %d\n" - " vmType42: %d %d\n" - " vmType1: %d %d\n", - len, - __cfFontEmbedGetULong(buf), - __cfFontEmbedGetLong(buf + 4) >> 16, - __cfFontEmbedGetULong(buf + 4) & 0xffff, - __cfFontEmbedGetShort(buf + 8), - __cfFontEmbedGetShort(buf + 10), - __cfFontEmbedGetULong(buf + 12), - __cfFontEmbedGetULong(buf + 16), - __cfFontEmbedGetULong(buf + 20), - __cfFontEmbedGetULong(buf + 24), - __cfFontEmbedGetULong(buf + 38)); - free(buf); -} -// }}} - - -void -show_name(_cf_fontembed_otf_file_t *otf) // {{{ -{ - DEBUG_assert(otf); - int iA, len = 0; - char *buf; - - buf = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('n', 'a', 'm', 'e'), - &len); - if (!buf) - { - DEBUG_assert(len == -1); - printf("No name table\n"); - return; - } - printf("NAME:\n"); - int name_count = __cfFontEmbedGetUShort(buf + 2); - const char *nstore = buf + __cfFontEmbedGetUShort(buf + 4); - for (iA = 0; iA < name_count; iA ++) - { - const char *nrec = buf + 6 + 12 * iA; - printf(" { platformID/encodingID/languageID/nameID: %d/%d/%d/%d\n" - " length: %d, offset: %d, data :", - __cfFontEmbedGetUShort(nrec), - __cfFontEmbedGetUShort(nrec + 2), - __cfFontEmbedGetUShort(nrec + 4), - __cfFontEmbedGetUShort(nrec + 6), - __cfFontEmbedGetUShort(nrec + 8), - __cfFontEmbedGetUShort(nrec + 10)); - if ((__cfFontEmbedGetUShort(nrec) == 0) || - (__cfFontEmbedGetUShort(nrec) == 3)) // WCHAR - { - int nlen = __cfFontEmbedGetUShort(nrec + 8); - int npos = __cfFontEmbedGetUShort(nrec + 10); - for (; nlen > 0; nlen -= 2, npos += 2) - { - if (nstore[npos] != 0x00) - printf("?"); - else - printf("%c", nstore[npos + 1]); - } - printf(" }\n"); - } - else - printf("%.*s }\n", - __cfFontEmbedGetUShort(nrec + 8), - nstore + __cfFontEmbedGetUShort(nrec + 10)); - } - free(buf); -} -// }}} - - -void -show_cmap(_cf_fontembed_otf_file_t *otf) // {{{ -{ - DEBUG_assert(otf); - int iA, len = 0; - - char *cmap = - _cfFontEmbedOTFGetTable(otf, _CF_FONTEMBED_OTF_TAG('c', 'm', 'a', 'p'), - &len); - if (!cmap) - { - DEBUG_assert(len == -1); - printf("No cmap table\n"); - return; - } - printf("cmap:\n"); - DEBUG_assert(__cfFontEmbedGetUShort(cmap) == 0x0000); // version - const int numTables = __cfFontEmbedGetUShort(cmap + 2); - printf(" numTables: %d\n", numTables); - for (iA = 0; iA < numTables; iA ++) - { - const char *nrec = cmap + 4 + 8 * iA; - const char *ndata = cmap + __cfFontEmbedGetULong(nrec + 4); - DEBUG_assert(ndata >= cmap + 4 + 8 * numTables); - printf(" platformID/encodingID: %d/%d\n" - " offset: %d data (format: %d, length: %d, language: %d);\n", - __cfFontEmbedGetUShort(nrec), __cfFontEmbedGetUShort(nrec + 2), - __cfFontEmbedGetULong(nrec + 4), - __cfFontEmbedGetUShort(ndata), __cfFontEmbedGetUShort(ndata + 2), - __cfFontEmbedGetUShort(ndata + 4)); - } - free(cmap); -} -// }}} - - -void -show_glyf(_cf_fontembed_otf_file_t *otf, - int full) // {{{ -{ - DEBUG_assert(otf); - - // ensure >glyphOffsets and >gly is there - if ((!otf->gly) || (!otf->glyphOffsets)) - { - if (__cfFontEmbedOTFLoadGlyf(otf) != 0) - { - DEBUG_assert(0); - return; - } - } - - int iA; - int compGlyf = 0, zeroGlyf = 0; - - // {{{ glyf - DEBUG_assert(otf->gly); - for (iA = 0; iA < otf->numGlyphs; iA ++) - { - int len = _cfFontEmbedOTFGetGlyph(otf, iA); - if (len == 0) - zeroGlyf ++; - else if (__cfFontEmbedGetShort(otf->gly) == -1) - compGlyf ++; - if (full) - printf("%d(%d) ", __cfFontEmbedGetShort(otf->gly), len); - } - if (full) - printf("\n"); - printf("numGlyf(nonnull): %d(%d), composites: %d\n", otf->numGlyphs, - otf->numGlyphs - zeroGlyf, compGlyf); - // }}} -} -// }}} - - -void -show_hmtx(_cf_fontembed_otf_file_t *otf) // {{{ -{ - DEBUG_assert(otf); - int iA; - - _cfFontEmbedOTFGetWidth(otf, 0); // load table. - if (!otf->hmtx) - { - printf("NOTE: no hmtx table!\n"); - return; - } - printf("hmtx (%d):\n", otf->numberOfHMetrics); - for (iA = 0; iA < otf->numberOfHMetrics; iA ++) - { - printf("(%d,%d) ", - __cfFontEmbedGetUShort(otf->hmtx + iA * 4), - __cfFontEmbedGetShort(otf->hmtx + iA * 4 + 2)); - } - printf(" (last is repeated for the remaining %d glyphs)\n", - otf->numGlyphs - otf->numberOfHMetrics); -} -// }}} - -int -main(int argc, - char **argv) -{ - const char *fn = TESTFONT; - _cf_fontembed_otf_file_t *otf = NULL; - if (argc == 2) - fn = argv[1]; - otf = _cfFontEmbedOTFLoad(fn); - if (!otf) - { - printf("Font %s was not loaded, exiting.\n", TESTFONT); - return (1); - } - - DEBUG_assert(otf); - if (otf->numTTC) - printf("TTC has %d fonts, using %d\n", otf->numTTC, otf->useTTC); - if (otf->version == 0x00010000) - printf("Got TTF 1.0\n"); - else if (otf->version == _CF_FONTEMBED_OTF_TAG('O','T','T','O')) - printf("Got OTF(CFF)\n"); - else if (otf->version == _CF_FONTEMBED_OTF_TAG('t','r','u','e')) - printf("Got TTF (true)\n"); - else if (otf->version == _CF_FONTEMBED_OTF_TAG('t','y','p','1')) - printf("Got SFNT(Type1)\n"); - - printf("Has %d tables\n", otf->numTables); - - int iA; - for (iA=0; iA < otf->numTables; iA ++) - { - printf("%c%c%c%c %d @%d\n", _CF_FONTEMBED_OTF_UNTAG(otf->tables[iA].tag), - otf->tables[iA].length, otf->tables[iA].offset); - } - printf("unitsPerEm: %d, indexToLocFormat: %d\n", - otf->unitsPerEm, otf->indexToLocFormat); - printf("num glyphs: %d\n", otf->numGlyphs); - _cfFontEmbedOTFGetWidth(otf, 0); // load table. - printf("numberOfHMetrics: %d\n", otf->numberOfHMetrics); - - printf("Embedding rights: %x\n", __cfFontEmbedEmbOTFGetRights(otf)); - - show_post(otf); - - show_name(otf); - - show_cmap(otf); - // printf("%d %d\n", _cfFontEmbedOTFFromUnicode(otf, 'A'), 0); - - if (!(otf->flags & _CF_FONTEMBED_OTF_F_FMT_CFF)) - show_glyf(otf, 1); - - show_hmtx(otf); - - _cfFontEmbedOTFClose(otf); - - return (0); -} diff --git a/cupsfilters/fontembed/test-pdf.c b/cupsfilters/fontembed/test-pdf.c deleted file mode 100644 index cf047fb3d..000000000 --- a/cupsfilters/fontembed/test-pdf.c +++ /dev/null @@ -1,245 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "config.h" -#include -#include - - -static void -example_outfn(const char *buf, - int len, - void *context) // {{{ -{ - FILE *f = (FILE *)context; - if (fwrite(buf, 1, len, f) != len) - { - fprintf(stderr, "Short write: %m\n"); - DEBUG_assert(0); - return; - } -} -// }}} - - -#define OBJ \ - xref[xrefpos++] = ftell(f); \ - fprintf(f, "%d 0 obj\n", xrefpos); - -#define ENDOBJ \ - fprintf(f, "endobj\n"); - -#define STREAMDICT \ - OBJ; \ - fprintf(f, "<<\n" \ - " /Length %d 0 R\n", xrefpos + 1); - -#define STREAMDATA \ - fprintf(f, ">>\n" \ - "stream\n"); \ - stream_len = -ftell(f); - -#define STREAM \ - STREAMDICT \ - STREAMDATA - -#define ENDSTREAM \ - stream_len += ftell(f); \ - fprintf(f, "endstream\n" \ - "endobj\n"); \ - OBJ; \ - fprintf(f, "%d\n", stream_len); \ - ENDOBJ; - - -static inline void -write_string(FILE *f, - _cf_fontembed_emb_params_t *emb, - const char *str) // {{{ -{ - DEBUG_assert(f); - DEBUG_assert(emb); - int iA; - - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) - { - putc('<', f); - for (iA = 0; str[iA]; iA ++) - { - const unsigned short gid = - _cfFontEmbedEmbGet(emb, (unsigned char)str[iA]); - fprintf(f, "%04x", gid); - } - putc('>', f); - } - else - { - putc('(', f); - for (iA = 0; str[iA]; iA ++) - _cfFontEmbedEmbGet(emb, (unsigned char)str[iA]); - fprintf(f, "%s", str); // TODO - putc(')', f); - } -} -// }}} - - -int -main(int argc, - char **argv) -{ - const char *fn = TESTFONT; - _cf_fontembed_otf_file_t *otf = NULL; - if (argc == 2) - fn = argv[1]; - otf = _cfFontEmbedOTFLoad(fn); - if (!otf) - { - printf("Font %s was not loaded, exiting.\n", TESTFONT); - return (1); - } - DEBUG_assert(otf); - _cf_fontembed_fontfile_t *ff = _cfFontEmbedFontFileOpenSFNT(otf); - _cf_fontembed_emb_params_t *emb = - _cfFontEmbedEmbNew(ff, - _CF_FONTEMBED_EMB_DEST_PDF16, - _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE| - _CF_FONTEMBED_EMB_C_TAKE_FONTFILE); - - FILE *f = fopen("test.pdf", "w"); - DEBUG_assert(f); - int xref[100], xrefpos = 3; - int stream_len; - - fprintf(f, "%%PDF-1.3\n"); - // content - STREAM; - fprintf(f, "BT\n" // content - " 100 100 Td\n" - " /F1 10 Tf\n"); - write_string(f, emb, "Hallo"); - fprintf(f, " Tj\n" - "ET\n"); - ENDSTREAM; - - _cfFontEmbedEmbGet(emb, 'a'); - - // {{{ do font - _cf_fontembed_emb_pdf_font_descr_t *fdes = _cfFontEmbedEmbPDFFontDescr(emb); - DEBUG_assert(fdes); - _cf_fontembed_emb_pdf_font_widths_t *fwid = _cfFontEmbedEmbPDFFontWidths(emb); - DEBUG_assert(fwid); - - STREAMDICT; - int ff_ref = xrefpos; - if (_cfFontEmbedEmbPDFGetFontFileSubType(emb)) - { - fprintf(f," /Subtype /%s\n", - _cfFontEmbedEmbPDFGetFontFileSubType(emb)); - } - if (emb->outtype == _CF_FONTEMBED_EMB_FMT_T1) - fprintf(f, " /Length1 ?\n" - " /Length2 ?\n" - " /Length3 ?\n"); - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_TTF) - fprintf(f, " /Length1 %d 0 R\n", xrefpos + 2); - STREAMDATA; - const int outlen = _cfFontEmbedEmbEmbed(emb, example_outfn, f); - ENDSTREAM; - if (emb->outtype == _CF_FONTEMBED_EMB_FMT_TTF) - { - OBJ; - fprintf(f, "%d\n", outlen); - ENDOBJ; - } - - OBJ; - const int fd_ref = xrefpos; - char *res = _cfFontEmbedEmbPDFSimpleFontDescr(emb, fdes, ff_ref); - DEBUG_assert(res); - fputs(res, f); - free(res); - ENDOBJ; - - OBJ; - int f_ref = xrefpos; - res = _cfFontEmbedEmbPDFSimpleFont(emb, fdes, fwid, fd_ref); - DEBUG_assert(res); - fputs(res, f); - free(res); - ENDOBJ; - - if (emb->plan&_CF_FONTEMBED_EMB_A_MULTIBYTE) - { - OBJ; - res = _cfFontEmbedEmbPDFSimpleCIDFont(emb, fdes->fontname, f_ref); - f_ref = xrefpos; - DEBUG_assert(res); - fputs(res, f); - free(res); - ENDOBJ; - } - - free(fdes); - free(fwid); - // }}} - - int iA; - - xref[2] = ftell(f); - fprintf(f, "3 0 obj\n" - "<>\n" - " >>\n" - ">>\n" - "endobj\n", - f_ref); - xref[1] = ftell(f); - fprintf(f, "2 0 obj\n" - "<>\n" - "endobj\n"); - xref[0] = ftell(f); - fprintf(f, "1 0 obj\n" - "<>\n" - "endobj\n"); - // {{{ pdf trailer - int xref_start = ftell(f); - fprintf(f, "xref\n" - "0 %d\n" - "%010d 65535 f \n", - xrefpos + 1,0); - for (iA = 0; iA < xrefpos; iA ++) - fprintf(f, "%010d 00000 n \n", xref[iA]); - fprintf(f, "trailer\n" - "<<\n" - " /Size %d\n" - " /Root 1 0 R\n" - ">>\n" - "startxref\n" - "%d\n" - "%%%%EOF\n", - xrefpos + 1, xref_start); - // }}} - fclose(f); - - _cfFontEmbedEmbClose(emb); - - return (0); -} diff --git a/cupsfilters/fontembed/test-ps.c b/cupsfilters/fontembed/test-ps.c deleted file mode 100644 index 2900019e4..000000000 --- a/cupsfilters/fontembed/test-ps.c +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright © 2008,2012 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "config.h" -#include -#include - -const char *__cfFontEmbedEmbOTFGetFontName(_cf_fontembed_otf_file_t *otf); - // TODO - - -static void -example_outfn(const char *buf, - int len, - void *context) // {{{ -{ - FILE *f = (FILE *)context; - if (fwrite(buf, 1, len, f) != len) - { - fprintf(stderr, "Short write: %m\n"); - DEBUG_assert(0); - return; - } -} -// }}} - - -static inline void -write_string(FILE *f, - _cf_fontembed_emb_params_t *emb, - const char *str) // {{{ -{ - DEBUG_assert(f); - DEBUG_assert(emb); - int iA; - - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) - { - putc('<', f); - for (iA=0; str[iA] ;iA ++) - { - const unsigned short gid = - _cfFontEmbedEmbGet(emb, (unsigned char)str[iA]); - fprintf(f, "%04x", gid); - } - putc('>', f); - } - else - { - putc('(', f); - for (iA = 0; str[iA]; iA ++) - _cfFontEmbedEmbGet(emb, (unsigned char)str[iA]); - fprintf(f, "%s", str); // TODO - putc(')', f); - } -} -// }}} - - -int -main(int argc, - char **argv) -{ - const char *fn = TESTFONT; - _cf_fontembed_otf_file_t *otf = NULL; - if (argc == 2) - fn = argv[1]; - otf = _cfFontEmbedOTFLoad(fn); - if (!otf) - { - printf("Font %s was not loaded, exiting.\n", TESTFONT); - return (1); - } - DEBUG_assert(otf); - _cf_fontembed_fontfile_t *ff = _cfFontEmbedFontFileOpenSFNT(otf); - _cf_fontembed_emb_params_t *emb = - _cfFontEmbedEmbNew(ff, - _CF_FONTEMBED_EMB_DEST_PS, - // _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE| // not yet... - _CF_FONTEMBED_EMB_C_TAKE_FONTFILE); - - FILE *f = fopen("test.ps", "w"); - DEBUG_assert(f); - - fprintf(f, "%%!PS-Adobe-2.0\n"); - - char *str = "Hallo"; - - _cfFontEmbedEmbGet(emb, 'a'); - - int iA; - for (iA = 0; str[iA]; iA ++) - _cfFontEmbedEmbGet(emb, (unsigned char)str[iA]); - - _cfFontEmbedEmbEmbed(emb, example_outfn, f); - - // content - fprintf(f, " 100 100 moveto\n" // content - " /%s findfont 10 scalefont setfont\n", - __cfFontEmbedEmbOTFGetFontName(emb->font->sfnt)); - write_string(f, emb, "Hallo"); - // Note that write_string sets subset bits, but it's too late - fprintf(f, " show\n" - "showpage\n"); - - fprintf(f, "%%%%EOF\n"); - fclose(f); - - _cfFontEmbedEmbClose(emb); - - return (0); -} diff --git a/cupsfilters/ghostscript.c b/cupsfilters/ghostscript.c deleted file mode 100644 index 5bc725129..000000000 --- a/cupsfilters/ghostscript.c +++ /dev/null @@ -1,1686 +0,0 @@ -// -// Ghostscript filter function for libcupsfilters. -// -// Used for PostScript -> PDF, PDF -> Raster, PDF -> PCL-XL -// -// Copyright (c) 2008-2020, Till Kamppeter -// Copyright (c) 2011, Tim Waugh -// Copyright (c) 2011-2013, Richard Hughes -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "filter.h" -#include "pdf.h" - -#define PDF_MAX_CHECK_COMMENT_LINES 20 - -typedef enum gs_doc_e -{ - GS_DOC_TYPE_PDF, - GS_DOC_TYPE_PS, - GS_DOC_TYPE_EMPTY, - GS_DOC_TYPE_UNKNOWN -} gs_doc_t; - -#ifdef CUPS_RASTER_SYNCv1 -typedef cups_page_header2_t gs_page_header; -#else -typedef cups_page_header_t gs_page_header; -#endif // CUPS_RASTER_SYNCv1 - -static gs_doc_t -parse_doc_type(FILE *fp) -{ - char buf[5]; - int is_empty = 1; - gs_doc_t type = GS_DOC_TYPE_UNKNOWN; - - // get the first few bytes of the file - rewind(fp); - // skip until PDF/PS start header - while (fgets(buf, sizeof(buf), fp) != 0) - { - if (is_empty && buf[0] != '\n') - is_empty = 0; - if (strncmp(buf ,"%PDF", 4) == 0) - type = GS_DOC_TYPE_PDF; - if (strncmp(buf, "%!", 2) == 0) - type = GS_DOC_TYPE_PS; - if (type != GS_DOC_TYPE_UNKNOWN) - break; - } - if (is_empty) - type = GS_DOC_TYPE_EMPTY; - rewind(fp); - return (type); -} - -static void -parse_pdf_header_options(FILE *fp, - gs_page_header *h) -{ - char buf[4096]; - int i; - - rewind(fp); - // skip until PDF start header - while (fgets(buf, sizeof(buf), fp) != 0) - if (strncmp(buf, "%PDF", 4) == 0) - break; - for (i = 0; i < PDF_MAX_CHECK_COMMENT_LINES; i++) - { - if (fgets(buf, sizeof(buf), fp) == 0) - break; - if (strncmp(buf, "%%PDFTOPDFNumCopies", 19) == 0) - { - char *p; - - p = strchr(buf + 19, ':'); - h->NumCopies = atoi(p + 1); - } - else if (strncmp(buf, "%%PDFTOPDFCollate", 17) == 0) - { - char *p; - - p = strchr(buf + 17, ':'); - while (*p == ' ' || *p == '\t') - p ++; - if (strncasecmp(p, "true", 4) == 0) - h->Collate = CUPS_TRUE; - else - h->Collate = CUPS_FALSE; - } - } -} - -static void -header_to_gs_args(gs_page_header *h, - cups_array_t *gs_args, - cf_filter_out_format_t outformat, - int pxlcolor) -{ - int i; - char tmpstr[1024]; - - // Simple boolean, enumerated choice, numerical, and string parameters - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - { - if (outformat != CF_FILTER_OUT_FORMAT_APPLE_RASTER && - (h->MediaClass[0] |= '\0')) - { - snprintf(tmpstr, sizeof(tmpstr), "-sMediaClass=%s", h->MediaClass); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->MediaColor[0] |= '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-sMediaColor=%s", h->MediaColor); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->MediaType[0] |= '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-sMediaType=%s", h->MediaType); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->OutputType[0] |= '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-sOutputType=%s", h->OutputType); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->AdvanceDistance) - { - snprintf(tmpstr, sizeof(tmpstr), "-dAdvanceDistance=%d", - (unsigned)(h->AdvanceDistance)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->AdvanceMedia) - { - snprintf(tmpstr, sizeof(tmpstr), "-dAdvanceMedia=%d", - (unsigned)(h->AdvanceMedia)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->Collate) - cupsArrayAdd(gs_args, strdup("-dCollate")); - if (h->CutMedia) - { - snprintf(tmpstr, sizeof(tmpstr), "-dCutMedia=%d", - (unsigned)(h->CutMedia)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - } - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PXL) - { - // PDF output is only for turning PostScript input data into PDF - // not for sending PDF to a PDF printer (this is done by pdftopdf) - // therefore we do not apply duplex/tumble here. - if (h->Duplex) - cupsArrayAdd(gs_args, strdup("-dDuplex")); - } - if (outformat != CF_FILTER_OUT_FORMAT_PCLM) - { - // In PCLM we have our own method to generate the needed - // resolution, to respect the printer's supported resolutions for - // PCLm, so this is only for non-PCLm output formats - snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d", - h->HWResolution[0], h->HWResolution[1]); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - { - if (h->InsertSheet) - cupsArrayAdd(gs_args, strdup("-dInsertSheet")); - if (h->Jog) - { - snprintf(tmpstr, sizeof(tmpstr), "-dJog=%d", - (unsigned)(h->Jog)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->LeadingEdge) - { - snprintf(tmpstr, sizeof(tmpstr), "-dLeadingEdge=%d", - (unsigned)(h->LeadingEdge)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->ManualFeed) - cupsArrayAdd(gs_args, strdup("-dManualFeed")); - } - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PXL) - { - if (h->MediaPosition) - { - int mediapos; - if (outformat == CF_FILTER_OUT_FORMAT_PXL) - { - // Convert PWG MediaPosition values to PXL-ones - if (h->MediaPosition == 1) // Main - mediapos = 4; - else if (h->MediaPosition == 2) // Alternate - mediapos = 5; - else if (h->MediaPosition == 3) // Large Capacity - mediapos = 7; - else if (h->MediaPosition == 4) // Manual - mediapos = 2; - else if (h->MediaPosition == 5) // Envelope - mediapos = 6; - else if (h->MediaPosition == 11) // Top - mediapos = 4; - else if (h->MediaPosition == 12) // Middle - mediapos = 5; - else if (h->MediaPosition == 13) // Bottom - mediapos = 7; - else if (h->MediaPosition == 19) // Bypass - mediapos = 3; - else if (h->MediaPosition == 20) // Tray 1 - mediapos = 3; - else if (h->MediaPosition == 21) // Tray 2 - mediapos = 4; - else if (h->MediaPosition == 22) // Tray 3 - mediapos = 5; - else if (h->MediaPosition == 23) // Tray 4 - mediapos = 7; - else - mediapos = 0; - } - else - mediapos = h->MediaPosition; - snprintf(tmpstr, sizeof(tmpstr), "-dMediaPosition=%d", - (unsigned)(mediapos)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - } - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - { - if (h->MediaWeight) - { - snprintf(tmpstr, sizeof(tmpstr), "-dMediaWeight=%d", - (unsigned)(h->MediaWeight)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->MirrorPrint) - cupsArrayAdd(gs_args, strdup("-dMirrorPrint")); - if (h->NegativePrint) - cupsArrayAdd(gs_args, strdup("-dNegativePrint")); - if (h->NumCopies != 1) - { - snprintf(tmpstr, sizeof(tmpstr), "-dNumCopies=%d", - (unsigned)(h->NumCopies)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->Orientation) - { - snprintf(tmpstr, sizeof(tmpstr), "-dOrientation=%d", - (unsigned)(h->Orientation)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->OutputFaceUp) - cupsArrayAdd(gs_args, strdup("-dOutputFaceUp")); - } - snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEWIDTHPOINTS=%d", h->PageSize[0]); - cupsArrayAdd(gs_args, strdup(tmpstr)); - snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEHEIGHTPOINTS=%d", h->PageSize[1]); - cupsArrayAdd(gs_args, strdup(tmpstr)); - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - { - if (h->Separations) - cupsArrayAdd(gs_args, strdup("-dSeparations")); - if (h->TraySwitch) - cupsArrayAdd(gs_args, strdup("-dTraySwitch")); - } - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PXL) - { - // PDF output is only for turning PostScript input data into PDF - // not for sending PDF to a PDF printer (this is done by pdftopdf) - // therefore we do not apply duplex/tumble here. - if (h->Tumble) - cupsArrayAdd(gs_args, strdup("-dTumble")); - } - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - { - if (h->cupsMediaType) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsMediaType=%d", - (unsigned)(h->cupsMediaType)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - snprintf(tmpstr, sizeof(tmpstr), "-dcupsBitsPerColor=%d", - h->cupsBitsPerColor); - cupsArrayAdd(gs_args, strdup(tmpstr)); - snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorOrder=%d", h->cupsColorOrder); - cupsArrayAdd(gs_args, strdup(tmpstr)); - snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorSpace=%d", h->cupsColorSpace); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - - if (outformat == CF_FILTER_OUT_FORMAT_PXL) - { - if (h->cupsColorSpace == CUPS_CSPACE_W || - h->cupsColorSpace == CUPS_CSPACE_K || - h->cupsColorSpace == CUPS_CSPACE_WHITE || - h->cupsColorSpace == CUPS_CSPACE_GOLD || - h->cupsColorSpace == CUPS_CSPACE_SILVER || - h->cupsColorSpace == CUPS_CSPACE_SW || - h->cupsColorSpace == CUPS_CSPACE_ICC1 || - h->cupsColorSpace == CUPS_CSPACE_DEVICE1) - // Monochrome color spaces -> use "pxlmono" device - pxlcolor = 0; - if (pxlcolor == 1) - cupsArrayAdd(gs_args, strdup("-sDEVICE=pxlcolor")); - else - cupsArrayAdd(gs_args, strdup("-sDEVICE=pxlmono")); - } - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - { - if (h->cupsCompression) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsCompression=%d", - (unsigned)(h->cupsCompression)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->cupsRowCount) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowCount=%d", - (unsigned)(h->cupsRowCount)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->cupsRowFeed) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowFeed=%d", - (unsigned)(h->cupsRowFeed)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->cupsRowStep) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowStep=%d", - (unsigned)(h->cupsRowStep)); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - } -#ifdef CUPS_RASTER_SYNCv1 - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - { - if (h->cupsBorderlessScalingFactor != 1.0f) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsBorderlessScalingFactor=%.4f", - h->cupsBorderlessScalingFactor); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - for (i = 0; i <= 15; i ++) - if (h->cupsInteger[i]) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsInteger%d=%d", - i, (unsigned)(h->cupsInteger[i])); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - for (i = 0; i <= 15; i ++) - if (h->cupsReal[i]) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsReal%d=%.4f", - i, h->cupsReal[i]); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - for (i = 0; i <= 15; i ++) - if (h->cupsString[i][0] != '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-scupsString%d=%s", - i, h->cupsString[i]); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->cupsMarkerType[0] != '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-scupsMarkerType=%s", - h->cupsMarkerType); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->cupsRenderingIntent[0] != '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-scupsRenderingIntent=%s", - h->cupsRenderingIntent); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - if (h->cupsPageSizeName[0] != '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-scupsPageSizeName=%s", - h->cupsPageSizeName); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - } -#endif // CUPS_RASTER_SYNCv1 -} - -static int -gs_spawn (const char *filename, - cups_array_t *gs_args, - char **envp, - FILE *fp, - int outputfd, - cf_logfunc_t log, - void *ld, - cf_filter_iscanceledfunc_t iscanceled, - void *icd) -{ - char *argument; - char buf[BUFSIZ]; - char **gsargv; - const char* apos; - int infds[2], errfds[2]; - int i; - int n; - int numargs; - int pid, gspid, errpid; - cups_file_t *logfp; - cf_loglevel_t log_level; - char *msg; - int status = 65536; - int wstatus; - - // Put Ghostscript command line argument into an array for the "exec()" - // call - numargs = cupsArrayCount(gs_args); - gsargv = calloc(numargs + 1, sizeof(char *)); - for (argument = (char *)cupsArrayFirst(gs_args), i = 0; argument; - argument = (char *)cupsArrayNext(gs_args), i++) - gsargv[i] = argument; - gsargv[i] = NULL; - - if (log) - { - // Debug output: Full Ghostscript command line and environment variables - snprintf(buf, sizeof(buf), - "cfFilterGhostscript: Ghostscript command line:"); - for (i = 0; gsargv[i]; i ++) - { - if ((strchr(gsargv[i],' ')) || (strchr(gsargv[i], '\t'))) - apos = "'"; - else - apos = ""; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " %s%s%s", apos, gsargv[i], apos); - } - log(ld, CF_LOGLEVEL_DEBUG, "%s", buf); - - for (i = 0; envp[i]; i ++) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: envp[%d]=\"%s\"", i, envp[i]); - } - - // Create a pipe for feeding the job into Ghostscript - if (pipe(infds)) - { - infds[0] = -1; - infds[1] = -1; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to establish stdin pipe for Ghostscript call"); - goto out; - } - - // Create a pipe for stderr output of Ghostscript - if (pipe(errfds)) - { - errfds[0] = -1; - errfds[1] = -1; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to establish stderr pipe for Ghostscript call"); - goto out; - } - - // Set the "close on exec" flag on each end of the pipes... - if (fcntl(infds[0], F_SETFD, fcntl(infds[0], F_GETFD) | FD_CLOEXEC)) - { - close(infds[0]); - close(infds[1]); - infds[0] = -1; - infds[1] = -1; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to set \"close on exec\" flag on read end of the stdin pipe for Ghostscript call"); - goto out; - } - if (fcntl(infds[1], F_SETFD, fcntl(infds[1], F_GETFD) | FD_CLOEXEC)) - { - close(infds[0]); - close(infds[1]); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to set \"close on exec\" flag on write end of the stdin pipe for Ghostscript call"); - goto out; - } - if (fcntl(errfds[0], F_SETFD, fcntl(errfds[0], F_GETFD) | FD_CLOEXEC)) - { - close(errfds[0]); - close(errfds[1]); - errfds[0] = -1; - errfds[1] = -1; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to set \"close on exec\" flag on read end of the stderr pipe for Ghostscript call"); - goto out; - } - if (fcntl(errfds[1], F_SETFD, fcntl(errfds[1], F_GETFD) | FD_CLOEXEC)) - { - close(errfds[0]); - close(errfds[1]); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to set \"close on exec\" flag on write end of the stderr pipe for Ghostscript call"); - goto out; - } - - if ((gspid = fork()) == 0) - { - // Couple infds pipe with stdin of Ghostscript process - if (infds[0] >= 0) - { - if (infds[0] != 0) - { - if (dup2(infds[0], 0) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to couple pipe with stdin of Ghostscript process"); - exit(1); - } - close(infds[0]); - } - close(infds[1]); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: invalid pipe file descriptor to couple with stdin of Ghostscript process"); - exit(1); - } - - // Couple errfds pipe with stdin of Ghostscript process - if (errfds[1] >= 2) - { - if (errfds[1] != 2) - { - if (dup2(errfds[1], 2) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to couple pipe with stderr of Ghostscript process"); - exit(1); - } - close(errfds[1]); - } - close(errfds[0]); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: invalid pipe file descriptor to couple with stderr of Ghostscript process"); - exit(1); - } - - // Couple stdout of Ghostscript process - if (outputfd >= 1) - { - if (outputfd != 1) - { - if (dup2(outputfd, 1) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to couple stdout of Ghostscript process"); - exit(1); - } - close(outputfd); - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Invalid file descriptor to couple with stdout of Ghostscript process"); - exit(1); - } - - // Execute Ghostscript command line ... - execvpe(filename, gsargv, envp); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to launch Ghostscript: %s: %s", - filename, strerror(errno)); - exit(1); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Started Ghostscript (PID %d)", gspid); - - close(infds[0]); - close(errfds[1]); - - if ((errpid = fork()) == 0) - { - close(infds[1]); - logfp = cupsFileOpenFd(errfds[0], "r"); - while (cupsFileGets(logfp, buf, sizeof(buf))) - if (log) - { - if (strncmp(buf, "DEBUG: ", 7) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 7; - } - else if (strncmp(buf, "DEBUG2: ", 8) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 8; - } - else if (strncmp(buf, "INFO: ", 6) == 0) - { - log_level = CF_LOGLEVEL_INFO; - msg = buf + 6; - } - else if (strncmp(buf, "WARNING: ", 9) == 0) - { - log_level = CF_LOGLEVEL_WARN; - msg = buf + 9; - } - else if (strncmp(buf, "ERROR: ", 7) == 0) - { - log_level = CF_LOGLEVEL_ERROR; - msg = buf + 7; - } - else - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf; - } - log(ld, log_level, "cfFilterGhostscript: %s", msg); - } - - cupsFileClose(logfp); - - // No need to close the fd errfds[0], as cupsFileClose(fp) does this - // already - // Ignore errors of the logging process - exit(0); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Started logging (PID %d)", errpid); - - close(errfds[0]); - - // Feed job data into Ghostscript - while ((!iscanceled || !iscanceled(icd)) && - (n = fread(buf, 1, BUFSIZ, fp)) > 0) - { - int count; - retry_write: - count = write(infds[1], buf, n); - if (count != n) - { - if (count == -1) - { - if (errno == EINTR) - goto retry_write; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: write failed: %s", strerror(errno)); - } - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Can't feed job data into Ghostscript"); - goto out; - } - } - close (infds[1]); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Input data feed completed"); - - while (gspid > 0 || errpid > 0) - { - if ((pid = wait(&wstatus)) < 0) - { - if (errno == EINTR && iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Job canceled, killing Ghostscript ..."); - kill(gspid, SIGTERM); - gspid = -1; - kill(errpid, SIGTERM); - errpid = -1; - break; - } - else - continue; - } - - // How did the filter terminate - if (wstatus) - { - if (WIFEXITED(wstatus)) - { - // Via exit() anywhere or return() in the main() function - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: %s (PID %d) stopped with status %d", - (pid == gspid ? "Ghostscript" : "Logging"), pid, - WEXITSTATUS(wstatus)); - status = WEXITSTATUS(wstatus); - } - else - { - // Via signal - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: %s (PID %d) crashed on signal %d", - (pid == gspid ? "Ghostscript" : "Logging"), pid, - WTERMSIG(wstatus)); - status = 256 * WTERMSIG(wstatus); - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: %s (PID %d) exited with no errors.", - (pid == gspid ? "Ghostscript" : "Logging"), pid); - status = 0; - } - if (pid == gspid) - gspid = -1; - else if (pid == errpid) - errpid = -1; - } - - out: - free(gsargv); - - return (status); -} - -// -// 'cfFilterGhostscript()' - Filter function to use Ghostscript for print -// data conversions -// - -int // O - Error status -cfFilterGhostscript(int inputfd, // I - File descriptor input - // stream - int outputfd, // I - File descriptor output - // stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - cf_filter_out_format_t outformat; - char buf[BUFSIZ]; - char *filename; - char *icc_profile = NULL; - char *tmp; - char tmpstr[1024], - tempfile[1024]; - const char *t = NULL; - char *envp[4]; - int num_env = 0; - cups_array_t *gs_args = NULL; - cups_option_t *options = NULL; - FILE *fp = NULL; - gs_doc_t doc_type; - gs_page_header h; - cups_cspace_t cspace = -1; - int bytes; - int fd; - int cm_disabled = 0; - int i; - int num_options; - int status = 1; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - struct sigaction sa; - cf_cm_calibration_t cm_calibrate; - int pxlcolor = 0; // 1 if printer is color printer otherwise 0. - ipp_attribute_t *ipp_attr; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - - if (parameters) - { - outformat = *(cf_filter_out_format_t *)parameters; - if (outformat != CF_FILTER_OUT_FORMAT_PDF && - outformat != CF_FILTER_OUT_FORMAT_PDF_IMAGE && - outformat != CF_FILTER_OUT_FORMAT_PCLM && - outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER && - outformat != CF_FILTER_OUT_FORMAT_PWG_RASTER && - outformat != CF_FILTER_OUT_FORMAT_APPLE_RASTER && - outformat != CF_FILTER_OUT_FORMAT_PXL) - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - } - else - { - t = data->final_content_type; - if (t) - { - if (strcasestr(t, "pwg")) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - else if (strcasestr(t, "urf")) - outformat = CF_FILTER_OUT_FORMAT_APPLE_RASTER; - else if (strcasestr(t, "pclm")) - outformat = CF_FILTER_OUT_FORMAT_PCLM; - else if (strcasestr(t, "pcl-xl")) - outformat = CF_FILTER_OUT_FORMAT_PXL; - else if (strcasestr(t, "pdf")) - outformat = CF_FILTER_OUT_FORMAT_PDF; - else - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - } - else - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - } - - // - // Note: With the CF_FILTER_OUT_FORMAT_APPLE_RASTER selection and a - // Ghostscript version without "appleraster" output device (9.55.x - // and older) the output is actually CUPS Raster but information - // about available color spaces and depths is taken from the - // urf-supported printer IPP attribute. This mode is for further - // processing with rastertopwg. With Ghostscript supporting Apple - // Raster output (9.56.0 and newer), we actually produce Apple - // Raster and no further filter is required. - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - (outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER ? - "Apple Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PDF ? "PDF" : - (outformat == CF_FILTER_OUT_FORMAT_PDF_IMAGE ? - "raster-only PDF" : - (outformat == CF_FILTER_OUT_FORMAT_PCLM ? "PCLm" : - "PCL XL"))))))); - - memset(&sa, 0, sizeof(sa)); - // Ignore SIGPIPE and have write return an error instead - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); - - // - // CUPS option list - // - - num_options = data->num_options; - options = data->options; - - // - // Environment variables for Ghostscript call ... - // - - if ((t = getenv("LD_LIBRARY_PATH")) != NULL) - { - snprintf(tmpstr, sizeof(tmpstr), "LD_LIBRARY_PATH=%s", t); - envp[num_env] = strdup(tmpstr); - num_env ++; - } - - if ((t = getenv("RIP_MAX_CACHE")) != NULL) - { - snprintf(tmpstr, sizeof(tmpstr), "RIP_MAX_CACHE=%s", t); - envp[num_env] = strdup(tmpstr); - num_env ++; - } - - envp[num_env] = NULL; - - // - // Open the input data stream specified by the inputfd ... - // - - if ((fp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Unable to open input data stream."); - } - - return (1); - } - - // - // Streaming mode without pre-checking input format or zero-page jobs - // - - if ((t = cupsGetOption("filter-streaming-mode", num_options, options)) == - NULL || - (!strcasecmp(t, "false") || !strcasecmp(t, "off") || - !strcasecmp(t, "no"))) - { - - // - // Find out file type ... - // - - if (inputseekable) - doc_type = parse_doc_type(fp); - - // - // Copy input into temporary file if needed ... - // (If the input is not seekable or if it is PostScript, to be able - // to count the pages) - // - - if (!inputseekable || doc_type == GS_DOC_TYPE_PS) - { - if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to copy PDF file: %s", - strerror(errno)); - fclose(fp); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0) - bytes = write(fd, buf, bytes); - - fclose(fp); - close(fd); - - filename = tempfile; - - // - // Open the temporary file to read it instead of the original input ... - // - - if ((fp = fopen(filename, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Unable to open temporary file."); - } - - goto out; - } - } - else - filename = NULL; - - if (!inputseekable) - doc_type = parse_doc_type(fp); - - if (doc_type == GS_DOC_TYPE_EMPTY) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Input is empty, outputting empty file."); - status = 0; - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - if (write(outputfd, "RaS2", 4)); - goto out; - } - if (doc_type == GS_DOC_TYPE_UNKNOWN) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Can't detect file type"); - goto out; - } - - if (doc_type == GS_DOC_TYPE_PDF) - { - int pages = cfPDFPagesFP(fp); - - if (pages == 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: No pages left, outputting empty file."); - status = 0; - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - if (write(outputfd, "RaS2", 4)); - goto out; - } - if (pages < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unexpected page count"); - goto out; - } - } - else - { - char gscommand[65536]; - char output[31] = ""; - int pagecount; - size_t bytes; - // Ghostscript runs too long on files converted from djvu files - // Using -dDEVICEWIDTHPOINTS -dDEVICEHEIGHTPOINTS params solves the - // problem - snprintf(gscommand, 65536, "%s -q -dNOPAUSE -dBATCH -sDEVICE=bbox -dDEVICEWIDTHPOINTS=1 -dDEVICEHEIGHTPOINTS=1 %s 2>&1 | grep -c HiResBoundingBox", - CUPS_GHOSTSCRIPT, filename); - - FILE *pd = popen(gscommand, "r"); - if (!pd) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Failed to execute ghostscript to determine " - "number of input pages!"); - goto out; - } - - bytes = fread(output, 1, 31, pd); - pclose(pd); - - if (bytes <= 0 || sscanf(output, "%d", &pagecount) < 1) - pagecount = -1; - - if (pagecount == 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: No pages left, outputting empty file."); - status = 0; - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - if (write(outputfd, "RaS2", 4)); - goto out; - } - if (pagecount < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unexpected page count"); - goto out; - } - } - - if (filename) - { - // Remove name of temp file - unlink(filename); - filename = NULL; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Input format: %s", - (doc_type == GS_DOC_TYPE_PDF ? "PDF" : - (doc_type == GS_DOC_TYPE_PS ? "PostScript" : - (doc_type == GS_DOC_TYPE_EMPTY ? "Empty file" : - "Unknown")))); - } - else - { - doc_type = GS_DOC_TYPE_UNKNOWN; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Input format: Not determined"); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Streaming mode, no checks for input format, zero-page input, instructions from previous filter"); - } - - // Ghostscript parameters - gs_args = cupsArrayNew(NULL, NULL); - if (!gs_args) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterGhostscript: Unable to allocate memory for Ghostscript arguments array"); - goto out; - } - - // Part of Ghostscript command line which is not dependent on the job and/or - // the driver - snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_GHOSTSCRIPT); - cupsArrayAdd(gs_args, strdup(tmpstr)); - cupsArrayAdd(gs_args, strdup("-dQUIET")); - //cupsArrayAdd(gs_args, strdup("-dDEBUG")); - cupsArrayAdd(gs_args, strdup("-dSAFER")); - cupsArrayAdd(gs_args, strdup("-dNOPAUSE")); - cupsArrayAdd(gs_args, strdup("-dBATCH")); - cupsArrayAdd(gs_args, strdup("-dNOINTERPOLATE")); - cupsArrayAdd(gs_args, strdup("-dNOMEDIAATTRS")); - if (cm_disabled) - cupsArrayAdd(gs_args, strdup("-dUseFastColor")); - else - cupsArrayAdd(gs_args, strdup("-dUsePDFX3Profile")); - cupsArrayAdd(gs_args, strdup("-sstdout=%stderr")); - cupsArrayAdd(gs_args, strdup("-sOutputFile=%stdout")); - - // Ghostscript output device - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER) - cupsArrayAdd(gs_args, strdup("-sDEVICE=cups")); - else if (outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - cupsArrayAdd(gs_args, strdup("-sDEVICE=appleraster")); - else if (outformat == CF_FILTER_OUT_FORMAT_PDF) - cupsArrayAdd(gs_args, strdup("-sDEVICE=pdfwrite")); - // In case of PCL XL, raster-obly PDF, or PCLm output we determine - // the exact output device later - - // Special Ghostscript options for PDF output - if (outformat == CF_FILTER_OUT_FORMAT_PDF) - { - // - // If we output PDF we are running as a PostScript-to-PDF filter - // for incoming PostScript jobs. If the client embeds a command - // for multiple copies in the PostScript job instead of using the - // CUPS argument for the number of copies, we need to run - // Ghostscript with the "-dDoNumCopies" option so that it respects - // the embedded command for the number of copies. - // - // We always supply this option if the number of copies CUPS got - // told about is 1, as this is the case if a client sets the - // number of copies as embedded PostScript command, and it is also - // not doing the wrong thing if the command is missing when the - // client only wants a single copy, independent how the client - // actually triggers multiple copies. If the CUPS arguments tells - // us that the clients wants more than one copy we do not supply - // "-dDoNumCopies" as the client does the right, modern CUPS way, - // and if the client got a "dirty" PostScript file with an - // embedded multi-copy setting, he does not get unwished copies. - // also a buggy client supplying the number of copies both via - // PostScript and CUPS will not cause an unwished number of copies - // this way. - // - // See https://github.com/OpenPrinting/cups-filters/issues/255 - // - // This was already correctly implemented in the former pdftops - // shell-script-based filter but overlooked when the filter's - // functionality got folded into this gstoraster.c filter. It was - // not seen for long time as clients sending PostScript jobs with - // embedded number of copies are rare. - // - - if (data->copies <= 1) - cupsArrayAdd(gs_args, strdup("-dDoNumCopies")); - - cupsArrayAdd(gs_args, strdup("-dShowAcroForm")); - cupsArrayAdd(gs_args, strdup("-dCompatibilityLevel=1.3")); - cupsArrayAdd(gs_args, strdup("-dAutoRotatePages=/None")); - cupsArrayAdd(gs_args, strdup("-dAutoFilterColorImages=false")); - cupsArrayAdd(gs_args, strdup("-dNOPLATFONTS")); - cupsArrayAdd(gs_args, strdup("-dColorImageFilter=/FlateEncode")); - cupsArrayAdd(gs_args, strdup("-dPDFSETTINGS=/default")); - cupsArrayAdd(gs_args, - strdup("-dColorConversionStrategy=/LeaveColorUnchanged")); - } - - // - // Generate a pseudo Raster header to collect all data from the - // printer and job attributes and also from the options which is - // relevant for the Raster output. The header is not actually - // inserted into the output Raster stream, but instead, converted to - // command line options for Ghostscript by the header_to_gs_args() - // function. Then Ghostscript generates the actual headers by - // itself. - // - // Ghostscript especially uses the sizes of each input page as - // output page sizes and not the page size requested on the call of - // this filter function. This means, for avoiding to send pages of - // unsupported size to the printer, to pass the input data through - // cfFilterPDFToPDF() before applying the cfFilterGhostscript() - // filter function. - // - - cspace = -1; - cfRasterPrepareHeader(&h, data, outformat, outformat, 0, &cspace); - - // Find print-rendering-intent - h.cupsRenderingIntent[0] = '\0'; - cfGetPrintRenderIntent(data, h.cupsRenderingIntent, - sizeof(h.cupsRenderingIntent)); - if(log) log(ld, CF_LOGLEVEL_DEBUG, - "Print rendering intent = %s", h.cupsRenderingIntent); - - // Check status of color management in CUPS - cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (cm_calibrate == CF_CM_CALIBRATION_ENABLED) - cm_disabled = 1; - else - cm_disabled = cfCmIsPrinterCmDisabled(data); - - if (!cm_disabled) - cfCmGetPrinterIccProfile(data, cfRasterColorSpaceString(h.cupsColorSpace), - h.MediaType, h.HWResolution[0], h.HWResolution[1], - &icc_profile); - - // Special Ghostscript options for raster-only PDF output - - // We use PCLm instead of general raster PDF here if the printer - // supports it, as PCLm can get streamed by the printer - - // Note that these output formats require Ghostscript 9.55.0 or later - - if (outformat == CF_FILTER_OUT_FORMAT_PDF_IMAGE || - outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - int res_x, res_y, - sup_res_x, sup_res_y, - best_res_x = 0, best_res_y = 0, - res_diff, - best_res_diff = INT_MAX, - n; - const char *res_str = NULL; - char c; - - ipp_attr = NULL; - if (outformat == CF_FILTER_OUT_FORMAT_PCLM || // PCLm forced - // PCLm supported according to printer IPP attributes - (printer_attrs && - (ipp_attr = - ippFindAttribute(printer_attrs, "pclm-source-resolution-supported", - IPP_TAG_ZERO)) != NULL)) - { - outformat = CF_FILTER_OUT_FORMAT_PCLM; - - // Resolution - - // Check whether the job's resolution is supported pn PCLm mode and - // correct if needed - res_x = h.HWResolution[0]; - res_y = h.HWResolution[1]; - if (ipp_attr) - { - ippAttributeString(ipp_attr, tmpstr, sizeof(tmpstr)); - res_str = tmpstr; - } - if (res_str) - while ((n = sscanf(res_str, "%d%c%d", - &sup_res_x, &c, &sup_res_y)) > 0) - { - if (n < 3 || (c != 'x' && c != 'X')) - sup_res_y = sup_res_x; - if (sup_res_x > 0 && sup_res_y > 0) - { - if (res_x == sup_res_x && res_y == sup_res_y) - { - best_res_x = res_x; - best_res_y = res_y; - break; - } - else - { - res_diff = (res_x * res_y) / (sup_res_x * sup_res_y); - if (res_diff < 1) - res_diff = (sup_res_x * sup_res_y) / (res_x * res_y); - if (res_diff <= best_res_diff) - { - best_res_x = sup_res_x; - best_res_y = sup_res_y; - } - } - } - res_str = strchr(res_str, ','); - if (res_str == NULL) - break; - } - if (best_res_x > 0 && best_res_y > 0) - { - snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d", best_res_x, best_res_y); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - else if (printer_attrs && - (ipp_attr = - ippFindAttribute(printer_attrs, - "pclm-source-resolution-default", - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, tmpstr, sizeof(tmpstr)); - if ((n = sscanf(tmpstr, "%d%c%d", - &best_res_x, &c, &best_res_y)) > 0) - { - if (n < 3 || (c != 'x' && c != 'X')) - best_res_y = best_res_x; - if (best_res_x > 0 && best_res_y > 0) - { - snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d", - best_res_x, best_res_y); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - } - } - if (best_res_x <= 0 || best_res_y <= 0) - { - snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d", res_x, res_y); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - - // Ghostscript output device - - if (h.cupsColorSpace == CUPS_CSPACE_SW) - cupsArrayAdd(gs_args, strdup("-sDEVICE=pclm8")); - else - cupsArrayAdd(gs_args, strdup("-sDEVICE=pclm")); - - // Strip/Band Height - - n = 0; - if (printer_attrs && - (ipp_attr = - ippFindAttribute(printer_attrs, - "pclm-strip-height-preferred", - IPP_TAG_ZERO)) != NULL) - n = ippGetInteger(ipp_attr, 0); - if (n <= 0) - n = 16; - snprintf(tmpstr, sizeof(tmpstr), "-dStripHeight=%d", n); - cupsArrayAdd(gs_args, strdup(tmpstr)); - - // Back side orientation for Duplex not (yet) supported by Ghostscript - - // Compression method - - if (printer_attrs && - (ipp_attr = - ippFindAttribute(printer_attrs, - "pclm-compression-method-preferred", - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, tmpstr, sizeof(tmpstr)); - if (strcasestr(tmpstr, "flate")) - cupsArrayAdd(gs_args, strdup("-sCompression=Flate")); - else if (strcasestr(tmpstr, "rle")) - cupsArrayAdd(gs_args, strdup("-sCompression=RLE")); - else if (strcasestr(tmpstr, "jpeg")) - cupsArrayAdd(gs_args, strdup("-sCompression=JPEG")); - else - cupsArrayAdd(gs_args, strdup("-sCompression=Flate")); - } - else - cupsArrayAdd(gs_args, strdup("-sCompression=Flate")); - } - else - { - // No PCLm supported or requested, use general raster PDF - - // Ghostscript output device and color/gray - - n = 0; - if (printer_attrs) - { - if ((ipp_attr = - ippFindAttribute(printer_attrs, - "color-supported", IPP_TAG_ZERO)) != NULL && - ippGetBoolean(ipp_attr, 0)) - // Color printer, according to printer attributes - n = 1; - } - if (n == 1 && h.cupsNumColors > 1) - cupsArrayAdd(gs_args, strdup("-sDEVICE=pdfimage24")); - else - cupsArrayAdd(gs_args, strdup("-sDEVICE=pdfimage8")); - - // Compression method - - cupsArrayAdd(gs_args, strdup("-sCompression=Flate")); - } - - // Common option: Downscaling factor - - cupsArrayAdd(gs_args, strdup("-dDownScaleFactor=1")); - } - - if (outformat == CF_FILTER_OUT_FORMAT_PXL) - { - if (printer_attrs) - { - if ((ipp_attr = - ippFindAttribute(printer_attrs, - "color-supported", IPP_TAG_BOOLEAN)) != NULL && - ippGetBoolean(ipp_attr, 0)) - // Color PCL XL printer, according to printer attributes - pxlcolor = 1; - } - - if (job_attrs) - { - if ((ipp_attr = - ippFindAttribute(job_attrs, "pwg-raster-document-type", - IPP_TAG_ZERO)) != NULL || - (ipp_attr = - ippFindAttribute(job_attrs, "color-space", IPP_TAG_ZERO)) != NULL || - (ipp_attr = - ippFindAttribute(job_attrs, "color-model", IPP_TAG_ZERO)) != NULL || - (ipp_attr = - ippFindAttribute(job_attrs, "print-color-mode", - IPP_TAG_ZERO)) != NULL || - (ipp_attr = - ippFindAttribute(job_attrs, "output-mode", IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (!strncasecmp(buf, "AdobeRgb", 8) || - !strncasecmp(buf, "adobe-rgb", 9) || - !strcasecmp(buf, "color") || - !strncasecmp(buf,"Cmyk", 4) || - !strncasecmp(buf, "Cmy", 3) || - !strncasecmp(buf, "Srgb", 4) || - !strncasecmp(buf, "Rgbw", 4) || - !strcasecmp(buf, "auto") || - !strncasecmp(buf, "Rgb", 3)) - pxlcolor = 1; - else if(!strncasecmp(buf, "Device", 6)) - { - char* ptr = buf + 6; - if (strtol(ptr, (char **)&ptr, 10) > 1) // If printer seems to - // support more than 1 - // color - pxlcolor = 1; - } - } - } - - if (pxlcolor == 0) // Still printer seems to be mono - { - const char* val; - if ((val = cupsGetOption("pwg-raster-document-type", num_options, - options)) != NULL || - (val = cupsGetOption("PwgRasterDocumentType", num_options, - options)) != NULL || - (val = cupsGetOption("color-space", num_options, options)) != NULL || - (val = cupsGetOption("ColorSpace", num_options, options)) != NULL || - (val = cupsGetOption("color-model", num_options, options)) != NULL || - (val = cupsGetOption("ColorModel", num_options, options)) != NULL || - (val = cupsGetOption("print-color-mode", num_options, options)) != - NULL || - (val = cupsGetOption("output-mode", num_options, options)) != NULL || - (val = cupsGetOption("OutputMode", num_options, options)) != NULL) - { - if(!strncasecmp(val, "AdobeRgb", 8) || - !strncasecmp(val, "adobe-rgb", 9) || - !strcasecmp(val, "color") || - !strncasecmp(val, "Cmyk", 4) || - !strncasecmp(val, "Cmy", 3) || - !strncasecmp(val, "Srgb", 4) || - !strncasecmp(val, "Rgbw", 4) || - !strncasecmp(val, "Rgb", 3) || - !strcasecmp(val, "auto")) - pxlcolor = 1; - else if(!strncasecmp(val, "Device", 6)) - { - const char *ptr = val + 6; - if (strtol(ptr, (char **)&ptr, 10) > 1) // Printer seems to support - // more then 1 color - pxlcolor = 1; - } - } - } - } - - // set PDF-specific options - if (doc_type == GS_DOC_TYPE_PDF) - parse_pdf_header_options(fp, &h); - - // fixed other values that pdftopdf handles - h.MirrorPrint = CUPS_FALSE; - h.Orientation = CUPS_ORIENT_0; - - // get all the data from the header and pass it to ghostscript - header_to_gs_args(&h, gs_args, outformat, pxlcolor); - - // CUPS Raster versions: 2 = compressed; 3 = uncompressed - // Requires Ghostscript 10.00.0 or later - if (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER && - (t = cupsGetOption("cups-raster-version", - num_options, options)) != NULL && - (!strcmp(t, "2") || !strcmp(t, "3"))) - { - snprintf(tmpstr, sizeof(tmpstr), "-dcupsRasterVersion=%s", t); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - - // Back side orientation for duplex printing: Normal, ManualTumble, - // Rotated, Flipped - // When printing duplex, margins on the back side meeds to get swapped? - // Requires Ghostscript 10.00.0 or later - if (h.Duplex) - { - int backside; - // analyze options relevant to Duplex - // APDuplexRequiresFlippedMargin - enum { - FM_NO, - FM_FALSE, - FM_TRUE - } flippedMargin; - - backside = cfGetBackSideOrientation(data); - - t = NULL; - flippedMargin = FM_NO; - - if (backside >= 0) - { - flippedMargin = (backside & 16 ? FM_TRUE : - (backside & 8 ? FM_FALSE : - FM_NO)); - backside &= 7; - - if (backside == CF_BACKSIDE_MANUAL_TUMBLE) - t = "ManualTumble"; - else if (backside == CF_BACKSIDE_ROTATED) - t = "Rotated"; - else if (backside == CF_BACKSIDE_FLIPPED) - t = "Flipped"; - else - t = "Normal"; - } - - if (t != NULL) - { - snprintf(tmpstr, sizeof(tmpstr), "-scupsBackSideOrientation=%s", t); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - - if (flippedMargin == FM_TRUE) - cupsArrayAdd(gs_args, strdup("-dcupsBackSideFlipMargins")); - } - - // Manual Copies needed (no device copies functionality available) - // Requires Ghostscript 10.00.0 or later - if ((t = cupsGetOption("hardware-copies", - num_options, options)) != NULL && - (!strcasecmp(t, "false") || !strcasecmp(t, "off") || - !strcasecmp(t, "no"))) - cupsArrayAdd(gs_args, strdup("-dcupsManualCopies")); - - // CUPS font path - if ((t = cupsGetOption("cups-fontpath", - num_options, options)) != NULL && - t[0] != '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-I%s", t); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - - // Set the device output ICC profile - if (icc_profile != NULL && icc_profile[0] != '\0') - { - snprintf(tmpstr, sizeof(tmpstr), "-sOutputICCProfile=%s", icc_profile); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - else if (!cm_disabled && - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER)) - { - // Set standard output ICC profile sGray/sRGB/AdobeRGB - if (h.cupsColorSpace == CUPS_CSPACE_SW) - cupsArrayAdd(gs_args, strdup("-sOutputICCProfile=sgray.icc")); - else if (h.cupsColorSpace == CUPS_CSPACE_SRGB) - cupsArrayAdd(gs_args, strdup("-sOutputICCProfile=srgb.icc")); - else if (h.cupsColorSpace == CUPS_CSPACE_ADOBERGB) - cupsArrayAdd(gs_args, strdup("-sOutputICCProfile=a98.icc")); - } - else if (!cm_disabled && - outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // Set standard output ICC profile sGray/sRGB - if (h.cupsColorSpace == CUPS_CSPACE_SW) - cupsArrayAdd(gs_args, strdup("-sOutputICCProfile=sgray.icc")); - else if (h.cupsColorSpace == CUPS_CSPACE_SRGB) - cupsArrayAdd(gs_args, strdup("-sOutputICCProfile=srgb.icc")); - } - else if (!cm_disabled) - cupsArrayAdd(gs_args, strdup("-sOutputICCProfile=srgb.icc")); - - // Switch to taking PostScript commands on the Ghostscript command line - cupsArrayAdd(gs_args, strdup("-c")); - - // - // Set margins if we have a bounding box defined and output format - // is not PDF, as PDF output we have only in the PostScript-to-PDF - // filtering case which happens for converting PostScript input - // files before pdftopdf so margins will be handled later, whereas - // the other output formats for PDF-to-something filtering after - // cfFilterPDFToPDF, to format the pages for the printer, so margins are - // important. - // - - if (h.cupsImagingBBox[3] > 0.0 && outformat != CF_FILTER_OUT_FORMAT_PDF) - { - snprintf(tmpstr, sizeof(tmpstr), - "<>setpagedevice", - h.cupsImagingBBox[0], h.cupsImagingBBox[1], - h.cupsPageSize[0] - h.cupsImagingBBox[2], - h.cupsPageSize[1] - h.cupsImagingBBox[3]); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - - if (!cm_disabled && - (t = cupsGetOption("profile", num_options, options)) != NULL) - { - snprintf(tmpstr, sizeof(tmpstr), "<>setpagedevice", t); - cupsArrayAdd(gs_args, strdup(tmpstr)); - } - - // - // Do we have a "center-of-pixel" or "CenterOfPixel" command line - // option set to "true"? In this case let Ghostscript use the - // center-of-pixel rule instead of the PostScript-standard - // any-part-of-pixel rule when filling a path. This improves the - // accuracy of graphics (like bar codes for example) on - // low-resolution printers (like label printers with typically 203 - // dpi). See - // https://bugs.linuxfoundation.org/show_bug.cgi?id=1373 - // - - if (((t = cupsGetOption("CenterOfPixel", num_options, options)) != NULL || - (t = cupsGetOption("center-of-pixel", num_options, options)) != NULL) && - (!strcasecmp(t, "true") || !strcasecmp(t, "on") || - !strcasecmp(t, "yes"))) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Ghostscript using Center-of-Pixel method to " - "fill paths."); - cupsArrayAdd(gs_args, strdup("0 0 .setfilladjust2")); - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterGhostscript: Ghostscript using Any-Part-of-Pixel method to " - "fill paths."); - - // Mark the end of PostScript commands supplied on the Ghostscript command - // line (with the "-c" option), so that we can supply the input file name - cupsArrayAdd(gs_args, strdup("-f")); - - // Let Ghostscript read from stdin - cupsArrayAdd(gs_args, strdup("-_")); - - // Execute Ghostscript command line ... - snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_GHOSTSCRIPT); - - // call Ghostscript - rewind(fp); - status = gs_spawn (tmpstr, gs_args, envp, fp, outputfd, log, ld, - iscanceled, icd); - if (status != 0) - status = 1; -out: - for (i = 0; envp[i]; i ++) - free(envp[i]); - if (fp) - fclose(fp); - if (filename) - // Remove name of temp file - unlink(filename); - if (gs_args) - { - while ((tmp = cupsArrayFirst(gs_args)) != NULL) - { - cupsArrayRemove(gs_args, tmp); - free(tmp); - } - cupsArrayDelete(gs_args); - } - free(icc_profile); - close(outputfd); - return (status); -} diff --git a/cupsfilters/ieee1284.c b/cupsfilters/ieee1284.c deleted file mode 100644 index d5ad84f3a..000000000 --- a/cupsfilters/ieee1284.c +++ /dev/null @@ -1,1346 +0,0 @@ -// -// IEEE-1284 Device ID support functions for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfIEEE1284GetDeviceID() - Get the IEEE-1284 device ID string and -// corresponding URI. -// cfIEEE1284GetMakeModel() - Get the make and model string from the -// device ID. -// cfIEEE1284GetValues() - Get 1284 device ID keys and values. -// cfIEEE1284NormalizeMakeModel() - Normalize a product/make-and-model -// string. -// - -// -// Include necessary headers. -// - -#include -#include "ieee1284.h" -#include "debug-internal.h" -#include -#include -#include -#include - - -// -// 'cfIEEE1284GetDeviceID()' - Get the IEEE-1284 device ID string and -// corresponding URI. -// - -int // O - 0 on success, -1 on failure -cfIEEE1284GetDeviceID( - int fd, // I - File descriptor - char *device_id, // O - 1284 device ID - int device_id_size, // I - Size of buffer - char *make_model, // O - Make/model - int make_model_size, // I - Size of buffer - const char *scheme, // I - URI scheme - char *uri, // O - Device URI - int uri_size) // I - Size of buffer -{ -#ifdef __APPLE__ // This function is a no-op - (void)fd; - (void)device_id; - (void)device_id_size; - (void)make_model; - (void)make_model_size; - (void)scheme; - (void)uri; - (void)uri_size; - - return (-1); - -#else // Get the device ID from the specified file descriptor... -# ifdef __linux - int length; // Length of device ID info - int got_id = 0; -# endif // __linux -# if defined(__sun) && defined(ECPPIOC_GETDEVID) - struct ecpp_device_id did; // Device ID buffer -# endif // __sun && ECPPIOC_GETDEVID - char *ptr; // Pointer into device ID - - - DEBUG_printf(("cfIEEE1284GetDeviceID(fd=%d, device_id=%p, device_id_size=%d, " - "make_model=%p, make_model_size=%d, scheme=\"%s\", " - "uri=%p, uri_size=%d)\n", fd, device_id, device_id_size, - make_model, make_model_size, scheme ? scheme : "(null)", - uri, uri_size)); - - // - // Range check input... - // - - if (!device_id || device_id_size < 32) - { - DEBUG_puts("cfIEEE1284GetDeviceID: Bad args!"); - return (-1); - } - - if (make_model) - *make_model = '\0'; - - if (fd >= 0) - { - // - // Get the device ID string... - // - - *device_id = '\0'; - -# ifdef __linux - if (ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id)) - { - // - // Linux has to implement things differently for every device it seems. - // Since the standard parallel port driver does not provide a simple - // ioctl() to get the 1284 device ID, we have to open the "raw" parallel - // device corresponding to this port and do some negotiation trickery - // to get the current device ID. - // - - if (uri && !strncmp(uri, "parallel:/dev/", 14)) - { - char devparport[16]; // /dev/parportN - int devparportfd, // File descriptor for raw device - mode; // Port mode - - - // - // Since the Linux parallel backend only supports 4 parallel port - // devices, just grab the trailing digit and use it to construct a - // /dev/parportN filename... - // - - snprintf(devparport, sizeof(devparport), "/dev/parport%s", - uri + strlen(uri) - 1); - - if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1) - { - // - // Claim the device... - // - - if (!ioctl(devparportfd, PPCLAIM)) - { - fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK); - - mode = IEEE1284_MODE_COMPAT; - - if (!ioctl(devparportfd, PPNEGOT, &mode)) - { - // - // Put the device into Device ID mode... - // - - mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID; - - if (!ioctl(devparportfd, PPNEGOT, &mode)) - { - // - // Read the 1284 device ID... - // - - if ((length = read(devparportfd, device_id, - device_id_size - 1)) >= 2) - { - device_id[length] = '\0'; - got_id = 1; - } - } - } - - // - // Release the device... - // - - ioctl(devparportfd, PPRELEASE); - } - - close(devparportfd); - } - } - } - else - got_id = 1; - - if (got_id) - { - // - // Extract the length of the device ID string from the first two - // bytes. The 1284 spec says the length is stored MSB first... - // - - length = (((unsigned)device_id[0] & 255) << 8) + - ((unsigned)device_id[1] & 255); - - // - // Check to see if the length is larger than our buffer; first - // assume that the vendor incorrectly implemented the 1284 spec, - // and then limit the length to the size of our buffer... - // - - if (length > device_id_size || length < 14) - length = (((unsigned)device_id[1] & 255) << 8) + - ((unsigned)device_id[0] & 255); - - if (length > device_id_size) - length = device_id_size; - - // - // The length field counts the number of bytes in the string - // including the length field itself (2 bytes). The minimum - // length for a valid/usable device ID is 14 bytes: - // - // MFG: ;MDL: ; - // 2 + 4 + 1 + 5 + 1 + 1 - // - - if (length < 14) - { - // - // Can't use this device ID, so don't try to copy it... - // - - device_id[0] = '\0'; - got_id = 0; - } - else - { - // - // Copy the device ID text to the beginning of the buffer and - // nul-terminate. - // - - length -= 2; - - memmove(device_id, device_id + 2, length); - device_id[length] = '\0'; - } - } - else - { - DEBUG_printf(("cfIEEE1284GetDeviceID: ioctl failed - %s\n", - strerror(errno))); - *device_id = '\0'; - } -# endif // __linux - -# if defined(__sun) && defined(ECPPIOC_GETDEVID) - did.mode = ECPP_CENTRONICS; - did.len = device_id_size - 1; - did.rlen = 0; - did.addr = device_id; - - if (!ioctl(fd, ECPPIOC_GETDEVID, &did)) - { - // - // Nul-terminate the device ID text. - // - - if (did.rlen < (device_id_size - 1)) - device_id[did.rlen] = '\0'; - else - device_id[device_id_size - 1] = '\0'; - } -# ifdef DEBUG - else - DEBUG_printf(("cfIEEE1284GetDeviceID: ioctl failed - %s\n", - strerror(errno))); -# endif // DEBUG -# endif // __sun && ECPPIOC_GETDEVID - } - - // - // Check whether device ID is valid. Turn line breaks and tabs to spaces and - // reject device IDs with non-printable characters. - // - - for (ptr = device_id; *ptr; ptr ++) - if (isspace(*ptr)) - *ptr = ' '; - else if ((*ptr & 255) < ' ' || *ptr == 127) - { - DEBUG_printf(("cfIEEE1284GetDeviceID: Bad device_id character %d.", - *ptr & 255)); - *device_id = '\0'; - break; - } - - DEBUG_printf(("cfIEEE1284GetDeviceID: device_id=\"%s\"\n", device_id)); - - if (scheme && uri) - *uri = '\0'; - - if (!*device_id) - return (-1); - - // - // Get the make and model... - // - - if (make_model) - cfIEEE1284GetMakeModel(device_id, make_model, make_model_size); - - // - // Then generate a device URI... - // - - if (scheme && uri && uri_size > 32) - { - int num_values; // Number of keys and values - cups_option_t *values; // Keys and values in device ID - const char *mfg, // Manufacturer - *mdl, // Model - *sern; // Serial number - char temp[256], // Temporary manufacturer string - *tempptr; // Pointer into temp string - - - // - // Get the make, model, and serial numbers... - // - - num_values = cfIEEE1284GetValues(device_id, &values); - - if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL) - if ((sern = cupsGetOption("SERN", num_values, values)) == NULL) - sern = cupsGetOption("SN", num_values, values); - - if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL) - mfg = cupsGetOption("MFG", num_values, values); - - if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) - mdl = cupsGetOption("MDL", num_values, values); - - if (mfg) - { - if (!strcasecmp(mfg, "Hewlett-Packard")) - mfg = "HP"; - else if (!strcasecmp(mfg, "Lexmark International")) - mfg = "Lexmark"; - } - else - { - strncpy(temp, make_model, sizeof(temp) - 1); - temp[sizeof(temp) - 1] = '\0'; - - if ((tempptr = strchr(temp, ' ')) != NULL) - *tempptr = '\0'; - - mfg = temp; - } - - if (!mdl) - mdl = ""; - - if (!strncasecmp(mdl, mfg, strlen(mfg))) - { - mdl += strlen(mfg); - - while (isspace(*mdl & 255)) - mdl ++; - } - - // - // Generate the device URI from the manufacturer, make_model, and - // serial number strings. - // - - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0, - "/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : ""); - - cupsFreeOptions(num_values, values); - } - - return (0); -#endif // __APPLE__ -} - - -// -// 'cfIEEE1284GetMakeModel()' - Get the make and model string from the -// device ID. -// - -int // O - 0 on success, -1 on failure -cfIEEE1284GetMakeModel( - const char *device_id, // O - 1284 device ID - char *make_model, // O - Make/model - int make_model_size) // I - Size of buffer -{ - int num_values; // Number of keys and values - cups_option_t *values; // Keys and values - const char *mfg, // Manufacturer string - *mdl, // Model string - *des; // Description string - - - DEBUG_printf(("cfIEEE1284GetMakeModel(device_id=\"%s\", " - "make_model=%p, make_model_size=%d)\n", device_id, - make_model, make_model_size)); - - // - // Range check input... - // - - if (!device_id || !*device_id || !make_model || make_model_size < 32) - { - DEBUG_puts("cfIEEE1284GetMakeModel: Bad args!"); - return (-1); - } - - *make_model = '\0'; - - // - // Look for the description field... - // - - num_values = cfIEEE1284GetValues(device_id, &values); - - if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) - mdl = cupsGetOption("MDL", num_values, values); - - if (mdl) - { - // - // Build a make-model string from the manufacturer and model attributes... - // - - if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL) - mfg = cupsGetOption("MFG", num_values, values); - - if (!mfg || !strncasecmp(mdl, mfg, strlen(mfg))) - { - // - // Just copy the model string, since it has the manufacturer... - // - - cfIEEE1284NormalizeMakeModel(mdl, NULL, CF_IEEE1284_NORMALIZE_HUMAN, NULL, - make_model, make_model_size, NULL, NULL, - NULL); - } - else - { - // - // Concatenate the make and model... - // - - char temp[1024]; // Temporary make and model - - snprintf(temp, sizeof(temp), "%s %s", mfg, mdl); - - cfIEEE1284NormalizeMakeModel(temp, NULL, CF_IEEE1284_NORMALIZE_HUMAN, - NULL, make_model, make_model_size, NULL, - NULL, NULL); - } - } - else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL || - (des = cupsGetOption("DES", num_values, values)) != NULL) - { - // - // Make sure the description contains something useful, since some - // printer manufacturers (HP) apparently don't follow the standards - // they helped to define... - // - // Here we require the description to be 8 or more characters in length, - // containing at least one space and one letter. - // - - if (strlen(des) >= 8) - { - const char *ptr; // Pointer into description - int letters, // Number of letters seen - spaces; // Number of spaces seen - - - for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++) - { - if (isspace(*ptr & 255)) - spaces ++; - else if (isalpha(*ptr & 255)) - letters ++; - - if (spaces && letters) - break; - } - - if (spaces && letters) - cfIEEE1284NormalizeMakeModel(des, NULL, CF_IEEE1284_NORMALIZE_HUMAN, - NULL, make_model, make_model_size, NULL, - NULL, NULL); - } - } - - if (!make_model[0]) - { - // - // Use "Unknown" as the printer make and model... - // - - strncpy(make_model, "Unknown", make_model_size - 1); - make_model[make_model_size - 1] = '\0'; - } - - cupsFreeOptions(num_values, values); - - return (0); -} - - -// -// 'cfIEEE1284GetValues()' - Get 1284 device ID keys and values. -// -// The returned dictionary is a CUPS option array that can be queried with -// cupsGetOption and freed with cupsFreeOptions. -// - -int // O - Number of key/value pairs -cfIEEE1284GetValues( - const char *device_id, // I - IEEE-1284 device ID string - cups_option_t **values) // O - Array of key/value pairs -{ - int num_values; // Number of values - char key[256], // Key string - value[256], // Value string - *ptr; // Pointer into key/value - - - // - // Range check input... - // - - if (values) - *values = NULL; - - if (!device_id || !values) - return (0); - - // - // Parse the 1284 device ID value into keys and values. The format is - // repeating sequences of: - // - // [whitespace]key:value[whitespace]; - // - - num_values = 0; - while (*device_id) - { - while (isspace(*device_id)) - device_id ++; - - if (!*device_id) - break; - - for (ptr = key; *device_id && *device_id != ':'; device_id ++) - if (ptr < (key + sizeof(key) - 1)) - *ptr++ = *device_id; - - if (!*device_id) - break; - - while (ptr > key && isspace(ptr[-1])) - ptr --; - - *ptr = '\0'; - device_id ++; - - while (isspace(*device_id)) - device_id ++; - - if (!*device_id) - break; - - for (ptr = value; *device_id && *device_id != ';'; device_id ++) - if (ptr < (value + sizeof(value) - 1)) - *ptr++ = *device_id; - - if (!*device_id) - break; - - while (ptr > value && isspace(ptr[-1])) - ptr --; - - *ptr = '\0'; - device_id ++; - - num_values = cupsAddOption(key, value, num_values, values); - } - - return (num_values); -} - -// -// 'move_right_part()' - Mark a start position in a string buffer and -// move all characters beginning from there by -// a given amount of characters. Characters will -// get lost when moving to the left, there will -// be undefined character positions when moving -// to the right. -// - -static void -move_right_part( - char *buffer, // I/O - String buffer - size_t bufsize, // I - Size of string buffer - char *start_pos, // I - Start of part to be moved - int num_chars) // I - Move by how many characters? -{ - int bytes_to_be_moved, - buf_space_available; - - if (num_chars == 0) - return; - - buf_space_available = bufsize - (start_pos - buffer); - bytes_to_be_moved = strlen(start_pos) + 1; - - if (num_chars > 0) - { - if (bytes_to_be_moved + num_chars > buf_space_available) - bytes_to_be_moved = buf_space_available - num_chars; - memmove(start_pos + num_chars, start_pos, bytes_to_be_moved); - } - else - { - bytes_to_be_moved += num_chars; - memmove(start_pos, start_pos - num_chars, bytes_to_be_moved); - } -} - -// -// 'cfIEEE1284NormalizeMakeModel()' - Normalize a product/make-and-model -// string. -// -// This function tries to undo the mistakes made by many printer manufacturers -// to produce a clean make-and-model string we can use. -// - -char * // O - Normalized make-and-model string - // or NULL on error -cfIEEE1284NormalizeMakeModel( - const char *make_and_model, // I - Original make-and-model string - // or device ID - const char *make, // I - Manufacturer name as hint for - // correct separation of - // make_and_model or adding - // make, or pointer into input - // string where model name starts - // or NULL, - // ignored on device ID with "MFG" - // field or for NO_MAKE_MODEL - cf_ieee1284_normalize_modes_t mode, // I - Bit field to describe how to - // normalize - regex_t *extra_regex, // I - Compiled regex to determine - // where the extra info after - // the driver name starts, also - // mark with parentheses which - // sub string should be the - // driver name - char *buffer, // O - String buffer, to hold the - // normalized input string, plus, - // after the terminating zero, the - // driver name if an appropriate - // extra_regex is supplied - // (*drvname will point to it) - size_t bufsize, // O - Size of string buffer - char **model, // O - Pointer to where model name - // starts in buffer or NULL - char **extra, // O - Pointer to where extra info - // starts in buffer (after comma, - // semicolon, parenthese, or - // start of extra_regex - // match) or NULL - char **drvname) // O - Driver name, string of the first - // matching parenthese expression - // in the extra_regex -{ - int i; - char *bufptr; // Pointer into buffer - char sepchr = ' '; // Word separator character - int compare = 0, // Format for comparing - human = 0, // Format for human-readable string - lower = 0, // All letters lowercase - upper = 0, // All letters uppercase - pad = 0, // Zero-pad numbers to 6 digits - separate = 0, // Separate components with '\0' - nomakemodel = 0; // No make/model/extra separation - char *makeptr = NULL, // Manufacturer name in buffer - *modelptr = NULL, // Model name in buffer - *extraptr = NULL, // Extra info in buffer - *drvptr = NULL; // Driver name in buffer - int numdigits = 0, - rightsidemoved = 0; - regmatch_t re_matches[10]; // Regular expression matches, - // first entry for the whole regex, - // following entries for each - // parenthese pair - - if (!make_and_model || !buffer || bufsize < 1) - { - if (buffer) - *buffer = '\0'; - - return (NULL); - } - - // - // Check formatting mode... - // - - if (!mode) - mode = CF_IEEE1284_NORMALIZE_HUMAN; - - if (mode & CF_IEEE1284_NORMALIZE_SEPARATOR_SPACE) - sepchr = ' '; - else if (mode & CF_IEEE1284_NORMALIZE_SEPARATOR_DASH) - sepchr = '-'; - else if (mode & CF_IEEE1284_NORMALIZE_SEPARATOR_UNDERSCORE) - sepchr = '_'; - - if (mode & CF_IEEE1284_NORMALIZE_LOWERCASE) - lower = 1; - if (mode & CF_IEEE1284_NORMALIZE_UPPERCASE) - upper = 1; - - if (mode & CF_IEEE1284_NORMALIZE_PAD_NUMBERS) - pad = 1; - - if (mode & CF_IEEE1284_NORMALIZE_SEPARATE_COMPONENTS) - separate = 1; - - if (mode & CF_IEEE1284_NORMALIZE_NO_MAKE_MODEL) - nomakemodel = 1; - - if (mode & CF_IEEE1284_NORMALIZE_IPP) - { - compare = 1; - lower = 1; - upper = 0; - sepchr = '-'; - } - else if (mode & CF_IEEE1284_NORMALIZE_ENV) - { - compare = 1; - lower = 0; - upper = 1; - sepchr = '_'; - } - else if (mode & CF_IEEE1284_NORMALIZE_COMPARE) - { - compare = 1; - if (lower == 0 && upper == 0) - lower = 1; - if (lower == 1 && upper == 1) - upper = 0; - } - else if (mode & CF_IEEE1284_NORMALIZE_HUMAN) - human = 1; - - // - // Skip leading whitespace... - // - - while (isspace(*make_and_model)) - make_and_model ++; - - // - // Remove parentheses... - // - - if (make_and_model[0] == '(') - { - strncpy(buffer, make_and_model + 1, bufsize - 1); - buffer[bufsize - 1] = '\0'; - - if ((bufptr = strrchr(buffer, ')')) != NULL) - *bufptr = '\0'; - } - - // - // Determine format of input string - // - - if ((((makeptr = strstr(make_and_model, "MFG:")) != NULL && - (makeptr == make_and_model || *(makeptr - 1) == ';')) || - ((makeptr = strstr(make_and_model, "MANUFACTURER:")) != NULL && - (makeptr == make_and_model || *(makeptr - 1) == ';'))) && - (((modelptr = strstr(make_and_model, "MDL:")) != NULL && - (modelptr == make_and_model || *(modelptr - 1) == ';')) || - ((modelptr = strstr(make_and_model, "MODEL:")) != NULL && - (modelptr == make_and_model || *(modelptr - 1) == ';')))) - { - // - // Input is device ID - // - - bufptr = buffer; - while (*makeptr != ':') makeptr ++; - makeptr ++; - while (isspace(*makeptr)) makeptr ++; - while (*makeptr != ';' && *makeptr != '\0' && - bufptr < buffer + bufsize - 1) - { - *bufptr = *makeptr; - makeptr ++; - bufptr ++; - } - while (isspace(*(bufptr - 1))) bufptr --; - if (bufptr < buffer + bufsize - 1) - { - *bufptr = ' '; - makeptr ++; - bufptr ++; - } - makeptr = bufptr; - while (*modelptr != ':') modelptr ++; - modelptr ++; - while (isspace(*modelptr)) modelptr ++; - while (*modelptr != ';' && *modelptr != '\0' && - bufptr < buffer + bufsize - 1) - { - *bufptr = *modelptr; - modelptr ++; - bufptr ++; - } - while (isspace(*(bufptr - 1))) bufptr --; - *bufptr = '\0'; - if (!nomakemodel && makeptr != bufptr) - modelptr = makeptr; - else - modelptr = NULL; - extraptr = NULL; - drvptr = NULL; - } - else - { - // - // Input is string of type "MAKE MODEL", "MAKE MODEL, EXTRA", or - // "MAKE MODEL (EXTRA)" - // - - modelptr = NULL; - extraptr = NULL; - drvptr = NULL; - strncpy(buffer, make_and_model, bufsize - 1); - buffer[bufsize - 1] = '\0'; - - if (!nomakemodel) - { - if (make) - { - if (make >= make_and_model && - make < make_and_model + strlen(make_and_model)) - // - // User-supplied pointer where model name starts - // - - modelptr = buffer + (make - make_and_model); - else if (!strncasecmp(buffer, make, strlen(make)) && - isspace(buffer[strlen(make)])) - // - // User-supplied make string matches start of input - // - - modelptr = buffer + strlen(make) + 1; - else - { - // - // Add user-supplied make string at start of input - // - - snprintf(buffer, bufsize, "%s %s", make, make_and_model); - modelptr = buffer + strlen(make) + 1; - } - } - - // - // Add manufacturers as needed... - // - - if (modelptr == NULL) - { - if (!strncasecmp(make_and_model, "XPrint", 6)) - { - // - // Xerox XPrint... - // - - snprintf(buffer, bufsize, "Xerox %s", make_and_model); - modelptr = buffer + 6; - } - else if (!strncasecmp(make_and_model, "Eastman", 7)) - { - // - // Kodak... - // - - snprintf(buffer, bufsize, "Kodak %s", make_and_model + 7); - modelptr = buffer + 6; - } - else if (!strncasecmp(make_and_model, "laserwriter", 11)) - { - // - // Apple LaserWriter... - // - - snprintf(buffer, bufsize, "Apple LaserWriter%s", make_and_model + 11); - modelptr = buffer + 6; - } - else if (!strncasecmp(make_and_model, "colorpoint", 10)) - { - // - // Seiko... - // - - snprintf(buffer, bufsize, "Seiko %s", make_and_model); - modelptr = buffer + 6; - } - else if (!strncasecmp(make_and_model, "fiery", 5)) - { - // - // EFI... - // - - snprintf(buffer, bufsize, "EFI %s", make_and_model); - modelptr = buffer + 4; - } - else if (!strncasecmp(make_and_model, "ps ", 3) || - !strncasecmp(make_and_model, "colorpass", 9)) - { - // - // Canon... - // - - snprintf(buffer, bufsize, "Canon %s", make_and_model); - modelptr = buffer + 6; - } - else if (!strncasecmp(make_and_model, "primera", 7)) - { - // - // Fargo... - // - - snprintf(buffer, bufsize, "Fargo %s", make_and_model); - modelptr = buffer + 6; - } - else if (!strncasecmp(make_and_model, "designjet", 9) || - !strncasecmp(make_and_model, "deskjet", 7) || - !strncasecmp(make_and_model, "laserjet", 8) || - !strncasecmp(make_and_model, "officejet", 9)) - { - // - // HP... - // - - snprintf(buffer, bufsize, "HP %s", make_and_model); - modelptr = buffer + 3; - } - else if (!strncasecmp(make_and_model, "ecosys", 6)) - { - // - // Kyocera... - // - - snprintf(buffer, bufsize, "Kyocera %s", make_and_model); - modelptr = buffer + 8; - } - - // - // Known make names with space - // - - else if (strncasecmp(buffer, "konica minolta", 14) && - isspace(buffer[14])) - modelptr = buffer + 15; - else if (strncasecmp(buffer, "fuji xerox", 10) && - isspace(buffer[10])) - modelptr = buffer + 11; - else if (strncasecmp(buffer, "lexmark international", 21) && - isspace(buffer[21])) - modelptr = buffer + 22; - else if (strncasecmp(buffer, "kyocera mita", 12) && - isspace(buffer[12])) - modelptr = buffer + 13; - - // - // Consider the first space as separation between make and model - // - - else - { - modelptr = buffer; - while (!isspace(*modelptr) && *modelptr != '\0') - modelptr ++; - } - } - - // - // Adjust modelptr to the actual start of the model name - // - - if (modelptr) - while (!isalnum(*modelptr) && *modelptr != '\0') - modelptr ++; - } - } - - if (!nomakemodel) - { - // - // Clean up the make... - // - - bufptr = buffer; - while ((bufptr = strcasestr(bufptr, "agfa")) != NULL && - (bufptr == buffer || !isalnum(*(bufptr - 1))) && - !isalnum(*(bufptr + 4))) - { - // - // Replace with AGFA (all uppercase)... - // - - bufptr[0] = 'A'; - bufptr[1] = 'G'; - bufptr[2] = 'F'; - bufptr[3] = 'A'; - bufptr += 4; - } - - bufptr = buffer; - while ((bufptr = strcasestr(bufptr, "Hewlett-Packard")) != NULL) - { - // - // Replace with "HP"... - // - - bufptr[0] = 'H'; - bufptr[1] = 'P'; - move_right_part(buffer, bufsize, bufptr + 2, -13); - if (modelptr >= bufptr + 15) - modelptr -= 13; - bufptr += 2; - } - - bufptr = buffer; - while ((bufptr = strcasestr(bufptr, "eastman kodak company")) != NULL && - (bufptr == buffer || !isalnum(*(bufptr - 1))) && - !isalnum(*(bufptr + 21))) - { - // - // Replace with Kodak... - // - - bufptr[0] = 'K'; - bufptr[1] = 'o'; - bufptr[2] = 'd'; - bufptr[3] = 'a'; - bufptr[4] = 'k'; - move_right_part(buffer, bufsize, bufptr + 5, -16); - if (modelptr >= bufptr + 21) - modelptr -= 16; - bufptr += 5; - } - - bufptr = buffer; - while ((bufptr = strcasestr(bufptr, "Lexmark International")) != NULL) - { - // - // Strip "International"... - // - - move_right_part(buffer, bufsize, bufptr + 7, -14); - if (modelptr >= bufptr + 21) - modelptr -= 14; - bufptr += 7; - } - - bufptr = buffer; - while ((bufptr = strcasestr(bufptr, "herk")) != NULL && - (bufptr == buffer || !isalnum(*(bufptr - 1))) && - !isalnum(*(bufptr + 4))) - { - // - // Replace with LHAG... - // - - bufptr[0] = 'L'; - bufptr[1] = 'H'; - bufptr[2] = 'A'; - bufptr[3] = 'G'; - bufptr += 4; - } - - bufptr = buffer; - while ((bufptr = strcasestr(bufptr, "linotype")) != NULL && - (bufptr == buffer || !isalnum(*(bufptr - 1))) && - !isalnum(*(bufptr + 8))) - { - // - // Replace with LHAG... - // - - bufptr[0] = 'L'; - bufptr[1] = 'H'; - bufptr[2] = 'A'; - bufptr[3] = 'G'; - move_right_part(buffer, bufsize, bufptr + 4, -4); - if (modelptr >= bufptr + 8) - modelptr -= 4; - bufptr += 4; - } - - bufptr = buffer; - while ((bufptr = strcasestr(bufptr, "TOSHIBA TEC Corp.")) != NULL) - { - // - // Strip "TEC Corp."... - // - - move_right_part(buffer, bufsize, bufptr + 7, -10); - if (modelptr >= bufptr + 17) - modelptr -= 10; - bufptr += 7; - } - - // - // Remove repeated manufacturer names... - // - - while (strncasecmp(buffer, modelptr, modelptr - buffer) == 0) - move_right_part(buffer, bufsize, modelptr, buffer - modelptr); - - // - // Clean up the model name... - // - - bufptr = modelptr; - while ((bufptr = strcasestr(bufptr, " series")) != NULL) - { - // - // Strip "series"... - // - - move_right_part(buffer, bufsize, bufptr, -7); - } - - // - // Find extra info... - // - - // - // If an appropriate regular expression is supplied we consider - // the end of the model name where the match of the whole regular - // expression begins. The rest of the string is considered extra - // info. To extract a driver name from it, the regular expression - // can contain parantheses, where the content of the first - // matching parenthese pair is considered the driver name - // - - if (extra_regex) - { - if (!regexec(extra_regex, buffer, - (size_t)(sizeof(re_matches) / sizeof(re_matches[0])), - re_matches, 0)) - { - // Regular expression matches - extraptr = buffer + re_matches[0].rm_so; - if (strlen(buffer) < bufsize - 3) - for (i = 1; i < (int)(sizeof(re_matches) / sizeof(re_matches[0])); - i ++) - if (re_matches[i].rm_so >= 0 && re_matches[i].rm_eo >= 0) - { - // We have a driver name (matching parentheses). Copy - // the driver name to the end of the output buffer, so - // it does not interfere with the output string and does - // not need to get moved when the length of the output - // string changes. Point drvptr to it for easy access - drvptr = buffer + bufsize - 1; - *drvptr = '\0'; - drvptr --; - for (bufptr = buffer + re_matches[i].rm_eo - 1; - bufptr >= buffer + re_matches[i].rm_so && - drvptr > buffer + strlen(buffer) + 1; - bufptr --, drvptr --) - *drvptr = *bufptr; - if (bufptr < buffer + re_matches[i].rm_so) - drvptr ++; - else - drvptr = NULL; - break; - } - } - } - else - { - // Not having a regular expression we consider comma, semicolon, - // isolated dash, or parenthese as the end of the model name and - // the rest of the string as extra info. So we set a pointer to - // this extra info if we find such a character - if ((extraptr = strchr(buffer, ',')) == NULL) - if ((extraptr = strchr(buffer, ';')) == NULL) - if ((extraptr = strstr(buffer, " - ")) == NULL) - extraptr = strchr(buffer, '('); - if (extraptr) - { - if (human) - // Include separator characters between model and extra info, pointer - // will be on first character after model - while (extraptr > buffer && isspace(*(extraptr - 1))) - extraptr --; - else - { - // Let extra info start at first alphanumeric character - while(!isalnum(*extraptr) && *extraptr != '\0') - extraptr ++; - } - } - } - } - - // - // Remove trailing whitespace... - // - - for (bufptr = buffer + strlen(buffer) - 1; - bufptr >= buffer && isspace(*bufptr); - bufptr --); - - bufptr[1] = '\0'; - - // - // Convert string into desired format - // - - // Word and component separation, number padding - bufptr = buffer; - while (*bufptr) - { - rightsidemoved = 0; - if (compare) // Comparison-optimized format - { - if (bufptr > buffer && - ((isdigit(*bufptr) && isalpha(*(bufptr - 1))) || // a0 boundary - (isalpha(*bufptr) && isdigit(*(bufptr - 1))) || // 0a boundary - (!separate && modelptr && bufptr == modelptr && - bufptr >= buffer + 2 && // 2 separator char between make/model - isalnum(*(bufptr - 2)) && !isalnum(*(bufptr - 1))) || - (!separate && extraptr && bufptr == extraptr && - bufptr >= buffer + 2 && // 2 separator char between model/extra - isalnum(*(bufptr - 2)) && !isalnum(*(bufptr - 1))))) - { - // Insert single separator - move_right_part(buffer, bufsize, bufptr, 1); - *bufptr = sepchr; - rightsidemoved += 1; - } - else if (*bufptr == '+') // Model names sometimes differ only by a '+' - { - // Replace with the word "plus" - move_right_part(buffer, bufsize, bufptr, 3); - *bufptr = 'p'; - *(bufptr + 1) = 'l'; - *(bufptr + 2) = 'u'; - *(bufptr + 3) = 's'; - rightsidemoved += 3; - } - else if (!isalnum(*bufptr)) // Space or punctuation character - { - if (bufptr == buffer || !isalnum(*(bufptr - 1))) - { - // The previous is already a separator, remove this one - move_right_part(buffer, bufsize, bufptr, -1); - rightsidemoved -= 1; - } - else - // Turn to standard separator character - *bufptr = sepchr; - } - if (pad) - { - if (isdigit(*bufptr)) - numdigits ++; - else if (numdigits && - (!(isdigit(*bufptr)) || - (modelptr && modelptr == bufptr) || - (extraptr && extraptr == bufptr))) - { - if (numdigits < 6) - { - move_right_part(buffer, bufsize, - bufptr - numdigits, 6 - numdigits); - memset(bufptr - numdigits, '0', 6 - numdigits); - rightsidemoved += 6 - numdigits; - } - numdigits = 0; - } - } - } - else if (human) // Human-readable format - { - if (isspace(*bufptr)) // White space - { - if (bufptr == buffer || isspace(*(bufptr - 1))) - { - // The previous is already white space, remove this one - move_right_part(buffer, bufsize, bufptr, -1); - rightsidemoved -= 1; - } - else - // Turn to standard separator character - *bufptr = sepchr; - } - } - // Separate component strings with '\0' if requested - if (separate && bufptr > buffer) - { - if (modelptr && bufptr == modelptr) - *(bufptr - 1) = '\0'; - if (extraptr && bufptr == extraptr) - *(bufptr - 1) = '\0'; - } - // Correct component start pointers - if (modelptr && modelptr >= bufptr) - modelptr += rightsidemoved; - if (extraptr && extraptr >= bufptr) - extraptr += rightsidemoved; - // Advance to next character - bufptr += (rightsidemoved > 0 ? rightsidemoved : - (rightsidemoved < 0 ? 0 : 1)); - } - // Remove separator at the end of the string - if (bufptr > buffer && *(bufptr - 1) == sepchr) - *(bufptr - 1) = '\0'; - - // Adjustment of upper/lowercase - if (lower == 1 || upper == 1) - { - bufptr = buffer; - while (*bufptr) - { - if (upper && islower(*bufptr)) *bufptr = toupper(*bufptr); - if (lower && isupper(*bufptr)) *bufptr = tolower(*bufptr); - bufptr ++; - } - } - - // - // Return resulting string and pointers - // - - if (drvptr <= buffer + strlen(buffer) + 1) - drvptr = NULL; - if (model) *model = modelptr; - if (extra) *extra = extraptr; - if (drvname) *drvname = drvptr; - return (buffer[0] ? buffer : NULL); -} diff --git a/cupsfilters/ieee1284.h b/cupsfilters/ieee1284.h deleted file mode 100644 index 94db460cb..000000000 --- a/cupsfilters/ieee1284.h +++ /dev/null @@ -1,129 +0,0 @@ -// -// IEEE1284 Device ID support definitions for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPSFILTERS_IEEE1284_H_ -# define _CUPSFILTERS_IEEE1284_H_ - - -// -// Include necessary headers. -// - -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# ifdef __linux -# include -# include -# define IOCNR_GET_DEVICE_ID 1 -# define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) -# include -# include -# endif // __linux - -# ifdef __sun -# ifdef __sparc -# include -# else -# include -# include -# endif // __sparc -# endif // __sun - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Types... -// - -// Bit field to describe how to normalize make/model/device ID strings -enum cf_ieee1284_normalize_modes_e -{ - CF_IEEE1284_NORMALIZE_COMPARE = 0x01, // Optimized for comparing, - // replacing any sequence of - // non-alpha-numeric characters - // by a single separator char, - // at any letter-number boundary - // and any camel-case boundary - // add a single separator char, - // 2 separator chars between - // make/model/extra, - // make all letters lowercase (or - // uppercase) - CF_IEEE1284_NORMALIZE_IPP = 0x02, // Only chars allowed in - // IPP keywords - CF_IEEE1284_NORMALIZE_ENV = 0x04, // Environment variable format - // upparcase and underscore - CF_IEEE1284_NORMALIZE_HUMAN = 0x08, // Human-readable, conserves - // spaces and special characters - // but does some clean-up - CF_IEEE1284_NORMALIZE_LOWERCASE = 0x10, // All letters lowercase - CF_IEEE1284_NORMALIZE_UPPERCASE = 0x20, // All letters uppercase - CF_IEEE1284_NORMALIZE_SEPARATOR_SPACE = 0x40,// Separator char is ' ' - CF_IEEE1284_NORMALIZE_SEPARATOR_DASH = 0x80, // Separator char is '-' - CF_IEEE1284_NORMALIZE_SEPARATOR_UNDERSCORE = 0x100,// Separator char is '_' - CF_IEEE1284_NORMALIZE_PAD_NUMBERS = 0x200, // Zero-pad numbers in strings - // to get better list sorting - // results - CF_IEEE1284_NORMALIZE_SEPARATE_COMPONENTS = 0x400,// In the output buffer put - // '\0' bytes between make, - // model, and extra, to use - // as separate strings - CF_IEEE1284_NORMALIZE_NO_MAKE_MODEL = 0x800, // No make/model/extra separation, - // do not try to identify, add, - // or clean up manufacturer - // name -}; -typedef unsigned cf_ieee1284_normalize_modes_t; - -// -// Prototypes... -// - -extern int cfIEEE1284GetDeviceID(int fd, char *device_id, - int device_id_size, - char *make_model, - int make_model_size, - const char *scheme, char *uri, - int uri_size); -extern int cfIEEE1284GetMakeModel(const char *device_id, - char *make_model, - int make_model_size); -extern int cfIEEE1284GetValues(const char *device_id, - cups_option_t **values); -extern char *cfIEEE1284NormalizeMakeModel(const char *make_and_model, - const char *make, - cf_ieee1284_normalize_modes_t mode, - regex_t *extra_regex, - char *buffer, size_t bufsize, - char **model, char **extra, - char **drvname); - - -# ifdef __cplusplus -} -# endif // __cplusplus -#endif // !_CUPSFILTERS_IEEE1284_H_ diff --git a/cupsfilters/image-colorspace.c b/cupsfilters/image-colorspace.c deleted file mode 100644 index 89687d150..000000000 --- a/cupsfilters/image-colorspace.c +++ /dev/null @@ -1,1556 +0,0 @@ -// -// Colorspace conversions for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2006 by Easy Software Products. -// -// The color saturation/hue matrix stuff is provided thanks to Mr. Paul -// Haeberli at "http://www.sgi.com/grafica/matrix/index.html". -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfImageCMYKToBlack() - Convert CMYK data to black. -// cfImageCMYKToCMY() - Convert CMYK colors to CMY. -// cfImageCMYKToCMYK() - Convert CMYK colors to CMYK. -// cfImageCMYKToRGB() - Convert CMYK colors to device-dependent -// RGB. -// cfImageCMYKToWhite() - Convert CMYK colors to luminance. -// cfImageLut() - Adjust all pixel values with the given -// LUT. -// cfImageRGBAdjust() - Adjust the hue and saturation of the -// given RGB colors. -// cfImageRGBToBlack() - Convert RGB data to black. -// cfImageRGBToCMY() - Convert RGB colors to CMY. -// cfImageRGBToCMYK() - Convert RGB colors to CMYK. -// cfImageRGBToRGB() - Convert RGB colors to device-dependent -// RGB. -// cfImageRGBToWhite() - Convert RGB colors to luminance. -// cfImageSetProfile() - Set the device color profile. -// cfImageSetRasterColorSpace() - Set the destination colorspace. -// cfImageWhiteToBlack() - Convert luminance colors to black. -// cfImageWhiteToCMY() - Convert luminance colors to CMY. -// cfImageWhiteToCMYK() - Convert luminance colors to CMYK. -// cfImageWhiteToRGB() - Convert luminance data to RGB. -// cfImageWhiteToWhite() - Convert luminance colors to device- -// dependent luminance. -// cie_lab() - Map CIE Lab transformation... -// hue_rotate() - Rotate the hue, maintaining luminance. -// ident() - Make an identity matrix. -// mult() - Multiply two matrices. -// rgb_to_lab() - Convert an RGB color to CIE Lab. -// rgb_to_xyz() - Convert an RGB color to CIE XYZ. -// saturate() - Make a saturation matrix. -// x_form() - Transform a 3D point using a matrix... -// x_rotate() - Rotate about the x (red) axis... -// y_rotate() - Rotate about the y (green) axis... -// z_rotate() - Rotate about the z (blue) axis... -// z_shear() - Shear z using x and y... -// - -// -// Include necessary headers... -// - -#include "image-private.h" - - -// -// Define some math constants that are required... -// - -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif // !M_PI - -#ifndef M_SQRT2 -# define M_SQRT2 1.41421356237309504880 -#endif // !M_SQRT2 - -#ifndef M_SQRT1_2 -# define M_SQRT1_2 0.70710678118654752440 -#endif // !M_SQRT1_2 - -// -// CIE XYZ whitepoint... -// - -#define D65_X (0.412453 + 0.357580 + 0.180423) -#define D65_Y (0.212671 + 0.715160 + 0.072169) -#define D65_Z (0.019334 + 0.119193 + 0.950227) - - -// -// Lookup table structure... -// - -typedef int cups_clut_t[3][256]; - - -// -// Local globals... -// - -static int cfImageHaveProfile = 0; - // Do we have a color profile? -static int *cfImageDensity; - // Ink/marker density LUT -static cups_clut_t *cfImageMatrix; - // Color transform matrix LUT -static cups_cspace_t cfImageColorSpace = CUPS_CSPACE_RGB; - // Destination colorspace - - -// -// Local functions... -// - -static float cie_lab(float x, float xn); -static void hue_rotate(float [3][3], float); -static void ident(float [3][3]); -static void mult(float [3][3], float [3][3], float [3][3]); -static void rgb_to_lab(cf_ib_t *val); -static void rgb_to_xyz(cf_ib_t *val); -static void saturate(float [3][3], float); -static void x_form(float [3][3], float, float, float, float *, float *, - float *); -static void x_rotate(float [3][3], float, float); -static void y_rotate(float [3][3], float, float); -static void z_rotate(float [3][3], float, float); -static void z_shear(float [3][3], float, float); - - -// -// 'cfImageCMYKToBlack()' - Convert CMYK data to black. -// - -void -cfImageCMYKToBlack( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - int k; // Black value - - - if (cfImageHaveProfile) - while (count > 0) - { - k = (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 + in[3]; - - if (k < 255) - *out++ = cfImageDensity[k]; - else - *out++ = cfImageDensity[255]; - - in += 4; - count --; - } - else - while (count > 0) - { - k = (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 + in[3]; - - if (k < 255) - *out++ = k; - else - *out++ = 255; - - in += 4; - count --; - } -} - - -// -// 'cfImageCMYKToCMY()' - Convert CMYK colors to CMY. -// - -void -cfImageCMYKToCMY( - const cf_ib_t *in, // I - Input pixels - cf_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 (cfImageHaveProfile) - while (count > 0) - { - c = *in++; - m = *in++; - y = *in++; - k = *in++; - - cc = cfImageMatrix[0][0][c] + - cfImageMatrix[0][1][m] + - cfImageMatrix[0][2][y] + k; - cm = cfImageMatrix[1][0][c] + - cfImageMatrix[1][1][m] + - cfImageMatrix[1][2][y] + k; - cy = cfImageMatrix[2][0][c] + - cfImageMatrix[2][1][m] + - cfImageMatrix[2][2][y] + k; - - if (cc < 0) - *out++ = 0; - else if (cc > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cc]; - - if (cm < 0) - *out++ = 0; - else if (cm > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cm]; - - if (cy < 0) - *out++ = 0; - else if (cy > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cy]; - - count --; - } - else - while (count > 0) - { - c = *in++; - m = *in++; - y = *in++; - k = *in++; - - c += k; - m += k; - y += k; - - if (c < 255) - *out++ = c; - else - *out++ = 255; - - if (m < 255) - *out++ = y; - else - *out++ = 255; - - if (y < 255) - *out++ = y; - else - *out++ = 255; - - count --; - } -} - - -// -// 'cfImageCMYKToCMYK()' - Convert CMYK colors to CMYK. -// - -void -cfImageCMYKToCMYK( - const cf_ib_t *in, // I - Input pixels - cf_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 (cfImageHaveProfile) - while (count > 0) - { - c = *in++; - m = *in++; - y = *in++; - k = *in++; - - cc = (cfImageMatrix[0][0][c] + - cfImageMatrix[0][1][m] + - cfImageMatrix[0][2][y]); - cm = (cfImageMatrix[1][0][c] + - cfImageMatrix[1][1][m] + - cfImageMatrix[1][2][y]); - cy = (cfImageMatrix[2][0][c] + - cfImageMatrix[2][1][m] + - cfImageMatrix[2][2][y]); - - if (cc < 0) - *out++ = 0; - else if (cc > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cc]; - - if (cm < 0) - *out++ = 0; - else if (cm > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cm]; - - if (cy < 0) - *out++ = 0; - else if (cy > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cy]; - - *out++ = cfImageDensity[k]; - - count --; - } - else if (in != out) - { - while (count > 0) - { - *out++ = *in++; - *out++ = *in++; - *out++ = *in++; - *out++ = *in++; - - count --; - } - } -} - - -// -// 'cfImageCMYKToRGB()' - Convert CMYK colors to device-dependent RGB. -// - -void -cfImageCMYKToRGB( - const cf_ib_t *in, // I - Input pixels - cf_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 (cfImageHaveProfile) - { - while (count > 0) - { - c = *in++; - m = *in++; - y = *in++; - k = *in++; - - cr = cfImageMatrix[0][0][c] + - cfImageMatrix[0][1][m] + - cfImageMatrix[0][2][y] + k; - cg = cfImageMatrix[1][0][c] + - cfImageMatrix[1][1][m] + - cfImageMatrix[1][2][y] + k; - cb = cfImageMatrix[2][0][c] + - cfImageMatrix[2][1][m] + - cfImageMatrix[2][2][y] + k; - - if (cr < 0) - *out++ = 255; - else if (cr > 255) - *out++ = 255 - cfImageDensity[255]; - else - *out++ = 255 - cfImageDensity[cr]; - - if (cg < 0) - *out++ = 255; - else if (cg > 255) - *out++ = 255 - cfImageDensity[255]; - else - *out++ = 255 - cfImageDensity[cg]; - - if (cb < 0) - *out++ = 255; - else if (cb > 255) - *out++ = 255 - cfImageDensity[255]; - else - *out++ = 255 - cfImageDensity[cb]; - - count --; - } - } - else - { - while (count > 0) - { - c = 255 - *in++; - m = 255 - *in++; - y = 255 - *in++; - k = *in++; - - c -= k; - m -= k; - y -= k; - - if (c > 0) - *out++ = c; - else - *out++ = 0; - - if (m > 0) - *out++ = m; - else - *out++ = 0; - - if (y > 0) - *out++ = y; - else - *out++ = 0; - - if (cfImageColorSpace == CUPS_CSPACE_CIELab || - cfImageColorSpace >= CUPS_CSPACE_ICC1) - rgb_to_lab(out - 3); - else if (cfImageColorSpace == CUPS_CSPACE_CIEXYZ) - rgb_to_xyz(out - 3); - - count --; - } - } -} - - -// -// 'cfImageCMYKToWhite()' - Convert CMYK colors to luminance. -// - -void -cfImageCMYKToWhite( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - int w; // White value - - - if (cfImageHaveProfile) - { - while (count > 0) - { - w = 255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 - in[3]; - - if (w > 0) - *out++ = cfImageDensity[w]; - else - *out++ = cfImageDensity[0]; - - in += 4; - count --; - } - } - else - { - while (count > 0) - { - w = 255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 - in[3]; - - if (w > 0) - *out++ = w; - else - *out++ = 0; - - in += 4; - count --; - } - } -} - - -// -// 'cfImageLut()' - Adjust all pixel values with the given LUT. -// - -void -cfImageLut(cf_ib_t *pixels, // IO - Input/output pixels - int count, // I - Number of pixels/bytes to adjust - const cf_ib_t *lut) // I - Lookup table -{ - while (count > 0) - { - *pixels = lut[*pixels]; - pixels ++; - count --; - } -} - - -// -// 'cfImageRGBAdjust()' - Adjust the hue and saturation of the given RGB colors. -// - -void -cfImageRGBAdjust(cf_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 cups_clut_t *lut = NULL; // Lookup table for matrix - - - if (saturation != last_sat || hue != last_hue || !lut) - { - // - // Build the color adjustment matrix... - // - - ident(mat); - saturate(mat, saturation * 0.01); - hue_rotate(mat, (float)hue); - - // - // Allocate memory for the lookup table... - // - - if (lut == NULL) - lut = calloc(3, sizeof(cups_clut_t)); - - if (lut == NULL) - return; - - // - // 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; - } -} - - -// -// 'cfImageRGBToBlack()' - Convert RGB data to black. -// - -void -cfImageRGBToBlack( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - if (cfImageHaveProfile) - while (count > 0) - { - *out++ = cfImageDensity[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 --; - } -} - - -// -// 'cfImageRGBToCMY()' - Convert RGB colors to CMY. -// - -void -cfImageRGBToCMY( - const cf_ib_t *in, // I - Input pixels - cf_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 (cfImageHaveProfile) - 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 = cfImageMatrix[0][0][c] + - cfImageMatrix[0][1][m] + - cfImageMatrix[0][2][y] + k; - cm = cfImageMatrix[1][0][c] + - cfImageMatrix[1][1][m] + - cfImageMatrix[1][2][y] + k; - cy = cfImageMatrix[2][0][c] + - cfImageMatrix[2][1][m] + - cfImageMatrix[2][2][y] + k; - - if (cc < 0) - *out++ = 0; - else if (cc > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cc]; - - if (cm < 0) - *out++ = 0; - else if (cm > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cm]; - - if (cy < 0) - *out++ = 0; - else if (cy > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[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 --; - } -} - - -// -// 'cfImageRGBToCMYK()' - Convert RGB colors to CMYK. -// - -void -cfImageRGBToCMYK( - const cf_ib_t *in, // I - Input pixels - cf_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 (cfImageHaveProfile) - 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 * k / (km * km); - - c -= k; - m -= k; - y -= k; - - cc = (cfImageMatrix[0][0][c] + - cfImageMatrix[0][1][m] + - cfImageMatrix[0][2][y]); - cm = (cfImageMatrix[1][0][c] + - cfImageMatrix[1][1][m] + - cfImageMatrix[1][2][y]); - cy = (cfImageMatrix[2][0][c] + - cfImageMatrix[2][1][m] + - cfImageMatrix[2][2][y]); - - if (cc < 0) - *out++ = 0; - else if (cc > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cc]; - - if (cm < 0) - *out++ = 0; - else if (cm > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cm]; - - if (cy < 0) - *out++ = 0; - else if (cy > 255) - *out++ = cfImageDensity[255]; - else - *out++ = cfImageDensity[cy]; - - *out++ = cfImageDensity[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 * k / (km * km); - - c -= k; - m -= k; - y -= k; - - *out++ = c; - *out++ = m; - *out++ = y; - *out++ = k; - - count --; - } -} - - -// -// 'cfImageRGBToRGB()' - Convert RGB colors to device-dependent RGB. -// - -void -cfImageRGBToRGB( - const cf_ib_t *in, // I - Input pixels - cf_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 (cfImageHaveProfile) - { - 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 = cfImageMatrix[0][0][c] + - cfImageMatrix[0][1][m] + - cfImageMatrix[0][2][y] + k; - cg = cfImageMatrix[1][0][c] + - cfImageMatrix[1][1][m] + - cfImageMatrix[1][2][y] + k; - cb = cfImageMatrix[2][0][c] + - cfImageMatrix[2][1][m] + - cfImageMatrix[2][2][y] + k; - - if (cr < 0) - *out++ = 255; - else if (cr > 255) - *out++ = 255 - cfImageDensity[255]; - else - *out++ = 255 - cfImageDensity[cr]; - - if (cg < 0) - *out++ = 255; - else if (cg > 255) - *out++ = 255 - cfImageDensity[255]; - else - *out++ = 255 - cfImageDensity[cg]; - - if (cb < 0) - *out++ = 255; - else if (cb > 255) - *out++ = 255 - cfImageDensity[255]; - else - *out++ = 255 - cfImageDensity[cb]; - - count --; - } - } - else - { - if (in != out) - memcpy(out, in, count * 3); - - if (cfImageColorSpace == CUPS_CSPACE_CIELab || - cfImageColorSpace >= CUPS_CSPACE_ICC1) - { - while (count > 0) - { - rgb_to_lab(out); - - out += 3; - count --; - } - } - else if (cfImageColorSpace == CUPS_CSPACE_CIEXYZ) - { - while (count > 0) - { - rgb_to_xyz(out); - - out += 3; - count --; - } - } - } -} - - -// -// 'cfImageRGBToWhite()' - Convert RGB colors to luminance. -// - -void -cfImageRGBToWhite( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - if (cfImageHaveProfile) - { - while (count > 0) - { - *out++ = 255 - cfImageDensity[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 --; - } - } -} - - -// -// 'cfImageSetProfile()' - Set the device color profile. -// - -void -cfImageSetProfile(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 cfImageMatrix - - - // - // Allocate memory for the profile data... - // - - if (cfImageMatrix == NULL) - cfImageMatrix = calloc(3, sizeof(cups_clut_t)); - - if (cfImageMatrix == NULL) - return; - - if (cfImageDensity == NULL) - cfImageDensity = calloc(256, sizeof(int)); - - if (cfImageDensity == NULL) - return; - - // - // Populate the profile lookup tables... - // - - cfImageHaveProfile = 1; - - for (i = 0, im = cfImageMatrix[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 = cfImageDensity; k < 256; k ++) - *im++ = 255.0 * d * pow((float)k / 255.0, g) + 0.5; -} - - -// -// 'cfImageSetRasterColorSpace()' - Set the destination colorspace. -// - -void -cfImageSetRasterColorSpace( - cups_cspace_t cs) // I - Destination colorspace -{ - // - // Set the destination colorspace... - // - - cfImageColorSpace = cs; - - // - // Don't use color profiles in colorimetric colorspaces... - // - - if (cs == CUPS_CSPACE_CIEXYZ || - cs == CUPS_CSPACE_CIELab || - cs >= CUPS_CSPACE_ICC1) - cfImageHaveProfile = 0; -} - - -// -// 'cfImageWhiteToBlack()' - Convert luminance colors to black. -// - -void -cfImageWhiteToBlack( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - if (cfImageHaveProfile) - while (count > 0) - { - *out++ = cfImageDensity[255 - *in++]; - count --; - } - else - while (count > 0) - { - *out++ = 255 - *in++; - count --; - } -} - - -// -// 'cfImageWhiteToCMY()' - Convert luminance colors to CMY. -// - -void -cfImageWhiteToCMY( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - if (cfImageHaveProfile) - while (count > 0) - { - out[0] = cfImageDensity[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 --; - } -} - - -// -// 'cfImageWhiteToCMYK()' - Convert luminance colors to CMYK. -// - -void -cfImageWhiteToCMYK( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - if (cfImageHaveProfile) - while (count > 0) - { - *out++ = 0; - *out++ = 0; - *out++ = 0; - *out++ = cfImageDensity[255 - *in++]; - count --; - } - else - while (count > 0) - { - *out++ = 0; - *out++ = 0; - *out++ = 0; - *out++ = 255 - *in++; - count --; - } -} - - -// -// 'cfImageWhiteToRGB()' - Convert luminance data to RGB. -// - -void -cfImageWhiteToRGB( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - if (cfImageHaveProfile) - { - while (count > 0) - { - out[0] = 255 - cfImageDensity[255 - *in++]; - out[1] = out[0]; - out[2] = out[0]; - out += 3; - count --; - } - } - else - { - while (count > 0) - { - *out++ = *in; - *out++ = *in; - *out++ = *in++; - - if (cfImageColorSpace == CUPS_CSPACE_CIELab || - cfImageColorSpace >= CUPS_CSPACE_ICC1) - rgb_to_lab(out - 3); - else if (cfImageColorSpace == CUPS_CSPACE_CIEXYZ) - rgb_to_xyz(out - 3); - - count --; - } - } -} - - -// -// 'cfImageWhiteToWhite()' - Convert luminance colors to device-dependent -// luminance. -// - -void -cfImageWhiteToWhite( - const cf_ib_t *in, // I - Input pixels - cf_ib_t *out, // I - Output pixels - int count) // I - Number of pixels -{ - if (cfImageHaveProfile) - while (count > 0) - { - *out++ = 255 - cfImageDensity[255 - *in++]; - count --; - } - else if (in != out) - memcpy(out, in, count); -} - - -// -// 'cie_lab()' - Map CIE Lab transformation... -// - -static float // O - Adjusted color value -cie_lab(float x, // I - Raw color value - float xn) // I - Whitepoint color value -{ - float x_xn; // Fraction of whitepoint - - - x_xn = x / xn; - - if (x_xn > 0.008856) - return (cbrt(x_xn)); - else - return (7.787 * x_xn + 16.0 / 116.0); -} - - -// -// 'hue_rotate()' - Rotate the hue, maintaining luminance. -// - -static void -hue_rotate(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; - x_rotate(hmat,xrs,xrc); - - yrs = -1.0 / sqrt(3.0); - yrc = -M_SQRT2 * yrs; - y_rotate(hmat,yrs,yrc); - - // - // Shear the space to make the luminance plane horizontal... - // - - x_form(hmat, 0.3086, 0.6094, 0.0820, &lx, &ly, &lz); - zsx = lx / lz; - zsy = ly / lz; - z_shear(hmat, zsx, zsy); - - // - // Rotate the hue... - // - - zrs = sin(rot * M_PI / 180.0); - zrc = cos(rot * M_PI / 180.0); - - z_rotate(hmat, zrs, zrc); - - // - // Unshear the space to put the luminance plane back... - // - - z_shear(hmat, -zsx, -zsy); - - // - // Rotate the grey vector back into place... - // - - y_rotate(hmat, -yrs, yrc); - x_rotate(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)); -} - - -// -// 'rgb_to_lab()' - Convert an RGB color to CIE Lab. -// - -static void -rgb_to_lab(cf_ib_t *val) // IO - Color value -{ - float r, // Red value - g, // Green value - b, // Blue value - ciex, // CIE X value - ciey, // CIE Y value - ciez, // CIE Z value - ciey_yn, // Normalized luminance - ciel, // CIE L value - ciea, // CIE a value - cieb; // CIE b value - - - // - // Convert sRGB to linear RGB... - // - - r = pow((val[0] / 255.0 + 0.055) / 1.055, 2.4); - g = pow((val[1] / 255.0 + 0.055) / 1.055, 2.4); - b = pow((val[2] / 255.0 + 0.055) / 1.055, 2.4); - - // - // Convert to CIE XYZ... - // - - ciex = 0.412453 * r + 0.357580 * g + 0.180423 * b; - ciey = 0.212671 * r + 0.715160 * g + 0.072169 * b; - ciez = 0.019334 * r + 0.119193 * g + 0.950227 * b; - - // - // Normalize and convert to CIE Lab... - // - - ciey_yn = ciey / D65_Y; - - if (ciey_yn > 0.008856) - ciel = 116 * cbrt(ciey_yn) - 16; - else - ciel = 903.3 * ciey_yn; - - //ciel = ciel; - ciea = 500 * (cie_lab(ciex, D65_X) - cie_lab(ciey, D65_Y)); - cieb = 200 * (cie_lab(ciey, D65_Y) - cie_lab(ciez, D65_Z)); - - // - // Scale the L value and bias the a and b values by 128 so that all - // numbers are from 0 to 255. - // - - ciel = ciel * 2.55 + 0.5; - ciea += 128.5; - cieb += 128.5; - - // - // Output 8-bit values... - // - - if (ciel < 0.0) - val[0] = 0; - else if (ciel < 255.0) - val[0] = (int)ciel; - else - val[0] = 255; - - if (ciea < 0.0) - val[1] = 0; - else if (ciea < 255.0) - val[1] = (int)ciea; - else - val[1] = 255; - - if (cieb < 0.0) - val[2] = 0; - else if (cieb < 255.0) - val[2] = (int)cieb; - else - val[2] = 255; -} - - -// -// 'rgb_to_xyz()' - Convert an RGB color to CIE XYZ. -// - -static void -rgb_to_xyz(cf_ib_t *val) // IO - Color value -{ - float r, // Red value - g, // Green value - b, // Blue value - ciex, // CIE X value - ciey, // CIE Y value - ciez; // CIE Z value - - - // - // Convert sRGB to linear RGB... - // - - r = pow((val[0] / 255.0 + 0.055) / 1.055, 2.4); - g = pow((val[1] / 255.0 + 0.055) / 1.055, 2.4); - b = pow((val[2] / 255.0 + 0.055) / 1.055, 2.4); - - // - // Convert to CIE XYZ... - // - - ciex = 0.412453 * r + 0.357580 * g + 0.180423 * b; - ciey = 0.212671 * r + 0.715160 * g + 0.072169 * b; - ciez = 0.019334 * r + 0.119193 * g + 0.950227 * b; - - // - // Encode as 8-bit XYZ... - // - - if (ciex < 0.0f) - val[0] = 0; - else if (ciex < 1.1f) - val[0] = (int)(231.8181f * ciex + 0.5); - else - val[0] = 255; - - if (ciey < 0.0f) - val[1] = 0; - else if (ciey < 1.1f) - val[1] = (int)(231.8181f * ciey + 0.5); - else - val[1] = 255; - - if (ciez < 0.0f) - val[2] = 0; - else if (ciez < 1.1f) - val[2] = (int)(231.8181f * ciez + 0.5); - else - val[2] = 255; -} - - -// -// '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); -} - - -// -// 'x_form()' - Transform a 3D point using a matrix... -// - -static void -x_form(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]; -} - - -// -// 'x_rotate()' - Rotate about the x (red) axis... -// - -static void -x_rotate(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); -} - - -// -// 'y_rotate()' - Rotate about the y (green) axis... -// - -static void -y_rotate(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); -} - - -// -// 'z_rotate()' - Rotate about the z (blue) axis... -// - -static void -z_rotate(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); -} - - -// -// 'z_shear()' - Shear z using x and y... -// - -static void -z_shear(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); -} diff --git a/cupsfilters/image-jpeg.c b/cupsfilters/image-jpeg.c deleted file mode 100644 index 561d51d68..000000000 --- a/cupsfilters/image-jpeg.c +++ /dev/null @@ -1,326 +0,0 @@ -// -// JPEG image routines for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// _cfImageReadJPEG() - Read a JPEG image file. -// - -// -// Include necessary headers... -// - -#include "image-private.h" - -#ifdef HAVE_LIBJPEG -# include // JPEG/JFIF image definitions - -#define JPEG_APP0 0xE0 // APP0 marker code - -// -// '_cfImageReadJPEG()' - Read a JPEG image file. -// - -int // O - Read status -_cfImageReadJPEG( - cf_image_t *img, // IO - Image - FILE *fp, // I - Image file - cf_icspace_t primary, // I - Primary choice for colorspace - cf_icspace_t secondary, // I - Secondary choice for colorspace - int saturation, // I - Color saturation (%) - int hue, // I - Color hue (degrees) - const cf_ib_t *lut) // I - Lookup table for - // gamma/brightness -{ - struct jpeg_decompress_struct cinfo; // Decompressor info - struct jpeg_error_mgr jerr; // Error handler info - cf_ib_t *in, // Input pixels - *out; // Output pixels - jpeg_saved_marker_ptr marker; // Pointer to marker data - int psjpeg = 0; // Non-zero if Photoshop CMYK JPEG - static const char *cspaces[] = - { // JPEG colorspaces... - "JCS_UNKNOWN", - "JCS_GRAYSCALE", - "JCS_RGB", - "JCS_YCbCr", - "JCS_CMYK", - "JCS_YCCK" - }; - - (void)cspaces; - - // - // Read the JPEG header... - // - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - jpeg_save_markers(&cinfo, JPEG_APP0 + 14, 0xffff); // Adobe JPEG - jpeg_stdio_src(&cinfo, fp); - jpeg_read_header(&cinfo, 1); - - // - // Parse any Adobe APPE data embedded in the JPEG file. Since Adobe doesn't - // bother following standards, we have to invert the CMYK JPEG data written by - // Adobe apps... - // - - for (marker = cinfo.marker_list; marker; marker = marker->next) - if (marker->marker == (JPEG_APP0 + 14) && marker->data_length >= 12 && - !memcmp(marker->data, "Adobe", 5)) - { - DEBUG_puts("DEBUG: Adobe CMYK JPEG detected (inverting color values)\n"); - psjpeg = 1; - } - - cinfo.quantize_colors = 0; - - DEBUG_printf(("DEBUG: num_components = %d\n", cinfo.num_components)); - DEBUG_printf(("DEBUG: jpeg_color_space = %s\n", - cspaces[cinfo.jpeg_color_space])); - - if (cinfo.num_components == 1) - { - DEBUG_puts("DEBUG: Converting image to grayscale...\n"); - - cinfo.out_color_space = JCS_GRAYSCALE; - cinfo.out_color_components = 1; - cinfo.output_components = 1; - - img->colorspace = secondary; - } - else if (cinfo.num_components == 4) - { - DEBUG_puts("DEBUG: Converting image to CMYK...\n"); - - cinfo.out_color_space = JCS_CMYK; - cinfo.out_color_components = 4; - cinfo.output_components = 4; - - img->colorspace = (primary == CF_IMAGE_RGB_CMYK) ? CF_IMAGE_CMYK : primary; - } - else - { - DEBUG_puts("DEBUG: Converting image to RGB...\n"); - - cinfo.out_color_space = JCS_RGB; - cinfo.out_color_components = 3; - cinfo.output_components = 3; - - img->colorspace = (primary == CF_IMAGE_RGB_CMYK) ? CF_IMAGE_RGB : primary; - } - - jpeg_calc_output_dimensions(&cinfo); - - if (cinfo.output_width <= 0 || cinfo.output_width > CF_IMAGE_MAX_WIDTH || - cinfo.output_height <= 0 || cinfo.output_height > CF_IMAGE_MAX_HEIGHT) - { - DEBUG_printf(("DEBUG: Bad JPEG dimensions %dx%d!\n", - cinfo.output_width, cinfo.output_height)); - - jpeg_destroy_decompress(&cinfo); - - fclose(fp); - return (1); - } - - img->xsize = cinfo.output_width; - img->ysize = cinfo.output_height; - - int temp = -1; - -#ifdef HAVE_EXIF - // - // Scan image file for exif data - // - - temp = _cfImageReadEXIF(img, fp); -#endif - - // - // Check headers only if EXIF contains no info about ppi - // - - if (temp != 1 && 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); - } - - if (img->xppi == 0 || img->yppi == 0) - { - DEBUG_printf(("DEBUG: Bad JPEG image resolution %dx%d PPI.\n", - img->xppi, img->yppi)); - img->xppi = img->yppi = 200; - } - } - - DEBUG_printf(("DEBUG: JPEG image %dx%dx%d, %dx%d PPI\n", - img->xsize, img->ysize, cinfo.output_components, - img->xppi, img->yppi)); - - cfImageSetMaxTiles(img, 0); - - in = malloc(img->xsize * cinfo.output_components); - out = malloc(img->xsize * cfImageGetDepth(img)); - - jpeg_start_decompress(&cinfo); - - while (cinfo.output_scanline < cinfo.output_height) - { - jpeg_read_scanlines(&cinfo, (JSAMPROW *)&in, (JDIMENSION)1); - - if (psjpeg && cinfo.output_components == 4) - { - // - // Invert CMYK data from Photoshop... - - - cf_ib_t *ptr; // Pointer into buffer - int i; // Looping var - - - for (ptr = in, i = img->xsize * 4; i > 0; i --, ptr ++) - *ptr = 255 - *ptr; - } - - if ((saturation != 100 || hue != 0) && cinfo.output_components == 3) - cfImageRGBAdjust(in, img->xsize, saturation, hue); - - if ((img->colorspace == CF_IMAGE_WHITE && cinfo.out_color_space == JCS_GRAYSCALE) || - (img->colorspace == CF_IMAGE_CMYK && cinfo.out_color_space == JCS_CMYK)) - { -#ifdef DEBUG - int i, j; - cf_ib_t *ptr; - - - DEBUG_puts("DEBUG: Direct Data...\n"); - - DEBUG_puts("DEBUG:"); - - for (i = 0, ptr = in; i < img->xsize; i ++) - { - DEBUG_puts(" "); - for (j = 0; j < cinfo.output_components; j ++, ptr ++) - DEBUG_printf(("%02X", *ptr & 255)); - } - - DEBUG_puts("\n"); -#endif // DEBUG - - if (lut) - cfImageLut(in, img->xsize * cfImageGetDepth(img), lut); - - _cfImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, in); - } - else if (cinfo.out_color_space == JCS_GRAYSCALE) - { - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_BLACK : - cfImageWhiteToBlack(in, out, img->xsize); - break; - case CF_IMAGE_RGB : - cfImageWhiteToRGB(in, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageWhiteToCMY(in, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageWhiteToCMYK(in, out, img->xsize); - break; - } - - if (lut) - cfImageLut(out, img->xsize * cfImageGetDepth(img), lut); - - _cfImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, out); - } - else if (cinfo.out_color_space == JCS_RGB) - { - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_RGB : - cfImageRGBToRGB(in, out, img->xsize); - break; - case CF_IMAGE_WHITE : - cfImageRGBToWhite(in, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(in, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(in, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(in, out, img->xsize); - break; - } - - if (lut) - cfImageLut(out, img->xsize * cfImageGetDepth(img), lut); - - _cfImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, out); - } - else // JCS_CMYK - { - DEBUG_puts("DEBUG: JCS_CMYK\n"); - - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_WHITE : - cfImageCMYKToWhite(in, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageCMYKToBlack(in, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageCMYKToCMY(in, out, img->xsize); - break; - case CF_IMAGE_RGB : - cfImageCMYKToRGB(in, out, img->xsize); - break; - } - - if (lut) - cfImageLut(out, img->xsize * cfImageGetDepth(img), lut); - - _cfImagePutRow(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 diff --git a/cupsfilters/image-png.c b/cupsfilters/image-png.c deleted file mode 100644 index 60bd6a863..000000000 --- a/cupsfilters/image-png.c +++ /dev/null @@ -1,318 +0,0 @@ -// -// PNG image routines for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// _cfImageReadPNG() - Read a PNG image file. -// - -// -// Include necessary headers... -// - -#include "image-private.h" - -#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) -# include // Portable Network Graphics (PNG) definitions - - -// -// '_cfImageReadPNG()' - Read a PNG image file. -// - -int // O - Read status -_cfImageReadPNG( - cf_image_t *img, // IO - Image - FILE *fp, // I - Image file - cf_icspace_t primary, // I - Primary choice for colorspace - cf_icspace_t secondary, // I - Secondary choice for colorspace - int saturation, // I - Color saturation (%) - int hue, // I - Color hue (degrees) - const cf_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 - png_uint_32 width, // Width of image - height; // Height of image - int bit_depth, // Bit depth - color_type, // Color type - interlace_type, // Interlace type - compression_type, // Compression type - filter_type; // Filter type - png_uint_32 xppm, // X pixels per meter - yppm; // Y pixels per meter - int bpp; // Bytes per pixel - int pass, // Current pass - passes; // Number of passes required - cf_ib_t *in, // Input pixels - *inptr, // Pointer into pixels - *out; // Output pixels - png_color_16 bg; // Background color - - - // - // 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); - - png_get_IHDR(pp, info, &width, &height, &bit_depth, &color_type, - &interlace_type, &compression_type, &filter_type); - - DEBUG_printf(("DEBUG: PNG image: %dx%dx%d, color_type=%x (%s%s%s)\n", - (int)width, (int)height, bit_depth, color_type, - (color_type & PNG_COLOR_MASK_COLOR) ? "RGB" : "GRAYSCALE", - (color_type & PNG_COLOR_MASK_ALPHA) ? "+ALPHA" : "", - (color_type & PNG_COLOR_MASK_PALETTE) ? "+PALETTE" : "")); - - if (color_type & PNG_COLOR_MASK_PALETTE) - png_set_expand(pp); - else if (bit_depth < 8) - { - png_set_packing(pp); - png_set_expand(pp); - } - else if (bit_depth == 16) - png_set_strip_16(pp); - - if (color_type & PNG_COLOR_MASK_COLOR) - img->colorspace = (primary == CF_IMAGE_RGB_CMYK) ? CF_IMAGE_RGB : - primary; - else - img->colorspace = secondary; - - if (width == 0 || width > CF_IMAGE_MAX_WIDTH || - height == 0 || height > CF_IMAGE_MAX_HEIGHT) - { - DEBUG_printf(("DEBUG: PNG image has invalid dimensions %ux%u!\n", - (unsigned)width, (unsigned)height)); - fclose(fp); - return (1); - } - - img->xsize = width; - img->ysize = height; - - - int temp = -1; - -#ifdef HAVE_EXIF - // - // Scan image file for EXIF data - // - - temp = _cfImageReadEXIF(img, fp); -#endif - - // - // Check headers only if EXIF contains no info about ppi - // - - if (temp != 1 && (xppm = png_get_x_pixels_per_meter(pp, info)) != 0 && - (yppm = png_get_y_pixels_per_meter(pp, info)) != 0) - { - img->xppi = (int)((float)xppm * 0.0254); - img->yppi = (int)((float)yppm * 0.0254); - - if (img->xppi == 0 || img->yppi == 0) - { - DEBUG_printf(("DEBUG: PNG image has invalid resolution %dx%d PPI\n", - img->xppi, img->yppi)); - - img->xppi = img->yppi = 200; - } - } - - cfImageSetMaxTiles(img, 0); - - passes = png_set_interlace_handling(pp); - - // - // Handle transparency... - // - - if (png_get_valid(pp, info, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(pp); - - bg.red = 65535; - bg.green = 65535; - bg.blue = 65535; - - png_set_background(pp, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); - - if (passes == 1) - { - // - // Load one row at a time... - // - - if (color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - in = malloc(img->xsize); - else - in = malloc(img->xsize * 3); - } - else - { - // - // Interlaced images must be loaded all at once... - // - - size_t bufsize; // Size of buffer - - - if (color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - bufsize = img->xsize * img->ysize; - - if ((bufsize / img->xsize) != img->ysize) - { - DEBUG_printf(("DEBUG: PNG image dimensions (%ux%u) too large!\n", - (unsigned)width, (unsigned)height)); - fclose(fp); - return (1); - } - } - else - { - bufsize = img->xsize * img->ysize * 3; - - if ((bufsize / (img->xsize * 3)) != img->ysize) - { - DEBUG_printf(("DEBUG: PNG image dimensions (%ux%u) too large!\n", - (unsigned)width, (unsigned)height)); - fclose(fp); - return (1); - } - } - - in = malloc(bufsize); - } - - bpp = cfImageGetDepth(img); - out = malloc(img->xsize * bpp); - - if (!in || !out) - { - DEBUG_puts("DEBUG: Unable to allocate memory for PNG image!\n"); - - if (in) - free(in); - - if (out) - free(out); - - fclose(fp); - - return (1); - } - - // - // Read the image, interlacing as needed... - // - - for (pass = 1; pass <= passes; pass ++) - for (inptr = in, y = 0; y < img->ysize; y ++) - { - png_read_row(pp, (png_bytep)inptr, NULL); - - if (pass == passes) - { - // - // Output this row... - // - - if (color_type & PNG_COLOR_MASK_COLOR) - { - if ((saturation != 100 || hue != 0) && bpp > 1) - cfImageRGBAdjust(inptr, img->xsize, saturation, hue); - - switch (img->colorspace) - { - case CF_IMAGE_WHITE : - cfImageRGBToWhite(inptr, out, img->xsize); - break; - case CF_IMAGE_RGB : - case CF_IMAGE_RGB_CMYK : - cfImageRGBToRGB(inptr, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(inptr, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(inptr, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(inptr, out, img->xsize); - break; - } - } - else - { - switch (img->colorspace) - { - case CF_IMAGE_WHITE : - memcpy(out, inptr, img->xsize); - break; - case CF_IMAGE_RGB : - case CF_IMAGE_RGB_CMYK : - cfImageWhiteToRGB(inptr, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageWhiteToBlack(inptr, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageWhiteToCMY(inptr, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageWhiteToCMYK(inptr, out, img->xsize); - break; - } - } - - if (lut) - cfImageLut(out, img->xsize * bpp, lut); - - _cfImagePutRow(img, 0, y, img->xsize, out); - } - - if (passes > 1) - { - if (color_type & PNG_COLOR_MASK_COLOR) - inptr += img->xsize * 3; - else - inptr += img->xsize; - } - } - - png_read_end(pp, info); - png_destroy_read_struct(&pp, &info, NULL); - - fclose(fp); - free(in); - free(out); - - return (0); -} -#endif // HAVE_LIBPNG && HAVE_LIBZ diff --git a/cupsfilters/image-private.h b/cupsfilters/image-private.h deleted file mode 100644 index d26deb43b..000000000 --- a/cupsfilters/image-private.h +++ /dev/null @@ -1,180 +0,0 @@ -// -// Private image library definitions for libcupsfilters. -// -// Copyright 2007-2010 by Apple Inc. -// Copyright 1993-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#ifndef _CUPS_IMAGE_PRIVATE_H_ -# define _CUPS_IMAGE_PRIVATE_H_ - - -// -// Include necessary headers... -// - -# include -# include "image.h" -# include -# define DEBUG_printf(x) -# define DEBUG_puts(x) -# include -# include -# include -# ifdef WIN32 -# include -# else -# include -# endif // WIN32 -# include -# include - -#ifdef HAVE_EXIF -# include -#endif - - -// -// Constants... -// - -# define CF_IMAGE_MAX_WIDTH 0x07ffffff - // 2^27-1 to allow for 15-channel data -# define CF_IMAGE_MAX_HEIGHT 0x3fffffff - // 2^30-1 - -# define CF_TILE_SIZE 256 // 256x256 pixel tiles -# define CF_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 - - -// -// Types and structures... -// - -typedef enum cf_iztype_e // **** Image zoom type **** -{ - CF_IZOOM_FAST, // Use nearest-neighbor sampling - CF_IZOOM_NORMAL, // Use bilinear interpolation - CF_IZOOM_BEST // Use bicubic interpolation -} cf_iztype_t; - -struct cf_ic_s; - -typedef struct cf_itile_s // **** Image tile **** -{ - int dirty; // True if tile is dirty - off_t pos; // Position of tile on disk (-1 if not - // written) - struct cf_ic_s *ic; // Pixel data -} cf_itile_t; - -typedef struct cf_ic_s // **** Image tile cache **** -{ - struct cf_ic_s *prev, // Previous tile in cache - *next; // Next tile in cache - cf_itile_t *tile; // Tile this is attached to - cf_ib_t *pixels; // Pixel data -} cf_ic_t; - -struct cf_image_s // **** Image file data **** -{ - cf_icspace_t 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 - cf_itile_t **tiles; // Tiles in image - cf_ic_t *first, // First cached tile in image - *last; // Last cached tile in image - int cachefile; // Tile cache file - char cachename[256]; // Tile cache filename -}; - -struct cf_izoom_s // **** Image zoom data **** -{ - cf_image_t *img; // Image to zoom - cf_iztype_t type; // Type of zooming - unsigned xorig, // X origin - yorig, // Y origin - 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 - cf_ib_t *rows[2], // Horizontally scaled pixel data - *in; // Unscaled input pixel data -}; - - -// -// Prototypes... -// - -extern int _cfImagePutCol(cf_image_t *img, int x, int y, - int height, const cf_ib_t *pixels); -extern int _cfImagePutRow(cf_image_t *img, int x, int y, - int width, const cf_ib_t *pixels); -extern int _cfImageReadJPEG(cf_image_t *img, FILE *fp, - cf_icspace_t primary, - cf_icspace_t secondary, - int saturation, int hue, - const cf_ib_t *lut); -extern int _cfImageReadPNG(cf_image_t *img, FILE *fp, - cf_icspace_t primary, - cf_icspace_t secondary, - int saturation, int hue, - const cf_ib_t *lut); -extern int _cfImageReadTIFF(cf_image_t *img, FILE *fp, - cf_icspace_t primary, - cf_icspace_t secondary, - int saturation, int hue, - const cf_ib_t *lut); -extern void _cfImageZoomDelete(cf_izoom_t *z); -extern void _cfImageZoomFill(cf_izoom_t *z, int iy); -extern cf_izoom_t *_cfImageZoomNew(cf_image_t *img, int xc0, int yc0, - int xc1, int yc1, int xsize, - int ysize, int rotated, - cf_iztype_t type); - -#ifdef HAVE_EXIF -extern int _cfImageReadEXIF(cf_image_t *img, FILE *fp); -#endif - -#endif // !_CUPS_IMAGE_PRIVATE_H_ diff --git a/cupsfilters/image-tiff.c b/cupsfilters/image-tiff.c deleted file mode 100644 index d92cce25f..000000000 --- a/cupsfilters/image-tiff.c +++ /dev/null @@ -1,1722 +0,0 @@ -// -// TIFF file routines for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// _cfImageReadTIFF() - Read a TIFF image file. -// - -// -// Include necessary headers... -// - -#include "image-private.h" - -#ifdef HAVE_LIBTIFF -# include // TIFF image definitions -# include -# include - - -// -// '_cfImageReadTIFF()' - Read a TIFF image file. -// - -int // O - Read status -_cfImageReadTIFF( - cf_image_t *img, // IO - Image - FILE *fp, // I - Image file - cf_icspace_t primary, // I - Primary choice for colorspace - cf_icspace_t secondary, // I - Secondary choice for colorspace - int saturation, // I - Color saturation (%) - int hue, // I - Color hue (degrees) - const cf_ib_t *lut) // I - Lookup table for gamma/brightness -{ - TIFF *tif; // TIFF file - uint32_t width, height; // Size of image - uint16_t photometric, // Colorspace - compression, // Type of compression - orientation, // Orientation - resunit, // Units for resolution - samples, // Number of samples/pixel - bits, // Number of bits/pixel - inkset, // Ink set for color separations - numinks; // Number of inks in set - float xres, // Horizontal resolution - yres; // Vertical resolution - uint16_t *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 - row, // Current row in image - 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? - cf_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) - { - DEBUG_puts("DEBUG: TIFFFdOpen() failed!\n"); - fclose(fp); - return (-1); - } - - if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width)) - { - DEBUG_puts("DEBUG: No image width tag in the file!\n"); - TIFFClose(tif); - fclose(fp); - return (-1); - } - - if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) - { - DEBUG_puts("DEBUG: No image height tag in the file!\n"); - TIFFClose(tif); - fclose(fp); - return (-1); - } - - if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) - { - DEBUG_puts("DEBUG: No photometric tag in the file!\n"); - TIFFClose(tif); - fclose(fp); - return (-1); - } - - if (!TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression)) - { - DEBUG_puts("DEBUG: No compression tag in the file!\n"); - TIFFClose(tif); - fclose(fp); - return (-1); - } - - if (!TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samples)) - samples = 1; - - if (!TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits)) - bits = 1; - - // - // Get the image orientation... - // - - if (!TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation)) - orientation = 0; - - // - // Get the image resolution... - // - - int temp = -1; - -#ifdef HAVE_EXIF - // - // Scan image file for EXIF data - // - - temp = _cfImageReadEXIF(img, fp); -#endif - - // - // Check headers only if EXIF contains no info about ppi - // - - if (temp != 1 && 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 = 200; - img->yppi = 200; - } - - if (img->xppi == 0 || img->yppi == 0) - { - DEBUG_puts("DEBUG: Bad TIFF resolution.\n"); - img->xppi = img->yppi = 128; - } - - DEBUG_printf(("DEBUG: TIFF resolution = %fx%f, units=%d\n", - xres, yres, resunit)); - DEBUG_printf(("DEBUG: Stored resolution = %dx%d PPI\n", - img->xppi, img->yppi)); - } - - // - // See if the image has an alpha channel... - // - - if (samples == 2 || (samples == 4 && photometric == PHOTOMETRIC_RGB)) - alpha = 1; - else - alpha = 0; - - // - // Check the size of the image... - // - - if (width == 0 || width > CF_IMAGE_MAX_WIDTH || - height == 0 || height > CF_IMAGE_MAX_HEIGHT || - (bits != 1 && bits != 2 && bits != 4 && bits != 8) || - samples < 1 || samples > 4) - { - DEBUG_printf(("DEBUG: Bad TIFF dimensions %ux%ux%ux%u!\n", - (unsigned)width, (unsigned)height, (unsigned)bits, - (unsigned)samples)); - TIFFClose(tif); - fclose(fp); - return (1); - } - - // - // Setup the image size and colorspace... - // - - img->xsize = width; - img->ysize = height; - if (photometric == PHOTOMETRIC_MINISBLACK || - photometric == PHOTOMETRIC_MINISWHITE) - img->colorspace = secondary; - else if (photometric == PHOTOMETRIC_SEPARATED && primary == CF_IMAGE_RGB_CMYK) - img->colorspace = CF_IMAGE_CMYK; - else if (primary == CF_IMAGE_RGB_CMYK) - img->colorspace = CF_IMAGE_RGB; - else - img->colorspace = primary; - - DEBUG_printf(("DEBUG: img->colorspace = %d\n", img->colorspace)); - - bpp = cfImageGetDepth(img); - - cfImageSetMaxTiles(img, 0); - - // - // Set the X & Y start and direction according to the image orientation... - // - - switch (orientation) - { - case ORIENTATION_TOPRIGHT : - DEBUG_puts("DEBUG: orientation = top-right\n"); - break; - case ORIENTATION_RIGHTTOP : - DEBUG_puts("DEBUG: orientation = right-top\n"); - break; - default : - case ORIENTATION_TOPLEFT : - DEBUG_puts("DEBUG: orientation = top-left\n"); - break; - case ORIENTATION_LEFTTOP : - DEBUG_puts("DEBUG: orientation = left-top\n"); - break; - case ORIENTATION_BOTLEFT : - DEBUG_puts("DEBUG: orientation = bottom-left\n"); - break; - case ORIENTATION_LEFTBOT : - DEBUG_puts("DEBUG: orientation = left-bottom\n"); - break; - case ORIENTATION_BOTRIGHT : - DEBUG_puts("DEBUG: orientation = bottom-right\n"); - break; - case ORIENTATION_RIGHTBOT : - DEBUG_puts("DEBUG: orientation = right-bottom\n"); - break; - } - - 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... - // - - DEBUG_printf(("DEBUG: photometric = %d\n", photometric)); - DEBUG_printf(("DEBUG: compression = %d\n", compression)); - - switch (photometric) - { - case PHOTOMETRIC_MINISWHITE : - case PHOTOMETRIC_MINISBLACK : - if (photometric == PHOTOMETRIC_MINISWHITE) - { - zero = 255; - one = 0; - } - else - { - zero = 0; - one = 255; - } - - if (orientation < ORIENTATION_LEFTTOP) - { - // - // Row major order... - // - - for (y = ystart, ycount = img->ysize, row = 0; - ycount > 0; - ycount --, y += ydir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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, row, 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, row, 0); - - if (img->colorspace == CF_IMAGE_WHITE) - { - if (lut) - cfImageLut(in, img->xsize, lut); - - _cfImagePutRow(img, 0, y, img->xsize, in); - } - else - { - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_RGB : - cfImageWhiteToRGB(in, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageWhiteToBlack(in, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageWhiteToCMY(in, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageWhiteToCMYK(in, out, img->xsize); - break; - } - - if (lut) - cfImageLut(out, img->xsize * bpp, lut); - - _cfImagePutRow(img, 0, y, img->xsize, out); - } - } - } - else - { - // - // Column major order... - // - - for (x = xstart, xcount = img->xsize, row = 0; - xcount > 0; - xcount --, x += xdir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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, row, 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, row, 0); - - if (img->colorspace == CF_IMAGE_WHITE) - { - if (lut) - cfImageLut(in, img->ysize, lut); - - _cfImagePutCol(img, x, 0, img->ysize, in); - } - else - { - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_RGB : - cfImageWhiteToRGB(in, out, img->ysize); - break; - case CF_IMAGE_BLACK : - cfImageWhiteToBlack(in, out, img->ysize); - break; - case CF_IMAGE_CMY : - cfImageWhiteToCMY(in, out, img->ysize); - break; - case CF_IMAGE_CMYK : - cfImageWhiteToCMYK(in, out, img->ysize); - break; - } - - if (lut) - cfImageLut(out, img->ysize * bpp, lut); - - _cfImagePutCol(img, x, 0, img->ysize, out); - } - } - } - break; - - case PHOTOMETRIC_PALETTE : - if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap)) - { - _TIFFfree(scanline); - free(in); - free(out); - - TIFFClose(tif); - DEBUG_puts("DEBUG: No colormap tag in the file!\n"); - 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, row = 0; - ycount > 0; - ycount --, y += ydir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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, row, 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++]; - } - } - - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_WHITE : - cfImageRGBToWhite(in, out, img->xsize); - break; - case CF_IMAGE_RGB : - cfImageRGBToRGB(in, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(in, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(in, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(in, out, img->xsize); - break; - } - - if (lut) - cfImageLut(out, img->xsize * bpp, lut); - - _cfImagePutRow(img, 0, y, img->xsize, out); - } - } - else - { - // - // Column major order... - // - - for (x = xstart, xcount = img->xsize, row = 0; - xcount > 0; - xcount --, x += xdir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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, row, 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++]; - } - } - - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_WHITE : - cfImageRGBToWhite(in, out, img->ysize); - break; - case CF_IMAGE_RGB : - cfImageRGBToRGB(in, out, img->ysize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(in, out, img->ysize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(in, out, img->ysize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(in, out, img->ysize); - break; - } - - if (lut) - cfImageLut(out, img->ysize * bpp, lut); - - _cfImagePutCol(img, x, 0, img->ysize, out); - } - } - break; - - case PHOTOMETRIC_RGB : - if (orientation < ORIENTATION_LEFTTOP) - { - // - // Row major order... - // - - for (y = ystart, ycount = img->ysize, row = 0; - ycount > 0; - ycount --, y += ydir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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, row, 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, row, 0); - - if ((saturation != 100 || hue != 0) && bpp > 1) - cfImageRGBAdjust(in, img->xsize, saturation, hue); - - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_WHITE : - cfImageRGBToWhite(in, out, img->xsize); - break; - case CF_IMAGE_RGB : - cfImageRGBToRGB(in, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(in, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(in, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(in, out, img->xsize); - break; - } - - if (lut) - cfImageLut(out, img->xsize * bpp, lut); - - _cfImagePutRow(img, 0, y, img->xsize, out); - } - } - else - { - // - // Column major order... - // - - for (x = xstart, xcount = img->xsize, row = 0; - xcount > 0; - xcount --, x += xdir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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, row, 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, row, 0); - - if ((saturation != 100 || hue != 0) && bpp > 1) - cfImageRGBAdjust(in, img->ysize, saturation, hue); - - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_WHITE : - cfImageRGBToWhite(in, out, img->ysize); - break; - case CF_IMAGE_RGB : - cfImageRGBToRGB(in, out, img->ysize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(in, out, img->ysize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(in, out, img->ysize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(in, out, img->ysize); - break; - } - - if (lut) - cfImageLut(out, img->ysize * bpp, lut); - - _cfImagePutCol(img, x, 0, img->ysize, out); - } - } - break; - - case PHOTOMETRIC_SEPARATED : - inkset = INKSET_CMYK; - numinks = 4; - -#ifdef TIFFTAG_NUMBEROFINKS - if (!TIFFGetField(tif, TIFFTAG_INKSET, &inkset) && - !TIFFGetField(tif, TIFFTAG_NUMBEROFINKS, &numinks)) -#else - if (!TIFFGetField(tif, TIFFTAG_INKSET, &inkset)) -#endif // TIFFTAG_NUMBEROFINKS - { - DEBUG_puts("WARNING: No inkset or number-of-inks tag in the file!\n"); - } - - if (inkset == INKSET_CMYK || numinks == 4) - { - if (orientation < ORIENTATION_LEFTTOP) - { - // - // Row major order... - // - - for (y = ystart, ycount = img->ysize, row = 0; - ycount > 0; - ycount --, y += ydir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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 if (img->colorspace == CF_IMAGE_CMYK) - { - TIFFReadScanline(tif, scanline, row, 0); - _cfImagePutRow(img, 0, y, img->xsize, scanline); - } - else - { - TIFFReadScanline(tif, scanline, row, 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) - cfImageRGBAdjust(in, img->xsize, saturation, hue); - - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_WHITE : - cfImageRGBToWhite(in, out, img->xsize); - break; - case CF_IMAGE_RGB : - cfImageRGBToRGB(in, out, img->xsize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(in, out, img->xsize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(in, out, img->xsize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(in, out, img->xsize); - break; - } - - if (lut) - cfImageLut(out, img->xsize * 3, lut); - - _cfImagePutRow(img, 0, y, img->xsize, out); - } - } - else - { - // - // Column major order... - // - - for (x = xstart, xcount = img->xsize, row = 0; - xcount > 0; - xcount --, x += xdir, row ++) - { - if (bits == 1) - { - TIFFReadScanline(tif, scanline, row, 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, row, 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, row, 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 if (img->colorspace == CF_IMAGE_CMYK) - { - TIFFReadScanline(tif, scanline, row, 0); - _cfImagePutCol(img, x, 0, img->ysize, scanline); - } - else - { - TIFFReadScanline(tif, scanline, row, 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) - cfImageRGBAdjust(in, img->ysize, saturation, hue); - - switch (img->colorspace) - { - default : - break; - - case CF_IMAGE_WHITE : - cfImageRGBToWhite(in, out, img->ysize); - break; - case CF_IMAGE_RGB : - cfImageRGBToRGB(in, out, img->ysize); - break; - case CF_IMAGE_BLACK : - cfImageRGBToBlack(in, out, img->ysize); - break; - case CF_IMAGE_CMY : - cfImageRGBToCMY(in, out, img->ysize); - break; - case CF_IMAGE_CMYK : - cfImageRGBToCMYK(in, out, img->ysize); - break; - } - - if (lut) - cfImageLut(out, img->ysize * bpp, lut); - - _cfImagePutCol(img, x, 0, img->ysize, out); - } - } - - break; - } - - default : - _TIFFfree(scanline); - free(in); - free(out); - - TIFFClose(tif); - DEBUG_puts("DEBUG: Unknown TIFF photometric value!\n"); - return (-1); - } - - // - // Free temporary buffers, close the TIFF file, and return. - // - - _TIFFfree(scanline); - free(in); - free(out); - - TIFFClose(tif); - return (0); -} -#endif // HAVE_LIBTIFF diff --git a/cupsfilters/image-zoom.c b/cupsfilters/image-zoom.c deleted file mode 100644 index ca1b6823b..000000000 --- a/cupsfilters/image-zoom.c +++ /dev/null @@ -1,349 +0,0 @@ -// -// Image zoom routines for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// _cfImageZoomDelete() - Free a zoom record... -// _cfImageZoomFill() - Fill a zoom record... -// _cfImageZoomNew() - Allocate a pixel zoom record... -// zoom_bilinear() - Fill a zoom record with image data utilizing -// bilinear interpolation. -// zoom_nearest() - Fill a zoom record quickly using nearest-neighbor -// sampling. - -// -// Include necessary headers... -// - -#include "image-private.h" - - -// -// Local functions... -// - -static void zoom_bilinear(cf_izoom_t *z, int iy); -static void zoom_nearest(cf_izoom_t *z, int iy); - - -// -// '_cfImageZoomDelete()' - Free a zoom record... -// - -void -_cfImageZoomDelete(cf_izoom_t *z) // I - Zoom record to free -{ - free(z->rows[0]); - free(z->rows[1]); - free(z->in); - free(z); -} - - -// -// '_cfImageZoomFill()' - Fill a zoom record with image data utilizing bilinear -// interpolation. -// - -void -_cfImageZoomFill(cf_izoom_t *z, // I - Zoom record to fill - int iy) // I - Zoom image row -{ - switch (z->type) - { - case CF_IZOOM_FAST : - zoom_nearest(z, iy); - break; - - default : - zoom_bilinear(z, iy); - break; - } -} - - -// -// '_cfImageZoomNew()' - Allocate a pixel zoom record... -// - -cf_izoom_t * -_cfImageZoomNew( - cf_image_t *img, // I - Image to zoom - int xc0, // I - Upper-lefthand corner - int yc0, // I - ... - int xc1, // I - Lower-righthand corner - int yc1, // 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 - // degrees - cf_iztype_t type) // I - Zoom type -{ - cf_izoom_t *z; // New zoom record - int flip; // Flip on X axis? - - - if (xsize > CF_IMAGE_MAX_WIDTH || - ysize > CF_IMAGE_MAX_HEIGHT || - (xc1 - xc0) > CF_IMAGE_MAX_WIDTH || - (yc1 - yc0) > CF_IMAGE_MAX_HEIGHT) - return (NULL); // Protect against integer overflow - - if ((z = (cf_izoom_t *)calloc(1, sizeof(cf_izoom_t))) == NULL) - return (NULL); - - z->img = img; - z->row = 0; - z->depth = cfImageGetDepth(img); - z->rotated = rotated; - z->type = type; - - if (xsize < 0) - { - flip = 1; - xsize = -xsize; - } - else - { - flip = 0; - } - - if (rotated) - { - z->xorig = xc1; - z->yorig = yc0; - z->width = yc1 - yc0 + 1; - z->height = xc1 - xc0 + 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; // z->xincr is always 1 - - 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 = xc0; - z->yorig = yc0; - z->width = xc1 - xc0 + 1; - z->height = yc1 - yc0 + 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; // z->xincr is always 1 - - 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 (flip) - { - z->instep = -z->instep; - z->inincr = -z->inincr; - } - - if ((z->rows[0] = (cf_ib_t *)malloc(z->xsize * z->depth)) == NULL) - { - free(z); - return (NULL); - } - - if ((z->rows[1] = (cf_ib_t *)malloc(z->xsize * z->depth)) == NULL) - { - free(z->rows[0]); - free(z); - return (NULL); - } - - if ((z->in = (cf_ib_t *)malloc(z->width * z->depth)) == NULL) - { - free(z->rows[0]); - free(z->rows[1]); - free(z); - return (NULL); - } - - return (z); -} - - -// -// 'zoom_bilinear()' - Fill a zoom record with image data utilizing bilinear -// interpolation. -// - -static void -zoom_bilinear(cf_izoom_t *z, // I - Zoom record to fill - int iy) // I - Zoom image row -{ - cf_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) - cfImageGetCol(z->img, z->xorig - iy, z->yorig, z->width, z->in); - else - cfImageGetRow(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; - } - } -} - - -// -// 'zoom_nearest()' - Fill a zoom record quickly using nearest-neighbor -// sampling. -// - -static void -zoom_nearest(cf_izoom_t *z, // I - Zoom record to fill - int iy) // I - Zoom image row -{ - cf_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) - cfImageGetCol(z->img, z->xorig - iy, z->yorig, z->width, z->in); - else - cfImageGetRow(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; - } - } -} diff --git a/cupsfilters/image.c b/cupsfilters/image.c deleted file mode 100644 index 76d1d8ca8..000000000 --- a/cupsfilters/image.c +++ /dev/null @@ -1,1004 +0,0 @@ -// -// Base image support for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfImageClose() - Close an image file. -// cfImageGetCol() - Get a column of pixels from an image. -// cfImageGetColorSpace() - Get the image colorspace. -// cfImageGetDepth() - Get the number of bytes per pixel. -// cfImageGetHeight() - Get the height of an image. -// cfImageGetRow() - Get a row of pixels from an image. -// cfImageGetWidth() - Get the width of an image. -// cfImageGetXPPI() - Get the horizontal resolution of an image. -// cfImageGetYPPI() - Get the vertical resolution of an image. -// cfImageOpen() - Open an image file and read it into memory. -// _cfImagePutCol() - Put a column of pixels to an image. -// _cfImagePutRow() - Put a row of pixels to an image. -// cfImageSetMaxTiles() - Set the maximum number of tiles to cache. -// cfImageCrop() - Crop an image. -// flush_tile() - Flush the least-recently-used tile in the cache. -// get_tile() - Get a cached tile. -// _cfImageReadEXIF() - to read exif metadata of images -// trim_spaces() - helper function to extract results from string -// returned by exif library functions -// find_bytes() - creates character array from image file, to make -// use in exif library functions - - -// -// Include necessary headers... -// - - -#include "image-private.h" - - -// -// Local functions... -// - -static int flush_tile(cf_image_t *img); -static cf_ib_t *get_tile(cf_image_t *img, int x, int y); -static void trim_spaces(char *buf); -static unsigned char *find_bytes(FILE *fp, long int *size); - -// -// 'cfImageClose()' - Close an image file. -// - -void -cfImageClose(cf_image_t *img) // I - Image to close -{ - cf_ic_t *current, // Current cached tile - *next; // Next cached tile - - - // - // Wipe the tile cache file (if any)... - // - - if (img->cachefile >= 0) - { - DEBUG_printf(("Closing/removing swap file \"%s\"...\n", img->cachename)); - - close(img->cachefile); - unlink(img->cachename); - } - - // - // Free the image cache... - // - - DEBUG_puts("Freeing memory..."); - - for (current = img->first, next = NULL; current != NULL; current = next) - { - DEBUG_printf(("Freeing cache (%p, next = %p)...\n", current, next)); - - next = current->next; - free(current); - } - - // - // Free the rest of memory... - // - - if (img->tiles != NULL) - { - DEBUG_printf(("Freeing tiles (%p)...\n", img->tiles[0])); - - free(img->tiles[0]); - - DEBUG_printf(("Freeing tile pointers (%p)...\n", img->tiles)); - - free(img->tiles); - } - - free(img); -} - - -// -// 'cfImageGetCol()' - Get a column of pixels from an image. -// - -int // O - -1 on error, 0 on success -cfImageGetCol(cf_image_t *img, // I - Image - int x, // I - Column - int y, // I - Start row - int height, // I - Column height - cf_ib_t *pixels) // O - Pixel data -{ - int bpp, // Bytes per pixel - twidth, // Tile width - count; // Number of pixels to get - const cf_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 = cfImageGetDepth(img); - twidth = bpp * (CF_TILE_SIZE - 1); - - while (height > 0) - { - ib = get_tile(img, x, y); - - if (ib == NULL) - return (-1); - - count = CF_TILE_SIZE - (y & (CF_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); -} - - -// -// 'cfImageGetColorSpace()' - Get the image colorspace. -// - -cf_icspace_t // O - Colorspace -cfImageGetColorSpace( - cf_image_t *img) // I - Image -{ - return (img->colorspace); -} - - -// -// 'cfImageGetDepth()' - Get the number of bytes per pixel. -// - -int // O - Bytes per pixel -cfImageGetDepth(cf_image_t *img) // I - Image -{ - return (abs(img->colorspace)); -} - - -// -// 'cfImageGetHeight()' - Get the height of an image. -// - -unsigned // O - Height in pixels -cfImageGetHeight(cf_image_t *img) // I - Image -{ - return (img->ysize); -} - - -// -// 'cfImageGetRow()' - Get a row of pixels from an image. -// - -int // O - -1 on error, 0 on success -cfImageGetRow(cf_image_t *img, // I - Image - int x, // I - Start column - int y, // I - Row - int width, // I - Width of row - cf_ib_t *pixels) // O - Pixel data -{ - int bpp, // Bytes per pixel - count; // Number of pixels to get - const cf_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 = CF_TILE_SIZE - (x & (CF_TILE_SIZE - 1)); - if (count > width) - count = width; - memcpy(pixels, ib, count * bpp); - pixels += count * bpp; - x += count; - width -= count; - } - - return (0); -} - - -// -// 'cfImageGetWidth()' - Get the width of an image. -// - -unsigned // O - Width in pixels -cfImageGetWidth(cf_image_t *img) // I - Image -{ - return (img->xsize); -} - - -// -// 'cfImageGetXPPI()' - Get the horizontal resolution of an image. -// - -unsigned // O - Horizontal PPI -cfImageGetXPPI(cf_image_t *img) // I - Image -{ - return (img->xppi); -} - - -// -// 'cfImageGetYPPI()' - Get the vertical resolution of an image. -// - -unsigned // O - Vertical PPI -cfImageGetYPPI(cf_image_t *img) // I - Image -{ - return (img->yppi); -} - - -// -// 'cfImageOpen()' - Open an image file and read it into memory. -// - -cf_image_t * // O - New image -cfImageOpen( - const char *filename, // I - Filename of image - cf_icspace_t primary, // I - Primary colorspace needed - cf_icspace_t secondary, // I - Secondary colorspace if primary - // no good - int saturation, // I - Color saturation level - int hue, // I - Color hue adjustment - const cf_ib_t *lut) // I - RGB gamma/brightness LUT -{ - FILE *fp; // File pointer - - DEBUG_printf(("cfImageOpen(\"%s\", %d, %d, %d, %d, %p)\n", - filename ? filename : "(null)", primary, secondary, - saturation, hue, lut)); - - if ((fp = fopen(filename, "r")) == NULL) - return (NULL); - - return (cfImageOpenFP(fp, primary, secondary, saturation, hue, lut)); -} - - -// -// 'cfImageOpenFP()' - Open an image file and read it into memory. -// - -cf_image_t * // O - New image -cfImageOpenFP( - FILE *fp, // I - File pointer of image - cf_icspace_t primary, // I - Primary colorspace needed - cf_icspace_t secondary, // I - Secondary colorspace if primary - // no good - int saturation, // I - Color saturation level - int hue, // I - Color hue adjustment - const cf_ib_t *lut) // I - RGB gamma/brightness LUT -{ - unsigned char header[16], // First 16 bytes of file - header2[16]; // Bytes 2048-2064 (PhotoCD) - cf_image_t *img; // New image buffer - int status; // Status of load... - - - DEBUG_printf(("cfImageOpen2(%p, %d, %d, %d, %d, %p)\n", - fp, primary, secondary, saturation, hue, lut)); - - // - // Figure out the file type... - // - - if (fp == 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)); - if (fread(header2, 1, sizeof(header2), fp) == 0 && ferror(fp)) - DEBUG_printf(("Error reading file!")); - fseek(fp, 0, SEEK_SET); - - // - // Allocate memory... - // - - img = calloc(sizeof(cf_image_t), 1); - - if (img == NULL) - { - fclose(fp); - return (NULL); - } - - // - // Load the image as appropriate... - // - - img->cachefile = -1; - img->max_ics = CF_TILE_MINIMUM; - img->xppi = 200; - img->yppi = 200; - -#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) - if (!memcmp(header, "\211PNG", 4)) - status = _cfImageReadPNG(img, fp, primary, secondary, saturation, hue, - lut); - else -#endif // HAVE_LIBPNG && HAVE_LIBZ -#ifdef HAVE_LIBJPEG - if (!memcmp(header, "\377\330\377", 3) && // Start-of-Image - header[3] >= 0xe0 && header[3] <= 0xef) // APPn - status = _cfImageReadJPEG(img, fp, primary, secondary, saturation, hue, - lut); - else -#endif // HAVE_LIBJPEG -#ifdef HAVE_LIBTIFF - if (!memcmp(header, "MM\000\052", 4) || - !memcmp(header, "II\052\000", 4)) - status = _cfImageReadTIFF(img, fp, primary, secondary, saturation, hue, - lut); - else -#endif // HAVE_LIBTIFF - { - fclose(fp); - status = -1; - } - - if (status) - { - free(img); - return (NULL); - } - else - return (img); -} - - -// -// '_cfImagePutCol()' - Put a column of pixels to an image. -// - -int // O - -1 on error, 0 on success -_cfImagePutCol( - cf_image_t *img, // I - Image - int x, // I - Column - int y, // I - Start row - int height, // I - Column height - const cf_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 - cf_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 = cfImageGetDepth(img); - twidth = bpp * (CF_TILE_SIZE - 1); - tilex = x / CF_TILE_SIZE; - tiley = y / CF_TILE_SIZE; - - while (height > 0) - { - ib = get_tile(img, x, y); - - if (ib == NULL) - return (-1); - - img->tiles[tiley][tilex].dirty = 1; - tiley ++; - - count = CF_TILE_SIZE - (y & (CF_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); -} - - -// -// '_cfImagePutRow()' - Put a row of pixels to an image. -// - -int // O - -1 on error, 0 on success -_cfImagePutRow( - cf_image_t *img, // I - Image - int x, // I - Start column - int y, // I - Row - int width, // I - Row width - const cf_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 - cf_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 / CF_TILE_SIZE; - tiley = y / CF_TILE_SIZE; - - while (width > 0) - { - ib = get_tile(img, x, y); - - if (ib == NULL) - return (-1); - - img->tiles[tiley][tilex].dirty = 1; - - count = CF_TILE_SIZE - (x & (CF_TILE_SIZE - 1)); - if (count > width) - count = width; - memcpy(ib, pixels, count * bpp); - pixels += count * bpp; - x += count; - width -= count; - tilex ++; - } - - return (0); -} - - -// -// 'cfImageSetMaxTiles()' - 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 -cfImageSetMaxTiles( - cf_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(CF_TILE_MINIMUM, - 1 + max((img->xsize + CF_TILE_SIZE - 1) / CF_TILE_SIZE, - (img->ysize + CF_TILE_SIZE - 1) / CF_TILE_SIZE)); - - if (max_tiles == 0) - max_tiles = ((img->xsize + CF_TILE_SIZE - 1) / CF_TILE_SIZE) * - ((img->ysize + CF_TILE_SIZE - 1) / CF_TILE_SIZE); - - cache_size = max_tiles * CF_TILE_SIZE * CF_TILE_SIZE * - cfImageGetDepth(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 * CF_TILE_SIZE * CF_TILE_SIZE; - break; - case 2 : - if (tolower(cache_units[0] & 255) == 'g') - max_size *= 1024 * 1024 * 1024; - else if (tolower(cache_units[0] & 255) == 'm') - max_size *= 1024 * 1024; - else if (tolower(cache_units[0] & 255) == 'k') - max_size *= 1024; - else if (tolower(cache_units[0] & 255) == 't') - max_size *= 4 * CF_TILE_SIZE * CF_TILE_SIZE; - break; - } - } - else - max_size = 32 * 1024 * 1024; - - if (cache_size > max_size) - max_tiles = max_size / CF_TILE_SIZE / CF_TILE_SIZE / - cfImageGetDepth(img); - - if (max_tiles < min_tiles) - max_tiles = min_tiles; - - img->max_ics = max_tiles; - - DEBUG_printf(("max_ics=%d...\n", img->max_ics)); -} - - -// -// 'cfImageCrop()' - Crop an image. -// (posw, posh): Position of left corner -// (width, height): Width and height of required image. - -cf_image_t* -cfImageCrop(cf_image_t* img, - int posw, - int posh, - int width, - int height) -{ - int image_width = cfImageGetWidth(img); - cf_image_t* temp = calloc(sizeof(cf_image_t), 1); - cf_ib_t *pixels = (cf_ib_t*)malloc(img->xsize * cfImageGetDepth(img)); - - temp->cachefile = -1; - temp->max_ics = CF_TILE_MINIMUM; - temp->colorspace = img->colorspace; - temp->xppi = img->xppi; - temp->yppi = img->yppi; - temp->num_ics = 0; - temp->first = temp->last = NULL; - temp->tiles = NULL; - temp->xsize = width; - temp->ysize = height; - - for (int i = posh; i < min(cfImageGetHeight(img), posh + height); i ++) - { - cfImageGetRow(img, posw, i, min(width, image_width - posw), pixels); - _cfImagePutRow(temp, 0, i - posh, min(width, image_width - posw), pixels); - } - - free(pixels); - - return (temp); -} - - -// -// 'flush_tile()' - Flush the least-recently-used tile in the cache. -// - -static int -flush_tile(cf_image_t *img) // I - Image -{ - int bpp; // Bytes per pixel - cf_itile_t *tile; // Pointer to tile - - - bpp = cfImageGetDepth(img); - if(img == NULL || img->first == NULL || img->first->tile == NULL) - return (-1); - - tile = img->first->tile; - - if (!tile->dirty) - { - tile->ic = NULL; - return (0); - } - - if (img->cachefile < 0) - { - if ((img->cachefile = cupsTempFd(img->cachename, - sizeof(img->cachename))) < 0) - { - tile->ic = NULL; - tile->dirty = 0; - return (0); - } - - DEBUG_printf(("Created swap file \"%s\"...\n", img->cachename)); - } - - if (tile->pos >= 0) - { - if (lseek(img->cachefile, tile->pos, SEEK_SET) != tile->pos) - { - tile->ic = NULL; - tile->dirty = 0; - return (0); - } - } - else - { - if ((tile->pos = lseek(img->cachefile, 0, SEEK_END)) < 0) - { - tile->ic = NULL; - tile->dirty = 0; - return (0); - } - } - - if (write(img->cachefile, tile->ic->pixels, - bpp * CF_TILE_SIZE * CF_TILE_SIZE) == -1) - DEBUG_printf(("Error writing cache tile!")); - - tile->ic = NULL; - tile->dirty = 0; - return (0); -} - - -// -// 'get_tile()' - Get a cached tile. -// - -static cf_ib_t * // O - Pointer to tile or NULL -get_tile(cf_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 - cf_ic_t *ic; // Cache pointer - cf_itile_t *tile; // Tile pointer - - - if (img->tiles == NULL) - { - xtiles = (img->xsize + CF_TILE_SIZE - 1) / CF_TILE_SIZE; - ytiles = (img->ysize + CF_TILE_SIZE - 1) / CF_TILE_SIZE; - - DEBUG_printf(("Creating tile array (%dx%d)\n", xtiles, ytiles)); - - if ((img->tiles = calloc(sizeof(cf_itile_t *), ytiles)) == NULL) - return (NULL); - - if ((tile = calloc(xtiles * sizeof(cf_itile_t), ytiles)) == NULL) - return (NULL); - - for (tiley = 0; tiley < ytiles; tiley ++) - { - img->tiles[tiley] = tile; - for (tilex = xtiles; tilex > 0; tilex --, tile ++) - tile->pos = -1; - } - } - - bpp = cfImageGetDepth(img); - tilex = x / CF_TILE_SIZE; - tiley = y / CF_TILE_SIZE; - tile = img->tiles[tiley] + tilex; - x &= (CF_TILE_SIZE - 1); - y &= (CF_TILE_SIZE - 1); - - if ((ic = tile->ic) == NULL) - { - if (img->num_ics < img->max_ics) - { - if ((ic = calloc(sizeof(cf_ic_t) + - bpp * CF_TILE_SIZE * CF_TILE_SIZE, 1)) == NULL) - { - if (img->num_ics == 0) - return (NULL); - - flush_tile(img); - ic = img->first; - } - else - { - ic->pixels = ((cf_ib_t *)ic) + sizeof(cf_ic_t); - - img->num_ics ++; - - DEBUG_printf(("Allocated cache tile %d (%p)...\n", img->num_ics, ic)); - } - } - else - { - DEBUG_printf(("Flushing old cache tile (%p)...\n", img->first)); - - int res = flush_tile(img); - if(res) - { - return NULL; - } - ic = img->first; - } - - ic->tile = tile; - tile->ic = ic; - - if (tile->pos >= 0) - { - DEBUG_printf(("Loading cache tile from file position " CUPS_LLFMT "...\n", - CUPS_LLCAST tile->pos)); - - lseek(img->cachefile, tile->pos, SEEK_SET); - if (read(img->cachefile, ic->pixels, - bpp * CF_TILE_SIZE * CF_TILE_SIZE) == -1) - DEBUG_printf(("Error reading cache tile!")); - } - else - { - DEBUG_puts("Clearing cache tile..."); - - memset(ic->pixels, 0, bpp * CF_TILE_SIZE * CF_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 * CF_TILE_SIZE + x)); -} - - -#ifdef HAVE_EXIF -// -// Helper function required by EXIF read function -// - -static void trim_spaces(char *buf) -{ - char *s = buf - 1; - for (; *buf; ++buf) - { - if (*buf != ' ') - s = buf; - } - *++s = 0; // null terminate the string on the first of the final spaces -} - - -// -// Helper function to extract bytes from image files -// - -static unsigned char * -find_bytes(FILE *fp, - long int *size) -{ - unsigned char *buf; - - long int originalOffset = ftell(fp); - fseek(fp, 0L, SEEK_END); - - // calculating the size of the file - long int res = ftell(fp); - - buf = (unsigned char *)malloc(res * sizeof(unsigned char) + 1); - fseek(fp, 0, SEEK_SET); - - if (fread(buf, 1, res, fp) < res) - { - free(buf); - buf = NULL; - *size = 0; - } - else - *size = res + 1; - - fseek(fp, originalOffset, SEEK_SET); - - return (buf); -} - - -// -// Implementation for EXIF read function -// - -int -_cfImageReadEXIF(cf_image_t *img, - FILE *fp) -{ - - if (fp == NULL) - { - return -1; - } - - long int bufSize = 0; - - unsigned char *buf = find_bytes(fp, &bufSize); - - ExifData *ed = NULL; - - if (buf == NULL || bufSize <= 0 || - (ed = exif_data_new_from_data(buf, bufSize)) == NULL) - { - DEBUG_printf(("DEBUG: No EXIF data found")); - return (2); - } - - ExifIfd ifd = EXIF_IFD_0; - ExifTag tagX = EXIF_TAG_X_RESOLUTION; - ExifTag tagY = EXIF_TAG_Y_RESOLUTION; - - ExifEntry *entryX = exif_content_get_entry(ed->ifd[ifd], tagX); - - ExifEntry *entryY = exif_content_get_entry(ed->ifd[ifd], tagY); - - if (entryX == NULL || entryY == NULL) - { - DEBUG_printf(("DEBUG: No EXIF data found")); - return (2); - } - - if (entryX) - { - char buf1[1024]; - - exif_entry_get_value(entryX, buf1, sizeof(buf1)); - - trim_spaces(buf1); - - if (*buf1) - { - int xRes; - sscanf(buf1, "%d", &xRes); - img->xppi = xRes; - } - else - { - free(buf); - return (2); - } - } - - if (entryY) - { - char buf2[1024]; - - exif_entry_get_value(entryY, buf2, sizeof(buf2)); - trim_spaces(buf2); - - if (*buf2) - { - int yRes; - sscanf(buf2, "%d", &yRes); - img->yppi = yRes; - } - else{ - free(buf); - return (2); - } - } - - free(buf); - return (1); -} -#endif // HAVE_EXIF diff --git a/cupsfilters/image.h b/cupsfilters/image.h deleted file mode 100644 index cb558a782..000000000 --- a/cupsfilters/image.h +++ /dev/null @@ -1,126 +0,0 @@ -// -// Image library definitions for libcupsilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#ifndef _CUPS_FILTERS_IMAGE_H_ -# define _CUPS_FILTERS_IMAGE_H_ - - -// -// Include necessary headers... -// - -# include -# include - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Constants... -// - -typedef enum cf_icspace_e // **** Image colorspaces **** -{ - CF_IMAGE_CMYK = -4, // Cyan, magenta, yellow, and black - CF_IMAGE_CMY = -3, // Cyan, magenta, and yellow - CF_IMAGE_BLACK = -1, // Black - CF_IMAGE_WHITE = 1, // White (luminance) - CF_IMAGE_RGB = 3, // Red, green, and blue - CF_IMAGE_RGB_CMYK = 4 // Use RGB or CMYK -} cf_icspace_t; - - -// -// Types and structures... -// - -typedef unsigned char cf_ib_t; // **** Image byte **** - -struct cf_image_s; -typedef struct cf_image_s cf_image_t; // **** Image file data **** - -struct cf_izoom_s; -typedef struct cf_izoom_s cf_izoom_t; // **** Image zoom data **** - - -// -// Prototypes... -// - -extern void cfImageClose(cf_image_t *img); -extern void cfImageCMYKToBlack(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageCMYKToCMY(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageCMYKToCMYK(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageCMYKToRGB(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageCMYKToWhite(const cf_ib_t *in, - cf_ib_t *out, int count); -extern int cfImageGetCol(cf_image_t *img, int x, int y, - int height, cf_ib_t *pixels); -extern cf_icspace_t cfImageGetColorSpace(cf_image_t *img); -extern int cfImageGetDepth(cf_image_t *img); -extern unsigned cfImageGetHeight(cf_image_t *img); -extern int cfImageGetRow(cf_image_t *img, int x, int y, - int width, cf_ib_t *pixels); -extern unsigned cfImageGetWidth(cf_image_t *img); -extern unsigned cfImageGetXPPI(cf_image_t *img); -extern unsigned cfImageGetYPPI(cf_image_t *img); -extern void cfImageLut(cf_ib_t *pixels, int count, - const cf_ib_t *lut); -extern cf_image_t *cfImageOpen(const char *filename, - cf_icspace_t primary, - cf_icspace_t secondary, - int saturation, int hue, - const cf_ib_t *lut); -extern cf_image_t *cfImageOpenFP(FILE *fp, - cf_icspace_t primary, - cf_icspace_t secondary, - int saturation, int hue, - const cf_ib_t *lut); -extern void cfImageRGBAdjust(cf_ib_t *pixels, int count, - int saturation, int hue); -extern void cfImageRGBToBlack(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageRGBToCMY(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageRGBToCMYK(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageRGBToRGB(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageRGBToWhite(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageSetMaxTiles(cf_image_t *img, int max_tiles); -extern void cfImageSetProfile(float d, float g, - float matrix[3][3]); -extern void cfImageSetRasterColorSpace(cups_cspace_t cs); -extern void cfImageWhiteToBlack(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageWhiteToCMY(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageWhiteToCMYK(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageWhiteToRGB(const cf_ib_t *in, - cf_ib_t *out, int count); -extern void cfImageWhiteToWhite(const cf_ib_t *in, - cf_ib_t *out, int count); -extern cf_image_t* cfImageCrop(cf_image_t* img,int posw, - int posh,int width,int height); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_IMAGE_H_ diff --git a/cupsfilters/image.pgm b/cupsfilters/image.pgm deleted file mode 100644 index c0b7a22f7feb992bb441fb93b9a88a7da6341857..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 152380 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

>3N@phAhq?B6*q1)Hxn3iDh1gIL8UU- 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

dy?XcE*B?&rA3q)@ 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 R>gwv^^aR`ZhrRWy{|C9b3Z?)6 diff --git a/cupsfilters/image.ppm b/cupsfilters/image.ppm deleted file mode 100644 index 3823fe48b0ef0e6342cd8c3c13e7bb1d26e79ff1..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 457020 zc-rjS<#VHXo9K7XG`ibvyW7mn%nUM07Bg6q#S9i$l5JVCEn8-knVFffnWtx->6r(1 z1Dj-%Y?4i~NjAwQ*(6)%?z>NI&a3?gqHq1mb+-&%WtTs`E&(R@k>=lan*p6EGJrS6A1kV9&tZz}(&4J-|G{yuiG@y?wxZ z!TiAd{rv;L0>Og7f`fxYz(T>oz{0^IA|fKeqQIiTVq#)q!Q#N;!4eV@62X$dlEG3^ zQc}Uvz|z4oGBPs3vcR&za&mHV!ScZJ!3qir3c-rNioqZdNC{XeSQ%J(d3gm`B^VS8 z23A#71qVZbA;C~66dDWzh6TgnaCk5R7!iy_B9Xx;U{o*~jaCgt2V;OSnamn678o0h z!{Knjcwl@mfj}Sx6M>1rBoc`fOa>+gQ-CRz%33fLm>Nu@(P+Wyz;s}Gz1{#;57q$I z*w|AuokdZFpI^~2G$N{1?%YO=mhHm>jvxT>FEXQ1M3GH7#J7?8v+{! zvw@9_jEsVffsKPrOiWCIO@U2=&CJZqg3W==gDos9EP^e8ErYGBtgM2qfvtmWY;0_T zZGmlr?d?g1({Tpf&C8lN3cKr z=}&(K`wQ4#f&KMg|MhPyPtMH?S9(*wEJoI)9$C;PrILXKka_n{j~dO_tWmD z-A}upc0cWY+WoZqY4_9ar`=DxpLRd(e%k%C`)T*n?x)>PyPtMH?S9(*wEJoI)9$C; zPrILXKka_n{j~dO_tWmD-A}upc0cWY+WoZqY4_9ar`=DxpLRd(e%k%C`)T*n?x)>P zyPtMH?S9(*wEJoI)9$C;PrILXKka_n{j~e}|7SmqQzmvRpJC?lta7fkRx{Szw9u;< zFxK|fi(BQKx*AR`nXkYzIAsWAVnMD?M%;tU_=jl$pDhLQTF$yS}sEbY`X8+cR|m83wib!e@X)G|P8 z?Zel1Rx-8m6m>LS5rtL6VU;N;d1|F7t%Q?;k`@$D;^Im?qjO!;As!{D;3`}g78`^v zb0Wi>s3=zo#s!agjKMjgNsd_BW2VHLE^=o|+$Gw8dUKT75^pp|8?|8#bk913leGM< z4E8{Re9T8U3Q4Xqjvs^S%H(*mq(L>cp>$PfwI-t45KV7N#5E?-ta+^NLUwndsJ~P? zP)={otdvGT<*~)W=yX;@e05YDJt3Z(lP)XIQKKs?yfWalb1S;J<=u>;c6>@hX{;(I zM39hWC`5M?XoGZGH;vLlBsO4ibws?5gQ(>Yv|>S-G7QRolko@XEyG*241H!P@2N z*6roqtJ{M&w+C;p_upLYzB=1}alCZ4wRFC*^XlmMCMa0)$^gH z^TE|C+s^ChlP?=rza3ouc5?Cc_^=fbR+5ls9k}^L|n6^>o#+WNJ+_ibu;v{X_Rx>vyUYpk) z?=@eax7=Jb9PP_jm-sW2T-#{v>_XG-e#iNx<>W%Mw#ghB!*q6$28S5q6R>t`rbe4n zU!PW|O;KwrtX8URgxJ@M=x8Z#t1oJ;Ewl*ByLiX}Hntas?<^s=rZUapq89(!W)H2= zNnL+W*5F|3@#-3kZf=e=sKS~xNxe<^wl+w!HeOd7&K3BeC{K$q&x*-m2w@??fGbrY z@|3VbHN3153o)QF6p$22K}Ky^fgV+5Wg)smSvqPY4;jtJr>i(c24ysz5>6s}LlKVI z#m-r!{$-f3N^(dkI;glZpddf6FeSP?DFvO7hKo#qdxe%e2UR`ugL#MJ;!-$e=^|__ z4;8^hM2nE&f|7V;MT!=du0v<$7(7R9382=c}aY5=JZ=5kk%P!6msN!=EAo zeTfl4v{*k<>=S6HBP9G;Rf-QL&j(WwO05W`mW5YWMAtxLIk2djvLJG<7bXe##9&&w z7dFNl7Zbrs53WuKV>gY$0U?^B^J3A!h;aRNHj45gYhRooobMeImD+N%2Oug36<LN4^v5LAVl_Zo|_yaZG0Tbash;tyNI^feB2zd_FvOCnO zdoHg)=0O-8*|DHGL5IAo=OtdlT} z5>c1VFx+bC2j;e>`sS9#ep~PK`t-)x=G^{#&rDZSuin&K*EwSB7-=x|sC2CogH_o& z(AYWFZtAxv%z9CyUSVldclVno=Gzyx&C}}*;2cy58plq{M>tex%2XL z^Y(D@baUZ!ZTscn(TB5t^%Jo1=;JYP4nG_L*MOn`p_hNS0tf}^o6)o;H~Gnsqzfij6_xnvK0?tJxS~Zx6F~Mrq3vSNr@?(k`z8FngI)`E)8du1=Zw72=XI^`O$)+6d9&K$1l<hHU?x<*2c+eA=k3S_Mye28UCe4=+?}bSYBo&0w%0WE8od`NCh6Rt~zyUu4 zh}j;f1i;Twdb%Gm-j^5?UXv0?jR|8WLYk@!Q)X_5RM?|ZjvD1QQ{7ajZlasjtWk8E zWUX~Pt&qwips>ZoMRA!){s~b}!vpV!`rq|%b+~uO;fb?DOk6-wc}^*^tcr{xi&*>y zmAuVZH`viI(XX*}3Oe=TA>FqTbg~unvr#?Jr0!LUTPb*H5>6C^WII(*9V!S8DB44& zz)z$~r3uod24sVk)zHb;S%k_a0ZU3lQDOOI86~hB6uyYYtmN>aLJ?FbEaeM}i0l*$ zClSR@K?}1`q8xzDqUzLaLVPABCL0x%iwG}3gg}r%73kn9Y#Ln?r@K13cIP7UEDD z<^YRzpcLPwRNSW^9}_T+D9i&G<_;8puY&AY!E~h?;-nq&ArA4kp7uq-u$LGf&m2Ra+ZsTN+i(O?9TGMqtx5H#Hfw8WBfC z$A~B}DHW-u(<~BspT52WSeX@aZLPf3Vj3LnpIx1rS)Ux9?QQAP8?Ew|Ze?4y!fX{b zv~%_Cyryo2b)>0fz*O5@$JNyWBdDy!svnp%O{_MIt>|pabt7x)p=D{$7^P8<)QIpp zrDUMPv^Z^CoKlYtN&35$LxW1&Q2q35=l1T@^~g9)CRDe|NC=c7ONH9&mtb07<}4Ku*BS?-u~10II<4z;t@? zhuhn~e7XGNPye5ve|dZL_4f4V)5DKD`|me*UoURHm|njbUB9ty-HdGCOzd4Re#7&6 z`QUc>@a5vko29c4tEZnWitZ_X|g#x2``NU%uHsx}9FT z=$tyPA3RWY>?xafbxk|1b=y6PjbYLH7;kf&yE7p;7!@8(6Bb5rvlE2bv48L9$_#&H zTCg<1U!0V0F6xhV06dMCCtASI)fMjajC5k!u&~s&eb9Y;-hObVTi)QHcJz~nM)6%e*e)x&r=_%6k*Xr4DPfRCSiU+F+ng;JEYyx= z>jq*q?LKOgYpuyy-R7>b1}GYXM9QGr+E`0d(Le`mpaVKOh!`A#H+2-#WeFKbpTGk5 zU`TirDlHP98i0)Qh6Z~;1D=+9xs<#6;= zQ@N$-d~^&Q7EUV#lnblLi{$2pv$H}NS+RUrj+$Lqr_5EzDjIYJYDuzy5<-D^qLW;! z!kwYM&M;5F&qz*E3@lNZ?~v7l$7KKV5%JObsur$jPtBgOXrGKCMP05;m#SUFL?7 zF5R$I+-VT@t5j1B+Nl=VK%=auv2L(Q)2k4+;84OytjHH9aK>`(V;FacHI7Wa7f%|^ zmFLLxNOc2IqQdYbD4rP4YXg^A>J0ao$GROfRahDSNkPv+b8|8qD zbD-xxW|TNHpe~duXAJZKwDN9c)txfbJ!rKP+Yqhp&R2F9)D2ZM%@P#DRharTM15w7 zHX}ot6v>YAqlLJUf}awCp5nv8Ie7upEH`wVQ&rd_c!(n+*b(Xf5bo`P56;s=IFlUB zirl!VwruN6%knx~P5lU4YQggwv9(rCpvXfZXJO%}7M)u7Xh*kUr0 zi!A0NAsRBlA`tay^_|+fdZ|<^leC%|hlcw`#s{YsM#g6Q+WT~kE&Qfdj@iO7w$htA znYvb5gO$@dpla^dE1MKdrI0KZ3ym$>?op#{(J;KI9asPe)eJ63dPeBxM!a55HP)#{ z`r6hPo0n%*BZGpjc2Reiw69k;G1a-X1Ngc4`oqla>#>uY_Qjp)^VjQdzs%phm^eM2 zJl~%g>_-#_b+3eD1wD+Iw>V___0Xcl*`O*2`_+0E7aA=yx`O3xLhEMN4kUG{EV4ewpgek1f| z>FCAs$(yyykDE6?@7{jhJO63z_|x*%o0-L{;pwx!*^7bIR}=dmmQP-8oW58;yqVg( z>RY&M9zSd7KdHAKH8pOwX*PQ0Yc|o^xL|uyus9OyA)+|l(S0+U( z6Oxq)-pr6}Wm31lW;#DKo*&m8?n^e-`ST0P>A8lb6~ND~qch9i3E(HsHc9U8$9MMA zZBte4effswl9uj5Q%6CQ6*n|N85qNL^x!%>@jb2OCV8?Hoh&NN*OsR8qEN>4+VP5( zrAqT;wyrZ&VRDl;y2@L9#7+Ker8k4`E0-r&S}KNm&;woYiE+ZzETw;vAZvz{Fk&Oh z1A|Iq!r&>s&~Vpc?}yn>9%Mhhn{od^-eX@(P?9h+S(KNjg_ksti|eV7dRD1XT%Z$X zNSWbSR2&{3i^PW(R|RI5`==JWgeE(<`rdiwbGv+ z%W{Rq0DeZ)WChU@{mC)mtOS2bcqlWr)C@Dun&gAEf=;=7NUs@dRt_~w`^@sbW>v36 z+N@_Pct|p$0FsuJ92yB9gd4 zA~V%0E&941ld`?Ora@fO$`kg=1)a4Fql9ZxYI^kz!|J+TCPom6;d`Pv58-r&Dl#yC z-lcKfS>i~hAOl~WUJ8pSC=NkkGxv%RK}u|3wUq{ zG^!k*gkj~AgymR4Wm$D$1|lgI5*`ByNiGe`hK3g-A}i66RhV!DDhQ46;u5_DL{~o9 zl}~jMFkHo)XJUbeSmH}!IKxSg%5jgNbT6Vfo+wMNWW|&sf~p~r*hn|1-+gT86H4?m zLbx+7;vpr$nUV37UF^k!`LYl`B-k@F%ms;bLE&9VJg*vch^!^GeyG$kPcYAu*w*4A zNkj@KIFS&!heX|7id_ZTK@%7JcaPiO65pMHEyV;k1m#>{hz1AE6~r&q&=XI<<2^`i^p$IrJv z{<`|+-SoxT)WyNf#n$-A5-@*`>`qJ`%q(840Q2Yg&g|6A!t%-1-m7as&g1v50YbNK z&sQ%Gme2QAFZTdE7fv?Tu6Ka_9l#TqKLJ011I(NNn*gD}0R~ZE?7aSZ^Wsl0Z-0Ld z?CgN90Hna=`E7ase0uro^Xor-KL72fvtK`4{thhfAFqCYdGPUQ{l)6y#q{Fk_|nDj z+-c9up>_V$x^dI9|9bS``79vk*_-vt_gmMWcW=M!0)AfovVHUG_TJmM>66~UO>_UI zarD44d)2-Ad}QlV@N@sAZR1tv{Bz65rKS6yP?Kcd*4(QHmj7X}5B-QwB7+OGD`$zHCZd|(+ z-`R|4Q59&(Ir7RvHKah2!myUq&tWagD9a3_c_c$;4HwpXat%Jh#!!mDi@@|zDAVfo zg-wRSfgVJEUscyYrPYQs4dXdYRr&Ockn%Y1;>d@Y&m6O#0OxMT!ynV`KFE9IS>+Q# zjtr$G#Bj1wCFSXIWU2(4A|+zWj^$Xxc4-`(cSmH z$1~T^XW@B)$%wE7lvfPWB^vk4t<=#A<{gGhOlFp(v(O3Es8|X-j#d#$$cnEiOyrdS zkjAiJ$)f5sDKK`5Ai7#W>1ZY@h+OE6O?H6=Kd$g}g1QIOql2oW{K-+Cm?&3RBp{>* zGTjeX1mGD?1MtMfvGH*nbQH5PthzXek{!X!OArCOLEs|92Ck}A+0fU}HfC)eZq;KP8&#f(L+0c`B8uU0Fj6+UrUbz(DV&7`6Cz|eFN`6*C^RMD3VifsE zMG>A~L`S6I5(CQuoS;F@$RL+0zXw(R_b{Q4Fj0=w3^!h7kQf)qCr7fVVa)1qIy;1? z3=!5xC|Z)WJq1mZDB~P~*IQnxO-bd4CGsMpIT0buP#>!QGlJJ6)KkZ*XD(HN-~J|6 z1UkWjpCBTfQQ=O=FsG`JP$^4^bVD>%d!e>1Phl#MHC5Di zGrK10yT)3Zx|*6=josay{cV;;nOI6diQ%PkI7C?m(PEK}bXqH4*eaHpWwo94#;Kv< zt+mz7jpfPd!M?$!-a&m&ugug;)#>4N^_BG&l+swKYC;=3In8}4okb-zDEW1DLW8-s zwO8LaRX;SV?wu635AvFOI4x~>jRK_q#0>fs^H@MzP_ zeD~%aKxo&-Zu8t)!`R~J;mzjzUshhf1^fi&&*}3`z)xWQ{MY?`;dE_ccW!2HY4vR9 zyPrqzUhThrzJ7JGbhf*2vb}h^HNC$K?C;ALo51|J^Xg#t_2JH|z3rE~-}iUGN#Gp3 zKLn_}`R(@QpI-rQzx(-pqX+Q(?&r;)f4cbn%lX%jfS)&ie1Gxv`TmE)wdX5Kmov-P zR~U(6i8Tmk&N`mhc9xqtIz|LW)6v(M`r&nHH9Egg$m z%e=CCO=CN3oH^@QJsR6Qp4zzpcK?C37oD>=mf>?t_i3kY*{a_j)ojlRSEtykW8B>_ z)^;CxxeGfxL|&Mr%#D#J2UxTHij@)N(tv2(BAsYc%=Xl+jyLQrH6HEg_jlx*>(b>F zK+A^Zb@TRK+rcp~e*%8iF06>B=EO7e+{tPB@CdQ5AKTf326pzIK4R}c_23x3tqa~{ zsx+tzYHQN?RhfJUL~J^po@>cd zZ-ieIJu!}xogyqv6~mJF@I*c;RZPm3)x=TJk;tk@XjN<_GCa4^Ej<09d-%hr!7kn* zk32nHgFQmBg5zLeF(|(noM#->FBI<|Nsdiq6r{7O(wVqqDmIafPNKqt5E;?b{CFlL zngIb;?=&eVQ(l`YmgQ^Y=@NQ$4I+q~?}bTlg$Fr7J)EG=f~q5c6F`af!bQ3vqudbj zKG@745+sa@h@fF&n1omsE{27SpqB%yeE=yljGhz6&ktoJ1yx5!^AbaABBS}Sm2FVN zpiFFGaZLHQxc zIox+~h)NEC5etyC;v8sPK0Fc4gm6`4rJ2{*t!W)Hn)(bXi&S7@%B=!rhf?0ERJLd| zR*kAnAT#5b>I}Lnk|gm!u^&{?@1s~xXrcgKZK}pZ;7Z_dbb4uN3>J~eqE^)4OL1jc z#PV!nSuPz8VdJ4xB%~rgxiCGtFfY2eEDnN5EXQS567x!NdD-xcL`XtZZe(nJWKv;N zc3FH0JQ0aWArRAOL zC5jZeAx3VDk~GDMTT^NWN;Tt1c5gYfJ}X@mog|Eo;YNnlg!oc?TrqBrFsFy*PHsp) zS6JZVa{tHBAXikBJ1zk@&(Odh>!M^q8QMf=^@=#1T@Lv=0v#>P&w!P;Ty z=&$b?Z5)`=_f6Dx4T{^lgchr?y&YRC!6=1PgIeBet)H*~M?Y$-A0Mr=*)&5#b;Dy# zQ*#|FTixrsmW8#(i3Q`tvTgrr<<+OID)-U!B-rNFs0`uqItJ}59V*t;rh>ixC9?Uy8Qa)zuP7gug()?ZHUyqVm3Wm~)HS-P~WUbU`YcOSi+IeE2w{&wT~eu1e#mFfu%WSCcGM#Bs!#a$%adI+l$HB;|OZV*o#$ zVZi+P%%2(@Knn?=M*0$?ym9e9xRfArZUntDmW7XJQxiCpL>@Vgjg4Z$LTM0S{tTgI zMRRh3Xz{@29>IwTqK8CqBFkD!O@k7Jg)VHO%G>#xzFK*YO4O+mcWF5-N{(3$EZ}qv zn^#PnP>3xlbw?k6_HFcwsO@mLb%j z8GIxPmseaATaX=Ini~TzO2QPUP%HCj(0mTATu4PQu;tLg)PjuY{G3PxCJTnksKjO# zqcXB8l4A3sgOUTi?6(L4ecDJFbzYW{oiZnEq$aQJ5dH~NbzA>C*iWPO`N{1>a*1}>}V$)k$<4a+&0@fFwxP|-`;Jt0L#0Ch~t()#KpPt!gPL4 zBDXNR7K?5di~0=>BQ5RI1Gep@^^23!!=v5RjoJC7f$4?T!BM%a=A5gxHNybx_NbQ^5NClr+2^srqGS+Q^3!;qfNlijhln* z7sm(h&Q3pHU;c7?^@rz|zg%DZdBP8dh?gxuK)N8;OFVjFZMs4ZoS?D{9JoJv-58D=+o@sr}3RP1FJV}E0@j7=dCAi zX3pNMUcTSB`Lqk*`LBLne%aZ5Gdp?I*}0@{n&P$0b9=TWX$nW z^R228t#(vroNcwN^|$U!7`JBCE7RhIDgNw~cz&^Vd0n@;+pv4kxVxv@*ix!yC15vZBPsG9)u9rT9U5>BDlocVYR{w2b>XnT~lG_bLkRbE|!)ClRBQ<%2U&%LI&T z%F7^yc;r5Pl`4<6+^KFE9gAlK!7uKV3w?>pK4 zcXC4RiFcx+ehiDg7a4aiD$*g?*57vJ7Zt1`?i0KxLCL z=_F)46&qVaisM$t3fQrHZX{0-!4!rwm2qTMGNLvuUzC=_%}AD%_X(NJU4Xj}L6;MnHU=E>3N$=UJd{_^V9 z$l^xF@TA;mL8}e-K#4d=DUW;be2``O)6% zv*VAK7e7D0{`JMp*Oxb6fs0!}$nV1^;OF-TiGa=h`5#{ZT7I8Pfm?uoet#hT`nQ+B z{Q2F_vtQpGeZJXwceMFxbN%J=*6W3XPmAZjES&#g`tTz#I(Dqxm{u-Z&fd*jykEWg zuzCA=5A^fs=F8FLmxGI+b`Rb!EL`;sZ0TF)`K=3_o*m)D3E-#gyPv1;r;a{P?0lYB z`7k^4ZndhU^=C*omMX@j|of#91_HYI* z)g3y5S%$9XBK0*G0~1v*z%`qo24k^8k;$e7SLM58hPY+p?|%Bd2N`!CWZij~efL4m{fGHZ_j8?o%ye)voA4VoWh>UY^f9eqA6@*xI=)`a_0143$oA?89$H&fe$lj;yk6Is>Hky^OiW)8PS z$Z3=Eta5&bf^U_w&0MmUgc4UlSrv|cj~xI%pE(4j_#o&-ygDpHQC3x*SVoA*t@6o( z`c@L6F^p6cB@K#8f?*P2$XEnAnOa>gQ!;f8GHs(&XA(46Ic#+yOPf)xj>SqtDmi|| z48Ia)cm*Q`#i+nDF$5Z-yfP;)+9%lSVP=dEsVoy$l!7ZxrNVMq_)-eG5L%jCTo7AY zl2BY01A)dA!xHnM$ysH|Nd@uInNcArky&NA*=4zTrP;-$*_GuvC}=JokxxeFqoIj- zWCAdVvgxS;PO3nd$dkm^NaN_LWP&;!R-2V0&5Rf3#mOr(j5K&V-7rS$9mfri6KuoS zv0l`G8Dii>vMZe^g?FmU9eD&#r6}A`pH^?l)wSi+T2dtDP+42JyfaSPn;{>8h)1F5 zmh4nvSX51LL``TIBRG`i7fA5*L%VoYJ#?>l;D`0{M0k6m0wS1cIcjv7nO$P!6>512 zHTZBmEQx~2p_7Uj#OgYkti`~pg-calI>RGvy@ygCqHiwf8D)n zn+^18F{DV8lP1lGcTc1Gtm+6N~k zjcsskQ=y@ySfR`1$}+^N5^KMBVPkOPU~zG4b$V@MWO1`|e9q9_FSoQvTN|Y923eP0 z-qk?TO4w$-yt}<_xX(N@VO^d#&rGQY`gFrK)6{hL+Sb_7+3dxOxyzT+XSWl_H`b-y z&XxV){qvct8^F(@{q>>U`F~@7KYsu6^yAy_^XKNx*>^t|Pq)^t_5ecn-=3a)x;p;? zOrAHu@Ol2_a`VO3_A6i%+yl1t@6TBRCQm?1VC?+vDscV%?oi-%z)wKV>py=5-k$#9 z?f!?$tv3h2{JH&hZTH>c@t38m-H=nkj|Fr-7 z%hB^M$KU-te!l_ux%y%l*x5VRByG#A-d(}?v2OXW57^)LuEzFXj_kZ0S$kufdo@1x zV!r)gzV&3=u)iZ*nFsu2ZH}_Gdugjy`g|X6$tGUtlT5YhW}B_6mab)^Wm0V(k$27+ z1~xi-HV2GLee&@xo~?^DGRT;mV$3Y^7gi)IYqFJ9>C(J-ZcIEq$Qm#+x^#OkwDyg1IXy9T^^@w_7rAD16s0oD^IHd+SCA#_M_@txbp_~Fn9Tr$cg$=Z7DXI{a z7MALknDexxGO)PVw=mZOQxQt83ar8T)hlC_Vh@4v5l8%}TILF4xu&9>qN<+7VuIXq zot-iq@2A|km*U`<@#CYMdyn%TJj%WQtjz6Eq3hjD*B>&T-pK$)(7-!cAwOmY-^~ua zn-S`m5$%$i=olK~;1cwsbJ!D)q#qxJ+;@xgNytt{q0;I2AWYuF;xLCSuV7+U3LBn4 zK|RgNb_hwh7nNCi72?%L)87H!c5mQ7> zMKG$WNC+6VqO2k>Eh#K2;Av*GAEq!FUz~z1P9{}kR$~hBh@A4`_@cs?((;6SNLW!t z6r?Jt1d&!)m6=hRnvj9o;}f%!Q!vGa<6{R8eM}q$p8~&Ti);`Z(f&GSg^5=Xgnvt$1j#c(^Oy ztPB_69D&2e-mk{ptD!sd<-W4UNTnrSZjBR~14OMs{MHCgdmN`Xo7q=X(U2O;4h*LF z1XBEhDSjbjpAe!~FxE8)>Ewra9E|t&!T9(QLgIKi#r34BR$*y_Fk8WnqTmAI&=?Fl zokT9Grq}58s`fU47B5qI80zop>RlCDUsYqerH?W=RcD)S8yV}F85y#*nDtaDGb4=> z6-p2DrG)umjrL7U4bQIvD|}r?m$bzsZ8gX`bka_>v{Q?bu~>~- zMOT}CxUX$)dT?vKdu6$Cbi_P4)w#4hx_7*A`F!Q}?b7w@x%1~!r_TXA0Ydw>j;1cI z06zzIR|mFd|Bc6wwx3^suRMCO^Wu8-;%NSO>-+tmfS;SUhyS6U0G_~EyH9N60Xbj))tm1xMEdqpqhCK?{_zi2zyEyl%e&oo=bNwhci!&of809w zxN`c-$~QlMn?Ct6y#JwV|6TjRyWXo$3)i34Za?q5_;T>?{ru_V@~1<<&(oh)4?fSX zyyzZ1t{d2s*iNOhR}Fxl8^@!&m!o?xY};>aE3a(R&&O;xbKm{c?`;c~=Kw$1TVtHv ze&%{t%|fqWaX>uVAsK7bj_X_J4DC}Y(*V~pEbN=rjci%_H+qb7R++7ZH)vrD_fkeD zh!b=4=>^XG0&iiCKQ|$m9uiD;*Yr0qyEXJSadk7hx`|$6rtwsPafIDP2rU zQZl10+9b8aLn?hNRzBuw-0@=fe9DtltVaqdG`P~+6PQA?93N-he_HU!v)siS_RJgV z9!ZJvMFqN)_&m(_ewZ8ZFgN5*R;_CFI4!G4UjvXH|hiPMAZorzavkfnF6)#<*l;I|Rhv3(0he&VL+{?h>2!G&$`_ zM!IuOwr4rg4^`t*UgMAf^FqOs_{3B(&Yzg^1RCs)4)VbI0e(J1yLl0O06)R`GdY-+ z9?mF;VpfJzv0-Fl2oW1ZKm-yh1BoSpq=GJs_MDQrc3e zke0A(va1={0!gL9K$103B@GObnImlBi95xLfm-#5Mlr15cX6pEBEAmilj`pl?ctN; zn}tfP)?j7r44H+@Z$uD88ATZX^b(iiD$goh2n-!mg$%~xqAN=S$_fKf&_rGhTp^`N zq*S@O+R(z`X`q6JO1`O#X)MO6GRp-ih0KIJYDQ%ZHV0LiUY?gxnwgXx6&d3mAN#a0 z$DdG@goDMC5Ggc#4j!3N0ZAw=PJltvN-E;Y;PI81v~PETBGWR<;$m|`!%_qMV!Z-l zy+dMsA`|?Q(n51`qf1MYq2;L%NK8320fWz^F$*|?QogK|qb#kdEv>1q!l;Y$gqew) z)Obl=wh>w0&!$Y!g~ORmQ^{>p8P>71fw8R7(Sr8ocsb8QUmGeD_*PdxB{H7TL;);a z9M_!Av!v0Ck<_LLTthgbJ`&xWglJ9AR!0WX-MmT8KIA8UWS2n7lMu2~2H@{Jp;BFHQwOelR601;VC~S`x-I?n1|=GUi;RLkbA>-~gg-KRudQyKTwJg0?o6-GjxG%j%rv+3@l{Pojj6)iiEgqYIkGG)ErBj5RU5fYordO~ z_U4|x;o0T3q4B!5E@@MftffI=H7Gi?vNkPQB@(xq^n<;oaa;H5+}PpPz}B*9a=2}N zs()j3`t)Sw_SM?$o2AQFv&YXT53j#HkhHYgy}3PcaXNK*+`qHXyFCLwe`#uT=iBp_ zmd;lefn9!kdU|JOy;Z+;!R_|kFm$#nLq;qp`K z%gZPVLllD*-ab;@zaSgkG4*a)4Rh^+VM}#?J9)StXB)+iPm-plt7oTK zbCc{@8)v$gH_^%+s^j&^d7T`78%5GasO`Y(dkMNhuAo<6Z8hSI>JkwnygdJLT%b#w ze@Jdb5wWm}Sq_|HTy7C64_RHsl+_5;Jb?&Try@xOPzkTttcNw%73rigEp;&+&C!YIeN-veJBj2LOY)1SLqoCBWtB|dbIb@YkxipvCkUE@$t z?~?R8seul0ZUL1^nT)EeYP45w=DqM3=eRV_v^>|ObhnJmXE~X!xtT5nc^*)NFRJ=+ z3E3g3$g8S6g@;d<;QaCF&Xpk^m{50opd-}91?Az6_w}WQ2UN!fQj!8GsUftS2u4{{ z4Iz?24yR*7s}Z5qP~a9|E{$U6g;b};@X~{-qXH$|!x`V`SEk z*VWmS{1z$^*y0&U1#sV@N_RNQ3yb!wDtlC%`9opq!_piNbVWoBB~LCys+1JD^4kmQ z1sbBsI@I&~)HO#>U__4$~ z1SUW9kH6;`=@6CaoK)zM3ymnlr&m#m;B*+A35T&zBo&ivV4!sP0%<|6JfqTFOdNu7 zCW_^w5p|=!#_>?=OhW&BR^N1P$53u_S1wNxB~T|b<*6u60*sqRtgB$RQ#rkKN)HCx zTUFIwT4c=1&?h9Q!b1ceZq@hP8F$=h4o}GrZbS!fszVUnA)Mn7!MWp0@d)E4r6~$a z8*oIcfYGH!*9o$#34w(L!9^v3`DFol<#9+-rL2zL(k-^OapeS=3MN&RO6wr{HbmDL zYhXg!)hB6gZ(Vg zWU9VJK@}7{Ss$)zh2PRv>eZ#C)h%77UQ6rXcvIhSvw5gxc(~ErTiM>Gs;be}HtNM#X^`1u-W)8QY)3(VDVcq_5fB)P0$@ja{Z;ywc zZco0xyZrsb&0n7%{_*+#A0Kc2c)0m~|M-s&kAJ?q`+j@%<^1gZ{_(^5(e3QvjrIED z=-ETZ$z9#aL+S2?)V7{vNteq!w4f|92 z)gjHwVBxk^w`VnM_SDQ))?3xpeeCj%^y>EbhOQV>Z;Z(t*FKiky};{U(>JX(6fbnh zrU%7yvyx?-#J0kk8W$|gh!!UWQv=%7QN!9G-)zuY4bpb5yfNL-l2+B4RMQmQ))iwO zr#Fn}>xYV^rh-hZAUq@0H!2FA7*Al*kO^pLEChu1aZE%!oXkv7=;d|Bs_y36#!`7@ zF}<-NtE-#b-IvhU7c(*tH*Fz|4HAY2$!+Egttp985{fH=;mXm899)tDlVyV?G>|X@m`4N(*?xBbWfr&}8WPiYi7c^?mu$TBfjs%v40>aKV{WS5op%NihIy7$7bLK#X?E zO#_KJQ5qRhtIE)5XmSl*q@k%Q@(PXe{AQWDSuAPfWfrAG39txGRxm3UluH8T#6fZp z2znqY*E5FUlEidNXL~dFP@y1F!olU`ByvcJIVrJBW;|Pn7swL@avWP0%@Bt%WJrb* zna1(OW;urwpN0{iV<@gznonGgZxqEHhynPa0A5IdJN#GgF!x|QGA1j9Ajs9URB1XZ zL?&5!NotZVBBKt?@58FCXmzi9vH4A{)vseAd}JeeVv9Pq!5CiQ8d{R{rgU))nXV^h z8#42nB#Pc5vDqM*DrJtU)4G_k^@Px}Xow!-r}pp=Kl4^R^;P}^R0D!k0FV*@Rs!G} z09^YVqJ~A4q>vgp>>f?NrCK*^6x8a85-t#jMG#5h88mbbCtjqcmeexq+BjA9+Ukm` z218v-VMCXq!=fCX*9}h>cJ-@9`pYMKjFXMV4v9vMNz8Khr8&Fg`Fmx!KVieY6iE>! z?9A4RqNy><>ekls{z1?5{IG4+vbNei;b@7q`ig?`>VgJS;qYAf;6hQ~Oi8=7q@kyz zyt%lrvPfQ3RMFJl)IHojFkv!}wG5244^KAtk2vCJQB!M4YiCVgZ`;&JpY6w$>@9QM zwWH0Z*)GTP@%`2Lvm=L}D`$_klMl<6?=5G0eP=7Z$BP4JE0(j>k>ka|-KhclxOsJW za(8OwblHLD>fyX?e|qb9Vdrw~;N6jl{$S;NeeQ5+_F#VgcwzNwZTJ1o`5!0O z|Gc>T>*V~8ql-U}uKsnr{oDHLr{$%0%Pa5K?Vk<~e!DvP?eXNd_eY=KoqYXp`TfiF zA7AeN{CxN4$J^iE-Trp>@gMI${CRi(?ega1(Zz%P_-66=a{TaO;OMer@3eC7v~d4a zvc8iwwSXC)3tL#mpY6-9jw)_$On09KZ-1Y={^$C|zxPi6eYF3_&f%ZiC%>;Bf0{nL zH6LHJUS2j|-_##n7wzBb4(`hK9~$;PbZmbxuRdDl9;bQ_hdWOvjfZ3UHM7Rn_a8qe z4C~!>6GcWdud|(2%QcFw;wJjK9GqSHc+%iG#n3p>?XHBP; zuFs1|PkS4R_6dtZCM0+x0{xLbK@pyCjBiXbJR>(rq~ID1n)a%4qfS~=Of$7)_711^ zT4Vc$QNzR0i<9KJ(X8n)-tf4n%ACt-N+LEyr?e)-m10r(F;GziSP%}E#$fbmgjxpK zB&2qz{$;LxfnNUrZ50~&HA;c*e{Wc@PKa=2!$NUTf{|X6qjRVx1}Y zHyH`f2si*H5)cXt%t%1-Nf;>w!p1uMbfN?~f zQGwjpAaQ!2C?h~f^cUiRA}m;jfhi+H@=-`NGE$F%%fZ3oz%V5oA_O8N&~#&(h@Kn? zaff<8LkGKs0i7@*?wKi}w45k550}G_qbU=`wYdh1x^zab7?!YG7>Eo|0M^SXQI(j%M9jxU5B`&gKuccQtD%wEbr80Ks*M3M**UtfRvaQoJ_EshfxXR<)UP{ zG+n03;z~)9qFhatK-G|^X%@(vxr`E0yd*x9O@T49pt&g!8U{{BgyjatFudbg?gY9k zh3UoN!6kVy3U0EDMih_;EFxaa!b$isQemuA6eSXcip2<-3aKg#=SqRuytk<|XF`rk z2FE8y2+GL|&g2K;vR;Oz{N#rQJPQB-+yH>LfY%TguPC6RNvG{LD7&PzT2g9hL}o(- zzaQi9Q{LgO?{Tdj4(wdOOzct?Px#BH%K4+hW-GgDfU7jo*_Bjo14m)jX{?pPp;G>6 zX?nLTraB8=hy^OceI!Ai!ndA67gzo>AIXa#l@mzuB0&6WfbbWH^eIB+1y`eD%Q7j= zLS9#)a-d4xQ^PD##dBy;)C^*tFk7q3Eh^0|t)f@fkoBd>Vwp%^P+F^R>Mm^`DISM@SP^^5Ux8>-9H#U)jy z&XzuFr)8#nWV+ol)nT1B4vdtvwU>8wm-lto4D~ln47SdVm}Um*MmtLfo2y4VO>-lI zo6F<-JJW~9(+6jB2iMc5mt$A^!&e)FXR8iBM^0^)lO>0r{cF|{``FCEtnF-N?PPIz zZ^kj3CkuPm8)siGPQP6rf4SU!cf5YR$k5}J5-~9gh?)Oi(zrVlv z{&@S_&8L68bNKo2_2Tl~?#a#S{>AM6sb%-1cl)TxzF)R=n7@4}T3kyvTf^G>1A7MH zwgtL3I z0<0S&4k#f7E2zG_R5vOG0Dn6aKfKPV@kWpKn-!&P=c?GuVTPc*_j&$^Fk+pdzxv&p=U1pf~{} z!3WD@;HoHuCIX>EpbEobG7w1UAF6-_=LP=lr-GRp2lqnyzKjlbj{rJF!CXj*uuO7j zY6b$E3c^spL}@I~NR#%9xLqu29R*2)1SY(F9sb-6xjzmYItY7s^$#7YEsnKVU}PZcTBrFxp8G*?oVBdMlK z8))2ea;iEhj7fm!rb1~rSaw86PB4n@7t8QUVz?7>Tr;vg>DfW-tWW_ZMo3B&5Quz2 zd;uphpC6^+p%k1D89P|c57Nkj_1X}zB!I*B$YwZG8P0T$H%l1A5eKuxK)T#JQ|d`z zyE$^H2NLkJ9{}L_Ls62P%&KHcT6ug^CbKD?-;I+E4)cT%Z)~BZT?n1qMif z?kpFdJog~!TcF6ppXUb5^9)ymVvA#WDb=J*3y5OAuWJ0BNT!lBj|D!Q8L#>YpO?3<4KXFDSE?0a0aq^I zYGllOEyYklYw6NhCL66YU0vpuhL*Ke8vd%XVb;rh3`n{U^j{`L6okE`2H$ESDp{qx1G;|cr0ko};0@3d|E zq-O7|WdB^fV$U&;qD?)4tv!&zfyAkC#=5=W@Ve>j%fRXPvHkB8Tfa>l{&W7|&xxH+ zeYT6bIlE?hnK!q{ShO*=_W8SKvcs#wovV`N)AH%Vn$g3S;gi1pV@vn>jPYo?V0}no z8&Ypis&}W=>w|`=y25^?q%m7mm#AsRmv-T5%=p@Y_~wc9=6PP@azXigrF_iD9qwR{ zS_PBS+Ld|r!i0WpRxxX#cN-;RU6mW7rAs|pOSz_3p)xTvMsksnP~Du=)|1+4p&Nz- z(muJUJ)cvpNfL1(Nr}Gb2w+qgzzN_5d5%v5vGZc&3Zha$Qb{v4k}M57UCBx;l*ctS z5&A5I-r?x>9(ZRPVsap9v^#CMo7U?{%U!vg+JuBM6t)}#6@>v=NMBlTAP0)pVp1AN z^qySy5SMR}HwXjgi)zc39X%?=P``ms`j`4q?2i_I6J^B7=C8azD>lbekq zrJ#v8R6-Oi0_fuT(=X2eUOqp?VEv-vydtr0qZ0kF6nH!{62puP&Vaim0udZMl${vN zN)BSC1kh4FQgKgX!T}*cpv;8Gy!2Q(70SUm{Cu4WdQAhp&JA>81ioelIQ(>DLEf^F zKAf-sHadXg&@uj;AKG;RrzlAnZP;*hxs@SI3Q7A%zNkIwdt&2dZ0 zami$PXJz?PQ~jy&AQ~3Q#>EN~Q)MZHIuWT>jH?i0ig+RUEKoidSR@TD)j;)XuuAGL z&hr-V-wK8P5*b*kgo`zhT)7*w(2G?VKo9uKH_w!z!Q zvI{0NwUa5Dk(hEzc+E&?<2a^!DRp#5K zXqU}55*p=b0|Ty3M;4Hf+9aSTB7hGCixE&M0xm}&&YU8G4Ca+4W!fks@?q-`*3 zTSn!r6T-T_Ttj_&aXGECML*P9*IrUwFP2ubczP09fQilwgH%XZ)4i=T!+m|-ZC(A{ z;|uev2M4ouyLEoCy?>yxs-{q-QRNBMJf23t;Lx#LPMofg*3(@vIn_HlVICSCwoFc1 zX6GkYwq6yE^}V>ljA_-Fttu{jk6H{>Q3xKbF?~e(J!}5kas1aeDKw^P7L2 zIr8VpKM(eP-`@JXY5%mf|K<4jx9ij2e)xIu+s7YxetmcO_5S+H-SyX->#tWg-!4D= zCc*R_A%xp&rhcwKvPTe^ObH$0utHh}0bhxMA1#wS?o z+q%;S)AjGe*Z-P4`N!nJpJS*0o;vz#Wanes;)&k6%<3B@^bW@L4nz%FV#jAv=GT}j zyYi*|qUrq#%dXM9*V%V!?Y@|;Kbq05k4RTXlv~sCojKXMRlnG!9xlym6A4@C`MtDC zOK$CWcHL-J?L=5_6=^0`e#)IxO>EgKRa1C0xP;tqgE zIx_|6vhoaL9izUSUZtZ~YUoNvl7tqck)Z1v;(Lb@dIlrgJE0vW*knIpxRuz~L}_o$ zEp4LGOR#YT2$VJqmKPb28|sx66u=2a7obxclXH4VbaMv1pPJf2jxgYS1ZYoIOh6tP zEM^1=>4Dq~ASV?mq2YB>qE3MkvXGf1B#D3~5uliGpI~2CuNO|vKh_xr2R*|?d*blk zu?cTu34ZYuXaYSvGAqPCDZnYx4VDuPp~pcSbTCsK?KL1h-Wi8_5)KZg;2nO(DY9T( zygL=~h6;W|2ftweU()@Z82&D-;I}-4e;yhn#vr8e2pIug#EsDN!gaaGf{YMN3RD>v zEQvr20l@x#OtL1gbNO56aCB%uMtnNBL61Ai0qk5ssuH zk}4R<6|CrDMnrxVL`LzGQr{|=-uZ%nA|*^C50VP~`FtN9KY%9$YVB#QlXS;P$*g|tA;!3tzG>q z-RaKS!SFDgPDX%V7 z7ZiyUI<8pDX31D-?Ce-s9;vIfWM#p+x;nFLTd>(TX4lpiclTy?_6ApM9rJU|GgH-; z!Gf-4eto{Q-B4g|C>d@m9_)7DS=iN6*x6e*Hq$z9>)+TO+T9=8+wR?7>)EpPY%llj zERCMq$1b*p4pzp_HdjBK+TIhaR%`Rd;7*1@}@qt6$P z{OM@%2Om!yZ`ghJV|mZNkG7xxadPvo%g6ug=KcS3_wYYgH~)2h_~-7l+_7 z_dXx}?dR#&hnwF&I+CXY&-2eW7oRRKKVMvZIluXO`uO|l^_RVqyS44px%K@q+s^RX zPQSy?-P7)!Q`5n9!^wTs=81B2DYJhZH!ztzyU1O&7aUz1AHENL`0ts=|9kQ3zh_VX zHF@^$iPOIZ_dl7IP75rHIUPf>O>J7;Jvtiy8B-+wmR zb+u4`GMm3SDp|Eiw`L?e^P=@J?W#pS(ah^F68Ec%CN#B++WJL#%}id!IKOIEQ8iy; zn67sCDH?7QPYmVHPb!8wrM-=+;ZB*kmEBrfFwx(%vrugt<+T(`It;Rwd{G@=UdPfk zb4pt{`6i}pK*{P>v)c8%)(Tp=K8}+coQMHNf}H&UF^L`ubzDnxPH!u_vo5!#EVsqL z((woi21YFoudc#$^u%`bgtfQBx=hH)9>PFRXR-eo;Y{KXCBxd)FOoMWQg6BE7S30{e5e(~ub3<(5H^m9YG z{0e;zCWnDDBB5Eh5PDJwjQ}Fy+~Y!@hXq11t2_hUqeq`Kd4!7Ay@9lE8p@K)3?x2%mnOw=gj% zi09|G*~Y0DmQ@dn>jt=G?PP65be;yt%yY?Qy-rI4q$fS0 zB)`l|am`8h$;}Dou)?|gC{-azRh%I!O66%2c)Iw!(j<;PE?tfy%CHHNIGg|#$AZMs z{P485$=PmcS?&x*AdT#o9_yVN;++ZsQDNciumoXLf{GZUP7PP4g^E(aJc2JL)ti^; zlgIE=Nf8n;lq&$`aQr9?KMFH2lMBukf+Q74c^yJh6`H94g^`@zqJMG=d*Tu0LXae5 z1-J+%GLjdPS&^J?<&`an4U_b$vGn>;a`RMn&kBEZUps$Mvv_Tqzv<}PGq&zD8Wt;f zgX*k41%0fTIa{4ORY5fuV9IF-buvmvKxuJc9t_L{MJZ9(d`zS)GE9VyRwNRO>697? zwN8~$rO7NaP>Kx%r5wY}O%!tlwFZZl&C~roD;DeS%=q4H+gx91U#+&iRAnlX*BgpV zP4(uk7Hd=Opr)*cR?$l??@lRcCJ2;hxtdsz&n(f38;S}$%PYFd%bJRGrs~S6@sZt~ zjji4F`IXt(wYlY;)%8RBv~70KI@H``tTvQZstU_RDg#rJpUqG)DLF(mn}Tbp(Qht| z@7kBvRu@+`mS@(M=e9S;HrKlrW*VnP8)wF9CWiHcE!;+xw8vl=ZLFN^F1PeL{4D6~ zE@+Qv>c)<2%DJsw;47H5uD zY*(87)_UD!k7gkSg8<+N-$D@<4R}MmtKA-J89@y`GEbqDdesAa9_Wq|I zhmh|7`|9C;uCM=jdi;HF@9U=hgKh18W#e&k_rt;Qx2v;ncW2-3Z~ypk^Znh`=UWG! z7avb9J|16vKEC~Wc>C?(^8Mz))#AqCgagl&?E%}adF`Ng_pEQ{to`t&_57V_@2YfR zTQs?rJ8S3domQUQwB3FheE-+XyZ>6a{^#7`_wk)CLnnWYpZz&>{I$(~RXnxM?zJSC zIzk(o!IjmXC1q~K6@E>fG1kSrrIVV4tDfojTlcS2VLe&hYmf0H*nC~kYM*9FNdE` zo&lU*0RsGfjSO|g$9N{jdnBiL5=j2IH2+ZCTc0qem%&c~fxiY3LO?0u(DYbjRsu3J z9!!jKiv&M`zJ*co(IN^~neFh?GaKoW4RK*WoS8u`9Iz`F^i~k!CkhA4V&O`Bgqj$k zA;Xl}Fclr4$qLb?|9|<@;b#Cp04fW1gil|#CsY&!KttZ3LS1|U0YPXdBsn00jR?s@fJAVh6b4ej9G^nTLqdg6Uy8eXvQt31XE+la z#|z0S!1BwJ<+TJs5h{c4iO>8cArp{Jdzzi)lu3F;CcYr2zR9NgvRN>`0L>F&_%ght zfFL!bi44h-vJ^=rfvt;86^CRN#FNyx6bUL(07+!|C+B*l(L5Sn|kBVOR<;guo3+ z$%`aO!;=MG^a7Bu3d1UnAqbG6=|Cs}1WADrq#1~;s8><$&!gTTxncY+ZrPH)dL_SN zf?YGlsGH!lEJ*q`^dtM#BZtPpBa>;fzG2g-ovWmFNpVdaT$?zdSC!N&m#x%hbntLh z-dlxojTU8?)?=HBGe?b`;4Pl%g7WUYVl6SYOd;s_ZM%wXjvjL|rSktShd( z9bZ_5%Tr+FiZo+g!9a(xx4B`st#y9XIyPpVn3)Fvwb+Y;D%Evo^cDFg`OsHoiD9zBW6wGHsd~ ztRL$&&J5N~n@dOA1Z`T?P+7%XTf@RY&4k&}-wWD13R*feO>M=!=7!1X_LarXjivUj z#g4uCo`a>~*)0-w$0nUza!}Jmve`o8#ngVtF?ub#l_PF+tt$UhpqqY{5(GVvb}k~vh#6m{c**1 zyR?3{zVm+H(fS>JUVOd3|MTPBZ;w}Bt{r%uy+6G8uy_4=|K{uN&1d`B+aBbPYHlIA2ufFu3yfsHS)$n zajhcTz+rS~Q;qV}T1|F+X?A6Cf{+U*VuGT4-GKmX!b^n=(NK zR)R1ioSES(f+OF!fuB4L z_~jS>r{LsJFcAeK#ULqoBpDlo4|hZQ{~YLy$oxNkBKQga+fR2s)I$LA%0oK*43c0V zvbYdgLWrE`$4~LkBZH(QM}>r|2o66Xa+IUL`}6!EQm{YQ%ZKIhzx|ZpBV*iOgunVJ z*86ps+b;-r04n%(7~(Y;{tOo99!3R4@Iw(|WS|7@Ee3mufI%Wqa9$vo7Z}9!3(WR$ z`01bG9+2eh5HCAFhE*6tl|u2fH__>U_^c-sj!P!(b!O(9jPy5|nI3dT5Stgm&O#8e(XLSfUsmnH3UDghGg+P;zt}p8}@DImLos#s+|BQKTwH!E|}^ zUQ7M5zG_-tF($5`)%4hFhfmsuPCB~w+j>s=T2A`)He;4qjcwvZn)qSOJY;=NN)L-M z!p$2Ou*}(%W_+d*BkCa(Pvr{Rl5?uDIWygHQv+6OuXWTsIXbyIyLP<3cfGZHxwdsQ zKRMIW)>2-nlh+rh+jIt#Qdi4USF)v2GKm-tAw`GOC-ZXFSShW-m*5~!;Pi1dcX-`L;b+CJF ze(-Rk_t4gJv^;daF?q4yx4C55-v_GC~zdzmiaI$&7yLh%b zy*EF(GwYc7Bb&|f?RWcI_XiGI*RJeqR~s9*n{!9=GY8Z2Co^j|EBl|ej=t{hecW{T zxpuc?yIHW^EZXlk4?Z59eYraOa&!9e>hH6aE8rhh;1YC?ODmrEN^vEV4KYv9U%3YXd@=I&0M)>t=Q_* zFE$rW*Otr~%NJV=)6MyoCe?^ZH`QOWXzAUU9NwDlT(Xo5wrYAB^_E_}bwJVAm0qG} zHPjT34wYE@c{Rn{ssf48kX0f{Z%}1-6?1xx?ACf}d2zgujUYt%gt@r-1JW}+%ZsDy zi!lxPk!=O>y*g5xnj%OJV-rJI8DJjMUn=#@SNj=M0j1I)4*oeO)|rNOPYHL63V#ic zeifADOL#SB{zR8JRDXCrw z$u7~j*T|?>0iiEFATOQ=KKse{87LwAZ4|^WE;J$|5tWYjjsU+5bbAT*2&W~Z1!Rnr zg3L?u$&GkJ1;3_&+&ICWd2r7>sJAdQP>P1B@Q(ftm*7DHoG*_UB*{c-bHer1P;Hu{ zzyH1e6C?t`@e5uaGj6;b7Np zZ{2*Yut|vH#e$N6epp`+9tw$z#!`s6O0v2p#n2j8(H&kl5NaBUtnZGgG9{MPWmGlr zTRTfEqg{*3W2<)Cq|IhoTpC%Nn^>P)+MBl>&Mex8W|le|O{!uYQ!CD@Q1Dv{we1>x zi=@yfQY$klY3PvDNO+loGjHkGvRP~^gG;M}w(ZgGvBvuT^3pDYu3g_aY3f++(+yPq z@N=Z5eyO|LZt33~>74H|jkeYg8f$v$D!S_$EFJypQ=_LFBWL!Zi>>k7qp9;l^QLX$ zaA)!Ac=}{_`R3UE{%ZDgYva!0=h^PZbNl1r@}+&|aB*UHc6?`M?P}YB=k~*)<8k?H zZDxOAa%XyScWP>HdSYjMa&K(mbavxzW#|3I*5m5>?b7P);>z`pT)KI;aroiz?90XZ z=PO5NzyJN=_UrYJyAi$HIeXYRe_X%(uzvN?_U`+}!*90B_Y3=%6YB><|G{(W^kM1n zX6E32>fnBC|8aQlv2X8P*TKimqfh4R?~7O8Y-eAVjz7-tKTdDmSl2Ix*3SCu*L@eC zERWyEu0Hgh-83DXRxYo|&4XmicuMaOwsR<9WS+5lTDJe$y!&-(<>UPLA1yeX*B&j%wrBY(Guaa(=>r|K(H_N$#jrK5UmH-*wCESw%a^(fpF96O@0WMDguK~Zj0XTaBz=$`IvEKNkfW#Ev6w=$IG>_Os*O2Hpeu$TEf}TI~ zfAy=s^DC(TD@dSkba-f55)>c#2JH1Jz|}J}FqE1Q&P|OHr#t-gW1=1T^9>#B@Y9tW z?7|6h<3POyNT4_xB*p-_QNGM5PiBH6e?pZML_XP(KZE5lAW4{`zXu6};0kCU-^Y*h z7A^sU1p!DIoNCHY#G>O}USMAQoapr?()HI+S3qQt6B6cR0TYd<{6prD>4EA4+fyXfJDS=a;Psc$P4H0MtmEP35%j7(8!5&QgU`iI*rES zvZO*rzL2I9r0aPEJqN2}M;0o)H7w4aTPyqll=x<3ASb>zcR`^7}_ z*{F7{J-shK%E&`C^P@V%F&&(kMgqB;$Q(@Ln3I^DXkJICsvli87GE(+D7BFCd&%O~ zbYVM-*Cr;Fv(Q2+iX}v334_Qqa27RE$RJfnew+wuEg8HWZQbwCkLj~2s3<1NH^I*% z(%mz{=edu205S+ifpXQMg$?kU-eA)Rq+=A>J`&YpPHgO;*0*q*JGFfl?n`Qp?{6|>!->DC)MHN_qI#X2r4Co!5D zA5pI1&X2clZw%R1x@YE1vny?;0ewZgys%lMHVW%46`hN%hJiA1dy#gy!MN1ZYabfe z=lf8c0OHhe>z{iKbSvVo7`U<+nHOt+4*e*=o6FBvci--Azg%2=I5>Z_ zpWUyXKP+FqU%vXV`0@AE$FECgkJCG6meu{i^+U_S_3YXEwWEi{gNM2O`>FkR;|Cu` zk3U;a9S!}riOWBi&%ZApf0^HVKecmjwciYFUJh(t^lYAWoV_1>{5Jadb?E+M&((d) z!9n?wO=>?<*!D%Mhl<_X%8TFoFa9yL^?7poZhYc?e&~M1bTU(7AJN%IOHbyCPv$gx zGpxB0NB*P?b>)usNtZ?o)+aPeL$Zku!_q+c>QLqKV988(zNJ|`Xw;8&)Xem@&G!s% zTF3V126yH<)+TG`EXuw%ho3xCQ*K=iuc?t)TSeBYm}Nzx`pOKQJh!!i(OJvwYT`CG zWESb7>C^xW!~^^s2nSH9K6*`fxhkSe6jsJYsnZa-;Q`sG@GMLeJsHJFhv!oKGLl}! zhXA6z0C8@BI41z+B_RAY0Okb&oHlE#JzAKIDr_x@rFo#AGL5n zqMemVtfH(o2Cg(cT$u(Jk)VPM1ULO`tM8Sv%_;LTHj^RGX- z{p{rP%p3MPB-S^U2u#QTCsBYziZ3C<2cO~tk8$&azI^8U>{s76zXo{y;_vek5)6!s z3nQfjMo0YW<@(&m*%u9gr(;8y$q|Co5J6%9JIb97cl396F2q9sbK`;Cxv;;FzX#?; z2C&gybd*>mgn@+qL3w^*N;pgm3Y8-=o72@X=p^SC zu`hlhc)P^7J_&OLpaWkbz|TXYToFm$@T?#(2kIw;yp_Q`HE{0&WMDy9ur?f~LBVAa zP<}{IHrPMK&lm6ca{%CHf53AP;4S(mM5;>+!xzu<$8&rz4w-UXQ@Q?hRk*M?SzJyQ zmSk}CWTwu+CWT*4<(E^qO5@Y00xnl3RBA-}0#30ir&vlZ5hWD!qxEcL zAroA{2r6U&i}-LoJ4Tarjj48Y+%PDvw5L&&JyB<_EuQjeXy@e%%@TwBB|#W4fLx-Zj&$rLiU3Pu=65FZ8QOpt+~my3lJoT%ERWj;?Ju{G8dIT|JtzpITOT z+ZR@=`v)Y(+UycdYNa~Mm@hD@WK9Z9v!WUAZKHi|xnXR+ zvZ+T}(aP5~GSqd9hGAX*lCfr_Le^ehWNB$xG7s3zLmRDqbEO>?Y0H41xt~|x&2Jhi z7+UL{b>zm&!-=bl>FXbBepSvIUQ2eVUqlaBo9@N?mGdhKS(kw14o zZ2TWT&zHAu?Z+REF27yfe7n8*a&`Ch`ufx9<-7gU`}LFCABFw=aqi;%^xfx~^M}#x zvw`KE&Ut&+%C6ad_G5+QllSX~?^gC77WO~PJG4CeeewLy`SU+#kA9!p`)b{MKVZA* zSUPW6I&HF@Hm;pCtQ=JBT{T_5?|Jytd;g*H=C1ktyl(HL^!!83;roh%_thug+AsdH z9{(}9@@~{}HaLDaH+;KfI-V?DH|tjWi+3!AJHs-YnL6B-@#79{ZMh>oqFIY_aZEWk zqL}R0&JG$DhD#TQ^y8gMvr*DrrRuLQ8fmPy8ao%dMs_DB&R56IHhXqgs^-SDBYm8v z`mD)cqAto z#>G4Qe9J_-v*8{*sJ}eIM-t-b?|<+AbmUJEALGx)I5z(PVfx?s6QL$Tm2og-BtjDr zCD3}`xWrQMW;St&4K)j!OnA0;5-~|NW8}mFY z(>sm_N)!gCh=WrlkPIb~UKqp10{(cOO(Wul}YKEB$7Iwq>dvO;4=;J z?0OQvnIvwd$Xe2c)k*aH2of_eiR?~DcEzG!#K4{QhR9GI- zpMm$GAsclti>^?umy zd{{SKEwnw@YR{(ybH;>r1*)EhtYspqGGiM^Nlk?G)}-8yctHn7+Ky6pgcWpz=9@x` zy0GfjByLS2zmYEOR?-@zky1J&n;DSK^dVD$^h~5ICrY0kU6O$|5O6gld}Rv0lo)Ry zVpU0DOpI?ruq)ClC>#=u^hHEI#is!{Qb0-l&!)bY-6O6Y13qniu*UY-N@Gf?kzU`e z8JcUK-ip;&-x-wE#etMOPRxRh&E9EA= zuC-EMlF!$xnXN|U+_Z6hy>)S=W^AU&I#bxv$E)vRRkYBHjI>ruzSUOOJYJ>ks;{t` zJC_GX?0wd?I`gcoW0-2{BeoBuH*}Jl`uW4ljdRC~vuFF`Czs>L*OLb)){V`T^NYjJ zAGaRvcHcj2f4G}G+?+c45ka>;UG06l+5UXF`f#}D0P}Kd>+x{!{mH?{Ge-n<^!KUV zxzWvW>*nbA_Qd%1*u?IrgV347@x`+lM|uBo$IRQ6jq4@*_1fP3&c&zG+uyG4f4g-w z_nXh>*B=fq9=1*1#UGoO|J*wJbM5f=ndQgUxx4C#vl7d`*0QZ0-_^_>XqS$3Ysb2si_(jS zy1Nfe*N+XCH#OHcwKpFc@BZpM{k`SzYs=xc-h=O!?XTmD52K@(Bi5U#!K>-!y%GIF zt9rIkvtW|VG%$y&iLIq6t<~gCV^)7F$I>gB7?RJ7XlAXFk#5a+zkYhKaI{<2TPJ8K z5w(`eJ4?z2tLnz;+gJJr4`v3BmfN@I>y{@g=En2dnwj<0qSj_bM~k$%wy@h&*xo?V zinFWqYJAdubz^9 z+_7%Y9DbsFpMm{5y0uM37Iletm7!G1bz!<0q zCI-WZ#WLbTQlM{9&MwF&-k4YLbZ-m?k{}65Q~keuz2{S++q$>QoZYj7cAK+gQIa62 z2xc*0L<|JU2m(qHMN!O{bIv*EoO8DGoZUU=T6^ua_pbe(Kjl2G^?p64>aJ0vfO?ZQ1^sDmpHt0B-}0<<`9oYh!Qj8S<)h9VXdXw z+E7<&EtQz$$pu*qLw2MwGq{lDUzqM&m<}_fLkn3*eL7K_LNlb%3Ry%o10e`=XH&rZ zFoZl7tx3cdrJ{=yA-V{Ue2R}bDrQK)->T%TcS?50_2%V{uSA{GG?$Pg;o|5RUV#F9ma16RknucT7wE($l7|6p*bwS zo-V40&ninxE9FFJGVuI#m*@oN=p>hTh8HUVE{a2`V*LstQJN5}f$Cd8^35gq2>nrP zB9a*ZkHf>mafHwS48aqH0|kYF*g}x03e?v3x^MJdTd#dn2c))%P+lKtY0hjK)DN$; z%pc6IpPwurA1@xRub(X*-cFv}_3WNjukREBcC@mf>hIunS@~Vnimoc%a6{E>PwU3q z(86@b)I{UlZ2jtLg8HHv&x%TUE|8hbxZ45nW?*_a%`k) zb!2qAePp$`dm_8JJG8C^*V-Li)f8OYmeN08x^O(ZaCtg;`Y?X{IJSG-zp^@ia=QEU zVeRH>^Zs_@@pg1?W9(oZP|%H!7rWnXcfVb4eZJWIbhh>B?DDUN>%Sha{(Jxsx^lSz z_-FshV9!!75aEY527&wz@N@EDWafB$`3gw!ONXDf_CIXxKW?9VKDhb)^6}5R#~=3( zfQ5c~me2G1jpq~Y*Hfpr6K8j$fO%eh96G-3-actxJ8a)P?%TZ>+rOFHyIDH^u(|)Z zzW-tU^vCY)e;(ib^YHk`+UA$x_MP13t*rW$l=_9FmgVGut*n`2;o7DA;7)URt2w{d zUw<%N+!~HAw6_oDFMl?D`rm=;|LQ&dTlc}wzP%qKn}19!ewv(m81Fe7>Npv$+UO`4 zuaFHCONUI{?t;X6c~q4;rlv5yzLe2emfBLC)!i%^=~YdQzXT* zRD!sC8C52TkjWBN1>rmaplWtdX9w^r4=5-;&Rs1FER@rVM3JVP1a(qec3>pSpP7VV zMq?r(ail1MZ?r!o*3TmW=9b{<8g1i1efc)n=$1%!VPL&7hz^PH--z!3a=v^8`t23y#VgRSuYdW? z{*dK-tLsQd(J1xL19q*cjb>(5*g&2q;0Ps&Y z0oFr+gNVq0f1*TGgdh~)rx!mSCW(VeBESNQ7vCQ&Ktsi-|F8V63nV4j*+$vEPWEyM zb9(dapPsJ}-oIc1?EIoVFj*+Hi~v{qyXE8THE8<+EW9KXVW9aGP<`YSUl9ez3n8(? zDB0nmSrI|0WB{S4XecTcj88+5IcSOiO_TWgXL&;7>|MiNyN23&MLI+15GWlEr6c@O zaU@Pq5I>m6CZJLf-m$>1$jO?Z6eTfDf=kQs;|U1dY#fh?qR=tS~1{mCVp4#~6}Bi&DtNDfr?Plrb4< zNQ7wFZB($S@H=<*o2ITUWD`Zv+zXZXDR(#-XC z;r@*NcDwS+Ny8uK4PQ>JkNb6ZTMZ8zEg!ckE@!zDRiSkfQU#A<6^A#;=uML7s*Ff; zTtZm{yE;7AN>kMYDk})ux}f};5LHcVZgm>7FohzH#d2eDybQoR!Ks`Zu0Dp*AK z$_<4mDbNBkrkIK~hF~?pSUCkN4Dshj1o2}da}q@=VHP`zk>sA9{UYB0s&9SSGx)N1 z@YmLkS9MK}mKwOlN~`bUbWatJZ?w(sPi|eEub!T)9IkJi%^u#29N)C>9F?wZssTIN z+tx14C>JO7%VU>$R(^g~OwvwT*_EWz)pGZge(pa7xnFpV>Lg zYa8I!wPo~;D`r+o+D1w$272nJ=la(t$G6)?m$jWENsaBeDyvsx8`4teQ_~zdG*`HG zv9R@Uw{rLA()FLiTSq(@8)r^m~e=W92YBipOPyDQVDJ1Y+-Tc5ACzFqD7 zakKyP{^YMmfS)&idjj}*^yT8;{2W*v>Rs*|Tpya)8(%n|UA|fX0{imS901SVkLwrT z56{0Ho_;yJ`2FPJ=k15TJv{wI2I^YQfZY5(kQ?dW>$_-gX_dgSbO_~K#k`oqBK zZP)%q=iX)CF#y%a`QwL`{oCck$MwC34S>d%f1N!1&-KlJo$Y>Ioxg3@4~J=nu(|=i zq7hu(eDKgdW8*&O?3?2JbKcRN^x#T#bSd6C;IC|DA7AEu{HylU|1)^=zoX~>HFETi z@!h{BR(~I#eHt6P8ExL}tl#c1FVrgsi$&c9;vNmVHJ4E(NT|+BsL?0X8tIk#Xp26f zri|UuBpc}i_?bU8U|F55U7s`04i@y+%iGGNO$L2WNq&c3*IQCDZY>;ZlC+g`tR>vW z3QXzFXJ7;dlVZxoglFf=lnC?{MXiqBQjGP&+}|KD6~-@EJ5sc zd#R)pH9uII8YoSm@j^qBG2!WWP7<0;N5@BFXwd`$o!}Sm3ui#VNv_~n8&}$k_f*?= zG^jl-z==q;L4<*9{{>H{s27kVFHB|to)eADPV{4?ddAT`8Fbsw01(C%6yV~-K-w`p zU;4kcb@|2J*~Z=Fy`9~wSFeAyv40Epg9e0?@!>>D3N=9-$JC@|7?U$f7+K{B{L19# zdnT0;)HFOg8jXwyKtzQ>BjPe!HK@nX`Uq7OHVMMppXz!FgFR46$MES zbV6hA{M>SIo?;wKN%qdg1Nq%khzI=BPZ5ffM`Chg;F3gSZW@r^k@6UrmgZ98iA5dq-Oe4GjSoA7)q8OB?}eKLlaXx zAv9ZO(o45M8(5?}Hp!2`BKk9lAv{{FBA$_-7@ZqK&L*J~kx;sOhyX_sp@aFpBn}Lh z>gE&u4ifaMCm!S%@GCXck(KN#OebbWA`=O28No0)gQm$&)r&Z#`O+%0steI3xjLY!Cu?BNII+<v2|uy-qUR!=;v4RW@Z!%v-;&J^X!miVbHof)w!`fv3D_la6P-c z+chw4>KvAL42!x(1byQ|fSHApOKBjj@h}&a<)NKrAi^)+9^R&JHWq1Gc&hDq}qi?%sKaQ{ec6IgR^5T#4s~=Ynf4hJBj}IUIe0=zE zbNTTY;OEJ|_<3+OaeO^?ax;2MP@L_TLdU5w|d*^O% z|Ksu1-*2A&&;9lP9B+J?9=og%HXucfPO@fKT{qM^6EJz4bo@>F@t^whkCKB6?$$x( z#!klUB4d0yW^;%A@U`^wfA>B8&*=Ssk6-^|>iFmM`p2=U%YpvGzJ}$d+Lfk~smi=w zoupGK>CWS{36rX`Q>^lo`huidO^jI;S}2b)Y18Yf#Xa59uCAh~$>!~?o`ap%wfTzi zp2F_hg0^RP%3G8Low~B|iqerPMYDldr4iH>WmF+*zAd ztdExG#Ypo~Rr;tbz6Z|#HNY;{-`?7SXukvFToZ{PQ5Ur!&nyMx0U(668uZ+`Li z1qa7cf*B!E*)eG8wS!L-Pe_m^O6vJwN!*U2r3VN z%LCD>P@*1(g@mkj55?$5s1kn;?({KG30Z(J!a z9f=@249Ers@+I3QFi@OyGCwhZABC4h`b*-7dU?Fjm{wGlT~MB>u3;-0dF+~uSW|qk zE|RQ^3Na=|ma(WssW|!bzg$T2gg|);QJ#iXFn#jUpqdPD5gSs@hg9ddmovS}84z;> z${ZY6L1wg2C1VNtWxjc{xNN7+dfD6lX|CbJg6VR)=xVO)!s2Q?U z%$O`=0!x2X(+IhL5^}g_E1Dt7j_*563(A+p|Z* zlSdsZ_dUxuO(VO71FOpU{i4}*eMdL1qmes5Y}lW-t}nFA>E z05aCtYs%fqnnEVlP3K>BZ-0D0eER3c)nBW(k7N7W-75=&TWinj?;mbv&klMw=DXKs z`!*Lw4pwF^c2*xxffRrI=ZEwE_;~pH`SJJjlRwV4KkhG_FOTew_pJ?duk;OXkIr8# z?R`Et{Nrfn7J^Oig{`2|N{Bvu$uNSAEPmZ7VPoMVAp0-Z! z*A6ac_Rc2`uP4uMmv8Tn&M$ZNPNp`F`nOKIx3Bv5?`99~H!i;2AAS08@bT&N$HVzQ z&kz5;JoBNU?^K!B>|4+Z*EGY5yZpKrqqiRt);Imq;4)T z4>s8+yVCpnijSXLpFVe=-*p{a_H3T^Pj0lgO<6mZS{oM{i-t?YEmCen4!=XdX=kTe zS?MhjZkIl%r#Pub9gxc*h`6a`<%W?dP0vXA@NCQc+TiBy_~z#D(rnjwU;R+Malm96 zGgeL()lC>{1~leYxw=X$E|nVEtK`;7SzWcUr@e7vpnbfjxu>SQsghG{W@yYA#!6{L zLyB18jm5ojv~%}xB4D9mf#?Jpi5^G@MEYP|p%`bRp9h+Zr(!WAj1QWK@`=FtB?aIz z0{pTG$Rx01s4Xbo^A*(vltQr8<`DQy?<}4xH4_x8v}LHggR{MS>E7^AKUX6D4b1ly z7!C4-yo7mp(~wjiDOOFUO1ueaj^r30NB6gOF0b%GC_ggdr6cH#+i(8S{=vy1L8%m4 zW_Utw61$iys+I_h7wah{fMo;#o=8=YZ$SuF7wWGI^VdcY zv=M=NIzEr;BPRHWacDUXtHh#YNT|RINbVSg4>Skj&GGh=;*dfNPEJm3V6ziYRMab~ z=P%J-Z-U?b8vOoMkcT7G#m3Xm6$}_MKNKntca#L$3UKxkqDy{=gE7XgIL6)>>1v>P zX@g)YqMrhTlcC50Bst4FAjLJ9;TD$Y9i0e`iHF6;!s8DbXcZYFTyN;>tdmX&_hCCt_LCLyVCmZ8$*_6k;>#S}+@ucOG@Q|<{X@WK`Qp>#e0r6I8`nUU2wcnvGWq+s{e=P&dXulE!$Rpk$e z3tD3<`Xl=1B4$>@XIH2b(?Q(>0nP0}Os#ifxuTVsqSdkDiII}7x#rI8f!>|o*5&%9+0w3slHPek z&$P0ko88otUEiKr-;&wYBN-jn_7A9Q8#GnTy^7){OH0exT2J4+p{_T<+=5k?JG6`j zmbc-{S|YktuTS zWODyv_T*;$;_Ce5?0D~Jar0<+-{gMy%kukwRNg%4&#&?h_C#B2S(}TQdn;Kdn}Uno z-2eRE{?DHSe|{Z&y6L&P=s&+0+c_GW-so$auv#Z-iu(#BjY3{Mo8O+zX=NtYFfy8i zJb<5Fro;+$pqv{h6=hY{mW~6$xzsqf*1xthwX?UlyE(f)H#RreGu>7-U0FGAuAeDv zn#^w*lGk@jDqEx_O-fypF}JotNkXYN z8vW{>jh(#>8VL)+V!{K+RD1v)ibgoY;Z8m3ae8EFr41ldAua4~p9s1JsYr)5SYE7Nn# z5^=RsSgGKa$vG7T(fI-_E7qGH;t7x?BE&5u$dOD$#>Du=#^K@<;9;RQNF>O~=`|eU z9!>^xV_f)Q_MD)%Ec`2`@4HN2H=#c?FVIgz_0v)?dMegH^Dhh|7{iH0Q6ytjupxp} z5Q5Jm;PP;IB?hnZ!{#B;a=4EKjLk#A_)u>)#8-kx@NpPfP;woYndn3E{f!L%B@FsD z$nm#87n>lM3moj=MF0dHhY&pT(^(SqUWf-Q)Kwi~rwe;$2zN3@coc>~bQDBB5tZwQ z79;#Q5Pzl{A;Fm(=S+=tkBIY%it&yL_ezcSji*4Pf*?^ecz8G>EC!Xp3T7)9nhL3^ zT%s%!7nJAftCWT+rM66=Di+9!Sm}9DAsJXyjE6_4ofFm8f%4Wq@QoerR}bPFOpqg$ z<{2LbOAkY4(|oeR;A$yNR~T!kNKsd&h$~XqRVnc$@uYkzSrbXp&}qeKbPJbW%%La} zDdL21IV&VLgPfa_HA@OjB1u7EhODrpzJ6?Vpm)|(*OOk^9%`~;JH}#byCbc=tl?z~ z!0^Mzhm*&@9e@1C>ecP|!A{@$^6<{a-1*_c&Wzt58^2sNpKn(!Pa9_^ zN|zTJ=N8+Br<<)k=F0xk!gjT|j+;}LE@;VMH!_kd;xcN*f)*3MwLGy@6PzmwRjLFH zEtc7px`nlt<<_*(4JG#$Td~6^~I5ySzdmqSMT1ue)|&YipSCcV@n4Y>qo7`mH2+zYrtE3MOtYPD=j zeq4co$WA~chC%35?`uh)H@=`J1f$KM{{Hc zz0dT2mw|RLKrUF8=vM1@G~&hH7wRMnhuVN z0!P!l;{(A_Shp~&D}YdXBq}xm$K-|!)T#Pvd48o-T_Mp_=N4MkC5;-BHQ!jL)YmK6 zdL|_!021lpLUVErcXE$$^NMkIrMz<|+dzpnSdv{B#XT_;%mOGH4&kwI94_!oYy=Y? z%tD6A$Wgkeu);W6VIs9KCCZ$gR3VNl;!&i@WNv)0F!}j%QBERKz(nL`!U}Sr#X@Mg z1XeBdEKT#PNegXZCU+)gcSZ_ZgK}H(sy4i)H&odfp4S_voysvR=4+OX075NSgOzua zWmluQOI7s7oRCrmy*7>2!O7|t^7^!SlePN!R_#Pn!Jt*yT_dV9XB25-Ws(?aZcMH! zR$UmcGshb%86{RmWi3X<_YlRs&nDY(@E*B1xCW2Zq97`WufZ2xNFWr4Q_9m~thp&| zC28%oDJ^x9@&4lVsfMG;hTXQtmBOZ3e&cX#bvv=Si%{Q+skY*(tmK*|dP7%cbHA*4 z$j~#@+&a|QGEmz$(J;QyIJHnaIc=VpG>(oG)LS#l&5TM*vbi)_uZuC4q&9cvTYD{4 z{nh3!lc_~(>C!X}8SDF0tj2|^0qHWAOE_&|MUCB=Rc1={#-i0m^j=8;5l`?zkGAC zd~-Isx6!sR4)Akye`V?BaPRB&$hSZ;m<4c<|xi;KTmO=Yx|EdwVyl>&Me8 zdjLPj_b(SN9<~oo_l_?P&+ayl@8-6zhF4Gf7LNwz4yN|*&-d=`_8&i-e0w_o+r{Bu zYwO>779T6y=h@8@X;nQjmd?nY*{uCr)5pJBzWzu1)u*bPk5zXcs~C`O2EQ^%*pSGx(o;&K)6GJm zwM0;FVH6gG$fU7ay{xUvGQVhv2N zK$WvS3RB5tsG<0A(l$7ofmUOiDxmaW^6xJCs%asW_0acPl$d1Re=vXEllN{q4A3;b-KtzSQk^SEwVIXG*ko`Mb z1Pl^Ifn-Ox=Y+X(gB`O8_8ERoY(H-a9;qau)MTVG04Dc`$napPKUhM9OG!u>87cC| z$O75c?2f8hcdik81VRbWLy*+_=6Sg>p}zDg~05iiQuG2Y-(~KS3u9zW*Dkv1yz## zO0l*sx2RcdZqt^wYKogRrgj6@$RRPwZd8x=6eq`UR~NdMJKfVW+RcMz?@hKrk!%8k z?PJ0`(juYh(a@X>JfBBovGI{9(7-fUm?SX4n2=D;iZy3Mmu54{g{hT_ID>$k!@#CS zV$z~usZo#&I*gYBmt`O{e0Z@4SuRFZ@{tx6u_iUVo{`uX#cCi6>T!xDzk)VDWgAx1 z;3sY(s0ZS8(>&v%s&v~@d)`@hGgx}i&L201Rb~(h>7nJxsU1A-K!IemE`PdDH$9{t z?p62osC#-O4Q<(_wFznyjjzF`$q3o`bgh+LKbF%vlhNGom&fxK(_MJMUeX|JVJN9Q z6jw}w7od<@KVXh642R^>LMx@prNP|U(Qu17v#TqAcB*D?zIlJ7b<5hasHq)DGu4yL zbvR4SGdvsGqC5N9UBlA$fxP-|O;u}gVTHz2q3P(hOpMi!_LUB|6^yp4Cpy$M##F78 zs+QB_ig1B6M5&LjY*N&9nwtme>$)uZO1Vs*nOm5yHnCL2X(hG1sz!08Ra{b$Q(Dfq zR7p!qg(?l7uT(^&XJpI8HJ#OM!&Pkq#^#6aR%`7=-VZrod7wvpN_U44xjG} zecA{7^8%pj=flJMjm`79)x$}^LJw|Mu0I|gTI)m{4OMjo<@IV^ zRgTD%m{S@psiq66Ls`XPOg%?jRRZMh6rDCgAx|}#R6Tv>`MKh$;gae8iuv~XmFD)X zhJgd?@jV5tJ38zw*R;o-cmL=%;G0N;vNm?*Jk-}p<7l#qdPV}Ku zT}kLyFi()16X?AS$j#LU5v=J1+4kuG#*M8NQAg2zQ#$ z@bl!OJ$M)op1&v8AC^P#laR5wfq-N}xCnrj2q7FJ_w|*c@Jbwv5A(`~A_Z75+ZQPy z#9CNPZ77=V2qwIMqkr@E2Kl1xsnHlxJl;2%0OJHfC86GOnrCj1hnNTv5x}wl2YH~K zD%d4I*h?D(Eg&J(cwadhBS8|lP*NtCk_wJWh9xHXr6iz}qI{C6hS%d`@g8WLMu3lnlQGG8}K_$(8+JSJ>RA%YCw0zZ2yH`{GugjOR+7+Q@CP7Chn-XFg zIn1v7yzzF;)Tm}+RM|h2*E6i@9u-;pn8i)0g-tYZ89Ggk%PveT=~E8wDhBtnT88}d zMJQz=EH@HW5E)QP52}drF9}9!aEJmNxjYJQq{9?ZG1h{jgC*_SVuYnaI5MN2TPj+f zEt>7NOjp;<7S)eSs=HHk{hdg)NSe z6{K0}6*VpSHI4b^id=JrqNYw;XDuwN)bo|O)D%XlAiJ!wqIIC5W1yz3yR4!{S!$7$ zSF2ijD#n+F=C&87cUBe-b~moBCU^J7cXp@t50);@HtuegFVBXzR$6C9x|gO#wilPK zb^&`l`}uJ8^X};PD_YKE(&04^MtSeeOs6 z^ZNAn%fqi{dmm1=ANGOTdE9#r@1Kv(fpq`n@c3zW_j+ahbPk~C;qCg_hyC5l?UToY zt8Yi=U$*w{XIC%B)-ISJ#Fq@Yp)-xDsAK!lssR$t1d?AYlw!{Xl+lH zWwEScwOcvR%B`x-GL|Pxbzy1Tn4FwgF*l0Cq$J0LCCAd!;>k3Uo41RTi@lSJqrJ1! zJ4ctd4sNd<+<&w8eC6Qe1#$BAeNQDjX2g3HaNy-qSgpjpPGpxC4U&X{O4+aVtk-2i zuvJUWiFb^n*#ZoCV-Eto2L1XDWa|cULH>dyI|RhQsY#gNL~KAT3LOCf2fMhF9Dwp7 zyVxUMyQ1E|@p=hz0J-DblBB75)f#D)CQhD}q!FYS=dnujlZq6PY9S?;O%|sG2@-?& zv1DEhi5nfri4I~%2XkU5>{xFq#>USBZ(1sZo(xY;MyDrW zQX_nsLCACrjN$DX>+O=_k6=fUI7t*9CrTtsC@5j8%d+#Uc=`rWQM6y z>UAk<4l03U7wm0Aaep7-WfS4{D%8O-#>JiP42^bxQ{N&3Ul4-dL`OL>lb{*NFmVn^ zBBQ1A17bM-(Gm(>9TTZ##GA5XP29vXDYIM|n=kZdGGGxjaAb&A(sPFt2rv2BKl5{t zdOo6%hcadn$}_1|%y=tzc4d9e##>KY1Hi)(q43l1;<$MMF&EAkQ?K zXBf)Q>sE1_WsGVrfY9L5)Uev@_$GyHxH*4pNH;#J9vo5kk14t)*cDwds_GzN34v$C zWa&tZmb;T8;Of@}N6Blesk0|BD6boVvvIJvcMrp0cSTD*i%c-+@@ur)yW&>sNPMw~q%OKOKJh zxPE;(vboYaHQYHjKD0hJce1hlcyj#v)#>-ElRqwxzMTT(+`QS?xL#jATbetZo!OsW zIG$fQU;g)t&+UdEc8)$Bp8tM!_2csT=hfx+=SJ{HA5Zok_jVt4_n-DoJ{_HXIXQgV zKl-?L@U*jg^L&(G{p|UE`-A(f^^4X0huzaJ`$wNPH?C)w&&JlT=63H_4<7f9Kc1X@ zIY0k;2E2Rtac%Fz%-+M;;oZ>j{ovkJ&(1~H>C?!Ezm}fP_Agu&|wKPhjjn6Mh&MS@MtFm?ZLX(P} zFG}PynSuYoLzr&c6;IE{+pvC1_vWzox@2^90sIB zjBQl;)k)keSugTqLBe2=E(xSg_^mVt-cUfxjBuo4UPHZpu>rL64am_A1oQjNH^7Dz z;S!OG3`v9lgz*lru}8gl4gKX6WzcuiZg*&=&wjJ^Y=+(`oA3f~wK_vf)N? zjXt|von*+3Pzfk9E#$4NzI(yTPF4Ma<#Py!l)7lgJCKVo>42={8rkA8FLAxMsugGG2-P%3Dy!p^)v3A`k*ZlLYgO`F^HLimk<~fjwK?>9nW(oS zf4I#s*{>fT(T)qmBr--Ly<;RAQwn;LKqPQdI(P?Z5in47#M14@9F3mXm09iYwc}s@9!HN8=09K zn3?FF85!T0TfaKF`2OSa``>T={LlNp|MTXrzwJFePVR1Z&P{gAj`XiiP9LtU-|ZcI zIX(H~dCw9M$+vDdx9_&Lt~XXMRu+%u=a1%A&z|AAeX|LSn^)^Q_uI!G4=?^Wzxi=> z^XD}%0`rqk#{fC^9(NBu?4N!wa|`I@&fde;*3H`L`67_e*Ma9;uP&U;Z``f# zf8N=CT3fuBoJe%6I&+<_6G<{}WcWH4)d7)LSF=r?ZRKPz0e(G!S+E!X#SE^y! zAR8(RGsw|wUJzduD>20iR3ejBRjrj3%d=$Jd_|5@FK1>XBTx`91VAW|)}3u#oZq-O zy>fB+#o76#!@D4|ZwTH!I>42m0I!siI`T>NLa&nKmqiI6AqgZ82l0bJ`AME;K~OB# z2IUO$@qXj*9`xQ81c8EZL|Y=omJ($jmF67~Z3iR0u!Vp?j?arkfNg>te|>J?Xb%K@ z5GK?!Nf=q!kl(S;*0b8*Fxy$wZxz=UB^YD?Jc*(#ydcYmlZIf$VlzVn*c39CLS_dM zGfDpG1VWmBKpGJe2DL*w{^Ireh3C7Mo=(5II=^sreFgLOq7&VdaGt3ck5r^*qr>`hDv4$I28iM3{_@sM~GLbk2#6Jm1$Rxm0F%VvWyCA?>1QgL-h=b%{ zkX%0m&(B*-a1r60MZTUg3^LyzuP2f9WU3}0AO}w6_(W%*7%Aw~cnm8Nok_)TNEkK> zl?6qn!;x$vUKSs!W+x~G46!ICH=mKKPm~%Nxg`u;9jmyFqpnF|YojTwKzJnDA<)Z# z3U-JDzm0T%73utz?&Ol>4o>qxv%Ih=&e#|mY}iXu*l)CmxACz~8R<|q50ftNPst5R z)5jzgB>?$7%E+XfxT%%O^ooLrJiZSj+LQXc+rci1WE)R*WyK(6nWW-8YLzC~q9T@v z@CFvOG(F0kKsV9S%gFLZs;-TqZo~8HyfQ0YWsRu3dVhW)N}?m?$O$yMTYLsCI-8WL zN|ZJ#^M*=Aqh{eug=W1)H&tCQSf1ByR`%Aa`dS1njd6v=0m3|IdIls-gy!Z`RApHW zgT}et;)VU(;c@wBhiJGuu14k~i-Joi0Yz~*L!?U%8lj@%i<9EpRnnz4-QjHh?xJ*Y zhCekeTiDEB-7nhQFIrtP&I}cew^mIzwJy|m%v206RCSLPS=;2T-I|G+n(?Wc_O9Zp zT0L2PHpBw9&7$~z^q{`e}wW82eER*Mk(<1@@Wao=Z8ftnbCI-f5 z+d4*?8VB23#(O$vW~R4S){bUZwkB3qW_H(?Pqr3M_Vyn?U4Q?_?azOneENR);mg6( z$HkMQp84s))tQlv+3ADD^_%VEuc!ZN&v6Ox^Vu6eJg=Su0NglVSw2~O=HAu%HbBnX zt)pj4JUst=3`i#M89*8j?sxVdxA&iSzi$AxdG_^LK7l6y2n9e2Ko#IEz~uF-wdIqg znWNe1)2Y#;F<@M}S)4tc9@^}k+8f%rpTGREasF-b_~X>k72H z=+Cp3e*oT?(Pm8yItl$5+_2s{gp8m0S{Aqscegcryjl1!+z0UDwmTvYK)`oJY`toK+mD8iL!Cpyco1~>dR97Rgm^eB)C!ftx#0ZsSxfWAUf+?sX zD6Q0@36XKGL^WB@Y%mAP^ZePeXo)e$SfQ>m6*m>@Dhk-Ltdy+8)bxbVuwW?6)7{O< z-of_0{X07shZlBlK)(TGd*gxhjEx{Nqwwq)j5Ho?W)s_UDXjvGCB;b{0?H44AtHg& zd_h^H_sT?kZXzj#f(*gBp%J!F4-gCt@+Z8Ah;~Rw^GM7Dhs4`Mi6DTVFFpR%_tq8! za(?aX>nhfy8LPMrefo}xvW8)EX=kCKS(o2zkk&mfwFCSN$>rhsEI$qt#bKd2DQFgj zkP#4&9YD;$`?JvgX}-jCG?|6{6#}wD+qn3<+4;DDTx>z_ZC<;1+WR7*F#*tI0*s0E zPJz2HoL!Uby|WxqLJv$X1g}B`XafiZ6pVrj7llA_XfR%ccV;L!lM2ZSfwF_)JQ9FV zqyP^f6d^$SO7QqRGB%e4X8XFckp7DBq*`8-nS$iHAz5yOY&a$nf=x!^vx(kJv?rI~ zEF?IJiOynwfS+J43Z8|4Wx^psoQDYO2?#BqzB)3=7(^)yq-jY2KZCR2p-HgFC@`Jk z9Z&F1K!a0}&@?Yd7Q~0`hs`081QEf~sA zEU$D9B3a;_Rh(!uH?CBaP$CP<kARK5^`GtrIvtnF*u8d zNM|C*gf~Hf&LLs2q?|}aU7l{FQZZg3n6Tu|l&go-`CYkr?Fwb5UfE;GX)KM_sc|_u z?u;~OdX6tAmmt>0m()pnCe$Nyvie zWo1ROQbOdh6hR;+1CL|{;yCm$8JB4>7xoTxF03szbhgR08jeKFlS#!2MXpjM&sS+I z#fEx|wYRHhe6nkBx}j;Xv3{hheWsyyczkqgd-rDN_;&5^c=2#=dS_*DWnpsbaQ)(G z|Ka!Tn~z)9Pg^&4^M{B1OLIeOvtwIxvq#IDciYF`PR@Uvo&Irh^lATj-}}cypa4|v zTyJb%tODfRyWRTtWcOwhKqbJ;XKdbW1Fs)GY@dJLzyA8XT6FaIES!LL?%r(!2nBTX zS!HjZYXcZ>cGm7TCeEh%_6G*{N2ku_=gt>rPNvrH7BBzYJoz$t__25Qu6^&e<>0pC z=(=bBtatmccl&5yI;5DV#f^wgt zR#Mque9355(X^syxm>f{D4uM|=xJp4_wf3LIPLu$YbUp=jaS~nH`gS~C0S~=qBzr3 zn_1tP(K?jTJkF_|6xA#jm#;S&7P|yJEevycyvD>Xu2$AG6gO9Ds|q;s%!qg@DG29J z@I|9wU{4pQm%E#@owL(>S2sHcHye9*8#vxOl1|EDhKiC%yhuMzh*w@Dv^3eTECF5^ z=2Q^;PD!>A{J$){2UA;Vg0`)8w#iv2=bW>IKtckPbIu8n1S030b51rG8xw4T2?mU9 zoZD@8_ssOp?(Dw1?-#1R|MID4Ue#SsJ(B8FsidmwT=zN8IU?QBqi-5WnN9)HRYEis z;rx1zLrXEMaRxQQpo1C=V2!?@LZ4Suo{3=rGLe9Lh5zJdD&S`BgB(nDs7@7cE^q2K zv<^BO2VId4tGPy99w;+4xm5|LIBcQ2bXbcDZC0Z!I-FHOG_sTAq?<`JGmd6JQT1?| z9?sOG?jix1q`N?RP8Kfbc77@#Gwn})qLCmW5hBMSRXCUmQc#gus7gcGvPpqLW*o(; z#WQNCq$)bb$AURHFuM@plz7SBby*~y;%FAK%`DC7u>7-B)J_yRqK z92O=0Q_{}H*b6X*0=gAJP(cV9B*{tv8*%wIQkI*N9c1K%X&~R9{DkXZ5H%R;z=GU3 zSb&I&Qt3?`UJIY!#1ST#93PfzL2}E$Tuz>VkW~cFl7e%}3xQ=>Km!EpB+^|>mX*t} zOWA(2I2tZD7->ca)#xG_ym&_?&0ov5R}#xDFa$Tfkeyj5%!SGd^GkE@7u~y4d?&p; z6>2HOyCCcUO5{frSwTE?23vZMExuW%PFEOm6y|)n6RN49Tl>ll0~%SULObCp>9&fR z%tZ}OZlxKikz{gdcW77uDG$JdXR3IZfR5D|;6)vGcM zm0JcSnl5J5hTc6Z_l=fX5-f?TpsW6adFIrv*^JZ4a(%e*?c5dL=bJz_l^0c7Xz~kq2*cc;;5oG zK@RIM{&GrOjjfVp*%s(pEO#A)V%aOyxXn*DP>C=Oq6_ZjTWt02r<9iG9kA_Ct zJwX$%h>-{4LJ$HJrkF@j2$;qSu}02Qa`6E!-h&6}Q3ZM;!pNmKl%@6Y$k@d2(W}#e zsmZE_#%NP>Wuo342v@}uJ)Z6TLliN&g%lS>Jm4>hkLQ z<@@g^?>@eGbM^G}+Bkl;d-eS6 z+u8LWZ$ACw-Sxk{e*OE|yWcK8{^jkbzrK6_+turz&t83cdH(tM^6Tlz`-5bTulHZR zdyyPZKOFw^t)q*thp)dKzWH{1{^j8K{qE8A&iOAVNefMC=RXgk?mxTQdvdY8dHJX7 zZoS*x`ntRHc5VLr(ZjdTwm%;o{&IBr$MKi{IXL}g;^<4);fKb<>xQ%U9p~45=dVYP z&nJ#9{=Ch|pYZ&7^Zobj4?mtI$E2OU{`!9$_2~8d{`uVTyQP!s)mN`(F1Nb&Co3o8 z+7??;ySsd%&O6s0n`o`>j=Pfl9B6lSwHcZkrSSweT1yKj&~<&-#wlvcN=e(MvFoU| z=k;*y@s$1Xn0&lXK02-*T`-QUTKiYc?emuUDN~}4W70`IN?Tl4+imHaa!joHr=P^e zcA9Ee8beE6wy9oKZ-=b0Q5LJ!#1gjJc(}dB95j@uiZ~(~iHwGU^9!>wAlX@v?9BYM zd)fDIWTgNy(g69un{+lrtD!fA<#l$BPl_^9Gwjr?8Uegngif#^ae83@pJ_v;7*Q!o zR7yEEUC#k~6@*4VueqAnRx4C7B}7zpt&lA20vG%ClaIxWtB=ARB_!(vb6$dQeaGKtW{64E2w5R!-%IF zu`~mMZh+DZAiA!QrUj)D0QvO0Fm`SMDK`b2c{?}j0T7&pL;%?+a4`y5jzlOx5JeVL zlY(_*QbPs2S~NF7VAPTc5h~Kngxa|8c8YLb5h1`Q26-4S z73Zgu!fZm2ohB)IRtw?UZ2OZB8kdB`AN1>|Kz8Gk{6&ARI$M^4kE%p zc?f6&45lswo8W~uB-D$=S5uSn$!q0u8tB|OSr8-(tQejQ#9?N0(HUY$maH&O1q7F8 zgEfU16NYS|P%Rv~qm&ac7Dc?$pidNx2s|OC#f!K3FpdD)U@H`try|)mA?&+QQ7WV~ zJ*VW}eZf!ZrFX!l9JHf=>;((LC}{*Obwh=^Y^LNEg$F3prIwjeOU!A-&OAkeXdRZB zCN%PXWyQEv+-DZHTLq04YDfc83A4qltYXG(9^!!%m#5_;y=qd-#b~IcH$`cU)x`rX z@{#7!f$HQY#hqH!u)#Q?*N&*{i+01P-rlEIM})j0u#^YY^RY5)ffNN)vhY5OqI;-m z{$OeD&1Uz-a`Qn?XjE1?$cqe?L?;dLxlmF&Br!X|q6Sqcka{c87NohW_@TP8hE7Y* zm~&(*FgNF%8ms7NqJ?c3j}jfI0J+2|S_+|7T{0FiZFXxPb(T!mize%3i`}aAG5zCt z>-Iy>?qmP6NB+l)l^Y8!I}e8sH)mgNZNEL*KHHgE8XXzy+}xPGJlp+rb@1l!S=4P- ziFxg{(T%yeslI_kpxSS8GBIQ}hRnl~rBu3JRAenJ)(bg03c7}iuOt?_umu((SVza2 zO8AvNM|XGU_R-%+Y-$6Mc@uYBBCe7*VT`o-?IS1*5ib@JQJ*>B5xA6mEHMz$^-+vkq` zi}3Mf;^bBP%hUdY^RcrJ%WuDJy!m-Ond|SHAAZ?<^ZDV~+l7mFOUayHEnK`=eEn|u z>U!n+YX0qB*VeGR%O|K*k!ww&p0Iwf#@kop9cl`VcU6z~hPvA=P4)8ndT~6#4%Ofr zhZzl1tcJzXmM8Z9i_W1>i#4b7y4B&*$w5*7kfdkKG`i-WcBbe5P|u9V11 z#S{z*mYt2v$w6dhBGMm#Qg0SK`12$-q7Wcp=NXmgrVyjfj`5XbYX~I@53K&HSR)*xIzA}NUjA1Jy zTa-krhG;iXoeGA9#V}!#+DSD)=*B{tF_)&xrl_*=nE(jyK9ZjUqUEK-vhL<*r51oQ zVK5L2_2+fyWhk_w5MGf1SKY^2Qb@i`TDX8&4P(Yhv?vQ7r1X4&O>oP0bIAmKsS)b3fz$q#)S&O7N zC=e4CT(%?hKwB&?ZI^;4gge}We5Gq}`0eX`Pbw%GQ(y>eI-?ITzBaTDXJ#H=Sc5>_PK43{Ol zNRltrqO@MREynZIivrE1l^u$@ZdGfKyroSLixIsJltYJftH5??mWfyBEfx#}3>%$_ z)n?vU6??R@Xf`2UXpt@TDj$uSHm9wdGtTwt(B?wRi^pSUfBv=l;?ds3`eN5e&)DS9 z-uA}j@!r|<&C8vQx!$(U+N#;np+_srEiG+ki_>KFaJj`y8kb9AN$Ff2M_^)ejTD*< zi>#+3YVf%sT&{-#F*7i>5?<8r>g(%STz~xR<;l*g%jNC8*@x@%4>x8OSNg^#=N@lQ ztUsH2xHY-5(K9?**Vxg}*w)=OFg7~9vbwRpxjVnMIkCJxwf<~j>&4vG;nc(Z>9vEo zjic4=v-RiaYr8KOx1P;C*%)4$9)CEqw7aGi?!+ZV|sC+8nuUVeG`?T^GJE!!PVsRSEJ|e z=ihxf6{%f8m5zV_EkzP%agWVdX3 zoYmRMYwk7<&jlw}4Gq0ar5(f;!OO%9N149XR@oD39IS2{h$Z^#s|P!xQ`4^DNqx_d zp?AnJ(C_JOu{U@PVLgtIhm(`U*|{-$!{9T038%y0pBv`-dEqh9fU(oxf#0L`#R`d83Z5#1B}9(ts!Vj zWma!}-e4=Nvk@K(7TApGYGqnQMJ|PY3r)P43kBTC0o=$;%IA$V#DjdyO;OQ}ic)}H z{XnP7P^q&@mAMiPv{;W(_=KhIVxdEl+&hp>GNP%1U{aARrED9EX+lx7Ac`)ZY0PDs zvZ)cP83M;j~>gjEF!L$)lp z0$izL^?EAi$%jFeJo}I?ifiYep!-6AL5O699H(2X#U+!ty z8m?W7)(rC!J-ApmEio*spLAA@RvMZ@MFC&7pfr=GDlB#oG*P;#j;5=jX=6-NjA#tu z%pR=Si8NV|Rs+JRgS(XwZ&{&7A{-3r*E{5k3HFGeJsA?t#rZRF?Xw}{-jro$%Jg_# zw>)TG90@;~Yub9$@^HC%b}HJ@9*9-7cebrA&aKT&kG8k04fh^BTs(aG=*8~S^(UK= zx_XYhf?uIymQ*nK5AN zSXg@a{KfLl^X{qH-ifKzt?j2T4i?s*PCwcnd$>Khwl%i+u%)Np7pk;79UhM>8jW`M z_DsyokIc;XPRw;rE_F?;whb@$jIY;q&epU|bd9adJlb1)`eOdc(CRLA6Cye_7R1dHn%6z!d=y0)_BVN2bF zvt}k(yVlfvFxB#U)AxL}bZ(?*e2CWC%xY*<_Vnx8+Bjxw0fl-83AxY0;H^@3PptpZ z==l0%_w-;@M~AJxO`A@?p+lKPzDF6D1dKs+B+*?H+-a3PRtx zmj}3+1-K9XDFb;osiTVWdrsrsuq)T^Dli*!6zZHZZDF|?CpA-qCMMUwq3MMrtr)M9 zVs&MBeG$jbWElUvGaE^pnS>|Plupsy!^!}Ziu=UUG$b!07oU<+_~2e{W?DWt8wy9T zFxX-=Re>QZK`8kHNEraG0w9e5m;pfWX7Ovu#Vrzky@DJrhuLI6eJRRfKwFJiho0cp zll>Z6M8&ACV8%)*ks?BXn}laFa61iRArpf_af{Z}%HdcFSi1Zo3zDS*GjvG0lLE6~ zAwfE@idmRMXbp=HXD9i|3(^EZ3c-m%Igm&v9PL99Dski}KFLp781sMnyA;Zy=5WF3 zf~*I{DXEeNnTkS8IT)`%k+c-LUBvUMijw>cJBq6PqPl8vZ52D{LHn$Q0SC}$%`)p! zROSqx;wD)NASwW4!)>DFKGl=SkAivC5Pld`l;o!$EOP=y>Qo9B03!lQtv3~+d-BQ@ zhUW&(eVbYf6!tNS$0eLWDWkuP)~P_(NYjlJKrt+}jEu00*;UG9=#^@`Uxje%Q2{5Z zCeCbYPq$h#-L4F`<$+U^9nfH!os5AXbD)aW-B>m{TD!hkKQU`D`Hg}KC$&UZfDaRR zW;j8VpU=ZWjn?vpiH4^2o|?r_{UE2l6BTVE#Ja_`!`AA-h`G6{G*U&>`M?qvQ0T^# z)zH*UBzY~aq!KF&7Zh1?B?e%b0i-g3w3-6Fvd~gq=#;}Nm7Bkp!Y z^r~kwqc0w>PEUK*asYnF zEiX39N`*O#Idx(0dMH{a_7W!c`P9|Me!x&p-a}A4z_``{g1jpHB}TC9{9Aodjl* zoXK2%K1t?Z6#dgh-|f9PUE4aC-aeXtvOm{47%NgS;1nQ?0lHIg`!3{uDl$70UjX6Y zct*LmJ>If9(04RB_v!JI-_EywyxINn_V|yh)4y)-e;?fW8+_F0;{jQw+s z-OJr=YlDp&6DZf$pOuwyM2v!yXpY#HGt-7Kz$ex58Mm8vmM4lof=wHR3IpV z;Td@ukQ;aN04cDff8Iy~0bJJXN@r?~KR+G>hkQ_n1FSPc%Jp!O3c*w3*;+b7&!ZW{ zbd#KFQPOQvo-4^u7)hB;R;RO!nJiNpMSTk`1#k_Sl=5^K`#~n+_MPloce2t_^FT>{ zf~jzn5K5NA$%+DW`F%t=0Hpz7%|GFtcas%fR8K2vDrVQnaXu-?RFbQb;GMQ)h;A#% zYoY|S|Kca3T1Kyy5CVLxmz4~{L5Er?Nq&l&)W%jm%K~EPfqWa9rU8=;aGHw@v!Y;L zA~;9{he_}Vi4Y+(!(>s2QWB){y#$sEL$@QTP6R7}<3&jPFhv+7tKtHsk0saOiUlA6 zK8**u&Cdq#GH(|F^UF|Z9gSk-(o90CO~wqHixU3QY8O8d6vVxBj}BsyXP8SD;?W0zhKDtEfsT zk7qHwH{mt_CY;9Z#1@SSiY8_Bo+4a=2lcbl)wo+i@BFq*_jJk(yD`gcN_S{;e0q3|9aU$C#T=MKOfooJx3$;4y5$Zwn#&yyzE*)? z`&i{#9Gg`LrD9PUyRvDnqkU(*?qMP}B#m@Zz0FKdyCgJd_YXx3?UmAKrM#wzWC}w| zT{vZgp{pfVRN_ngcv+x8Y{)Iq7RWT<3N5grBD1tOQ!dI;36b6kUb~0i=Yhp#*+Cwx zNsjGN!&(*Dl_lUBHNM5h=nRVbaC^Tz zp%5R19H^@s=xDPz>`ISIV6&hqD(E&lQKRDunr@!QbT(>pyqy13XgHaajlv9PhZvU9eueZI7FwzRcB zG&^6{-WIE`X=ra~>1^&A>}nn8iMF(a8rvEMCkL0erXL^8t{=u52hFZ{b$!p+-1^ev zy}5^*lgleZGt>Q3BmL9;lWU`o4_01%-v9ddtKa|U%fJ0!zyJ7;Z*P9S+`rh{I@;Vh zeRlTY+|vcFGt6}Jw5t1 z^5S!J=dEG$tZ4IyzkMVaRU zFm>09yJLc(nv%Iz&GAC?x3j^chyLXei%YLZ-ckW zT@i9CTAF>m{jrA5NZV9j>%w@$@=W#0eD%}ERWDw+p6w`>mIZ@-;-OycSg)$97O&>r zfZR-if?kHjsPKZ2#MSMq8*i*1Y^`o?_Ql&=vA$w+gV5Z_x6}$PVUbbKlCv?)ynOUs zC>esH6Cen9=7V(bgIqq8BttPp`6wfq?PRiyWR$q@wipa>@-o}3_~8g@BzkkI4lowJ z)2g}^W!>>(QmuKZs=N0j0Dw08t`-O=%?D_RfQ0GRU?OLz@m_Bn&{+*=tpc>if9k3$ zXiMPZ5xUpSDOZxqRBWaQpN~p^0Q@NfdMgcjBP9nAasjLD_hZhqx=>+b6jf7+b30H< z4WzgnCRE~iIwsd7;5tgUJ{dP$&W}iW9y;9w!>O_es!X~mk7dduDN_-}H~D5bwG0TM zXQY5`|CD~~R{Ed(%z?vm2nA3c5HE+~m4!%U21IoSVZ4KOKA=?s*|jK9BTHB>VuZyQ zn*gll<(CO5E-TgTpn2>xznLD?F~TZ(Re7?%Golhgkd5~;l0n<42n&hqK4?C>s>*Mq)fjVi3ouCb6psoFJOxh6|l=i5+J06D>ZX&W@L8VH`P- zUjda{XvScXHC*Bdi5-5f-h`1!Gr4pC1q&b{01N~`O35`8p{-K9Ns6(`@nHkA-cuCT zQ~heZy9{m>1C6{aBPYYiN>%bxN>%A1OP0u8zz@OMQ7pZVLT}?Sx{K+pMXbhRVVy)2 z6jI7C5L^lbd+sxV&y1&L4) zJ}zH~#raEd2_>z`h_6wBd?g^agb>pU+5@@fVz66*4x7*+GsLR}xz#Yg9usk*s=Ot= z9pSZyZBKR@CRXg>W{K3wq)6SCDoL@1#goV_7I$Z3c&5ia({3G)D_fMBwsL)!)--50 z4tq8IzLFLPr^YNyco<l7Hwd-d@PqAZ!o2C*bkVrf+Btz zmjbHb3gQ-Sy&mcl|N)EPLPA$CWYQswoeyqw+J z99#bL3ZT`6*@dCO&dREwTIW%i>w|U84c)e2HA`c_snsNhl@;<3&3eAg=B|pjBwAxC zry`#yzg6hUL~3u{wxr&%=Ve$Rc@`YPCuTQ$EJLk{C+iFI3ln|Ay=}d1-6Q>zOS21W zOH(VW(@&p|KRFtI{9&Rr|z-ZD@+egQmhK3UT{j~%A4HFYRYpdhCd&@6h zEIv=l9EpPY%AE zEFG=P?9LBA8gCeF>zo~&-Cmn|x;(%8X!6O@==$8uljZfJXM1msjy|65e?C6>+xf+R zzkU6`KA!&d{P@?G7k@dv`pe$s_lE~>`}QxJ_fBIw2Z6ok!TlHEKq5^}69M1E}7|8f`R;);ntKnxNU#SliSyFx@mf-8erVTbip{S`4i|axHI|mmXUl zZF{y4G_#AUu`y{=EybpQiZBQckXH&?|Y(%EF@r#iE9CPP~W}};v zW(PUkXtx7n(87z0vT3vjY)-CPP4flIE907~T2-W`)DvPFtPr{CesLL~MEaAqyuhx) z`Hj?oiRRUkyedM(K#SWr_F|Y>2r{zsG_*_&CBs0?vM_Qr{OlrGI^UEl@WObNIBJ|q zYUEIQWb6?=sZ+^l)d(7OJii`(i zGKixbV*m5#D8g$*gq_Uh8pFhB)!L)l#dTlTSh+LKlv;Qdc5;!LUZL04BqB2-!Np2>V0YnNHJUpKiJ{ z@OX0g{r>8YtHqBm*RPKk4>zYC&dttGt<6oZjg3vWw)a-mc~mZkCZN~(BpL@*rNd}7 zIERH6a^oyow%u%rL~5(+{AH$+ENn3#-+4&OH(xwCdwcZtuV23X+h5*)|8f4|CwmLWPx>BDj_)k5oIQK=YWL{px7UCFEs55xi{p*c=j%s1PtTqo zygNDn`OVcYZ!W&SKKb?h^55UR`Sr_{KKA0c@5NEa z(NV|QN!RPk-m~{ZGZ(EbFRFbrYDF`(I0CAu1i2fr_1%n`T5L1`i#eeUHdKQi-J-!y zd5gE&EgM6&m2vabh^noP>#u|u?WtySs>hU5WrFnvi+X*^N|nrAVTc8qo7)#^dKVK5 zE0LuI|H6uMVbwgpZeD)k+B^uqI5W+ysfLDyu`o_s3>BhqLa?|3t#e>)mGIggM&GPv zV9MG#=xXf@H8fPktEwZO>ae4#KHOOAtFf!h4yDdgreINtf-)Xg#uRcGLKH!qgO#Tf zbh#WWR1{%rdhLgqbP!X+ViWyl&ex={`8QD#h<3dc0qdJe=1&C}&%OYYvI0|4AR z0C5&TTmUGB0JKy;nA?j(GuMsH4&C$rI)RqxKJG-un&vnz`8ON&v(GMYfm;aa&2KZ8whh56J%ysU_(*YKStrbCG_ z6v2%wvQNOPm2hf=tQe2r##4P{VI_}j!_zDTL#xW%XAs4NcpDC7MWU>5h^Y`{DuCMx z2wn(12$ifI)nSaL2s5CLEOpIzXvz(GvO1LjY-!4JkEJfW`5K@e^ zY^@-VSNwoy$P)*!!fGlt#vnBa82##^X*aFM!0EP$+iaXb8JGjVl>xYa7m!x~klSJ9 zRS;PW%n4=@11W@PI-w>T9nC~n=OaS}a0d{s2O-NKZ~>^0oOchK0Vsl}Sb5llp4H)I zwb^M6CPssq*K9}oWpI}iWECd^b(A9A3Y13)^JzgL3#uj{>S=Q<%-21BR=2R>ZXHqj z>a~ITGIvy6l?V(C$5-bZQ+-8EAwpQoOW1e`tDw)Kv^q1PQiBbs&~63U zqZ35j(#l{(b(O8Dxq4)v=jlRpwmUf7tgi7OB#az763D@&Q6TBex%3-75R zX?7&Aipn0)E2e9`kH@N?FC^1)_IT!()74)u=ieVLU+pg*Je^rznw_6nnwi;{o_jJi z_he$RHPU7(cX2so46%};)S^^Mv`J43xVT}DG!k&ccpnJ)JF~pvP)6+U-_kAqLI zpI^V)eS5lhl~mT#lTR0~zFu8?y*&Q>>hS0D)4#mA_}jbFALp-rJ%9Jd<-5O~U4Gv? ze7kaZHGgGQVcy|8Cep=zedsu5*jkTwjmg&{6KB;bL? zz4#VCz17KRvoePRrOQp`%`xw@mB^Dv!J**_UnNqdPca!YA}&~^9^UTcby!P7vSOD^ z8TG`Q8^_{ZQ_<=9(88Q&e!)63Z=7AVu5S6aPpXeDEz29S?jF3;Tp(p6aX~G`mYS$~ zFVWM8jrCJnMvGg96>US7?xApdN3^ac5|30xeO1j>^$o$OUvCPTOqFJ1MKPyTD=t=w zqynxKPn847>Rg%y$aBC&VWy(j8kmg5y8`Aqi>SrU>2ff;jHrHH-jo?KuEn|Apd16RZ~!q)R;wM+=FS_400v`#uBu!0fz*fxWHuw%B48dm z70JZl_*4V~mjTJXn|&h}d>>25VbN0R0;ncmZiDwv1+8_Y2leSjX_}OuSti96mor2f zLDD~&UM4w$Bg7zt7)ad6D5>QXMOiE_1y)}8fcD@4IW?aHL6(p>1}RG=AxnkSQVEDk zzFSamAC?E=gOTNMv=#+7z>y9#p^8MPp`t2r@FYJ&1ag2zb<-(E3a%6dWdgJD`CuLs zC6iEe6-0v+q2WXI47`iOtQIBZlTppVyKqT@wb-B<+1DwlCP4W|ixu>XL#%gRe zaYs$Ukw}=MF{M8!w7V!42gYv0x{O4(mgv!uLMD2`C1?(mw1-POf>H_YZV~Dx9|GWm z07Vdh41FhQp=w?Zwe#4i9kb2L>j-e$JW0ffy8MiU86Q%@T*XkQ80{+~N7SUS3K>%6h76EIfH%-?SzWBz z-feoaS39>69+;~gTMqS2h6cx?^K;cJvzCc&X;)0pmxTvd=*BYSI z*w7&@CZI#RR3x{a7jlY2?xcTeV%6S``j*wM_+)i_JZ5dO5oE+%YC$d)co&_PM$N}+ znJ^6{RSHViz_V-^V2DERP-$lpfv2O5Co3(l9(G?n8UK7Z{qb=8VtevvYv%dZ{L{w^ zYip|uE1Qe!2M@PTHxD*uo`hWqzEH_5RuRipg;EJrqhwe5%>$k8zOGnrPokmKtFg&Y zOlcNcorhIq0~LAs6+n;z3e%E^ZmFcv>*;T-Tb`a6?CET(OEe~G8)_4=NHpXPdb}Zf zWu3F3qq=XbZFaeLWut3pt$t#rYGANwZocc`>ge9q{OQ5s#mlLa7lV6GlKkvnnp@c1 zczXD9=j42S?`ZDP_Vnu3+@qbP!KfWckbMxwa?d;{7fB)OX-+t>|TWDV%ZJr;PJKQ+@ ze)a125AXitm+OE3W#`@T%P((UetCWRJdWyVpMCghVx`Ehg@)mi0#!Gfnn~1Hqkz+82-Owl-s<6UI=KtT*RbO!<`# zRJ)%w6fEttm)BNELke}RtE#oGufB8C(>LIqp7bp)Ip&wFi|g*kd%?Yv(DM`X(nEf8 zV}ZIfm5&37F>D<};Sm@j40kgw+=Gt}uxkcNT8B+tBf+LNe>iCMIjsSky)jhT9FA0( zjB%?WZqsW@xTPwQu#8vCW=e3BibAR$$Z$dgUQ|(yP~UI$k5)xGJced_Nv~hr@8u== z*{#eOPy_qQQ7s}|B^hG@gG$rWxVHfG+W>JsK!F7q7yzI2UV|0d=_9oJpzZ#9E&iYC zeD^B7ISxBiri7qr>30inW@7X6Nw8cT=x*Mfn;C!{@J$>kgUh;KfKZ}!-%LO8& zM7FVnp%GK$LWV*D<j2?#nz66y7X@8|d=mY%aW1 z4kVK9k;z$V6+a$z$0}VlF;61yuC6k=-6aMiL#f896iA~SX(~mSWk|1z7`O9UL!~{{ zivFmwjGQLL-xniq31L5pVK=0xI~9afIU_?*oXa=Bi@gkCC7)VdL`=wO-B$iYC2u4m z9;uS|Rf?i!xQKW&>&8#F0V$aPiZp|3$`$#bY%h@N%_WEOuz_r_D;4g`M*99~(Oxvu zfupIBNKsA(BMZQV-K^jgI4aPUMquS7+Rb(jI@{a)szj?(xO&s zwS^k9Qfuw>gqu?9#6*m6uNvjkpqxsQTgweuMM0a`W2vb0+ME2LUQ?jEJTYnwb!vH9 zEP|U0VdtmgAEXj8unG!FL(VRRW@`{x21L3M0rj&by>8QdL*=uPy2IJFR}Y7;cV<2v zExbP-J=~f=;@2Y?PmwipB$Y&JA3`&?a`C7mPCJ9g^exIpo*k9d_KycEpM%l zt_^W4br#^Ta&Z*OaT zV-opoiKd!BG;H(xo#9|zyQiVk*V-HHA8nkPZ=G9en3=Dcoa}r0WO(=4_|e|X*}>fT z(bTKwW5?U$FP=_6d;D;JZ~Odw|IPK`yHC$we^}ptHUH$r#^I~gy~Fjx!@bw%hwm@; z-W=^-JwJMXa`xeElAmw>3D37be*8;5ll=Ubd2U{utzVq(e0clefBtdu`!8*aQ(bFQ zgHPsnKfSvCuU}vP@%jCKe*f|R{{6#0zF+<2^V`3DdH=^h???Ch=c^wduD)Gge*3Wd z=JmnH4@aLr9({Rt@$0AefB$y%*XyHChew~EAAZ_C`0#lD!`lA!^8WSW{`LIchuPLg z0so{*(ZCQ#P|^re9V1w3sg7EjqmmLy@aq~&8|#zbu6C&1y*!e5y3n@2(XqYWw6a{; zKWvOO2t7f9*NzIC(9`v**{F8Jscfi_R+p7WjrO`=O|-UEm#DXmjfGa%{A=spjjhnm zOaIQ1edC#Oa+*|KnIh%gVxz&OB!OL|3d?j+zM&3nZ%6vNXyG1FZJ(;H%i@cu6iS{- zCNNdV>inLr>TrX@(&#YO8Wj~hqOh1w=Tq1OoB)ccC?J{&Szf5Xk186_1{NyoR+}3q zYkgx;!+2F$yM-DSrbU>55ET$4-SgqHEs(tOj12D08#DkwlnJOn0CZGZ1zFjZvAiu20KNRkSUNF(H$1av2Zi$@J1NdIUj-Qa6;T+s*8d7-c3Q%~+Hjo8%|b0Va4sv|u4S1muTvg#L7)>rRQ| zrrdK|<4@Cj@@)aM#f?kuS(vPBHVdj%imL;zSkPBj6>h4jtf}_++!~FVCz0UA0vad-tA&pKaGX+iTuAh%Rr$me#75S3--^ zs-8wt&u^`Rv8f!`H8N-n>10{r>p${mJ3GS9|ZypI`5+9kn+MDb+4A zUtY-K14TvLfX_8P)bMzvX>F-tYP_kLTZizxw^}`|p2V**|@F z^lD*ufARU=)63Jn>sQ-vkDk4K{`~#R(@*a%zJ5)TGijcezkECU_VxVxw~JrDpML+j zd1fNm)z-DTFnh4Oe0n&xy%`(neRy&B^!@3F|Ni~^|M!ox->zT&a`onqFRy-kd;InF z>9;o*-`~CZ_U7c%<;xG}XCJR#efhY1_4dW}mxB*q4nKXo{Pp{XzyE&u+sB>j!>u&e)`-t^`1{Qhp=+Qa&ZxzNCbv%62<(jaRMix(O#(;-cdP8uuX`uIF| zvBam=+wAW0NW?ZWQuSy(vi>CWWViaoNnm^5u(-M|C*{yw5kv5mT)nSQi-CQZ#Zhs#0}n7l0onq94tPhEn_hJ9 zZnC^n0DubUT@CS$p8k`X1~Bq&`YLiOwXlGuup>qdI0`Gu(~3$mD1yvF(t|s&I{?tF zY$^oEMikHsQ{cBA6av6F0G;=sr1)-?J*(D!x5jxV;Yo>m(}VU5t0uQxRw$JdiYwRx zjeuhmG93(x2aETE@c|&w1tmL?C>=1n_+A?Sc0p+tR*N7SaELMlQA>fApbI!KL@_y+ zf%*xWbqARVVHe<}V2TDsHey(QYH@=|+E!fDTvXIlEJ_rKszj1diQH3Kq7zXC1Sk=n zLm?LMIViapr!B^rim`S9$;(grC)PtkyYL`wA>06^I!OpM1g(UY#Y;*ON|KXZs6v7? zFsKRf=P4>Xkl+C_gFs$5PZY{5^4%?R03=R;!uyjhkfQbGY28qT2_lf>QRta$22?FC z3fQb+ho>eGX{xTNud4RDZ0d?)0guQc7m~p@SWo~5ev1JHkn;ib0)P;CS4K)#vw&(= zzM4^>q~%mla^%Ex87^H!Ok=Qfal%5Z3{A3dnK2Eo)5jf*vxn{g#P36Oln%=lH zWW=L7vYx(diHFw>1-yAUi3jAX!N zl`+#QB&kYinyNHSBh5C-3mrWye$E@Q~>xG01Na}F*-v-PfwO^4K35a%C!pf zY@%!vCtXL&(o%BNB!rpH^a;g5kt86{L?!+*MlCI6}68FbzX3>Hcw{3i425X zN-Cio50_z5xp^64a7r=oCO<1h2EJVkN-O*SnYzp8wiX3j@H=KkF*7qWlf{x{v24kf zL1t#A*a63Jm}5>Hvcr5%P7d7zeW~x5Z)`Qyp8Z=c`!`g-T<+k-FfkG_1^d;j+Cit}p~0T^#hJiBcWAh;eR=NLFJGVj>D%xA=U;#LKmYdRhc`R#k9OWYymWN^ z_Q}rO7kk?;_U|6=-Fmcr`_bOs@#BYY-t52lc<|=S!JBU$y!-a#`=8(b_UqHHpKm>X zaP{f^D^G7-d~)^t(Z<^T>Ba52**jCyw}-mVhcjnA=~a8vtT8sJZJaW;FS`2If=f5L zF6~cUJ6t?>d;Ij}(Mxxh?;Kv(K0N>I>E{0a(zRRT8&~_zUg}&s-?};BbV-@Q=M$4OD48jOqP~ZczLD$+KuKb*HhQ;WcH3)?(h3HF4Eds!Cq(9&tesu z;8Doab(SutwcDs~XVvwv>pJ=R1WVy%h>R2p8wr7wp}{p=JU(o)^amqdE>DlcnNq7| zSfq$gqtb9R7>rj5F;rrFwX`sl9fq^K2x@{W=+G+$-J11w_r1x)wQl>ghnD2$dvSmj z4AjAjt&kc;NeMsil%No>63ZQoLK_p%Gk`i_PTUI1)HB+H%uGsXv%=||d<-KGMk}qP zmRB;tU@?wj;d2~fmW7TLSEE_EY)PTUTf-X3YsX)ARewSE8qa7LV)CiN#aatf`igS$aO@R5&;q; zuv%KJ00*GTa?mAc5tOb+^PD7(pCW4HX#13=QN3nJZ5Y;Tx;2`#+K|#);%2>D&6lz< zR02xCL#q{3o1W!2vco2JqmG+W@ml4yMs~J;qTEQF7nAi9TZhD3kcM`Zv{gs&GHZ=+ zumuWtBM=@aw!W4U0STHR(&idvs!SQrRfhn55U|GpPg9|@5o8L&6xM2{sF*}8;j)lc zjXdi1N1P4mP^vf8)zjSB*bufFv;r=JNrZ7wMKWTMnp`Tu1NK z2Os7Xz_W^)*|i2*rHod=VAo)|Ae016a*Bi*zjCfqxiTzV9+ID#QY?>{mIghGeU5>+ z$R)+HAs~EyAshg+0KBq{sIMf|fjJ>;ws10ISb7Xa4#9|lT1o)K3B!dkv?Pk=g&0_` z8tHMsJa(|#R^zhPdTo%P4H2!Qwfp%!_0mDVxW~e2kuu^;R-8s_5kh+Gps^Tbxm$N_ zIdpilt*{yule;U^+tOT6={pOf`qN7zu=D z=i&|2G9j!=iLTaQOVtR7J?lTQ#zup=Ik*NXGi5c6r@C&hpMCfE+P7~nzkap#=y+@I z>GkdB*KQwQx%6oB-0t@64QvYY+r(}%+k?;d~q`Q)eX_ddPj+RfE0sxy-g4)qHCPHS6o>gMG~KYu;^ z?!$+F{Py$z`p2h#{Pgy(pI`j)_V$Z=w@>cgd%3&)V)xFo-J3`E?>s(y`0B;+=Pw7( zKRG_=Z!Mg@GIjmd?3Jsd zXD;?FoX%NUd>#&+H~P! z3)19OeRzu-qUZ2DGwPx4zqJ>bC1Ul-4e#F)bImnG^*c%SJ&^ zHD#rR75PQjDrlWRl{9%1Dr=L{9OlV+wNROm%Vdz5aJV4rX9dz*O$gPH8)}FRD6$Wa zH=#f}WN}o2nUAQ}ZeY&>8E=g0Uw7Ws;NQ(B?zFDSX#w`pfz-^UnPqgv`Mo%EasU}Y-NekRO<49BEjmR z7RYF-Vjxx4pQ`9=s!Rkc9CZ~cZH+=lk{G#St5ock3j#txgu#vDxe0Xk1gCpY2z^mNs(r{VMaA zK|8E74VyGwI!&|2&}6j5YzDVMDCVKa1cFS0*K3$gGe2Y#B%$QII8KE5|M{uyvl9HYY6BQr2Sa$^AbSPEUB-xm1*uv|s#4QjVs0sRG#9#?bG^x| zp9Ly^p~P9n(H4`qd3aJGiw-l&r6HRqY73_Q&Ao}9?#8aTKW5SxxJ(um1EqoUB$xs< zu}qFF;(`HYHNb-bGF*`yU#y{2nAzYuKHSEGY3bG3->@)w7+evQQi%{^NDi5xy+JY4 ztv)>|U!Rq)Pb*d@GzXcZGuELf0O<;B$ciLfvQ+CZprBVZl~xULlAC}IW5h4qCZA7E)LY96Y+bc=Q6 zI`!NZZDyI=F+gxfpkiYcNm3=KW8syA8UeCKjIC7?t5tZgfsA%=YW1W%F%)c}OEYe1 zx1XJIgxAJz{ps`7Z{A&e_3F&wll6N?XKy_^bLHXc+3kg;n_CxmXXh_OntH@WH`i!4 zHK!8`3+cy-r$$y*X3w3QTR$^3 zw>Uq$lnOS+%)X?-9pM?0d}FV|o08e1&X~;+Fxvf|XtH%|YI199W$)hVqur~YUtRg; z#m(>DZ2$7{(O&GuQ z9~@nM_V~e<568cJllAk#*W>5EynplC4{v_?`SqWEdinE@*}VGo=QqFq^7_xeUVicr zr<71l+N_+8&a}QcMyoTN{N@@Qpu-O=r#+USA?QE=3A=3r!Pw|ntU$Mm)I?DdY7d;ROXqo?nWuiPCQxzIUu zzGLzB#M<`4Rann2_4TZ!O1B#M0hc{y12ZmthlNouM$=WWz|wtSfLuh5myluRU{3LWl+e3 zJSHEFk=DR1RpoZmI8k&C(s0V8w=#Ve4(q1Z73lN3i0F;79C-biUUSR z$lzH0*ls8##_30IQA(zLa5=fN*s|MhK01oJnAF_l>Q29z?sR)&qRa8wb*D-3c zQBx~rgc2UU1ck}^S&fzuscx01Bc_<{m#<8TS7$Vv%j&Z;x|KoWOslcKUKW+pbSO9{ zzYGIZ5`Y>i0OjPMq$LDHEzOBy*5es|Ojc2f4^H-h{*zD;D+ppX5ZPffGZJJbqRi$5 zqcz27Z>Dx6DIIZS%ua1}^SWH3E|aQ9W9XI3n;EzUEMSC{x9GU@NyEkA`a8>wyBm=k zYtHov(@Zz2UIz$KxpFM3Uc_kDGFw!P7749|O==>eqj-1&77@fDgE+7kR&A>Q*A>F7 z0M(nLYAmv}6x5}FPVsjIA_AGj40oOTz4>Y0wxLLOj#W6FH5@Hn>TO%UX z$f;EdO0AlTu&^<9KGe*lHE7Hu3Hwx=ZYUW#Ju&lSd-KD~)hCZ<@9fN6-#&fw;rjJQ z%NKTMSFWEvb8C2P)f;Zj`YCq>>iY&e&YbPNvh_#*++2^YOj`#t#-0|}NO%22uY0hg zVSF$)HQF>cnO>UhUR@emU!7QAn_gKS9UcqTc|9^iTxN=~RB5)ZUtn!yY65Ck_Tp`` z_&kAVytAi&b$RLjt@9^`cYb((^XseZY`^v8`Oc4TkAC^^#!N&wu_Q zTRV^c^ur(d^~bXZ+Z3aQ=c$v1eb$bYp(W0CTX(*Ec=fk`dH=6}{ru0re)z{PuYUh@ z^!4fXyX{*qZfw7~fB*Hp?U&p4pYQFQ9POMuJve!>ck*WI{-cXKkFOp+yLbF%@6CsU z_wV-KzuJ9&eDnF<`p$*U)jn(5Mm13gdaS8M-*c|z(vz7hPbRjG#xER>Z5~c+9?e{M zFn?};HnWgO&qlgdl9N~a$1e3Gr-OB^T0=~vchLM1sXw7qIao|NR;FjgqppeW^nALd z*J_J!`5r3ONW#iUa2Xe))(ay&-Qlsx&a<06o0l^ewkCG=hVI?BkB_5V_A0%)QZK{$ z%>0y3oc4>;US+FK8nFpIda7Om=g=!K=;9h^NjapV1XfcBgBKyGm1HrLE3ReoOG&aS znhwv`N~BUQOGzM`kVHR>5{FR}U}B^eT@QuVfvWW2A{(|ez^I9_Ys2IsHxkH}PX{*7 z3NKbw7W4B9=%;dMIRL#7P~(f$l$VC$d~4Fz~ZF-~8G zQk6r+m6e=|Vrpq2sQ@XgCaOT}I-D>>mo*C%osz7dnnAs3#HR1H>RK(Dm|5x73pFw_ zodHJUP<#Qwpw0GArq95Mn8j^2QJbEX661noYJ!RLW3v;I>L=msD4L64?ba)M6ode_ z)>f7M1o2@g!c&U$5Y*PhATtcFi zv#D?`pXX7UeDb;ml_#k8`?M~b(xj8Blv0US!j)hvh0q*s6~L_gLs2mzUydnO;!3#? zfDg+Npz=kyA_<}7zrrP!!jbtfYEAZH%Jvy0osIIT0mZk&4RnSZM{}boZUoT{A$n@az8Y$vmRgUY z*Q@CP7b8;7Xo@h?jaffwnHE}S5*~BmlMZsyL`}$r%@SRg#E=mPQcO@i4cDd@j0e>7 zO{SH0^KwQro)l#K5T~eAPpvdjXpJh}m{&jU7Idn~jZ9h-g%rc%!x%yYM~D#60W{QA z3$c}=?S*7_jx1W>>MV~BmZygDyCw>!mP$^ql$=>ETUi24&!T$=$+1?NEzDH9c~l(} zA|RE}@g;0Z36Gr1#O1P3#R6QZ7@xJ2_4;C-&&YihEw|X_xH)Hk0MLMTG5NjNp>OOnEedBEW^n7?_)-f}v>Q5_r zQ|5_o*WzF{)`?#4+(>k7rs>RlVr9B{VWMYwc6x1jbab?#&f$}&(+W$M)HI=REGV2E zOm#?N_3B)HTfN^G3N>8C87&wlvs(f8jz{OcA z-{1V@w`_?$`tJGtce^)Eu3dV3;r^RD+3dX9-hQ$Fho8sCd(U3%JbU%>hu_})_P3Mo zf8KrbdiV9qqfc+{zkIlUe={@RUEfu&2`Qvro;oD*cU#9Uc3(ePzxHJI>f@;^kEXXC zOs?M@ncnIeTx;%HY7#oA5--yb7dzWE=A>BQ!BdR2bX|=j%ytIY8YhjZLcthSXa-E9 zl{SZilj-&$f27mwY|$ISI+fcX@wgTKgsZW?Kh!&#o>}f%J>Pe3Yi@gY^u{e)UoX~b zf$G&TvxXLQ2$J=zn1fWWC%biYtCpgYBRK2|9HAJ3Dz1W+RUoR$5YRF-q8NoQ!!jyJ zj1oMp03)tp7|2q+M4=H0EiA5&!cE}WDFm$%j0;yoT~(ECXqBG;3NgSDW^II85ya*B zkbsw1U?&tC;iZ!DLS}9r2>?i^08$~KA{3jsC3bnaQ&U}U#)RxNw~itc;!41U1*obr z5){NEV6+^XTh5PYrHxu?M5K-LY;me5ifIUgqM8#mLvOiuh*qGm+D(G%0YE6|y zW99Au#OT3lTpW!@rKvY*8jR|YN}G^c+C-i%nYMvS5Es=@0G#xc#9gBfK}=03XNE#E z=TMBfcugKgQ3#imK!xQHehrvcjgiz4)F7G_%?Xf2Nv^C@s_fIK2KAaDtFGU!%Q)l_ z6UQVY@mNSA1%f4^#Zt1_!1dN~0%l&!Dr&W6_d6}Y|1UqY`yFA2kvw>m1xB=?v-@4y zD_XnzUPn?ns3@?yoZ#$sct*x6DXXf6pQ^FwjK7t3eY0d!{>%UVNM zR8g5VEDA!wU|0o8mq70pn(RWONubdR67$Ofrt0t=$ynsd8)g+zh6V8sY=SMlqW8B4l(QK=H zBBUCys@g<~7)eo&kU60;JB+6X;e-WMRG^xXLsUZqJ{sAKB3hAnD-2%;#<@U5UoEKt zL=IqxZXwBSBG=cELSAA#h;NKwlTl16jB5^NCpmFlIyo>q7h+=roNBa}YnyCHY_>P7$8^I+VTXv{#$&{(#0DJJhr-w6@b!QE z*DlV3q}3y(jaXe1+|gbe>#uDet?ZvH9-l8>O#2P4o>dPcJRcH3tGoy=C0tUv-AB z`1{OwOKfBEUz&);Y9-2e8&&ZoC`KfE5hx|tnW9FI+%jKt^0mk;l+Ki+@w_ka2H z@Bi`kZ-0OG^ACq#UhjT*bob46*3TDxVSrW{b=#-@#_BDvv*IHZakj-PdtZ) z*PFxLR!dZ>3kqB<+J-K3e8kZ-VULfQnkJ21Ywp=AiOt*HOB*eNi?MjWOI*i;(cw%U zqu%A2XziU%_bs>eFJ^kCdpgF3+9wu!<~L?C6BCin;imq{uGy85_08ow+Y?(?9hnTt zWJG8b2!n#+u`(JQSeFiBlEU>8q=XM+Feb(Ex!5_RGI7Mx9um9Zc=bOn@zK#N!m zi-Paf3j#)dNGD0?R4J{dQEW}|>sy)87DB2C+#WCQjh75J7Wafs^~QkSrkt+k{FdgT zaH8B5gqr+#t(T|qX>cyZhWOOz2y`_<9`t)y44>aEjcT!IEY zUdE{*;HweXDm0O#(y*L$;-E(qb%>KrS;mp=pIJZ2F-CU3XZt6-4vKT2A-Wo*4lGLu z6a#ER1Pt{Q5u(-9CJ?rv5be*QM~nE0B4tyNrMcXjE)O=Bg&T{4ksMbrpJDSx`+eMV?s_*!2lTyP#^#imIFiNpqU`Ljw1+J#F>O}q>DQ}ob{8vG|FA< z70$JY$9&=*mAIKMi6W!{kR^t*Cn)9+S!9Ldg~d>I0ZLRw)*{eKFj@`C9wW*KLf3(C zUI?)PMhOyVUNP0Hqt;u94R&0}hl&Is5q~zMwm7*ZfDY+l4i>>flO_13w7}5JGiDU5 zdTOPt3ZR?<2-&M|4!|nS<5!g{q0&}e`f}&!;dtg&n}5ZrAC@b+gq%2=;3OfkI8SHk&rjblijZROkY@E4#ckA}S^_@rej*j<_pYI(%-F|wwcXIgP_2I*J zkF$Qh`Tdtyzy6$^?O8wfzIlK5-OC%VPiAgkrPUd*I(gR5%(<2OKYV)r&p*HVx4(V( z=YPEX{jU$d`)23ki`|b8cR%fIzrSv8!8t)Z4G;E`UBxlr6K|~3zE~al83JxzdU*4I0_2tH=pYJ~VaDMmk%-y5uy;p13 zkLEXbCReWycF#l{afQmoRQovH^RcPT_Vs%M=lA+I4!Uo>96J8f>97BG?b9z?&pxa_ zcz6ER$=UQ&iY%oQ*mRfE-yUh3ZXZ2Aw!AU3v@$=vaC&Ot^6bj(Gh>UZ$&Qi6wt=pZ zx#5K~OP6m=p4;@bweuZzl2L=#tH^dE!C{1(lpuo)ZBT$2cl~FmL^Vvf*+A8nk2SXsXrrZ>SDKc5Id6Ku4qYrD0eWN z+tUDaM}V$Iprbi2l`0H2mb$_aV?9CXWT`x=te?h^Lle;Q>*xe|If4xoR|8-KKh)Af_P?tsRt-ZfFK2Ml0uTailITW^f;0ZhS7o$`WmzYL66aR8IiE> zk2`6wJ5=Isv#ia|^lBh{Mj;YWPzx`HVX1lp-{)2(g36R%+2&XEc%&HbFRvm1v z2*nBm^`~s!T)Ymz*OjszaJ~`E79g2qI1hu7qA3QVKu;EG$vhR6rDQV1C^t)}5>uu?mx_>uN&rNI3lhZ5`YtOhANpl1fnl(3T!^`WEn&`1L)>L;|uiKzfG zpa)x7I6FlYV(SuYZG=wNR#mfrTnvzd0rChy4hhJi<>qq=*dea2OBY;j=(*i7^JHlJ zs4I5Pq3q&|;!={0h0u^NdOB9e#A<0mpWNH!?3@dCPdhS0qSg$mtFvlixME?nWM&Xp z8P8uHFPR^QjE|9fhQ!GpeW1hTXpQo`9<;$$t29Cl4t$-D?Dn%mL0PhiWU<1Ps$w=f zkHtio4D$B0XLj7bG!tH5>bP-f;$VAX=Wg#-_Do(IxNu?N#+`w=wP?CuX7X}WR!gX< zV`g#S%tm^3McdIV?`l?$Wz1`1rVEp@Geg4lUg5cZ&E+xe)~N03wCDPaVX=eX?$NaR zn?~D4#(N@mV_YE{HW-(5)~goJjxBIrS=TSM#|4&<)Y9%t%neU2ubdv7os9N%*wYbR z+%5GRWp2IBZ*s-lkyJ2|j{9R_S14%rH#h>3P-9zr|HS0{x#e@$E?m8T=itf1lb8F? zp6)(AJb3=#(d!2f-yHq7pa1g9^Iv~@{L^>Y{(1Y|%WJQXm-g;t`zKr_uIp~?yL>L| z=coVmABVsE`0gM7e*C9@+57nM{`-^t&yOB__weAG?d|tBFF!ta^~vVVC)aL2y?gu7 z!HvVCYuk@5-rt|Sa#rfsR!~7;7Oa|DlLrHs8baTE^33&-t-Is9j~0*LoIQTM_Tcg4 z(bK7ucWak+M;9)2jjgu~EH=h_9Oj@Xd+?DCYsYx~!dA!iqse>EC-1)+KKMBN_`9)Z zU#H&xxOVVj=IOVa@Beai`reA5PL5&F3}$!4n;OqdZ!TWkT-{h)UKl$)IdpMiWNUtY zeIwmA5>Ixwb&hroPERhK8C_UuXl*qF8$>QU!)m12jChL{tdW7VGMrHjBBN{3pek5- zEwTcE17Qdt1hO1ln}>uI!fJ95)j8%j(AlODM#f(DgKp;&8)CwkqNvsY5 zw_m~QSJB!PyiSw2)j{{`AYyJ28j}w~=7G^vohj?5x-q0~31~76+J3*R)BOMUGrQlh zc2te30-~%EgjmLDCN)_J^8vU>5vj2R8_Xewa)d4A@|JRAYo)urwxPW?(p()36?$EO zu?|3G8?vp8?LrEyIIalIAi-D&7!Qh;BdJO>U4^G9C{#I%q>(W!HerKD?%-i91dtqD zD5%a6f%0X@QaP$jf-03`DG5fez#Mu%nf+Q-0GXR{Rh@_}R%PJw_GO(;d9OW7^3jd6)bt^LUuxswPxBaTu zIwX_FH8iUXs}+#V3W83Cl5%)9rMt!7G7#<@_H_0t+geG3o$#em#OiR>R41^|pEoyH zJUs*%9j5jU$y)~;k-n(Er$ZQsV9nlYWgS@W#@01Zd|_czv$3m3m1yR90(hetrqO0g zDcVuTO++<)UE0Ae$Mk4yZK>_znat+dj&p07_0^u!YsrBLXE@E0nXqh`(BuqcdYh-_ zVpEfrzKp83RXLt9o*CC~PKwS9u+Q`ew}xd`Muq3Pm74?Bt7E#A4t~b1Y<0y)l3jz% z0gEys;q}R-69UOux%Q6EzNxSd2+U19Q&ebftqYB2`sZfmhv%l+CWa#YZI0Hky1^>4 zsU#+;!X($2WO|cYlQrLm9_=AI?XHUo9zF(U= zmuwsNM!L+wcB3OI*L(Sa7Srf*%i7h!dry~MeA#&Q{ramP7vKFl_x6|ZXW#a}{eJQ4 zUgplp?6?1Za`){WeaH?blb8ZkojEv|nZCGoWApsgnYHENGc#Qm$Ggu@E}q?NYZ;D) z+Zq#HO-)^$J)_-26QNX#EgV*QT--Vf-DX5-m8Al9K9^Z5VnZl!IIaqcDZ`PgX>16M zRg1wFfIt8Y3c$bs8U_%M02%@yssJJ!;L{;u7FEmQxtW3xiJd?YQy^3a6y1eJ#-J5# zOyrQ3k@d4pfC%G?1CUc5NRAeiE2_$2ZjriIk$ zur@ta&ckCcpb}(tB@>I)aM*6SIHHxtm9n@*5o0;pxPdlWQyU?jgk-`cJ@vqd9~ch; zeL{HP@ zwY+YNu*1m?o3ksXn$9dl;Bz5pyyB1jE|1n{`=_eCUft`JbQtN40#cOzU;9042*HJg z89*2#OqG%t=XsQtTBsjDhx5pZ0$dQFMgVzhnI>IsZm)KCLh3Wnh8BoBTxoR`%58;U zB>=a9=w2erNu??Y7&;t_0u!N778E5!;N%#hoJ3U62`T}_tmV2bLMsbyAyz68g<{Yt z5ja;2E0Q2fq{wm!s$7hz5@V}{xNK^8m?{pcf{rO8kxFO+D9^;?`_-I;gWVb9Pjrb+ zkC7(YSc@6?T8DZyrkt=ydW4d8inh zP=vY`rm2K!%Awj4xVD01z)*}-ij7BcDTqE3uEB*0`4HiHMn?-PlVY|6>2ZfRWwZ2n zU43q6hb}d4w>7dwcBI&a7P+xZ3!H2KQLHdp9m3izPoE9;TuyXfiS=BKwqEc?W*z#N zj&D{mwHl$t#nL#jTrEpswKqgUO@WqnXM0A`*+w1fC7zk0oEb$=b`(r^oto^+9~-D0 z93pp*D$=9w#Aqrw*6$dc5G8wvz7)Ziq6OPH@r*h%>=>O(o!<0~%^NxfM2Qxp$)3v< z6!C>{vq{{Pkfxf9-5tT1@%D}N?u%!dSLPyPBlTT<8h-@Mmz82E2(C!#4Or4?V_UO( zY(P8ErXK4sogOiq8xfuE5w3SDFOMoVhq$Y4!nKU)(y(@|liTZ4W&E-6boX#;*kNc? z%lg%-VS!{trnzBpUp9G0B-V_?+9IoKHT%26so}2v;n9))sgaKH-sa(sSa-ak+2;vb z3}%5yh^0~BVv&H)6LZ*N7E2-!>5OK7Fxu9d8R_bqTswbhd;ehX;r@e1JI`M~c>Ce# z{HK%OemMO8o4cRh+<5nLKN0pAPQ7xpnR7#huqTcV6GRe{%QMqq`UH-#C5g z()8)`LkkPpOPeXCQFu5KD^X%aJHm{Km4<`k*@MT^yN4qWAB?|#x%Bq+($T}A+c#5} zt~Sk{i8c>eeJyHdQf&;%boJuofbZBB^MkP{MVmVq>dI*O$*;8d{s{IzQ5SxxaODaCT!O z)iM~UPlXy<0^Y_{W2U2{Kb%Ne>+9uCJI|`88&n7}rvQ%t;GjZe4VD7J5z4ZmvA{eL zn$3sd2qo1ZAWJ9&4B)a;7n?_g1Gp-H3IRmKY6=J>L{aL9oFJ9oh+`(vq%<1eM!+`_ zkr@$X*u)#Qa5HjZj9$}#De<6+rB$cce!oI2oX^0J$v3 zYAJU*t6Z*fm%F6SnQyE+C6EDJ8NikT4CyJlq=Y7`rYj(1HJsugfDO=6Rb{CW1Pzgi z88LN0L+v-v`z_o)r>N5_Y;@6WIuu_}jUg36kSMW$X4Q*>4soMX(&m=-xHug;T!Ig6 zAmQS4m=_CmAP8PO#)_mkaOO^}a#4b4tOhmYBO`eTFF+0hvep7w5^(fZ*)mnubd4G0SVI<3329p)m=-_Y+bZKb zlq@;EP=y7Q7(ju_k)aB*<6VKNQsF>SRILOJlVadfEK-U^OE4%A3c*(aA`>63=La>Vx>6Nz6xMwaVHtq$_ z-e@>`&9}H|9-5aVd+DAy$>bsG9qMSaxw%79ACfeL;?q+-o99|iFEy_&I{JD<;V{l* zK&X_}B3=Q5QZ8U3403TYC~J$T2AYi18S6@~b*5R_0ZugHXjRR6+he($aY8qw6R;#Z+(K6jXyf{5Q zKR3CsG(J7oJ2H|O?Dch~4SqXW%C6zya1xqW%1|=!0z8_-WGM7TzdzU>xV-hVUq{zK34ad_vz zdiSC6;alI^?~_0L-1q)%&x@y{Pxh8BtyG$mTRTrD`bQnTQOkro zd?hk)G&X+yY_wy*A5JIYo$+8g6=`crc802wsSoXD48xiTbM z2B8Ye34DN~0<1y6-&kb#opN|`8-g`1M}=4f2zY=%SjdxBQ2AwORw;s8K~y4XRw_u1 ztT13IY=m+zwIaf+ZkB>Ob?{y@rprq0cJVqq?4XID62eFXFdS72g%L$8rd7uC>lr~U zIj*L4n5n%ce2=y)Ob0cx%e>ff8w}ybA|!FX#^}*Mo5-t%O@iSsN0H^ zNuaK)yso{{oCX<^2t@?Va={RqiZXFIF(9Ed8tEMlTE>A*8o*vrwT227;1C2T2?^ms zp)v$Yizn)-G!31mqM^kYK!O6qNI(P!#PCy6WPu!2AxGCJ&`>27r6J(8WQvK(G}74F zXc=S*B8Nx>DC|<25=pl+nPDY8ZJ~5|IO7TSREj^>u2|{SuMTJyyJTa{(q5}54puc5 zs+vlb$tpz>Ds4oH;uv8RPq5ZvwG}8?5k_1{k(Y5a)nXl3riUn0V5t%&P@*_00>i+f z+hjDanbP24H^q5vZPLD8<DaK~H zJb+e(2+p)9Ip%1c3Pk&z?lwz(i>1!V7jXyzhFr=D(9tm#zD>^SlkyWvso7)@Hu$Nn zDZ*eUVYCxBlEw@)R}XdqyX}Ro&DvW!^@(I;Y$7r>sc%h* z5+PM4&5j3Y4Q^)GNAcNfHR608HHS@PN1T#$NI#G=j<)DVQks!Q?L^8r+hJYl*Dv;} zSNb&TgVLo8e=MOMZVF6vBr;8Iqdp*#HVNb@j(9+2nlw8yB1MACiI7+!8n02T?sod- z+A^E7v)9h8-M)0@>gM95jmeAW`pzsQN4sqaAKk9PiwPtVLBPTBX=n}w%jdB*YOUMq z2s(YCOi%aLrL)IR?|piA|C_hBzP!Hr?faYG{dD~HreTcKcS> z<&D7Xq-mhL`NG=Pr*JuCs%hNx9e)|2Bryq9@U)|k*e)r(Ty~CF~2QPN^ zj(2Vz+`M&o=l0>f+lSj%?%zDUwJ|b3-8tNqYV!<_dghnh%PYq9HOZwd*^MiTtDB00 zyY#Ev>P(=2)}EVq=MUOeK$@W{`v!6uuM+ z29T8iwi4jrvfpQkhyxSwu}n6OK*ga51SFOK1|h%|V01+-y{eW~UdqY`WW}dUpqc

{taos=0HI6@bNY9nCPuqqv@N{_Bo!Ae9`0KWprzDmFa5=12rT*j^`7Q!ku z6qH>+^T;`F1y{!*SVT03l9P>2K;{!6BuFu`A|F*#1*@unRum(v^GWc0HX%<$%aJhv zvk362^CRYxsHrHb%Z(dzGtR31XmxK#L8db&(^i;nDoq3{{LU)7q1L1V*BOZxJyEa5 zNo8=3sD>%5U`Z>v>Kt7?;BN&QT666Iz~RYh2-G^QRbmbxP*Wgc zxPX<&Q@57M6S;<#GE=Hj7q1cftEi?jq^zh`P*lz@=f<^+CNr(YN^Pqnr>%sj7F{R6 z$Z1GCl#GDzAW#_$slwo77%UHoU_#3TFhB(TBSJ7B$}R|4t`t!sLsS|_C?kbnp;2rs zri0COa0J=dcq}@ikc`VEQOigI7*R(ey9A_&8lN;1JDj8*KYOfEzK}7V?l-UWXeZN} zL8mwlmqm&Mp<;fhLJ$MXnhBatuBlJX4H2m>6joP_QI-=_l?;8YzzmgG;6i1MKmlec z;8X<$uO?v)9F#={v+EUo8P)KBVRB4AJ+7Y|){PD5#|JG_8OvnQHS6)tdK@EWU7JuA zr7FV&ZItXv^UM*7CBz7{%9{r2ng$$=eXiy{pW7#r@Uni2rECua8)6YsQf`Nk6O>Ez zdM(f6#3v%CjwGzN8P?y79BirSZUH*lfJ|3W*D$_!LEE<;={(=pd~UXWVa_(WpzNCw zw2lkgri??UBkOlMZas}&*{{EN-?ehtHnC$kOM z96jCASco0;avQ^pSc9-RZWzcI`!lqlGnY>V5ZMzCkFY5fnTTULU6(&p`AMtQw+rY!|6~e?9gfIg_1_TEWzMsM5=zJzLC!LBZ!{tpdpYG zV#$!tv)-S%xi)?O^78$g%a<;UZfy+RyfS`nr6ZHnyNzV4f-0plcvvnK!6YGBG?I|d z(_VTH!j_~bK%yl#q$^Xr)JWCPZ>GQfI{or}|Nhga8+&1~`V?J|OO{q@Ba)u8eb-;#Jo)v( zr+?Z0^uzkQ_q`{NeUA_IPoL;tytlvovEiFvo8G+&+}u#j^|0qNy3?KUv2e0MvY*l@+KYCI8_kSB4}CAZS4`L4hEf$W$j6UnW3$1yH{L9FY`N$O|jgpo|vN z@ES>RgkD@u z_>X^b{4`IeO+Ko{q;a?;l2eCcRThh@OU%u1eRYw&zSLF?*F>QlYd%^Azy$zK0U?`6 z@=7Z=Wo1@cS#{1Y6s>h}f;zTYOk)yw1gr#wRw6MP96?1OO7J)V2BP>6Jh5Nkslfp{ ze7=!bl2uT@fbJ7=0%Ac(Dh^9!A&ERFmieV30~e>K~1~4XCgWK4Z09<#d{j2R>KvH&4J@Q5 z)|%(HL!G1k);?aDAE(w!qcK}^qrSdI-(175N^q+aik4c}Sbtz>4dNMyFJW@EbY(P(VGFH6zT@}P6B*S6g6+#I#8_UmVwZ6gh_;pTK*DrB>!)%r%I zv6e4ym+5;nrVLAvrtuS0c7nrcFet}TzV*TC!_9SM`J5u zQCpDOa%SZ~bm{T(lh3#B{{H0C-%tPg-|IjB)cNLx|K&5|yLa{`rdOag_D!x1QG3=fZyp`o(Ud@~vpWMX}Ea*)lA@wpKm$Iqa+$QUOP=^!E96qJjC zbdgH!M5rAP$!^^ggqMLe<4SdCm=T94=g=xOvW!9L7qg8NjGaYt@~GM9=~ywkmAcl*?=AuCt%}PPA!>JiIUZ#?QJA;9n#Z6wqA>)785Lj+>O;E87+L2DfXs2be zx9mZWsx@fs4tn}S?oPL*T4RXtO+mUN$cUyzz6dLx6n6ELP0ghzA5`{_#=80f%?%Ex ziEmWOEo!%tA5aJrdS#_dRAw?*-DS#HgkP1W)@QJ7b(q0c%0w?^Zi>7xOIchIukJWD zPSOug+olft`*+5Zlik75URzJ6s<~O!+-mOYk1Q-^b`Bb^UesQ^sy=*Hv33@Gu<0LN ziHs~)j4oShyUdvus<{j*Q}F!(U3HZ-85gJG-0CE~rh;2vsqAjDPxM=cI#}rd*rEUg zlzcUp;5BL*pbTVUI9|`RY%Bw-@cZ+qL_9tykxh%X6KH zfZeQ;ddwEJP^l7WbqbS4W#x<1T#>@=4K?-jHTU)mKNwx#U%7g^|LV=ryLX50-|fA9 zxA*$P<*RR>zWw>DZ~yw~-JgFxc=Nn%W!}-!psmd?{GPy2@9q!pUj5HsU;gv=um0zc zZ~pnq_y79w<6pmd{p;5+eth-vQ+E62`FC&czInI2zrV1zx43h(d~m+JdpWwaTiZDv zNT(Auww_`8(zVb^G3Pa__VG zpc*^g8j&7J z{P9igo8#!sV)@~4`C>y^t6S@r@!bkJ7E%I+6^rdUdsAFo<6%`>)jbLCcxT1TNc-Y+ z({Oi9bM?STTWfEn%Fd)~U=$sQVJ?z+p`HptZ6mj@Lpsu>YszSnQI*dtFY~HhK9k99 z7VFhCkpR!2;|O>X4$UT@#MlxkGT%x9h4>gd0pub>%c*cTwX}=~aS%(q6ht{4?Ix9$ zk>Or4%1gueY1r)6O+?u7@G=6*f`(^PM!`n~>@Q38Srjt?<>xW16s)AQhywz$;g{qI zAo+sg0&Z@uxUfK8QXq$XISHUA06uYU$_#D~V;X(nEH)#0Afy8->_BUzu(zgoq`#!U zx3Ht5u(`3gwi;HM#-=MM$plL#&DH8k^#+7kl1F6#bRJ-^7bmJHzF>H^<-%D77y*o~XHxVWl9`8f3eY|oI;uvejJPTb zCTU{Ucx3evMbgK!>hWwQ0*MAgA!s&{s^xPWN}5|v3n-as1GCOduGL`E5=y-SlVGF6 zRCbcfh|onzUU<}_9dQtn41A2psb;Ve7)gd;YhjpcNZw|yIYAOwim^NZP6tRrsKUat zgiXHINLhQ#+7UH%M$|2SVamdCNtr4Zmqp}Z5bVN2c5WW82qZ-!vi#IA@Jd|%7xTpC zXmPn&ewxTdb{fP%FSSx&PCB}rOU^1YGgFM!U%)W4P6HkC|2G<~l+GQ-x4d zC6Lw%g!KY`qfpc)SN7`kgH~O?RoQNk)yWlAJXIA}RV`4|NagjaFL#P=Hi=p->OPO6 z+a>F;NZSnJCIzR8PYMt)4jA4HL#e@Faekg4KUWHc7^xV)oKj&Ur``P4q;ayty);s` zIBc5iQuNhJ+f$mhn5{cr-j{T@`OHb3#>3Xwhz2V*=w}6e^y-v!Y|ytnpP8Sn92toY z^#}VpJb^NyT_<%J96DKQsfaHp6MCCbiY$2HMFJ1GJSz&Z9N{ z_MZOuDs*|@c>26=>+#V1`DootLwc$uG}huCY;p~@`KS6)t21>+JDKChnUj~*$1hUb zH=+4G@7P1nz`VDAnqcv<+!3_Sg4UTdm6@`R4s~@#RGH+|rCAM?jJg!3DWmFdH&66w z2U@swu~L^VSIq|mRFIO(4mup|nfP!=e4^Xj-=OZV)lar&ohf`c6WADz%=cDLb=TBa zRydsv25Y<8Qpb_Di!_58OS4c>%@$TLS!q7K)y99&px>S>+g)_6FPKjcV*9&^Gsvr!zZt=zWV9S>)(Gkdh?>|(UP~l#nIYKbXc{S)ZEjnyT5#X^^f1) z{Li1>|MT;QzkmPc_czbJe}4PTv&SFqZ{FQsy?Jr|>gCwV>d4aS*z(qcjpN0go3W+C z+OBDTvZ|&fI6UoN-45@ZlpS3dE^bv9*OL7s_SL2G$(`lJ*MYa+Cf|HYy! z|Eu-CJ~#jC`}RNF*S^{coQzvnJ9J|yd83E#ke~^OFVl=LS}85H3B| zq33&TBD+qY5t!XdYq^Xp28o$cYB&@?a`<{7%_)a z-tr2+&Sm4M6?i5CO~m4HNCpAM$AToNTr&j}kszHUpo|2#2!I_6SWti!1-OaD0U9zX zp#D4bN-?WS%uVu{Av)1RKnKZ09|7mZVZ20~pF#}K$bKrxO~xmLTn7;&F3Mp60KWi` z!HSfKVimMlkyoJ3&({^@8DTjtLS9S=ZnUC%L&T9ZsWS?#c73VvAtR7<0!@*;uIj?p zdZ4KRsH+1qncRvbI2tPrhtPolL8i!+$@9gM9G(EkMy>)}9%wR2sE8smaddT@RN=>Y zOfVHMhmHgg1%QxNBIcpQA}pJaz|xDLWT2Fmi)0s*B}B56N>DTKCJxRe#s$=bgpriC z5)u}6m0MOFlttYvqZ-Skm!iX<6Jl(9 zn8HqRm{FRzk{6ovn8$tO6c-yLQ6gll8%FgZx6|?yS zzMP@i_KI7C>JNXGc*(ty($pkKjMMiSqCCvQXFKcuP@&wRgJ_?rY?-EqL3<=0k zfC2-ka5*|sLH2jF(M$BWd=nAuq{7SD_z<5S6LTxX!nmC0;G)Xpn1GcW3v=QbL8M9O zYv(z-*vclnxCtj}Cd%6B>TaHK$s2|GMvbmPuWB$T8qBh0o3y=5+Ep&;4Jd~q zvVOm~*Cp+>%X-byE}fuB!mZ|!d_?kJ$E`>v6?HX&h8jw9J+`+EKQ=&IT4JrN zbGP=@$CsY7Cy9gWs+Gg4iOqD|e5`4qvTm`ia-lxC*j%~R-E=h8cD3%BUvV!U`d3bT ziwEABP50P}XKdL&wnQ$AGRtG7S_@2NP*v4Y7xA-BGVKx5x}NR6!e0IjpsIN;LI+X(gAF5mUNc?1g5dK|fNAv58i<=AU2aEd`56_>h z-#*`Z^5P3cZ=UR&U2UH}-n)By_v)u_u0FnbaB^7xVA|c@hSqD@URTG)%I53)qi

)9WL+RtR3G!SiflO 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 t -#include -#include -#include -#include -#include -#include -#include -#include - -#define N_OBJECT_ALLOC 100 -#define LINEBUFSIZE 1024 - - -// -// Types... -// - -struct pdfObject -{ - int offset; -}; - -typedef struct imagetopdf_doc_s // **** Document information **** -{ - int Flip, // Flip/mirror pages - XPosition, // Horizontal position on page - YPosition, // Vertical position on page - Collate, // Collate copies? - Copies, // Number of copies - Reverse, // Output order - EvenDuplex; // Duplex needs even number of pages? - int Orientation, // 0 = portrait, 1 = landscape, etc. - Duplex, // Duplexed? - Color; // Print in color? - float PageLeft, // Left margin - PageRight, // Right margin - PageBottom, // Bottom margin - PageTop, // Top margin - PageWidth, // Total page width - PageLength; // Total page length - struct pdfObject *objects; - int currentObjectNo; - int allocatedObjectNum; - int currentOffset; - int xrefOffset; - int *pageObjects; - int catalogObj; - int pagesObj; - const char *title; - int xpages, // # x pages - ypages, // # y pages - xpage, // Current x page - ypage, // Current y page - page; // Current page number - int xc0, yc0, // Corners of the page in - xc1, yc1; // image coordinates - float left, top; // Left and top of image - float xprint, // Printable area - yprint, - xinches, // Total size in inches - yinches; - float xsize, // Total size in points - ysize, - xsize2, - ysize2; - float aspect; // Aspect ratio - cf_image_t *img; // Image to print - int colorspace; // Output colorspace - cf_ib_t *row; // Current row - float gammaval; // Gamma correction value - float brightness; // Gamma correction value - char linebuf[LINEBUFSIZE]; - FILE *outputfp; -} imagetopdf_doc_t; - - -// -// Local functions... -// - -#ifdef OUT_AS_HEX -static void out_hex(imagetopdf_doc_t *doc, cf_ib_t *, int, int); -#else -#ifdef OUT_AS_ASCII85 -static void out_ascii85(imagetopdf_doc_t *doc, cf_ib_t *, int, int); -#else -static void out_bin(imagetopdf_doc_t *doc, cf_ib_t *, int, int); -#endif -#endif -static void out_pdf(imagetopdf_doc_t *doc, const char *str); -static void putc_pdf(imagetopdf_doc_t *doc, char c); -static int new_obj(imagetopdf_doc_t *doc); -static void free_all_obj(imagetopdf_doc_t *doc); -static void out_xref(imagetopdf_doc_t *doc); -static void out_trailer(imagetopdf_doc_t *doc); -static void out_string(imagetopdf_doc_t *doc, const char *s); -static int out_prologue(imagetopdf_doc_t *doc, int nPages); -static int alloc_page_objects(imagetopdf_doc_t *doc, int noPages); -static void set_offset(imagetopdf_doc_t *doc, int obj); -static int out_page_object(imagetopdf_doc_t *doc, int pageObj, - int contentsObj, int imgObj); -static int out_page_contents(imagetopdf_doc_t *doc, int contentsObj); -static int out_image(imagetopdf_doc_t *doc, int imgObj); - -static void -set_offset(imagetopdf_doc_t *doc, - int obj) -{ - doc->objects[obj].offset = doc->currentOffset; -} - -static int -alloc_page_objects(imagetopdf_doc_t *doc, - int nPages) -{ - int i, n; - - if ((doc->pageObjects = malloc(sizeof(int)*nPages)) == NULL) - return (-1); - for (i = 0;i < nPages;i++) - { - if ((n = new_obj(doc)) >= 0) - doc->pageObjects[i] = n; - else - return (-1); - } - return (0); -} - -static int -new_obj(imagetopdf_doc_t *doc) -{ - if (doc->objects == NULL) - { - if ((doc->objects = malloc(sizeof(struct pdfObject) * N_OBJECT_ALLOC)) == - NULL) - return(-1); - doc->allocatedObjectNum = N_OBJECT_ALLOC; - } - if (doc->currentObjectNo >= doc->allocatedObjectNum) - { - if ((doc->objects = realloc(doc->objects, - sizeof(struct pdfObject) * - (doc->allocatedObjectNum + N_OBJECT_ALLOC))) == - NULL) - return(-1); - doc->allocatedObjectNum += N_OBJECT_ALLOC; - } - doc->objects[doc->currentObjectNo].offset = doc->currentOffset; - return (doc->currentObjectNo++); -} - -static void -free_all_obj(imagetopdf_doc_t *doc) -{ - if (doc->objects != NULL) - { - free(doc->objects); - doc->objects = NULL; - } -} - -static void -putc_pdf(imagetopdf_doc_t *doc, - char c) -{ - fputc(c, doc->outputfp); - doc->currentOffset++; -} - -static void -out_pdf(imagetopdf_doc_t *doc, - const char *str) -{ - unsigned long len = strlen(str); - - fputs(str, doc->outputfp); - doc->currentOffset += len; -} - -static void -out_xref(imagetopdf_doc_t *doc) -{ - char buf[21]; - int i; - - doc->xrefOffset = doc->currentOffset; - out_pdf(doc, "xref\n"); - snprintf(buf, 21, "0 %d\n", doc->currentObjectNo); - out_pdf(doc, buf); - out_pdf(doc, "0000000000 65535 f \n"); - for (i = 1; i < doc->currentObjectNo; i ++) - { - snprintf(buf, 21, "%010d 00000 n \n", doc->objects[i].offset); - out_pdf(doc, buf); - } -} - -static void -out_string(imagetopdf_doc_t *doc, - const char *s) -{ - char c; - - putc_pdf(doc, '('); - for (; (c = *s) != '\0'; s ++) - { - if (c == '\\' || c == '(' || c == ')') - putc_pdf(doc, '\\'); - putc_pdf(doc, c); - } - putc_pdf(doc, ')'); -} - -static void -out_trailer(imagetopdf_doc_t *doc) -{ - time_t curtime; - struct tm *curtm; - char curdate[255]; - - curtime = time(NULL); - curtm = localtime(&curtime); - strftime(curdate, sizeof(curdate), "D:%Y%m%d%H%M%S%z", curtm); - - out_pdf(doc, "trailer\n"); - snprintf(doc->linebuf, LINEBUFSIZE, "<currentObjectNo); - out_pdf(doc, doc->linebuf); - out_pdf(doc, "/Root 1 0 R\n"); - out_pdf(doc, "/Info << /Title "); - out_string(doc, doc->title); - putc_pdf(doc, ' '); - snprintf(doc->linebuf, LINEBUFSIZE, "/CreationDate (%s) ", curdate); - out_pdf(doc, doc->linebuf); - snprintf(doc->linebuf, LINEBUFSIZE, "/ModDate (%s) ", curdate); - out_pdf(doc, doc->linebuf); - out_pdf(doc, "/Producer (imagetopdf) "); - out_pdf(doc, "/Trapped /False >>\n"); - out_pdf(doc, ">>\n"); - out_pdf(doc, "startxref\n"); - snprintf(doc->linebuf, LINEBUFSIZE, "%d\n", doc->xrefOffset); - out_pdf(doc, doc->linebuf); - out_pdf(doc, "%%EOF\n"); -} - -static int -out_prologue(imagetopdf_doc_t *doc, - int nPages) -{ - int i; - - // out header - if (new_obj(doc) < 0) // dummy for no 0 object - return (-1); - out_pdf(doc, "%PDF-1.3\n"); - // out binary for transfer program - doc->linebuf[0] = '%'; - doc->linebuf[1] = (char)129; - doc->linebuf[2] = (char)130; - doc->linebuf[3] = (char)131; - doc->linebuf[4] = (char)132; - doc->linebuf[5] = '\n'; - doc->linebuf[6] = (char)0; - out_pdf(doc, doc->linebuf); - out_pdf(doc, "% This file was generated by imagetopdf\n"); - - if ((doc->catalogObj = new_obj(doc)) < 0) - return (-1); - if ((doc->pagesObj = new_obj(doc)) < 0) - return (-1); - if (alloc_page_objects(doc, nPages) < 0) - return (-1); - - // out catalog - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj <catalogObj, doc->pagesObj); - out_pdf(doc, doc->linebuf); - out_pdf(doc, ">> endobj\n"); - - // out Pages - set_offset(doc, doc->pagesObj); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj <pagesObj); - out_pdf(doc, doc->linebuf); - if (doc->Reverse) - { - for (i = nPages - 1; i >= 0; i --) - { - snprintf(doc->linebuf, LINEBUFSIZE, "%d 0 R ", doc->pageObjects[i]); - out_pdf(doc, doc->linebuf); - } - } - else - { - for (i = 0; i < nPages; i ++) - { - snprintf(doc->linebuf, LINEBUFSIZE, "%d 0 R ", doc->pageObjects[i]); - out_pdf(doc, doc->linebuf); - } - } - out_pdf(doc, "] "); - snprintf(doc->linebuf, LINEBUFSIZE, "/Count %d >> endobj\n", nPages); - out_pdf(doc, doc->linebuf); - return (0); -} - -static int -out_page_object(imagetopdf_doc_t *doc, - int pageObj, - int contentsObj, - int imgObj) -{ - int trfuncObj; - int lengthObj; - int startOffset; - int length; - int outTrfunc = (doc->gammaval != 1.0 || doc->brightness != 1.0); - - // out Page Object - set_offset(doc, pageObj); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj <pagesObj); - out_pdf(doc, doc->linebuf); - snprintf(doc->linebuf, LINEBUFSIZE, - "/MediaBox [ 0 0 %f %f ] ", doc->PageWidth, doc->PageLength); - out_pdf(doc, doc->linebuf); - snprintf(doc->linebuf, LINEBUFSIZE, - "/TrimBox [ 0 0 %f %f ] ", doc->PageWidth, doc->PageLength); - out_pdf(doc, doc->linebuf); - snprintf(doc->linebuf, LINEBUFSIZE, - "/CropBox [ 0 0 %f %f ] ", doc->PageWidth, doc->PageLength); - out_pdf(doc, doc->linebuf); - if (contentsObj >= 0) - { - snprintf(doc->linebuf, LINEBUFSIZE, - "/Contents %d 0 R ", contentsObj); - out_pdf(doc, doc->linebuf); - snprintf(doc->linebuf, LINEBUFSIZE, - "/Resources <>\n", imgObj); - out_pdf(doc, doc->linebuf); - } - else - { - // empty page - snprintf(doc->linebuf, LINEBUFSIZE, - "/Resources <linebuf); - } - if (outTrfunc) - { - if ((trfuncObj = new_obj(doc)) < 0) - return (-1); - if ((lengthObj = new_obj(doc)) < 0) - return (-1); - snprintf(doc->linebuf, LINEBUFSIZE, - "/ExtGState << /GS1 << /TR %d 0 R >> >>\n", trfuncObj); - out_pdf(doc, doc->linebuf); - } - out_pdf(doc, " >>\n>>\nendobj\n"); - - if (outTrfunc) - { - // out translate function - set_offset(doc, trfuncObj); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj <>\n", - trfuncObj, lengthObj); - out_pdf(doc, doc->linebuf); - out_pdf(doc, "stream\n"); - startOffset = doc->currentOffset; - snprintf(doc->linebuf, LINEBUFSIZE, - "{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } " - "ifelse %.3f mul }\n", doc->gammaval, doc->brightness); - out_pdf(doc, doc->linebuf); - length = doc->currentOffset - startOffset; - snprintf(doc->linebuf, LINEBUFSIZE, - "endstream\nendobj\n"); - out_pdf(doc, doc->linebuf); - - // out length object - set_offset(doc, lengthObj); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj %d endobj\n", lengthObj, length); - out_pdf(doc, doc->linebuf); - } - return (0); -} - -static int -out_page_contents(imagetopdf_doc_t *doc, - int contentsObj) -{ - int startOffset; - int lengthObj; - int length; - - set_offset(doc, contentsObj); - if ((lengthObj = new_obj(doc)) < 0) - return (-1); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj <> stream\n", contentsObj, lengthObj); - out_pdf(doc, doc->linebuf); - startOffset = doc->currentOffset; - - if (doc->gammaval != 1.0 || doc->brightness != 1.0) - out_pdf(doc, "/GS1 gs\n"); - if (doc->Flip) - { - snprintf(doc->linebuf, LINEBUFSIZE, - "-1 0 0 1 %.0f 0 cm\n", doc->PageWidth); - out_pdf(doc, doc->linebuf); - } - - switch (doc->Orientation) - { - case 1: - snprintf(doc->linebuf, LINEBUFSIZE, - "0 1 -1 0 %.0f 0 cm\n", doc->PageWidth); - out_pdf(doc, doc->linebuf); - break; - case 2: - snprintf(doc->linebuf, LINEBUFSIZE, - "-1 0 0 -1 %.0f %.0f cm\n", doc->PageWidth, doc->PageLength); - out_pdf(doc, doc->linebuf); - break; - case 3: - snprintf(doc->linebuf, LINEBUFSIZE, - "0 -1 1 0 0 %.0f cm\n", doc->PageLength); - out_pdf(doc, doc->linebuf); - break; - } - - doc->xc0 = cfImageGetWidth(doc->img) * doc->xpage / doc->xpages; - doc->xc1 = cfImageGetWidth(doc->img) * (doc->xpage + 1) / doc->xpages - 1; - doc->yc0 = cfImageGetHeight(doc->img) * doc->ypage / doc->ypages; - doc->yc1 = cfImageGetHeight(doc->img) * (doc->ypage + 1) / doc->ypages - 1; - - snprintf(doc->linebuf, LINEBUFSIZE, - "1 0 0 1 %.1f %.1f cm\n", doc->left, doc->top); - out_pdf(doc, doc->linebuf); - - snprintf(doc->linebuf, LINEBUFSIZE, - "%.3f 0 0 %.3f 0 0 cm\n", - doc->xprint * 72.0, doc->yprint * 72.0); - out_pdf(doc, doc->linebuf); - out_pdf(doc, "/Im Do\n"); - length = doc->currentOffset - startOffset - 1; - out_pdf(doc, "endstream\nendobj\n"); - - // out length object - set_offset(doc, lengthObj); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj %d endobj\n", lengthObj, length); - out_pdf(doc, doc->linebuf); - return (0); -} - -static int -out_image(imagetopdf_doc_t *doc, - int imgObj) -{ - int y; // Current Y coordinate in image -#ifdef OUT_AS_ASCII85 - int out_offset; // Offset into output buffer -#endif - int out_length; // Length of output buffer - int startOffset; - int lengthObj; - int length; - - set_offset(doc, imgObj); - if ((lengthObj = new_obj(doc)) < 0) - return (-1); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj << /Length %d 0 R /Type /XObject " - "/Subtype /Image /Name /Im" -#ifdef OUT_AS_HEX - "/Filter /ASCIIHexDecode " -#else -#ifdef OUT_AS_ASCII85 - "/Filter /ASCII85Decode " -#endif -#endif - , imgObj, lengthObj); - out_pdf(doc, doc->linebuf); - snprintf(doc->linebuf, LINEBUFSIZE, - "/Width %d /Height %d /BitsPerComponent 8 ", - doc->xc1 - doc->xc0 + 1, doc->yc1 - doc->yc0 + 1); - out_pdf(doc, doc->linebuf); - - switch (doc->colorspace) - { - case CF_IMAGE_WHITE : - out_pdf(doc, "/ColorSpace /DeviceGray "); - out_pdf(doc, "/Decode[0 1] "); - break; - case CF_IMAGE_RGB : - out_pdf(doc, "/ColorSpace /DeviceRGB "); - out_pdf(doc, "/Decode[0 1 0 1 0 1] "); - break; - case CF_IMAGE_CMYK : - out_pdf(doc, "/ColorSpace /DeviceCMYK "); - out_pdf(doc, "/Decode[0 1 0 1 0 1 0 1] "); - break; - } - if (((doc->xc1 - doc->xc0 + 1) / doc->xprint) < 100.0) - out_pdf(doc, "/Interpolate true "); - - out_pdf(doc, ">>\n"); - out_pdf(doc, "stream\n"); - startOffset = doc->currentOffset; - -#ifdef OUT_AS_ASCII85 - // out ascii85 needs multiple of 4bytes - for (y = doc->yc0, out_offset = 0; y <= doc->yc1; y ++) - { - cfImageGetRow(doc->img, doc->xc0, y, doc->xc1 - doc->xc0 + 1, - doc->row + out_offset); - - out_length = (doc->xc1 - doc->xc0 + 1) * abs(doc->colorspace) + out_offset; - out_offset = out_length & 3; - - out_ascii85(doc, doc->row, out_length, y == doc->yc1); - - if (out_offset > 0) - memcpy(doc->row, doc->row + out_length - out_offset, out_offset); - } -#else - for (y = doc->yc0; y <= doc->yc1; y ++) - { - cfImageGetRow(doc->img, doc->xc0, y, doc->xc1 - doc->xc0 + 1, doc->row); - - out_length = (doc->xc1 - doc->xc0 + 1) * abs(doc->colorspace); - -#ifdef OUT_AS_HEX - out_hex(doc, doc->row, out_length, y == doc->yc1); -#else - out_bin(doc, doc->row, out_length, y == doc->yc1); -#endif - } -#endif - length = doc->currentOffset - startOffset; - out_pdf(doc, "\nendstream\nendobj\n"); - - // out length object - set_offset(doc, lengthObj); - snprintf(doc->linebuf, LINEBUFSIZE, - "%d 0 obj %d endobj\n", lengthObj, length); - out_pdf(doc, doc->linebuf); - return (0); -} - - -// -// 'cfFilterImageToPDF()' - Filter function to convert many common image file -// formats into PDF -// - -int // O - Error status -cfFilterImageToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - imagetopdf_doc_t doc; // Document information - cups_page_header2_t h; // CUPS Raster page header, to - // accommodate results of command - // line and IPP parsing - int num_options = 0; // Number of print options - cups_option_t *options = NULL; // Print options - const char *val; // Option value - float zoom; // Zoom facter - int xppi, yppi; // Pixels-per-inch - int hue, sat; // Hue and saturation adjustment - int pdf_printer = 0; - char tempfile[1024]; // Name of file to print - FILE *fp; // Input file - int fd; // File descriptor for temp file - char buf[BUFSIZ]; - int bytes; - int deviceCopies = 1; - int deviceCollate = 0; - int deviceReverse = 0; - int pl, pr; - int fillprint = 0; // print-scaling = fill - int cropfit = 0; // -o crop-to-fit = true - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *ipp; - cups_cspace_t cspace = (cups_cspace_t)(-1); - int min_length = __INT32_MAX__, - min_width = __INT32_MAX__, - max_length = 0, - max_width = 0; - float customLeft = 0.0, - customBottom = 0.0, - customRight = 0.0, - customTop = 0.0; - char defSize[41]; - - - // - // Make sure status messages are not buffered... - // - - setbuf(stderr, NULL); - - // - // Initialize data structure - // - - doc.Flip = 0; - doc.XPosition = 0; - doc.YPosition = 0; - doc.Collate = 0; - doc.Copies = 1; - doc.Reverse = 0; - doc.EvenDuplex = 0; - doc.objects = NULL; - doc.currentObjectNo = 0; - doc.allocatedObjectNum = 0; - doc.currentOffset = 0; - doc.pageObjects = NULL; - doc.gammaval = 1.0; - doc.brightness = 1.0; - - // - // Open the input data stream specified by the inputfd ... - // - - if ((fp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Unable to open input data stream."); - } - - return (1); - } - - // - // Copy input into temporary file if needed ... - // - - if (!inputseekable) - { - if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Unable to copy input: %s", - strerror(errno)); - fclose(fp); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0) - bytes = write(fd, buf, bytes); - - fclose(fp); - close(fd); - - // - // Open the temporary file to read it instead of the original input ... - // - - if ((fp = fopen(tempfile, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Unable to open temporary file."); - } - - unlink(tempfile); - return (1); - } - } - - // - // Open the output data stream specified by the outputfd... - // - - if ((doc.outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Unable to open output data stream."); - } - - fclose(fp); - if (!inputseekable) - unlink(tempfile); - - return (1); - } - - // - // Process options and write the prolog... - // - - zoom = 1.0; - xppi = 0; - yppi = 0; - hue = 0; - sat = 100; - - doc.title = data->job_title; - doc.Copies = data->copies; - - // - // Option list... - // Also add job-attrs in options list itself. - // - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - // - // Compute custom margins and min_width and min_length of the page... - // - - if (printer_attrs != NULL) - { - int left, bottom, right, top; - cfGenerateSizes(printer_attrs, CF_GEN_SIZES_DEFAULT, NULL, &ipp, - NULL, NULL, NULL, NULL, NULL, NULL, - &min_width, &min_length, &max_width, &max_length, - &left, &bottom, &right, &top, defSize, NULL); - customLeft = left * 72.0 / 2540.0; - customBottom = bottom * 72.0 / 2540.0; - customRight = right * 72.0 / 2540.0; - customTop = top * 72.0 / 2540.0; - } - - // - // Process job options... - // - - // To find the correct output color space, resolution page size, ... - // and to parse all releveant command line options we run - // cfRasterPrepareHeader() here and afterwards we simply take the - // needed parameters from the header. - cfRasterPrepareHeader(&h, data, CF_FILTER_OUT_FORMAT_CUPS_RASTER, - CF_FILTER_OUT_FORMAT_CUPS_RASTER, 0, &cspace); - doc.Color = h.cupsNumColors <= 1 ? 0 : 1; - doc.Orientation = h.Orientation; - doc.Duplex = h.Duplex; - doc.PageWidth = h.cupsPageSize[0] != 0.0 ? h.cupsPageSize[0] : - (float)h.PageSize[0]; - doc.PageLength = h.cupsPageSize[1] != 0.0 ? h.cupsPageSize[1] : - (float)h.PageSize[1]; - doc.PageLeft = h.cupsImagingBBox[0] != 0.0 ? h.cupsImagingBBox[0] : - (float)h.ImagingBoundingBox[0]; - doc.PageBottom = h.cupsImagingBBox[1] != 0.0 ? h.cupsImagingBBox[1] : - (float)h.ImagingBoundingBox[1]; - doc.PageRight = h.cupsImagingBBox[2] != 0.0 ? h.cupsImagingBBox[2] : - (float)h.ImagingBoundingBox[2]; - doc.PageTop = h.cupsImagingBBox[3] != 0.0 ? h.cupsImagingBBox[3] : - (float)h.ImagingBoundingBox[3]; - doc.Flip = h.MirrorPrint ? 1 : 0; - doc.Collate = h.Collate ? 1 : 0; - doc.Copies = data->copies; - - if (doc.Copies == 0) - doc.Copies = 1; - - // Do we need to print the pages in reverse order? - if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL || - (val = cupsGetOption("output-order", num_options, options)) != NULL || - (val = cupsGetOption("page-delivery", num_options, options)) != NULL) - doc.Reverse = (strcasecmp(val, "Reverse") == 0 || - strcasecmp(val, "reverse-order") == 0); - else - doc.Reverse = cfIPPReverseOutput(printer_attrs, job_attrs); - - // adjust to even page when duplex - if ((val = cupsGetOption("even-duplex", num_options, options)) != 0 && - (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes"))) - doc.EvenDuplex = 1; - - // Collate - if ((val = cupsGetOption("Collate", num_options, options)) != NULL && - (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes"))) - doc.Collate = 1; - else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) - doc.Collate = (strcasecmp(val, "uncollated") != 0); - else if (((val = cupsGetOption("multiple-document-handling", - num_options, options)) != NULL && - (strcasecmp(val, "separate-documents-collated-copies") == 0 || - strcasecmp(val, "separate-documents-uncollated-copies") == 0 || - strcasecmp(val, "single-document") == 0 || - strcasecmp(val, "single-document-new-sheet") == 0)) || - (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "multiple-document-handling")) != - NULL) - // - // This IPP attribute is unnecessarily complicated: - // - // single-document, separate-documents-collated-copies, - // single-document-new-sheet: - // -> collate (true) - // - // separate-documents-uncollated-copies: - // -> can be uncollated (false) - // - doc.Collate = - (strcasecmp(val, "separate-documents-uncollated-copies") != 0); - - if ((val = cupsGetOption("gamma", num_options, options)) != NULL) - doc.gammaval = atoi(val) * 0.001f; - - if ((val = cupsGetOption("brightness", num_options, options)) != NULL) - doc.brightness = atoi(val) * 0.01f; - - if ((val = cupsGetOption("ppi", num_options, options)) != NULL) - { - sscanf(val, "%d", &xppi); - yppi = xppi; - zoom = 0.0; - } - - if ((val = cupsGetOption("position", num_options, options)) != NULL) - { - if (strcasecmp(val, "center") == 0) - { - doc.XPosition = 0; - doc.YPosition = 0; - } - else if (strcasecmp(val, "top") == 0) - { - doc.XPosition = 0; - doc.YPosition = 1; - } - else if (strcasecmp(val, "left") == 0) - { - doc.XPosition = -1; - doc.YPosition = 0; - } - else if (strcasecmp(val, "right") == 0) - { - doc.XPosition = 1; - doc.YPosition = 0; - } - else if (strcasecmp(val, "top-left") == 0) - { - doc.XPosition = -1; - doc.YPosition = 1; - } - else if (strcasecmp(val, "top-right") == 0) - { - doc.XPosition = 1; - doc.YPosition = 1; - } - else if (strcasecmp(val, "bottom") == 0) - { - doc.XPosition = 0; - doc.YPosition = -1; - } - else if (strcasecmp(val, "bottom-left") == 0) - { - doc.XPosition = -1; - doc.YPosition = -1; - } - else if (strcasecmp(val, "bottom-right") == 0) - { - doc.XPosition = 1; - doc.YPosition = -1; - } - } - - if ((val = cupsGetOption("saturation", num_options, options)) != NULL) - sat = atoi(val); - - if ((val = cupsGetOption("hue", num_options, options)) != NULL) - hue = atoi(val); - - if ((val = cupsGetOption("mirror", num_options, options)) != NULL && - strcasecmp(val, "True") == 0) - doc.Flip = 1; - - // - // Open the input image to print... - // - - doc.colorspace = doc.Color ? CF_IMAGE_RGB_CMYK : CF_IMAGE_WHITE; - - doc.img = cfImageOpenFP(fp, doc.colorspace, CF_IMAGE_WHITE, sat, hue, - NULL); - if (doc.img != NULL) - { - int margin_defined = 0; - int fidelity = 0; - int document_large = 0; - - if (customLeft != 0 || customRight != 0 || - customBottom != 0 || customTop != 0 || - doc.PageLength != doc.PageTop - doc.PageBottom || - doc.PageWidth != doc.PageRight - doc.PageLeft) - margin_defined = 1; - - if ((val = cupsGetOption("ipp-attribute-fidelity",num_options,options)) != - NULL) - { - if(!strcasecmp(val, "true") || !strcasecmp(val, "yes") || - !strcasecmp(val, "on")) - fidelity = 1; - } - - float w = (float)cfImageGetWidth(doc.img); - float h = (float)cfImageGetHeight(doc.img); - float pw = doc.PageRight - doc.PageLeft; - float ph = doc.PageTop - doc.PageBottom; - int tempOrientation = doc.Orientation; - if ((val = cupsGetOption("orientation-requested", - num_options,options)) != NULL) - tempOrientation = atoi(val); - else if ((val = cupsGetOption("landscape", num_options,options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - tempOrientation = 4; - } - if (tempOrientation == 0) - { - if(((pw > ph) && (w < h)) || ((pw < ph) && (w > h))) - tempOrientation = 4; - } - if (tempOrientation == 4 || tempOrientation == 5) - { - int tmp = pw; - pw = ph; - ph = tmp; - } - if (w * 72.0 / doc.img->xppi > pw || h * 72.0 / doc.img->yppi > ph) - document_large = 1; - - if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) - { - if (!strcasecmp(val, "auto")) - { - if (fidelity || document_large) - { - if (margin_defined) - zoom = 1.0; // fit method - else - fillprint = 1; // fill method - } - else - cropfit = 1; // none method - } - else if (!strcasecmp(val, "auto-fit")) - { - if (fidelity || document_large) - zoom = 1.0; // fit method - else - cropfit = 1; // none method - } - else if (!strcasecmp(val, "fill")) - fillprint = 1; // fill method - else if (!strcasecmp(val, "fit")) - zoom = 1.0; // fitplot = 1 or fit method - else - cropfit = 1; // none or crop-to-fit - } - else - { // print-scaling is not defined, look for alternate options. - if ((val = cupsGetOption("scaling", num_options, options)) != NULL) - zoom = atoi(val) * 0.01; - else if (((val = - cupsGetOption("fit-to-page", num_options, options)) != NULL) || - ((val = cupsGetOption("fitplot", num_options, options)) != NULL)) - { - if (!strcasecmp(val, "yes") || !strcasecmp(val, "on") || - !strcasecmp(val, "true")) - zoom = 1.0; - else - zoom = 0.0; - } - else if ((val = cupsGetOption("natural-scaling", num_options, options)) != - NULL) - zoom = 0.0; - - if ((val = cupsGetOption("fill", num_options, options)) != 0) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - fillprint = 1; - } - - if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val , "yes")) - cropfit = 1; - } - } - } - if (fillprint || cropfit) - { - // For cropfit do the math without the unprintable margins to get correct - // centering - if (cropfit) - { - doc.PageBottom = 0.0; - doc.PageTop = doc.PageLength; - doc.PageLeft = 0.0; - doc.PageRight = doc.PageWidth; - } - float w = (float)cfImageGetWidth(doc.img); - float h = (float)cfImageGetHeight(doc.img); - float pw = doc.PageRight - doc.PageLeft; - float ph = doc.PageTop - doc.PageBottom; - int tempOrientation = doc.Orientation; - const char *val; - int flag = 3; - if ((val = cupsGetOption("orientation-requested", num_options, options)) != - NULL) - tempOrientation = atoi(val); - else if((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - tempOrientation = 4; - } - if (tempOrientation > 0) - { - if (tempOrientation == 4 || tempOrientation == 5) - { - float temp = pw; - pw = ph; - ph = temp; - flag = 4; - } - } - if (tempOrientation == 0) - { - if(((pw > ph) && (w < h)) || ((pw < ph) && (w > h))) - { - int temp = pw; - pw = ph; - ph = temp; - flag = 4; - } - } - if (fillprint) - { - float final_w, final_h; - if (w * ph / pw <= h) - { - final_w = w; - final_h = w * ph / pw; - } - else - { - final_w = h * pw / ph; - final_h = h; - } - float posw = (w - final_w) / 2, posh = (h - final_h) / 2; - posw = (1 + doc.XPosition) * posw; - posh = (1 - doc.YPosition) * posh; - cf_image_t *img2 = cfImageCrop(doc.img, posw, posh, final_w, final_h); - cfImageClose(doc.img); - doc.img = img2; - } - else - { - float final_w = w, final_h = h; - if (w > pw * doc.img->xppi / 72.0) - final_w = pw * doc.img->xppi / 72.0; - if (h > ph * doc.img->yppi / 72.0) - final_h = ph * doc.img->yppi / 72.0; - float posw = (w - final_w) / 2, posh = (h - final_h) / 2; - posw = (1 + doc.XPosition) * posw; - posh = (1 - doc.YPosition) * posh; - cf_image_t *img2 = cfImageCrop(doc.img, posw, posh, final_w, final_h); - cfImageClose(doc.img); - doc.img = img2; - if (flag == 4) - { - doc.PageBottom += (doc.PageLength - final_w * 72.0 / doc.img->xppi) / 2; - doc.PageTop = doc.PageBottom + final_w * 72.0 / doc.img->xppi; - doc.PageLeft += (doc.PageWidth - final_h * 72.0 / doc.img->yppi) / 2; - doc.PageRight = doc.PageLeft + final_h * 72.0 / doc.img->yppi; - } - else - { - doc.PageBottom += (doc.PageLength - final_h * 72.0 / doc.img->yppi) / 2; - doc.PageTop = doc.PageBottom + final_h * 72.0 / doc.img->yppi; - doc.PageLeft += (doc.PageWidth - final_w * 72.0 / doc.img->xppi) / 2; - doc.PageRight = doc.PageLeft + final_w * 72.0 / doc.img->xppi; - } - if (doc.PageBottom < 0) doc.PageBottom = 0; - if (doc.PageLeft < 0) doc.PageLeft = 0; - } - } - - if (!inputseekable) - unlink(tempfile); - - if (doc.img == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Unable to open image file for printing!"); - fclose(doc.outputfp); - close(outputfd); - return (1); - } - - doc.colorspace = cfImageGetColorSpace(doc.img); - - // - // Scale as necessary... - // - - if (zoom == 0.0 && xppi == 0) - { - xppi = cfImageGetXPPI(doc.img); - yppi = cfImageGetYPPI(doc.img); - } - - if (yppi == 0) - yppi = xppi; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Before scaling: xppi=%d, yppi=%d, zoom=%.2f", - xppi, yppi, zoom); - - if (xppi > 0) - { - // - // Scale the image as neccesary to match the desired pixels-per-inch. - // - - if (doc.Orientation & 1) - { - doc.xprint = (doc.PageTop - doc.PageBottom) / 72.0; - doc.yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - doc.xprint = (doc.PageRight - doc.PageLeft) / 72.0; - doc.yprint = (doc.PageTop - doc.PageBottom) / 72.0; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Before scaling: xprint=%.1f, yprint=%.1f", - doc.xprint, doc.yprint); - - doc.xinches = (float)cfImageGetWidth(doc.img) / (float)xppi; - doc.yinches = (float)cfImageGetHeight(doc.img) / (float)yppi; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Image size is %.1f x %.1f inches...", - doc.xinches, doc.yinches); - - if ((val = cupsGetOption("natural-scaling", num_options, options)) != NULL) - { - doc.xinches = doc.xinches * atoi(val) / 100; - doc.yinches = doc.yinches * atoi(val) / 100; - } - - if (cupsGetOption("orientation-requested", num_options, options) == NULL && - cupsGetOption("landscape", num_options, options) == NULL) - { - // - // Rotate the image if it will fit landscape but not portrait... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Auto orientation..."); - - if ((doc.xinches > doc.xprint || doc.yinches > doc.yprint) && - doc.xinches <= doc.yprint && doc.yinches <= doc.xprint) - { - // - // Rotate the image as needed... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Using landscape orientation..."); - - doc.Orientation = (doc.Orientation + 1) & 3; - doc.xsize = doc.yprint; - doc.yprint = doc.xprint; - doc.xprint = doc.xsize; - } - } - } - else - { - // - // Scale percentage of page size... - // - - doc.xprint = (doc.PageRight - doc.PageLeft) / 72.0; - doc.yprint = (doc.PageTop - doc.PageBottom) / 72.0; - doc.aspect = (float)cfImageGetYPPI(doc.img) / - (float)cfImageGetXPPI(doc.img); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Before scaling: xprint=%.1f, yprint=%.1f", - doc.xprint, doc.yprint); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: cfImageGetXPPI(img) = %d, " - "cfImageGetYPPI(img) = %d, aspect = %f", - cfImageGetXPPI(doc.img), cfImageGetYPPI(doc.img), - doc.aspect); - - doc.xsize = doc.xprint * zoom; - doc.ysize = doc.xsize * cfImageGetHeight(doc.img) / - cfImageGetWidth(doc.img) / doc.aspect; - - if (doc.ysize > (doc.yprint * zoom)) - { - doc.ysize = doc.yprint * zoom; - doc.xsize = doc.ysize * cfImageGetWidth(doc.img) * - doc.aspect / cfImageGetHeight(doc.img); - } - - doc.xsize2 = doc.yprint * zoom; - doc.ysize2 = doc.xsize2 * cfImageGetHeight(doc.img) / - cfImageGetWidth(doc.img) / doc.aspect; - - if (doc.ysize2 > (doc.xprint * zoom)) - { - doc.ysize2 = doc.xprint * zoom; - doc.xsize2 = doc.ysize2 * cfImageGetWidth(doc.img) * - doc.aspect / cfImageGetHeight(doc.img); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Portrait size is %.2f x %.2f inches", - doc.xsize, doc.ysize); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Landscape size is %.2f x %.2f inches", - doc.xsize2, doc.ysize2); - - if (cupsGetOption("orientation-requested", num_options, options) == NULL && - cupsGetOption("landscape", num_options, options) == NULL) - { - // - // Choose the rotation with the largest area, but prefer - // portrait if they are equal... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Auto orientation..."); - - if ((doc.xsize * doc.ysize) < (doc.xsize2 * doc.xsize2)) - { - // - // Do landscape orientation... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Using landscape orientation..."); - - doc.Orientation = 1; - doc.xinches = doc.xsize2; - doc.yinches = doc.ysize2; - doc.xprint = (doc.PageTop - doc.PageBottom) / 72.0; - doc.yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - // - // Do portrait orientation... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Using portrait orientation..."); - - doc.Orientation = 0; - doc.xinches = doc.xsize; - doc.yinches = doc.ysize; - } - } - else if (doc.Orientation & 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Using landscape orientation..."); - - doc.xinches = doc.xsize2; - doc.yinches = doc.ysize2; - doc.xprint = (doc.PageTop - doc.PageBottom) / 72.0; - doc.yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Using portrait orientation..."); - - doc.xinches = doc.xsize; - doc.yinches = doc.ysize; - doc.xprint = (doc.PageRight - doc.PageLeft) / 72.0; - doc.yprint = (doc.PageTop - doc.PageBottom) / 72.0; - } - } - - // - // Compute the number of pages to print and the size of the image on each - // page... - // - - if (zoom == 1.0) - { - // If fitplot is specified, make xpages, ypages 1 forcedly. - // Because calculation error may be caused and - // result of ceil function may be larger than 1. - doc.xpages = doc.ypages = 1; - } - else - { - doc.xpages = ceil(doc.xinches / doc.xprint); - doc.ypages = ceil(doc.yinches / doc.yprint); - } - - doc.xprint = doc.xinches / doc.xpages; - doc.yprint = doc.yinches / doc.ypages; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: xpages = %dx%.2fin, ypages = %dx%.2fin", - doc.xpages, doc.xprint, doc.ypages, doc.yprint); - - // - // Update the page size for custom sizes... - // - - strcpy(defSize, h.cupsPageSizeName); - - if ((strncasecmp(defSize, "Custom", 6)) == 0 || - strcasestr(defSize, "_custom_")) - { - float width, // New width in points - length; // New length in points - - - // - // Use the correct width and length for the current orientation... - // - - if (doc.Orientation & 1) - { - width = doc.yprint * 72.0; - length = doc.xprint * 72.0; - } - else - { - width = doc.xprint * 72.0; - length = doc.yprint * 72.0; - } - - // - // Add margins to page size... - // - - width += customLeft + customRight; - length += customTop + customBottom; - - // - // Enforce minimums... - // - - if (width < min_width) - width = min_width; - if (length < min_length) - length = min_length; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Updated custom page size to %.2f x %.2f " - "inches...", - width / 72.0, length / 72.0); - - // - // Set the new custom size... - // - - strcpy(h.cupsPageSizeName, "Custom"); - - h.cupsPageSize[0] = width + 0.5; - h.cupsPageSize[1] = length + 0.5; - h.PageSize[0] = width + 0.5; - h.PageSize[1] = length + 0.5; - - // - // Update page variables... - // - - doc.PageWidth = width; - doc.PageLength = length; - doc.PageLeft = customLeft; - doc.PageRight = width - customRight; - doc.PageBottom = customBottom; - doc.PageTop = length - customTop; - } - - if (doc.Copies == 1) - { - // collate is not needed - doc.Collate = 0; - } - - if (!doc.Duplex) - { - // evenDuplex is not needed - doc.EvenDuplex = 0; - } - - // Check options for caller's instructions about hardware copies/collate - if ((val = cupsGetOption("hardware-copies", - num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes")) - { - // Use hardware copies according to the caller's instructions - deviceCopies = doc.Copies; - doc.Copies = 1; - } - } - else if (data->final_content_type && - (strcasestr(data->final_content_type, "/pdf") || - strcasestr(data->final_content_type, "/vnd.cups-pdf"))) - { - // Caller did not tell us whether the printer does Hardware copies - // or not, so we assume hardware copies on PDF printers, and software - // copies on other (usually raster) printers or if we do not know the - // final output format. - deviceCopies = doc.Copies; - doc.Copies = 1; - } - - if (deviceCopies > 1 && doc.Collate) - { - if ((val = cupsGetOption("hardware-collate", - num_options, options)) != NULL) - { - // Use hardware collate according to the caller's instructions - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes")) - deviceCollate = 1; - } - else - // Check output format MIME type whether it is - // of a driverless IPP printer (PDF, Apple Raster, PWG Raster, PCLm). - // These printers do always hardware collate if they do hardware copies. - // https://github.com/apple/cups/issues/5433 - deviceCollate = (data->final_content_type && - (strcasestr(data->final_content_type, "/pdf") || - strcasestr(data->final_content_type, "/vnd.cups-pdf") || - strcasestr(data->final_content_type, "/pwg-raster") || - strcasestr(data->final_content_type, "/urf") || - strcasestr(data->final_content_type, "/PCLm"))); - } - - if (deviceCopies && doc.Collate && !deviceCollate) - { - // Copying by device , software collate is impossible - // Enable software copying - doc.Copies = deviceCopies; - deviceCopies = 1; - } - - if (doc.Copies > 1 && deviceCopies == 1 && doc.Duplex) - { - // Enable software collate, or same pages are printed in both sides - doc.Collate = 1; - if (deviceCollate) - deviceCollate = 0; - } - - if (doc.Duplex && doc.Collate && !deviceCollate) - // Enable evenDuplex or the first page of the second copy may be - // printed on the back side of the end of the first copy - doc.EvenDuplex = 1; - - if (doc.Duplex && doc.Reverse && !deviceReverse) - // Enable evenDuplex or the first page may be empty. - doc.EvenDuplex = 1; - - // change feature for software - if (deviceCollate) - doc.Collate = 0; - - if (deviceReverse) - doc.Reverse = 0; - - // - // See if we need to collate, and if so how we need to do it... - // - - if (doc.xpages == 1 && doc.ypages == 1 && - (doc.Collate || deviceCollate) && !doc.EvenDuplex) - { - // collate is not needed, disable it - deviceCollate = 0; - doc.Collate = 0; - } - - if (((doc.xpages*doc.ypages) % 2) == 0) - { - // even pages, disable EvenDuplex - doc.EvenDuplex = 0; - } - - // - // Start sending the document with any commands needed... - // - - if (out_prologue(&doc, doc.Copies * doc.xpages * doc.ypages + - (doc.EvenDuplex ? 1 : 0)) < 0) - goto out_of_memory; - - // - // Output the pages... - // - - doc.row = malloc(cfImageGetWidth(doc.img) * abs(doc.colorspace) + 3); - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: XPosition=%d, YPosition=%d, Orientation=%d", - doc.XPosition, doc.YPosition, doc.Orientation); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: xprint=%.2f, yprint=%.2f", doc.xprint, doc.yprint); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: PageLeft=%.0f, PageRight=%.0f, PageWidth=%.0f", - doc.PageLeft, doc.PageRight, doc.PageWidth); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: PageBottom=%.0f, PageTop=%.0f, PageLength=%.0f", - doc.PageBottom, doc.PageTop, doc.PageLength); - } - - if (doc.Flip) - { - pr = doc.PageWidth - doc.PageLeft; - pl = doc.PageWidth - doc.PageRight; - } - else - { - pr = doc.PageRight; - pl = doc.PageLeft; - } - - switch (doc.Orientation) - { - default : - switch (doc.XPosition) - { - case -1 : - doc.left = pl; - break; - default : - doc.left = (pr + pl - doc.xprint * 72) / 2; - break; - case 1 : - doc.left = pr - doc.xprint * 72; - break; - } - - switch (doc.YPosition) - { - case -1 : - doc.top = doc.PageBottom; - break; - default : - doc.top = (doc.PageTop + doc.PageBottom - doc.yprint * 72) / 2; - break; - case 1 : - doc.top = doc.PageTop - doc.yprint * 72;; - break; - } - break; - - case 1 : - switch (doc.XPosition) - { - case -1 : - doc.left = doc.PageBottom; - break; - default : - doc.left = (doc.PageTop + doc.PageBottom - doc.xprint * 72) / 2; - break; - case 1 : - doc.left = doc.PageTop - doc.xprint * 72; - break; - } - - switch (doc.YPosition) - { - case -1 : - doc.top = pl; - break; - default : - doc.top = (pr + pl - doc.yprint * 72) / 2; - break; - case 1 : - doc.top = pr - doc.yprint * 72;; - break; - } - break; - - case 2 : - switch (doc.XPosition) - { - case -1 : - doc.left = pr - doc.xprint * 72; - break; - default : - doc.left = (pr + pl - doc.xprint * 72) / 2; - break; - case 1 : - doc.left = pl; - break; - } - - switch (doc.YPosition) - { - case -1 : - doc.top = doc.PageTop - doc.yprint * 72; - break; - default : - doc.top = (doc.PageTop + doc.PageBottom - doc.yprint * 72) / 2; - break; - case 1 : - doc.top = doc.PageBottom; - break; - } - break; - - case 3 : - switch (doc.XPosition) - { - case -1 : - doc.left = doc.PageTop - doc.xprint * 72; - break; - default : - doc.left = (doc.PageTop + doc.PageBottom - doc.xprint * 72) / 2; - break; - case 1 : - doc.left = doc.PageBottom; - break; - } - - switch (doc.YPosition) - { - case -1 : - doc.top = pr - doc.yprint * 72;; - break; - default : - doc.top = (pr + pl - doc.yprint * 72) / 2; - break; - case 1 : - doc.top = pl; - break; - } - break; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: left=%.2f, top=%.2f", doc.left, doc.top); - - if (doc.Collate) - { - int *contentsObjs; - int *imgObjs; - - if ((contentsObjs = malloc(sizeof(int) * doc.xpages * doc.ypages)) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Can't allocate contentsObjs"); - goto out_of_memory; - } - if ((imgObjs = malloc(sizeof(int) * doc.xpages * doc.ypages)) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Can't allocate imgObjs"); - goto out_of_memory; - } - for (doc.xpage = 0; doc.xpage < doc.xpages; doc.xpage ++) - for (doc.ypage = 0; doc.ypage < doc.ypages; doc.ypage ++) - { - int imgObj; - int contentsObj; - - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Job canceled"); - goto canceled; - } - - if ((contentsObj = contentsObjs[doc.ypages * doc.xpage + doc.ypage] = - new_obj(&doc)) < 0) - goto out_of_memory; - if ((imgObj = imgObjs[doc.ypages * doc.xpage + doc.ypage] = - new_obj(&doc)) < 0) - goto out_of_memory; - - // out contents object - if (out_page_contents(&doc, contentsObj) < 0) - goto out_of_memory; - - // out image object - if (out_image(&doc, imgObj) < 0) - goto out_of_memory; - } - for (doc.page = 0; doc.Copies > 0; doc.Copies --) - { - for (doc.xpage = 0; doc.xpage < doc.xpages; doc.xpage ++) - for (doc.ypage = 0; doc.ypage < doc.ypages; doc.ypage ++, doc.page ++) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Job canceled"); - goto canceled; - } - - // out Page Object - if (out_page_object(&doc, doc.pageObjects[doc.page], - contentsObjs[doc.ypages * doc.xpage + doc.ypage], - imgObjs[doc.ypages * doc.xpage + doc.ypage]) < 0) - goto out_of_memory; - if (pdf_printer && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d\n", doc.page + 1, 1); - } - if (doc.EvenDuplex) - { - // out empty page - if (out_page_object(&doc, doc.pageObjects[doc.page], -1, -1) < 0) - goto out_of_memory; - if (pdf_printer && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d\n", doc.page + 1, 1); - } - } - free(contentsObjs); - free(imgObjs); - } - else - { - for (doc.page = 0, doc.xpage = 0; doc.xpage < doc.xpages; doc.xpage ++) - for (doc.ypage = 0; doc.ypage < doc.ypages; doc.ypage ++) - { - int imgObj; - int contentsObj; - int p; - - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Job canceled"); - goto canceled; - } - - if ((imgObj = new_obj(&doc)) < 0) - goto out_of_memory; - if ((contentsObj = new_obj(&doc)) < 0) - goto out_of_memory; - - // out contents object - if (out_page_contents(&doc, contentsObj) < 0) - goto out_of_memory; - - // out image object - if (out_image(&doc, imgObj) < 0) - goto out_of_memory; - - for (p = 0; p < doc.Copies; p ++, doc.page ++) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Job canceled"); - goto canceled; - } - - // out Page Object - if (out_page_object(&doc, doc.pageObjects[doc.page], contentsObj, - imgObj) < 0) - goto out_of_memory; - if (pdf_printer && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d\n", doc.page + 1, 1); - } - } - if (doc.EvenDuplex) - { - // out empty pages - int p; - - for (p = 0; p < doc.Copies; p++, doc.page++) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToPDF: Job canceled"); - goto canceled; - } - - if (out_page_object(&doc, doc.pageObjects[doc.page], -1, -1) < 0) - goto out_of_memory; - if (pdf_printer && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d\n", doc.page + 1, 1); - } - } - } - - canceled: - out_xref(&doc); - out_trailer(&doc); - free_all_obj(&doc); - - // - // Close files... - // - - cfImageClose(doc.img); - fclose(doc.outputfp); - close(outputfd); - return (0); - - out_of_memory: - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToPDF: Cannot allocate any more memory."); - free_all_obj(&doc); - cfImageClose(doc.img); - fclose(doc.outputfp); - close(outputfd); - return (2); -} - - -#ifdef OUT_AS_HEX -// -// 'out_hex()' - Print binary data as a series of hexadecimal numbers. -// - -static void -out_hex(imagetopdf_doc_t *doc, - cf_ib_t *data, // I - Data to print - int length, // I - Number of bytes to print - int last_line) // I - Last line of raster data? -{ - static int col = 0; // Current column - static char *hex = "0123456789ABCDEF"; - // Hex digits - - - while (length > 0) - { - // - // Put the hex chars out to the file; note that we don't use printf() - // for speed reasons... - // - - putc_pdf(doc, hex[*data >> 4]); - putc_pdf(doc, hex[*data & 15]); - - data ++; - length --; - - col += 2; - if (col > 78) - { - putc_pdf(doc, '\n'); - col = 0; - } - } - - if (last_line && col) - { - putc_pdf(doc, '\n'); - col = 0; - } -} -#else - - -#ifdef OUT_AS_ASCII85 -// -// 'out_ascii85()' - Print binary data as a series of base-85 numbers. -// - -static void -out_ascii85(imagetopdf_doc_t *doc, - cf_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[6]; // ASCII85 encoded chars - static int col = 0; // Current column - - - c[5] = '\0'; // end mark - while (length > 3) - { - b = (((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3]; - - if (b == 0) - { - putc_pdf(doc, 'z'); - col ++; - } - 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 + '!'; - - out_pdf(doc, c); - col += 5; - } - - data += 4; - length -= 4; - - if (col >= 75) - { - putc_pdf(doc, '\n'); - col = 0; - } - } - - 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 + '!'; - - c[length + 1] = '\0'; - out_pdf(doc, c); - } - - out_pdf(doc, "~>"); - col = 0; - } -} -#else - - -// -// 'out_bin()' - Print binary data as binary. -// - -static void -out_bin(imagetopdf_doc_t *doc, - cf_ib_t *data, // I - Data to print - int length, // I - Number of bytes to print - int last_line) // I - Last line of raster data? -{ - while (length > 0) - { - putc_pdf(doc, *data); - data ++; - length --; - } - - if (last_line) - { - putc_pdf(doc, '\n'); - } -} -#endif -#endif diff --git a/cupsfilters/imagetoraster.c b/cupsfilters/imagetoraster.c deleted file mode 100644 index 7c4e5be82..000000000 --- a/cupsfilters/imagetoraster.c +++ /dev/null @@ -1,4802 +0,0 @@ -// -// Image file to raster filter function for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfFilterImageToRaster() - The image conversion filter function -// blank_line() - Clear a line buffer to the blank value... -// 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/RGBW. -// 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. -// raster_cb() - Validate the page header. -// - -// -// Include necessary headers... -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// -// Types... -// - -typedef struct // **** Document information ****¨ -{ - int Flip, // Flip/mirror pages - XPosition, // Horizontal position on page - YPosition, // Vertical position on page - Collate, // Collate copies? - Copies; // Number of copies - int Orientation, // 0 = portrait, 1 = landscape, etc. - Duplex, // Duplexed? - Color; // Print in color? - float PageLeft, // Left margin - PageRight, // Right margin - PageBottom, // Bottom margin - PageTop, // Top margin - PageWidth, // Total page width - PageLength; // Total page length - cf_ib_t OnPixels[256], // On-pixel LUT - OffPixels[256]; // Off-pixel LUT - cf_logfunc_t logfunc; // Logging function, NULL for no - // logging - void *logdata; // User data for logging function, can - // be NULL -} imagetoraster_doc_t; - - -// -// Constants... -// - -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 }, - { 255, 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 } - }; - - -// -// Local functions... -// - -static void blank_line(cups_page_header2_t *header, unsigned char *row); -static void format_cmy(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void format_cmyk(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void format_K(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void format_kcmycm(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void format_kcmy(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -#define format_RGB format_cmy -static void format_rgba(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void format_w(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void format_ymc(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void format_ymck(imagetoraster_doc_t *doc, - cups_page_header2_t *header, unsigned char *row, - int y, int z, int xsize, int ysize, int yerr0, - int yerr1, cf_ib_t *r0, cf_ib_t *r1); -static void make_lut(cf_ib_t *, int, float, float); - - -// -// 'cfFilterImageToRaster()' - Filter function to convert many common image file -// formats into CUPS Raster -// - -int // O - Error status -cfFilterImageToRaster(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - imagetoraster_doc_t doc; // Document information - int i; // Looping var - cf_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; - float aspect; // Aspect ratio - 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 xc0, yc0, // Corners of the page in image coords - xc1, yc1; - cups_cspace_t cspace = -1; // CUPS color space - cups_raster_t *ras; // Raster stream - cups_page_header2_t header; // Page header - int num_options = 0;// Number of print options - cups_option_t *options = NULL;// 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 xppi, yppi; // Pixels-per-inch - int hue, sat; // Hue and saturation adjustment - cf_izoom_t *z; // Image zoom buffer - cf_iztype_t zoom_type; // Image zoom type - int primary, // Primary image colorspace - secondary; // Secondary image colorspace - cf_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 - cf_ib_t lut[256]; // Gamma/brightness LUT - int plane, // Current color plane - num_planes; // Number of color planes - char tempfile[1024]; // Name of temporary file - FILE *fp; // Input file - int fd; // File descriptor for temp file - char buf[BUFSIZ]; - int bytes; - cf_cm_calibration_t cm_calibrate; // Are we color calibrating the device? - int cm_disabled = 0;// Color management disabled? - int fillprint = 0; // print-scaling = fill - int cropfit = 0; // -o crop-to-fit - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *ipp; - int min_length = __INT32_MAX__, - min_width = __INT32_MAX__; - float customLeft = 0.0, - customBottom = 0.0, - customRight = 0.0, - customTop = 0.0; - char defSize[41]; - cf_filter_out_format_t outformat; - - // - // Note: With the CF_FILTER_OUT_FORMAT_APPLE_RASTER, - // CF_FILTER_OUT_FORMAT_PWG_RASTER, or CF_FILTER_OUT_FORMAT_PCLM - // selections the output is actually CUPS Raster but information - // about available color spaces and depths are taken from the - // urf-supported or pwg-raster-document-type-supported printer IPP - // attributes (PCLM is always sRGB 8-bit). These modes are for - // further processing with rastertopwg or pwgtopclm. This can - // change in the future when we add Apple Raster and PWG Raster - // output support to this filter. - // - - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - val = data->final_content_type; - if (val) - { - if (strcasestr(val, "pwg")) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - else if (strcasestr(val, "urf")) - outformat = CF_FILTER_OUT_FORMAT_APPLE_RASTER; - else if (strcasestr(val, "pclm")) - outformat = CF_FILTER_OUT_FORMAT_PCLM; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Final output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - (outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER ? "Apple Raster" : - "PCLm")))); - - // - // Make sure status messages are not buffered... - // - - setbuf(stderr, NULL); - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - if (printer_attrs != NULL) - { - int minw, minl, maxw, maxl; - int left, bottom, right, top; - cfGenerateSizes(printer_attrs, CF_GEN_SIZES_DEFAULT, NULL, &ipp, - NULL, NULL, NULL, NULL, NULL, NULL, - &minw, &minl, &maxw, &maxl, - &left, &bottom, &right, &top, - defSize, NULL); - min_width = minw * 72 / 2540; - min_length = minl * 72 / 2540; - customLeft = left * 72.0 / 2540.0; - customBottom = bottom * 72.0 / 2540.0; - customRight = right * 72.0 / 2540.0; - customTop = top * 72.0 / 2540.0; - } - - // - // Initialize data structure - // - - doc.Flip = 0; - doc.XPosition = 0; - doc.YPosition = 0; - doc.Collate = 0; - doc.Copies = 1; - doc.logfunc = data->logfunc; - doc.logdata = data->logdata; - - // - // Option list... - // - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - // - // Open the input data stream specified by the inputfd ... - // - - if ((fp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Unable to open input data stream."); - } - - return (1); - } - - // - // Copy input into temporary file if needed ... - // - - if (!inputseekable) - { - if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToRaster: Unable to copy input: %s", - strerror(errno)); - fclose(fp); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0) - bytes = write(fd, buf, bytes); - - fclose(fp); - close(fd); - - // - // Open the temporary file to read it instead of the original input ... - // - - if ((fp = fopen(tempfile, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Unable to open temporary file."); - } - - unlink(tempfile); - return (1); - } - } - - // - // Process options and write the prolog... - // - - zoom = 1.0; - xppi = 0; - yppi = 0; - hue = 0; - sat = 100; - g = 1.0; - b = 1.0; - - doc.Copies = data->copies; - - // - // Process job options... - // - - cfRasterPrepareHeader(&header, data, outformat, - CF_FILTER_OUT_FORMAT_CUPS_RASTER, 1, &cspace); - doc.Orientation = header.Orientation; - doc.Duplex = header.Duplex; - doc.Color = header.cupsNumColors>1?1:0; - doc.PageLeft = header.cupsImagingBBox[0] != 0.0 ? - header.cupsImagingBBox[0] : - (float)header.ImagingBoundingBox[0]; - doc.PageRight = header.cupsImagingBBox[2] != 0.0 ? - header.cupsImagingBBox[2] : - (float)header.ImagingBoundingBox[2]; - doc.PageTop = header.cupsImagingBBox[3] != 0.0 ? - header.cupsImagingBBox[3] : - (float)header.ImagingBoundingBox[3]; - doc.PageBottom = header.cupsImagingBBox[1] != 0.0 ? - header.cupsImagingBBox[1] : - (float)header.ImagingBoundingBox[1]; - doc.PageWidth = header.cupsPageSize[0] != 0.0 ? - header.cupsPageSize[0] : - (float)header.PageSize[0]; - doc.PageLength = header.cupsPageSize[1] != 0.0 ? - header.cupsPageSize[1] : - (float)header.PageSize[1]; - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.color = %d", doc.Color); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.Orientation = %d", doc.Orientation); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.Duplex = %d", doc.Duplex); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.PageWidth = %.1f", doc.PageWidth); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.PageLength = %.1f", doc.PageLength); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.PageLeft = %.1f", doc.PageLeft); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.PageRight = %.1f", doc.PageRight); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.PageTop = %.1f", doc.PageTop); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: doc.PageBottom = %.1f", doc.PageBottom); - } - - // Find print-rendering-intent - - header.cupsRenderingIntent[0] = '\0'; - cfGetPrintRenderIntent(data, header.cupsRenderingIntent, - sizeof(header.cupsRenderingIntent)); - if(log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Print rendering intent = %s", - header.cupsRenderingIntent); - - 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. - // - - doc.Collate = strcasecmp(val, "separate-documents-collated-copies") != 0; - } - - if ((val = cupsGetOption("Collate", num_options, options)) != NULL && - strcasecmp(val, "True") == 0) - doc.Collate = 1; - - if ((val = cupsGetOption("gamma", num_options, options)) != NULL) - { - // - // Get gamma value from 1 to 10000... - // - - g = atoi(val) * 0.001f; - - if (g < 0.001f) - g = 0.001f; - else if (g > 10.0f) - g = 10.0f; - } - - if ((val = cupsGetOption("brightness", num_options, options)) != NULL) - { - // - // Get brightness value from 10 to 1000. - // - - b = atoi(val) * 0.01f; - - if (b < 0.1f) - b = 0.1f; - else if (b > 10.0f) - b = 10.0f; - } - - if ((val = cupsGetOption("ppi", num_options, options)) != NULL) - { - sscanf(val, "%d", &xppi); - yppi = xppi; - zoom = 0.0; - } - - if ((val = cupsGetOption("position", num_options, options)) != NULL) - { - if (strcasecmp(val, "center") == 0) - { - doc.XPosition = 0; - doc.YPosition = 0; - } - else if (strcasecmp(val, "top") == 0) - { - doc.XPosition = 0; - doc.YPosition = 1; - } - else if (strcasecmp(val, "left") == 0) - { - doc.XPosition = -1; - doc.YPosition = 0; - } - else if (strcasecmp(val, "right") == 0) - { - doc.XPosition = 1; - doc.YPosition = 0; - } - else if (strcasecmp(val, "top-left") == 0) - { - doc.XPosition = -1; - doc.YPosition = 1; - } - else if (strcasecmp(val, "top-right") == 0) - { - doc.XPosition = 1; - doc.YPosition = 1; - } - else if (strcasecmp(val, "bottom") == 0) - { - doc.XPosition = 0; - doc.YPosition = -1; - } - else if (strcasecmp(val, "bottom-left") == 0) - { - doc.XPosition = -1; - doc.YPosition = -1; - } - else if (strcasecmp(val, "bottom-right") == 0) - { - doc.XPosition = 1; - doc.YPosition = -1; - } - } - - if ((val = cupsGetOption("saturation", num_options, options)) != NULL) - sat = atoi(val); - - if ((val = cupsGetOption("hue", num_options, options)) != NULL) - hue = atoi(val); - - if ((val = cupsGetOption("MirrorPrint", num_options, options)) != NULL || - (val = cupsGetOption("mirror-print", num_options, options)) != NULL || - (val = cupsGetOption("mirror", num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "mirror-print", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "mirror-print-default", - IPP_TAG_ZERO)) != NULL) - { - if (val == NULL && ipp != NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - } - - if (val && (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes"))) - doc.Flip = 1; - - // support the "cm-calibration" option - cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (cm_calibrate == CF_CM_CALIBRATION_ENABLED) - cm_disabled = 1; - else - cm_disabled = cfCmIsPrinterCmDisabled(data); - - // - // Choose the appropriate colorspace... - // - - switch (header.cupsColorSpace) - { - case CUPS_CSPACE_W : - case CUPS_CSPACE_SW : - if (header.cupsBitsPerColor >= 8) - { - primary = CF_IMAGE_WHITE; - secondary = CF_IMAGE_WHITE; - } - else - { - primary = CF_IMAGE_BLACK; - secondary = CF_IMAGE_BLACK; - } - break; - - default : - case CUPS_CSPACE_RGB : - case CUPS_CSPACE_RGBA : - case CUPS_CSPACE_RGBW : - case CUPS_CSPACE_SRGB : - case CUPS_CSPACE_ADOBERGB : - if (header.cupsBitsPerColor >= 8) - { - primary = CF_IMAGE_RGB; - secondary = CF_IMAGE_RGB; - } - else - { - primary = CF_IMAGE_CMY; - secondary = CF_IMAGE_CMY; - } - break; - - case CUPS_CSPACE_K : - case CUPS_CSPACE_WHITE : - case CUPS_CSPACE_GOLD : - case CUPS_CSPACE_SILVER : - primary = CF_IMAGE_BLACK; - secondary = CF_IMAGE_BLACK; - break; - - case CUPS_CSPACE_CMYK : - case CUPS_CSPACE_YMCK : - case CUPS_CSPACE_KCMY : - case CUPS_CSPACE_KCMYcm : - case CUPS_CSPACE_GMCK : - case CUPS_CSPACE_GMCS : - if (header.cupsBitsPerColor == 1) - { - primary = CF_IMAGE_CMY; - secondary = CF_IMAGE_CMY; - } - else - { - primary = CF_IMAGE_CMYK; - secondary = CF_IMAGE_CMYK; - } - break; - - case CUPS_CSPACE_CMY : - case CUPS_CSPACE_YMC : - primary = CF_IMAGE_CMY; - secondary = CF_IMAGE_CMY; - break; - - case CUPS_CSPACE_CIEXYZ : - case CUPS_CSPACE_CIELab : - case CUPS_CSPACE_ICC1 : - case CUPS_CSPACE_ICC2 : - case CUPS_CSPACE_ICC3 : - case CUPS_CSPACE_ICC4 : - case CUPS_CSPACE_ICC5 : - case CUPS_CSPACE_ICC6 : - case CUPS_CSPACE_ICC7 : - case CUPS_CSPACE_ICC8 : - case CUPS_CSPACE_ICC9 : - case CUPS_CSPACE_ICCA : - case CUPS_CSPACE_ICCB : - case CUPS_CSPACE_ICCC : - case CUPS_CSPACE_ICCD : - case CUPS_CSPACE_ICCE : - case CUPS_CSPACE_ICCF : - case CUPS_CSPACE_DEVICE1 : - case CUPS_CSPACE_DEVICE2 : - case CUPS_CSPACE_DEVICE3 : - case CUPS_CSPACE_DEVICE4 : - case CUPS_CSPACE_DEVICE5 : - case CUPS_CSPACE_DEVICE6 : - case CUPS_CSPACE_DEVICE7 : - case CUPS_CSPACE_DEVICE8 : - case CUPS_CSPACE_DEVICE9 : - case CUPS_CSPACE_DEVICEA : - case CUPS_CSPACE_DEVICEB : - case CUPS_CSPACE_DEVICEC : - case CUPS_CSPACE_DEVICED : - case CUPS_CSPACE_DEVICEE : - case CUPS_CSPACE_DEVICEF : - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Colorspace %d not supported.", - header.cupsColorSpace); - if (!inputseekable) - unlink(tempfile); - return(1); - break; - } - - // - // Apply a color profile... - // - - if ((val = cupsGetOption("profile", num_options, options)) != NULL && - !cm_disabled) - { - float density; // Ink density to use - float gamma; // Gamma correction to use - float matrix[3][3]; // Transform matrix - - sscanf(val, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", - &(density), &(gamma), - matrix[0] + 0, matrix[0] + 1, matrix[0] + 2, - matrix[1] + 0, matrix[1] + 1, matrix[1] + 2, - matrix[2] + 0, matrix[2] + 1, matrix[2] + 2); - - density *= 0.001f; - gamma *= 0.001f; - - matrix[0][0] *= 0.001f; - matrix[0][1] *= 0.001f; - matrix[0][2] *= 0.001f; - matrix[1][0] *= 0.001f; - matrix[1][1] *= 0.001f; - matrix[1][2] *= 0.001f; - matrix[2][0] *= 0.001f; - matrix[2][1] *= 0.001f; - matrix[2][2] *= 0.001f; - - cfImageSetProfile(density, gamma, matrix); - } - - cfImageSetRasterColorSpace(header.cupsColorSpace); - - // - // Create a gamma/brightness LUT... - // - - make_lut(lut, primary, g, b); - - // - // Open the input image to print... - // - - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterImageToRaster: Loading print file."); - - if (header.cupsColorSpace == CUPS_CSPACE_CIEXYZ || - header.cupsColorSpace == CUPS_CSPACE_CIELab || - header.cupsColorSpace >= CUPS_CSPACE_ICC1) - img = cfImageOpenFP(fp, primary, secondary, sat, hue, NULL); - else - img = cfImageOpenFP(fp, primary, secondary, sat, hue, lut); - - if (img != NULL) - { - int margin_defined = 0; - int fidelity = 0; - int document_large = 0; - - if (customLeft != 0 || customRight != 0 || - customBottom != 0 || customTop != 0 || - doc.PageLength != doc.PageTop - doc.PageBottom || - doc.PageWidth != doc.PageRight - doc.PageLeft) - margin_defined = 1; - - if ((val = cupsGetOption("ipp-attribute-fidelity", - num_options, options)) != NULL) - { - if(!strcasecmp(val, "true") || !strcasecmp(val, "yes")|| - !strcasecmp(val, "on")) - fidelity = 1; - } - - float w = (float)cfImageGetWidth(img); - float h = (float)cfImageGetHeight(img); - float pw = doc.PageRight - doc.PageLeft; - float ph = doc.PageTop - doc.PageBottom; - int tempOrientation = doc.Orientation; - if ((val = cupsGetOption("orientation-requested", - num_options, options)) != NULL) - tempOrientation = atoi(val); - else if ((val = cupsGetOption("landscape", - num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - tempOrientation = 4; - } - if (tempOrientation == 0) - { - if (((pw > ph) && (w < h)) || ((pw < ph) && (w > h))) - tempOrientation = 4; - } - if (tempOrientation == 4 || tempOrientation == 5) - { - int tmp = pw; - pw = ph; - ph = tmp; - } - if (w * 72.0 / img->xppi > pw || h * 72.0 / img->yppi > ph) - document_large = 1; - - if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) - { - if (!strcasecmp(val, "auto")) - { - if (fidelity || document_large) - { - if (margin_defined) - zoom = 1.0; // fit method - else - fillprint = 1; // fill method - } - else - cropfit = 1; // none method - } - else if (!strcasecmp(val, "auto-fit")) - { - if (fidelity || document_large) - zoom = 1.0; // fit method - else - cropfit = 1; // none method - } - else if (!strcasecmp(val, "fill")) - fillprint = 1; // fill method - else if(!strcasecmp(val, "fit")) - zoom = 1.0; // fitplot = 1 or fit method - else - cropfit = 1; // none or crop-to-fit - } - else // print-scaling is not defined, look for alternate options. - { - if ((val = cupsGetOption("scaling", num_options, options)) != NULL) - zoom = atoi(val) * 0.01; - else if (((val = - cupsGetOption("fit-to-page", num_options, options)) != NULL) || - ((val = cupsGetOption("fitplot", num_options, options)) != NULL)) - { - if (!strcasecmp(val, "yes") || !strcasecmp(val, "on") || - !strcasecmp(val, "true")) - zoom = 1.0; - else - zoom = 0.0; - } - else if ((val = cupsGetOption("natural-scaling", num_options, options)) - != NULL) - zoom = 0.0; - - if ((val = cupsGetOption("fill", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - fillprint = 1; - } - if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - cropfit = 1; - } - } - } - - if (img != NULL) - { - if (fillprint || cropfit) - { - float w = (float)cfImageGetWidth(img); - float h = (float)cfImageGetHeight(img); - // For cropfit do the math without the unprintable margins to get correct - // centering, for fillprint, fill the printable area - float pw = (cropfit ? doc.PageWidth : doc.PageRight - doc.PageLeft); - float ph = (cropfit ? doc.PageLength : doc.PageTop - doc.PageBottom); - const char *val; - int tempOrientation = doc.Orientation; - int flag = 3; - if ((val = cupsGetOption("orientation-requested", - num_options, options)) != NULL) - tempOrientation = atoi(val); - else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if(!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - tempOrientation = 4; - } - if (tempOrientation > 0) - { - if (tempOrientation == 4 || tempOrientation == 5) - { - float temp = pw; - pw = ph; - ph = temp; - flag = 4; - } - } - if (tempOrientation == 0) - { - if (((pw > ph) && (w < h)) || ((pw < ph) && (w > h))) - { - int temp = pw; - pw = ph; - ph = temp; - flag = 4; - } - } - if (fillprint) - { - // Final width and height of cropped image. - float final_w, final_h; - if(w * ph / pw <= h) - { - final_w = w; - final_h = w * ph / pw; - } - else - { - final_w = h * pw / ph; - final_h = h; - } - // posw and posh are position of the cropped image along width and - // height. - float posw = (w - final_w) / 2, posh = (h - final_h) / 2; - posw = (1 + doc.XPosition) * posw; - posh = (1 - doc.YPosition) * posh; - cf_image_t *img2 = cfImageCrop(img, posw, posh, final_w, final_h); - cfImageClose(img); - img = img2; - } - else - { - float final_w = w, final_h = h; - if (w > pw * img->xppi / 72.0) - final_w = pw * img->xppi / 72.0; - if (h > ph * img->yppi / 72.0) - final_h = ph * img->yppi / 72.0; - float posw = (w - final_w) / 2, posh = (h - final_h) / 2; - posw = (1 + doc.XPosition) * posw; - posh = (1 - doc.YPosition) * posh; - // Check whether the unprintable margins hide away a part of the image, - // if so, correct the image cut - if (flag == 4) - { - float margin, cutoff; - margin = (doc.PageLength - final_w * 72.0 / img->xppi) / 2; - if (margin >= doc.PageBottom) - doc.PageBottom = margin; - else - { - cutoff = (doc.PageBottom - margin) * img->xppi / 72.0; - final_w -= cutoff; - posw += cutoff; - } - margin = doc.PageBottom + final_w * 72.0 / img->xppi; - if (margin <= doc.PageTop) - doc.PageTop = margin; - else - final_w -= (margin - doc.PageTop) * img->xppi / 72.0; - margin = (doc.PageWidth - final_h * 72.0 / img->yppi) / 2; - if (margin >= doc.PageLeft) - doc.PageLeft = margin; - else - { - cutoff = (doc.PageLeft - margin) * img->yppi / 72.0; - final_h -= cutoff; - posh += cutoff; - } - margin = doc.PageLeft + final_h * 72.0 / img->yppi; - if (margin <= doc.PageRight) - doc.PageRight = margin; - else - final_h -= (margin - doc.PageRight) * img->yppi / 72.0; - } - else - { - float margin, cutoff; - margin = (doc.PageLength - final_h * 72.0 / img->yppi) / 2; - if (margin >= doc.PageBottom) - doc.PageBottom = margin; - else - { - cutoff = (doc.PageBottom - margin) * img->yppi / 72.0; - final_h -= cutoff; - posh += cutoff; - } - margin = doc.PageBottom + final_h * 72.0 / img->yppi; - if (margin <= doc.PageTop) - doc.PageTop = margin; - else - final_h -= (margin - doc.PageTop) * img->yppi / 72.0; - margin = (doc.PageWidth - final_w * 72.0 / img->xppi) / 2; - if (margin >= doc.PageLeft) - doc.PageLeft = margin; - else - { - cutoff = (doc.PageLeft - margin) * img->xppi / 72.0; - final_w -= cutoff; - posw += cutoff; - } - margin = doc.PageLeft + final_w * 72.0 / img->xppi; - if (margin <= doc.PageRight) - doc.PageRight = margin; - else - final_w -= (margin - doc.PageRight) * img->xppi / 72.0; - } - if (doc.PageBottom<0) doc.PageBottom = 0; - if (doc.PageLeft<0) doc.PageLeft = 0; - cf_image_t *img2 = cfImageCrop(img, posw, posh, final_w, final_h); - cfImageClose(img); - img = img2; - } - } - } - - if (!inputseekable) - unlink(tempfile); - - if (img == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToRaster: The print file could not be opened."); - return (1); - } - - // - // Scale as necessary... - // - - if (zoom == 0.0 && xppi == 0) - { - xppi = img->xppi; - yppi = img->yppi; - } - - if (yppi == 0) - yppi = xppi; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Before scaling: xppi=%d, yppi=%d, zoom=%.2f", - xppi, yppi, zoom); - - if (xppi > 0) - { - // - // Scale the image as neccesary to match the desired pixels-per-inch. - // - - if (doc.Orientation & 1) - { - xprint = (doc.PageTop - doc.PageBottom) / 72.0; - yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - xprint = (doc.PageRight - doc.PageLeft) / 72.0; - yprint = (doc.PageTop - doc.PageBottom) / 72.0; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Before scaling: xprint=%.1f, yprint=%.1f", - xprint, yprint); - - xinches = (float)img->xsize / (float)xppi; - yinches = (float)img->ysize / (float)yppi; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Image size is %.1f x %.1f inches...", - xinches, yinches); - - if ((val = cupsGetOption("natural-scaling", num_options, options)) != NULL) - { - xinches = xinches * atoi(val) / 100; - yinches = yinches * atoi(val) / 100; - } - - if (cupsGetOption("orientation-requested", num_options, options) == NULL && - cupsGetOption("landscape", num_options, options) == NULL) - { - // - // Rotate the image if it will fit landscape but not portrait... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Auto orientation..."); - - if ((xinches > xprint || yinches > yprint) && - xinches <= yprint && yinches <= xprint) - { - // - // Rotate the image as needed... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Using landscape orientation..."); - - doc.Orientation = (doc.Orientation + 1) & 3; - xsize = yprint; - yprint = xprint; - xprint = xsize; - } - } - } - else - { - // - // Scale percentage of page size... - // - - xprint = (doc.PageRight - doc.PageLeft) / 72.0; - yprint = (doc.PageTop - doc.PageBottom) / 72.0; - aspect = (float)img->yppi / (float)img->xppi; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Before scaling: xprint=%.1f, yprint=%.1f", - xprint, yprint); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: img->xppi = %d, img->yppi = %d, aspect = %f", - img->xppi, img->yppi, aspect); - - xsize = xprint * zoom; - ysize = xsize * img->ysize / img->xsize / aspect; - - if (ysize > (yprint * zoom)) - { - ysize = yprint * zoom; - xsize = ysize * img->xsize * aspect / img->ysize; - } - - xsize2 = yprint * zoom; - ysize2 = xsize2 * img->ysize / img->xsize / aspect; - - if (ysize2 > (xprint * zoom)) - { - ysize2 = xprint * zoom; - xsize2 = ysize2 * img->xsize * aspect / img->ysize; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Portrait size is %.2f x %.2f inches", - xsize, ysize); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Landscape size is %.2f x %.2f inches", - xsize2, ysize2); - - if (cupsGetOption("orientation-requested", num_options, options) == NULL && - cupsGetOption("landscape", num_options, options) == NULL) - { - // - // Choose the rotation with the largest area, but prefer - // portrait if they are equal... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Auto orientation..."); - - if ((xsize * ysize) < (xsize2 * ysize2)) - { - // - // Do landscape orientation... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Using landscape orientation..."); - - doc.Orientation = 1; - xinches = xsize2; - yinches = ysize2; - xprint = (doc.PageTop - doc.PageBottom) / 72.0; - yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - // - // Do portrait orientation... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Using portrait orientation..."); - - doc.Orientation = 0; - xinches = xsize; - yinches = ysize; - } - } - else if (doc.Orientation & 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Using landscape orientation..."); - - xinches = xsize2; - yinches = ysize2; - xprint = (doc.PageTop - doc.PageBottom) / 72.0; - yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Using portrait orientation..."); - - xinches = xsize; - yinches = ysize; - xprint = (doc.PageRight - doc.PageLeft) / 72.0; - yprint = (doc.PageTop - doc.PageBottom) / 72.0; - } - } - - // - // Compute the number of pages to print and the size of the image on each - // page... - // - - xpages = ceil(xinches / xprint); - ypages = ceil(yinches / yprint); - - xprint = xinches / xpages; - yprint = yinches / ypages; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: xpages = %dx%.2fin, ypages = %dx%.2fin", - xpages, xprint, ypages, yprint); - - // - // Compute the bitmap size... - // - - // If size if specified by user, use it, else default size from - // printer_attrs - strcpy(defSize, header.cupsPageSizeName); - - if ((strncasecmp(defSize, "Custom", 6)) == 0 || - strcasestr(defSize, "_custom_")) - { - float width, // New width in points - length; // New length in points - - - // - // Use the correct width and length for the current orientation... - // - - if (doc.Orientation & 1) - { - width = yprint * 72.0; - length = xprint * 72.0; - } - else - { - width = xprint * 72.0; - length = yprint * 72.0; - } - - // - // Add margins to page size... - // - - width += customLeft + customRight; - length += customBottom + customTop; - - // - // Enforce minimums... - // - - if (width < min_width) - width = min_width; - if(length < min_length) - length = min_length; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Updated custom page size to %.2f x %.2f " - "inches...", - width / 72.0, length / 72.0); - - // - // Set the new custom size... - // - - strcpy(header.cupsPageSizeName, "Custom"); - - header.cupsPageSize[0] = width + 0.5; - header.cupsPageSize[1] = length + 0.5; - header.PageSize[0] = width + 0.5; - header.PageSize[1] = length + 0.5; - - // - // Update page variables... - // - - doc.PageWidth = width; - doc.PageLength = length; - doc.PageLeft = customLeft; - doc.PageRight = width - customRight; - doc.PageBottom = customBottom; - doc.PageTop = length - customTop; - - // - // Remove margins from page size... - // - - width -= customLeft + customRight; - length -= customTop + customBottom; - - // - // Set the bitmap size... - // - - header.cupsWidth = width * header.HWResolution[0] / 72.0; - header.cupsHeight = length * header.HWResolution[1] / 72.0; - - } - else - { - // - // Set the bitmap size... - // - - header.cupsWidth = (doc.Orientation & 1 ? yprint : xprint) * - header.HWResolution[0]; - header.cupsHeight = (doc.Orientation & 1 ? xprint : yprint) * - header.HWResolution[1]; - } - header.cupsBytesPerLine = (header.cupsBitsPerPixel * - header.cupsWidth + 7) / 8; - - if (header.cupsColorOrder == CUPS_ORDER_BANDED) - header.cupsBytesPerLine *= header.cupsNumColors; - - header.Margins[0] = doc.PageLeft; - header.Margins[1] = doc.PageBottom; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: PageSize = [%d %d]", header.PageSize[0], - header.PageSize[1]); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: PageLeft = %f, PageRight = %f, " - "PageBottom = %f, PageTop = %f", - doc.PageLeft, doc.PageRight, doc.PageBottom, doc.PageTop); - - switch (doc.Orientation) - { - default : - switch (doc.XPosition) - { - case -1 : - header.cupsImagingBBox[0] = doc.PageLeft; - header.cupsImagingBBox[2] = doc.PageLeft + xprint * 72; - break; - default : - header.cupsImagingBBox[0] = (doc.PageRight + doc.PageLeft - - xprint * 72) / 2; - header.cupsImagingBBox[2] = (doc.PageRight + doc.PageLeft + - xprint * 72) / 2; - break; - case 1 : - header.cupsImagingBBox[0] = doc.PageRight - xprint * 72; - header.cupsImagingBBox[2] = doc.PageRight; - break; - } - - switch (doc.YPosition) - { - case -1 : - header.cupsImagingBBox[1] = doc.PageBottom; - header.cupsImagingBBox[3] = doc.PageBottom + yprint * 72; - break; - default : - header.cupsImagingBBox[1] = (doc.PageTop + doc.PageBottom - - yprint * 72) / 2; - header.cupsImagingBBox[3] = (doc.PageTop + doc.PageBottom + - yprint * 72) / 2; - break; - case 1 : - header.cupsImagingBBox[1] = doc.PageTop - yprint * 72; - header.cupsImagingBBox[3] = doc.PageTop; - break; - } - break; - - case 1 : - switch (doc.XPosition) - { - case -1 : - header.cupsImagingBBox[0] = doc.PageLeft; - header.cupsImagingBBox[2] = doc.PageLeft + yprint * 72; - break; - default : - header.cupsImagingBBox[0] = (doc.PageRight + doc.PageLeft - - yprint * 72) / 2; - header.cupsImagingBBox[2] = (doc.PageRight + doc.PageLeft + - yprint * 72) / 2; - break; - case 1 : - header.cupsImagingBBox[0] = doc.PageRight - yprint * 72; - header.cupsImagingBBox[2] = doc.PageRight; - break; - } - - switch (doc.YPosition) - { - case -1 : - header.cupsImagingBBox[1] = doc.PageBottom; - header.cupsImagingBBox[3] = doc.PageBottom + xprint * 72; - break; - default : - header.cupsImagingBBox[1] = (doc.PageTop + doc.PageBottom - - xprint * 72) / 2; - header.cupsImagingBBox[3] = (doc.PageTop + doc.PageBottom + - xprint * 72) / 2; - break; - case 1 : - header.cupsImagingBBox[1] = doc.PageTop - xprint * 72; - header.cupsImagingBBox[3] = doc.PageTop; - break; - } - break; - - case 2 : - switch (doc.XPosition) - { - case 1 : - header.cupsImagingBBox[0] = doc.PageLeft; - header.cupsImagingBBox[2] = doc.PageLeft + xprint * 72; - break; - default : - header.cupsImagingBBox[0] = (doc.PageRight + doc.PageLeft - - xprint * 72) / 2; - header.cupsImagingBBox[2] = (doc.PageRight + doc.PageLeft + - xprint * 72) / 2; - break; - case -1 : - header.cupsImagingBBox[0] = doc.PageRight - xprint * 72; - header.cupsImagingBBox[2] = doc.PageRight; - break; - } - - switch (doc.YPosition) - { - case 1 : - header.cupsImagingBBox[1] = doc.PageBottom; - header.cupsImagingBBox[3] = doc.PageBottom + yprint * 72; - break; - default : - header.cupsImagingBBox[1] = (doc.PageTop + doc.PageBottom - - yprint * 72) / 2; - header.cupsImagingBBox[3] = (doc.PageTop + doc.PageBottom + - yprint * 72) / 2; - break; - case -1 : - header.cupsImagingBBox[1] = doc.PageTop - yprint * 72; - header.cupsImagingBBox[3] = doc.PageTop; - break; - } - break; - - case 3 : - switch (doc.XPosition) - { - case 1 : - header.cupsImagingBBox[0] = doc.PageLeft; - header.cupsImagingBBox[2] = doc.PageLeft + yprint * 72; - break; - default : - header.cupsImagingBBox[0] = (doc.PageRight + doc.PageLeft - - yprint * 72) / 2; - header.cupsImagingBBox[2] = (doc.PageRight + doc.PageLeft + - yprint * 72) / 2; - break; - case -1 : - header.cupsImagingBBox[0] = doc.PageRight - yprint * 72; - header.cupsImagingBBox[2] = doc.PageRight; - break; - } - - switch (doc.YPosition) - { - case 1 : - header.cupsImagingBBox[1] = doc.PageBottom; - header.cupsImagingBBox[3] = doc.PageBottom + xprint * 72; - break; - default : - header.cupsImagingBBox[1] = (doc.PageTop + doc.PageBottom - - xprint * 72) / 2; - header.cupsImagingBBox[3] = (doc.PageTop + doc.PageBottom + - xprint * 72) / 2; - break; - case -1 : - header.cupsImagingBBox[1] = doc.PageTop - xprint * 72; - header.cupsImagingBBox[3] = doc.PageTop; - break; - } - break; - } - - header.ImagingBoundingBox[0] = header.cupsImagingBBox[0]; - header.ImagingBoundingBox[1] = header.cupsImagingBBox[1]; - header.ImagingBoundingBox[2] = header.cupsImagingBBox[2]; - header.ImagingBoundingBox[3] = header.cupsImagingBBox[3]; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Orientation: %d, XPosition: %d, YPosition: %d, " - "ImagingBoundingBox = [%d %d %d %d]", - doc.Orientation, doc.XPosition, doc.YPosition, - header.ImagingBoundingBox[0], header.ImagingBoundingBox[1], - header.ImagingBoundingBox[2], header.ImagingBoundingBox[3]); - - if (header.cupsColorOrder == CUPS_ORDER_PLANAR) - num_planes = header.cupsNumColors; - else - num_planes = 1; - - if (header.cupsBitsPerColor >= 8) - zoom_type = CF_IZOOM_NORMAL; - else - zoom_type = CF_IZOOM_FAST; - - // - // See if we need to collate, and if so how we need to do it... - // - - if (xpages == 1 && ypages == 1) - doc.Collate = 0; - - // We should also check printer-attrs here, but printer-attrs shows - // hardware copies ("copies-supported = 1-99") and hardware collate - // ("multiple-document-handling-supported = - // separate-documents-uncollated-copies, - // separate-documents-collated-copies") even on cheapest raster - // printers - slowcollate = doc.Collate && - ((val = cupsGetOption("hardware-collate", num_options, options)) == NULL || - !strcasecmp(val, "false") || !strcasecmp(val, "off") || - !strcasecmp(val, "no")); - slowcopies = - ((val = cupsGetOption("hardware-copies", num_options, options)) == NULL || - !strcasecmp(val, "false") || !strcasecmp(val, "off") || - !strcasecmp(val, "no")); - - if (doc.Copies > 1 && !slowcollate && !slowcopies) - { - header.Collate = (cups_bool_t)doc.Collate; - header.NumCopies = doc.Copies; - - doc.Copies = 1; - } - else - header.NumCopies = 1; - - // - // Create the dithering lookup tables... - // - - doc.OnPixels[0] = 0x00; - doc.OnPixels[255] = 0xff; - doc.OffPixels[0] = 0x00; - doc.OffPixels[255] = 0xff; - - switch (header.cupsBitsPerColor) - { - case 2 : - for (i = 1; i < 255; i ++) - { - doc.OnPixels[i] = 0x55 * (i / 85 + 1); - doc.OffPixels[i] = 0x55 * (i / 64); - } - break; - case 4 : - for (i = 1; i < 255; i ++) - { - doc.OnPixels[i] = 17 * (i / 17 + 1); - doc.OffPixels[i] = 17 * (i / 16); - } - break; - } - - // - // Output the pages... - // - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: cupsWidth = %d", header.cupsWidth); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: cupsHeight = %d", header.cupsHeight); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: cupsBitsPerColor = %d", header.cupsBitsPerColor); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: cupsBitsPerPixel = %d", header.cupsBitsPerPixel); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: cupsBytesPerLine = %d", header.cupsBytesPerLine); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: cupsColorOrder = %d", header.cupsColorOrder); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: cupsColorSpace = %d", header.cupsColorSpace); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: img->colorspace = %d", img->colorspace); - } - - row = malloc(2 * header.cupsBytesPerLine); - ras = cupsRasterOpen(outputfd, CUPS_RASTER_WRITE); - - for (i = 0, page = 1; i < doc.Copies; i ++) - for (xpage = 0; xpage < xpages; xpage ++) - for (ypage = 0; ypage < ypages; ypage ++, page ++) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Job canceled"); - goto canceled; - } - - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterImageToRaster: Formatting page %d.", page); - - if (doc.Orientation & 1) - { - xc0 = img->xsize * ypage / ypages; - xc1 = img->xsize * (ypage + 1) / ypages - 1; - yc0 = img->ysize * xpage / xpages; - yc1 = img->ysize * (xpage + 1) / xpages - 1; - - xtemp = header.HWResolution[0] * yprint; - ytemp = header.HWResolution[1] * xprint; - } - else - { - xc0 = img->xsize * xpage / xpages; - xc1 = img->xsize * (xpage + 1) / xpages - 1; - yc0 = img->ysize * ypage / ypages; - yc1 = img->ysize * (ypage + 1) / ypages - 1; - - xtemp = header.HWResolution[0] * xprint; - ytemp = header.HWResolution[1] * yprint; - } - - cupsRasterWriteHeader2(ras, &header); - - for (plane = 0; plane < num_planes; plane ++) - { - // - // Initialize the image "zoom" engine... - // - - if (doc.Flip) - z = _cfImageZoomNew(img, xc0, yc0, xc1, yc1, -xtemp, ytemp, - doc.Orientation & 1, zoom_type); - else - z = _cfImageZoomNew(img, xc0, yc0, xc1, yc1, xtemp, ytemp, - doc.Orientation & 1, zoom_type); - - // - // Write leading blank space as needed... - // - - if (header.cupsHeight > z->ysize && doc.YPosition <= 0) - { - blank_line(&header, row); - - y = header.cupsHeight - z->ysize; - if (doc.YPosition == 0) - y /= 2; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Writing %d leading blank lines...", - y); - - for (; y > 0; y --) - { - if (cupsRasterWritePixels(ras, row, header.cupsBytesPerLine) < - header.cupsBytesPerLine) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToRaster: Unable to send raster data."); - cfImageClose(img); - return (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 (zoom_type != CF_IZOOM_FAST && (iy - last_iy) > 1) - _cfImageZoomFill(z, iy); - - _cfImageZoomFill(z, iy + z->yincr); - - last_iy = iy; - } - - // - // Format this line of raster data for the printer... - // - - blank_line(&header, row); - - r0 = z->rows[z->row]; - r1 = z->rows[1 - z->row]; - - switch (header.cupsColorSpace) - { - case CUPS_CSPACE_W : - case CUPS_CSPACE_SW : - format_w(&doc, &header, row, y, plane, z->xsize, z->ysize, - yerr0, yerr1, r0, r1); - break; - default : - case CUPS_CSPACE_RGB : - case CUPS_CSPACE_SRGB : - case CUPS_CSPACE_ADOBERGB : - format_RGB(&doc, &header, row, y, plane, z->xsize, z->ysize, - yerr0, yerr1, r0, r1); - break; - case CUPS_CSPACE_RGBA : - case CUPS_CSPACE_RGBW : - format_rgba(&doc, &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(&doc, &header, row, y, plane, z->xsize, z->ysize, - yerr0, yerr1, r0, r1); - break; - case CUPS_CSPACE_CMY : - format_cmy(&doc, &header, row, y, plane, z->xsize, z->ysize, - yerr0, yerr1, r0, r1); - break; - case CUPS_CSPACE_YMC : - format_ymc(&doc, &header, row, y, plane, z->xsize, z->ysize, - yerr0, yerr1, r0, r1); - break; - case CUPS_CSPACE_CMYK : - format_cmyk(&doc, &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(&doc, &header, row, y, plane, z->xsize, z->ysize, - yerr0, yerr1, r0, r1); - break; - case CUPS_CSPACE_KCMYcm : - if (header.cupsBitsPerColor == 1) - { - format_kcmycm(&doc, &header, row, y, plane, z->xsize, - z->ysize, yerr0, yerr1, r0, r1); - break; - } - case CUPS_CSPACE_KCMY : - format_kcmy(&doc, &header, row, y, plane, z->xsize, z->ysize, - yerr0, yerr1, r0, r1); - break; - } - - // - // Write the raster data ... - // - - if (cupsRasterWritePixels(ras, row, header.cupsBytesPerLine) < - header.cupsBytesPerLine) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Unable to send raster data."); - cfImageClose(img); - return (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 && doc.YPosition >= 0) - { - blank_line(&header, row); - - y = header.cupsHeight - z->ysize; - if (doc.YPosition == 0) - y = y - y / 2; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterImageToRaster: Writing %d trailing blank lines...", - y); - - for (; y > 0; y --) - { - if (cupsRasterWritePixels(ras, row, header.cupsBytesPerLine) < - header.cupsBytesPerLine) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterImageToRaster: Unable to send raster data."); - cfImageClose(img); - return (1); - } - } - } - - // - // Free memory used for the "zoom" engine... - // - - _cfImageZoomDelete(z); - } - } - - // - // Close files... - // - - canceled: - free(row); - cupsRasterClose(ras); - cfImageClose(img); - close(outputfd); - - return (0); -} - - -// -// 'blank_line()' - Clear a line buffer to the blank value... -// - -static void -blank_line(cups_page_header2_t *header, // I - Page header - unsigned char *row) // I - Row buffer -{ - int count; // Remaining bytes - - - count = header->cupsBytesPerLine; - - switch (header->cupsColorSpace) - { - case CUPS_CSPACE_CIEXYZ : - while (count > 2) - { - *row++ = 242; - *row++ = 255; - *row++ = 255; - count -= 3; - } - break; - - case CUPS_CSPACE_CIELab : - case CUPS_CSPACE_ICC1 : - case CUPS_CSPACE_ICC2 : - case CUPS_CSPACE_ICC3 : - case CUPS_CSPACE_ICC4 : - case CUPS_CSPACE_ICC5 : - case CUPS_CSPACE_ICC6 : - case CUPS_CSPACE_ICC7 : - case CUPS_CSPACE_ICC8 : - case CUPS_CSPACE_ICC9 : - case CUPS_CSPACE_ICCA : - case CUPS_CSPACE_ICCB : - case CUPS_CSPACE_ICCC : - case CUPS_CSPACE_ICCD : - case CUPS_CSPACE_ICCE : - case CUPS_CSPACE_ICCF : - while (count > 2) - { - *row++ = 255; - *row++ = 128; - *row++ = 128; - count -= 3; - } - break; - - case CUPS_CSPACE_K : - case CUPS_CSPACE_CMY : - case CUPS_CSPACE_CMYK : - case CUPS_CSPACE_YMC : - case CUPS_CSPACE_YMCK : - case CUPS_CSPACE_KCMY : - case CUPS_CSPACE_KCMYcm : - case CUPS_CSPACE_GMCK : - case CUPS_CSPACE_GMCS : - case CUPS_CSPACE_WHITE : - case CUPS_CSPACE_GOLD : - case CUPS_CSPACE_SILVER : - memset(row, 0, count); - break; - - default : - memset(row, 255, count); - break; - } -} - - -// -// 'format_cmy()' - Convert image data to CMY. -// - -static void -format_cmy(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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 & doc->OnPixels[r0[0]]); - else - *ptr ^= (0x30 & doc->OffPixels[r0[0]]); - - if ((r0[1] & 63) > dither[x & 7]) - *ptr ^= (0x0c & doc->OnPixels[r0[1]]); - else - *ptr ^= (0x0c & doc->OffPixels[r0[1]]); - - if ((r0[2] & 63) > dither[x & 7]) - *ptr++ ^= (0x03 & doc->OnPixels[r0[2]]); - else - *ptr++ ^= (0x03 & doc->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 & doc->OnPixels[r0[0]]); - else - *ptr++ ^= (0x0f & doc->OffPixels[r0[0]]); - - if ((r0[1] & 15) > dither[x & 3]) - *ptr ^= (0xf0 & doc->OnPixels[r0[1]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[1]]); - - if ((r0[2] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[2]]); - else - *ptr++ ^= (0x0f & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - int pc, pm, py; // CMY pixels - - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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 --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if (pc && pm && py) - { - bitmask >>= 3; - *ptr ^= bitmask; - } - else - { - if (pc) - *ptr ^= bitmask; - bitmask >>= 1; - - if (pm) - *ptr ^= bitmask; - bitmask >>= 1; - - if (py) - *ptr ^= bitmask; - bitmask >>= 1; - } - - 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 & doc->OnPixels[r0[0]]); - else - *ptr ^= (0xc0 & doc->OffPixels[r0[0]]); - - if ((r0[1] & 63) > dither[x & 7]) - *ptr ^= (0x30 & doc->OnPixels[r0[1]]); - else - *ptr ^= (0x30 & doc->OffPixels[r0[1]]); - - if ((r0[2] & 63) > dither[x & 7]) - *ptr ^= (0x0c & doc->OnPixels[r0[2]]); - else - *ptr ^= (0x0c & doc->OffPixels[r0[2]]); - - if ((r0[3] & 63) > dither[x & 7]) - *ptr++ ^= (0x03 & doc->OnPixels[r0[3]]); - else - *ptr++ ^= (0x03 & doc->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 & doc->OnPixels[r0[0]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[0]]); - - if ((r0[1] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[1]]); - else - *ptr++ ^= (0x0f & doc->OffPixels[r0[1]]); - - if ((r0[2] & 15) > dither[x & 3]) - *ptr ^= (0xf0 & doc->OnPixels[r0[2]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[2]]); - - if ((r0[3] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[3]]); - else - *ptr++ ^= (0x0f & doc->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 --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if (pc && pm && py) - *kptr ^= bitmask; - else - { - if (pc) - *cptr ^= bitmask; - if (pm) - *mptr ^= bitmask; - if (py) - *yptr ^= 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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *kptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *kptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *kptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *kptr ^= (bitmask & doc->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]; - - for (x = xsize; x > 0; x --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if ((pc && pm && py && z == 3) || - (pc && z == 0) || (pm && z == 1) || (py && z == 2)) - *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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - - - (void)z; - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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 & doc->OnPixels[*r0++]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *ptr ^= (bitmask & doc->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(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - int pc, pm, py; // CMY pixels - - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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 --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if (pc && pm && py) - { - *ptr ^= bitmask; - bitmask >>= 3; - } - else - { - bitmask >>= 1; - if (pc) - *ptr ^= bitmask; - - bitmask >>= 1; - if (pm) - *ptr ^= bitmask; - - bitmask >>= 1; - if (py) - *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 & doc->OnPixels[r0[3]]); - else - *ptr ^= (0xc0 & doc->OffPixels[r0[3]]); - - if ((r0[0] & 63) > dither[x & 7]) - *ptr ^= (0x30 & doc->OnPixels[r0[0]]); - else - *ptr ^= (0x30 & doc->OffPixels[r0[0]]); - - if ((r0[1] & 63) > dither[x & 7]) - *ptr ^= (0x0c & doc->OnPixels[r0[1]]); - else - *ptr ^= (0x0c & doc->OffPixels[r0[1]]); - - if ((r0[2] & 63) > dither[x & 7]) - *ptr++ ^= (0x03 & doc->OnPixels[r0[2]]); - else - *ptr++ ^= (0x03 & doc->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 & doc->OnPixels[r0[3]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[3]]); - - if ((r0[0] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[0]]); - else - *ptr++ ^= (0x0f & doc->OffPixels[r0[0]]); - - if ((r0[1] & 15) > dither[x & 3]) - *ptr ^= (0xf0 & doc->OnPixels[r0[1]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[1]]); - - if ((r0[2] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[2]]); - else - *ptr++ ^= (0x0f & doc->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 --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if (pc && pm && py) - *kptr ^= bitmask; - else - { - if (pc) - *cptr ^= bitmask; - if (pm) - *mptr ^= bitmask; - if (py) - *yptr ^= 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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *kptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *kptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *kptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *kptr ^= (bitmask & doc->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]; - - for (x = xsize; x > 0; x --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if ((pc && pm && py && z == 0) || - (pc && z == 1) || (pm && z == 2) || (py && z == 3)) - *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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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( - imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - int pc, pm, py, pk; // Cyan, magenta, yellow, and - // black values - cf_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 - - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - ptr = row + bitoffset / 8; - bandwidth = header->cupsBytesPerLine / 6; - - switch (header->cupsColorOrder) - { - case CUPS_ORDER_CHUNKED : - 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 = pc && pm && py; - - 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; - else - ptr ++; - } - 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; - - 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 = pc && pm && py; - - 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 CUPS_ORDER_PLANAR : - 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 = pc && pm && py; - - if (pk && z == 0) - *ptr ^= bitmask; - else if (pc && pm && (z == 1 || z == 5)) - *ptr ^= bitmask; // Blue (cyan + light magenta) - else if (pc && py && (z == 3 || z == 4)) - *ptr ^= bitmask; // Green (light cyan + yellow) - else if (pm && py && (z == 2 || z == 3)) - *ptr ^= bitmask; // Red (magenta + yellow) - else if (pc && z == 1) - *ptr ^= bitmask; - else if (pm && z == 2) - *ptr ^= bitmask; - else if (py && z == 3) - *ptr ^= bitmask; - - if (bitmask > 1) - bitmask >>= 1; - else - { - bitmask = 0x80; - ptr ++; - } - } - break; - } -} - - -// -// 'format_rgba()' - Convert image data to RGBA/RGBW. -// - -static void -format_rgba(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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) - bitmask >>= 2; - else - { - bitmask = 128; - ptr ++; - } - } - break; - - case 2 : - dither = Floyd8x8[y & 7]; - - for (x = xsize ; x > 0; x --, r0 += 3) - { - if ((r0[0] & 63) > dither[x & 7]) - *ptr ^= (0xc0 & doc->OnPixels[r0[0]]); - else - *ptr ^= (0xc0 & doc->OffPixels[r0[0]]); - - if ((r0[1] & 63) > dither[x & 7]) - *ptr ^= (0x30 & doc->OnPixels[r0[1]]); - else - *ptr ^= (0x30 & doc->OffPixels[r0[1]]); - - if ((r0[2] & 63) > dither[x & 7]) - *ptr ^= (0x0c & doc->OnPixels[r0[2]]); - else - *ptr ^= (0x0c & doc->OffPixels[r0[2]]); - - ptr ++; - } - break; - - case 4 : - dither = Floyd4x4[y & 3]; - - for (x = xsize ; x > 0; x --, r0 += 3) - { - if ((r0[0] & 15) > dither[x & 3]) - *ptr ^= (0xf0 & doc->OnPixels[r0[0]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[0]]); - - if ((r0[1] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[1]]); - else - *ptr++ ^= (0x0f & doc->OffPixels[r0[1]]); - - if ((r0[2] & 15) > dither[x & 3]) - *ptr ^= (0xf0 & doc->OnPixels[r0[2]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[2]]); - - ptr ++; - } - 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 ++; - } - 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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - - - (void)z; - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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 & doc->OnPixels[*r0++]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *ptr ^= (bitmask & doc->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(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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 & doc->OnPixels[r0[2]]); - else - *ptr ^= (0x30 & doc->OffPixels[r0[2]]); - - if ((r0[1] & 63) > dither[x & 7]) - *ptr ^= (0x0c & doc->OnPixels[r0[1]]); - else - *ptr ^= (0x0c & doc->OffPixels[r0[1]]); - - if ((r0[0] & 63) > dither[x & 7]) - *ptr++ ^= (0x03 & doc->OnPixels[r0[0]]); - else - *ptr++ ^= (0x03 & doc->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 & doc->OnPixels[r0[2]]); - else - *ptr++ ^= (0x0f & doc->OffPixels[r0[2]]); - - if ((r0[1] & 15) > dither[x & 3]) - *ptr ^= (0xf0 & doc->OnPixels[r0[1]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[1]]); - - if ((r0[0] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[0]]); - else - *ptr++ ^= (0x0f & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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(imagetoraster_doc_t *doc, - cups_page_header2_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 - cf_ib_t *r0, // I - Primary image data - cf_ib_t *r1) // I - Image data for interpolation -{ - cf_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 - int pc, pm, py; // CMY pixels - - - switch (doc->XPosition) - { - case -1 : - bitoffset = 0; - break; - default : - bitoffset = header->cupsBitsPerPixel * - ((header->cupsWidth - xsize) / 2); - break; - case 1 : - bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize); - break; - } - - 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 --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if (pc && pm && py) - { - bitmask >>= 3; - *ptr ^= bitmask; - } - else - { - if (py) - *ptr ^= bitmask; - bitmask >>= 1; - - if (pm) - *ptr ^= bitmask; - bitmask >>= 1; - - if (pc) - *ptr ^= bitmask; - bitmask >>= 1; - } - - 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 & doc->OnPixels[r0[2]]); - else - *ptr ^= (0xc0 & doc->OffPixels[r0[2]]); - - if ((r0[1] & 63) > dither[x & 7]) - *ptr ^= (0x30 & doc->OnPixels[r0[1]]); - else - *ptr ^= (0x30 & doc->OffPixels[r0[1]]); - - if ((r0[0] & 63) > dither[x & 7]) - *ptr ^= (0x0c & doc->OnPixels[r0[0]]); - else - *ptr ^= (0x0c & doc->OffPixels[r0[0]]); - - if ((r0[3] & 63) > dither[x & 7]) - *ptr++ ^= (0x03 & doc->OnPixels[r0[3]]); - else - *ptr++ ^= (0x03 & doc->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 & doc->OnPixels[r0[2]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[2]]); - - if ((r0[1] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[1]]); - else - *ptr++ ^= (0x0f & doc->OffPixels[r0[1]]); - - if ((r0[0] & 15) > dither[x & 3]) - *ptr ^= (0xf0 & doc->OnPixels[r0[0]]); - else - *ptr ^= (0xf0 & doc->OffPixels[r0[0]]); - - if ((r0[3] & 15) > dither[x & 3]) - *ptr++ ^= (0x0f & doc->OnPixels[r0[3]]); - else - *ptr++ ^= (0x0f & doc->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 --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if (pc && pm && py) - *kptr ^= bitmask; - else - { - if (pc) - *cptr ^= bitmask; - if (pm) - *mptr ^= bitmask; - if (py) - *yptr ^= 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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 63) > dither[x & 7]) - *kptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *kptr ^= (bitmask & doc->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 & doc->OnPixels[*r0++]); - else - *cptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *mptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *mptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *yptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *yptr ^= (bitmask & doc->OffPixels[*r0++]); - - if ((*r0 & 15) > dither[x & 3]) - *kptr ^= (bitmask & doc->OnPixels[*r0++]); - else - *kptr ^= (bitmask & doc->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]; - - for (x = xsize; x > 0; x --) - { - pc = *r0++ > dither[x & 15]; - pm = *r0++ > dither[x & 15]; - py = *r0++ > dither[x & 15]; - - if ((pc && pm && py && z == 3) || - (pc && z == 2) || (pm && z == 1) || (py && z == 0)) - *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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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 & doc->OnPixels[*r0]); - else - *ptr ^= (bitmask & doc->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(cf_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; - } -} diff --git a/cupsfilters/ipp.c b/cupsfilters/ipp.c deleted file mode 100644 index 8f9b650ce..000000000 --- a/cupsfilters/ipp.c +++ /dev/null @@ -1,2717 +0,0 @@ -// -// IPP-related functions for libcupsfilters. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfGetBackSideOrientation() - Return backside orientation for duplex -// printing -// cfGetPrintRenderIntent() - Return rendering intent for a job -// cfJoinJobOptionsAndAttrs() - Join job IPP attributes and job options in -// one option list -// - -// -// Include necessary headers. -// - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -enum resolve_uri_converter_type // **** Resolving DNS-SD based URI **** -{ - CUPS_BACKEND_URI_CONVERTER = -1, - IPPFIND_BASED_CONVERTER_FOR_PRINT_URI = 0, - IPPFIND_BASED_CONVERTER_FOR_FAX_URI = 1 -}; - -char cf_get_printer_attributes_log[CF_GET_PRINTER_ATTRIBUTES_LOGSIZE]; - -static int -convert_to_port(char *a) -{ - int port = 0; - for (int i = 0; i < strlen(a); i ++) - port = port*10 + (a[i] - '0'); - - return (port); -} - -static void -log_printf(char *log, - const char *format, ...) -{ - va_list arglist; - va_start(arglist, format); - vsnprintf(log + strlen(log), - CF_GET_PRINTER_ATTRIBUTES_LOGSIZE - strlen(log) - 1, - format, arglist); - log[CF_GET_PRINTER_ATTRIBUTES_LOGSIZE - 1] = '\0'; - va_end(arglist); -} - -char * -cfResolveURI(const char *raw_uri) -{ - char *pseudo_argv[2]; - const char *uri; - int fd1, fd2; - char *save_device_uri_var; - - // Eliminate any output to stderr, to get rid of the CUPS-backend-specific - // output of the cupsBackendDeviceURI() function - fd1 = dup(2); - fd2 = open("/dev/null", O_WRONLY); - dup2(fd2, 2); - close(fd2); - - // If set, save the DEVICE_URI environment and then unset it, so that - // if we are running under CUPS (as filter or backend) our raw_uri gets - // resolved and not whatever URI is set in DEVICE_URI - if ((save_device_uri_var = getenv("DEVICE_URI")) != NULL) - { - save_device_uri_var = strdup(save_device_uri_var); - unsetenv("DEVICE_URI"); - } - - // Use the URI resolver of libcups to support DNS-SD-service-name-based - // URIs. The function returns the corresponding host-name-based URI - pseudo_argv[0] = (char *)raw_uri; - pseudo_argv[1] = NULL; - uri = cupsBackendDeviceURI(pseudo_argv); - - // Restore DEVICE_URI environment variable if we had unset it - if (save_device_uri_var) - { - setenv("DEVICE_URI", save_device_uri_var, 1); - free(save_device_uri_var); - } - - // Re-activate stderr output - dup2(fd1, 2); - close(fd1); - - return (uri ? strdup(uri) : NULL); -} - -// Check how the driverless support is provided -int -cfCheckDriverlessSupport(const char* uri) -{ - int support_status = CF_DRVLESS_CHECKERR; - ipp_t *response = NULL; - - response = cfGetPrinterAttributes3(NULL, uri, NULL, 0, NULL, 0, 1, - &support_status); - if (response != NULL) - ippDelete(response); - - return (support_status); -} - -// Get attributes of a printer specified only by URI -ipp_t * -cfGetPrinterAttributes(const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug) -{ - return (cfGetPrinterAttributes2(NULL, raw_uri, pattrs, pattrs_size, - req_attrs, req_attrs_size, debug)); -} - -// Get attributes of a printer specified by URI and under a given HTTP -// connection, for example via a domain socket -ipp_t * -cfGetPrinterAttributes2(http_t *http_printer, - const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug) -{ - return (cfGetPrinterAttributes3(http_printer, raw_uri, pattrs, pattrs_size, - req_attrs, req_attrs_size, debug, NULL)); -} - -// Get attributes of a printer specified by URI and under a given HTTP -// connection, for example via a domain socket, and give info about used -// fallbacks -ipp_t * -cfGetPrinterAttributes3(http_t *http_printer, - const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug, - int* driverless_info) -{ - return (cfGetPrinterAttributes5(http_printer, raw_uri, pattrs, pattrs_size, - req_attrs, req_attrs_size, debug, - driverless_info, CUPS_BACKEND_URI_CONVERTER)); -} - -// Get attributes of a printer specified only by URI and given info about -// fax-support -ipp_t *cfGetPrinterAttributes4(const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug, - int is_fax) -{ - if (is_fax) - return (cfGetPrinterAttributes5(NULL, raw_uri, pattrs, pattrs_size, - req_attrs, req_attrs_size, debug, NULL, - IPPFIND_BASED_CONVERTER_FOR_FAX_URI)); - else - return (cfGetPrinterAttributes5(NULL, raw_uri, pattrs, pattrs_size, - req_attrs, req_attrs_size, debug, NULL, - IPPFIND_BASED_CONVERTER_FOR_PRINT_URI)); -} - -// Get attributes of a printer specified by URI and under a given HTTP -// connection, for example via a domain socket, and give info about used -// fallbacks -ipp_t * -cfGetPrinterAttributes5(http_t *http_printer, - const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug, - int* driverless_info, - int resolve_uri_type ) -{ - char *uri; - int have_http, uri_status, host_port, i = 0, total_attrs = 0, fallback, - cap = 0; - char scheme[10], userpass[1024], host_name[1024], resource[1024]; - http_encryption_t encryption; - ipp_t *request, *response = NULL; - ipp_attribute_t *attr; - char valuebuffer[65536]; - const char *kw; - ipp_status_t ipp_status; - // Default attributes for get-printer-attributes requests to - // obtain complete capability lists of a printer - const char * const pattrs_cap_standard[] = - { - "all", - "media-col-database", - }; - const char * const pattrs_cap_fallback[] = - { - "all", - }; - // Attributes required in the IPP response of a complete printer - // capability list - const char * const req_attrs_cap[] = - { - "attributes-charset", - "attributes-natural-language", - "charset-configured", - "charset-supported", - "compression-supported", - "document-format-default", - "document-format-supported", - "generated-natural-language-supported", - "ipp-versions-supported", - "natural-language-configured", - "operations-supported", - "printer-is-accepting-jobs", - "printer-name", - "printer-state", - "printer-state-reasons", - "printer-up-time", - "printer-uri-supported", - "uri-authentication-supported", - "uri-security-supported" - }; - - // Expect a device capable of standard IPP Everywhere - if (driverless_info != NULL) - *driverless_info = CF_DRVLESS_FULL; - - // - // Request printer properties via IPP, for example to - // - Find capabilities, options, and default settings - // - Printer's status: Accepting jobs? Busy? With how many jobs? - // - Generate a PPD file for the printer - // (mainly driverless-capable printers with CUPS 2.x) - // - - cf_get_printer_attributes_log[0] = '\0'; - - // Convert DNS-SD-service-name-based URIs to host-name-based URIs - if (resolve_uri_type == CUPS_BACKEND_URI_CONVERTER) - uri = cfResolveURI(raw_uri); - else - uri = cfippfindBasedURIConverter(raw_uri, resolve_uri_type); - - if (uri == NULL) - { - log_printf(cf_get_printer_attributes_log, - "get-printer-attibutes: Cannot resolve URI: %s\n", raw_uri); - return NULL; - } - - // Extract URI componants needed for the IPP request - uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, - scheme, sizeof(scheme), - userpass, sizeof(userpass), - host_name, sizeof(host_name), - &(host_port), - resource, sizeof(resource)); - if (uri_status != HTTP_URI_OK) - { - // Invalid URI - log_printf(cf_get_printer_attributes_log, - "get-printer-attributes: Cannot parse the printer URI: %s\n", - uri); - if (uri) free(uri); - return NULL; - } - - if (!strcmp(scheme, "ipps")) - encryption = HTTP_ENCRYPTION_ALWAYS; - else - encryption = HTTP_ENCRYPTION_IF_REQUESTED; - - // Connect to the server if not already done - if (http_printer == NULL) - { - have_http = 0; - if ((http_printer = - httpConnect2 (host_name, host_port, NULL, AF_UNSPEC, - encryption, 1, 3000, NULL)) == NULL) - { - log_printf(cf_get_printer_attributes_log, - "get-printer-attributes: Cannot connect to printer with URI %s.\n", - uri); - if (uri) free(uri); - return NULL; - } - } - else - have_http = 1; - - // If we got called without attribute list, use the attributes for polling - // a complete list of capabilities of the printer. - // If also no list of required attributes in the response is supplied, use - // the default list - if (pattrs == NULL || pattrs_size == 0) - { - cap = 1; - pattrs = pattrs_cap_standard; - pattrs_size = sizeof(pattrs_cap_standard) / sizeof(pattrs_cap_standard[0]); - if (req_attrs == NULL || req_attrs_size == 0) - { - req_attrs = req_attrs_cap; - req_attrs_size = sizeof(req_attrs_cap) / sizeof(req_attrs_cap[0]); - } - } - - // Loop through all fallbacks until getting a successful result - for (fallback = 0; fallback < 2 + cap; fallback ++) - { - request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); - if (fallback == 1) - // Fallback 1: Try IPP 1.1 instead of 2.0 - ippSetVersion(request, 1, 1); - if (fallback == 2 && cap) - { - // Fallback 2: (Only for full capability list) Try only "all", - // without "media-col-database" - pattrs = pattrs_cap_fallback; - pattrs_size = sizeof(pattrs_cap_fallback) / - sizeof(pattrs_cap_fallback[0]); - } - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, - uri); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", pattrs_size, - NULL, pattrs); - - response = cupsDoRequest(http_printer, request, resource); - ipp_status = cupsLastError(); - - if (response) - { - log_printf(cf_get_printer_attributes_log, - "Requested IPP attributes (get-printer-attributes) for printer with URI %s\n", - uri); - // Log all printer attributes for debugging and count them - if (debug) - log_printf(cf_get_printer_attributes_log, - "Full list of all IPP attributes:\n"); - attr = ippFirstAttribute(response); - while (attr) - { - total_attrs ++; - if (debug) - { - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - log_printf(cf_get_printer_attributes_log, - " Attr: %s\n",ippGetName(attr)); - log_printf(cf_get_printer_attributes_log, - " Value: %s\n", valuebuffer); - for (i = 0; i < ippGetCount(attr); i ++) - if ((kw = ippGetString(attr, i, NULL)) != NULL) - log_printf(cf_get_printer_attributes_log, " Keyword: %s\n", kw); - } - attr = ippNextAttribute(response); - } - - // Check whether the IPP response contains the required attributes - // and is not incomplete - if (req_attrs) - for (i = req_attrs_size; i > 0; i --) - if (ippFindAttribute(response, req_attrs[i - 1], IPP_TAG_ZERO) == - NULL) - break; - if (ipp_status == IPP_STATUS_ERROR_BAD_REQUEST || - ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED || - (req_attrs && i > 0) || (cap && total_attrs < 20)) - { - log_printf(cf_get_printer_attributes_log, - "get-printer-attributes IPP request failed:\n"); - if (ipp_status == IPP_STATUS_ERROR_BAD_REQUEST) - log_printf(cf_get_printer_attributes_log, - " - ipp_status == IPP_STATUS_ERROR_BAD_REQUEST\n"); - else if (ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED) - log_printf(cf_get_printer_attributes_log, - " - ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED\n"); - if (req_attrs && i > 0) - log_printf(cf_get_printer_attributes_log, - " - Required IPP attribute %s not found\n", - req_attrs[i - 1]); - if (cap && total_attrs < 20) - log_printf(cf_get_printer_attributes_log, - " - Too few IPP attributes: %d (30 or more expected)\n", - total_attrs); - ippDelete(response); - } - else - { - // Suitable response, we are done - if (have_http == 0) httpClose(http_printer); - if (uri) free(uri); - return response; - } - } - else - { - log_printf(cf_get_printer_attributes_log, - "Request for IPP attributes (get-printer-attributes) for printer with URI %s failed: %s\n", - uri, cupsLastErrorString()); - log_printf(cf_get_printer_attributes_log, "get-printer-attributes IPP request failed:\n"); - log_printf(cf_get_printer_attributes_log, " - No response\n"); - } - if (fallback == 1 + cap) - { - log_printf(cf_get_printer_attributes_log, - "No further fallback available, giving up\n"); - if (driverless_info != NULL) - *driverless_info = CF_DRVLESS_CHECKERR; - } - else if (cap && fallback == 1) - { - log_printf(cf_get_printer_attributes_log, - "The server doesn't support the standard IPP request, trying request without media-col\n"); - if (driverless_info != NULL) - *driverless_info = CF_DRVLESS_INCOMPLETEIPP; - } - else if (fallback == 0) - { - log_printf(cf_get_printer_attributes_log, - "The server doesn't support IPP2.0 request, trying IPP1.1 request\n"); - if (driverless_info != NULL) - *driverless_info = CF_DRVLESS_IPP11; - } - } - - if (have_http == 0) httpClose(http_printer); - if (uri) free(uri); - return (NULL); -} - -char* -cfippfindBasedURIConverter (const char *uri, int is_fax) -{ - int ippfind_pid = 0, // Process ID of ippfind for IPP - post_proc_pipe[2], // Pipe to post-processing for IPP - wait_children, // Number of child processes left - wait_pid, // Process ID from wait() - wait_status, // Status from child - exit_status = 0, // Exit status - bytes, - port, - i, - output_of_fax_uri = 0, - is_local; - char *ippfind_argv[100], // Arguments for ippfind - *ptr_to_port = NULL, - *reg_type, - *resolved_uri = NULL, // Buffer for resolved URI - *resource_field = NULL, - *service_hostname = NULL, - // URI components... - scheme[32], - userpass[256], - hostname[1024], - resource[1024], - *buffer = NULL, // Copy buffer - *ptr; // Pointer into string; - cups_file_t *fp; // Post-processing input file - int status; // Status of GET request - - status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), - userpass, sizeof(userpass), - hostname, sizeof(hostname), &port, resource, - sizeof(resource)); - if (status < HTTP_URI_OK) - // Invalid URI - goto error; - - // URI is not DNS-SD-based, so do not resolve - if ((reg_type = strstr(hostname, "._tcp")) == NULL) - return (strdup(uri)); - - resolved_uri = - (char *)malloc(CF_GET_PRINTER_ATTRIBUTES_MAX_URI_LEN * (sizeof(char))); - if (resolved_uri == NULL) - goto error; - memset(resolved_uri, 0, CF_GET_PRINTER_ATTRIBUTES_MAX_URI_LEN); - - reg_type --; - while (reg_type >= hostname && *reg_type != '.') - reg_type --; - if (reg_type < hostname) - goto error; - *reg_type++ = '\0'; - - i = 0; - ippfind_argv[i++] = "ippfind"; - ippfind_argv[i++] = reg_type; // list IPP(S) entries - ippfind_argv[i++] = "-T"; // DNS-SD poll timeout - ippfind_argv[i++] = "0"; // Minimum time required - if (is_fax) - { - ippfind_argv[i++] = "--txt"; - ippfind_argv[i++] = "rfo"; - } - ippfind_argv[i++] = "-N"; - ippfind_argv[i++] = hostname; - ippfind_argv[i++] = "-x"; - ippfind_argv[i++] = "echo"; // Output the needed data fields - ippfind_argv[i++] = "-en"; // separated by tab characters - if(is_fax) - ippfind_argv[i++] = "\n{service_hostname}\t{txt_rfo}\t{service_port}\t"; - else - ippfind_argv[i++] = "\n{service_hostname}\t{txt_rp}\t{service_port}\t"; - ippfind_argv[i++] = ";"; - ippfind_argv[i++] = "--local"; // Rest only if local service - ippfind_argv[i++] = "-x"; - ippfind_argv[i++] = "echo"; // Output an 'L' at the end of the - ippfind_argv[i++] = "-en"; // line - ippfind_argv[i++] = "L"; - ippfind_argv[i++] = ";"; - ippfind_argv[i++] = NULL; - - // - // Create a pipe for passing the ippfind output to post-processing - // - - if (pipe(post_proc_pipe)) - goto error; - - if ((ippfind_pid = fork()) == 0) - { - // - // Child comes here... - // - - dup2(post_proc_pipe[1], 1); - close(post_proc_pipe[0]); - close(post_proc_pipe[1]); - - execvp(CUPS_IPPFIND, ippfind_argv); - - exit(1); - } - else if (ippfind_pid < 0) - { - // - // Unable to fork! - // - - goto error; - } - - close(post_proc_pipe[1]); - - fp = cupsFileOpenFd(post_proc_pipe[0], "r"); - - buffer = - (char*)malloc(CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN * sizeof(char)); - if (buffer == NULL) - goto error; - memset(buffer, 0, CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN); - - while ((bytes = - cupsFileGetLine(fp, buffer, - CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN)) > 0) - { - // Mark all the fields of the output of ippfind - ptr = buffer; - - // ignore new lines - if (bytes < 3) - goto read_error; - - // First, build the DNS-SD-service-name-based URI ... - while (ptr && !isalnum(*ptr & 255)) ptr ++; - - service_hostname = ptr; - ptr = memchr(ptr, '\t', - CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN - (ptr - buffer)); - if (!ptr) goto read_error; - *ptr = '\0'; - ptr ++; - - resource_field = ptr; - ptr = memchr(ptr, '\t', - CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN - (ptr - buffer)); - if (!ptr) goto read_error; - *ptr = '\0'; - ptr ++; - - ptr_to_port = ptr; - ptr = memchr(ptr, '\t', - CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN - (ptr - buffer)); - if (!ptr) goto read_error; - *ptr = '\0'; - ptr ++; - - // Do we have a local service so that we have to set the host name to - // "localhost"? - is_local = (*ptr == 'L'); - - ptr = strchr(reg_type, '.'); - if (!ptr) goto read_error; - *ptr = '\0'; - - port = convert_to_port(ptr_to_port); - - httpAssembleURIf(HTTP_URI_CODING_ALL, resolved_uri, - 2047, reg_type + 1, NULL, - (is_local ? "localhost" : service_hostname), port, "/%s", - resource_field); - - if (is_fax) - output_of_fax_uri = 1; // fax-uri requested from fax-capable device - - read_error: - memset(buffer, 0, CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN); - } - - cupsFileClose(fp); - - if (buffer != NULL) - free(buffer); - - // - // Wait for the child processes to exit... - // - - wait_children = 1; - - while (wait_children > 0) - { - // - // Wait until we get a valid process ID or the job is canceled... - // - - while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR); - - if (wait_pid < 0) - break; - - wait_children --; - - // - // Report child status... - // - - if (wait_status) - { - if (WIFEXITED(wait_status)) - { - exit_status = WEXITSTATUS(wait_status); - if (wait_pid == ippfind_pid && exit_status <= 2) - exit_status = 0; - } - else if (WTERMSIG(wait_status) == SIGTERM) - { - // All OK, no error - } - else - { - exit_status = WTERMSIG(wait_status); - } - } - } - if (is_fax && !output_of_fax_uri) - goto error; - - return (resolved_uri); - - // - // Exit... - // - - error: - if (resolved_uri != NULL) - free(resolved_uri); - return (NULL); -} - - -const char* // O - Attribute value as string -cfIPPAttrEnumValForPrinter(ipp_t *printer_attrs, // I - Printer attributes, same - // as to respond - // get-printer-attributes, - // or NULL to not consider - ipp_t *job_attrs, // I - Job attributes - const char *attr_name)// I - Attribute name -{ - ipp_attribute_t *attr; - char printer_attr_name[256]; - int i; - const char *res; - - - if ((printer_attrs == NULL && job_attrs == NULL) || attr_name == NULL) - return NULL; - - // Check whether job got supplied the named attribute and read out its value - // as string - if (job_attrs == NULL || - (attr = ippFindAttribute(job_attrs, attr_name, IPP_TAG_ZERO)) == NULL) - res = NULL; - else - res = ippGetString(attr, 0, NULL); - - // Check the printer properties if supplied to see whether the job attribute - // value is valid or if the job attribute was not supplied. Use printer - // default value of job attribute is invalid or not supplied - // If no printer attributes are supplied (NULL), simply accept the job - // attribute value - if (printer_attrs) - { - if (res && res[0]) - { - // Check whether value is valid according to printer attributes - snprintf(printer_attr_name, sizeof(printer_attr_name) - 1, - "%s-supported", attr_name); - if ((attr = ippFindAttribute(printer_attrs, printer_attr_name, - IPP_TAG_ZERO)) != NULL) - { - for (i = 0; i < ippGetCount(attr); i ++) - if (strcasecmp(res, ippGetString(attr, i, NULL)) == 0) - break; // Job attribute value is valid - if (i == ippGetCount(attr)) - res = NULL; // Job attribute value is not valid - } - } - if (!res || !res[0]) - { - // Use default value from printer attributes - snprintf(printer_attr_name, sizeof(printer_attr_name) - 1, - "%s-default", attr_name); - if ((attr = ippFindAttribute(printer_attrs, printer_attr_name, - IPP_TAG_ZERO)) != NULL) - res = ippGetString(attr, 0, NULL); - } - } - - return (res); -} - - -int // O - 1: Success; 0: Error -cfIPPAttrIntValForPrinter(ipp_t *printer_attrs, // I - Printer attributes, same - // as to respond - // get-printer-attributes, - // or NULL to not consider - ipp_t *job_attrs, // I - Job attributes - const char *attr_name,// I - Attribute name - int *value) // O - Attribute value as - // integer -{ - ipp_attribute_t *attr; - char printer_attr_name[256]; - int retval, val, min, max; - - if ((printer_attrs == NULL && job_attrs == NULL) || attr_name == NULL) - return (0); - - // Check whether job got supplied the named attribute and read out its value - // as integer - if (job_attrs == NULL || - (attr = ippFindAttribute(job_attrs, attr_name, IPP_TAG_ZERO)) == NULL) - retval = 0; - else - { - retval = 1; - val = ippGetInteger(attr, 0); - } - - // Check the printer properties if supplied to see whether the job attribute - // value is valid or if the job attribute was not supplied. Use printer - // default value of job attribute is invalid or not supplied - // If no printer attributes are supplied (NULL), simply accept the job - // attribute value - if (printer_attrs) - { - if (retval == 1) - { - // Check whether value is valid according to printer attributes - snprintf(printer_attr_name, sizeof(printer_attr_name) - 1, - "%s-supported", attr_name); - if ((attr = ippFindAttribute(printer_attrs, printer_attr_name, - IPP_TAG_RANGE)) != NULL) - { - min = ippGetRange(attr, 0, &max); - if (val < min || val > max) - retval = 0; // Job attribute value out of range - } - } - if (retval == 0) - { - // Use default value from printer attributes - snprintf(printer_attr_name, sizeof(printer_attr_name) - 1, - "%s-default", attr_name); - if ((attr = ippFindAttribute(printer_attrs, printer_attr_name, - IPP_TAG_ZERO)) != NULL) - { - retval = 1; - val = ippGetInteger(attr, 0); - } - } - } - - if (retval == 1) - *value = val; - return (retval); -} - - -int // O - 1: Success; 0: Error -cfIPPAttrResolutionForPrinter(ipp_t *printer_attrs,// I - Printer attributes - ipp_t *job_attrs, // I - Job attributes - const char *attr_name,// I - Attribute name - int *xres, // O - X resolution (dpi) - int *yres) // O - Y resolution (dpi) -{ - int i; - ipp_attribute_t *attr; - char printer_attr_name[256]; - int retval, x, y; - ipp_res_t units; - - if ((printer_attrs == NULL && job_attrs == NULL)) - return (0); - - if (attr_name == NULL) - attr_name = "printer-resolution"; - - // Check whether job got supplied the named attribute and read out its value - // as integer - if (job_attrs == NULL || - (attr = ippFindAttribute(job_attrs, attr_name, IPP_TAG_ZERO)) == NULL) - retval = 0; - else - { - retval = 1; - x = ippGetResolution(attr, 0, &y, &units); - if (units == IPP_RES_PER_CM) - { - // Get resolutions in dpi - x = (int)((float)x * 2.54); - y = (int)((float)y * 2.54); - } - } - - // Check the printer properties if supplied to see whether the job attribute - // value is valid or if the job attribute was not supplied. Use printer - // default value of job attribute is invalid or not supplied - // If no printer attributes are supplied (NULL), simply accept the job - // attribute value - if (printer_attrs) - { - if (retval == 1) - { - // Check whether value is valid according to printer attributes - snprintf(printer_attr_name, sizeof(printer_attr_name) - 1, - "%s-supported", attr_name); - if ((attr = ippFindAttribute(printer_attrs, printer_attr_name, - IPP_TAG_RANGE)) != NULL) - { - for (i = 0; i < ippGetCount(attr); i ++) - { - int sx, sy; - ipp_res_t su; - sx = ippGetResolution(attr, i, &sy, &su); - if (su == IPP_RES_PER_CM) - { - // Get resolutions in dpi - sx = (int)((float)sx * 2.54); - sy = (int)((float)sy * 2.54); - } - if ((x - sx) * (x - sx) < 10 && - (y - sy) * (y - sy) < 10) - break; // Job attribute value is valid - } - if (i == ippGetCount(attr)) - retval = 0; // Job attribute value is not valid - } - } - if (retval == 0) - { - // Use default value from printer attributes - snprintf(printer_attr_name, sizeof(printer_attr_name) - 1, - "%s-default", attr_name); - if ((attr = ippFindAttribute(printer_attrs, printer_attr_name, - IPP_TAG_ZERO)) != NULL) - { - retval = 1; - x = ippGetResolution(attr, 0, &y, &units); - if (units == IPP_RES_PER_CM) - { - // Get resolutions in dpi - x = (int)((float)x * 2.54); - y = (int)((float)y * 2.54); - } - } - } - } - - if (retval == 1) - { - *xres = x; - *yres = y; - } - return retval; -} - - -int -cfIPPReverseOutput(ipp_t *printer_attrs, - ipp_t *job_attrs) -{ - int i; - ipp_attribute_t *attr1, *attr2; - const char *val1, *val2; - char buf[1024]; - int length; - - // Figure out the right default output order from the IPP attributes... - if ((val1 = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "output-bin")) != NULL) - { - // Find corresponding "printer-output-tray" entry - if ((attr1 = ippFindAttribute(printer_attrs, "output-bin-supported", - IPP_TAG_ZERO)) != NULL && - (attr2 = ippFindAttribute(printer_attrs, "printer-output-tray", - IPP_TAG_ZERO)) != NULL) - { - for (i = 0; i < ippGetCount(attr1) && i < ippGetCount(attr2); i ++) - { - if ((val2 = ippGetString(attr1, i, 0)) != NULL && - strcmp(val1, val2) == 0) - { - if ((val2 = - (const char *)ippGetOctetString(attr2, i, &length)) != NULL) - { - if (length > (int)(sizeof(buf) - 1)) - length = (int)(sizeof(buf) - 1); - memcpy(buf, val2, length); - buf[length] = '\0'; - if (strcasestr(buf, "stackingorder=firstToLast")) - return (0); - if (strcasestr(buf, "stackingorder=lastToFirst")) - return (1); - if (strcasestr(buf, "pagedelivery=faceDown")) - return (0); - if (strcasestr(buf, "pagedelivery=faceUp")) - return (1); - } - break; - } - } - } - // Check whether output bin name is "face-down" or "face-up" - if (strcasestr(val1, "face-down")) - return (0); - if (strcasestr(val1, "face-up")) - return (1); - } - - // No hint of whether to print in original or reverse order. Usually this - // happens for a fax-out queue, where one has no output bin. Use original - // order then. - return (0); -} - - -// -// 'cfGetBackSideOrientation()' - This functions returns the back -// side orientation using printer -// attributes. Meaning and reason for -// backside orientation: It only makes -// sense if printer supports duplex, -// so, if printer reports that it -// supports duplex printing via -// sides-supported IPP attribute, then -// it also reports back-side -// orientation for each PDL in PDL -// specific IPP attributes. Backside -// orientation is specially needed for -// raster PDLs as raster PDLs are -// specially made for raster printers -// which do not have sufficient memory -// to hold a full page bitmap(raster -// page). So they cannot build the -// whole page in memory before -// starting to print it. For one-sided -// printing it is easy to manage. The -// printer's mechanism pulls the page -// in on its upper edge and starts to -// print, from top to bottom, after -// that it ejects the page. For -// double-sided printing it does the -// same for the front side, but for -// the back side the mechanics of the -// printer has to turn over the sheet, -// and now, depending on how the sheet -// is turned over it happens that the -// edge arriving in the printing -// mechanism is the lower edge of the -// back side. And if the printer -// simply prints then, the back side -// is the wrong way around. The -// printer reports its need via back -// side orientation in such a case, so -// that the client knows to send the -// back side upside down for example. -// In vector PDLs, PDF and PostScript, -// always the full page's raster image -// is completely generated in the -// printer before the page is started, -// and therefore the printer can start -// to take the pixels from the lower -// edge of the raster image if needed, -// so back side orientation is always -// "normal" for these PDLs. And if a -// printer does not support duplex, -// back side orientation is not -// needed. -// - -int // O - Backside orientation (bit 0-2) - // Requires flipped margin? - // Yes: bit 4 set; No: bit 3 set -cfGetBackSideOrientation(cf_filter_data_t *data) // I - Filter data -{ - ipp_t *printer_attrs = data->printer_attrs; - int num_options = data->num_options; - cups_option_t *options = data->options; - char *final_content_type = data->final_content_type; - ipp_attribute_t *ipp_attr = NULL; // IPP attribute - int i, // Looping variable - count; - const char *keyword; - int backside = -1; // backside obtained using printer attributes - - - // also check options - if ((ipp_attr = ippFindAttribute(printer_attrs, "sides-supported", - IPP_TAG_ZERO)) != NULL) - { - if (ippContainsString(ipp_attr, "two-sided-long-edge")) - { - if (final_content_type && - strcasestr(final_content_type, "/urf") && - (ipp_attr = ippFindAttribute(printer_attrs, "urf-supported", - IPP_TAG_ZERO)) != NULL) - { - for (i = 0, count = ippGetCount(ipp_attr); i < count; i ++) - { - const char *dm = ippGetString(ipp_attr, i, NULL); // DM value - if (!strcasecmp(dm, "DM1")) - { - backside = CF_BACKSIDE_NORMAL; - break; - } - if (!strcasecmp(dm, "DM2")) - { - backside = CF_BACKSIDE_FLIPPED; - break; - } - if (!strcasecmp(dm, "DM3")) - { - backside = CF_BACKSIDE_ROTATED; - break; - } - if (!strcasecmp(dm, "DM4")) - { - backside = CF_BACKSIDE_MANUAL_TUMBLE; - break; - } - } - } - else if ((final_content_type && - strcasestr(final_content_type, "/vnd.pwg-raster") && - (ipp_attr = ippFindAttribute(printer_attrs, - "pwg-raster-document-sheet-back", - IPP_TAG_ZERO)) != NULL) || - (final_content_type && - strcasestr(final_content_type, "/pclm") && - (ipp_attr = ippFindAttribute(printer_attrs, - "pclm-raster-back-side", - IPP_TAG_ZERO)) != NULL) || - ((ipp_attr = NULL) || - (keyword = cupsGetOption("back-side-orientation", - num_options, options)) != NULL)) - { - if (ipp_attr) - keyword = ippGetString(ipp_attr, 0, NULL); - if (!strcasecmp(keyword, "flipped")) - backside = CF_BACKSIDE_FLIPPED; - else if (!strncasecmp(keyword, "manual", 6)) - backside = CF_BACKSIDE_MANUAL_TUMBLE; - else if (!strcasecmp(keyword, "normal")) - backside = CF_BACKSIDE_NORMAL; - else if (!strcasecmp(keyword, "rotated")) - backside = CF_BACKSIDE_ROTATED; - } - - if (backside == -1) - backside = CF_BACKSIDE_NORMAL; - else if ((keyword = cupsGetOption("duplex-requires-flipped-margin", - num_options, options)) != NULL) - { - if (strcasecmp(keyword, "true") == 0) - backside |= 16; - else - backside |= 8; - } - } - } - - return (backside); -} - - -const char * -cfGetPrintRenderIntent(cf_filter_data_t *data, - char *ri, - int ri_len) -{ - const char *val; - int num_options = 0; - cups_option_t *options = NULL; - ipp_t *printer_attrs = data->printer_attrs; - ipp_attribute_t *ipp_attr; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - int i; - - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - if ((val = cupsGetOption("print-rendering-intent", num_options, - options)) != NULL || - (val = cupsGetOption("PrintRenderingIntent", num_options, - options)) != NULL || - (val = cupsGetOption("RenderingIntent", num_options, - options)) != NULL) - { - if (!strcasecmp(val, "absolute")) - snprintf(ri, ri_len, "%s", "Absolute"); - else if (!strcasecmp(val, "auto") || !strcasecmp(val, "automatic")) - snprintf(ri, ri_len, "%s", "Automatic"); - else if (!strcasecmp(val, "perceptual")) - snprintf(ri, ri_len, "%s", "Perceptual"); - else if (!strcasecmp(val, "relative")) - snprintf(ri, ri_len, "%s", "Relative"); - else if (!strcasecmp(val, "relative-bpc") || - !strcasecmp(val, "relativebpc")) - snprintf(ri, ri_len, "%s", "RelativeBpc"); - else if (!strcasecmp(val, "saturation")) - snprintf(ri, ri_len, "%s", "Saturation"); - } - - if ((ipp_attr = ippFindAttribute(printer_attrs, - "print-rendering-intent-supported", - IPP_TAG_ZERO)) != NULL) - { - int autoRender = 0; - int count; - - if ((count = ippGetCount(ipp_attr)) > 0) - { - for (i = 0; i < count; i ++) - { - const char *temp = ippGetString(ipp_attr, i, NULL); - if (!strcasecmp(temp, "auto")) autoRender = 1; - if (ri[0] != '\0') - // User has supplied a setting - if (!strcasecmp(ri, temp)) - break; - } - if (ri[0] != '\0' && i == count) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "User specified print-rendering-intent not supported " - "by printer, using default print rendering intent."); - ri[0] = '\0'; - } - if (ri[0] == '\0') - { // Either user has not supplied any setting - // or user supplied value is not supported by printer - if ((ipp_attr = ippFindAttribute(printer_attrs, - "print-rendering-intent-default", - IPP_TAG_ZERO)) != NULL) - snprintf(ri, ri_len, "%s", ippGetString(ipp_attr, 0, NULL)); - else if (autoRender == 1) - snprintf(ri, ri_len, "%s", "auto"); - } - } - } - - cupsFreeOptions(num_options, options); - return (ri); -} - - -// -// 'cfJoinJobOptionsAndAttrs()' - Function for storing job IPP attribute in -// option list, together with the options -// - -int // O - New number of options - // in new option list -cfJoinJobOptionsAndAttrs(cf_filter_data_t* data, // I - Filter data - int num_options, // I - Current mumber of - // options in new option - // list - cups_option_t **options) // IO - New option lsit -{ - ipp_t *job_attrs = data->job_attrs; // Job attributes - ipp_attribute_t *ipp_attr; // IPP attribute - int i = 0; // Looping variable - char buf[2048]; // Buffer for storing value of ipp attr - cups_option_t *opt; - - for (i = 0, opt = data->options; i < data->num_options; i ++, opt ++) - num_options = cupsAddOption(opt->name, opt->value, num_options, options); - - for (ipp_attr = ippFirstAttribute(job_attrs); ipp_attr; - ipp_attr = ippNextAttribute(job_attrs)) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - num_options = cupsAddOption(ippGetName(ipp_attr), buf, - num_options, options); - } - - return (num_options); -} - - -#ifndef HAVE_STRLCPY -// -// 'strlcpy()' - Safely copy two strings. -// - -size_t // O - Length of string -strlcpy(char *dst, // O - Destination string - const char *src, // I - Source string - size_t size) // I - Size of destination string buffer -{ - size_t srclen; // Length of source string - - - // - // Figure out how much room is needed... - // - - size --; - - srclen = strlen(src); - - // - // Copy the appropriate amount... - // - - if (srclen > size) - srclen = size; - - memmove(dst, src, srclen); - dst[srclen] = '\0'; - - return (srclen); -} -#endif // !HAVE_STRLCPY - -// -// 'cfStrFormatd()' - Format a floating-point number. -// - -char * // O - Pointer to end of string -cfStrFormatd(char *buf, // I - String - char *bufend, // I - End of string buffer - double number, // I - Number to format - struct lconv *loc) // I - Locale data -{ - char *bufptr, // Pointer into buffer - temp[1024], // Temporary string - *tempdec, // Pointer to decimal point - *tempptr; // Pointer into temporary string - const char *dec; // Decimal point - int declen; // Length of decimal point - - - // - // Format the number using the "%.12f" format and then eliminate - // unnecessary trailing 0's. - // - - snprintf(temp, sizeof(temp), "%.12f", number); - for (tempptr = temp + strlen(temp) - 1; - tempptr > temp && *tempptr == '0'; - *tempptr-- = '\0'); - - // - // Next, find the decimal point... - // - - if (loc && loc->decimal_point) - { - dec = loc->decimal_point; - declen = (int)strlen(dec); - } - else - { - dec = "."; - declen = 1; - } - - if (declen == 1) - tempdec = strchr(temp, *dec); - else - tempdec = strstr(temp, dec); - - // - // Copy everything up to the decimal point... - // - - if (tempdec) - { - for (tempptr = temp, bufptr = buf; - tempptr < tempdec && bufptr < bufend; - *bufptr++ = *tempptr++); - - tempptr += declen; - - if (*tempptr && bufptr < bufend) - { - *bufptr++ = '.'; - - while (*tempptr && bufptr < bufend) - *bufptr++ = *tempptr++; - } - - *bufptr = '\0'; - } - else - { - strlcpy(buf, temp, (size_t)(bufend - buf + 1)); - bufptr = buf + strlen(buf); - } - - return (bufptr); -} - - -int -cfCompareResolutions(void *resolution_a, - void *resolution_b, - void *user_data) -{ - cf_res_t *res_a = (cf_res_t *)resolution_a; - cf_res_t *res_b = (cf_res_t *)resolution_b; - int i, a, b; - - // Compare the pixels per square inch - a = res_a->x * res_a->y; - b = res_b->x * res_b->y; - i = (a > b) - (a < b); - if (i) return (i); - - // Compare how much the pixel shape deviates from a square, the - // more, the worse - a = 100 * res_a->y / res_a->x; - if (a > 100) a = 10000 / a; - b = 100 * res_b->y / res_b->x; - if (b > 100) b = 10000 / b; - return ((a > b) - (a < b)); -} - -void * -cfCopyResolution(void *resolution, - void *user_data) -{ - cf_res_t *res = (cf_res_t *)resolution; - cf_res_t *copy; - - copy = (cf_res_t *)calloc(1, sizeof(cf_res_t)); - if (copy) - { - copy->x = res->x; - copy->y = res->y; - } - - return copy; -} - -void -cfFreeResolution(void *resolution, - void *user_data) -{ - cf_res_t *res = (cf_res_t *)resolution; - - if (res) free(res); -} - -cups_array_t * -cfNewResolutionArray() -{ - return (cupsArrayNew3(cfCompareResolutions, NULL, NULL, 0, - cfCopyResolution, cfFreeResolution)); -} - -cf_res_t * -cfNewResolution(int x, - int y) -{ - cf_res_t *res = (cf_res_t *)calloc(1, sizeof(cf_res_t)); - if (res) - { - res->x = x; - res->y = y; - } - return (res); -} - -// Read a single resolution from an IPP attribute, take care of -// obviously wrong entries (printer firmware bugs), ignoring -// resolutions of less than 60 dpi in at least one dimension and -// fixing Brother's "600x2dpi" resolutions. -cf_res_t * -cfIPPResToResolution(ipp_attribute_t *attr, - int index) -{ - cf_res_t *res = NULL; - int x = 0, y = 0; - ipp_res_t units; - - if (attr) - { - ipp_tag_t tag = ippGetValueTag(attr); - int count = ippGetCount(attr); - - if (tag == IPP_TAG_RESOLUTION && index < count) - { - x = ippGetResolution(attr, index, &y, &units); - if (units == IPP_RES_PER_CM) - { - x = (int)(x * 2.54); - y = (int)(y * 2.54); - } - if (y == 2) y = x; // Brother quirk ("600x2dpi") - if (x >= 60 && y >= 60) - res = cfNewResolution(x, y); - } - } - - return (res); -} - -cups_array_t * -cfIPPAttrToResolutionArray(ipp_attribute_t *attr) -{ - cups_array_t *res_array = NULL; - cf_res_t *res; - int i; - - if (attr) - { - ipp_tag_t tag = ippGetValueTag(attr); - int count = ippGetCount(attr); - - if (tag == IPP_TAG_RESOLUTION && count > 0) - { - res_array = cfNewResolutionArray(); - if (res_array) - { - for (i = 0; i < count; i ++) - if ((res = cfIPPResToResolution(attr, i)) != NULL) - { - if (cupsArrayFind(res_array, res) == NULL) - cupsArrayAdd(res_array, res); - cfFreeResolution(res, NULL); - } - } - if (cupsArrayCount(res_array) == 0) - { - cupsArrayDelete(res_array); - res_array = NULL; - } - } - } - - return (res_array); -} - -// Build up an array of common resolutions and most desirable default -// resolution from multiple arrays of resolutions with an optional -// default resolution. -// Call this function with each resolution array you find as "new", and -// in "current" an array of the common resolutions will be built up. -// You do not need to create an empty array for "current" before -// starting. Initialize it with NULL. -// "current_default" holds the default resolution of the array "current". -// It will get replaced by "new_default" if "current_default" is either -// NULL or a resolution which is not in "current" any more. -// "new" and "new_default" will be deleted/freed and set to NULL after -// each, successful or unsuccssful operation. -// Note that when calling this function the addresses of the pointers -// to the resolution arrays and default resolutions have to be given -// (call by reference) as all will get modified by the function. - -int // 1 on success, 0 on failure -cfJoinResolutionArrays(cups_array_t **current, - cups_array_t **new_arr, - cf_res_t **current_default, - cf_res_t **new_default) -{ - cf_res_t *res; - int retval; - - if (current == NULL || new_arr == NULL || *new_arr == NULL || - cupsArrayCount(*new_arr) == 0) - { - retval = 0; - goto finish; - } - - if (*current == NULL) - { - // We are adding the very first resolution array, simply make it - // our common resolutions array - *current = *new_arr; - if (current_default) - { - if (*current_default) - free(*current_default); - *current_default = (new_default ? *new_default : NULL); - } - return 1; - } - else if (cupsArrayCount(*current) == 0) - { - retval = 1; - goto finish; - } - - // Dry run: Check whether the two arrays have at least one resolution - // in common, if not, do not touch the original array - for (res = cupsArrayFirst(*current); - res; res = cupsArrayNext(*current)) - if (cupsArrayFind(*new_arr, res)) - break; - - if (res) - { - // Reduce the original array to the resolutions which are in both - // the original and the new array, at least one resolution will - // remain. - for (res = cupsArrayFirst(*current); - res; res = cupsArrayNext(*current)) - if (!cupsArrayFind(*new_arr, res)) - cupsArrayRemove(*current, res); - if (current_default) - { - // Replace the current default by the new one if the current default - // is not in the array any more or if it is NULL. If the new default - // is not in the list or NULL in such a case, set the current default - // to NULL - if (*current_default && !cupsArrayFind(*current, *current_default)) - { - free(*current_default); - *current_default = NULL; - } - if (*current_default == NULL && new_default && *new_default && - cupsArrayFind(*current, *new_default)) - *current_default = cfCopyResolution(*new_default, NULL); - } - retval = 1; - } - else - retval = 0; - - finish: - if (new_arr && *new_arr) - { - cupsArrayDelete(*new_arr); - *new_arr = NULL; - } - if (new_default && *new_default) - { - free(*new_default); - *new_default = NULL; - } - - return (retval); -} - - -// -// 'pwg_compare_sizes()' - Compare two media sizes... -// - -static int // O - Result of comparison -pwg_compare_sizes(cups_size_t *a, // I - First media size - cups_size_t *b) // I - Second media size -{ - return (strcmp(a->media, b->media)); -} - - -// -// 'pwg_copy_size()' - Copy a media size. -// - -static cups_size_t * // O - New media size -pwg_copy_size(cups_size_t *size) // I - Media size to copy -{ - cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t)); - // New media size - - if (newsize) - memcpy(newsize, size, sizeof(cups_size_t)); - - return (newsize); -} - - -int // O - 1: Requested page size supported - // 2: Requested page size supported - // when rotated by 90 degrees - // 0: No page size requested - // -1: Requested size unsupported -cfGetPageDimensions(ipp_t *printer_attrs, // I - Printer attributes - ipp_t *job_attrs, // I - Job attributes - int num_options, // I - Number of options - cups_option_t *options, // I - Options - cups_page_header2_t *header, // I - Raster page header - int transverse_fit, // I - Accept transverse fit? - float *width, // O - Width (in pt, 1/72 inches) - float *height, // O - Height - float *left, // O - Left margin - float *bottom, // O - Bottom margin - float *right, // O - Right margin - float *top, // O - Top margin - char *name, // O - Page size name - ipp_t **media_col_entry)// O - media-col-database record of - // match -{ - int i; - const char *attr_name; - char size_name_buf[IPP_MAX_NAME + 1]; - int size_requested = 0; - const char * const media_size_attr_names[] = - { - "Jmedia-col", - "Jmedia-size", - "Jmedia", - "JPageSize", - "JMediaSize", - "J", // A raster header with media dimensions - "jmedia-col", - "jmedia-size", - "jmedia", - "jPageSize", - "jMediaSize", - "j", // A raster header with media dimensions - "Dmedia-col-default", - "Dmedia-default", - }; - - - if (name == NULL) - name = size_name_buf; - name[0] = '\0'; - - if (media_col_entry) - *media_col_entry = NULL; - - // - // Media from job_attrs and options, defaults from printer_attrs... - // - - // Go through all job attributes and options which could contain the - // page size, afterwards go through the page size defaults in the - // printer attributes - for (i = 0; - i < sizeof(media_size_attr_names) / sizeof(media_size_attr_names[0]); - i ++) - { - ipp_attribute_t *attr = NULL; // Job attribute - char valstr[8192]; // Attribute value string - const char *value = NULL; // Option value - const char *name_ptr = NULL; // Pointer to page size name - int num_media_col = 0; // Number of media-col values - cups_option_t *media_col = NULL; // media-col values - int ipp_width = 0, - ipp_height = 0, - ipp_left = -1, - ipp_bottom = -1, - ipp_right = -1, - ipp_top = -1; - - attr_name = media_size_attr_names[i]; - if (*attr_name == 'J' || - (transverse_fit && *attr_name == 'j')) // Job attribute/option - { - if (*(attr_name + 1) == '\0') - { - if (header) - { - // Raster header - if (header->cupsPageSize[0] > 0.0) - ipp_width = (int)(header->cupsPageSize[0] * 2540.0 / 72.0); - else if (header->PageSize[0] > 0) - ipp_width = (int)(header->PageSize[0] * 2540 / 72); - if (header->cupsPageSize[1] > 0.0) - ipp_height = (int)(header->cupsPageSize[1] * 2540.0 / 72.0); - else if (header->PageSize[1] > 0) - ipp_height = (int)(header->PageSize[1] * 2540 / 72); - if (header->ImagingBoundingBox[3] > 0) - { - if (header->cupsImagingBBox[0] >= 0.0) - ipp_left = (int)(header->cupsImagingBBox[0] * 2540.0 / 72.0); - else if (header->ImagingBoundingBox[0] >= 0) - ipp_left = (int)(header->ImagingBoundingBox[0] * 2540 / 72); - if (header->cupsImagingBBox[1] >= 0.0) - ipp_bottom = (int)(header->cupsImagingBBox[1] * 2540.0 / 72.0); - else if (header->ImagingBoundingBox[1] >= 0) - ipp_bottom = (int)(header->ImagingBoundingBox[1] * 2540 / 72); - if (header->cupsImagingBBox[2] > 0.0) - ipp_right = ipp_width - - (int)(header->cupsImagingBBox[2] * 2540.0 / 72.0); - else if (header->ImagingBoundingBox[2] > 0) - ipp_right = ipp_width - - (int)(header->ImagingBoundingBox[2] * 2540 / 72); - if (header->cupsImagingBBox[3] > 0.0) - ipp_top = ipp_height - - (int)(header->cupsImagingBBox[3] * 2540.0 / 72.0); - else if (header->ImagingBoundingBox[3] > 0) - ipp_top = ipp_height - - (int)(header->ImagingBoundingBox[3] * 2540 / 72); - } - else - ipp_left = ipp_bottom = ipp_right = ipp_top = 0; - } - else - continue; - } - else if ((attr = ippFindAttribute(job_attrs, attr_name + 1, - IPP_TAG_ZERO)) != NULL) - { - // String from IPP attribute - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - } - else if ((value = cupsGetOption(attr_name + 1, num_options, - options)) == NULL) - continue; - } - else if (*attr_name == 'D') // Printer default - { - if (*(attr_name + 1)) - { - if ((attr = ippFindAttribute(printer_attrs, attr_name + 1, - IPP_TAG_ZERO)) != NULL) - { - // String from IPP attribute - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - } - else - continue; - } - else - continue; - } - else - continue; - - if (value) - { - if (*value == '{') - { - // - // String is a dictionary -> "media-col" value... - // - - num_media_col = cupsParseOptions(value, 0, &media_col); - - // Actual size in dictionary? - if ((value = cupsGetOption("media-size", num_media_col, media_col)) - != NULL) - { - int num_media_size; // Number of media-size values - cups_option_t *media_size; // media-size values - const char *x_dimension, // x-dimension value - *y_dimension; // y-dimension value - - num_media_size = cupsParseOptions(value, 0, &media_size); - - if ((x_dimension = cupsGetOption("x-dimension", num_media_size, media_size)) != NULL && (y_dimension = cupsGetOption("y-dimension", num_media_size, media_size)) != NULL) - { - ipp_width = atoi(x_dimension); - ipp_height = atoi(y_dimension); - } - - cupsFreeOptions(num_media_size, media_size); - } - // Name in dictionary? Use only if actual dimensions are not supplied - if ((ipp_width <= 0 || ipp_height <= 0) && - (name_ptr = cupsGetOption("media-size-name", - num_media_col, media_col)) == NULL) - { - cupsFreeOptions(num_media_col, media_col); - continue; - } - - // Grab margins from media-col - if ((value = cupsGetOption("media-left-margin", - num_media_col, media_col)) - != NULL) - ipp_left = atoi(value); - if ((value = cupsGetOption("media-bottom-margin", - num_media_col, media_col)) - != NULL) - ipp_bottom = atoi(value); - if ((value = cupsGetOption("media-right-margin", - num_media_col, media_col)) - != NULL) - ipp_right = atoi(value); - if ((value = cupsGetOption("media-top-margin", - num_media_col, media_col)) - != NULL) - ipp_top = atoi(value); - } - else - { - // - // String is not dictionary, check also if it contains commas (list - // of media properties supplied via "media" CUPS option - // - - char *ptr; - name_ptr = value; - if (strchr(value, ',')) - { - // Comma-separated list of media properties, supplied with "media" - // CUPS option - if (value != valstr) - { - // Copy string for further manipulation - strlcpy(valstr, value, sizeof(valstr)); - value = valstr; - name_ptr = value; - } - for (ptr = (char *)value; *ptr;) - { - ptr ++; - if (*ptr == ',' || *ptr == '\0') - { - // End of item name - if (*ptr == ',') - { - *ptr = '\0'; - ptr ++; - } - // Find PWG media entry for the name, if we find one, the name - // is actually a page size name - if (pwgMediaForPWG(name_ptr) || - pwgMediaForPPD(name_ptr) || - pwgMediaForLegacy(name_ptr)) - // This is a page size name - break; - else if (*ptr) - // Next item - name_ptr = ptr; - else - // No further item - name_ptr = NULL; - } - } - } - } - } - - // Get name from media - if (name_ptr) - { - if (ipp_left == 0 && ipp_bottom == 0 && - ipp_right == 0 && ipp_top == 0) - snprintf(name, IPP_MAX_NAME, "%.29s.Borderless", name_ptr); - else - strlcpy(name, name_ptr, IPP_MAX_NAME); - } - - // Landscape/Transverse fit - if (*attr_name == 'j') // Only job attributes/options - { - int swap; - - swap = ipp_width; - ipp_width = ipp_height; - ipp_height = swap; - - swap = ipp_left; - ipp_left = ipp_top; - ipp_top = ipp_right; - ipp_right = ipp_bottom; - ipp_bottom = swap; - } - - cupsFreeOptions(num_media_col, media_col); - - // We have a valid request for a page size - if (*attr_name == 'J' || *attr_name == 'j') - size_requested = 1; - - // Validate collected information - // If we have a size, we use the size as search term (name = "" then), - // if we have no size but a name, use the name, always pass in margins - // if available - cfGenerateSizes(printer_attrs, CF_GEN_SIZES_SEARCH, NULL, NULL, - &ipp_width, &ipp_height, - &ipp_left, &ipp_bottom, &ipp_right, &ipp_top, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, name, - media_col_entry); - - // Return resulting numbers - if (ipp_width > 0 && ipp_height > 0 && - ipp_left >= 0 && ipp_bottom >= 0 && - ipp_right >= 0 && ipp_top >= 0) - { - if (width) - *width = ipp_width * 72.0 / 2540.0; - if (height) - *height = ipp_height * 72.0 / 2540.0; - - if (left) - *left = ipp_left * 72.0 / 2540.0; - if (bottom) - *bottom = ipp_bottom * 72.0 / 2540.0; - if (right) - *right = ipp_right * 72.0 / 2540.0; - if (top) - *top = ipp_top * 72.0 / 2540.0; - - return (*attr_name == 'J' ? 1 : - (*attr_name == 'j' ? 2 : - (size_requested ? -1 : 0))); - } - else if (media_col_entry) - *media_col_entry = NULL; - } - return (size_requested ? -1 : 0); -} - - -void -cfGenerateSizes(ipp_t *response, - cf_gen_sizes_mode_t mode, - cups_array_t **sizes, - ipp_attribute_t **defattr, - int *width, - int *length, - int *left, - int *bottom, - int *right, - int *top, - int *min_width, - int *min_length, - int *max_width, - int *max_length, - int *custom_left, - int *custom_bottom, - int *custom_right, - int *custom_top, - char *size_name, - ipp_t **media_col_entry) -{ - ipp_attribute_t *default_attr, - *attr, // xxx-supported - *x_dim, *y_dim, // Media dimensions - *name; // Media size name - ipp_t *media_col, // Media collection - *media_size; // Media size collection - int i, x = 0, y = 0, count = 0; - pwg_media_t *pwg, *pwg_by_name; // PWG media size - int local_min_width, local_min_length, - local_max_width, local_max_length; - int local_left, local_right, local_bottom, local_top; - ipp_attribute_t *margin; // media-xxx-margin attribute - const char *psname; - const char *entry_name; - char size_name_buf[IPP_MAX_NAME + 1] = ""; - pwg_media_t *search = NULL; - int search_width = 0, - search_length = 0, - search_left = -1, - search_bottom = -1, - search_right = -1, - search_top = -1, - borderless = 0; - long long min_border_mismatch = LLONG_MAX, - border_mismatch; - - - if (media_col_entry) - *media_col_entry = NULL; - - if (custom_left == NULL) - custom_left = &local_left; - if ((attr = ippFindAttribute(response, "media-left-margin-supported", - IPP_TAG_INTEGER)) != NULL) - { - for (i = 1, *custom_left = ippGetInteger(attr, 0), - count = ippGetCount(attr); - i < count; i ++) - if (ippGetInteger(attr, i) < *custom_left) - *custom_left = ippGetInteger(attr, i); - } - else - *custom_left = 635; - - if (custom_bottom == NULL) - custom_bottom = &local_bottom; - if ((attr = ippFindAttribute(response, "media-bottom-margin-supported", - IPP_TAG_INTEGER)) != NULL) - { - for (i = 1, *custom_bottom = ippGetInteger(attr, 0), - count = ippGetCount(attr); - i < count; i ++) - if (ippGetInteger(attr, i) < *custom_bottom) - *custom_bottom = ippGetInteger(attr, i); - } - else - *custom_bottom = 1270; - - if (custom_right == NULL) - custom_right = &local_right; - if ((attr = ippFindAttribute(response, "media-right-margin-supported", - IPP_TAG_INTEGER)) != NULL) - { - for (i = 1, *custom_right = ippGetInteger(attr, 0), - count = ippGetCount(attr); - i < count; i ++) - if (ippGetInteger(attr, i) < *custom_right) - *custom_right = ippGetInteger(attr, i); - } - else - *custom_right = 635; - - if (custom_top == NULL) - custom_top = &local_top; - if ((attr = ippFindAttribute(response, "media-top-margin-supported", - IPP_TAG_INTEGER)) != NULL) - { - for (i = 1, *custom_top = ippGetInteger(attr, 0), - count = ippGetCount(attr); - i < count; i ++) - if (ippGetInteger(attr, i) < *custom_top) - *custom_top = ippGetInteger(attr, i); - } - else - *custom_top = 1270; - - if (mode != CF_GEN_SIZES_DEFAULT) - { - if (min_width == NULL) - min_width = &local_min_width; - *min_width = 0; - if (min_length == NULL) - min_length = &local_min_length; - *min_length = 0; - if (max_width == NULL) - max_width = &local_max_width; - *max_width = 0; - if (max_length == NULL) - max_length = &local_max_length; - *max_length = 0; - } - - if (size_name == NULL) - { - size_name = size_name_buf; - size_name[0] = '\0'; - } - if (mode == CF_GEN_SIZES_DEFAULT) - size_name[0] = '\0'; - if (defattr == NULL && mode == CF_GEN_SIZES_DEFAULT) - defattr = &default_attr; - if (defattr && - (*defattr = ippFindAttribute(response, "media-col-default", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - if (mode == CF_GEN_SIZES_DEFAULT && - (attr = ippFindAttribute(ippGetCollection(*defattr, 0), "media-size", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - media_size = ippGetCollection(attr, 0); - x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER); - y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER); - - if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0), - "media-bottom-margin", IPP_TAG_INTEGER)) - != NULL) - local_bottom = ippGetInteger(margin, 0); - else - local_bottom = *custom_bottom; - if (bottom) - *bottom = local_bottom; - - if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0), - "media-left-margin", IPP_TAG_INTEGER)) - != NULL) - local_left = ippGetInteger(margin, 0); - else - local_left = *custom_left; - if (left) - *left = local_left; - - if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0), - "media-right-margin", IPP_TAG_INTEGER)) - != NULL) - local_right = ippGetInteger(margin, 0); - else - local_right = *custom_right; - if (right) - *right = local_right; - - if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0), - "media-top-margin", IPP_TAG_INTEGER)) - != NULL) - local_top = ippGetInteger(margin, 0); - else - local_top = *custom_top; - if (top) - *top = local_top; - - if (x_dim && y_dim) - { - x = ippGetInteger(x_dim, 0); - y = ippGetInteger(y_dim, 0); - if (x > 0 && y > 0 && - (pwg = pwgMediaForSize(x, y)) != NULL) - { - psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg); - if (local_bottom == 0 && local_left == 0 && - local_right == 0 && local_top == 0) - snprintf(size_name, IPP_MAX_NAME, "%s.Borderless", psname); - else - strlcpy(size_name, psname, IPP_MAX_NAME); - } - } - } - } - if (mode == CF_GEN_SIZES_DEFAULT && - (pwg = - pwgMediaForPWG(ippGetString(ippFindAttribute(response, - "media-default", - IPP_TAG_ZERO), 0, - NULL))) != NULL) - { - psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg); - strlcpy(size_name, psname, IPP_MAX_NAME); - if (x <= 0 || y <= 0) - { - x = pwg->width; - y = pwg->length; - } - } - - if (mode == CF_GEN_SIZES_DEFAULT) - { - // Output the default page size dimensions, 0 if no valid size found - if (!size_name[0]) - strlcpy(size_name, "Unknown", IPP_MAX_NAME); - if (width) - { - if (x > 0) - *width = x; - else - *width = 0; - } - if (length) - { - if (y > 0) - *length = y; - else - *length = 0; - } - } - else - { - // Find the dimensions for the page size name we got as search term - char *ptr; - int is_transverse = (strcasestr(size_name, ".Transverse") ? 1 : 0); - if (strcasestr(size_name, ".Fullbleed") || - strcasestr(size_name, ".Borderless") || - strcasestr(size_name, ".FB")) - mode = CF_GEN_SIZES_SEARCH_BORDERLESS_ONLY; - if (size_name != size_name_buf) - strlcpy(size_name_buf, size_name, IPP_MAX_NAME); - if ((ptr = strchr(size_name_buf, '.')) != NULL && - strncasecmp(size_name_buf, "Custom.", 7) != 0) - *ptr = '\0'; - if ((search = pwgMediaForPWG(size_name_buf)) == NULL) - if ((search = pwgMediaForPPD(size_name_buf)) == NULL) - search = pwgMediaForLegacy(size_name_buf); - if (search != NULL) - { - // Set the appropriate dimensions - if (is_transverse) - { - search_width = search->length; - search_length = search->width; - } - else - { - search_width = search->width; - search_length = search->length; - } - } - else - { - // Set the dimensions if we search by dimensions - if (width) - search_width = *width; - if (length) - search_length = *length; - } - if (search_width <= 0 || search_length <= 0) - { - // No valid search dimensions, de-activate searching and set 0 as - // result - mode = CF_GEN_SIZES_DEFAULT; - if (width) - *width = 0; - if (length) - *length = 0; - } - else - { - // Check whether we have margin info so that we can search for a - // size with similar/the same margins, otherwise set the margins - // -1 to pick the first entry from the list which fits the size - // dimensions (if there are variants of a size, the first entry - // is usually the standard size) - if (left && *left >= 0) - search_left = *left; - else - search_left = -1; - if (bottom && *bottom >= 0) - search_bottom = *bottom; - else - search_bottom = -1; - if (right && *right >= 0) - search_right = *right; - else - search_right = -1; - if (top && *top >= 0) - search_top = *top; - else - search_top = -1; - } - } - - if (mode == CF_GEN_SIZES_DEFAULT && - !sizes && !min_width && !max_width && !min_length && !max_length) - return; - - if (sizes) - *sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0, - (cups_acopy_func_t)pwg_copy_size, - (cups_afree_func_t)free); - - if ((attr = ippFindAttribute(response, "media-col-database", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - cups_size_t temp, temp_by_name; // Current size - - media_col = ippGetCollection(attr, i); - media_size = - ippGetCollection(ippFindAttribute(media_col, "media-size", - IPP_TAG_BEGIN_COLLECTION), 0); - - // These are the numeric paper dimensions explicitly mentioned - // in this media entry. if we were called in a retro-fitting - // setup via a ppdFilter...() wrapper filter function of libppd, - // these dimensions can deviate from the paper dimensions which - // the PWG-style page size name in the same entry suggests. In - // this case we match both sizes against the size requested for - // the job and consider the entry as matching if one of the two - // sizes matches. In this case the entry gets included in all - // entries which are selected by the closest fit of the margins. - // - // We do this as some PPD files (especially of HPLIP) contain - // page size entries which are variants of a standard size with - // the base name of a standard size (like "A4.Borderless", base - // name "A4") but different dimensions. - // - // Especially there are larger dimensions for borderless, for - // overspraying over the borders of the sheet so that there will - // be no faint white borders if the sheet is a little - // mis-aligned. - // - // So if such overspraying borderless size entry is present and - // has zero margins while the standard size entry has regular - // margins, this entry will get automatically selected if - // borderless printing (standard size name or dimensions plus - // zero margins) is selected. - - x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO); - y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO); - // Move "if" for custom size parameters here - //if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || - // ippGetValueTag(y_dim) == IPP_TAG_RANGE) - //{ - pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), - ippGetInteger(y_dim, 0)); - name = ippFindAttribute(media_col, "media-size-name", - IPP_TAG_ZERO); - pwg_by_name = NULL; - if (name) - { - entry_name = ippGetString(name, 0, NULL); - if (entry_name) - pwg_by_name = pwgMediaForPWG(entry_name); - } - - if (pwg || pwg_by_name) - { - if (!sizes && mode == CF_GEN_SIZES_DEFAULT) - continue; - - if (pwg) - { - temp.width = pwg->width; - temp.length = pwg->length; - } - else - temp.width = temp.length = 0; - - if (pwg_by_name) - { - temp_by_name.width = pwg_by_name->width; - temp_by_name.length = pwg_by_name->length; - } - else - temp_by_name.width = temp_by_name.length = 0; - - if ((margin = ippFindAttribute(media_col, "media-bottom-margin", - IPP_TAG_INTEGER)) != NULL) - temp.bottom = ippGetInteger(margin, 0); - else - temp.bottom = *custom_bottom; - - if ((margin = ippFindAttribute(media_col, "media-left-margin", - IPP_TAG_INTEGER)) != NULL) - temp.left = ippGetInteger(margin, 0); - else - temp.left = *custom_left; - - if ((margin = ippFindAttribute(media_col, "media-right-margin", - IPP_TAG_INTEGER)) != NULL) - temp.right = ippGetInteger(margin, 0); - else - temp.right = *custom_right; - - if ((margin = ippFindAttribute(media_col, "media-top-margin", - IPP_TAG_INTEGER)) != NULL) - temp.top = ippGetInteger(margin, 0); - else - temp.top = *custom_top; - - psname = (pwg_by_name ? - (pwg_by_name->ppd != NULL ? - pwg_by_name->ppd : pwg_by_name->pwg) : - (pwg->ppd != NULL ? pwg->ppd : pwg->pwg)); - if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && - temp.top == 0) - { - snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname); - borderless = 1; - } - else - { - strlcpy(temp.media, psname, sizeof(temp.media)); - borderless = 0; - } - - // Check whether this size matches our search criteria - if (mode != CF_GEN_SIZES_DEFAULT && - min_border_mismatch > 0 && - search_width > 0 && search_length > 0 && - ((abs(search_width - temp_by_name.width) < 70 && // 2pt - abs(search_length - temp_by_name.length) < 70) || // 2pt - (abs(search_width - temp.width) < 70 && // 2pt - abs(search_length - temp.length) < 70))) // 2pt - { - // Found size with the correct dimensions - int match = 0; - if (mode == CF_GEN_SIZES_SEARCH_BORDERLESS_ONLY && - borderless == 1) - { - // We search only for borderless sizes and have found a match - border_mismatch = 0; - min_border_mismatch = 0; - if (media_col_entry) - *media_col_entry = media_col; - match = 1; - } - else if (mode == CF_GEN_SIZES_SEARCH) - { - // We search a size in general, borders are accepted. find the - // best match in terms of border size - border_mismatch = - (long long)(search_left < 0 ? 1 : - (abs(search_left - temp.left) + 1)) * - (long long)(search_bottom < 0 ? 1 : - (abs(search_bottom - temp.bottom) + 1)) * - (long long)(search_right < 0 ? 1 : - (abs(search_right - temp.right) + 1)) * - (long long)(search_top < 0 ? 1 : - (abs(search_top - temp.top) + 1)); - if (border_mismatch < min_border_mismatch) - { - min_border_mismatch = border_mismatch; - if (media_col_entry) - *media_col_entry = media_col; - match = 1; - } - } - if (match) - { - if (width) - *width = temp.width; - if (length) - *length = temp.length; - if (left) - *left = temp.left; - if (bottom) - *bottom = temp.bottom; - if (right) - *right = temp.right; - if (top) - *top = temp.top; - strlcpy(size_name, temp.media, IPP_MAX_NAME); - } - } - - // Add size to list - if (sizes && !cupsArrayFind(*sizes, &temp)) - cupsArrayAdd(*sizes, &temp); - - } - else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || - ippGetValueTag(y_dim) == IPP_TAG_RANGE) - { - // - // Custom size - record the min/max values... - // - - int lower, upper; // Range values - - if (ippGetValueTag(x_dim) == IPP_TAG_RANGE) - lower = ippGetRange(x_dim, 0, &upper); - else - lower = upper = ippGetInteger(x_dim, 0); - - if (min_width && lower < *min_width) - *min_width = lower; - if (max_width && upper > *max_width) - *max_width = upper; - - if (ippGetValueTag(y_dim) == IPP_TAG_RANGE) - lower = ippGetRange(y_dim, 0, &upper); - else - lower = upper = ippGetInteger(y_dim, 0); - - if (min_length && lower < *min_length) - *min_length = lower; - if (max_length && upper > *max_length) - *max_length = upper; - } - } - if (min_border_mismatch < LLONG_MAX) - { - // If we have found a matching page size in the media-col-database - // we stop searching - min_border_mismatch = 0; - } - } - if ((attr = ippFindAttribute(response, "media-size-supported", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - cups_size_t temp; // Current size - - media_size = ippGetCollection(attr, i); - x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO); - y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO); - pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), - ippGetInteger(y_dim, 0)); - - if (pwg) - { - if (!sizes && mode == CF_GEN_SIZES_DEFAULT) - continue; - - temp.width = pwg->width; - temp.length = pwg->length; - temp.left = *custom_left; - temp.bottom = *custom_bottom; - temp.right = *custom_right; - temp.top = *custom_top; - - psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg); - if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && - temp.top == 0) - { - snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname); - borderless = 1; - } - else - { - strlcpy(temp.media, psname, sizeof(temp.media)); - borderless = 0; - } - - // Check whether this size matches our search criteria - if (mode != CF_GEN_SIZES_DEFAULT && - min_border_mismatch > 0 && - search_width > 0 && search_length > 0 && - abs(search_width - temp.width) < 70 && // 2pt - abs(search_length - temp.length) < 70 ) // 2pt - { - // Found size with the correct dimensions - if (mode != CF_GEN_SIZES_SEARCH_BORDERLESS_ONLY || - borderless == 1) - { - // We accept the entry just by the size dimensions as - // "media-size-supported" has no per-size margin info - if (width) - *width = temp.width; - if (length) - *length = temp.length; - if (left) - *left = temp.left; - if (bottom) - *bottom = temp.bottom; - if (right) - *right = temp.right; - if (top) - *top = temp.top; - strlcpy(size_name, temp.media, IPP_MAX_NAME); - // Found it, stop searching - min_border_mismatch = 0; - } - } - - if (sizes && !cupsArrayFind(*sizes, &temp)) - cupsArrayAdd(*sizes, &temp); - } - else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || - ippGetValueTag(y_dim) == IPP_TAG_RANGE) - { - // - // Custom size - record the min/max values... - // - - int lower, upper; // Range values - - if (ippGetValueTag(x_dim) == IPP_TAG_RANGE) - lower = ippGetRange(x_dim, 0, &upper); - else - lower = upper = ippGetInteger(x_dim, 0); - - if (min_width && lower < *min_width) - *min_width = lower; - if (max_width && upper > *max_width) - *max_width = upper; - - if (ippGetValueTag(y_dim) == IPP_TAG_RANGE) - lower = ippGetRange(y_dim, 0, &upper); - else - lower = upper = ippGetInteger(y_dim, 0); - - if (min_length && lower < *min_length) - *min_length = lower; - if (max_length && upper > *max_length) - *max_length = upper; - } - } - } - if ((attr = ippFindAttribute(response, "media-supported", IPP_TAG_ZERO)) - != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - const char *pwg_size = ippGetString(attr, i, NULL); - // PWG size name - cups_size_t temp, *temp2; // Current size, found size - - if ((pwg = pwgMediaForPWG(pwg_size)) != NULL) - { - if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max.")) - { - if (max_width && pwg->width > *max_width) - *max_width = pwg->width; - if (max_length && pwg->length > *max_length) - *max_length = pwg->length; - } - else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min.")) - { - if (min_width && pwg->width < *min_width) - *min_width = pwg->width; - if (min_length && pwg->length < *min_length) - *min_length = pwg->length; - } - else - { - if (!sizes && mode == CF_GEN_SIZES_DEFAULT) - continue; - - temp.width = pwg->width; - temp.length = pwg->length; - temp.left = *custom_left; - temp.bottom = *custom_bottom; - temp.right = *custom_right; - temp.top = *custom_top; - - psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg); - if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && - temp.top == 0) - snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname); - else - strlcpy(temp.media, psname, sizeof(temp.media)); - - // Add the printer's original IPP name to an already found size - if (sizes) - { - if ((temp2 = cupsArrayFind(*sizes, &temp)) != NULL) - { - snprintf(temp2->media + strlen(temp2->media), - sizeof(temp2->media) - strlen(temp2->media), - " %s", pwg_size); - // Check if we have also a borderless version of the size and add - // the original IPP name also there - snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname); - if ((temp2 = cupsArrayFind(*sizes, &temp)) != NULL) - snprintf(temp2->media + strlen(temp2->media), - sizeof(temp2->media) - strlen(temp2->media), - " %s", pwg_size); - } - else - cupsArrayAdd(*sizes, &temp); - } - } - } - } - } - if (mode != CF_GEN_SIZES_DEFAULT && min_border_mismatch > 0 && - search_width > 0 && search_length > 0 && - *min_width >= 0 && *min_length >= 0 && - *max_width >= *min_width && *max_length >= *min_length && - *custom_left >= 0 && *custom_bottom >= 0 && - *custom_right >= 0 && *custom_top >= 0) - { - // Do we have support for a custom page size and have valid size ranges for - // it? Check whether the size we are searching for can go as custom size - if (search_width >= *min_width - 70 && // 2pt - search_width <= *max_width + 70 && // 2pt - search_length >= *min_length - 70 && // 2pt - search_length <= *max_length + 70) // 2pt - { - if (width) - *width = (search_width < *min_width ? *min_width : - (search_width > *max_width ? *max_width : search_width)); - if (length) - *length = (search_length < *min_length ? *min_length : - (search_length > *max_length ? *max_length : search_length)); - if (left) *left = *custom_left; - if (bottom) *bottom = *custom_bottom; - if (right) *right = *custom_right; - if (top) *top = *custom_top; - min_border_mismatch = 0; - } - } - if (mode != CF_GEN_SIZES_DEFAULT && min_border_mismatch > 0) - { - // Size not found - if (width) *width = 0; - if (length) *length = 0; - if (left) *left = -1; - if (bottom) *bottom = -1; - if (right) *right = -1; - if (top) *top = -1; - } -} diff --git a/cupsfilters/ipp.h b/cupsfilters/ipp.h deleted file mode 100644 index fd143dd68..000000000 --- a/cupsfilters/ipp.h +++ /dev/null @@ -1,206 +0,0 @@ -// -// IPP-related functions for libcupsfilters. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_IPP_H_ -# define _CUPS_FILTERS_IPP_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - -// -// Include necessary headers... -// - -#include - -#include "filter.h" -#include -#include -#include -#include -#include - -#if defined(WIN32) || defined(__EMX__) -# include -#else -# include -# include -#endif // WIN32 || __EMX__ - -#include -#include -#include - -#define CF_GET_PRINTER_ATTRIBUTES_LOGSIZE 4 * 65536 -#define CF_GET_PRINTER_ATTRIBUTES_MAX_OUTPUT_LEN 8192 -#define CF_GET_PRINTER_ATTRIBUTES_MAX_URI_LEN 2048 - -extern char cf_get_printer_attributes_log[CF_GET_PRINTER_ATTRIBUTES_LOGSIZE]; - - -// -// Types... -// - -// Enum of possible driverless options -enum cf_driverless_support_modes_e -{ - CF_DRVLESS_CHECKERR, // Unable to get get-printer-attributes response*/ - CF_DRVLESS_FULL, // Standard IPP Everywhere support, works with - // 'everywhere' model - CF_DRVLESS_IPP11, // Driverless support via IPP 1.1 request - CF_DRVLESS_INCOMPLETEIPP // Driverless support without media-col-database - // attribute -}; - -// Backside orientations for duplex printing -typedef enum cf_backside_orient_e -{ - CF_BACKSIDE_MANUAL_TUMBLE, - CF_BACKSIDE_ROTATED, - CF_BACKSIDE_FLIPPED, - CF_BACKSIDE_NORMAL -} cf_backside_orient_t; - - -// Data structure for resolution (X x Y dpi) -typedef struct cf_res_s -{ - int x, y; -} cf_res_t; - -typedef enum cf_gen_sizes_mode_e -{ - CF_GEN_SIZES_DEFAULT = 0, - CF_GEN_SIZES_SEARCH, - CF_GEN_SIZES_SEARCH_BORDERLESS_ONLY -} cf_gen_sizes_mode_t; - - -// -// Prototypes... -// - -char *cfResolveURI(const char *raw_uri); -char *cfippfindBasedURIConverter(const char *uri ,int is_fax); -int cfCheckDriverlessSupport(const char* uri); -ipp_t *cfGetPrinterAttributes(const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug); -ipp_t *cfGetPrinterAttributes2(http_t *http_printer, - const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug); -ipp_t *cfGetPrinterAttributes3(http_t *http_printer, - const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug, - int* driverless_support); -ipp_t *cfGetPrinterAttributes4(const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug, - int isFax); -ipp_t *cfGetPrinterAttributes5(http_t *http_printer, - const char* raw_uri, - const char* const pattrs[], - int pattrs_size, - const char* const req_attrs[], - int req_attrs_size, - int debug, - int* driverless_support, - int resolve_uri_type); - -const char *cfIPPAttrEnumValForPrinter(ipp_t *printer_attrs, - ipp_t *job_attrs, - const char *attr_name); -int cfIPPAttrIntValForPrinter(ipp_t *printer_attrs, - ipp_t *job_attrs, - const char *attr_name, - int *value); -int cfIPPAttrResolutionForPrinter(ipp_t *printer_attrs, - ipp_t *job_attrs, - const char *attr_name, - int *xres, int *yres); -int cfIPPReverseOutput(ipp_t *printer_attrs, ipp_t *job_attrs); -int cfGetBackSideOrientation(cf_filter_data_t *data); -const char *cfGetPrintRenderIntent(cf_filter_data_t *data, - char *ri, int ri_len); - -int cfJoinJobOptionsAndAttrs(cf_filter_data_t *data, - int num_options, - cups_option_t **options); - -char *cfStrFormatd(char *buf, char *bufend, double number, - struct lconv *loc); - -int cfCompareResolutions(void *resolution_a, void *resolution_b, - void *user_data); -void *cfCopyResolution(void *resolution, void *user_data); -void cfFreeResolution(void *resolution, void *user_data); -cf_res_t *cfNewResolution(int x, int y); -cups_array_t *cfNewResolutionArray(); -cf_res_t *cfIPPResToResolution(ipp_attribute_t *attr, int index); -cups_array_t *cfIPPAttrToResolutionArray(ipp_attribute_t *attr); -int cfJoinResolutionArrays(cups_array_t **current, - cups_array_t **new_arr, - cf_res_t **current_default, - cf_res_t **new_default); - -int cfGetPageDimensions(ipp_t *printer_attrs, - ipp_t *job_attrs, - int num_options, - cups_option_t *options, - cups_page_header2_t *header, - int transverse_fit, - float *width, - float *height, - float *left, - float *bottom, - float *right, - float *top, - char *name, - ipp_t **media_col_entry); -void cfGenerateSizes(ipp_t *response, - cf_gen_sizes_mode_t mode, - cups_array_t **sizes, - ipp_attribute_t **defattr, - int *width, - int *length, - int *left, - int *bottom, - int *right, - int *top, - int *min_width, - int *min_length, - int *max_width, - int *max_length, - int *custom_left, - int *custom_bottom, - int *custom_right, - int *custom_top, - char *size_name, - ipp_t **media_col_entry); - - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_IPP_H_ diff --git a/cupsfilters/log.h b/cupsfilters/log.h deleted file mode 100644 index 5e050a582..000000000 --- a/cupsfilters/log.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Log functions header file for libcupsfilters. -// -// Copyright 2020-2022 by Till Kamppeter. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_LOG_H_ -# define _CUPS_FILTERS_LOG_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Types... -// - -typedef enum cf_loglevel_e // Log levels, same as PAPPL, similar -{ // to CUPS - CF_LOGLEVEL_UNSPEC = -1, // Not specified - CF_LOGLEVEL_DEBUG, // Debug message - CF_LOGLEVEL_INFO, // Informational message - CF_LOGLEVEL_WARN, // Warning message - CF_LOGLEVEL_ERROR, // Error message - CF_LOGLEVEL_FATAL, // Fatal message - CF_LOGLEVEL_CONTROL // Control message -} cf_loglevel_t; - -typedef void (*cf_logfunc_t)(void *data, cf_loglevel_t level, - const char *message, ...); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_LOG_H_ diff --git a/cupsfilters/lut.c b/cupsfilters/lut.c deleted file mode 100644 index 11cb56f33..000000000 --- a/cupsfilters/lut.c +++ /dev/null @@ -1,143 +0,0 @@ -// -// Lookup table routines for libcupsfilters. -// -// Copyright 2007 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfLutDelete() - Free the memory used by a lookup table. -// cfLutNew() - Make a lookup table from a list of pixel values. -// - -// -// Include necessary headers. -// - -#include "driver.h" -#include - - -// -// 'cfLutDelete()' - Free the memory used by a lookup table. -// - -void -cfLutDelete(cf_lut_t *lut) // I - Lookup table to free -{ - if (lut != NULL) - free(lut); -} - - -// -// 'cfLutNew()' - Make a lookup table from a list of pixel values. -// -// Returns a pointer to the lookup table on success, NULL on failure. -// - -cf_lut_t * // O - New lookup table -cfLutNew(int num_values, // I - Number of values - const float *values, // I - Lookup table values - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - int pixel; // Pixel value - cf_lut_t *lut; // Lookup table - int start, // Start value - end, // End value - maxval; // Maximum value - - - // - // Range check... - // - - if (!num_values || !values) - return (NULL); - - // - // Allocate memory for the lookup table... - // - - if ((lut = (cf_lut_t *)calloc((CF_MAX_LUT + 1), - sizeof(cf_lut_t))) == NULL) - return (NULL); - - // - // Generate the dither lookup table. The pixel values are roughly - // defined by a piecewise linear curve that has an intensity value - // at each output pixel. This isn't perfectly accurate, but it's - // close enough for jazz. - // - - maxval = CF_MAX_LUT / values[num_values - 1]; - - for (start = 0; start <= CF_MAX_LUT; start ++) - lut[start].intensity = start * maxval / CF_MAX_LUT; - - for (pixel = 0; pixel < num_values; pixel ++) - { - // - // Select start and end values for this pixel... - // - - if (pixel == 0) - start = 0; - else - start = (int)(0.5 * maxval * (values[pixel - 1] + - values[pixel])) + 1; - - if (start < 0) - start = 0; - else if (start > CF_MAX_LUT) - start = CF_MAX_LUT; - - if (pixel == (num_values - 1)) - end = CF_MAX_LUT; - else - end = (int)(0.5 * maxval * (values[pixel] + values[pixel + 1])); - - if (end < 0) - end = 0; - else if (end > CF_MAX_LUT) - end = CF_MAX_LUT; - - if (start == end) - break; - - // - // Generate lookup values and errors for each pixel. - // - - while (start <= end) - { - lut[start].pixel = pixel; - if (start == 0) - lut[0].error = 0; - else - lut[start].error = start - maxval * values[pixel]; - - start ++; - } - } - - // - // Show the lookup table... - // - - if (log) - for (start = 0; start <= CF_MAX_LUT; start += CF_MAX_LUT / 15) - log(ld, CF_LOGLEVEL_DEBUG, - "%d = %d/%d/%d", start, lut[start].intensity, - lut[start].pixel, lut[start].error); - - // - // Return the lookup table... - // - - return (lut); -} diff --git a/cupsfilters/mupdftopwg.c b/cupsfilters/mupdftopwg.c deleted file mode 100644 index d7dd59e4c..000000000 --- a/cupsfilters/mupdftopwg.c +++ /dev/null @@ -1,621 +0,0 @@ -// -// mutool-based PDF to PWG Raster filter function for libcupsfilters. -// -// Copyright (c) 2016, Pranjal Bhor -// Copyright (c) 2008-2016, Till Kamppeter -// Copyright (c) 2011, Tim Waugh -// Copyright (c) 2011-2013, Richard Hughes -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PDF_MAX_CHECK_COMMENT_LINES 20 - -#define CUPS_IPTEMPFILE "/tmp/ip-XXXXXX" -#define CUPS_OPTEMPFILE "/tmp/op-XXXXXX" - -#ifdef CUPS_RASTER_SYNCv1 -typedef cups_page_header2_t mupdf_page_header; -#else -typedef cups_page_header_t mupdf_page_header; -#endif // CUPS_RASTER_SYNCv1 - - -static int -parse_doc_type(FILE *fp, - cf_logfunc_t log, - void *ld) -{ - char buf[5]; - char *rc; - - // get the first few bytes of the file - rewind(fp); - rc = fgets(buf, sizeof(buf), fp); - - // empty input - if (rc == NULL) - return (1); - - // is PDF - if (strncmp(buf, "%PDF", 4) == 0) - return (0); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterMuPDFToPWG: input file cannot be identified"); - - return (-1); -} - -static void -parse_pdf_header_options(FILE *fp, - mupdf_page_header *h) -{ - char buf[4096]; - int i; - - rewind(fp); - - // skip until PDF start header - while (fgets(buf, sizeof(buf), fp) != 0) - if (strncmp(buf, "%PDF", 4) == 0) - break; - for (i = 0; i < PDF_MAX_CHECK_COMMENT_LINES; i++) - { - if (fgets(buf, sizeof(buf), fp) == 0) - break; - if (strncmp(buf, "%%PDFTOPDFNumCopies", 19) == 0) - { - char *p; - - p = strchr(buf + 19, ':'); - h->NumCopies = atoi(p + 1); - } - else if (strncmp(buf, "%%PDFTOPDFCollate", 17) == 0) - { - char *p; - - p = strchr(buf + 17, ':'); - while (*p == ' ' || *p == '\t') - p ++; - if (strncasecmp(p, "true", 4) == 0) - h->Collate = CUPS_TRUE; - else - h->Collate = CUPS_FALSE; - } - } -} - -static void -header_to_gs_args(mupdf_page_header *h, - cf_filter_out_format_t outformat, - cups_array_t *mupdf_args) -{ - char tmpstr[1024]; - - if ((h->HWResolution[0] != 100) || (h->HWResolution[1] != 100)) - { - snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d", h->HWResolution[0], - h->HWResolution[1]); - cupsArrayAdd(mupdf_args, strdup(tmpstr)); - } - else - { - snprintf(tmpstr, sizeof(tmpstr), "-r100x100"); - cupsArrayAdd(mupdf_args, strdup(tmpstr)); - } - - snprintf(tmpstr, sizeof(tmpstr), "-w%d", h->cupsWidth); - cupsArrayAdd(mupdf_args, strdup(tmpstr)); - - snprintf(tmpstr, sizeof(tmpstr), "-h%d", h->cupsHeight); - cupsArrayAdd(mupdf_args, strdup(tmpstr)); - - switch (h->cupsColorSpace) - { - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_RGBW: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - default: - snprintf(tmpstr, sizeof(tmpstr), "-crgb"); - break; - - case CUPS_CSPACE_CMYK: - if (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER) - // No post-filtering needed - snprintf(tmpstr, sizeof(tmpstr), "-ccmyk"); - else - snprintf(tmpstr, sizeof(tmpstr), "-crgb"); - // Post-filtering needed, only - // 3-color modes supported as - // input for the post filters*/ - break; - - case CUPS_CSPACE_SW: - snprintf(tmpstr, sizeof(tmpstr), "-cgray"); - break; - - case CUPS_CSPACE_K: - case CUPS_CSPACE_W: - snprintf(tmpstr, sizeof(tmpstr), "-cmono"); - break; - } - cupsArrayAdd(mupdf_args, strdup(tmpstr)); -} - -static int -mutool_spawn(const char *filename, - cups_array_t *mutool_args, - int outputfd, - cf_logfunc_t log, - void *ld, - cf_filter_iscanceledfunc_t iscanceled, - void *icd) -{ - char *argument; - char buf[BUFSIZ]; - char **mutoolargv; - const char* apos; - int errfds[2]; - int i; - int numargs; - int pid, mutoolpid, errpid; - cups_file_t *logfp; - cf_loglevel_t log_level; - char *msg; - int status = 65536; - int wstatus; - - // Put mutool command line argument into an array for the "exec()" - // call - numargs = cupsArrayCount(mutool_args); - mutoolargv = calloc(numargs + 1, sizeof(char *)); - for (argument = (char *)cupsArrayFirst(mutool_args), i = 0; argument; - argument = (char *)cupsArrayNext(mutool_args), i++) - mutoolargv[i] = argument; - mutoolargv[i] = NULL; - - if (log) - { - // Debug output: Full mutool command line and environment variables - snprintf(buf, sizeof(buf), "cfFilterMuPDFToPWG: mutool command line:"); - for (i = 0; mutoolargv[i]; i ++) - { - if ((strchr(mutoolargv[i],' ')) || (strchr(mutoolargv[i],'\t'))) - apos = "'"; - else - apos = ""; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " %s%s%s", apos, mutoolargv[i], apos); - } - log(ld, CF_LOGLEVEL_DEBUG, "%s", buf); - } - - // Create a pipe for stderr output of mutool - if (pipe(errfds)) - { - errfds[0] = -1; - errfds[1] = -1; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Unable to establish stderr pipe for mutool call"); - goto out; - } - - // Set the "close on exec" flag on each end of the pipes... - if (fcntl(errfds[0], F_SETFD, fcntl(errfds[0], F_GETFD) | FD_CLOEXEC)) - { - close(errfds[0]); - close(errfds[1]); - errfds[0] = -1; - errfds[1] = -1; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Unable to set \"close on exec\" flag on read end of the stderr pipe for mutool call"); - goto out; - } - if (fcntl(errfds[1], F_SETFD, fcntl(errfds[1], F_GETFD) | FD_CLOEXEC)) - { - close(errfds[0]); - close(errfds[1]); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Unable to set \"close on exec\" flag on write end of the stderr pipe for mutool call"); - goto out; - } - - if ((mutoolpid = fork()) == 0) - { - // Couple errfds pipe with stdin of mutool process - if (errfds[1] >= 2) - { - if (errfds[1] != 2) - { - if (dup2(errfds[1], 2) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Unable to couple pipe with stderr of mutool process"); - exit(1); - } - close(errfds[1]); - } - close(errfds[0]); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: invalid pipe file descriptor to couple with stderr of mutool process"); - exit(1); - } - - // Couple stdout of mutool process - if (outputfd >= 1) - { - if (outputfd != 1) - { - if (dup2(outputfd, 1) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Unable to couple stdout of mutool process"); - exit(1); - } - close(outputfd); - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Invalid file descriptor to couple with stdout of mutool process"); - exit(1); - } - - // Execute mutool command line ... - execvp(filename, mutoolargv); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Unable to launch mutool: %s: %s", - filename, strerror(errno)); - exit(1); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterMuPDFToPWG: Started mutool (PID %d)", mutoolpid); - - close(errfds[1]); - - if ((errpid = fork()) == 0) - { - logfp = cupsFileOpenFd(errfds[0], "r"); - while (cupsFileGets(logfp, buf, sizeof(buf))) - if (log) - { - if (strncmp(buf, "DEBUG: ", 7) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 7; - } - else if (strncmp(buf, "DEBUG2: ", 8) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 8; - } - else if (strncmp(buf, "INFO: ", 6) == 0) - { - log_level = CF_LOGLEVEL_INFO; - msg = buf + 6; - } - else if (strncmp(buf, "WARNING: ", 9) == 0) - { - log_level = CF_LOGLEVEL_WARN; - msg = buf + 9; - } - else if (strncmp(buf, "ERROR: ", 7) == 0) - { - log_level = CF_LOGLEVEL_ERROR; - msg = buf + 7; - } - else - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf; - } - log(ld, log_level, "cfFilterMuPDFToPWG: %s", msg); - } - cupsFileClose(logfp); - // No need to close the fd errfds[0], as cupsFileClose(fp) does this - // already - // Ignore errors of the logging process - exit(0); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterMuPDFToPWG: Started logging (PID %d)", errpid); - - close(errfds[0]); - - while (mutoolpid > 0 || errpid > 0) - { - if ((pid = wait(&wstatus)) < 0) - { - if (errno == EINTR && iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterMuPDFToPWG: Job canceled, killing mutool ..."); - kill(mutoolpid, SIGTERM); - mutoolpid = -1; - kill(errpid, SIGTERM); - errpid = -1; - break; - } else - continue; - } - - // How did the filter terminate - if (wstatus) - { - if (WIFEXITED(wstatus)) - { - // Via exit() anywhere or return() in the main() function - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: %s (PID %d) stopped with status %d", - (pid == mutoolpid ? "mutool" : "Logging"), pid, - WEXITSTATUS(wstatus)); - status = WEXITSTATUS(wstatus); - } - else - { - // Via signal - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: %s (PID %d) crashed on signal %d", - (pid == mutoolpid ? "mutool" : "Logging"), pid, - WTERMSIG(wstatus)); - status = 256 * WTERMSIG(wstatus); - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterMuPDFToPWG: %s (PID %d) exited with no errors.", - (pid == mutoolpid ? "mutool" : "Logging"), pid); - status = 0; - } - if (pid == mutoolpid) - mutoolpid = -1; - else if (pid == errpid) - errpid = -1; - } - -out: - free(mutoolargv); - return (status); -} - - -int -cfFilterMuPDFToPWG(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - cf_filter_out_format_t outformat; - char *val; - char buf[BUFSIZ]; - char *icc_profile = NULL; - char tmpstr[1024]; - cups_array_t *mupdf_args = NULL; - FILE *fp = NULL; - char infilename[1024]; - mupdf_page_header h; - int fd = -1; - int cm_disabled = 0; - int n; - int empty = 0; - int status = 1; - struct sigaction sa; - cf_cm_calibration_t cm_calibrate; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - cups_cspace_t cspace = -1; - - (void)inputseekable; - (void)parameters; - - val = data->final_content_type; - if (val) - { - if (strcasestr(val, "pwg")) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - else if (strcasestr(val, "urf")) - outformat = CF_FILTER_OUT_FORMAT_APPLE_RASTER; - else if (strcasestr(val, "pclm")) - outformat = CF_FILTER_OUT_FORMAT_PCLM; - else if (strcasestr(val, "cups")) - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - else - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - else - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterMuPDFToPWG: Output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - (outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER ? "Apple Raster" : - "PCLM")))); - - memset(&sa, 0, sizeof(sa)); - // Ignore SIGPIPE and have write return an error instead - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); - - fd = cupsTempFd(infilename, 1024); - if (fd < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Can't create temporary file"); - goto out; - } - - // copy input file to the tmp file - while ((n = read(inputfd, buf, BUFSIZ)) > 0) - { - if (write(fd, buf, n) != n) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Can't copy input to temporary file"); - close(fd); - goto out; - } - } - - if (!inputfd) - { - if (lseek(fd, 0, SEEK_SET) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Can't rewind temporary file"); - close(fd); - goto out; - } - - if ((fp = fdopen(fd, "rb")) == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Can't open temporary file"); - close(fd); - goto out; - } - } - else - { - // filename is specified - - if ((fp = fdopen(fd, "rb")) == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Can't open temporary file"); - goto out; - } - } - - // If doc type is not PDF exit - empty = parse_doc_type(fp, log, ld); - if (empty == -1) - goto out; - - // mutool parameters - mupdf_args = cupsArrayNew(NULL, NULL); - if (!mupdf_args) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Unable to allocate memory for mutool arguments array"); - goto out; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterMuPDFToPWG: command: %s", - CUPS_MUTOOL); - snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_MUTOOL); - cupsArrayAdd(mupdf_args, strdup(tmpstr)); - cupsArrayAdd(mupdf_args, strdup("draw")); - cupsArrayAdd(mupdf_args, strdup("-L")); - cupsArrayAdd(mupdf_args, strdup("-o-")); - cupsArrayAdd(mupdf_args, strdup("-smtf")); - - // mutool output parameters - cupsArrayAdd(mupdf_args, strdup("-Fpwg")); - - // Note that MuPDF only creates PWG Raster so we select this as header format, - // We also supply the final output format (to which will be converted with - // further filters) to determine the correct color space and depth. - // From the header h only cupsWidth/cupsHeight (dimensions in pixels), - // resolution, and color space are used here. - cfRasterPrepareHeader(&h, data, outformat, - CF_FILTER_OUT_FORMAT_PWG_RASTER, 1, &cspace); - - // set PDF-specific options - parse_pdf_header_options(fp, &h); - - // fixed other values that pdftopdf handles - h.MirrorPrint = CUPS_FALSE; - h.Orientation = CUPS_ORIENT_0; - - // Check status of color management in CUPS - cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (cm_calibrate == CF_CM_CALIBRATION_ENABLED) - cm_disabled = 1; - else - cm_disabled = cfCmIsPrinterCmDisabled(data); - - if (!cm_disabled) - cfCmGetPrinterIccProfile(data, cfRasterColorSpaceString(h.cupsColorSpace), - h.MediaType, h.HWResolution[0], h.HWResolution[1], - &icc_profile); - - // Note: No ICC profile support in mutool! - - // Find print-rendering-intent - h.cupsRenderingIntent[0] = '\0'; - cfGetPrintRenderIntent(data, h.cupsRenderingIntent, - sizeof(h.cupsRenderingIntent)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Print rendering intent = %s", h.cupsRenderingIntent); - - // get all the data from the header and pass it to mutool - header_to_gs_args(&h, outformat, mupdf_args); - - snprintf(tmpstr, sizeof(tmpstr), "%s", infilename); - cupsArrayAdd(mupdf_args, strdup(tmpstr)); - - // Execute mutool command line ... - snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_MUTOOL); - - // call mutool - status = mutool_spawn (tmpstr, mupdf_args, outputfd, log, ld, - iscanceled, icd); - if (status != 0) - status = 1; - - if (empty) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterMuPDFToPWG: Input is empty, outputting empty file."); - status = 0; - } - -out: - close(outputfd); - if (fp) - fclose(fp); - if (mupdf_args) - cupsArrayDelete(mupdf_args); - - free(icc_profile); - - if (fd >= 0) - unlink(infilename); - - return (status); -} diff --git a/cupsfilters/pack.c b/cupsfilters/pack.c deleted file mode 100644 index 1278da13f..000000000 --- a/cupsfilters/pack.c +++ /dev/null @@ -1,299 +0,0 @@ -// -// Bit packing routines for libcupsfilters. -// -// Copyright 2007 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfPackHorizontal() - Pack pixels horizontally... -// cfPackHorizontal2() - Pack 2-bit pixels horizontally... -// cfPackHorizontalBit() - Pack pixels horizontally by bit... -// cfPackVertical() - Pack pixels vertically... -// - -// -// Include necessary headers... -// - -#include "driver.h" - - -// -// 'cfPackHorizontal()' - Pack pixels horizontally... -// - -void -cfPackHorizontal(const unsigned char *ipixels, // I - Input pixels - unsigned char *obytes, // O - Output bytes - int width, // I - Number of pixels - const unsigned char clearto, // I - Initial value of bytes - const int step) // I - Step value between pixels -{ - register unsigned char b; // Current byte - - - // - // Do whole bytes first... - // - - while (width > 7) - { - b = clearto; - - if (*ipixels) - b ^= 0x80; - ipixels += step; - if (*ipixels) - b ^= 0x40; - ipixels += step; - if (*ipixels) - b ^= 0x20; - ipixels += step; - if (*ipixels) - b ^= 0x10; - ipixels += step; - if (*ipixels) - b ^= 0x08; - ipixels += step; - if (*ipixels) - b ^= 0x04; - ipixels += step; - if (*ipixels) - b ^= 0x02; - ipixels += step; - if (*ipixels) - b ^= 0x01; - ipixels += step; - - *obytes++ = b; - - width -= 8; - } - - // - // Then do the last N bytes (N < 8)... - // - - b = clearto; - - switch (width) - { - case 7 : - if (ipixels[6 * step]) - b ^= 0x02; - case 6 : - if (ipixels[5 * step]) - b ^= 0x04; - case 5 : - if (ipixels[4 * step]) - b ^= 0x08; - case 4 : - if (ipixels[3 * step]) - b ^= 0x10; - case 3 : - if (ipixels[2 * step]) - b ^= 0x20; - case 2 : - if (ipixels[1 * step]) - b ^= 0x40; - case 1 : - if (ipixels[0]) - b ^= 0x80; - *obytes = b; - break; - } -} - - -// -// 'cfPackHorizontal2()' - Pack 2-bit pixels horizontally... -// - -void -cfPackHorizontal2(const unsigned char *ipixels, // I - Input pixels - unsigned char *obytes, // O - Output bytes - int width, // I - Number of pixels - const int step) // I - Stepping value -{ - register unsigned char b; // Current byte - - - // - // Do whole bytes first... - // - - while (width > 3) - { - b = *ipixels; - ipixels += step; - b = (b << 2) | *ipixels; - ipixels += step; - b = (b << 2) | *ipixels; - ipixels += step; - b = (b << 2) | *ipixels; - ipixels += step; - - *obytes++ = b; - - width -= 4; - } - - // - // Then do the last N bytes (N < 4)... - // - - b = 0; - - switch (width) - { - case 3 : - b = ipixels[2 * step]; - case 2 : - b = (b << 2) | ipixels[step]; - case 1 : - b = (b << 2) | ipixels[0]; - *obytes = b << (8 - 2 * width); - break; - } -} - - -// -// 'cfPackHorizontalBit()' - Pack pixels horizontally by bit... -// - -void -cfPackHorizontalBit(const unsigned char *ipixels, // I - Input pixels - unsigned char *obytes, // O - Output bytes - int width, // I - Number of pixels - const unsigned char clearto, // I - Initial value of - // bytes - const unsigned char bit) // I - Bit to check -{ - register unsigned char b; // Current byte - - - // - // Do whole bytes first... - // - - while (width > 7) - { - b = clearto; - - if (*ipixels++ & bit) - b ^= 0x80; - if (*ipixels++ & bit) - b ^= 0x40; - if (*ipixels++ & bit) - b ^= 0x20; - if (*ipixels++ & bit) - b ^= 0x10; - if (*ipixels++ & bit) - b ^= 0x08; - if (*ipixels++ & bit) - b ^= 0x04; - if (*ipixels++ & bit) - b ^= 0x02; - if (*ipixels++ & bit) - b ^= 0x01; - - *obytes++ = b; - - width -= 8; - } - - // - // Then do the last N bytes (N < 8)... - // - - b = clearto; - - switch (width) - { - case 7 : - if (ipixels[6] & bit) - b ^= 0x02; - case 6 : - if (ipixels[5] & bit) - b ^= 0x04; - case 5 : - if (ipixels[4] & bit) - b ^= 0x08; - case 4 : - if (ipixels[3] & bit) - b ^= 0x10; - case 3 : - if (ipixels[2] & bit) - b ^= 0x20; - case 2 : - if (ipixels[1] & bit) - b ^= 0x40; - case 1 : - if (ipixels[0] & bit) - b ^= 0x80; - *obytes = b; - break; - } -} - - -// -// 'cfPackVertical()' - Pack pixels vertically... -// - -void -cfPackVertical(const unsigned char *ipixels, // I - Input pixels - unsigned char *obytes, // O - Output bytes - int width, // I - Number of input pixels - const unsigned char bit, // I - Output bit - const int step) // I - Number of bytes between - // columns -{ - // - // Loop through the entire array... - // - - while (width > 7) - { - if (*ipixels++) - *obytes ^= bit; - obytes += step; - if (*ipixels++) - *obytes ^= bit; - obytes += step; - if (*ipixels++) - *obytes ^= bit; - obytes += step; - if (*ipixels++) - *obytes ^= bit; - obytes += step; - if (*ipixels++) - *obytes ^= bit; - obytes += step; - if (*ipixels++) - *obytes ^= bit; - obytes += step; - if (*ipixels++) - *obytes ^= bit; - obytes += step; - if (*ipixels++) - *obytes ^= bit; - obytes += step; - - width -= 8; - } - - while (width > 0) - { - if (*ipixels++) - *obytes ^= bit; - - obytes += step; - width --; - } -} diff --git a/cupsfilters/pclmtoraster.cxx b/cupsfilters/pclmtoraster.cxx deleted file mode 100644 index fc8fe88da..000000000 --- a/cupsfilters/pclmtoraster.cxx +++ /dev/null @@ -1,1206 +0,0 @@ -// -// PCLm/Raster-only PDF to Raster filter function for libcupsfilters. -// -// Copyright © 2020 by Vikrant Malik -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "filter.h" -#include -#include -#include -#include -#include -#include "image.h" -#include "bitmap.h" -#include "raster.h" -#include "filter.h" -#include "ipp.h" - - -#define MAX_BYTES_PER_PIXEL 32 - - -typedef struct pclmtoraster_data_s -{ - cf_filter_out_format_t outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - int numcolors = 0; - int rowsize = 0; - cups_page_header2_t header; - char pageSizeRequested[64]; - int bi_level = 0; - // image swapping - bool swap_image_x = false; - bool swap_image_y = false; - // margin swapping - bool swap_margin_x = false; - bool swap_margin_y = false; - unsigned int nplanes; - unsigned int nbands; - unsigned int bytesPerLine; // number of bytes per line - // Note: When CUPS_ORDER_BANDED, - // cupsBytesPerLine = bytesPerLine * cupsNumColors - std::string colorspace; // Colorspace of raster data -} pclmtoraster_data_t; - -typedef unsigned char *(*convert_cspace_func)(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data); - -typedef unsigned char *(*convert_line_func)(unsigned char *src, - unsigned char *dst, - unsigned char *buf, - unsigned int row, - unsigned int plane, - pclmtoraster_data_t *data, - convert_cspace_func convertcspace); - -typedef struct conversion_function_s -{ - convert_cspace_func convertcspace;// Function for conversion of colorspaces - convert_line_func convertline; // Function tom modify raster data of a line -} conversion_function_t; - - -static int -parse_opts(cf_filter_data_t *data, - cf_filter_out_format_t outformat, - pclmtoraster_data_t *pclmtoraster_data) -{ - int num_options = 0; - cups_option_t* options = NULL; - const char* t = NULL; - const char *val; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cups_page_header2_t *header = &(pclmtoraster_data->header); - cups_cspace_t cspace = (cups_cspace_t)(-1); - - - pclmtoraster_data->outformat = outformat; - - // - // CUPS option list - // - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - t = cupsGetOption("media-class", num_options, options); - if (t == NULL) - t = cupsGetOption("MediaClass", num_options, options); - if (t != NULL) - { - if (strcasestr(t, "pwg")) - pclmtoraster_data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - - cfRasterPrepareHeader(header, data, outformat, outformat, 0, &cspace); - - if (header->Duplex) - { - int backside; - // analyze options relevant to Duplex - // APDuplexRequiresFlippedMargin - enum - { - FM_NO, - FM_FALSE, - FM_TRUE - } flippedMargin = FM_NO; - - backside = cfGetBackSideOrientation(data); - - if (backside >= 0) - { - flippedMargin = (backside & 16 ? FM_TRUE : - (backside & 8 ? FM_FALSE : - FM_NO)); - backside &= 7; - - if (backside == CF_BACKSIDE_MANUAL_TUMBLE && header->Tumble) - { - pclmtoraster_data->swap_image_x = pclmtoraster_data->swap_image_y = - true; - pclmtoraster_data->swap_margin_x = pclmtoraster_data->swap_margin_y = - true; - if (flippedMargin == FM_TRUE) - pclmtoraster_data->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_ROTATED && !header->Tumble) - { - pclmtoraster_data->swap_image_x = pclmtoraster_data->swap_image_y = - true; - pclmtoraster_data->swap_margin_x = pclmtoraster_data->swap_margin_y = - true; - if (flippedMargin == FM_TRUE) - pclmtoraster_data->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_FLIPPED) - { - if (header->Tumble) - { - pclmtoraster_data->swap_image_x = true; - pclmtoraster_data->swap_margin_x = - pclmtoraster_data->swap_margin_y = true; - } - else - pclmtoraster_data->swap_image_y = true; - if (flippedMargin == FM_FALSE) - pclmtoraster_data->swap_margin_y = - !(pclmtoraster_data->swap_margin_y); - } - } - } - - if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL && - !strncasecmp(val, "bi-level", 8)) - pclmtoraster_data->bi_level = 1; - - strncpy(pclmtoraster_data->pageSizeRequested, header->cupsPageSizeName, 64); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Page size requested: %s.", - header->cupsPageSizeName); - return(0); -} - - -static bool -media_box_lookup(QPDFObjectHandle object, - float rect[4]) -{ - // preliminary checks - if (!object.isDictionary() || !object.hasKey("/MediaBox")) - return (false); - - // assign mediabox values to rect - std::vector mediabox = - object.getKey("/MediaBox").getArrayAsVector(); - for (int i = 0; i < 4; ++i) - rect[i] = mediabox[i].getNumericValue(); - - return (mediabox.size() == 4); -} - - -// -// 'rotate_bitmap()' - Function to rotate a bitmap -// (assumed that bits-per-component of the bitmap is 8). -// - -static unsigned char * // O - Output Bitmap -rotate_bitmap(unsigned char *src, // I - Input string - unsigned char *dst, // O - Destination string - unsigned int rotate, // I - Rotate value (0, 90, 180, 270) - unsigned int height, // I - Height of raster image in pixels - unsigned int width, // I - Width of raster image in pixels - int rowsize, // I - Length of one row of pixels - std::string colorspace,// I - Colorspace of input bitmap - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - unsigned char *bp = src; - unsigned char *dp = dst; - unsigned char *temp = dst; - - if (rotate == 0) - return (src); - else if (rotate == 180) - { - if (colorspace == "/DeviceGray") - { - bp = src + height * rowsize - 1; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - for (unsigned int w = 0; w < width; w ++, bp --, dp ++) - *dp = *bp; - } - else if (colorspace == "/DeviceCMYK") - { - bp = src + height * rowsize - 4; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - for (unsigned int w = 0; w < width; w ++, bp -= 4, dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (colorspace == "/DeviceRGB") - { - bp = src + height * rowsize - 3; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - for (unsigned int w = 0; w < width; w ++, bp -= 3, dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else if (rotate == 270) - { - if (colorspace == "/DeviceGray") - { - bp = src; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (height - h) - 1; - for (unsigned int w = 0; w < width; w ++, bp += height , dp ++) - *dp = *bp; - } - } - else if (colorspace == "/DeviceCMYK") - { - for (unsigned int h = 0; h < height; h++) - { - bp = src + (height - h) * 4 - 4; - for (unsigned int i = 0; i < width; i ++, bp += height * 4 , dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (colorspace == "/DeviceRGB") - { - bp = src; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (height - h) * 3 - 3; - for (unsigned int i = 0; i < width; i ++, bp += height * 3 , dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else if (rotate == 90) - { - if (colorspace == "/DeviceGray") - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height + h; - for (unsigned int i = 0; i < width; i ++, bp -= height , dp ++) - *dp = *bp; - } - } - else if (colorspace == "/DeviceCMYK") - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height * 4 + 4 * h; - for (unsigned int i = 0; i < width; i ++, bp -= height * 4 , dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (colorspace == "/DeviceRGB") - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height * 3 + 3 * h; - for (unsigned int i = 0; i < width; i ++, bp -= height * 3 , dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Incorrect Rotate Value %d, not rotating", - rotate); - return (src); - } - - return (temp); -} - - -static unsigned char * -rgb_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMYK(src, dst, pixels); - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~dst[i]; - return (dst); -} - - -static unsigned char * -rgb_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_white_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageRGBToWhite(src, dst, pixels); - else - { - cfImageRGBToWhite(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -rgb_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageRGBToBlack(src, dst, pixels); - else - { - cfImageRGBToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -cmyk_to_rgb_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageCMYKToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~src[i]; - return (dst); -} - - -static unsigned char * -cmyk_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - // Converted first to RGB and then to cmy for better outputs. - cfImageCMYKToRGB(src, src, pixels); - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_white_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageCMYKToWhite(src, dst, pixels); - else - { - cfImageCMYKToWhite(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -cmyk_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageCMYKToBlack(src, dst, pixels); - else - { - cfImageCMYKToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -gray_to_rgb_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMYK(src, dst, pixels); - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~dst[i]; - return (dst); -} - - -static unsigned char * -gray_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageWhiteToBlack(src, dst, pixels); - else - { - cfImageWhiteToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -convert_cspace_no_op(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - return (src); -} - - -// -// 'convert_line()' - Function to convert colorspace and bits-per-pixel -// of a single line of raster data. -// - -static unsigned char * // O - Output string -convert_line(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination string - unsigned char *buf, // I - Buffer string - unsigned int row, // I - Current Row - unsigned int plane, // I - Plane/Band - pclmtoraster_data_t *data, - convert_cspace_func convertcspace) -{ - // - // Use only convertcspace if conversion of bits and conversion of color order - // is not required, or if dithering is required, for faster processing of - // raster output. - // - - unsigned int pixels = data->header.cupsWidth; - if ((data->header.cupsBitsPerColor == 1 && - data->header.cupsNumColors == 1) || - (data->header.cupsBitsPerColor == 8 && - data->header.cupsColorOrder == CUPS_ORDER_CHUNKED)) - dst = convertcspace(src, dst, row, pixels, data); - else - { - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - pb = convertcspace(src + i * data->numcolors, pixelBuf1, row, 1, data); - pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, - data->header.cupsBitsPerColor); - cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, - data->header.cupsBitsPerColor, data->header.cupsColorOrder); - } - } - return (dst); -} - - -// -// 'convert_reverse_line()' - Function to convert colorspace and bits-per-pixel -// of a single line of raster data and reverse the -// line. -// - -static unsigned char * // O - Output string -convert_reverse_line(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination - // string - unsigned char *buf, // I - Buffer string - unsigned int row, // I - Current Row - unsigned int plane, // I - Plane/Band - pclmtoraster_data_t *data, // I - pclmtoraster - // filter data - convert_cspace_func convertcspace) // I - Function for - // conversion of - // colorspace -{ - // - // Use only convertcspace if conversion of bits and conversion of color order - // is not required, or if dithering is required, for faster processing of - // raster output. - // - - unsigned int pixels = data->header.cupsWidth; - if (data->header.cupsBitsPerColor == 1 && data->header.cupsNumColors == 1) - { - buf = convertcspace(src, buf, row, pixels, data); - dst = cfReverseOneBitLine(buf, dst, pixels, data->bytesPerLine); - } - else if (data->header.cupsBitsPerColor == 8 && - data->header.cupsColorOrder == CUPS_ORDER_CHUNKED) - { - unsigned char *dp = dst; - // Assign each pixel of buf to dst in the reverse order. - buf = convertcspace(src, buf, row, pixels, data) + - (data->header.cupsWidth - 1) * data->header.cupsNumColors; - for (unsigned int i = 0; i < pixels; - i ++, buf-=data->header.cupsNumColors, dp+=data->header.cupsNumColors) - for (unsigned int j = 0; j < data->header.cupsNumColors; j ++) - dp[j] = buf[j]; - } - else - { - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - pb = convertcspace(src + (pixels - i - 1) * (data->numcolors), pixelBuf1, - row, 1, data); - pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, - data->header.cupsBitsPerColor); - cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, - data->header.cupsBitsPerColor, data->header.cupsColorOrder); - } - } - return (dst); -} - - -static void // O - Exit status -select_convert_func(int pgno, // I - Page number - cf_logfunc_t log, // I - Log function - void *ld, // I - Aux. data for log - // function - pclmtoraster_data_t *data, // I - pclmtoraster filter - // data - conversion_function_t *convert)// I - Conversion function -{ - // Set rowsize and numcolors based on colorspace of raster data - cups_page_header2_t header = data->header; - std::string colorspace = data->colorspace; - if (colorspace == "/DeviceRGB") - { - data->rowsize = header.cupsWidth * 3; - data->numcolors = 3; - } - else if (colorspace == "/DeviceCMYK") - { - data->rowsize = header.cupsWidth * 4; - data->numcolors = 4; - } - else if (colorspace == "/DeviceGray") - { - data->rowsize = header.cupsWidth; - data->numcolors = 1; - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Colorspace %s not supported, " - "defaulting to /deviceRGB", - colorspace.c_str()); - data->colorspace = "/DeviceRGB"; - data->rowsize = header.cupsWidth * 3; - data->numcolors = 3; - } - - convert->convertcspace = convert_cspace_no_op; //Default function - // Select convertcspace function - switch (header.cupsColorSpace) - { - case CUPS_CSPACE_K: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_black_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_black_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_black_line; - break; - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_white_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_white_line; - break; - case CUPS_CSPACE_CMY: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_cmy_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_cmy_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_cmy_line; - break; - case CUPS_CSPACE_CMYK: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_cmyk_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_cmyk_line; - break; - case CUPS_CSPACE_RGBW: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_rgbw_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_rgbw_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_rgbw_line; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_SRGB: - default: - if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_rgb_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_rgb_line; - break; - } - - // Select convertline function - if (header.Duplex && (pgno & 1) && data->swap_image_x) - convert->convertline = convert_reverse_line; - else - convert->convertline = convert_line; -} - - -// -// 'out_page()' - Function to convert a single page of raster-only PDF/PCLm -// input to CUPS/PWG Raster. -// - -static int // O - Exit status -out_page(cups_raster_t* raster, // I - Raster stream - QPDFObjectHandle page, // I - QPDF Page Object - int pgno, // I - Page number - cf_logfunc_t log, // I - Log function - void* ld, // I - Aux. data for log function - pclmtoraster_data_t *data, // I - pclmtoraster filter data - cf_filter_data_t *filter_data, // I - filter data - conversion_function_t *convert)// I - Conversion functions -{ - long long rotate = 0, - height, - width; - float paperdimensions[2], margins[4], l, swap; - int bufsize = 0, pixel_count = 0, - temp = 0; - float mediaBox[4]; - unsigned char *bitmap = NULL, - *colordata = NULL, - *lineBuf = NULL, - *line = NULL, - *dp = NULL; - QPDFObjectHandle image; - QPDFObjectHandle imgdict; - QPDFObjectHandle colorspace_obj; - - - // Check if page is rotated. - if (page.getKey("/Rotate").isInteger()) - rotate = page.getKey("/Rotate").getIntValueAsInt(); - - // Get pagesize by the mediabox key of the page. - if (!media_box_lookup(page, mediaBox)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: PDF page %d doesn't contain a valid mediaBox", - pgno + 1); - return (1); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: mediaBox = [%f %f %f %f]: ", - mediaBox[0], mediaBox[1], mediaBox[2], mediaBox[3]); - l = mediaBox[2] - mediaBox[0]; - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - data->header.PageSize[1] = (unsigned)l; - else - data->header.PageSize[0] = (unsigned)l; - l = mediaBox[3] - mediaBox[1]; - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - data->header.PageSize[0] = (unsigned)l; - else - data->header.PageSize[1] = (unsigned)l; - } - - memset(paperdimensions, 0, sizeof(paperdimensions)); - memset(margins, 0, sizeof(margins)); - if (filter_data != NULL && (filter_data->printer_attrs) != NULL) - { - cfGetPageDimensions(filter_data->printer_attrs, filter_data->job_attrs, - filter_data->num_options, filter_data->options, - &(data->header), 0, - &(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), NULL, NULL); - if (data->outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER) - memset(margins, 0, sizeof(margins)); - } - else - { - for (int i = 0; i < 2; i ++) - paperdimensions[i] = data->header.PageSize[i]; - if (data->header.cupsImagingBBox[3] > 0.0) - { - // Set margins if we have a bounding box defined ... - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - { - margins[0] = data->header.cupsImagingBBox[0]; - margins[1] = data->header.cupsImagingBBox[1]; - margins[2] = paperdimensions[0] - data->header.cupsImagingBBox[2]; - margins[3] = paperdimensions[1] - data->header.cupsImagingBBox[3]; - } - } - else - // ... otherwise use zero margins - for (int i = 0; i < 4; i ++) - margins[i] = 0.0; - } - - if (data->header.Duplex && (pgno & 1)) - { - // backside: change margin if needed - if (data->swap_margin_x) - { - swap = margins[2]; margins[2] = margins[0]; margins[0] = swap; - } - if (data->swap_margin_y) - { - swap = margins[3]; margins[3] = margins[1]; margins[1] = swap; - } - } - - // write page header - for (int i = 0; i < 2; i ++) - { - data->header.cupsPageSize[i] = paperdimensions[i]; - data->header.PageSize[i] = (unsigned int)(data->header.cupsPageSize[i] + - 0.5); - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - data->header.Margins[i] = margins[i] + 0.5; - else - data->header.Margins[i] = 0; - } - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - { - data->header.cupsImagingBBox[0] = margins[0]; - data->header.cupsImagingBBox[1] = margins[1]; - data->header.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; - data->header.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; - for (int i = 0; i < 4; i ++) - data->header.ImagingBoundingBox[i] = - (unsigned int)(data->header.cupsImagingBBox[i] + 0.5); - } - else - { - for (int i = 0; i < 4; i ++) - { - data->header.cupsImagingBBox[i] = 0.0; - data->header.ImagingBoundingBox[i] = 0; - } - } - - data->header.cupsWidth = 0; - data->header.cupsHeight = 0; - - // Loop over all raster images in a page and store them in bitmap. - std::map images = page.getPageImages(); - for (auto const& iter: images) - { - image = iter.second; - imgdict = image.getDict(); // XObject dictionary - - PointerHolder actual_data = image.getStreamData(qpdf_dl_all); - width = imgdict.getKey("/Width").getIntValue(); - height = imgdict.getKey("/Height").getIntValue(); - colorspace_obj = imgdict.getKey("/ColorSpace"); - data->header.cupsHeight += height; - bufsize = actual_data->getSize(); - - if (!pixel_count) - bitmap = (unsigned char *) malloc(bufsize); - else - bitmap = (unsigned char *) realloc(bitmap, pixel_count + bufsize); - memcpy(bitmap + pixel_count, actual_data->getBuffer(), bufsize); - pixel_count += bufsize; - - if (width > data->header.cupsWidth) - data->header.cupsWidth = width; - } - - // Swap width and height in landscape images - if (rotate == 270 || rotate == 90) - { - temp = data->header.cupsHeight; - data->header.cupsHeight = data->header.cupsWidth; - data->header.cupsWidth = temp; - } - - data->bytesPerLine = data->header.cupsBytesPerLine = - (data->header.cupsBitsPerPixel * data->header.cupsWidth + 7) / 8; - if (data->header.cupsColorOrder == CUPS_ORDER_BANDED) - data->header.cupsBytesPerLine *= data->header.cupsNumColors; - - if (!cupsRasterWriteHeader2(raster, &(data->header))) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Can't write page %d header", pgno + 1); - return (1); - } - - data->colorspace = (colorspace_obj.isName() ? - colorspace_obj.getName() : "/DeviceRGB"); - // Default for pclm files in DeviceRGB - - // Select convertline and convertscpace function - select_convert_func(pgno, log, ld, data, convert); - - // If page is to be swapped in both x and y, rotate it by 180 degress - if (data->header.Duplex && (pgno & 1) && data->swap_image_y && - data->swap_image_x) - { - rotate = (rotate + 180) % 360; - data->swap_image_y = false; - data->swap_image_x = false; - } - - // Rotate Bitmap - if (rotate) - { - unsigned char *bitmap2 = (unsigned char *) malloc(pixel_count); - bitmap2 = rotate_bitmap(bitmap, bitmap2, rotate, data->header.cupsHeight, - data->header.cupsWidth, data->rowsize, - data->colorspace, log, ld); - free(bitmap); - bitmap = bitmap2; - } - - colordata = bitmap; - - // Write page image - lineBuf = new unsigned char [data->bytesPerLine]; - line = new unsigned char [data->bytesPerLine]; - if (data->header.Duplex && (pgno & 1) && data->swap_image_y) - { - for (unsigned int plane = 0; plane < data->nplanes; plane ++) - { - unsigned char *bp = colordata + - (data->header.cupsHeight - 1) * (data->rowsize); - for (unsigned int h = data->header.cupsHeight; h > 0; h--) - { - for (unsigned int band = 0; band < data->nbands; band ++) - { - dp = convert->convertline(bp, line, lineBuf, h - 1, plane + band, - data, convert->convertcspace); - cupsRasterWritePixels(raster, dp, data->bytesPerLine); - } - bp -= data->rowsize; - } - } - } - else - { - for (unsigned int plane = 0; plane < data->nplanes; plane ++) - { - unsigned char *bp = colordata; - for (unsigned int h = 0; h < data->header.cupsHeight; h ++) - { - for (unsigned int band = 0; band < data->nbands; band ++) - { - dp = convert->convertline(bp, line, lineBuf, h, plane + band, - data, convert->convertcspace); - cupsRasterWritePixels(raster, dp, data->bytesPerLine); - } - bp += data->rowsize; - } - } - } - delete[] lineBuf; - delete[] line; - free(bitmap); - - return (0); -} - - -// -// 'cfFilterPCLmToRaster()' - Filter function to convert raster-only PDF/PCLm -// input to CUPS/PWG Raster output. -// - -int // O - Error status -cfFilterPCLmToRaster(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - cf_filter_out_format_t outformat; - FILE *inputfp; // Input file pointer - int fd = 0; // Copy file descriptor - char *filename, // PDF file to convert - tempfile[1024]; // Temporary file - char buffer[8192]; // Copy buffer - int bytes; // Bytes copied - int npages = 0; - QPDF *pdf = new QPDF(); - cups_raster_t *raster; - pclmtoraster_data_t pclmtoraster_data; - conversion_function_t convert; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - if (parameters) - { - outformat = *(cf_filter_out_format_t *)parameters; - if (outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER && - outformat != CF_FILTER_OUT_FORMAT_PWG_RASTER && - outformat != CF_FILTER_OUT_FORMAT_APPLE_RASTER) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - else - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - "Apple Raster"))); - - // - // Open the input data stream specified by the inputfd... - // - - if ((inputfp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Unable to open input data stream."); - } - - return (1); - } - - if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Unable to copy PDF file: %s", - strerror(errno)); - fclose(inputfp); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) - bytes = write(fd, buffer, bytes); - - fclose(inputfp); - close(fd); - - filename = tempfile; - pdf->processFile(filename); - - if (parse_opts(data, outformat, &pclmtoraster_data) != 0) - { - delete(pdf); - unlink(tempfile); - return (1); - } - - if (pclmtoraster_data.header.cupsBitsPerColor != 1 - && pclmtoraster_data.header.cupsBitsPerColor != 2 - && pclmtoraster_data.header.cupsBitsPerColor != 4 - && pclmtoraster_data.header.cupsBitsPerColor != 8 - && pclmtoraster_data.header.cupsBitsPerColor != 16) - { - if(log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Specified color format is not supported: %s", - strerror(errno)); - delete(pdf); - unlink(tempfile); - return (1); - } - - if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_PLANAR) - pclmtoraster_data.nplanes = pclmtoraster_data.header.cupsNumColors; - else - pclmtoraster_data.nplanes = 1; - if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_BANDED) - pclmtoraster_data.nbands = pclmtoraster_data.header.cupsNumColors; - else - pclmtoraster_data.nbands = 1; - - if ((raster = cupsRasterOpen(outputfd, - (pclmtoraster_data.outformat == - CF_FILTER_OUT_FORMAT_CUPS_RASTER ? - CUPS_RASTER_WRITE : - (pclmtoraster_data.outformat == - CF_FILTER_OUT_FORMAT_PWG_RASTER ? - CUPS_RASTER_WRITE_PWG : - CUPS_RASTER_WRITE_APPLE)))) == 0) - { - if(log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Can't open raster stream: %s", - strerror(errno)); - delete(pdf); - unlink(tempfile); - return (1); - } - - std::vector pages = pdf->getAllPages(); - npages = pages.size(); - - for (int i = 0; i < npages; ++i) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Job canceled"); - break; - } - - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPCLmToRaster: Starting page %d.", i + 1); - if (out_page(raster, pages[i], i, log, ld, &pclmtoraster_data,data, - &convert) != 0) - break; - } - - cupsRasterClose(raster); - delete pdf; - unlink(tempfile); - return (0); -} - -void operator delete[](void *p) throw () -{ - free(p); -} diff --git a/cupsfilters/pdf.cxx b/cupsfilters/pdf.cxx deleted file mode 100644 index aad48816a..000000000 --- a/cupsfilters/pdf.cxx +++ /dev/null @@ -1,477 +0,0 @@ -// -// Copyright 2012 Canonical Ltd. -// Copyright 2013 ALT Linux, Andrew V. Stepanov -// Copyright 2018 Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include "pdf.h" -#include -#include -#include -#include -#include -#include -#include -#include - -// -// Useful reference: -// -// http://www.gnupdf.org/Indirect_Object -// http://www.gnupdf.org/Introduction_to_PDF -// http://blog.idrsolutions.com/2011/05/understanding-the-pdf-file-format-%E2%80%93-pdf-xref-tables-explained -// http://labs.appligent.com/pdfblog/pdf-hello-world/ -// https://github.com/OpenPrinting/cups-filters/pull/25 -// - - -// -// 'make_real_box()' - Return a QPDF array object of real values for a box. -// - -static QPDFObjectHandle // O - QPDFObjectHandle for an array -make_real_box(float values[4]) // I - Dimensions of the box in a float array -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - for (int i = 0; i < 4; ++i) - ret.appendItem(QPDFObjectHandle::newReal(values[i])); - return (ret); -} - - -// -// 'cfPDFLoadTemplate()' - Load an existing PDF file and do initial parsing -// using QPDF. -// - -extern "C" cf_pdf_t * -cfPDFLoadTemplate(const char *filename) // I - Filename to open -{ - QPDF *pdf = new QPDF(); - - try - { - pdf->processFile(filename); - } - catch(...) - { - delete pdf; - return (NULL); - } - - unsigned pages = (pdf->getAllPages()).size(); - - if (pages != 1) - { - delete pdf; - return (NULL); - } - - return (pdf); -} - - -// -// 'cfPDFFree()' - Free pointer used by PDF object -// - -extern "C" void -cfPDFFree(cf_pdf_t *pdf) // I - Pointer to PDF object -{ - delete pdf; -} - - -// -// 'cfPDFPages()' - Count number of pages in file using QPDF. -// - -int // O - Number of pages or -1 on error -cfPDFPages(const char *filename) // I - Filename to open -{ - QPDF *pdf = new QPDF(); - - if (pdf) - { - try - { - pdf->processFile(filename); - } - catch(...) - { - cfPDFFree(pdf); - return (-1); - } - int pages = (pdf->getAllPages()).size(); - cfPDFFree(pdf); - return (pages); - } else - return (-1); -} - - -// -// 'cfPDFPagesFP()' - Count number of pages in file -// using QPDF. -// - -int // O - Number of pages or -1 on error -cfPDFPagesFP(FILE *file) // I - Pointer to opened PDF file (stdio FILE*) -{ - QPDF *pdf = new QPDF(); - - if (pdf) - { - try - { - pdf->processFile("", file, false); - } - catch(...) - { - cfPDFFree(pdf); - return (-1); - } - int pages = (pdf->getAllPages()).size(); - cfPDFFree(pdf); - return (pages); - } else - return (-1); -} - - -// -// 'cfPDFPrependStream' - Prepend a stream to the contents of a specified -// page in PDF file. -// - -extern "C" int -cfPDFPrependStream(cf_pdf_t *pdf, // I - Pointer to QPDF object - unsigned page_num, // I - page number of page to prepend - // stream to - char const *buf, // I - buffer containing data to be - // prepended - size_t len) // I - length of buffer -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - - // get page contents stream / array - QPDFObjectHandle contents = page.getKey("/Contents"); - if (!contents.isStream() && !contents.isArray()) - return (1); - - // prepare the new stream which is to be prepended - PointerHolder stream_data = PointerHolder(new Buffer(len)); - memcpy(stream_data->getBuffer(), buf, len); - QPDFObjectHandle stream = QPDFObjectHandle::newStream(pdf, stream_data); - stream = pdf->makeIndirectObject(stream); - - // if the contents is an array, prepend the new stream to the array, - // else convert the contents to an array and the do the same. - if (contents.isStream()) - { - QPDFObjectHandle old_streamdata = contents; - contents = QPDFObjectHandle::newArray(); - contents.appendItem(old_streamdata); - } - - contents.insertItem(0, stream); - page.replaceKey("/Contents", contents); - - return (0); -} - - -// -// 'cfPDFAddType1Font()' - Add the specified type1 fontface to the specified -// page in a PDF document. -// - -extern "C" int -cfPDFAddType1Font(cf_pdf_t *pdf, // I - QPDF object - unsigned page_num, // I - Page number of the page to which - // the font is to be added - const char *name) // I - name of the font to be added -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - - QPDFObjectHandle resources = page.getKey("/Resources"); - if (!resources.isDictionary()) - return (1); - - QPDFObjectHandle font = QPDFObjectHandle::newDictionary(); - font.replaceKey("/Type", QPDFObjectHandle::newName("/Font")); - font.replaceKey("/Subtype", QPDFObjectHandle::newName("/Type1")); - font.replaceKey("/BaseFont", - QPDFObjectHandle::newName(std::string("/") + - std::string(name))); - - QPDFObjectHandle fonts = resources.getKey("/Font"); - if (fonts.isNull()) - fonts = QPDFObjectHandle::newDictionary(); - else if (!fonts.isDictionary()) - return (1); - - font = pdf->makeIndirectObject(font); - fonts.replaceKey("/bannertopdf-font", font); - resources.replaceKey("/Font", fonts); - - return (0); -} - - -// -// 'dict_lookup_rect()' - Lookup for an array of rectangle dimensions in a QPDF -// dictionary object. If it is found, store the values in -// an array and return true, else return false. -// - -static bool -dict_lookup_rect(QPDFObjectHandle object, // O - Key is found in the dictionary? - std::string const& key, // I - PDF dictionary object - float rect[4], // I - Key to lookup - bool inheritable) // I - Array to store values if key - // is found -{ - // preliminary checks - if (!object.isDictionary()) - return (false); - - QPDFObjectHandle value; - if (!object.hasKey(key) && inheritable) - { - QPDFFormFieldObjectHelper helper(object); - value = helper.getInheritableFieldValue(key); - if (value.isNull()) - return (false); - } - else - value = object.getKey(key); - - // check if the key is array or some other type - if (!value.isArray()) - return (false); - - // get values in a vector and assign it to rect - std::vector array = value.getArrayAsVector(); - for (int i = 0; i < 4; ++i) - { - // if the value in the array is not real, we have an invalid array - if (!array[i].isReal() && !array[i].isInteger()) - return (false); - - rect[i] = array[i].getNumericValue(); - } - - return (array.size() == 4); -} - - -// -// 'fit_rect()' - Update the scale of the page using old media box dimensions -// and new media box dimensions. -// - -static void -fit_rect(float oldrect[4], // I - Old media box - float newrect[4], // I - New media box - float *scale) // I - Pointer to scale which needs to be updated -{ - float oldwidth = oldrect[2] - oldrect[0]; - float oldheight = oldrect[3] - oldrect[1]; - float newwidth = newrect[2] - newrect[0]; - float newheight = newrect[3] - newrect[1]; - - *scale = newwidth / oldwidth; - if (oldheight * *scale > newheight) - *scale = newheight / oldheight; -} - - -// -// 'cfPDFResizePage()' - Resize page in a PDF with the given dimensions. -// - -extern "C" int -cfPDFResizePage(cf_pdf_t *pdf, // I - Pointer to QPDF object - unsigned page_num, // I - Page number - float width, // I - Width of page to set - float length, // I - Length of page to set - float *scale) // I - Scale of page to set -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - float new_mediabox[4] = { 0.0, 0.0, width, length }; - float old_mediabox[4]; - QPDFObjectHandle media_box; - - if (!dict_lookup_rect(page, "/MediaBox", old_mediabox, true)) - return (1); - - fit_rect(old_mediabox, new_mediabox, scale); - media_box = make_real_box(new_mediabox); - - page.replaceKey("/ArtBox", media_box); - page.replaceKey("/BleedBox", media_box); - page.replaceKey("/CropBox", media_box); - page.replaceKey("/MediaBox", media_box); - page.replaceKey("/TrimBox", media_box); - - return (0); -} - - -// -// 'cfPDFDuplicatePage()' - Duplicate a specified pdf page in a PDF -// - -extern "C" int -cfPDFDuplicatePage (cf_pdf_t *pdf, // I - Pointer to QPDF object - unsigned page_num, // I - Page number of the page to be - // duplicated - unsigned count) // I - Number of copies to be duplicated -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - for (unsigned i = 0; i < count; ++i) - { - page = pdf->makeIndirectObject(page); - pdf->addPage(page, false); - } - - return (0); -} - - -// -// 'cfPDFWrite()' - Write the contents of PDF object to an already open FILE*. -// - -extern "C" void -cfPDFWrite(cf_pdf_t *pdf, // I - Pointer to QPDF structure - FILE *file) // I - File pointer to write to -{ - QPDFWriter output(*pdf, "cfPDFWrite", file, false); - output.write(); -} - - -// -// 'lookup_opt()' - Get value according to key in the options list. -// - -static std::string // O - character string which corresponds - // to the value of the key or NULL if - // key is not found in the list. -lookup_opt(cf_opt_t *opt, // I - pointer to the cf_opt_t type list - std::string const& key) // I - key to be found in the list -{ - if (!opt || key.empty()) - return (""); - - while (opt) - { - if (opt->key && opt->val) - { - if (strcmp(opt->key, key.c_str()) == 0) - return (std::string(opt->val)); - } - opt = opt->next; - } - - return (""); -} - - -// -// 'cfPDFFillForm()' - 1. Look for form in PDF template file -// 2. Look for form fields' names -// 3. Fill recognized fields with information -// - -extern "C" int // O - Status of form fill - 0 for success, - // 1 for failure -cfPDFFillForm(cf_pdf_t *doc, // I - Pointer to the QPDF structure - cf_opt_t *opt) // I - Pointer to the cf_opt_t type list -{ - // Initialize AcroFormDocumentHelper and PageDocumentHelper objects - // to work with forms in the PDF - QPDFAcroFormDocumentHelper afdh(*doc); - QPDFPageDocumentHelper pdh(*doc); - - // Check if the PDF has a form or not - if (!afdh.hasAcroForm()) - return 1; - - // Get the first page from the PDF to fill the form. Since this - // is a banner file, it must contain only a single page, and that - // check has already been performed in the `cfPDFLoadTemplate()` function - std::vector pages = pdh.getAllPages(); - if (pages.empty()) - return 1; - QPDFPageObjectHelper page = pages.front(); - - // Get the annotations in the page - std::vector annotations = - afdh.getWidgetAnnotationsForPage(page); - - for (std::vector::iterator annot_iter = - annotations.begin(); - annot_iter != annotations.end(); - ++annot_iter) - { - // For each annotation, find its associated field. If it's a - // text field, we try to set its value. This will automatically - // update the document to indicate that appearance streams need - // to be regenerated. At the time of this writing, QPDF doesn't - // have any helper code to assist with appearance stream generation, - // though there's nothing that prevents it from being possible. - QPDFFormFieldObjectHelper ffh = - afdh.getFieldForAnnotation(*annot_iter); - if (ffh.getFieldType() == "/Tx") - { - // Look in the options setting for the value of this field and fill the - // value accordingly. This will automatically set /NeedAppearances to - // true. - std::string const name = ffh.getFullyQualifiedName(); - std::string fill_with = lookup_opt(opt, name); - if (fill_with.empty()) - { - std::cerr << "DEBUG: Lack information for widget: " << name << ".\n"; - fill_with = "N/A"; - } - - // Convert the 'fill_with' string to UTF16 before filling to the widget - QPDFObjectHandle fill_with_utf_16 = - QPDFObjectHandle::newUnicodeString(fill_with); - ffh.setV(fill_with_utf_16); - std::cerr << "DEBUG: Fill widget name " << name << " with value " - << fill_with_utf_16.getUTF8Value() << ".\n"; - } - } - - // Status 0 notifies that the function successfully filled all the - // identifiable fields in the form - return (0); -} diff --git a/cupsfilters/pdf.h b/cupsfilters/pdf.h deleted file mode 100644 index 14f198200..000000000 --- a/cupsfilters/pdf.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright 2012 Canonical Ltd. -// Copyright 2018 Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDF_H_ -#define _CUPS_FILTERS_PDF_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct QPDF cf_pdf_t; - -typedef struct _cf_opt cf_opt_t; - -// -// Type to bunch PDF form field name and its value. -// - -struct _cf_opt -{ - const char* key; - const char* val; - cf_opt_t *next; -}; - -cf_pdf_t *cfPDFLoadTemplate(const char *filename); -void cfPDFFree(cf_pdf_t *pdf); -void cfPDFWrite(cf_pdf_t *doc, FILE *file); -int cfPDFPrependStream(cf_pdf_t *doc, unsigned page, char const *buf, - size_t len); -int cfPDFAddType1Font(cf_pdf_t *doc, unsigned page, const char *name); -int cfPDFResizePage(cf_pdf_t *doc, unsigned page, float width, float length, - float *scale); -int cfPDFDuplicatePage(cf_pdf_t *doc, unsigned page, unsigned count); -int cfPDFFillForm(cf_pdf_t *doc, cf_opt_t *opt); -int cfPDFPages(const char *filename); -int cfPDFPagesFP(FILE *file); - -#ifdef __cplusplus -} -#endif - -#endif // !_CUPS_FILTERS_PDF_H_ diff --git a/cupsfilters/pdftopdf/intervalset-private.h b/cupsfilters/pdftopdf/intervalset-private.h deleted file mode 100644 index 0fc48ca65..000000000 --- a/cupsfilters/pdftopdf/intervalset-private.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ -#define _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ - -#include "pdftopdf-private.h" -#include -#include - -class _cfPDFToPDFIntervalSet -{ - typedef int key_t; // TODO?! template - typedef std::pair value_t; - typedef std::vector data_t; - public: - static const key_t npos; - - void clear(); - // [start; end) ! - void add(key_t start, key_t end = npos); - void finish(); - - size_t size() const { return data.size(); } - - // only after finish() has been called: - bool contains(key_t val) const; - key_t next(key_t val) const; - - void dump(pdftopdf_doc_t *doc) const; - private: - // currently not used - bool intersect(const value_t &a, const value_t &b) const; - void unite(value_t &aret, const value_t &b) const; - private: - data_t data; -}; - -#endif // !_CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ diff --git a/cupsfilters/pdftopdf/intervalset.cxx b/cupsfilters/pdftopdf/intervalset.cxx deleted file mode 100644 index 38b1b7797..000000000 --- a/cupsfilters/pdftopdf/intervalset.cxx +++ /dev/null @@ -1,149 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "intervalset-private.h" -#include -#include "cupsfilters/debug-internal.h" -#include -#include - -const _cfPDFToPDFIntervalSet::key_t _cfPDFToPDFIntervalSet::npos = - std::numeric_limits<_cfPDFToPDFIntervalSet::key_t>::max(); - -void -_cfPDFToPDFIntervalSet::clear() // {{{ -{ - data.clear(); -} -// }}} - -void -_cfPDFToPDFIntervalSet::add(key_t start, - key_t end) // {{{ -{ - if (start < end) - data.push_back(std::make_pair(start, end)); -} -// }}} - -void -_cfPDFToPDFIntervalSet::finish() // {{{ -{ - data_t::iterator it = data.begin(), - end = data.end(), - pos = it; - if (it == end) - return; - - std::sort(it, end); - - while (1) - { - ++ it; - if (it == end) - { - ++ pos; - break; - } - if (pos->second >= it->first) - pos->second = it->second; - else - { - ++ pos; - if (pos != it) - *pos = *it; - } - } - - data.erase(pos, data.end()); -} -// }}} - -bool -_cfPDFToPDFIntervalSet::contains(key_t val) const // {{{ -{ - data_t::const_iterator it = - std::upper_bound(data.begin(), data.end(), std::make_pair(val, npos)); - if (it == data.begin()) - return false; - -- it; - return (val < it->second); -} -// }}} - -_cfPDFToPDFIntervalSet::key_t -_cfPDFToPDFIntervalSet::next(key_t val) const // {{{ -{ - val ++; - data_t::const_iterator it = - std::upper_bound(data.begin(), data.end(), std::make_pair(val, npos)); - if (it == data.begin()) - { - if (it == data.end()) // empty - return (npos); - return (it->first); - } - -- it; - if (val < it->second) - return (val); - ++ it; - if (it == data.end()) - return npos; - return (it->first); -} -// }}} - -bool -_cfPDFToPDFIntervalSet::intersect(const value_t &a, - const value_t &b) const // {{{ -{ - return (((a.first >= b.first) && (a.first < b.second)) || - ((b.first >= a.first) && (b.first < a.second))); -} -// }}} - -void -_cfPDFToPDFIntervalSet::unite(value_t &aret, - const value_t &b) const // {{{ -{ - DEBUG_assert(intersect(aret, b)); - if (b.first < aret.first) - aret.first = b.first; - if (b.second > aret.second) - aret.second = b.second; -} -// }}} - -void -_cfPDFToPDFIntervalSet::dump(pdftopdf_doc_t *doc) const // {{{ -{ - int len = data.size(); - if (len == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (empty)"); - return; - } - len --; - for (int iA = 0; iA < len; iA ++) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,%d)", - data[iA].first, data[iA].second); - } - if (data[len].second == npos) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,inf)", - data[len].first); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,%d)", - data[len].first, data[len].second); - } -} -// }}} diff --git a/cupsfilters/pdftopdf/nup-private.h b/cupsfilters/pdftopdf/nup-private.h deleted file mode 100644 index 53f688b2f..000000000 --- a/cupsfilters/pdftopdf/nup-private.h +++ /dev/null @@ -1,108 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_NUP_H_ -#define _CUPS_FILTERS_PDFTOPDF_NUP_H_ - -#include "pptypes-private.h" -#include - -// you have to provide this -struct _cfPDFToPDFNupParameters -{ - _cfPDFToPDFNupParameters() - : nupX(1), nupY(1), - width(NAN), height(NAN), - landscape(false), - first(X), - xstart(LEFT), ystart(TOP), - xalign(CENTER), yalign(CENTER) - {} - - // --- "calculated" parameters --- - int nupX, nupY; - float width, height; - bool landscape; // post-rotate! - - // --- other settings --- - // ordering - pdftopdf_axis_e first; - pdftopdf_position_e xstart, ystart; - - pdftopdf_position_e xalign, yalign; - - static bool possible(int nup); // TODO? float in_ratio, float out_ratio - static void preset(int nup, _cfPDFToPDFNupParameters &ret); - static float calculate(int nup, float in_ratio, float out_ratio, - _cfPDFToPDFNupParameters &ret); // returns "quality", - // 1 is best - - void dump(pdftopdf_doc_t *doc) const; -}; - -// you get this -struct _cfPDFToPDFNupPageEdit -{ - // required transformation: first translate, then scale - float xpos, ypos; // TODO: already given by sub.left, sub.bottom - // [but for rotation?] - float scale; // uniform - - // ? "landscape" e.g. to rotate labels - - // for border, clip, ... - // also stores in_width/in_height, unscaled! - // everything in "outer"-page coordinates - _cfPDFToPDFPageRect sub; - - void dump(pdftopdf_doc_t *doc) const; -}; - -// -// This class does the number-up calculation. Example: -// -// _cfPDFToPDFNupParameters param; -// param.xyz = ...; // fill it with your data! -// -// _cfPDFToPDFNupState nup(param); -// _cfPDFToPDFNupPageEdit edit; -// for (auto page : your_pages) -// { -// bool newPage = nup.mext_page(page.w, page.h, edit); // w, h from input -// // page -// // create newPage, if required; then place current page as specified -// // in edit -// } -// - -class _cfPDFToPDFNupState -{ -public: - _cfPDFToPDFNupState(const _cfPDFToPDFNupParameters ¶m); - - void reset(); - - // will overwrite ret with the new parameters - // returns true, if a new output page should be started first - bool mext_page(float in_width, float in_height, _cfPDFToPDFNupPageEdit &ret); - -private: - std::pair convert_order(int subpage) const; - void calculate_edit(int subx, int suby, _cfPDFToPDFNupPageEdit &ret) const; - -private: - _cfPDFToPDFNupParameters param; - - int in_pages, out_pages; - int nup; // max. per page (== nupX * nupY) - int subpage; // on the current output-page -}; - -// TODO? elsewhere -// parsing functions for cups parameters (will not calculate nupX, nupY!) -bool _cfPDFToPDFParseNupLayout(const char *val, _cfPDFToPDFNupParameters &ret); - // lrtb, btlr, ... - -#endif // !_CUPS_FILTERS_PDFTOPDF_NUP_H_ diff --git a/cupsfilters/pdftopdf/nup.cxx b/cupsfilters/pdftopdf/nup.cxx deleted file mode 100644 index 2e1dc341d..000000000 --- a/cupsfilters/pdftopdf/nup.cxx +++ /dev/null @@ -1,312 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "nup-private.h" -#include -#include "cupsfilters/debug-internal.h" -#include -#include - -void -_cfPDFToPDFNupParameters::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: NupX: %d, NupY: %d, " - "width: %f, height: %f", - nupX, nupY, - width, height); - - int opos = -1, - fpos = -1, - spos = -1; - - if (xstart == pdftopdf_position_e::LEFT) // or Bottom - fpos = 0; - else if (xstart == pdftopdf_position_e::RIGHT) // or Top - fpos = 1; - if (ystart == pdftopdf_position_e::LEFT) // or Bottom - spos = 0; - else if (ystart == pdftopdf_position_e::RIGHT) // or Top - spos = 1; - if (first == pdftopdf_axis_e::X) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: First Axis: X"); - opos = 0; - } - else if (first == pdftopdf_axis_e::Y) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: First Axis: Y"); - opos = 2; - std::swap(fpos, spos); - } - - if ((opos == -1) || (fpos == -1) || (spos == -1)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Bad Spec: %d; start: %d, %d", - first, xstart, ystart); - } - else - { - static const char *order[4] = {"lr", "rl", "bt", "tb"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Order: %s%s", - order[opos + fpos], - order[(opos + 2) % 4 + spos]); - } - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Alignment:"); - _cfPDFToPDFPositionDump(xalign, pdftopdf_axis_e::X,doc); - _cfPDFToPDFPositionDump(yalign, pdftopdf_axis_e::Y,doc); -} -// }}} - -bool -_cfPDFToPDFNupParameters::possible(int nup) // {{{ -{ - // 1 2 3 4 6 8 9 10 12 15 16 - return ((nup >= 1) && (nup <= 16) && - ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && - (nup != 14))); -} -// }}} - -void -_cfPDFToPDFNupParameters::preset(int nup, - _cfPDFToPDFNupParameters &ret) // {{{ -{ - switch (nup) - { - case 1: - ret.nupX=1; - ret.nupY=1; - break; - case 2: - ret.nupX=2; - ret.nupY=1; - ret.landscape=true; - break; - case 3: - ret.nupX=3; - ret.nupY=1; - ret.landscape=true; - break; - case 4: - ret.nupX=2; - ret.nupY=2; - break; - case 6: - ret.nupX=3; - ret.nupY=2; - ret.landscape=true; - break; - case 8: - ret.nupX=4; - ret.nupY=2; - ret.landscape=true; - break; - case 9: - ret.nupX=3; - ret.nupY=3; - break; - case 10: - ret.nupX=5; - ret.nupY=2; - ret.landscape=true; - break; - case 12: - ret.nupX=3; - ret.nupY=4; - break; - case 15: - ret.nupX=5; - ret.nupY=3; - ret.landscape=true; - break; - case 16: - ret.nupX=4; - ret.nupY=4; - break; - } -} -// }}} - - -_cfPDFToPDFNupState::_cfPDFToPDFNupState(const _cfPDFToPDFNupParameters ¶m) // {{{ - : param(param), - in_pages(0), out_pages(0), - nup(param.nupX * param.nupY), - subpage(nup) -{ - DEBUG_assert((param.nupX > 0) && (param.nupY > 0)); -} -// }}} - -void -_cfPDFToPDFNupState::reset() // {{{ -{ - in_pages = 0; - out_pages = 0; -// nup = param.nupX * param.nupY; - subpage = nup; -} -// }}} - -void _cfPDFToPDFNupPageEdit::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", - xpos, ypos, scale); - sub.dump(doc); -} -// }}} - -std::pair -_cfPDFToPDFNupState::convert_order(int subpage) const // {{{ -{ - int subx, suby; - if (param.first==pdftopdf_axis_e::X) - { - subx = subpage % param.nupX; - suby = subpage / param.nupX; - } - else - { - subx = subpage / param.nupY; - suby = subpage % param.nupY; - } - - subx = (param.nupX - 1) * (param.xstart + 1) / 2 - param.xstart * subx; - suby = (param.nupY - 1) * (param.ystart + 1) / 2 - param.ystart * suby; - - return (std::make_pair(subx, suby)); -} -// }}} - -static inline float -lin(pdftopdf_position_e pos, - float size) // {{{ -{ - if (pos == -1) - return (0); - else if (pos == 0) - return (size / 2); - else if (pos == 1) - return (size); - return (size * (pos + 1) / 2); -} -// }}} - -void -_cfPDFToPDFNupState::calculate_edit(int subx, - int suby, - _cfPDFToPDFNupPageEdit &ret) const // {{{ -{ - // dimensions of a "nup cell" - const float width = param.width / param.nupX, - height = param.height / param.nupY; - - // first calculate only for bottom-left corner - ret.xpos = subx * width; - ret.ypos = suby * height; - - const float scalex = width / ret.sub.width, - scaley = height / ret.sub.height; - float subwidth = ret.sub.width * scaley, - subheight = ret.sub.height * scalex; - - // TODO? if ((!fitPlot) && (ret.scale > 1)) ret.scale = 1.0; - if (scalex > scaley) - { - ret.scale = scaley; - subheight = height; - ret.xpos += lin(param.xalign, width-subwidth); - } - else - { - ret.scale = scalex; - subwidth = width; - ret.ypos += lin(param.yalign, height-subheight); - } - - ret.sub.left = ret.xpos; - ret.sub.bottom = ret.ypos; - ret.sub.right = ret.sub.left + subwidth; - ret.sub.top = ret.sub.bottom + subheight; -} -// }}} - -bool -_cfPDFToPDFNupState::mext_page(float in_width, - float in_height, - _cfPDFToPDFNupPageEdit &ret) // {{{ -{ - in_pages ++; - subpage ++; - if (subpage >= nup) - { - subpage = 0; - out_pages ++; - } - - ret.sub.width = in_width; - ret.sub.height = in_height; - - auto sub = convert_order(subpage); - calculate_edit(sub.first, sub.second, ret); - - return (subpage == 0); -} -// }}} - - -static std::pair -parsePosition(char a, - char b) // {{{ returns ,CENTER(0) on invalid -{ - a |= 0x20; // make lowercase - b |= 0x20; - if ((a == 'l') && (b == 'r')) - return (std::make_pair(pdftopdf_axis_e::X, pdftopdf_position_e::LEFT)); - else if ((a == 'r') && (b == 'l')) - return (std::make_pair(pdftopdf_axis_e::X, pdftopdf_position_e::RIGHT)); - else if ((a == 't') && (b == 'b')) - return (std::make_pair(pdftopdf_axis_e::Y, pdftopdf_position_e::TOP)); - else if ((a == 'b') && (b == 't')) - return (std::make_pair(pdftopdf_axis_e::Y, pdftopdf_position_e::BOTTOM)); - return (std::make_pair(pdftopdf_axis_e::X, pdftopdf_position_e::CENTER)); -} -// }}} - -bool -_cfPDFToPDFParseNupLayout(const char *val, - _cfPDFToPDFNupParameters &ret) // {{{ -{ - DEBUG_assert(val); - auto pos0 = parsePosition(val[0], val[1]); - if (pos0.second == CENTER) - return (false); - auto pos1 = parsePosition(val[2], val[3]); - if ((pos1.second == CENTER) || (pos0.first == pos1.first)) - return (false); - - ret.first = pos0.first; - if (ret.first == pdftopdf_axis_e::X) - { - ret.xstart = pos0.second; - ret.ystart = pos1.second; - } - else - { - ret.xstart = pos1.second; - ret.ystart = pos0.second; - } - - return (val[4] == 0); // everything seen? -} -// }}} diff --git a/cupsfilters/pdftopdf/pdftopdf-private.h b/cupsfilters/pdftopdf/pdftopdf-private.h deleted file mode 100644 index 4e1d8fc5f..000000000 --- a/cupsfilters/pdftopdf/pdftopdf-private.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright 2020 by Jai Luthra. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H -#define _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H - -#include - -typedef struct // **** Document information **** -{ - cf_logfunc_t logfunc; // Log function - void *logdata; // Log data - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled - // function, can be NULL -} pdftopdf_doc_t; - -#endif // !_CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdftopdf-processor-private.h deleted file mode 100644 index 92ed198e5..000000000 --- a/cupsfilters/pdftopdf/pdftopdf-processor-private.h +++ /dev/null @@ -1,239 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_PROCESSOR_H -#define _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_PROCESSOR_H - -#include "pptypes-private.h" -#include "nup-private.h" -#include "pdftopdf-private.h" -#include "intervalset-private.h" -#include -#include -#include -#include - -enum pdftopdf_booklet_mode_e { - CF_PDFTOPDF_BOOKLET_OFF, - CF_PDFTOPDF_BOOKLET_ON, - CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE -}; - -struct _cfPDFToPDFProcessingParameters { -_cfPDFToPDFProcessingParameters() -: job_id(0), - num_copies(1), - user(0), - title(0), - pagesize_requested(false), - fitplot(false), - fillprint(false), //print-scaling = fill - cropfit(false), - autoprint(false), - autofit(false), - fidelity(false), - no_orientation(false), - orientation(ROT_0), - normal_landscape(ROT_270), - paper_is_landscape(false), - duplex(false), - border(NONE), - reverse(false), - - page_label(), - even_pages(true), - odd_pages(true), - - mirror(false), - - xpos(CENTER), - ypos(CENTER), - - collate(false), - even_duplex(false), - - booklet(CF_PDFTOPDF_BOOKLET_OFF), - book_signature(-1), - - auto_rotate(false), - - device_copies(1), - device_collate(false), - set_duplex(false), - - page_logging(-1) - { - page.width = 612.0; // Letter - page.height = 792.0; - page.top = page.height - 36.0; - page.bottom = 36.0; - page.left = 18.0; - page.right = page.width - 18.0; - - // everything - input_page_ranges.add(1); - input_page_ranges.finish(); - page_ranges.add(1); - page_ranges.finish(); - } - - int job_id, num_copies; - const char *user, *title; // will stay around - bool pagesize_requested; - bool fitplot; - bool fillprint; //print-scaling = fill - bool cropfit; // -o crop-to-fit - bool autoprint; // print-scaling = auto - bool autofit; // print-scaling = auto-fit - bool fidelity; - bool no_orientation; - _cfPDFToPDFPageRect page; - pdftopdf_rotation_e orientation,normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 - bool paper_is_landscape; - bool duplex; - pdftopdf_border_type_e border; - _cfPDFToPDFNupParameters nup; - bool reverse; - - std::string page_label; - bool even_pages, odd_pages; - _cfPDFToPDFIntervalSet page_ranges; - _cfPDFToPDFIntervalSet input_page_ranges; - - bool mirror; - - pdftopdf_position_e xpos, ypos; - - bool collate; - - bool even_duplex; // make number of pages a multiple of 2 - - pdftopdf_booklet_mode_e booklet; - int book_signature; - - bool auto_rotate; - - int device_copies; - bool device_collate; - bool set_duplex; - - int page_logging; - int copies_to_be_logged; - - // helper functions - bool with_page(int outno) const; // 1 based - bool have_page(int pageno) const; //1 based - void dump(pdftopdf_doc_t *doc) const; -}; - -enum pdftopdf_arg_ownership_e { - CF_PDFTOPDF_WILL_STAY_ALIVE, - CF_PDFTOPDF_MUST_DUPLICATE, - CF_PDFTOPDF_TAKE_OWNERSHIP -}; - -class _cfPDFToPDFPageHandle { - public: - virtual ~_cfPDFToPDFPageHandle() {} - - virtual _cfPDFToPDFPageRect get_rect() const = 0; - - // fscale: inverse_scale (from nup, fitplot) - - virtual void add_border_rect(const _cfPDFToPDFPageRect &rect, - pdftopdf_border_type_e border, float fscale) = 0; - - // TODO?! add standalone crop(...) method (not only for subpages) - - virtual pdftopdf_rotation_e crop(const _cfPDFToPDFPageRect &cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, - pdftopdf_position_e ypos, - bool scale, bool autorotate, - pdftopdf_doc_t *doc) = 0; - - virtual bool is_landscape(pdftopdf_rotation_e orientation) = 0; - - virtual void add_subpage(const std::shared_ptr<_cfPDFToPDFPageHandle> &sub, - float xpos, float ypos, float scale, - const _cfPDFToPDFPageRect *crop=NULL) = 0; - - virtual void mirror() = 0; - - virtual void rotate(pdftopdf_rotation_e rot) = 0; - - virtual void add_label(const _cfPDFToPDFPageRect &rect, - const std::string label) = 0; -}; - -// TODO: ... error output? -class _cfPDFToPDFProcessor { // abstract interface - public: - virtual ~_cfPDFToPDFProcessor() {} - - // TODO: ... qpdf wants password at load time - virtual bool load_file(FILE *f,pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take = - CF_PDFTOPDF_WILL_STAY_ALIVE, - int flatten_forms = 1) = 0; - - virtual bool load_filename(const char *name, pdftopdf_doc_t *doc, - int flatten_forms = 1) = 0; - - // TODO? virtual bool may_modify/may_print/? - virtual bool check_print_permissions(pdftopdf_doc_t *doc) = 0; - - virtual std::vector> - get_pages(pdftopdf_doc_t *doc) = 0; // shared_ptr because of type - // erasure (deleter) - - virtual std::shared_ptr<_cfPDFToPDFPageHandle> - new_page(float width, float height, pdftopdf_doc_t *doc) = 0; - - virtual void add_page(std::shared_ptr<_cfPDFToPDFPageHandle> page, - bool front) = 0; // at back/front -- either from - // get_pages() or - // new_page()+add_subpage()-calls - // (or [also allowed]: empty) - - // void remove_page(std::shared_ptr<_cfPDFToPDFPageHandle> ph); - // not needed: we construct from scratch, at least conceptually. - - virtual void multiply(int copies, bool collate) = 0; - - virtual void auto_rotate_all(bool dst_lscape, - pdftopdf_rotation_e normal_landscape) = 0; - // TODO elsewhere?! - virtual void add_cm(const char *defaulticc, const char *outputicc) = 0; - - virtual void set_comments(const std::vector &comments) = 0; - - virtual void emit_file(FILE *dst,pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take = - CF_PDFTOPDF_WILL_STAY_ALIVE) = 0; - virtual void emit_filename(const char *name,pdftopdf_doc_t *doc) = 0; - // NULL -> stdout - - virtual bool has_acro_form() = 0; -}; - -class _cfPDFToPDFFactory { - public: - // never NULL, but may throw. - static _cfPDFToPDFProcessor *processor(); -}; - -//bool _cfPDFToPDFCheckBookletSignature(int signature) -// { return (signature%4==0); } - -std::vector _cfPDFToPDFBookletShuffle(int numPages, int signature = -1); - -// This is all we want: -bool _cfProcessPDFToPDF(_cfPDFToPDFProcessor &proc, - _cfPDFToPDFProcessingParameters ¶m, - pdftopdf_doc_t *doc); - -#endif // !_CUPS_FILTERS_PDFTOPDF_PDFTOPDF_PROCESSOR_H diff --git a/cupsfilters/pdftopdf/pdftopdf-processor.cxx b/cupsfilters/pdftopdf/pdftopdf-processor.cxx deleted file mode 100644 index e422f507f..000000000 --- a/cupsfilters/pdftopdf/pdftopdf-processor.cxx +++ /dev/null @@ -1,521 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "pdftopdf-processor-private.h" -#include "qpdf-pdftopdf-processor-private.h" -#include -#include "cupsfilters/debug-internal.h" -#include - -void -BookletMode_dump(pdftopdf_booklet_mode_e bkm, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *bstr[3] = {"Off", "On", "Shuffle-Only"}; - - if ((bkm < CF_PDFTOPDF_BOOKLET_OFF) || - (bkm > CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Booklet mode: (Bad booklet mode: %d)", - bkm); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Booklet mode: %s", - bstr[bkm]); - } -} -// }}} - -bool -_cfPDFToPDFProcessingParameters::with_page(int outno) const // {{{ -{ - if (outno % 2 == 0) - { // 1-based - if (!even_pages) - return (false); - } - else if (!odd_pages) - return (false); - return (page_ranges.contains(outno)); -} -// }}} - -bool -_cfPDFToPDFProcessingParameters::have_page(int pageno) const -{ - return (input_page_ranges.contains(pageno)); -} - -void -_cfPDFToPDFProcessingParameters::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: job_id: %d, num_copies: %d", - job_id, num_copies); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: user: %s, title: %s", - (user) ? user : "(null)", - (title) ? title : "(null)"); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: fitplot: %s", - (fitplot) ? "true" : "false"); - - page.dump(doc); - - _cfPDFToPDFRotationDump(orientation, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: paper_is_landscape: %s", - (paper_is_landscape) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: duplex: %s", - (duplex) ? "true" : "false"); - - _cfPDFToPDFBorderTypeDump(border, doc); - - nup.dump(doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: reverse: %s", - (reverse) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: even_pages: %s, odd_pages: %s", - (even_pages) ? "true" : "false", - (odd_pages) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: input page range:"); - input_page_ranges.dump(doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: page range:"); - page_ranges.dump(doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: mirror: %s", - (mirror) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position:"); - _cfPDFToPDFPositionDump(xpos, pdftopdf_axis_e::X,doc); - _cfPDFToPDFPositionDump(ypos, pdftopdf_axis_e::Y,doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: collate: %s", - (collate) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: even_duplex: %s", - (even_duplex) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: page_label: %s", - page_label.empty () ? "(none)" : - page_label.c_str()); - - BookletMode_dump(booklet,doc); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: booklet signature: %d", - book_signature); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: auto_rotate: %s", - (auto_rotate) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: device_copies: %d", - device_copies); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: device_collate: %s", - (device_collate) ? "true" : "false"); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: set_duplex: %s", - (set_duplex) ? "true" : "false"); -} -// }}} - - -_cfPDFToPDFProcessor -*_cfPDFToPDFFactory::processor() -{ - return new _cfPDFToPDFQPDFProcessor(); -} - -// -// (1-based) -// 9: [*] [1] [2] [*] [*] [3] [4] [9] [8] [5] [6] [7] -// 1 2 3 4 5 6 7 8 9 10 11 12 -// -// -> signature = 12 = 3*4 = ((9+3)/4)*4 -// -// NOTE: psbook always fills the sig completely (results in completely -// white pages (4-set), depending on the input) -// -// empty pages must be added for output values >= numPages -// - -std::vector -_cfPDFToPDFBookletShuffle(int numPages, - int signature) // {{{ -{ - if (signature < 0) - signature = (numPages + 3) & ~0x3; - DEBUG_assert(signature % 4 == 0); - - std::vector ret; - ret.reserve(numPages + signature - 1); - - int curpage = 0; - while (curpage < numPages) - { - // as long as pages to be done -- i.e. multiple times the signature - int firstpage = curpage, - lastpage = curpage + signature - 1; - // one signature - while (firstpage < lastpage) - { - ret.push_back(lastpage --); - ret.push_back(firstpage ++); - ret.push_back(firstpage ++); - ret.push_back(lastpage --); - } - curpage += signature; - } - return (ret); -} -// }}} - -bool -_cfProcessPDFToPDF(_cfPDFToPDFProcessor &proc, - _cfPDFToPDFProcessingParameters ¶m, - pdftopdf_doc_t *doc) // {{{ -{ - if (!proc.check_print_permissions(doc)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Not allowed to print"); - return false; - } - - // Certain features require a given page size for the page to be - // printed or all pages of the document being the same size. Here we - // set param.pagesize_requested so that the default pag size is used - // when no size got specified by the user. - if (param.fitplot || param.fillprint || param.autoprint || param.autofit || - param.booklet != CF_PDFTOPDF_BOOKLET_OFF || - param.nup.nupX > 1 || param.nup.nupY > 1) - param.pagesize_requested = true; - - const bool dst_lscape = - (param.paper_is_landscape == - ((param.orientation == ROT_0) || (param.orientation == ROT_180))); - - if (param.paper_is_landscape) - std::swap(param.nup.nupX, param.nup.nupY); - - if (param.auto_rotate) - proc.auto_rotate_all(dst_lscape, param.normal_landscape); - - std::vector> pages = - proc.get_pages(doc); - - std::vector> input_page_range_list; - - for (int i = 1; i <= (int)pages.size(); i ++) - if (param.have_page(i)) - input_page_range_list.push_back(pages[i - 1]); - - const int numOrigPages = input_page_range_list.size(); - - // TODO FIXME? elsewhere - std::vector shuffle; - if (param.booklet != CF_PDFTOPDF_BOOKLET_OFF) - { - shuffle = _cfPDFToPDFBookletShuffle(numOrigPages, param.book_signature); - if (param.booklet == CF_PDFTOPDF_BOOKLET_ON) - { // override options - // We do not "sides=two-sided-short-edge" / DuplexTumble here. - // We assume it done by caller, for example ppdFilterLoadPPD() of libppd - // param.duplex=true; - // param.set_duplex=true; - _cfPDFToPDFNupParameters::preset(2, param.nup); // TODO?! better - } - } - else - { // 0 1 2 3 ... - shuffle.resize(numOrigPages); - std::iota(shuffle.begin(), shuffle.end(), 0); - } - const int numPages=std::max(shuffle.size(), input_page_range_list.size()); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: \"print-scaling\" IPP attribute: %s", - (param.autoprint ? "auto" : - (param.autofit ? "auto-fit" : - (param.fitplot ? "fit" : - (param.fillprint ? "fill" : - (param.cropfit ? "none" : - "Not defined, should never happen")))))); - - if (param.autoprint || param.autofit) - { - bool margin_defined = true; - bool document_large = false; - int pw = param.page.right - param.page.left; - int ph = param.page.top - param.page.bottom; - - if ((param.page.width == pw) && (param.page.height == ph)) - margin_defined = false; - - for (int i = 0; i < (int)input_page_range_list.size(); i ++) - { - _cfPDFToPDFPageRect r = input_page_range_list[i]->get_rect(); - int w = r.width * 100 / 102; // 2% of tolerance - int h = r.height * 100 / 102; - if ((w > param.page.width || h > param.page.height) && - (h > param.page.width || w > param.page.height)) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Page %d too large for output page size, scaling pages to fit.", - i + 1); - document_large = true; - } - } - if (param.fidelity && doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit."); - - if (param.autoprint) - { - if (param.fidelity || document_large) - { - if (margin_defined) - param.fitplot = true; - else - param.fillprint = true; - } - else - param.cropfit = true; - } - else - { - if (param.fidelity || document_large) - param.fitplot = true; - else - param.cropfit = true; - } - } - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Print scaling mode: %s", - (param.fitplot ? - "Scale to fit printable area" : - (param.fillprint ? - "Scale to fill page and crop" : - (param.cropfit ? - "Do not scale, center, crop if needed" : - "Not defined, should never happen")))); - - // In Crop mode we do not scale the original document, it should keep the - // exact same size. With N-Up it should be scaled to fit exacly the halves, - // quarters, ... of the sheet, regardless of unprintable margins. - // Therefore we remove the unprintable margins to do all the math without - // them. - if (param.cropfit) - { - param.page.left = 0; - param.page.bottom = 0; - param.page.right = param.page.width; - param.page.top = param.page.height; - } - - if (param.pagesize_requested && (param.fillprint || param.cropfit)) - { - for (int i = 0; i < (int)input_page_range_list.size(); i ++) - { - std::shared_ptr<_cfPDFToPDFPageHandle> page = input_page_range_list[i]; - pdftopdf_rotation_e orientation; - if (page->is_landscape(param.orientation)) - orientation = param.normal_landscape; - else - orientation = ROT_0; - page->crop(param.page, orientation, param.orientation, - param.xpos, param.ypos, - !param.cropfit, param.auto_rotate, doc); - } - if (param.fillprint) - param.fitplot = true; - } - - std::shared_ptr<_cfPDFToPDFPageHandle> curpage; - int outputpage = 0; - int outputno = 0; - - if ((param.nup.nupX == 1) && (param.nup.nupY == 1) && !param.fitplot) - { - param.nup.width = param.page.width; - param.nup.height = param.page.height; - } - else - { - param.nup.width = param.page.right - param.page.left; - param.nup.height = param.page.top - param.page.bottom; - } - - if ((param.orientation == ROT_90) || (param.orientation == ROT_270)) - { - std::swap(param.nup.nupX, param.nup.nupY); - param.nup.landscape = !param.nup.landscape; - param.orientation = param.orientation - param.normal_landscape; - } - - double xpos = 0, ypos = 0; - if (param.nup.landscape) - { - // pages[iA]->rotate(param.normal_landscape); - param.orientation = param.orientation + param.normal_landscape; - // TODO? better - if (param.nup.nupX != 1 || param.nup.nupY != 1 || param.fitplot) - { - xpos = param.page.height - param.page.top; - ypos = param.page.left; - } - std::swap(param.page.width, param.page.height); - std::swap(param.nup.width, param.nup.height); - } - else - { - if (param.nup.nupX != 1 || param.nup.nupY != 1 || param.fitplot) - { - xpos = param.page.left; - ypos = param.page.bottom; // for whole page... TODO from position... - } - } - - _cfPDFToPDFNupState nupstate(param.nup); - _cfPDFToPDFNupPageEdit pgedit; - for (int iA = 0; iA < numPages; iA ++) - { - std::shared_ptr<_cfPDFToPDFPageHandle> page; - if (shuffle[iA] >= numOrigPages) - // add empty page as filler - page = proc.new_page(param.page.width, param.page.height, doc); - else - page = input_page_range_list[shuffle[iA]]; - - _cfPDFToPDFPageRect rect; - rect = page->get_rect(); - //rect.dump(doc); - if (!param.pagesize_requested) - { - param.page.width = param.page.right = rect.width; - param.page.height = param.page.top = rect.height; - } - - bool newPage = nupstate.mext_page(rect.width, rect.height, pgedit); - if (newPage) - { - if ((curpage) && (param.with_page(outputpage))) - { - curpage->rotate(param.orientation); - if (param.mirror) - curpage->mirror(); - // TODO? update rect? --- not needed any more - proc.add_page(curpage, param.reverse); // reverse -> insert at beginning - // Log page in /var/log/cups/page_log - outputno ++; - if (param.page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, - CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param.copies_to_be_logged); - } - curpage = proc.new_page(param.page.width, param.page.height, doc); - outputpage++; - } - - if (shuffle[iA] >= numOrigPages) - continue; - - if (param.border != pdftopdf_border_type_e::NONE) - // TODO FIXME... border gets cutted away, if orignal page had wrong size - // page->"uncrop"(rect); // page->setMedia() - // Note: currently "fixed" in add_subpage(...&rect); - page->add_border_rect(rect, param.border, 1.0 / pgedit.scale); - - if (!param.page_label.empty()) - page->add_label(param.page, param.page_label); - - if (param.cropfit) - { - if ((param.nup.nupX == 1) && (param.nup.nupY == 1)) - { - double xpos2, ypos2; - if ((param.page.height - param.page.width) * - (page->get_rect().height - page->get_rect().width) < 0) - { - xpos2 = (param.page.width - (page->get_rect().height)) / 2; - ypos2 = (param.page.height - (page->get_rect().width)) / 2; - curpage->add_subpage(page, ypos2 + xpos, xpos2 + ypos, 1); - } - else - { - xpos2 = (param.page.width - (page->get_rect().width)) / 2; - ypos2 = (param.page.height - (page->get_rect().height)) / 2; - curpage->add_subpage(page, xpos2 + xpos, ypos2 + ypos, 1); - } - } - else - curpage->add_subpage(page, pgedit.xpos + xpos, pgedit.ypos + ypos, - pgedit.scale); - } - else - curpage->add_subpage(page, pgedit.xpos + xpos, pgedit.ypos + ypos, - pgedit.scale); - -#ifdef DEBUG - if (auto dbg=dynamic_cast<_cfPDFToPDFQPDFPageHandle *>(curpage.get())) - dbg->debug(pgedit.sub, xpos, ypos); -#endif - - //pgedit.dump(doc); - } - if ((curpage) && (param.with_page(outputpage))) - { - curpage->rotate(param.orientation); - if (param.mirror) - curpage->mirror(); - proc.add_page(curpage, param.reverse); // reverse -> insert at beginning - // Log page in /var/log/cups/page_log - outputno ++; - if (param.page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param.copies_to_be_logged); - } - - if ((param.even_duplex || !param.odd_pages) && (outputno & 1)) - { - // need to output empty page to not confuse duplex - proc.add_page(proc.new_page(param.page.width, - param.page.height, doc), param.reverse); - // Log page in /var/log/cups/page_log - if (param.page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno + 1, - param.copies_to_be_logged); - } - - proc.multiply(param.num_copies, param.collate); - - return (true); -} -// }}} diff --git a/cupsfilters/pdftopdf/pdftopdf.cxx b/cupsfilters/pdftopdf/pdftopdf.cxx deleted file mode 100644 index b0d6e1e7b..000000000 --- a/cupsfilters/pdftopdf/pdftopdf.cxx +++ /dev/null @@ -1,1020 +0,0 @@ -// -// Copyright (c) 2012 Tobias Hoffmann -// -// Copyright (c) 2006-2011, BBR Inc. All rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include "cupsfilters/debug-internal.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pdftopdf-private.h" -#include "cupsfilters/raster.h" -#include "cupsfilters/ipp.h" -#include "cupsfilters/ipp.h" -#include "pdftopdf-processor-private.h" - -#include - - -// namespace {} - -static bool -optGetInt(const char *name, - int num_options, - cups_option_t *options, - int *ret) // {{{ -{ - DEBUG_assert(ret); - const char *val = cupsGetOption(name, num_options, options); - if (val) - { - *ret = atoi(val); - return (true); - } - return (false); -} -// }}} - -static bool -optGetFloat(const char *name, - int num_options, - cups_option_t *options, - float *ret) // {{{ -{ - DEBUG_assert(ret); - const char *val = cupsGetOption(name, num_options, options); - if (val) - { - *ret = atof(val); - return (true); - } - return (false); -} -// }}} - -static bool -is_false(const char *value) // {{{ -{ - if (!value) - return (false); - return ((strcasecmp(value, "no") == 0) || - (strcasecmp(value, "off") == 0) || - (strcasecmp(value, "false") == 0)); -} -// }}} - -static bool -is_true(const char *value) // {{{ -{ - if (!value) - return (false); - return ((strcasecmp(value, "yes") == 0) || - (strcasecmp(value, "on") == 0) || - (strcasecmp(value, "true") == 0)); -} -// }}} - - -static bool -parsePosition(const char *value, - pdftopdf_position_e &xpos, - pdftopdf_position_e &ypos) // {{{ -{ - // ['center','top','left','right','top-left','top-right','bottom', - // 'bottom-left','bottom-right'] - xpos = pdftopdf_position_e::CENTER; - ypos = pdftopdf_position_e::CENTER; - int next = 0; - if (strcasecmp(value, "center") == 0) - return (true); - else if (strncasecmp(value, "top", 3) == 0) - { - ypos = pdftopdf_position_e::TOP; - next = 3; - } - else if (strncasecmp(value, "bottom", 6) == 0) - { - ypos = pdftopdf_position_e::BOTTOM; - next = 6; - } - if (next) - { - if (value[next] == 0) - return (true); - else if (value[next] != '-') - return (false); - value += next + 1; - } - if (strcasecmp(value, "left") == 0) - xpos = pdftopdf_position_e::LEFT; - else if (strcasecmp(value, "right") == 0) - xpos = pdftopdf_position_e::RIGHT; - else - return (false); - return (true); -} -// }}} - -static void -parseRanges(const char *range, _cfPDFToPDFIntervalSet &ret) // {{{ -{ - ret.clear(); - if (!range) - { - ret.add(1); // everything - ret.finish(); - return; - } - - int lower, upper; - while (*range) - { - if (*range == '-') - { - range ++; - upper = strtol(range, (char **)&range, 10); - if (upper >= 2147483647) // see also cups/encode.c - ret.add(1); - else - ret.add(1, upper + 1); - } - else - { - lower = strtol(range, (char **)&range, 10); - if (*range == '-') - { - range ++; - if (!isdigit(*range)) - ret.add(lower); - else - { - upper=strtol(range, (char **)&range, 10); - if (upper>=2147483647) - ret.add(lower); - else - ret.add(lower, upper + 1); - } - } - else - ret.add(lower, lower + 1); - } - - if (*range != ',') - break; - range++; - } - ret.finish(); -} -// }}} - -static bool -_cfPDFToPDFParseBorder(const char *val, - pdftopdf_border_type_e &ret) // {{{ -{ - DEBUG_assert(val); - if (strcasecmp(val, "none") == 0) - ret = pdftopdf_border_type_e::NONE; - else if (strcasecmp(val, "single") == 0) - ret = pdftopdf_border_type_e::ONE_THIN; - else if (strcasecmp(val, "single-thick") == 0) - ret = pdftopdf_border_type_e::ONE_THICK; - else if (strcasecmp(val, "double") == 0) - ret = pdftopdf_border_type_e::TWO_THIN; - else if (strcasecmp(val, "double-thick") == 0) - ret = pdftopdf_border_type_e::TWO_THICK; - else - return (false); - return (true); -} -// }}} - -void -getParameters(cf_filter_data_t *data, - int num_options, - cups_option_t *options, - _cfPDFToPDFProcessingParameters ¶m, - pdftopdf_doc_t *doc) // {{{ -{ - char *final_content_type = data->final_content_type; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *attr; - const char *val; - int ipprot; - int nup; - std::string rawlabel; - char *classification; - std::ostringstream cookedlabel; - - - if ((val = cupsGetOption("copies", num_options, options)) != NULL || - (val = cupsGetOption("Copies", num_options, options)) != NULL || - (val = cupsGetOption("num-copies", num_options, options)) != NULL || - (val = cupsGetOption("NumCopies", num_options, options)) != NULL) - { - int copies = atoi(val); - if (copies > 0) - param.num_copies = copies; - } - - if (param.num_copies == 0) - param.num_copies = 1; - - if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) != - NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || - !strcasecmp(val, "on")) - param.fidelity = true; - } - - if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) - { - if (!strcasecmp(val, "auto")) - param.autoprint = true; - else if (!strcasecmp(val, "auto-fit")) - param.autofit = true; - else if (!strcasecmp(val, "fill")) - param.fillprint = true; - else if (!strcasecmp(val, "fit")) - param.fitplot = true; - else - param.cropfit = true; - } - else - { - if ((val = cupsGetOption("fitplot", num_options, options)) == NULL) - { - if ((val = cupsGetOption("fit-to-page", num_options, options)) == NULL) - val = cupsGetOption("ipp-attribute-fidelity", num_options, options); - } - // TODO? pstops checks == "true", pdftops !is_false ... pstops says: - // fitplot only for PS (i.e. not for PDF, cmp. cgpdftopdf) - param.fitplot = (val) && (!is_false(val)); - - if ((val = cupsGetOption("fill", num_options, options)) != 0) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param.fillprint = true; - } - if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param.cropfit=1; - } - if (!param.autoprint && !param.autofit && !param.fitplot && - !param.fillprint && !param.cropfit) - param.autoprint = true; - } - - // direction the printer rotates landscape - // (landscape-orientation-requested-preferred: 4: 90 or 5: -90) - if (printer_attrs != NULL && - (attr = ippFindAttribute(printer_attrs, - "landscape-orientation-requested-preferred", - IPP_TAG_ZERO)) != NULL && - ippGetInteger(attr, 0) == 5) - param.normal_landscape = ROT_270; - else - param.normal_landscape = ROT_90; - - param.orientation = ROT_0; - if ((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if (!is_false(val)) - param.orientation = param.normal_landscape; - } - else if (optGetInt("orientation-requested", num_options, options, &ipprot)) - { - // IPP orientation values are: - // 3: 0 degrees, 4: 90 degrees, 5: -90 degrees, 6: 180 degrees - - if ((ipprot < 3) || (ipprot > 6)) - { - if (ipprot && doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Bad value (%d) for " - "orientation-requested, using 0 degrees", - ipprot); - param.no_orientation = true; - } - else - { - static const pdftopdf_rotation_e - ipp2rot[4]={ROT_0, ROT_90, ROT_270, ROT_180}; - param.orientation = ipp2rot[ipprot - 3]; - } - } - else - param.no_orientation = true; - - if (printer_attrs != NULL) - param.pagesize_requested = - (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, - 0, - &(param.page.width), &(param.page.height), - &(param.page.left), &(param.page.bottom), - &(param.page.right), &(param.page.top), - NULL, NULL) == 1); - - if (param.page.width <= 0 || param.page.height <= 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_WARN, - "cfFilterPDFToPDF: Could not determine the output page dimensions, falling back to US Letter format"); - param.page.width = 612; - param.page.height = 792; - } - if (param.page.left < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_WARN, - "cfFilterPDFToPDF: Could not determine the width of the left margin, falling back to 18 pt/6.35 mm"); - param.page.left = 18.0; - } - if (param.page.bottom < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_WARN, - "cfFilterPDFToPDF: Could not determine the width of the bottom margin, falling back to 36 pt/12.7 mm"); - param.page.bottom = 36.0; - } - if (param.page.right < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_WARN, - "cfFilterPDFToPDF: Could not determine the width of the right margin, falling back to 18 pt/6.35 mm"); - param.page.right = param.page.width - 18.0; - } - else - param.page.right = param.page.width - param.page.right; - if (param.page.top < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_WARN, - "cfFilterPDFToPDF: Could not determine the width of the top margin, falling back to 36 pt/12.7 mm"); - param.page.top = param.page.height - 36.0; - } - else - param.page.top = param.page.height - param.page.top; - - param.paper_is_landscape = (param.page.width > param.page.height); - - _cfPDFToPDFPageRect tmp; // borders (before rotation) - - optGetFloat("page-top", num_options, options, &tmp.top); - optGetFloat("page-left", num_options, options, &tmp.left); - optGetFloat("page-right", num_options, options, &tmp.right); - optGetFloat("page-bottom", num_options, options, &tmp.bottom); - - if ((val = cupsGetOption("media-top-margin", num_options, options)) - != NULL) - tmp.top = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-left-margin", num_options, options)) - != NULL) - tmp.left = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-right-margin", num_options, options)) - != NULL) - tmp.right = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-bottom-margin", num_options, options)) - != NULL) - tmp.bottom = atof(val) * 72.0 / 2540.0; - - if ((param.orientation == ROT_90) || (param.orientation == ROT_270)) - { // unrotate page - // NaN stays NaN - tmp.right = param.page.height - tmp.right; - tmp.top = param.page.width - tmp.top; - tmp.rotate_move(param.orientation, param.page.height, param.page.width); - } - else - { - tmp.right = param.page.width - tmp.right; - tmp.top = param.page.height - tmp.top; - tmp.rotate_move(param.orientation, param.page.width, param.page.height); - } - - param.page.set(tmp); // replace values, where tmp.* != NaN - // (because tmp needed rotation, param.page not!) - - if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != - NULL && - strncmp(val, "two-sided-", 10) == 0) - param.duplex = true; - else if (is_true(cupsGetOption("Duplex", num_options, options))) - { - param.duplex = true; - param.set_duplex = true; - } - else if ((val = cupsGetOption("sides", num_options, options)) != NULL) - { - if ((strcasecmp(val, "two-sided-long-edge") == 0) || - (strcasecmp(val, "two-sided-short-edge") == 0)) - { - param.duplex = true; - param.set_duplex = true; - } - else if (strcasecmp(val, "one-sided") != 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported sides value %s, using sides=one-sided!", - val); - } - } - - // default nup is 1 - nup = 1; - if (optGetInt("number-up", num_options, options, &nup)) - { - if (!_cfPDFToPDFNupParameters::possible(nup)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported number-up value %d, using number-up=1!", - nup); - nup = 1; - } - // TODO ; TODO? nup enabled? ... fitplot - // _cfPDFToPDFNupParameters::calculate(nup, param.nup); - _cfPDFToPDFNupParameters::preset(nup, param.nup); - } - - if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) - { - if (!_cfPDFToPDFParseNupLayout(val, param.nup)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", - val); - param.nup.first = pdftopdf_axis_e::X; - param.nup.xstart = pdftopdf_position_e::LEFT; - param.nup.ystart = pdftopdf_position_e::TOP; - } - } - - if ((val = cupsGetOption("page-border", num_options, options)) != NULL) - { - if (!_cfPDFToPDFParseBorder(val, param.border)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page-border value %s, using page-border=none!", - val); - param.border = pdftopdf_border_type_e::NONE; - } - } - - if ((val=cupsGetOption("OutputOrder",num_options,options)) != NULL || - (val=cupsGetOption("output-order",num_options,options)) != NULL || - (val=cupsGetOption("page-delivery",num_options,options)) != NULL) - { - param.reverse = (strcasecmp(val, "Reverse") == 0 || - strcasecmp(val, "reverse-order") == 0); - } - else - param.reverse = cfIPPReverseOutput(printer_attrs, job_attrs); - - classification = getenv("CLASSIFICATION"); - if (classification) - rawlabel.append (classification); - - if ((val = cupsGetOption("page-label", num_options, options)) != NULL) - { - if (!rawlabel.empty()) - rawlabel.append (" - "); - rawlabel.append(cupsGetOption("page-label", num_options, options)); - } - - for (std::string::iterator it = rawlabel.begin(); - it != rawlabel.end (); - ++ it) - { - if (*it < 32 || *it > 126) - cookedlabel << "\\" << std::oct << std::setfill('0') << std::setw(3) << (unsigned int) *it; - else - cookedlabel.put (*it); - } - param.page_label = cookedlabel.str (); - - if ((val = cupsGetOption("page-set", num_options, options)) != NULL) - { - if (strcasecmp(val, "even") == 0) - param.odd_pages = false; - else if (strcasecmp(val, "odd") == 0) - param.even_pages = false; - else if (strcasecmp(val, "all") != 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page-set value %s, using page-set=all!", - val); - } - } - - if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL) - parseRanges(val, param.page_ranges); - - if ((val = cupsGetOption("input-page-ranges", num_options, options)) != NULL) - parseRanges(val, param.input_page_ranges); - - if ((val = cupsGetOption("mirror", num_options, options)) != NULL || - (val = cupsGetOption("mirror-print", num_options, options)) != NULL) - param.mirror = is_true(val); - - param.booklet = pdftopdf_booklet_mode_e::CF_PDFTOPDF_BOOKLET_OFF; - if ((val = cupsGetOption("booklet", num_options, options)) != NULL) - { - if (strcasecmp(val, "shuffle-only") == 0) - param.booklet = pdftopdf_booklet_mode_e::CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE; - else if (is_true(val)) - param.booklet = pdftopdf_booklet_mode_e::CF_PDFTOPDF_BOOKLET_ON; - else if (!is_false(val)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported booklet value %s, using booklet=off!", - val); - } - } - param.book_signature = -1; - if (optGetInt("booklet-signature", num_options, options, - ¶m.book_signature)) - { - if (param.book_signature == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported booklet-signature value, using booklet-signature=-1 (all)!", - val); - param.book_signature = -1; - } - } - - if ((val = cupsGetOption("position", num_options, options)) != NULL) - { - if (!parsePosition(val, param.xpos, param.ypos)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unrecognized position value %s, using position=center!", - val); - param.xpos = pdftopdf_position_e::CENTER; - param.ypos = pdftopdf_position_e::CENTER; - } - } - - // Collate - if (is_true(cupsGetOption("Collate", num_options, options))) - param.collate = true; - else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) - param.collate = (strcasecmp(val, "uncollated") != 0); - else if (((val = cupsGetOption("multiple-document-handling", - num_options, options)) != NULL && - (strcasecmp(val, "separate-documents-collated-copies") == 0 || - strcasecmp(val, "separate-documents-uncollated-copies") == 0 || - strcasecmp(val, "single-document") == 0 || - strcasecmp(val, "single-document-new-sheet") == 0)) || - (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "multiple-document-handling")) != - NULL) - // - // This IPP attribute is unnecessarily complicated: - // single-document, separate-documents-collated-copies, - // single-document-new-sheet: - // -> collate (true) - // separate-documents-uncollated-copies: - // -> can be uncollated (false) - // - param.collate = - (strcasecmp(val, "separate-documents-uncollated-copies") != 0); - -#if 0 - // TODO: scaling - // TODO: natural-scaling - - // Scaling - if ((val = cupsGetOption("scaling", num_options, options)) != 0) - { - scaling = atoi(val) * 0.01; - fitplot = true; - } - else if (fitplot) - scaling = 1.0; - if ((val = cupsGetOption("natural-scaling", num_options, options)) != 0) - naturalScaling = atoi(val) * 0.01; -#endif - - // Make pages a multiple of two (only considered when duplex is on). - // i.e. printer has hardware-duplex, but needs pre-inserted filler pages - // FIXME? pdftopdf also supports it as cmdline option (via checkFeature()) - param.even_duplex = - (param.duplex && - is_true(cupsGetOption("even-duplex", num_options, options))); - - // TODO? pdftopdf* ? - // TODO?! pdftopdfAutoRotate - - // TODO?! Choose default by whether pdfautoratate filter has already been - // run (e.g. by mimetype) - param.auto_rotate = - (!is_false(cupsGetOption("pdfAutoRotate", num_options, options)) && - !is_false(cupsGetOption("pdftopdfAutoRotate", num_options, options))); - - // - // Do we have to do the page logging in page_log? - // - // CUPS standard is that the last filter (not the backend, usually the - // printer driver) does page logging in the /var/log/cups/page_log file - // by outputting "PAGE: <# of current page> <# of copies>" to stderr. - // - // cfFilterPDFToPDF() would have to do this only for PDF printers as - // in this case cfFilterPDFToPDF() is the last filter, but some of - // the other filters are not able to do the logging because they do - // not have access to the number of pages of the file to be printed, - // so cfFilterPDFToPDF() overtakes their logging duty. - // - - // Check whether page logging is forced or suppressed by the options - if ((val = cupsGetOption("pdf-filter-page-logging", - num_options, options)) != NULL) - { - if (strcasecmp(val, "auto") == 0) - { - param.page_logging = -1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Automatic page logging selected by options."); - } - else if (is_true(val)) - { - param.page_logging = 1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Forced page logging selected by options."); - } - else if (is_false(val)) - { - param.page_logging = 0; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Suppressed page logging selected by options."); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page logging setting \"pdf-filter-page-logging=%s\", using \"auto\"!", - val); - param.page_logging = -1; - } - } - - if (param.page_logging == -1) - { - // We determine whether to log pages or not - // using the output data MIME type. log pages only when the output is - // either pdf or PWG Raster - if (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf") || - strcasestr(final_content_type, "/pwg-raster"))) - param.page_logging = 1; - else - param.page_logging = 0; - - // If final_content_type is not clearly available we are not sure whether - // to log pages or not - if ((char*)final_content_type == NULL || - sizeof(final_content_type) == 0 || - final_content_type[0] == '\0') - param.page_logging = -1; - if (doc->logfunc) - { - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Determined whether to log pages or not using output data type."); - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "final_content_type = %s => page_logging = %d", - final_content_type ? final_content_type : "NULL", - param.page_logging); - } - if (param.page_logging == -1) - param.page_logging = 0; - } -} -// }}} - -void -calculate(int num_options, - cups_option_t *options, - _cfPDFToPDFProcessingParameters ¶m, - char *final_content_type) // {{{ -{ - const char *val; - bool hw_copies = false, - hw_collate = false; - - - // Check options for caller's instructions about hardware copies/collate - if ((val = cupsGetOption("hardware-copies", - num_options, options)) != NULL) - // Use hardware copies according to the caller's instructions - hw_copies = is_true(val); - else - // Caller did not tell us whether the printer does Hardware copies - // or not, so we assume hardware copies on PDF printers, and software - // copies on other (usually raster) printers or if we do not know the - // final output format. - hw_copies = (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf"))); - if (hw_copies) - { - if ((val = cupsGetOption("hardware-collate", - num_options, options)) != NULL) - // Use hardware collate according to the caller's instructions - hw_collate = is_true(val); - else - // Check output format MIME type whether it is - // of a driverless IPP printer (PDF, Apple Raster, PWG Raster, PCLm). - // These printers do always hardware collate if they do hardware copies. - // https://github.com/apple/cups/issues/5433 - hw_collate = (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf") || - strcasestr(final_content_type, "/pwg-raster") || - strcasestr(final_content_type, "/urf") || - strcasestr(final_content_type, "/PCLm"))); - } - - if (param.reverse && param.duplex) - // Enable even_duplex or the first page may be empty. - param.even_duplex = true; // disabled later, if non-duplex - - if (param.num_copies == 1) - { - param.device_copies = 1; - // collate is never needed for a single copy - param.collate = false; // (does not make a big difference for us) - } - else if (hw_copies) - { // hw copy generation available - param.device_copies = param.num_copies; - if (param.collate) - { // collate requested by user - param.device_collate = hw_collate; - if (!param.device_collate) - // printer can't hw collate -> we must copy collated in sw - param.device_copies = 1; - } // else: printer copies w/o collate and takes care of duplex/even_duplex - } - else - { // sw copies - param.device_copies = 1; - if (param.duplex) - { // &&(num_copies>1) - // sw collate + even_duplex must be forced to prevent copies on the - // back sides - param.collate = true; - param.device_collate = false; - } - } - - if (param.device_copies != 1) // hw copy - param.num_copies = 1; // disable sw copy - - if (param.duplex && - param.collate && !param.device_collate) // software collate - param.even_duplex = true; // fillers always needed - - if (!param.duplex) - param.even_duplex = false; -} -// }}} - -// reads from stdin into temporary file. returns FILE * or NULL on error -FILE * -copy_fd_to_temp(int infd, - pdftopdf_doc_t *doc) // {{{ -{ - char buf[BUFSIZ]; - int n; - - // FIXME: what does >buf mean here? - int outfd = cupsTempFd(buf, sizeof(buf)); - if (outfd < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't create temporary file"); - return (NULL); - } - // remove name - unlink(buf); - - // copy stdin to the tmp file - while ((n = read(infd, buf, BUFSIZ)) > 0) - { - if (write(outfd, buf, n) != n) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't copy stdin to temporary file"); - close(outfd); - return (NULL); - } - } - if (lseek(outfd, 0, SEEK_SET) < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't rewind temporary file"); - close(outfd); - return (NULL); - } - - FILE *f; - if ((f = fdopen(outfd, "rb")) == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't fdopen temporary file"); - close(outfd); - return (NULL); - } - return f; -} -// }}} - -// check whether a given file is empty -bool is_empty(FILE *f) // {{{ -{ - char buf[1]; - - // Try to read a single byte of data - if (fread(buf, 1, 1, f) == 0) - return (true); - - rewind(f); - - return (false); -} -// }}} - - -int // O - Error status -cfFilterPDFToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - pdftopdf_doc_t doc; // Document information - char *final_content_type = data->final_content_type; - FILE *inputfp, - *outputfp; - const char *t; - int streaming = 0; - size_t bytes; - char buf[BUFSIZ]; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int num_options = 0; - cups_option_t *options = NULL; - - - try - { - _cfPDFToPDFProcessingParameters param; - - param.job_id = data->job_id; - param.user = data->job_user; - param.title = data->job_title; - param.num_copies = data->copies; - param.copies_to_be_logged = data->copies; - - // TODO?! sanity checks - - doc.logfunc = log; - doc.logdata = ld; - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - getParameters(data, num_options, options, param, &doc); - - calculate(num_options, options, param, final_content_type); - -#ifdef DEBUG - param.dump(&doc); -#endif - - // If we are in streaming mode we only apply JCL and do not run the - // job through QPDL (so no page management, form flattening, - // page size/orientation adjustment, ...) - if ((t = cupsGetOption("filter-streaming-mode", - num_options, options)) != NULL && - (strcasecmp(t, "false") && strcasecmp(t, "off") && - strcasecmp(t, "no"))) - { - streaming = 1; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Streaming mode: No PDF processing, only adding of JCL"); - } - - cupsFreeOptions(num_options, options); - - std::unique_ptr<_cfPDFToPDFProcessor> proc(_cfPDFToPDFFactory::processor()); - - if ((inputseekable && inputfd > 0) || streaming) - { - if ((inputfp = fdopen(inputfd, "rb")) == NULL) - return (1); - } - else - { - if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) - return (1); - } - - if (!streaming) - { - if (is_empty(inputfp)) - { - fclose(inputfp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Input is empty, outputting empty file."); - return (0); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Processing PDF input with QPDF: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); - - // Load the PDF input data into QPDF - if (!proc->load_file(inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) - { - fclose(inputfp); - return (1); - } - - // Process the PDF input data - if (!_cfProcessPDFToPDF(*proc, param, &doc)) - return (2); - - // Pass information to subsequent filters via PDF comments - std::vector output; - - output.push_back("% This file was generated by pdftopdf"); - - // This is not standard, but like PostScript. - if (param.device_copies > 0) - { - char buf[256]; - snprintf(buf, sizeof(buf), "%d", param.device_copies); - output.push_back(std::string("%%PDFTOPDFNumCopies : ")+buf); - - if (param.device_collate) - output.push_back("%%PDFTOPDFCollate : true"); - else - output.push_back("%%PDFTOPDFCollate : false"); - } - - proc->set_comments(output); - } - - outputfp = fdopen(outputfd, "w"); - if (outputfp == NULL) - return (1); - - if (!streaming) - { - // Pass on the processed input data - proc->emit_file(outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); - // proc->emit_filename(NULL); - } - else - { - // Pass through the input data - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Passing on unchanged PDF data from input"); - while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) - if (fwrite(buf, 1, bytes, outputfp) != bytes) - break; - fclose(inputfp); - } - - fclose(outputfp); - } - catch (std::exception &e) - { - // TODO? exception type - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Exception: %s", e.what()); - return (5); - } - catch (...) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unknown exception caught. Exiting."); - return (6); - } - - return (0); -} diff --git a/cupsfilters/pdftopdf/pptypes-private.h b/cupsfilters/pdftopdf/pptypes-private.h deleted file mode 100644 index 0cacf8e0b..000000000 --- a/cupsfilters/pdftopdf/pptypes-private.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ -#define _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ - -#include "pdftopdf-private.h" -#include // NAN - -// namespace PPTypes {} TODO? - -enum pdftopdf_axis_e { X, Y }; -enum pdftopdf_position_e { // PS order - CENTER = 0, - LEFT = -1, - RIGHT = 1, - TOP = 1, - BOTTOM = -1 -}; - -void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_doc_t *doc); -void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, - pdftopdf_doc_t *doc); - -enum pdftopdf_rotation_e { ROT_0, ROT_90, ROT_180, ROT_270 }; // CCW - -void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, pdftopdf_doc_t *doc); -pdftopdf_rotation_e operator+(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e operator-(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e operator-(pdftopdf_rotation_e rhs); -//pdftopdf_rotation_e operator+=(pdftopdf_rotation_e &lhs, -// pdftopdf_rotation_e rhs); - -enum pdftopdf_border_type_e { - NONE = 0, - ONE_THIN = 2, - ONE_THICK = 3, - TWO_THIN = 4, - TWO_THICK = 5, - ONE = 0x02, - TWO = 0x04, - THICK = 0x01 -}; - -void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, - pdftopdf_doc_t *doc); - -struct _cfPDFToPDFPageRect { -_cfPDFToPDFPageRect() - : top(NAN), - left(NAN), - right(NAN), - bottom(NAN), - width(NAN), - height(NAN) {} - float top, left, right, bottom; // i.e. margins - float width, height; - - void rotate_move(pdftopdf_rotation_e r, float pwidth, float pheight); - // pwidth original "page size" (i.e. before rotation) - void scale(float mult); - void translate(float tx, float ty); - - void set(const _cfPDFToPDFPageRect &rhs); // only for rhs.* != NAN - void dump(pdftopdf_doc_t *doc) const; -}; - -// bool _cfPDFToPDFParseBorder(const char *val,pdftopdf_border_type_e &ret); -// // none, single, ..., double-thick - -#endif // !_CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ diff --git a/cupsfilters/pdftopdf/pptypes.cxx b/cupsfilters/pdftopdf/pptypes.cxx deleted file mode 100644 index f8158b481..000000000 --- a/cupsfilters/pdftopdf/pptypes.cxx +++ /dev/null @@ -1,238 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "pptypes-private.h" -#include -#include -#include "cupsfilters/debug-internal.h" - -void -_cfPDFToPDFPositionDump(pdftopdf_position_e pos, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (bad position: %d)", pos); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: %s", pstr[pos+1]); - } -} -// }}} - -void -_cfPDFToPDFPositionDump(pdftopdf_position_e pos, - pdftopdf_axis_e axis, - pdftopdf_doc_t *doc) // {{{ -{ - DEBUG_assert((axis == pdftopdf_axis_e::X) || (axis == pdftopdf_axis_e::Y)); - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position %s: (bad position: %d)", - (axis == pdftopdf_axis_e::X) ? "X" : "Y", pos); - return; - } - if (axis == pdftopdf_axis_e::X) - { - static const char *pxstr[3] = {"Left", "Center", "Right"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position X: %s", pxstr[pos+1]); - } - else - { - static const char *pystr[3] = {"Bottom", "Center", "Top"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position Y: %s", pystr[pos+1]); - } -} -// }}} - -void -_cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW - if ((rot < ROT_0) || (rot > ROT_270)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): (bad rotation: %d)", rot); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); - } -} -// }}} - -pdftopdf_rotation_e -operator+(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); -} -// }}} - -pdftopdf_rotation_e -operator-(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); -} -// }}} - -pdftopdf_rotation_e -operator-(pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)((4 - (int)rhs) % 4); -} -// }}} - -void -_cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, - pdftopdf_doc_t *doc) // {{{ -{ - if ((border < NONE) || (border == 1) || (border > TWO_THICK)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: (bad border: %d)", border); - } - else - { - static const char *bstr[6] = - {"None", NULL, "one thin", "one thick", "two thin", "two thick"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: %s", bstr[border]); - } -} -// }}} - -void -_cfPDFToPDFPageRect::rotate_move(pdftopdf_rotation_e r, - float pwidth, - float pheight) // {{{ -{ -#if 1 - if (r >= ROT_180) - { - std::swap(top, bottom); - std::swap(left, right); - } - if ((r == ROT_90) || (r == ROT_270)) - { - const float tmp = bottom; - bottom = left; - left = top; - top = right; - right = tmp; - - std::swap(width, height); - std::swap(pwidth, pheight); - } - if ((r == ROT_90) || (r == ROT_180)) - { - left = pwidth - left; - right = pwidth - right; - } - if ((r == ROT_270) || (r == ROT_180)) - { - top = pheight - top; - bottom = pheight - bottom; - } -#else - switch (r) - { - case ROT_0: // no-op - break; - case ROT_90: - const float tmp0 = bottom; - bottom = left; - left = pheight - top; - top = right; - right = pheight - tmp0; - - std::swap(width, height); - break; - case ROT_180: - const float tmp1 = left; - left = pwidth - right; - right = pwidth - tmp1; - - const float tmp2 = top; - top = pheight - bottom; - bottom = pheight - tmp2; - break; - case ROT_270: - const float tmp3 = top; - top = pwidth - left; - left = bottom; - bottom = pwidth - right; - right = tmp3; - - std::swap(width, height); - break; - } -#endif -} -// }}} - -void -_cfPDFToPDFPageRect::scale(float mult) // {{{ -{ - if (mult == 1.0) - return; - - DEBUG_assert(mult != 0.0); - - bottom *= mult; - left *= mult; - top *= mult; - right *= mult; - - width *= mult; - height *= mult; -} -// }}} - -void -_cfPDFToPDFPageRect::translate(float tx, - float ty) // {{{ -{ - left += tx; - bottom += ty; - right += tx; - top += ty; -} -// }}} - -void -_cfPDFToPDFPageRect::set(const _cfPDFToPDFPageRect &rhs) // {{{ -{ - if (!std::isnan(rhs.top)) - top = rhs.top; - if (!std::isnan(rhs.left)) - left = rhs.left; - if (!std::isnan(rhs.right)) - right = rhs.right; - if (!std::isnan(rhs.bottom)) - bottom = rhs.bottom; -} -// }}} - -void -_cfPDFToPDFPageRect::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " - "width: %f, height: %f", - top, left, right, bottom, - width, height); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-cm-private.h b/cupsfilters/pdftopdf/qpdf-cm-private.h deleted file mode 100644 index 4f22e4688..000000000 --- a/cupsfilters/pdftopdf/qpdf-cm-private.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_CM_H_ -#define _CUPS_FILTERS_PDFTOPDF_QPDF_CM_H_ - -#include - -bool _cfPDFToPDFHasOutputIntent(QPDF &pdf); -void _cfPDFToPDFAddOutputIntent(QPDF &pdf, const char *filename); - -void _cfPDFToPDFAddDefaultRGB(QPDF &pdf, QPDFObjectHandle srcicc); -QPDFObjectHandle _cfPDFToPDFSetDefaultICC(QPDF &pdf, const char *filename); - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_CM_H_ diff --git a/cupsfilters/pdftopdf/qpdf-cm.cxx b/cupsfilters/pdftopdf/qpdf-cm.cxx deleted file mode 100644 index 400a9faf7..000000000 --- a/cupsfilters/pdftopdf/qpdf-cm.cxx +++ /dev/null @@ -1,167 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-cm-private.h" -#include -#include "cupsfilters/debug-internal.h" - -#include - -// TODO? instead use qpdf's StreamDataProvider, FileInputSource, Buffer etc. -static std::string -load_file(const char *filename) // {{{ -{ - if (!filename) - throw std::invalid_argument("NULL filename not allowed"); - - FILE *f = fopen(filename, "r"); - if (!f) - throw std::runtime_error(std::string("file ") + filename + " could not be opened"); - - const int bsize = 2048; - int pos = 0; - - std::string ret; - while (!feof(f)) - { - ret.resize(pos + bsize); - int res = fread(&ret[pos], 1, bsize, f); - pos += res; - if (res < bsize) - { - ret.resize(pos); - break; - } - } - - fclose(f); - return (ret); -} -// }}} - - -// TODO? -// TODO? test -bool -_cfPDFToPDFHasOutputIntent(QPDF &pdf) // {{{ -{ - auto catalog = pdf.getRoot(); - if (!catalog.hasKey("/OutputIntents")) - return (false); - return (true); // TODO? -} -// }}} - -// TODO: test -// TODO? find existing , replace and return (?) -void -_cfPDFToPDFAddOutputIntent(QPDF &pdf, - const char *filename) // {{{ -{ - std::string icc = load_file(filename); - // TODO: check icc fitness - // ICC data, subject to "version limitations" per pdf version... - - QPDFObjectHandle outicc = QPDFObjectHandle::newStream(&pdf, icc); - - auto sdict = outicc.getDict(); - sdict.replaceKey("/N",QPDFObjectHandle::newInteger(4)); // must match ICC - // /Range ? // must match ICC, default [0.0 1.0 ...] - // /Alternate ? (/DeviceCMYK for N=4) - - auto intent = QPDFObjectHandle::parse( - "<<" - " /Type /OutputIntent" // Must be so (the standard requires). - " /S /GTS_PDFX" // Must be so (the standard requires). - " /OutputCondition (Commercial and specialty printing)" // TODO: Customize [optional(?)] - " /Info (none)" // TODO: Customize - " /OutputConditionIdentifier (CGATS TR001)" // TODO: FIXME: Customize - " /RegistryName (http://www.color.org)" // Must be so (the standard requires). - " /DestOutputProfile null " - ">>"); - intent.replaceKey("/DestOutputProfile", outicc); - - auto catalog = pdf.getRoot(); - if (!catalog.hasKey("/OutputIntents")) - catalog.replaceKey("/OutputIntents", QPDFObjectHandle::newArray()); - catalog.getKey("/OutputIntents").appendItem(intent); -} -// }}} - -// -// for color management: -// Use /DefaultGray, /DefaultRGB, /DefaultCMYK ... from *current* resource -// dictionary ... -// i.e. set -// /Resources << -// /ColorSpace << --- can use just one indirect ref for this (probably) -// /DefaultRGB [/ICCBased 5 0 R] ... sensible use is sRGB for DefaultRGB, etc. -// >> -// >> -// for every page (what with form /XObjects?) and most importantly RGB -// (leave CMYK, Gray for now, as this is already printer native(?)) -// -// ? also every form XObject, pattern, type3 font, annotation appearance -// stream(=form xobject +X) -// -// ? what if page already defines /Default? -- probably keep! -// -// ? maybe we need to set /ColorSpace in /Images ? -// [gs idea is to just add the /Default-key and then reprocess...] -// - - -// TODO? test -void -_cfPDFToPDFAddDefaultRGB(QPDF &pdf, QPDFObjectHandle srcicc) // {{{ -{ - srcicc.assertStream(); - - auto pages = pdf.getAllPages(); - for (auto it = pages.begin(), end = pages.end(); it != end; ++ it) - { - if (!it->hasKey("/Resources")) - it->replaceKey("/Resources", QPDFObjectHandle::newDictionary()); - - auto rdict = it->getKey("/Resources"); - - if (!rdict.hasKey("/ColorSpace")) - rdict.replaceKey("/ColorSpace", QPDFObjectHandle::newDictionary()); - - auto cdict = rdict.getKey("/ColorSpace"); - - if (!cdict.hasKey("/DefaultRGB")) - { - cdict.replaceKey("/DefaultRGB", QPDFObjectHandle::parse("[/ICCBased ]")); - cdict.getKey("/DefaultRGB").appendItem(srcicc); - } - } -} -// }}} - -// TODO? test -// TODO: find existing , replace and return (?) -// TODO: check icc fitness -QPDFObjectHandle -_cfPDFToPDFSetDefaultICC(QPDF &pdf, - const char *filename) // {{{ -{ - // TODO: find existing, replace and return (?) - - std::string icc = load_file(filename); - // TODO: check icc fitness - // ICC data, subject to "version limitations" per pdf version... - - QPDFObjectHandle ret = QPDFObjectHandle::newStream(&pdf, icc); - - auto sdict = ret.getDict(); - sdict.replaceKey("/N", QPDFObjectHandle::newInteger(3)); // must match ICC - // /Range ? // must match ICC, default [0.0 1.0 ...] - // /Alternate ? (/DeviceRGB for N=3) - - return ret; -} -// }}} - diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf-private.h b/cupsfilters/pdftopdf/qpdf-pdftopdf-private.h deleted file mode 100644 index 1146a8fea..000000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf-private.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_H -#define _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_H - -#include -#include "pptypes-private.h" - -// helper functions - -_cfPDFToPDFPageRect _cfPDFToPDFGetBoxAsRect(QPDFObjectHandle box); -QPDFObjectHandle _cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect &rect); - -// Note that PDF specification is CW, but our Rotation is CCW -pdftopdf_rotation_e _cfPDFToPDFGetRotate(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot); // Integer - -double _cfPDFToPDFGetUserUnit(QPDFObjectHandle page); - -// PDF CTM -class _cfPDFToPDFMatrix { - public: - _cfPDFToPDFMatrix(); // identity - _cfPDFToPDFMatrix(QPDFObjectHandle ar); - - _cfPDFToPDFMatrix &rotate(pdftopdf_rotation_e rot); - _cfPDFToPDFMatrix &rotate_move(pdftopdf_rotation_e rot, double width, - double height); - _cfPDFToPDFMatrix &rotate(double rad); - // _cfPDFToPDFMatrix &rotate_deg(double deg); - - _cfPDFToPDFMatrix &translate(double tx, double ty); - _cfPDFToPDFMatrix &scale(double sx, double sy); - _cfPDFToPDFMatrix &scale(double s) { return (scale(s, s)); } - - _cfPDFToPDFMatrix &operator*=(const _cfPDFToPDFMatrix &rhs); - - QPDFObjectHandle get() const; - std::string get_string() const; - private: - double ctm[6]; -}; - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h deleted file mode 100644 index c8041205b..000000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h +++ /dev/null @@ -1,99 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_PROCESSOR_H -#define _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_PROCESSOR_H - -#include "pdftopdf-processor-private.h" -#include - -class _cfPDFToPDFQPDFPageHandle : public _cfPDFToPDFPageHandle { - public: - virtual _cfPDFToPDFPageRect get_rect() const; - virtual void add_border_rect(const _cfPDFToPDFPageRect &rect, - pdftopdf_border_type_e border, float fscale); - virtual void add_subpage(const std::shared_ptr<_cfPDFToPDFPageHandle> &sub, - float xpos, float ypos, float scale, - const _cfPDFToPDFPageRect *crop=NULL); - virtual void mirror(); - virtual void rotate(pdftopdf_rotation_e rot); - virtual void add_label(const _cfPDFToPDFPageRect &rect, - const std::string label); - virtual pdftopdf_rotation_e crop(const _cfPDFToPDFPageRect &cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, - pdftopdf_position_e ypos, - bool scale, bool autorotate, - pdftopdf_doc_t *doc); - virtual bool is_landscape(pdftopdf_rotation_e orientation); - void debug(const _cfPDFToPDFPageRect &rect, float xpos, float ypos); - private: - bool is_existing() const; - QPDFObjectHandle get(); // only once! - private: - friend class _cfPDFToPDFQPDFProcessor; - // 1st mode: existing - _cfPDFToPDFQPDFPageHandle(QPDFObjectHandle page, int orig_no = -1); - QPDFObjectHandle page; - int no; - - // 2nd mode: create new - _cfPDFToPDFQPDFPageHandle(QPDF *pdf, float width, float height); - std::map xobjs; - std::string content; - - pdftopdf_rotation_e rotation; -}; - -class _cfPDFToPDFQPDFProcessor : public _cfPDFToPDFProcessor { - public: - virtual bool load_file(FILE *f,pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e - take = CF_PDFTOPDF_WILL_STAY_ALIVE, - int flatten_forms = 1); - virtual bool load_filename(const char *name, - pdftopdf_doc_t *doc, int flatten_forms = 1); - - // TODO: virtual bool may_modify/may_print/? - virtual bool check_print_permissions(pdftopdf_doc_t *doc); - - // virtual bool set_process(const _cfPDFToPDFProcessingParameters ¶m) = 0; - - virtual std::vector> - get_pages(pdftopdf_doc_t *doc); - virtual std::shared_ptr<_cfPDFToPDFPageHandle> new_page(float width, - float height, - pdftopdf_doc_t *doc); - - virtual void add_page(std::shared_ptr<_cfPDFToPDFPageHandle> page, - bool front); - - virtual void multiply(int copies, bool collate); - - virtual void auto_rotate_all(bool dst_lscape, - pdftopdf_rotation_e normal_landscape); - virtual void add_cm(const char *defaulticc, const char *outputicc); - - virtual void set_comments(const std::vector &comments); - - virtual void emit_file(FILE *dst, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e - take = CF_PDFTOPDF_WILL_STAY_ALIVE); - virtual void emit_filename(const char *name, pdftopdf_doc_t *doc); - - virtual bool has_acro_form(); - private: - void close_file(); - void start(int flatten_forms); - private: - std::unique_ptr pdf; - std::vector orig_pages; - - bool hasCM; - std::string extraheader; -}; - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_PROCESSOR_H diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx b/cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx deleted file mode 100644 index ba7753d5a..000000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx +++ /dev/null @@ -1,920 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "cupsfilters/debug-internal.h" -#include -#include -#include -#include -#include -#include "qpdf-tools-private.h" -#include "qpdf-xobject-private.h" -#include "qpdf-pdftopdf-private.h" -#include "qpdf-pdftopdf-processor-private.h" -#include "qpdf-cm-private.h" -#include "pdftopdf-private.h" - -// Use: content.append(debug_box(pe.sub, xpos, ypos)); -static std::string -debug_box(const _cfPDFToPDFPageRect &box, - float xshift, - float yshift) // {{{ -{ - return (std::string("q 1 w 0.1 G\n ") + - QUtil::double_to_string(box.left + xshift) + " " + - QUtil::double_to_string(box.bottom + yshift) + " m " + - QUtil::double_to_string(box.right + xshift) + " " + - QUtil::double_to_string(box.top + yshift) + " l " + "S \n " + - - QUtil::double_to_string(box.right + xshift) + " " + - QUtil::double_to_string(box.bottom + yshift) + " m " + - QUtil::double_to_string(box.left + xshift) + " " + - QUtil::double_to_string(box.top + yshift) + " l " + "S \n " + - - QUtil::double_to_string(box.left + xshift) + " " + - QUtil::double_to_string(box.bottom + yshift) + " " + - QUtil::double_to_string(box.right - box.left) + " " + - QUtil::double_to_string(box.top - box.bottom) + " re " + "S Q\n"); -} -// }}} - -_cfPDFToPDFQPDFPageHandle::_cfPDFToPDFQPDFPageHandle(QPDFObjectHandle page, - int orig_no) // {{{ - : page(page), - no(orig_no), - rotation(ROT_0) -{ -} -// }}} - -_cfPDFToPDFQPDFPageHandle::_cfPDFToPDFQPDFPageHandle(QPDF *pdf, - float width, - float height) // {{{ - : no(0), - rotation(ROT_0) -{ - DEBUG_assert(pdf); - page = QPDFObjectHandle::parse( - "<<" - " /Type /Page" - " /Resources <<" - " /XObject null " - " >>" - " /MediaBox null " - " /Contents null " - ">>"); - page.replaceKey("/MediaBox", _cfPDFToPDFMakeBox(0, 0, width, height)); - page.replaceKey("/Contents", QPDFObjectHandle::newStream(pdf)); - // xobjects: later (in get()) - content.assign("q\n"); // TODO? different/not needed - - page = pdf->makeIndirectObject(page); // stores *pdf -} -// }}} - -// Note: _cfPDFToPDFProcessor always works with "/Rotate"d and "/UserUnit"-scaled pages/coordinates/..., having 0,0 at left,bottom of the TrimBox -_cfPDFToPDFPageRect -_cfPDFToPDFQPDFPageHandle::get_rect() const // {{{ -{ - page.assertInitialized(); - _cfPDFToPDFPageRect ret = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - ret.translate(-ret.left, -ret.bottom); - ret.rotate_move(_cfPDFToPDFGetRotate(page), ret.width, ret.height); - ret.scale(_cfPDFToPDFGetUserUnit(page)); - return (ret); -} -// }}} - -bool -_cfPDFToPDFQPDFPageHandle::is_existing() const // {{{ -{ - page.assertInitialized(); - return (content.empty()); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFQPDFPageHandle::get() // {{{ -{ - QPDFObjectHandle ret = page; - - if (!is_existing()) - { // finish up page - page.getKey("/Resources").replaceKey("/XObject", - QPDFObjectHandle::newDictionary(xobjs)); - content.append("Q\n"); - page.getKey("/Contents").replaceStreamData(content, - QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(rotation)); - } - else - { - pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page) + rotation; - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(rot)); - } - page = QPDFObjectHandle(); // i.e. uninitialized - return (ret); -} -// }}} - -// TODO: We probably need a function "ungetRect()" to transform to page/form -// space, as member -static _cfPDFToPDFPageRect -ungetRect(_cfPDFToPDFPageRect rect, - const _cfPDFToPDFQPDFPageHandle &ph, - pdftopdf_rotation_e rotation, - QPDFObjectHandle page) -{ - _cfPDFToPDFPageRect pg1 = ph.get_rect(); - _cfPDFToPDFPageRect pg2 = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - - // we have to invert /Rotate, /UserUnit and the left,bottom (TrimBox) - // translation - //_cfPDFToPDFRotationDump(rotation); - //_cfPDFToPDFRotationDump(_cfPDFToPDFGetRotate(page)); - rect.width = pg1.width; - rect.height = pg1.height; - //std::swap(rect.width, rect.height); - //rect.rotate_move(-rotation, rect.width, rect.height); - - rect.rotate_move(-_cfPDFToPDFGetRotate(page), pg1.width, pg1.height); - rect.scale(1.0 / _cfPDFToPDFGetUserUnit(page)); - - //_cfPDFToPDFPageRect pg2=_cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - rect.translate(pg2.left, pg2.bottom); - //rect.dump(); - - return (rect); -} - -// TODO FIXME rotations are strange ... (via ungetRect) -// TODO? for non-existing (either drop comment or facility to create split -// streams needed) -void -_cfPDFToPDFQPDFPageHandle::add_border_rect(const _cfPDFToPDFPageRect &_rect, - pdftopdf_border_type_e border, - float fscale) // {{{ -{ - DEBUG_assert(is_existing()); - DEBUG_assert(border != pdftopdf_border_type_e::NONE); - - // straight from pstops - const double lw = (border & THICK) ? 0.5 : 0.24; - double line_width = lw * fscale; - double margin = 2.25 * fscale; - // (PageLeft + margin, PageBottom + margin) - // rect (PageRight - PageLeft - 2 * margin, ...) ... - // for nup > 1: PageLeft = 0, etc. - // if (double) margin += 2 * fscale ...rect... - - _cfPDFToPDFPageRect rect = ungetRect(_rect, *this, rotation, page); - - DEBUG_assert(rect.left <= rect.right); - DEBUG_assert(rect.bottom <= rect.top); - - std::string boxcmd="q\n"; - boxcmd += " " +QUtil::double_to_string(line_width) + " w 0 G \n"; - boxcmd += " " +QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(rect.top - rect.bottom - 2 * margin) + " re S \n"; - if (border & TWO) - { - margin += 2 * fscale; - boxcmd += " " + QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(rect.top - rect.bottom - 2 * margin) + " re S \n"; - } - boxcmd += "Q\n"; - - // if (!is_existing()) - // // TODO: only after - // return; - - DEBUG_assert(page.getOwningQPDF()); // existing pages are always indirect -#ifdef DEBUG // draw it on top - static const char *pre = "%pdftopdf q\n" - "q\n", - *post = "%pdftopdf Q\n" - "Q\n"; - - QPDFObjectHandle stm1 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - pre), - stm2 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - std::string(post) + - boxcmd); - - page.addPageContents(stm1, true); // before - page.addPageContents(stm2, false); // after -#else - QPDFObjectHandle stm = QPDFObjectHandle::newStream(page.getOwningQPDF(), - boxcmd); - page.addPageContents(stm, true); // before -#endif -} -// }}} - - -// -// This crop function is written for print-scaling=fill option. -// Trim Box is used for trimming the page in required size. -// scale tells if we need to scale input file. -// - -pdftopdf_rotation_e -_cfPDFToPDFQPDFPageHandle::crop(const _cfPDFToPDFPageRect &cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, - pdftopdf_position_e ypos, - bool scale, - bool autorotate, - pdftopdf_doc_t *doc) -{ - page.assertInitialized(); - pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(page); - if (orientation == ROT_0 || orientation == ROT_180) - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_90)); - else - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_0)); - - _cfPDFToPDFPageRect currpage = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - double width = currpage.right - currpage.left; - double height = currpage.top - currpage.bottom; - double pageWidth = cropRect.right - cropRect.left; - double pageHeight = cropRect.top - cropRect.bottom; - double final_w, final_h; //Width and height of cropped image. - - pdftopdf_rotation_e pageRot = _cfPDFToPDFGetRotate(page); - if ((autorotate && - (((pageRot == ROT_0 || pageRot == ROT_180) && - pageWidth <= pageHeight) || - ((pageRot == ROT_90 || pageRot == ROT_270) && - pageWidth > pageHeight))) || - (!autorotate && - (param_orientation == ROT_90 || param_orientation == ROT_270))) - std::swap(pageHeight, pageWidth); - if (scale) - { - if(width * pageHeight / pageWidth <= height) - { - final_w = width; - final_h = width * pageHeight / pageWidth; - } - else - { - final_w = height * pageWidth / pageHeight; - final_h = height; - } - } - else - { - final_w = pageWidth; - final_h = pageHeight; - } - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: After Cropping: %lf %lf %lf %lf", - width, height, final_w, final_h); - double posw = (width - final_w) / 2, - posh = (height - final_h) / 2; - // posw, posh : pdftopdf_position_e along width and height respectively. - // Calculating required position. - if (xpos == pdftopdf_position_e::LEFT) - posw = 0; - else if (xpos == pdftopdf_position_e::RIGHT) - posw *= 2; - - if (ypos == pdftopdf_position_e::TOP) - posh *= 2; - else if (ypos == pdftopdf_position_e::BOTTOM) - posh = 0; - - // making _cfPDFToPDFPageRect for cropping. - currpage.left += posw; - currpage.bottom += posh; - currpage.top = currpage.bottom + final_h; - currpage.right = currpage.left + final_w; - // Cropping. - // TODO: Borders are covered by the image. buffer space? - page.replaceKey("/TrimBox", - _cfPDFToPDFMakeBox(currpage.left, currpage.bottom, - currpage.right, currpage.top)); - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(save_rotate)); - - return (_cfPDFToPDFGetRotate(page)); -} - -bool -_cfPDFToPDFQPDFPageHandle::is_landscape(pdftopdf_rotation_e orientation) -{ - page.assertInitialized(); - pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(page); - if (orientation == ROT_0 || orientation == ROT_180) - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_90)); - else - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_0)); - - _cfPDFToPDFPageRect currpage = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - double width = currpage.right - currpage.left; - double height = currpage.top - currpage.bottom; - page.replaceOrRemoveKey("/Rotate", _cfPDFToPDFMakeRotate(save_rotate)); - if (width > height) - return (true); - return (false); -} - -// TODO: better cropping -// TODO: test/fix with qsub rotation -void -_cfPDFToPDFQPDFPageHandle::add_subpage - (const std::shared_ptr<_cfPDFToPDFPageHandle> &sub, - float xpos, - float ypos, - float scale, - const _cfPDFToPDFPageRect *crop) // {{{ -{ - auto qsub = dynamic_cast<_cfPDFToPDFQPDFPageHandle *>(sub.get()); - DEBUG_assert(qsub); - - std::string xoname = - "/X" + QUtil::int_to_string((qsub->no != -1) ? qsub->no : ++ no); - if (crop) - { - _cfPDFToPDFPageRect pg = qsub->get_rect(), - tmp = *crop; - // we need to fix a too small cropbox. - tmp.width = tmp.right - tmp.left; - tmp.height = tmp.top - tmp.bottom; - tmp.rotate_move(-_cfPDFToPDFGetRotate(qsub->page), tmp.width, tmp.height); - // TODO TODO (pg.width? / unneeded?) - // TODO: better - // TODO: we need to obey page./Rotate - if (pg.width < tmp.width) - pg.right = pg.left + tmp.width; - if (pg.height < tmp.height) - pg.top = pg.bottom + tmp.height; - - _cfPDFToPDFPageRect rect = ungetRect(pg, *qsub, ROT_0, qsub->page); - - qsub->page.replaceKey("/TrimBox", - _cfPDFToPDFMakeBox(rect.left, rect.bottom, - rect.right, rect.top)); - // TODO? do everything for cropping here? - } - xobjs[xoname] = _cfPDFToPDFMakeXObject(qsub->page.getOwningQPDF(), - qsub->page); // trick: should be the - // same as - // page->getOwningQPDF() - // [only after it's made - // indirect] - - _cfPDFToPDFMatrix mtx; - mtx.translate(xpos, ypos); - mtx.scale(scale); - mtx.rotate(qsub->rotation); // TODO? -sub.rotation ? - // TODO FIXME: this might need another - // translation!? - if (crop) // TODO? other technique: set trim-box before - // _cfPDFToPDFMakeXObject (but this modifies original page) - { - mtx.translate(crop->left, crop->bottom); - // crop->dump(); - } - - content.append("q\n "); - content.append(mtx.get_string() + " cm\n "); - if (crop) - { - content.append("0 0 " + QUtil::double_to_string(crop->right-crop->left) + - " " + QUtil::double_to_string(crop->top-crop->bottom) + - " re W n\n "); - //content.append("0 0 " + QUtil::double_to_string(crop->right-crop->left) + - // " " + QUtil::double_to_string(crop->top-crop->bottom) + - // " re S\n "); - } - content.append(xoname + " Do\n"); - content.append("Q\n"); -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::mirror() // {{{ -{ - _cfPDFToPDFPageRect orig = get_rect(); - - if (is_existing()) - { - // need to wrap in XObject to keep patterns correct - // TODO? refactor into internal ..._subpage fn ? - std::string xoname = "/X" + QUtil::int_to_string(no); - - QPDFObjectHandle subpage = get(); // this->page, with rotation - - // replace all our data - *this = _cfPDFToPDFQPDFPageHandle(subpage.getOwningQPDF(), - orig.width, orig.height); - - xobjs[xoname] = _cfPDFToPDFMakeXObject(subpage.getOwningQPDF(), subpage); - // we can only now set this->xobjs - - // content.append(std::string("1 0 0 1 0 0 cm\n "); - content.append(xoname + " Do\n"); - - DEBUG_assert(!is_existing()); - } - - static const char *pre = "%pdftopdf cm\n"; - // Note: we don't change (TODO need to?) the media box - std::string mrcmd("-1 0 0 1 " + - QUtil::double_to_string(orig.right) + " 0 cm\n"); - - content.insert(0, std::string(pre) + mrcmd); -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::rotate(pdftopdf_rotation_e rot) // {{{ -{ - rotation = rot; // "rotation += rot;" ? -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::add_label(const _cfPDFToPDFPageRect &_rect, - const std::string label) // {{{ -{ - DEBUG_assert(is_existing()); - - _cfPDFToPDFPageRect rect = ungetRect (_rect, *this, rotation, page); - - DEBUG_assert(rect.left <= rect.right); - DEBUG_assert(rect.bottom <= rect.top); - - // TODO: Only add in the font once, not once per page. - QPDFObjectHandle font = page.getOwningQPDF()->makeIndirectObject - (QPDFObjectHandle::parse( - "<<" - " /Type /Font" - " /Subtype /Type1" - " /Name /pagelabel-font" - " /BaseFont /Helvetica" // TODO: support UTF-8 labels? - ">>")); - QPDFObjectHandle resources = page.getKey ("/Resources"); - QPDFObjectHandle rfont = resources.getKey ("/Font"); - rfont.replaceKey ("/pagelabel-font", font); - - double margin = 2.25; - double height = 12; - - std::string boxcmd = "q\n"; - - // White filled rectangle (top) - boxcmd += " 1 1 1 rg\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.top - height - 2 * margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re f\n"; - - // White filled rectangle (bottom) - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + height + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re f\n"; - - // Black outline (top) - boxcmd += " 0 0 0 RG\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.top - height - 2 * margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re S\n"; - - // Black outline (bottom) - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + height + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re S\n"; - - // Black text (top) - boxcmd += " 0 0 0 rg\n"; - boxcmd += " BT\n"; - boxcmd += " /pagelabel-font 12 Tf\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + 2 * margin) + " " + - QUtil::double_to_string(rect.top - height - margin) + " Td\n"; - boxcmd += " (" + label + ") Tj\n"; - boxcmd += " ET\n"; - - // Black text (bottom) - boxcmd += " BT\n"; - boxcmd += " /pagelabel-font 12 Tf\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + 2 * margin) + " " + - QUtil::double_to_string(rect.bottom + height + 2 * margin) + " Td\n"; - boxcmd += " (" + label + ") Tj\n"; - boxcmd += " ET\n"; - - boxcmd += "Q\n"; - - DEBUG_assert(page.getOwningQPDF()); // existing pages are always indirect - static const char *pre = "%pdftopdf q\n" - "q\n", - *post="%pdftopdf Q\n" - "Q\n"; - - QPDFObjectHandle stm1 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - std::string(pre)), - stm2 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - std::string(post) + - boxcmd); - - page.addPageContents(stm1, true); // before - page.addPageContents(stm2, false); // after -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::debug(const _cfPDFToPDFPageRect &rect, - float xpos, - float ypos) // {{{ -{ - DEBUG_assert(!is_existing()); - content.append(debug_box(rect, xpos, ypos)); -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::close_file() // {{{ -{ - pdf.reset(); - hasCM = false; -} -// }}} - -// TODO? try/catch for PDF parsing errors? - -bool -_cfPDFToPDFQPDFProcessor::load_file(FILE *f, - pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take, - int flatten_forms) // {{{ -{ - close_file(); - - if (!f) - throw std::invalid_argument("load_file(NULL, ...) not allowed"); - try - { - pdf.reset(new QPDF); - } - catch (...) - { - if (take == CF_PDFTOPDF_TAKE_OWNERSHIP) - fclose(f); - throw; - } - - switch (take) - { - case CF_PDFTOPDF_WILL_STAY_ALIVE: - try - { - pdf->processFile("temp file", f, false); - } - catch (const std::exception &e) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_file failed: %s", e.what()); - return (false); - } - break; - case CF_PDFTOPDF_TAKE_OWNERSHIP: - try - { - pdf->processFile("temp file", f, true); - } - catch (const std::exception &e) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_file failed: %s", e.what()); - return (false); - } - break; - case CF_PDFTOPDF_MUST_DUPLICATE: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_file with CF_PDFTOPDF_MUST_DUPLICATE is not supported"); - return (false); - } - - start(flatten_forms); - return (true); -} -// }}} - -bool -_cfPDFToPDFQPDFProcessor::load_filename(const char *name, - pdftopdf_doc_t *doc, - int flatten_forms) // {{{ -{ - close_file(); - - try - { - pdf.reset(new QPDF); - pdf->processFile(name); - } - catch (const std::exception &e) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_filename failed: %s",e.what()); - return (false); - } - - start(flatten_forms); - return (true); -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::start(int flatten_forms) // {{{ -{ - DEBUG_assert(pdf); - - if (flatten_forms) - { - QPDFAcroFormDocumentHelper afdh(*pdf); - afdh.generateAppearancesIfNeeded(); - - QPDFPageDocumentHelper dh(*pdf); - dh.flattenAnnotations(an_print); - } - - pdf->pushInheritedAttributesToPage(); - orig_pages = pdf->getAllPages(); - - // remove them (just unlink, data still there) - const int len = orig_pages.size(); - for (int iA = 0; iA < len; iA ++) - pdf->removePage(orig_pages[iA]); - - // we remove stuff that becomes defunct (probably) TODO - pdf->getRoot().removeKey("/PageMode"); - pdf->getRoot().removeKey("/Outlines"); - pdf->getRoot().removeKey("/OpenAction"); - pdf->getRoot().removeKey("/PageLabels"); -} -// }}} - -bool -_cfPDFToPDFQPDFProcessor::check_print_permissions(pdftopdf_doc_t *doc) // {{{ -{ - if (!pdf) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); - return (false); - } - return (pdf->allowPrintHighRes() || - pdf->allowPrintLowRes()); // from legacy pdftopdf -} -// }}} - -std::vector> -_cfPDFToPDFQPDFProcessor::get_pages(pdftopdf_doc_t *doc) // {{{ -{ - std::vector> ret; - if (!pdf) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); - DEBUG_assert(0); - return (ret); - } - const int len = orig_pages.size(); - ret.reserve(len); - for (int iA = 0; iA < len; iA ++) - ret.push_back(std::shared_ptr<_cfPDFToPDFPageHandle>(new _cfPDFToPDFQPDFPageHandle(orig_pages[iA], iA+1))); - return (ret); -} -// }}} - -std::shared_ptr<_cfPDFToPDFPageHandle> -_cfPDFToPDFQPDFProcessor::new_page(float width, - float height, - pdftopdf_doc_t *doc) // {{{ -{ - if (!pdf) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); - DEBUG_assert(0); - return (std::shared_ptr<_cfPDFToPDFPageHandle>()); - } - return (std::shared_ptr<_cfPDFToPDFQPDFPageHandle>(new _cfPDFToPDFQPDFPageHandle(pdf.get(), width, height))); - // return std::make_shared<_cfPDFToPDFQPDFPageHandle>(pdf.get(), width, height); - // problem: make_shared not friend -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::add_page(std::shared_ptr<_cfPDFToPDFPageHandle> page, - bool front) // {{{ -{ - DEBUG_assert(pdf); - auto qpage = dynamic_cast<_cfPDFToPDFQPDFPageHandle *>(page.get()); - if (qpage) - pdf->addPage(qpage->get(), front); -} -// }}} - -#if 0 -// we remove stuff now probably defunct TODO -pdf->getRoot().removeKey("/PageMode"); -pdf->getRoot().removeKey("/Outlines"); -pdf->getRoot().removeKey("/OpenAction"); -pdf->getRoot().removeKey("/PageLabels"); -#endif - -void -_cfPDFToPDFQPDFProcessor::multiply(int copies, - bool collate) // {{{ -{ - DEBUG_assert(pdf); - DEBUG_assert(copies > 0); - - std::vector pages = pdf->getAllPages(); // need copy - const int len = pages.size(); - - if (collate) - { - for (int iA = 1; iA < copies; iA ++) - for (int iB = 0; iB < len; iB ++) - pdf->addPage(pages[iB].shallowCopy(), false); - } - else - { - for (int iB = 0; iB < len; iB ++) - for (int iA = 1; iA < copies; iA ++) - pdf->addPageAt(pages[iB].shallowCopy(), false, pages[iB]); - } -} -// }}} - -// TODO? elsewhere? -void -_cfPDFToPDFQPDFProcessor::auto_rotate_all(bool dst_lscape, - pdftopdf_rotation_e normal_landscape) // {{{ -{ - DEBUG_assert(pdf); - - const int len = orig_pages.size(); - for (int iA = 0; iA < len; iA ++) - { - QPDFObjectHandle page = orig_pages[iA]; - - pdftopdf_rotation_e src_rot = _cfPDFToPDFGetRotate(page); - - // copy'n'paste from _cfPDFToPDFQPDFPageHandle::get_rect - _cfPDFToPDFPageRect ret = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - // ret.translate(-ret.left, -ret.bottom); - ret.rotate_move(src_rot, ret.width, ret.height); - // ret.scale(_cfPDFToPDFGetUserUnit(page)); - - const bool src_lscape = (ret.width > ret.height); - if (src_lscape != dst_lscape) - { - pdftopdf_rotation_e rotation = normal_landscape; - // TODO? other rotation direction, e.g. - // if (src_rot == ROT_0) && (param.orientation == ROT_270) ... etc. - // rotation = ROT_270; - - page.replaceOrRemoveKey("/Rotate", - _cfPDFToPDFMakeRotate(src_rot + rotation)); - } - } -} -// }}} - -// TODO -void -_cfPDFToPDFQPDFProcessor::add_cm(const char *defaulticc, - const char *outputicc) // {{{ -{ - DEBUG_assert(pdf); - - if (_cfPDFToPDFHasOutputIntent(*pdf)) - return; // nothing to do - - QPDFObjectHandle srcicc = _cfPDFToPDFSetDefaultICC(*pdf, defaulticc); - // TODO? rename to putDefaultICC? - _cfPDFToPDFAddDefaultRGB(*pdf, srcicc); - - _cfPDFToPDFAddOutputIntent(*pdf, outputicc); - - hasCM = true; -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::set_comments - (const std::vector &comments) // {{{ -{ - extraheader.clear(); - const int len = comments.size(); - for (int iA = 0; iA < len; iA ++) - { - DEBUG_assert(comments[iA].at(0) == '%'); - extraheader.append(comments[iA]); - extraheader.push_back('\n'); - } -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::emit_file(FILE *f, - pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take) // {{{ -{ - if (!pdf) - return; - - QPDFWriter out(*pdf); - switch (take) - { - case CF_PDFTOPDF_WILL_STAY_ALIVE: - out.setOutputFile("temp file", f, false); - break; - case CF_PDFTOPDF_TAKE_OWNERSHIP: - out.setOutputFile("temp file", f, true); - break; - case CF_PDFTOPDF_MUST_DUPLICATE: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: emit_file with CF_PDFTOPDF_MUST_DUPLICATE is not supported"); - return; - } - if (hasCM) - out.setMinimumPDFVersion("1.4"); - else - out.setMinimumPDFVersion("1.2"); - if (!extraheader.empty()) - out.setExtraHeaderText(extraheader); - out.setPreserveEncryption(false); - out.write(); -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::emit_filename(const char *name, - pdftopdf_doc_t *doc) // {{{ -{ - if (!pdf) - return; - - // special case: name == NULL -> stdout - QPDFWriter out(*pdf, name); - if (hasCM) - out.setMinimumPDFVersion("1.4"); - else - out.setMinimumPDFVersion("1.2"); - if (!extraheader.empty()) - out.setExtraHeaderText(extraheader); - out.setPreserveEncryption(false); - std::vector pages = pdf->getAllPages(); - int len = pages.size(); - if (len) - out.write(); - else - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: No pages left, outputting empty file."); -} -// }}} - -// TODO: -// loadPDF(); success? - -bool -_cfPDFToPDFQPDFProcessor::has_acro_form() // {{{ -{ - if (!pdf) - return false; - - QPDFObjectHandle root = pdf->getRoot(); - if (!root.hasKey("/AcroForm")) - return (false); - return (true); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf.cxx b/cupsfilters/pdftopdf/qpdf-pdftopdf.cxx deleted file mode 100644 index 2152d2d10..000000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf.cxx +++ /dev/null @@ -1,248 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-pdftopdf-private.h" -#include "qpdf-tools-private.h" -#include "cupsfilters/debug-internal.h" -#include -#include - - -_cfPDFToPDFPageRect -_cfPDFToPDFGetBoxAsRect(QPDFObjectHandle box) // {{{ -{ - _cfPDFToPDFPageRect ret; - - ret.left = box.getArrayItem(0).getNumericValue(); - ret.bottom = box.getArrayItem(1).getNumericValue(); - ret.right = box.getArrayItem(2).getNumericValue(); - ret.top = box.getArrayItem(3).getNumericValue(); - - ret.width = ret.right - ret.left; - ret.height = ret.top - ret.bottom; - - return (ret); -} -// }}} - -pdftopdf_rotation_e -_cfPDFToPDFGetRotate(QPDFObjectHandle page) // {{{ -{ - if (!page.hasKey("/Rotate")) - return (ROT_0); - double rot = page.getKey("/Rotate").getNumericValue(); - rot = fmod(rot, 360.0); - if (rot < 0) - rot += 360.0; - if (rot == 90.0) // CW - return (ROT_270); // CCW - else if (rot == 180.0) - return (ROT_180); - else if (rot == 270.0) - return (ROT_90); - else if (rot != 0.0) - throw std::runtime_error("Unexpected /Rotate value: " + - QUtil::double_to_string(rot)); - return (ROT_0); -} -// }}} - -double -_cfPDFToPDFGetUserUnit(QPDFObjectHandle page) // {{{ -{ - if (!page.hasKey("/UserUnit")) - return 1.0; - return (page.getKey("/UserUnit").getNumericValue()); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) // {{{ -{ - switch (rot) - { - case ROT_0: - return (QPDFObjectHandle::newNull()); - case ROT_90: // CCW - return (QPDFObjectHandle::newInteger(270)); // CW - case ROT_180: - return (QPDFObjectHandle::newInteger(180)); - case ROT_270: - return (QPDFObjectHandle::newInteger(90)); - default: - throw std::invalid_argument("Bad rotation"); - } -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect &rect) // {{{ -{ - return (_cfPDFToPDFMakeBox(rect.left, rect.bottom, rect.right, rect.top)); -} -// }}} - -_cfPDFToPDFMatrix::_cfPDFToPDFMatrix() // {{{ - : ctm{1,0,0,1,0,0} -{ -} -// }}} - -_cfPDFToPDFMatrix::_cfPDFToPDFMatrix(QPDFObjectHandle ar) // {{{ -{ - if (ar.getArrayNItems() != 6) - throw std::runtime_error("Not a ctm matrix"); - for (int iA = 0; iA < 6; iA ++) - ctm[iA] = ar.getArrayItem(iA).getNumericValue(); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::rotate(pdftopdf_rotation_e rot) // {{{ -{ - switch (rot) - { - case ROT_0: - break; - case ROT_90: - std::swap(ctm[0], ctm[2]); - std::swap(ctm[1], ctm[3]); - ctm[2] = -ctm[2]; - ctm[3] = -ctm[3]; - break; - case ROT_180: - ctm[0] = -ctm[0]; - ctm[3] = -ctm[3]; - break; - case ROT_270: - std::swap(ctm[0], ctm[2]); - std::swap(ctm[1], ctm[3]); - ctm[0] = -ctm[0]; - ctm[1] = -ctm[1]; - break; - default: - DEBUG_assert(0); - } - return (*this); -} -// }}} - -// TODO: test -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::rotate_move(pdftopdf_rotation_e rot, - double width, - double height) // {{{ -{ - rotate(rot); - switch (rot) - { - case ROT_0: - break; - case ROT_90: - translate(width, 0); - break; - case ROT_180: - translate(width, height); - break; - case ROT_270: - translate(0, height); - break; - } - return (*this); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::rotate(double rad) // {{{ -{ - _cfPDFToPDFMatrix tmp; - - tmp.ctm[0] = cos(rad); - tmp.ctm[1] = sin(rad); - tmp.ctm[2] = -sin(rad); - tmp.ctm[3] = cos(rad); - - return (*this *= tmp); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::translate(double tx, - double ty) // {{{ -{ - ctm[4] += ctm[0] * tx + ctm[2] * ty; - ctm[5] += ctm[1] * tx + ctm[3] * ty; - return (*this); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::scale(double sx, - double sy) // {{{ -{ - ctm[0] *= sx; - ctm[1] *= sx; - ctm[2] *= sy; - ctm[3] *= sy; - return (*this); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::operator*=(const _cfPDFToPDFMatrix &rhs) // {{{ -{ - double tmp[6]; - - std::copy(ctm, ctm + 6, tmp); - - ctm[0] = tmp[0] * rhs.ctm[0] + tmp[2] * rhs.ctm[1]; - ctm[1] = tmp[1] * rhs.ctm[0] + tmp[3] * rhs.ctm[1]; - - ctm[2] = tmp[0] * rhs.ctm[2] + tmp[2] * rhs.ctm[3]; - ctm[3] = tmp[1] * rhs.ctm[2] + tmp[3] * rhs.ctm[3]; - - ctm[4] = tmp[0] * rhs.ctm[4] + tmp[2] * rhs.ctm[5] + tmp[4]; - ctm[5] = tmp[1] * rhs.ctm[4] + tmp[3] * rhs.ctm[5] + tmp[5]; - - return (*this); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMatrix::get() const // {{{ -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - - ret.appendItem(QPDFObjectHandle::newReal(ctm[0])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[1])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[2])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[3])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[4])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[5])); - - return (ret); -} -// }}} - -std::string -_cfPDFToPDFMatrix::get_string() const // {{{ -{ - std::string ret; - - ret.append(QUtil::double_to_string(ctm[0])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[1])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[2])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[3])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[4])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[5])); - - return (ret); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-tools-private.h b/cupsfilters/pdftopdf/qpdf-tools-private.h deleted file mode 100644 index 577f2c4f3..000000000 --- a/cupsfilters/pdftopdf/qpdf-tools-private.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ -#define _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ - -#include -#include -#include - -QPDFObjectHandle _cfPDFToPDFGetMediaBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetCropBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetBleedBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetTrimBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetArtBox(QPDFObjectHandle page); - -QPDFObjectHandle _cfPDFToPDFMakePage(QPDF &pdf, const std::map &xobjs, - QPDFObjectHandle mediabox, - const std::string &content); - -QPDFObjectHandle _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2); - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ diff --git a/cupsfilters/pdftopdf/qpdf-tools.cxx b/cupsfilters/pdftopdf/qpdf-tools.cxx deleted file mode 100644 index d247dd1dd..000000000 --- a/cupsfilters/pdftopdf/qpdf-tools.cxx +++ /dev/null @@ -1,83 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-tools-private.h" - -QPDFObjectHandle -_cfPDFToPDFGetMediaBox(QPDFObjectHandle page) // {{{ -{ - return (page.getKey("/MediaBox")); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetCropBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/CropBox")) - return (page.getKey("/CropBox")); - return (page.getKey("/MediaBox")); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetBleedBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/BleedBox")) - return (page.getKey("/BleedBox")); - return (_cfPDFToPDFGetCropBox(page)); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetTrimBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/TrimBox")) - return (page.getKey("/TrimBox")); - return (_cfPDFToPDFGetCropBox(page)); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetArtBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/ArtBox")) - return (page.getKey("/ArtBox")); - return (_cfPDFToPDFGetCropBox(page)); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMakePage(QPDF &pdf, - const std::map &xobjs, - QPDFObjectHandle mediabox, - const std::string &content) // {{{ -{ - QPDFObjectHandle ret = QPDFObjectHandle::newDictionary(); - ret.replaceKey("/Type", QPDFObjectHandle::newName("/Page")); - - auto resdict = QPDFObjectHandle::newDictionary(); - resdict.replaceKey("/XObject", QPDFObjectHandle::newDictionary(xobjs)); - ret.replaceKey("/Resources", resdict); - ret.replaceKey("/MediaBox", mediabox); - ret.replaceKey("/Contents", QPDFObjectHandle::newStream(&pdf, content)); - - return (ret); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMakeBox(double x1, - double y1, - double x2, - double y2) // {{{ -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - ret.appendItem(QPDFObjectHandle::newReal(x1)); - ret.appendItem(QPDFObjectHandle::newReal(y1)); - ret.appendItem(QPDFObjectHandle::newReal(x2)); - ret.appendItem(QPDFObjectHandle::newReal(y2)); - return (ret); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-xobject-private.h b/cupsfilters/pdftopdf/qpdf-xobject-private.h deleted file mode 100644 index f318b1e21..000000000 --- a/cupsfilters/pdftopdf/qpdf-xobject-private.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_XOBJECT_H_ -#define _CUPS_FILTERS_PDFTOPDF_QPDF_XOBJECT_H_ - -#include - -QPDFObjectHandle _cfPDFToPDFMakeXObject(QPDF *pdf, QPDFObjectHandle page); - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_XOBJECT_H_ diff --git a/cupsfilters/pdftopdf/qpdf-xobject.cxx b/cupsfilters/pdftopdf/qpdf-xobject.cxx deleted file mode 100644 index 31610e9da..000000000 --- a/cupsfilters/pdftopdf/qpdf-xobject.cxx +++ /dev/null @@ -1,199 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-xobject-private.h" -//#include -#include -#include -#include -#include -#include "qpdf-tools-private.h" -#include "qpdf-pdftopdf-private.h" - -// TODO: need to remove Struct Parent stuff (or fix) - -// NOTE: use /TrimBox to position content inside Nup cell, -// /BleedBox to clip against - -class CombineFromContents_Provider : public QPDFObjectHandle::StreamDataProvider { -public: - CombineFromContents_Provider(const std::vector &contents); - - void provideStreamData(int objid, int generation, Pipeline* pipeline); -private: - std::vector contents; -}; - -CombineFromContents_Provider::CombineFromContents_Provider(const std::vector &contents) - : contents(contents) -{ -} - -void -CombineFromContents_Provider::provideStreamData(int objid, - int generation, - Pipeline* pipeline) -{ - Pl_Concatenate concat("concat", pipeline); - const int clen = contents.size(); - for (int iA = 0; iA < clen; iA ++) - contents[iA].pipeStreamData(&concat, true, false, false); - concat.manualFinish(); -} - -// -// To convert a page to an XObject there are several keys to consider: -// -// /Type /Page -> /Type /XObject (/Type optional for XObject) -// -> /Subtype /Form -// -> [/FormType 1] (optional) -// /Parent ? ? R -> remove -// /Resources dict -> copy -// /MediaBox rect [/CropBox /BleedBox /TrimBox /ArtBox] -// -> /BBox (use TrimBox [+ Bleed consideration?], -// with fallback to /MediaBox) -// note that /BBox is in *Form Space*, see /Matrix! -// [/BoxColorInfo dict] (used for guidelines that may be shown by viewer) -// -> ignore/remove -// [/Contents asfd] -> concatenate into stream data of the XObject -// (page is a dict, XObject a stream) -// -// [/Rotate 90] ... must be handled (either use CTM where XObject is -// /used/ -- or set /Matrix) -// [/UserUnit] (PDF 1.6) -> to /Matrix ? -- it MUST be handled. -// -// [/Group dict] -> copy -// [/Thumb stream] -> remove, not needed any more / would have to be -// regenerated (combined) -// [/B] article beads -- ignore for now -// [/Dur] -> remove (transition duration) -// [/Trans] -> remove (transitions) -// [/AA] -> remove (additional-actions) -// -// [/Metadata] what shall we do?? (kill: we can't combine XML) -// [/PieceInfo] -> remove, we can't combine private app data (?) -// [/LastModified date] (opt except /PieceInfo) -> see there -// -// [/PZ] -> remove, can't combine/keep (preferred zoom level) -// [/SeparationInfo] -> remove, no way to keep this (needed for separation) -// -// [/ID] related to web capture -- ignore/kill? -// [/StructParents] (opt except pdf contains "structural content items") -// -> copy (is this correct?) -// -// [/Annots] annotations -- ignore for now -// [/Tabs] tab order for annotations (/R row, /C column, -// /S structure order) -- see /Annots -// -// [/TemplateInstantiated] (reqd, if page was created from named page obj, -// 1.5) -- ? just ignore? -// [/PresSteps] -> remove (sub-page navigation for presentations) -// [no subpage navigation for printing / nup] -// [/VP] viewport rects -- ignore/drop or recalculate into new -// page -// - -QPDFObjectHandle -_cfPDFToPDFMakeXObject(QPDF *pdf, QPDFObjectHandle page) -{ - page.assertPageObject(); - - QPDFObjectHandle ret = QPDFObjectHandle::newStream(pdf); - QPDFObjectHandle dict = ret.getDict(); - - dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); // optional - dict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Form")); // required - // dict.replaceKey("/FormType", QPDFObjectHandle::newInteger(1)); // optional - - QPDFObjectHandle box = _cfPDFToPDFGetTrimBox(page); // already in "form space" - dict.replaceKey("/BBox", box); // reqd - - // [/Matrix .] ... default is [1 0 0 1 0 0]; we incorporate /UserUnit and - // /Rotate here - _cfPDFToPDFMatrix mtx; - if (page.hasKey("/UserUnit")) - mtx.scale(page.getKey("/UserUnit").getNumericValue()); - - // transform, so that bbox is [0 0 w h] (in outer space, but after UserUnit) - pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page); - - // calculate rotation effect on [0 0 w h] - _cfPDFToPDFPageRect bbox = _cfPDFToPDFGetBoxAsRect(box), - tmp; - tmp.left = 0; - tmp.bottom = 0; - tmp.right = 0; - tmp.top = 0; - tmp.rotate_move(rot, bbox.width, bbox.height); - // tmp.rotate_move moves the bbox; we must achieve this move with the matrix. - mtx.translate(tmp.left, tmp.bottom); // 1. move origin to end up at - // left,bottom after rotation - - mtx.rotate(rot); // 2. rotate coordinates according to /Rotate - mtx.translate(-bbox.left, -bbox.bottom); // 3. move origin from 0,0 to - // "form space" - - dict.replaceKey("/Matrix", mtx.get()); - - dict.replaceKey("/Resources", page.getKey("/Resources")); - if (page.hasKey("/Group")) - dict.replaceKey("/Group", page.getKey("/Group")); // (transparency); opt, - // copy if there - - // ?? /StructParents ... can basically copy from page, but would need - // fixup in Structure Tree - // FIXME: remove (globally) Tagged spec (/MarkInfo), and Structure Tree - - // Note: [/Name] (reqd. only in 1.0 -- but there we even can't use our - // normal img/patter procedures) - - // none: - // QPDFObjectHandle filter = QPDFObjectHandle::newArray(); - // QPDFObjectHandle decode_parms = QPDFObjectHandle::newArray(); - // null leads to use of "default filters" from qpdf's settings - QPDFObjectHandle filter = QPDFObjectHandle::newNull(); - QPDFObjectHandle decode_parms = QPDFObjectHandle::newNull(); - - std::vector contents = page.getPageContents(); - // (will assertPageObject) - - auto ph = PointerHolder(new CombineFromContents_Provider(contents)); - ret.replaceStreamData(ph, filter, decode_parms); - - return (ret); -} - -// -// we will have to fix up the structure tree (e.g. /K in element), when copying -// /StructParents; -// (there is /Pg, which has to point to the containing page, /Stm when it's not -// part of the page's content stream -// i.e. when it is in our XObject!; then there is /StmOwn ...) -// when not copying, we have to remove the structure tree completely -// (also /MarkInfo dict) -// Still this might not be sufficient(?), as there are probably BDC and EMC -// operators in the stream. -// -// -// /XObject /Form has -// [/Type /XObject] -// /Subtype /Form -// [/FormType 1] -// /BBox rect from crop box, or recalculate -// [/Matrix .] ... default is [1 0 0 1 0 0] --- we have to incorporate -// /UserUnit here?! -// [/Resources dict] from page. -// [/Group dict] used for transparency -- can copy from page -// [/Ref dict] not needed; for external reference -// [/Metadata] not, as long we can not combine. -// [/PieceInfo] can copy, but not combine -// [/LastModified date] copy if /PieceInfo there -// [/StructParent] . don't want to be one ... have to read more spec -// [/StructParents] . copy from page! -// [/OPI] no opi version. don't set -// [/OC] is this optional content? NO! not needed. -// [/Name] (only reqd. in 1.0 -- but there we even can't use our -// normal img/patter procedures) -// diff --git a/cupsfilters/pdftoraster.cxx b/cupsfilters/pdftoraster.cxx deleted file mode 100644 index 96c03ec23..000000000 --- a/cupsfilters/pdftoraster.cxx +++ /dev/null @@ -1,2200 +0,0 @@ -// -// PDF to Raster filter function for libcupsfilters. -// -// Copyright (c) 2008-2011 BBR Inc. All rights reserved. -// Copyright (c) 2012-2019 by Till Kamppeter -// Copyright (c) 2019 by Tanmay Anand. -// Modified 2021 by Pratyush Ranjan. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "colormanager.h" -#include "image.h" -#include "filter.h" -#include "ipp.h" -#include -#include -#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6) -#define HAVE_CUPS_1_7 1 -#endif - -#define USE_CMS - -#include -#include -#include -#ifdef HAVE_CPP_POPPLER_VERSION_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef USE_LCMS1 -#include -#define cmsColorSpaceSignature icColorSpaceSignature -#define cmsSetLogErrorHandler cmsSetErrorHandler -#define cmsToneCurve LPGAMMATABLE -#define cmsSigXYZData icSigXYZData -#define cmsSigLuvData icSigLuvData -#define cmsSigLabData icSigLabData -#define cmsSigYCbCrData icSigYCbCrData -#define cmsSigYxyData icSigYxyData -#define cmsSigRgbData icSigRgbData -#define cmsSigHsvData icSigHsvData -#define cmsSigHlsData icSigHlsData -#define cmsSigCmyData icSigCmyData -#define cmsSig3colorData icSig3colorData -#define cmsSigGrayData icSigGrayData -#define cmsSigCmykData icSigCmykData -#define cmsSig4colorData icSig4colorData -#define cmsSig2colorData icSig2colorData -#define cmsSig5colorData icSig5colorData -#define cmsSig6colorData icSig6colorData -#define cmsSig7colorData icSig7colorData -#define cmsSig8colorData icSig8colorData -#define cmsSig9colorData icSig9colorData -#define cmsSig10colorData icSig10colorData -#define cmsSig11colorData icSig11colorData -#define cmsSig12colorData icSig12colorData -#define cmsSig13colorData icSig13colorData -#define cmsSig14colorData icSig14colorData -#define cmsSig15colorData icSig15colorData -#else -#include -#endif - -#define MAX_CHECK_COMMENT_LINES 20 -#define MAX_BYTES_PER_PIXEL 32 - -typedef struct cms_profile_s -{ - // for color profiles - cmsHPROFILE colorProfile = NULL; - cmsHPROFILE popplerColorProfile = NULL; - cmsHTRANSFORM colorTransform = NULL; - cmsCIEXYZ D65WhitePoint; - int renderingIntent = INTENT_PERCEPTUAL; - int cm_disabled = 0; - cf_cm_calibration_t cm_calibrate; -} cms_profile_t; - -typedef struct pdftoraster_doc_s -{ // **** Document information **** - int pwgraster = 0; - int bi_level = 0; - bool allocLineBuf = false; - unsigned int bitspercolor; - unsigned int popplerNumColors; - unsigned int bitmapoffset[2]; - poppler::document *poppler_doc; - cups_page_header2_t header; - cf_logfunc_t logfunc; // Logging function, NULL for no - // logging - void *logdata; // User data for logging function, can - // be NULL - cups_file_t *inputfp; // Temporary file, if any - FILE *outputfp; // Temporary file, if any - bool swap_image_x = false; - bool swap_image_y = false; - // margin swapping - bool swap_margin_x = false; - bool swap_margin_y = false; - unsigned int nplanes; - unsigned int nbands; - unsigned int bytesPerLine; // number of bytes per line - // Note: When CUPS_ORDER_BANDED, - // cupsBytesPerLine = bytesPerLine * cupsNumColors - cms_profile_t colour_profile; -} pdftoraster_doc_t; - -typedef unsigned char *(*convert_cspace_func)(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc); - -typedef unsigned char *(*convert_line_func)(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace); - -typedef struct conversion_function_s -{ - convert_cspace_func convertCSpace; // Function for conversion of colorspaces - convert_line_func convertLineOdd; // Function tom modify raster data of a - // line - convert_line_func convertLineEven; -} conversion_function_t; - - -static cmsCIExyY adobergb_wp() -{ - double * xyY = cfCmWhitePointAdobeRGB(); - cmsCIExyY wp; - - wp.x = xyY[0]; - wp.y = xyY[1]; - wp.Y = xyY[2]; - - return (wp); -} - - -static cmsCIExyY sgray_wp() -{ - double * xyY = cfCmWhitePointSGray(); - cmsCIExyY wp; - - wp.x = xyY[0]; - wp.y = xyY[1]; - wp.Y = xyY[2]; - - return (wp); -} - - -static cmsCIExyYTRIPLE adobergb_matrix() -{ - cmsCIExyYTRIPLE m; - - double * matrix = cfCmMatrixAdobeRGB(); - - m.Red.x = matrix[0]; - m.Red.y = matrix[1]; - m.Red.Y = matrix[2]; - m.Green.x = matrix[3]; - m.Green.y = matrix[4]; - m.Green.Y = matrix[5]; - m.Blue.x = matrix[6]; - m.Blue.y = matrix[7]; - m.Blue.Y = matrix[8]; - - return (m); -} - - -static cmsHPROFILE -adobergb_profile() -{ - cmsHPROFILE adobergb; - - cmsCIExyY wp; - cmsCIExyYTRIPLE primaries; - -#if USE_LCMS1 - cmsToneCurve Gamma = cmsBuildGamma(256, 2.2); - cmsToneCurve Gamma3[3]; -#else - cmsToneCurve * Gamma = cmsBuildGamma(NULL, 2.2); - cmsToneCurve * Gamma3[3]; -#endif - Gamma3[0] = Gamma3[1] = Gamma3[2] = Gamma; - - // Build AdobeRGB profile - primaries = adobergb_matrix(); - wp = adobergb_wp(); - adobergb = cmsCreateRGBProfile(&wp, &primaries, Gamma3); - - return (adobergb); -} - - -static cmsHPROFILE -sgray_profile() -{ - cmsHPROFILE sgray; - - cmsCIExyY wp; - -#if USE_LCMS1 - cmsToneCurve Gamma = cmsBuildGamma(256, 2.2); -#else - cmsToneCurve * Gamma = cmsBuildGamma(NULL, 2.2); -#endif - // Build sGray profile - wp = sgray_wp(); - sgray = cmsCreateGrayProfile(&wp, Gamma); - - return (sgray); -} - - -#ifdef USE_LCMS1 -static int -lcms_error_handler(int ErrorCode, - const char *ErrorText) -{ - return 1; -} -#else -static void -lcms_error_handler(cmsContext contextId, - cmsUInt32Number ErrorCode, - const char *ErrorText) -{ - return; -} -#endif - - -static int -parse_opts(cf_filter_data_t *data, - cf_filter_out_format_t *outformat, - pdftoraster_doc_t *doc) -{ - int num_options = 0; - cups_option_t *options = NULL; - char *profile = NULL; - const char *t = NULL; - const char *val; - cf_logfunc_t log = data->logfunc; - void *ld = data ->logdata; - cups_cspace_t cspace = (cups_cspace_t)(-1); - - if (*outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - *outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER|| - *outformat == CF_FILTER_OUT_FORMAT_PCLM) - doc->pwgraster = 1; - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - if (*outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER || - *outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER) - { - t = cupsGetOption("media-class", num_options, options); - if (t == NULL) - t = cupsGetOption("MediaClass", num_options, options); - if (t != NULL) - { - if (*outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER && - strcasestr(t, "pwg")) - { - doc->pwgraster = 1; - *outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - else if (*outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER && - !strcasestr(t, "pwg")) - { - doc->pwgraster = 0; - *outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - } - } - } - - memset(&(doc->header), 0, sizeof(doc->header)); - cfRasterPrepareHeader(&(doc->header), data, *outformat, - (*outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - *outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER ? - *outformat : - (*outformat == CF_FILTER_OUT_FORMAT_PCLM ? - CF_FILTER_OUT_FORMAT_PWG_RASTER : - CF_FILTER_OUT_FORMAT_CUPS_RASTER)), 0, - &cspace); - - doc->header.cupsRenderingIntent[0] = '\0'; - cfGetPrintRenderIntent(data, doc->header.cupsRenderingIntent, - sizeof(doc->header.cupsRenderingIntent)); - if (strcasecmp(doc->header.cupsRenderingIntent, "PERCEPTUAL") == 0) - doc->colour_profile.renderingIntent = INTENT_PERCEPTUAL; - else if (strcasecmp(doc->header.cupsRenderingIntent, "RELATIVE") == 0) - doc->colour_profile.renderingIntent = INTENT_RELATIVE_COLORIMETRIC; - else if (strcasecmp(doc->header.cupsRenderingIntent, "SATURATION") == 0) - doc->colour_profile.renderingIntent = INTENT_SATURATION; - else if (strcasecmp(doc->header.cupsRenderingIntent, "ABSOLUTE") == 0) - doc->colour_profile.renderingIntent = INTENT_ABSOLUTE_COLORIMETRIC; - // XXX relative-bpc ??? - - if(log) log(ld, CF_LOGLEVEL_DEBUG, - "Print rendering intent = %s", doc->header.cupsRenderingIntent); - - if (doc->header.Duplex) - { - int backside; - // analyze options relevant to Duplex - // APDuplexRequiresFlippedMargin - enum - { - FM_NO, - FM_FALSE, - FM_TRUE - } flippedMargin; - - backside = cfGetBackSideOrientation(data); - - if (backside >= 0) - { - flippedMargin = (backside & 16 ? FM_TRUE : - (backside & 8 ? FM_FALSE : - FM_NO)); - backside &= 7; - - if (backside == CF_BACKSIDE_MANUAL_TUMBLE && doc->header.Tumble) - { - doc->swap_image_x = doc->swap_image_y = true; - doc->swap_margin_x = doc->swap_margin_y = true; - if (flippedMargin == FM_TRUE) - doc->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_ROTATED && !doc->header.Tumble) - { - doc->swap_image_x = doc->swap_image_y = true; - doc->swap_margin_x = doc->swap_margin_y = true; - if (flippedMargin == FM_TRUE) - doc->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_FLIPPED) - { - if (doc->header.Tumble) - { - doc->swap_image_x = true; - doc->swap_margin_x = doc->swap_margin_y = true; - } - else - doc->swap_image_y = true; - if (flippedMargin == FM_FALSE) - doc->swap_margin_y = !doc->swap_margin_y; - } - } - } - - // support the CUPS "cm-calibration" option - doc->colour_profile.cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (doc->colour_profile.cm_calibrate == CF_CM_CALIBRATION_ENABLED) - doc->colour_profile.cm_disabled = 1; - else - doc->colour_profile.cm_disabled = cfCmIsPrinterCmDisabled(data); - - if (!doc->colour_profile.cm_disabled) - cfCmGetPrinterIccProfile - (data, - cfRasterColorSpaceString(doc->header.cupsColorSpace), - doc->header.MediaType, - doc->header.HWResolution[0], doc->header.HWResolution[1], - &profile); - - if (profile != NULL) - { - doc->colour_profile.colorProfile = cmsOpenProfileFromFile(profile,"r"); - free(profile); - } - - if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL - && !strncasecmp(val, "bi-level", 8)) - doc->bi_level = 1; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: Page size requested: %s", doc->header.cupsPageSizeName); - - cupsFreeOptions(num_options, options); - - return (0); -} - - -static void -parse_pdftopdf_comment(FILE *fp, - int* deviceCopies, - bool* deviceCollate) -{ - char buf[4096]; - int i; - - // skip until PDF start header - while (fgets(buf, sizeof(buf), fp) != 0) - if (strncmp(buf, "%PDF", 4) == 0) - break; - - for (i = 0; i < MAX_CHECK_COMMENT_LINES; i ++) - { - if (fgets(buf, sizeof(buf), fp) == 0) - break; - if (strncmp(buf, "%%PDFTOPDFNumCopies", 19) == 0) - { - char *p; - - p = strchr(buf + 19, ':'); - (*deviceCopies) = atoi(p + 1); - } - else if (strncmp(buf, "%%PDFTOPDFCollate", 17) == 0) - { - char *p; - - p = strchr(buf + 17, ':'); - while (*p == ' ' || *p == '\t') p ++; - if (strncasecmp(p, "true", 4) == 0) - *deviceCollate = true; - else - *deviceCollate = false; - } - } -} - - -static unsigned char * -reverse_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *p = src; - - for (unsigned int j = 0; j < size; j ++, p ++) - *p = ~*p; - return (src); -} - - -static unsigned char * -reverse_line_swap_byte(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 1; - unsigned char *dp = dst; - - for (unsigned int j = 0; j < size; j ++, bp --, dp ++) - *dp = ~*bp; - return (dst); -} - - -static unsigned char * -reverse_line_swap_bit(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - dst = cfReverseOneBitLineSwap(src, dst, pixels, size); - return (dst); -} - - -static unsigned char * -rgb_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_cmyk_line_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + (pixels - 1) * 3; - unsigned char *dp = dst; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 4) - cfImageRGBToCMYK(bp, dp, 1); - return (dst); -} - - -static unsigned char * -rgb_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_cmy_line_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 3; - unsigned char *dp = dst; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 3) - cfImageRGBToCMY(bp, dp, 1); - return (dst); -} - - -static unsigned char * -rgb_to_kcmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src; - unsigned char *dp = dst; - unsigned char d; - - cfImageRGBToCMYK(src, dst, pixels); - // CMYK to KCMY - for (unsigned int i = 0; i < pixels; i++, bp += 3, dp += 4) - { - d = dp[3]; - dp[3] = dp[2]; - dp[2] = dp[1]; - dp[1] = dp[0]; - dp[0] = d; - } - return (dst); -} - - -static unsigned char * -rgb_to_kcmy_line_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + (pixels - 1) * 3; - unsigned char *dp = dst; - unsigned char d; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 4) - { - cfImageRGBToCMYK(bp, dp, 1); - // CMYK to KCMY - d = dp[3]; - dp[3] = dp[2]; - dp[2] = dp[1]; - dp[1] = dp[0]; - dp[0] = d; - } - return (dst); -} - - -static unsigned char * -line_no_op(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - // do nothing - return (src); -} - - -static unsigned char * -line_swap_24(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 3; - unsigned char *dp = dst; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - return (dst); -} - - -static unsigned char * -line_swap_byte(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 1; - unsigned char *dp = dst; - - for (unsigned int j = 0; j < size; j++, bp --, dp ++) - *dp = *bp; - return (dst); -} - - -static unsigned char * -line_swap_bit(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - dst = cfReverseOneBitLine(src, dst, pixels, size); - return (dst); -} - - -typedef struct func_table_s -{ - enum cups_cspace_e cspace; - unsigned int bitsPerPixel; - unsigned int bitsPerColor; - convert_line_func convertLine; - bool allocLineBuf; - convert_line_func convertLineSwap; - bool allocLineBufSwap; -} func_table_t; - - -static func_table_t specialCaseFuncs[] = -{ - {CUPS_CSPACE_K, 8, 8, reverse_line, false, reverse_line_swap_byte, true}, - {CUPS_CSPACE_K, 1, 1, reverse_line, false, reverse_line_swap_bit, true}, - {CUPS_CSPACE_GOLD, 8, 8, reverse_line, false, reverse_line_swap_byte, true}, - {CUPS_CSPACE_GOLD, 1, 1, reverse_line, false, reverse_line_swap_bit, true}, - {CUPS_CSPACE_SILVER, 8, 8, reverse_line, false, reverse_line_swap_byte, true}, - {CUPS_CSPACE_SILVER, 1, 1, reverse_line, false, reverse_line_swap_bit, true}, - {CUPS_CSPACE_CMYK, 32, 8, rgb_to_cmyk_line, true, rgb_to_cmyk_line_swap,true}, - {CUPS_CSPACE_KCMY, 32, 8, rgb_to_kcmy_line, true, rgb_to_kcmy_line_swap,true}, - {CUPS_CSPACE_CMY, 24, 8, rgb_to_cmy_line, true, rgb_to_cmy_line_swap, true}, - {CUPS_CSPACE_RGB, 24, 8, line_no_op, false, line_swap_24, true}, - {CUPS_CSPACE_SRGB, 24, 8, line_no_op, false, line_swap_24, true}, - {CUPS_CSPACE_ADOBERGB, 24, 8, line_no_op, false, line_swap_24, true}, - {CUPS_CSPACE_W, 8, 8, line_no_op, false, line_swap_byte, true}, - {CUPS_CSPACE_W, 1, 1, line_no_op, false, line_swap_bit, true}, - {CUPS_CSPACE_SW, 8, 8, line_no_op, false, line_swap_byte, true}, - {CUPS_CSPACE_SW, 1, 1, line_no_op, false, line_swap_bit, true}, - {CUPS_CSPACE_WHITE, 8, 8, line_no_op, false, line_swap_byte, true}, - {CUPS_CSPACE_WHITE, 1, 1, line_no_op, false, line_swap_bit, true}, - {CUPS_CSPACE_RGB, 0, 0, NULL, false, NULL, false} // end mark -}; - - -static unsigned char * -convert_cspace_none(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t *doc) -{ - return (src); -} - - -static unsigned char * -convert_cspace_with_profiles(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t *doc) -{ - cmsDoTransform(doc->colour_profile.colorTransform, src, pixelBuf, 1); - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_xyz_8(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t *doc) -{ - double alab[3]; - - cmsDoTransform(doc->colour_profile.colorTransform, src, alab, 1); - cmsCIELab lab; - cmsCIEXYZ xyz; - - lab.L = alab[0]; - lab.a = alab[1]; - lab.b = alab[2]; - - cmsLab2XYZ(&(doc->colour_profile.D65WhitePoint), &xyz, &lab); - pixelBuf[0] = 231.8181 * xyz.X + 0.5; - pixelBuf[1] = 231.8181 * xyz.Y + 0.5; - pixelBuf[2] = 231.8181 * xyz.Z + 0.5; - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_xyz_16(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t *doc) -{ - double alab[3]; - unsigned short *sd = (unsigned short *)pixelBuf; - - cmsDoTransform(doc->colour_profile.colorTransform, src, alab, 1); - cmsCIELab lab; - cmsCIEXYZ xyz; - - lab.L = alab[0]; - lab.a = alab[1]; - lab.b = alab[2]; - - cmsLab2XYZ(&(doc->colour_profile.D65WhitePoint), &xyz, &lab); - sd[0] = 59577.2727 * xyz.X + 0.5; - sd[1] = 59577.2727 * xyz.Y + 0.5; - sd[2] = 59577.2727 * xyz.Z + 0.5; - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_lab_8(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t *doc) -{ - double lab[3]; - - cmsDoTransform(doc->colour_profile.colorTransform , src, lab, 1); - pixelBuf[0] = 2.55 * lab[0] + 0.5; - pixelBuf[1] = lab[1] + 128.5; - pixelBuf[2] = lab[2] + 128.5; - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_lab_16(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t *doc) -{ - double lab[3]; - - cmsDoTransform(doc->colour_profile.colorTransform, src, lab, 1); - unsigned short *sd = (unsigned short *)pixelBuf; - sd[0] = 655.35 * lab[0] + 0.5; - sd[1] = 256 * (lab[1] + 128) + 0.5; - sd[2] = 256 * (lab[2] + 128) + 0.5; - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_rgba(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - unsigned char *dp = pixelBuf; - - for (int i = 0; i < 3; i ++) - *dp++ = *src++; - *dp = 255; - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_rgbw(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - unsigned char cmyk[4]; - unsigned char *dp = pixelBuf; - - cfImageRGBToCMYK(src, cmyk, 1); - for (int i = 0; i < 4; i ++) - *dp++ = ~cmyk[i]; - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_cmyk(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - cfImageRGBToCMYK(src, pixelBuf, 1); - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_cmy(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - cfImageRGBToCMY(src, pixelBuf, 1); - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_ymc(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - cfImageRGBToCMY(src, pixelBuf, 1); - // swap C and Y - unsigned char d = pixelBuf[0]; - pixelBuf[0] = pixelBuf[2]; - pixelBuf[2] = d; - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_kcmy(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - cfImageRGBToCMYK(src, pixelBuf, 1); - unsigned char d = pixelBuf[3]; - pixelBuf[3] = pixelBuf[2]; - pixelBuf[2] = pixelBuf[1]; - pixelBuf[1] = pixelBuf[0]; - pixelBuf[0] = d; - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_kcmycm_temp(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - return (cfRGB8toKCMYcm(src, pixelBuf, x, y)); -} - - -static unsigned char * -rgb_8_to_ymck(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t* doc) -{ - cfImageRGBToCMYK(src, pixelBuf, 1); - // swap C and Y - unsigned char d = pixelBuf[0]; - pixelBuf[0] = pixelBuf[2]; - pixelBuf[2] = d; - return (pixelBuf); -} - - -static unsigned char * -w_8_to_k_8(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pdftoraster_doc_t *doc) -{ - *pixelBuf = ~(*src); - return (pixelBuf); -} - - -static unsigned char * -convert_line_chunked(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - // Assumed that BitsPerColor is 8 - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + i * (doc->popplerNumColors), pixelBuf1, i, - row, doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->header.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, 0, i, pb, doc->header.cupsNumColors, - doc->header.cupsBitsPerColor, doc->header.cupsColorOrder); - } - return (dst); -} - - -static unsigned char * -convert_line_chunked_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - // Assumed that BitsPerColor is 8 - for (unsigned int i = 0; i < pixels; i++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + (pixels - i - 1) * doc->popplerNumColors, - pixelBuf1, i, row, doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->header.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, 0, i, pb, doc->header.cupsNumColors, - doc->header.cupsBitsPerColor, doc->header.cupsColorOrder); - } - return (dst); -} - - -static unsigned char * -convert_line_plane(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - // Assumed that BitsPerColor is 8 - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + i * doc->popplerNumColors, - pixelBuf1, i, row, doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->header.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, plane, i, pb, doc->header.cupsNumColors, - doc->header.cupsBitsPerColor, doc->header.cupsColorOrder); - } - return (dst); -} - - -static unsigned char * -convert_line_plane_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pdftoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + (pixels - i - 1) * doc->popplerNumColors, - pixelBuf1, i, row, doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->header.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, plane, i, pb, doc->header.cupsNumColors, - doc->header.cupsBitsPerColor, doc->header.cupsColorOrder); - } - return (dst); -} - - -// Handle special cases which appear in the Gutenprint driver -static bool -select_special_case(pdftoraster_doc_t* doc, - conversion_function_t* convert) -{ - int i; - - for (i = 0; specialCaseFuncs[i].bitsPerPixel > 0; i ++) - { - if (doc->header.cupsColorSpace == specialCaseFuncs[i].cspace && - doc->header.cupsBitsPerPixel == specialCaseFuncs[i].bitsPerPixel && - doc->header.cupsBitsPerColor == specialCaseFuncs[i].bitsPerColor) - { - convert->convertLineOdd = specialCaseFuncs[i].convertLine; - if (doc->header.Duplex && doc->swap_image_x) - { - convert->convertLineEven = specialCaseFuncs[i].convertLineSwap; - doc->allocLineBuf = specialCaseFuncs[i].allocLineBufSwap; - } - else - { - convert->convertLineEven = specialCaseFuncs[i].convertLine; - doc->allocLineBuf = specialCaseFuncs[i].allocLineBuf; - } - return (true); // found - } - } - return (false); -} - - -static unsigned int -get_cms_color_space_type(cmsColorSpaceSignature cs) -{ - switch (cs) - { - case cmsSigXYZData: - return (PT_XYZ); - break; - case cmsSigLabData: - return (PT_Lab); - break; - case cmsSigLuvData: - return (PT_YUV); - break; - case cmsSigYCbCrData: - return (PT_YCbCr); - break; - case cmsSigYxyData: - return (PT_Yxy); - break; - case cmsSigRgbData: - return (PT_RGB); - break; - case cmsSigGrayData: - return (PT_GRAY); - break; - case cmsSigHsvData: - return (PT_HSV); - break; - case cmsSigHlsData: - return (PT_HLS); - break; - case cmsSigCmykData: - return (PT_CMYK); - break; - case cmsSigCmyData: - return (PT_CMY); - break; - case cmsSig2colorData: - case cmsSig3colorData: - case cmsSig4colorData: - case cmsSig5colorData: - case cmsSig6colorData: - case cmsSig7colorData: - case cmsSig8colorData: - case cmsSig9colorData: - case cmsSig10colorData: - case cmsSig11colorData: - case cmsSig12colorData: - case cmsSig13colorData: - case cmsSig14colorData: - case cmsSig15colorData: - default: - break; - } - return (PT_RGB); -} - - -// select convertLine function -static int -select_convert_func(cups_raster_t *raster, - pdftoraster_doc_t* doc, - conversion_function_t *convert, - cf_logfunc_t log, - void* ld) -{ - doc->bitspercolor = doc->header.cupsBitsPerColor; - if ((doc->colour_profile.colorProfile == NULL || - doc->colour_profile.popplerColorProfile == - doc->colour_profile.colorProfile) && - (doc->header.cupsColorOrder == CUPS_ORDER_CHUNKED || - doc->header.cupsNumColors == 1)) - { - if (select_special_case(doc, convert)) - return (0); - } - - switch (doc->header.cupsColorOrder) - { - case CUPS_ORDER_BANDED: - case CUPS_ORDER_PLANAR: - if (doc->header.cupsNumColors > 1) - { - convert->convertLineEven = convert_line_plane_swap; - convert->convertLineOdd = convert_line_plane; - break; - } - default: - case CUPS_ORDER_CHUNKED: - convert->convertLineEven = convert_line_chunked_swap; - convert->convertLineOdd = convert_line_chunked; - break; - } - if (!doc->header.Duplex || !doc->swap_image_x) - convert->convertLineEven = convert->convertLineOdd; - doc->allocLineBuf = true; - - if (doc->colour_profile.colorProfile != NULL && - doc->colour_profile.popplerColorProfile != - doc->colour_profile.colorProfile) - { - unsigned int bytes; - - switch (doc->header.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - if (doc->header.cupsBitsPerColor == 8) - convert->convertCSpace = convert_cspace_lab_8; - else - // 16 bits - convert->convertCSpace = convert_cspace_lab_16; - bytes = 0; // double - break; - case CUPS_CSPACE_CIEXYZ: - if (doc->header.cupsBitsPerColor == 8) - convert->convertCSpace = convert_cspace_xyz_8; - else - // 16 bits - convert->convertCSpace = convert_cspace_xyz_16; - bytes = 0; // double - break; - default: - convert->convertCSpace = convert_cspace_with_profiles; - bytes = doc->header.cupsBitsPerColor / 8; - break; - } - doc->bitspercolor = 0; // convert bits in convertCSpace - if (doc->colour_profile.popplerColorProfile == NULL) - doc->colour_profile.popplerColorProfile = cmsCreate_sRGBProfile(); - unsigned int dcst = - get_cms_color_space_type(cmsGetColorSpace(doc->colour_profile.colorProfile)); - if ((doc->colour_profile.colorTransform = - cmsCreateTransform(doc->colour_profile.popplerColorProfile, - COLORSPACE_SH(PT_RGB) | CHANNELS_SH(3) | - BYTES_SH(1), - doc->colour_profile.colorProfile, - COLORSPACE_SH(dcst) | - CHANNELS_SH(doc->header.cupsNumColors) | - BYTES_SH(bytes), - doc->colour_profile.renderingIntent, 0)) == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Can't create color transform."); - return (1); - } - } - else - { - // select convertCSpace function - switch (doc->header.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - case CUPS_CSPACE_CIEXYZ: - convert->convertCSpace = convert_cspace_none; - break; - case CUPS_CSPACE_CMY: - convert->convertCSpace = rgb_8_to_cmy; - break; - case CUPS_CSPACE_YMC: - convert->convertCSpace = rgb_8_to_ymc; - break; - case CUPS_CSPACE_CMYK: - convert->convertCSpace = rgb_8_to_cmyk; - break; - case CUPS_CSPACE_KCMY: - convert->convertCSpace = rgb_8_to_kcmy; - break; - case CUPS_CSPACE_KCMYcm: - if (doc->header.cupsBitsPerColor > 1) - convert->convertCSpace = rgb_8_to_kcmy; - else - convert->convertCSpace = rgb_8_to_kcmycm_temp; - break; - case CUPS_CSPACE_GMCS: - case CUPS_CSPACE_GMCK: - case CUPS_CSPACE_YMCK: - convert->convertCSpace = rgb_8_to_ymck; - break; - case CUPS_CSPACE_RGBW: - convert->convertCSpace = rgb_8_to_rgbw; - break; - case CUPS_CSPACE_RGBA: - convert->convertCSpace = rgb_8_to_rgba; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - convert->convertCSpace = convert_cspace_none; - break; - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - case CUPS_CSPACE_WHITE: - convert->convertCSpace = convert_cspace_none; - break; - case CUPS_CSPACE_K: - case CUPS_CSPACE_GOLD: - case CUPS_CSPACE_SILVER: - convert->convertCSpace = w_8_to_k_8; - break; - default: - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Specified ColorSpace is not supported"); - return (1); - } - } - - if (doc->header.cupsBitsPerColor == 1 && - (doc->header.cupsNumColors == 1 || - doc->header.cupsColorSpace == CUPS_CSPACE_KCMYcm )) - doc->bitspercolor = 0; // Do not convert the bits - - return (0); -} - - -static unsigned char * -one_bit_pixel(unsigned char *src, - unsigned char *dst, - unsigned int width, - unsigned int height, - pdftoraster_doc_t* doc) -{ - unsigned char *temp; - - temp = dst; - for (unsigned int i = 0; i < height; i ++) - cfOneBitLine(src + doc->bytesPerLine * 8 * i, - dst + doc->bytesPerLine * i, - doc->header.cupsWidth, i, doc->bi_level); - return (temp); -} - - -static unsigned char * -remove_alpha(unsigned char *src, - unsigned char *dst, - unsigned int width, - unsigned int height) -{ - unsigned char *temp; - - temp = dst; - for (unsigned int i = 0; i < height; i ++) - { - for (unsigned int j = 0; j < width; j ++) - { - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - src += 4; - dst += 3; - } - } - return (temp); -} - - -static void -write_page_image(cups_raster_t *raster, - pdftoraster_doc_t *doc, - int pageNo, - conversion_function_t* convert, - float overspray_factor, - cf_filter_iscanceledfunc_t iscanceled, - void *icd) -{ - int i; - convert_line_func convertLine; - unsigned char *lineBuf = NULL; - unsigned char *dp; - unsigned int rowsize; - int fakeres[2]; - - if (iscanceled && iscanceled(icd)) - return; - - poppler::page *current_page = doc->poppler_doc->create_page(pageNo - 1); - poppler::page_renderer pr; - pr.set_render_hint(poppler::page_renderer::antialiasing, true); - pr.set_render_hint(poppler::page_renderer::text_antialiasing, true); - - // Overspray borderless page size: If the dimensions of the page - // size are up to 10% larger than the ones of the input page, zoom - // the image by rendering with an appropriately larger fake - // resolution. - poppler::page_box_enum box = poppler::page_box_enum::crop_box; - poppler::rectf inputPageBox = current_page->page_rect(box); - for (i = 0; i < 2; i ++) - fakeres[i] = doc->header.HWResolution[i]; - if (overspray_factor != 1.0) - for (i = 0; i < 2; i ++) - fakeres[i] = (int)(fakeres[i] * overspray_factor); - - unsigned char *colordata, *newdata, *graydata, *onebitdata; - unsigned int pixel_count; - poppler::image im; - // Render the page according to the colourspace and generate the requried data - switch (doc->header.cupsColorSpace) - { - case CUPS_CSPACE_W: // Gray - case CUPS_CSPACE_K: // Black - case CUPS_CSPACE_SW: // sGray - if (doc->header.cupsBitsPerColor == 1) // Special case for 1-bit - // colorspaces - { - im = pr.render_page(current_page, fakeres[0], fakeres[1], - doc->bitmapoffset[0], doc->bitmapoffset[1], - doc->bytesPerLine * 8, doc->header.cupsHeight); - newdata = (unsigned char *)malloc(sizeof(char) * 3 * im.width() * - im.height()); - newdata = remove_alpha((unsigned char *)im.const_data(), newdata, - im.width(), im.height()); - graydata = (unsigned char *)malloc(sizeof(char) * - im.width() * im.height()); - cfImageRGBToWhite(newdata, graydata, im.width() * im.height()); - onebitdata = (unsigned char *)malloc(sizeof(char) * - doc->bytesPerLine * im.height()); - one_bit_pixel(graydata, onebitdata, im.width(), im.height(), doc); - colordata = onebitdata; - rowsize = doc->bytesPerLine; - } - else - { - im = pr.render_page(current_page, fakeres[0], fakeres[1], - doc->bitmapoffset[0], doc->bitmapoffset[1], - doc->header.cupsWidth, doc->header.cupsHeight); - newdata = (unsigned char *)malloc(sizeof(char) * 3 * - im.width() * im.height()); - newdata = remove_alpha((unsigned char *)im.const_data(), newdata, - im.width(), im.height()); - pixel_count = im.width() * im.height(); - graydata = (unsigned char *)malloc(sizeof(char) * - im.width() * im.height()); - cfImageRGBToWhite(newdata, graydata, pixel_count); - colordata = graydata; - rowsize = doc->header.cupsWidth; - } - - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_CMYK: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_RGBW: - default: - im = pr.render_page(current_page, fakeres[0], fakeres[1], - doc->bitmapoffset[0], doc->bitmapoffset[1], - doc->header.cupsWidth, doc->header.cupsHeight); - newdata = (unsigned char *)malloc(sizeof(char) * 3 * - im.width() * im.height()); - newdata = remove_alpha((unsigned char *)im.const_data(), newdata, - im.width(), im.height()); - pixel_count = im.width() * im.height(); - rowsize = doc->header.cupsWidth * 3; - colordata = newdata; - break; - } - - if (doc->allocLineBuf) - lineBuf = new unsigned char [doc->bytesPerLine]; - if ((pageNo & 1) == 0) - convertLine = convert->convertLineEven; - else - convertLine = convert->convertLineOdd; - if (doc->header.Duplex && (pageNo & 1) == 0 && doc->swap_image_y) - { - for (unsigned int plane = 0; plane < doc->nplanes; plane ++) - { - unsigned char *bp = colordata + (doc->header.cupsHeight - 1) * rowsize; - for (unsigned int h = doc->header.cupsHeight; h > 0; h--) - { - for (unsigned int band = 0; band < doc->nbands; band ++) - { - dp = convertLine(bp, lineBuf, h - 1, plane + band, - doc->header.cupsWidth, - doc->bytesPerLine, doc, convert->convertCSpace); - cupsRasterWritePixels(raster, dp, doc->bytesPerLine); - } - bp -= rowsize; - } - } - } - else - { - for (unsigned int plane = 0; plane < doc->nplanes; plane ++) - { - unsigned char *bp = colordata; - for (unsigned int h = 0; h < doc->header.cupsHeight; h ++) - { - for (unsigned int band = 0; band < doc->nbands; band ++) - { - dp = convertLine(bp, lineBuf, h, plane + band, doc->header.cupsWidth, - doc->bytesPerLine, doc, convert->convertCSpace); - cupsRasterWritePixels(raster, dp, doc->bytesPerLine); - } - bp += rowsize; - } - } - } - free(colordata); - if (doc->allocLineBuf) - delete[] lineBuf; -} - - -static int -out_page(pdftoraster_doc_t *doc, - int pageNo, - cf_filter_data_t *data, - cups_raster_t *raster, - conversion_function_t *convert, - cf_logfunc_t log, - void* ld, - cf_filter_iscanceledfunc_t iscanceled, - void *icd) -{ - int rotate = 0; - float paperdimensions[2], // Physical size of the paper - margins[4]; // Physical margins of print - double l, swap; - int imageable_area_fit = 0; - float overspray_factor = 1.0; - int i; - - if (iscanceled && iscanceled(icd)) - return (0); - - poppler::page *current_page =doc->poppler_doc->create_page(pageNo - 1); - poppler::page_box_enum box = poppler::page_box_enum::crop_box; - poppler::rectf inputPageBox = current_page->page_rect(box); - poppler::page::orientation_enum orient = current_page->orientation(); - switch (orient) - { - case poppler::page::landscape: - rotate = 90; - break; - case poppler::page::upside_down: - rotate = 180; - break; - case poppler::page::seascape: - rotate = 270; - break; - default: - rotate = 0; - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: cropbox = [ %f %f %f %f ]; rotate = %d", - inputPageBox.left(), inputPageBox.top(), inputPageBox.right(), - inputPageBox.bottom(), rotate); - // Enter input page dimensions in header, so that if no page size got - // specified for the job, the input size gets used via the header - l = inputPageBox.width(); - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - doc->header.cupsPageSize[1] = l; - else - doc->header.cupsPageSize[0] = l; - l = inputPageBox.height(); - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - doc->header.cupsPageSize[0] = l; - else - doc->header.cupsPageSize[1] = l; - if (rotate == 90 || rotate == 270) - { - doc->header.cupsImagingBBox[0] = - doc->header.cupsPageSize[0] - inputPageBox.bottom(); - doc->header.cupsImagingBBox[1] = inputPageBox.right(); - doc->header.cupsImagingBBox[2] = - doc->header.cupsPageSize[0] - inputPageBox.top(); - doc->header.cupsImagingBBox[3] = inputPageBox.left(); - } - else - { - doc->header.cupsImagingBBox[0] = inputPageBox.left(); - doc->header.cupsImagingBBox[1] = - doc->header.cupsPageSize[1] - inputPageBox.bottom(); - doc->header.cupsImagingBBox[2] = inputPageBox.right(); - doc->header.cupsImagingBBox[3] = - doc->header.cupsPageSize[1] - inputPageBox.top(); - } - for (i = 0; i < 2; i ++) - doc->header.PageSize[i] = (unsigned)(doc->header.cupsPageSize[i]); - for (i = 0; i < 4; i ++) - doc->header.ImagingBoundingBox[i] = - (unsigned)(doc->header.cupsImagingBBox[i]); - - memset(paperdimensions, 0, sizeof(paperdimensions)); - memset(margins, 0, sizeof(margins)); - if (data != NULL && (data->printer_attrs) != NULL) - { - i = cfGetPageDimensions(data->printer_attrs, data->job_attrs, - data->num_options, data->options, - &(doc->header), 0, - &(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), NULL, NULL); - // Overspray borderless page size: If the dimensions of the page - // size are up to 10% larger than the ones of the input page, zoom - // the image by rendering with an appropriately larger fake - // resolution. - if (i == 1 && // User-requested page size or size of the input - // page (if user did not request a page size) was - // fitting one of the printer - margins[0] == 0 && margins[1] == 0 && - margins[2] == 0 && margins[3] == 0 && // Borderless only - paperdimensions[0] > (int)(doc->header.cupsPageSize[0]) && - paperdimensions[0] <= (int)(doc->header.cupsPageSize[0] * 1.10) && - paperdimensions[1] > (int)(doc->header.cupsPageSize[1]) && - paperdimensions[1] <= (int)(doc->header.cupsPageSize[1] * 1.10)) - { - float factor0, factor1; - factor0 = paperdimensions[0] / doc->header.cupsPageSize[0]; - factor1 = paperdimensions[1] / doc->header.cupsPageSize[1]; - overspray_factor = (factor0 > factor1 ? factor0 : factor1); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: Zoom factor for borderless printing with overspray: %f", - overspray_factor); - } - if (doc->pwgraster == 1) - memset(margins, 0, sizeof(margins)); - } - else - { - for (i = 0; i < 2; i ++) - paperdimensions[i] = doc->header.PageSize[i]; - if (doc->header.cupsImagingBBox[3] > 0.0) - { - // Set margins if we have a bounding box defined ... - if (doc->pwgraster == 0) - { - margins[0] = doc->header.cupsImagingBBox[0]; - margins[1] = doc->header.cupsImagingBBox[1]; - margins[2] = paperdimensions[0] - doc->header.cupsImagingBBox[2]; - margins[3] = paperdimensions[1] - doc->header.cupsImagingBBox[3]; - } - } else - // ... otherwise use zero margins - for (i = 0; i < 4; i ++) - margins[i] = 0.0; - //margins[0] = 0.0; - //margins[1] = 0.0; - //margins[2] = header.PageSize[0]; - //margins[3] = header.PageSize[1]; - } - if (doc->header.Duplex && (pageNo & 1) == 0) - { - // backside: change margin if needed - if (doc->swap_margin_x) - { - swap = margins[2]; margins[2] = margins[0]; margins[0] = swap; - } - if (doc->swap_margin_y) - { - swap = margins[3]; margins[3] = margins[1]; margins[1] = swap; - } - } - - if (imageable_area_fit == 0) - { - doc->bitmapoffset[0] = margins[0] / 72.0 * doc->header.HWResolution[0]; - doc->bitmapoffset[1] = margins[3] / 72.0 * doc->header.HWResolution[1]; - } - else - { - doc->bitmapoffset[0] = 0; - doc->bitmapoffset[1] = 0; - } - - // write page header - if (doc->pwgraster == 0) - { - doc->header.cupsWidth = ((paperdimensions[0] - margins[0] - margins[2]) / - 72.0 * doc->header.HWResolution[0]) + 0.5; - doc->header.cupsHeight = ((paperdimensions[1] - margins[1] - margins[3]) / - 72.0 * doc->header.HWResolution[1]) + 0.5; - } - else - { - doc->header.cupsWidth = (paperdimensions[0] / - 72.0 * doc->header.HWResolution[0]) + 0.5; - doc->header.cupsHeight = (paperdimensions[1] / - 72.0 * doc->header.HWResolution[1]) + 0.5; - } - for (i = 0; i < 2; i ++) - { - doc->header.cupsPageSize[i] = paperdimensions[i]; - doc->header.PageSize[i] = (unsigned int)(doc->header.cupsPageSize[i] + 0.5); - if (doc->pwgraster == 0) - doc->header.Margins[i] = margins[i] + 0.5; - else - doc->header.Margins[i] = 0; - } - if (doc->pwgraster == 0) - { - doc->header.cupsImagingBBox[0] = margins[0]; - doc->header.cupsImagingBBox[1] = margins[1]; - doc->header.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; - doc->header.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; - for (i = 0; i < 4; i ++) - doc->header.ImagingBoundingBox[i] = - (unsigned int)(doc->header.cupsImagingBBox[i] + 0.5); - } else - for (i = 0; i < 4; i ++) - { - doc->header.cupsImagingBBox[i] = 0.0; - doc->header.ImagingBoundingBox[i] = 0; - } - - doc->bytesPerLine = - doc->header.cupsBytesPerLine = (doc->header.cupsBitsPerPixel * - doc->header.cupsWidth + 7) / 8; - if (doc->header.cupsColorOrder == CUPS_ORDER_BANDED) - doc->header.cupsBytesPerLine *= doc->header.cupsNumColors; - - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: Page %d: Dimensions: %fx%f; Bounding box: %f %f %f %f", - pageNo, - doc->header.cupsPageSize[0], doc->header.cupsPageSize[1], - doc->header.cupsImagingBBox[0], - doc->header.cupsImagingBBox[1], - doc->header.cupsImagingBBox[2], - doc->header.cupsImagingBBox[3]); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: Page %d: Pixel dimensions: %dx%d; Bitmap offsets: %d %d", - pageNo, doc->header.cupsWidth, doc->header.cupsHeight, - doc->bitmapoffset[0], doc->bitmapoffset[1]); - - if (!cupsRasterWriteHeader2(raster, &(doc->header))) - { - if (log) log(ld,CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Cannot write page %d header", pageNo); - return (1); - } - - // write page image - write_page_image(raster, doc, pageNo, convert, overspray_factor, - iscanceled, icd); - return (0); -} - - -static int -set_poppler_color_profile(pdftoraster_doc_t *doc, - cf_logfunc_t log, - void *ld) -{ - if (doc->header.cupsBitsPerColor != 8 && doc->header.cupsBitsPerColor != 16) - // color Profile is not supported - return (0); - - // set poppler color profile - switch (doc->header.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - if (doc->colour_profile.colorProfile == NULL) - { - cmsCIExyY wp; -#ifdef USE_LCMS1 - cmsWhitePointFromTemp(6504, &wp); // D65 White point -#else - cmsWhitePointFromTemp(&wp, 6504); // D65 White point -#endif - doc->colour_profile.colorProfile = cmsCreateLab4Profile(&wp); - } - break; - case CUPS_CSPACE_CIEXYZ: - if (doc->colour_profile.colorProfile == NULL) - { - // transform color space via CIELab - cmsCIExyY wp; -#ifdef USE_LCMS1 - cmsWhitePointFromTemp(6504, &wp); // D65 White point -#else - cmsWhitePointFromTemp(&wp, 6504); // D65 White point -#endif - cmsxyY2XYZ(&(doc->colour_profile.D65WhitePoint), &wp); - doc->colour_profile.colorProfile = cmsCreateLab4Profile(&wp); - } - break; - case CUPS_CSPACE_SRGB: - doc->colour_profile.colorProfile = cmsCreate_sRGBProfile(); - break; - case CUPS_CSPACE_ADOBERGB: - doc->colour_profile.colorProfile = adobergb_profile(); - break; - case CUPS_CSPACE_SW: - doc->colour_profile.colorProfile = sgray_profile(); - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_K: - case CUPS_CSPACE_W: - case CUPS_CSPACE_WHITE: - case CUPS_CSPACE_GOLD: - case CUPS_CSPACE_SILVER: - // We can set specified profile to poppler profile - doc->colour_profile.popplerColorProfile = - doc->colour_profile.colorProfile ; - break; - case CUPS_CSPACE_CMYK: - case CUPS_CSPACE_KCMY: - case CUPS_CSPACE_KCMYcm: - case CUPS_CSPACE_YMCK: - case CUPS_CSPACE_RGBA: - case CUPS_CSPACE_RGBW: - case CUPS_CSPACE_GMCK: - case CUPS_CSPACE_GMCS: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_YMC: - // use standard RGB - doc->colour_profile.popplerColorProfile = NULL; - break; - default: - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Specified ColorSpace is not supported"); - return (1); - break; - } - return (0); -} - - -int -cfFilterPDFToRaster(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - const char *val; - cf_filter_out_format_t outformat; - pdftoraster_doc_t doc; - int i; - int npages = 0; - cups_raster_t *raster = NULL; - cups_file_t *inputfp; // Print file - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - int deviceCopies = 1; - bool deviceCollate = false; - conversion_function_t convert; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int ret = 0; - - (void)inputseekable; - (void)parameters; - - cmsSetLogErrorHandler(lcms_error_handler); - - val = data->final_content_type; - if (val) - { - if (strcasestr(val, "pwg")) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - else if (strcasestr(val, "urf")) - outformat = CF_FILTER_OUT_FORMAT_APPLE_RASTER; - else if (strcasestr(val, "pclm")) - outformat = CF_FILTER_OUT_FORMAT_PCLM; - else - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - } - else - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - - // Note: With the CF_FILTER_OUT_FORMAT_PCLM selection the output is - // actually PWG Raster but color spaces and depth are always - // assumed to be 8-bit sRGB or sGray, the only color spaces in - // PCLm. This mode is for further processing with pwgtopclm. - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: Final output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - (outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER ? - "Apple Raster" : - "PCLm")))); - - // - // Open the input data stream specified by inputfd ... - // - - if ((inputfp = cupsFileOpenFd(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Unable to open input data stream."); - } - - return (1); - } - - // - // Make a temporary file if input is stdin... - // - - // Make a temporary file and save input data in it... - int fd; - char name[BUFSIZ]; - char buf[BUFSIZ]; - int n; - - fd = cupsTempFd(name, sizeof(name)); - if (fd < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Can't create temporary file."); - return (1); - } - - // copy input data to the tmp file - while ((n = read(inputfd, buf, BUFSIZ)) > 0) - { - if (write(fd, buf, n) != n) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Can't copy input data to temporary file."); - close(fd); - unlink(name); - return (1); - } - } - close(fd); - - if (parse_opts(data, &outformat, &doc) == 1) - { - unlink(name); - return (1); - } - - doc.poppler_doc = poppler::document::load_from_file(name, "", ""); - unlink(name); - - FILE *fp; - if ((fp = fdopen(inputfd, "rb")) == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Can't open input file."); - ret = 1; - goto out; - } - - parse_pdftopdf_comment(fp, &deviceCopies, &deviceCollate); - fclose(fp); - - if(doc.poppler_doc != NULL) - npages = doc.poppler_doc->pages(); - - // fix NumCopies, Collate ccording to PDFTOPDFComments - doc.header.NumCopies = deviceCopies; - doc.header.Collate = deviceCollate ? CUPS_TRUE : CUPS_FALSE; - // fixed other values that pdftopdf handles - doc.header.MirrorPrint = CUPS_FALSE; - doc.header.Orientation = CUPS_ORIENT_0; - - if (doc.header.cupsBitsPerColor != 1 && - doc.header.cupsBitsPerColor != 2 && - doc.header.cupsBitsPerColor != 4 && - doc.header.cupsBitsPerColor != 8 && - doc.header.cupsBitsPerColor != 16) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Specified color format is not supported."); - ret = 1; - goto out; - } - - if (doc.header.cupsColorOrder == CUPS_ORDER_PLANAR) - doc.nplanes = doc.header.cupsNumColors; - else - doc.nplanes = 1; - if (doc.header.cupsColorOrder == CUPS_ORDER_BANDED) - doc.nbands = doc.header.cupsNumColors; - else - doc.nbands = 1; - // set image's values - switch (doc.header.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - case CUPS_CSPACE_CIEXYZ: - if (doc.header.cupsColorOrder != CUPS_ORDER_CHUNKED || - (doc.header.cupsBitsPerColor != 8 && - doc.header.cupsBitsPerColor != 16)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Specified color format is not supported."); - ret = 1; - goto out; - } - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_YMC: - case CUPS_CSPACE_CMYK: - case CUPS_CSPACE_KCMY: - case CUPS_CSPACE_KCMYcm: - case CUPS_CSPACE_YMCK: - case CUPS_CSPACE_RGBA: - case CUPS_CSPACE_RGBW: - case CUPS_CSPACE_GMCK: - case CUPS_CSPACE_GMCS: - doc.popplerNumColors = 3; - break; - case CUPS_CSPACE_K: - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - case CUPS_CSPACE_WHITE: - case CUPS_CSPACE_GOLD: - case CUPS_CSPACE_SILVER: - // set paper color white - doc.popplerNumColors = 1; - break; - default: - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Specified ColorSpace is not supported."); - ret = 1; - goto out; - } - if (!(doc.colour_profile.cm_disabled)) - { - if (set_poppler_color_profile(&doc, log, ld) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Cannot set color profile."); - ret = 1; - goto out; - } - } - - if ((raster = cupsRasterOpen(outputfd, (outformat == - CF_FILTER_OUT_FORMAT_CUPS_RASTER ? - CUPS_RASTER_WRITE : - (outformat == - CF_FILTER_OUT_FORMAT_PWG_RASTER ? - CUPS_RASTER_WRITE_PWG : - (outformat == - CF_FILTER_OUT_FORMAT_APPLE_RASTER ? - CUPS_RASTER_WRITE_APPLE : - (outformat == - CF_FILTER_OUT_FORMAT_PCLM ? - CUPS_RASTER_WRITE_PWG : - CUPS_RASTER_WRITE)))))) == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Cannot open raster stream."); - ret = 1; - goto out; - } - memset(&convert, 0, sizeof(conversion_function_t)); - if (select_convert_func(raster, &doc, &convert, log, ld) == 1) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToRaster: Unable to select color conversion function."); - ret = 1; - goto out; - } - if (doc.poppler_doc != NULL) - { - for (i = 1; i <= npages; i ++) - { - if (out_page(&doc, i, data, raster, &convert, log, ld, iscanceled, - icd) == 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: Unable to output page %d.", i); - ret = 1; - goto out; - } - } - } else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToRaster: Input is empty, outputting empty file."); - - out: - if (raster) - cupsRasterClose(raster); - close(outputfd); - - // Delete doc - if (doc.colour_profile.colorProfile != NULL) - cmsCloseProfile(doc.colour_profile.colorProfile); - if (doc.colour_profile.popplerColorProfile != NULL && - doc.colour_profile.popplerColorProfile != - doc.colour_profile.colorProfile) - cmsCloseProfile(doc.colour_profile.popplerColorProfile); - if (doc.colour_profile.colorTransform != NULL) - cmsDeleteTransform(doc.colour_profile.colorTransform); - - return (ret); -} - -// Replace memory allocation methods for memory check -// For compatibility with g++ >= 4.7 compilers _GLIBCXX_THROW -// should be used as a guard, otherwise use traditional definition -#ifndef _GLIBCXX_THROW -#define _GLIBCXX_THROW throw -#endif - -void * operator new(size_t size) _GLIBCXX_THROW (std::bad_alloc) -{ - return malloc(size); -} - -void operator delete(void *p) throw () -{ - free(p); -} - -void * operator new[](size_t size) _GLIBCXX_THROW (std::bad_alloc) -{ - return malloc(size); -} - -#if 0 -void operator delete[](void *p) throw () -{ - free(p); -} -#endif // 0 diff --git a/cupsfilters/pdfutils.c b/cupsfilters/pdfutils.c deleted file mode 100644 index d5304c2af..000000000 --- a/cupsfilters/pdfutils.c +++ /dev/null @@ -1,550 +0,0 @@ -// -// PDF file output routines for libcupsfilters. -// -// Copyright 2008 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include -#include "pdfutils.h" -#include "fontembed-private.h" -#include "debug-internal.h" - - -// -// 'cfPDFOutPrintF()' - General output routine for our PDF -// -// Keeps track of characters actually written out -// - -void -cfPDFOutPrintF(cf_pdf_out_t *pdf, - const char *fmt,...) // {{{ -{ - int len; - va_list ap; - - - DEBUG_assert(pdf); - - va_start(ap, fmt); - len = vprintf(fmt, ap); - va_end(ap); - pdf->filepos += len; -} -// }}} - - -// -// 'cfPDFOutputString()' - Write out an escaped PDF string: e.g. -// "(Text \(Test\)\n)" -// - -void -cfPDFOutputString(cf_pdf_out_t *pdf, - const char *str, - int len) // {{{ -> len == -1: strlen() -{ - DEBUG_assert(pdf); - DEBUG_assert(str); - - if (len == -1) - len = strlen(str); - putc('(', stdout); - // escape special chars: \0 \\ \( \) -- don't bother about balanced parens - int iA = 0; - for (; len > 0; iA ++, len --) - { - if ((str[iA] < 32) || (str[iA] > 126)) - { - fwrite(str, 1, iA, stdout); - fprintf(stdout, "\\%03o", (unsigned char)str[iA]); - pdf->filepos += iA + 4; - str += iA + 1; - iA = -1; - } - else if ((str[iA] == '(') || (str[iA] == ')') || (str[iA] == '\\')) - { - fwrite(str, 1, iA, stdout); - fprintf(stdout, "\\%c" ,str[iA]); - pdf->filepos += iA + 2; - str += iA + 1; - iA = -1; - } - } - pdf->filepos += iA + 2; - fwrite(str, 1, iA, stdout); - putc(')', stdout); -} -// }}} - - -// -// 'cfPDFOutputHexString()' - Write ot a string in hex, 2 digits per byte -// - -void -cfPDFOutputHexString(cf_pdf_out_t *pdf, - const char *str, - int len) // {{{ -> len == -1: strlen() -{ - DEBUG_assert(pdf); - DEBUG_assert(str); - - if (len == -1) - len = strlen(str); - pdf->filepos += 2 * len + 2; - putc('<', stdout); - for (; len > 0; str++, len--) - fprintf(stdout, "%02x", (unsigned char)*str); - putc('>', stdout); -} -// }}} - - -// -// 'cfPDFOutNew()' - Allocates a new cf_pdf_out_t structure -// - -cf_pdf_out_t * // O - NULL on error -cfPDFOutNew() // {{{ -{ - cf_pdf_out_t *ret = malloc(sizeof(cf_pdf_out_t)); - - if (ret) - memset(ret, 0, sizeof(cf_pdf_out_t)); - - return (ret); -} -// }}} - - -// -// 'cfPDFOutToPDFDate()' - Format the broken up timestamp according to -// PDF requirements for /CreationDate -// -// NOTE: uses statically allocated buffer -// - -const char * -cfPDFOutToPDFDate(struct tm *curtm) // {{{ -{ - static char curdate[250]; - - - if (!curtm) - { - time_t curtime; - curtime = time(NULL); - curtm = localtime(&curtime); - } - strftime(curdate, sizeof(curdate), "D:%Y%m%d%H%M%S%z", curtm); - curdate[23] = 0; - curdate[22] = '\''; - curdate[21] = curdate[18]; - curdate[20] = curdate[17]; - curdate[19] = '\''; - return (curdate); -} -// }}} - - -// -// 'cfPDFOutAddXRef()' - Begin a new object at current point of the -// output stream and add it to the xref table. -// - -int // O - Object number -cfPDFOutAddXRef(cf_pdf_out_t *pdf) // {{{ -{ - DEBUG_assert(pdf); - DEBUG_assert(pdf->xrefsize <= pdf->xrefalloc); - - if (pdf->xrefsize == pdf->xrefalloc) - { - long *tmp; - pdf->xrefalloc += 50; - tmp = realloc(pdf->xref, sizeof(long) * pdf->xrefalloc); - if (!tmp) - { - pdf->xrefalloc=-1; - return -1; - } - pdf->xref = tmp; - } - pdf->xref[pdf->xrefsize++] = pdf->filepos; - return (pdf->xrefsize); // xrefsize + 1 -} -// }}} - - -// -// 'cfPDFOutAddPage()' - Adds page dictionary Object to the global pages tree -// - -int // O - Return 0 on error -cfPDFOutAddPage(cf_pdf_out_t *pdf, - int obj) // {{{ -{ - DEBUG_assert(pdf); - DEBUG_assert(obj > 0); - DEBUG_assert(pdf->pagessize <= pdf->pagesalloc); - - if (pdf->pagessize == pdf->pagesalloc) - { - int *tmp; - pdf->pagesalloc += 10; - tmp = realloc(pdf->pages, sizeof(int) * pdf->pagesalloc); - if (!tmp) - { - pdf->pagesalloc = -1; - return (0); - } - pdf->pages = tmp; - } - pdf->pages[pdf->pagessize++] = obj; - return (1); -} -// }}} - - -// -// 'cfPDFOutAddKeyValue()' - Add a key/value pair to the document's info -// dictionary -// - -int // O - Return 0 on error -cfPDFOutAddKeyValue(cf_pdf_out_t *pdf, - const char *key, - const char *val) // {{{ -{ - DEBUG_assert(pdf); - DEBUG_assert(pdf->kvsize <= pdf->kvalloc); - - if (pdf->kvsize == pdf->kvalloc) - { - struct cf_keyval_t *tmp; - pdf->kvalloc += 10; - tmp = realloc(pdf->kv, sizeof(struct cf_keyval_t) * pdf->kvalloc); - if (!tmp) - { - pdf->kvalloc = -1; - return (0); - } - pdf->kv = tmp; - } - pdf->kv[pdf->kvsize].key = strdup(key); - pdf->kv[pdf->kvsize].value = strdup(val); - if ((!pdf->kv[pdf->kvsize].key) || (!pdf->kv[pdf->kvsize].value)) - return (0); - pdf->kvsize ++; - return (1); -} -// }}} - - -// -// 'cfPDFOutBeginPDF()' - Start outputting a PDF -// - -int // O - Return 0 on error -cfPDFOutBeginPDF(cf_pdf_out_t *pdf) // ,...output_device?...) // {{{ -{ - int pages_obj; - - - DEBUG_assert(pdf); - DEBUG_assert(pdf->kvsize == 0); // otherwise: finish_pdf has not been called - - pdf->xrefsize = pdf->pagessize = 0; - pdf->filepos = 0; - pages_obj = cfPDFOutAddXRef(pdf); // fixed later - if (pages_obj != 1) - return (0); - cfPDFOutPrintF(pdf, "%%PDF-1.3\n"); - return (1); -} -// }}} - - -// -// 'cfPDFOutFinishPDF()' - Finish outputting the PDF -// - -void -cfPDFOutFinishPDF(cf_pdf_out_t *pdf) // {{{ -{ - int iA; - int root_obj, - info_obj = 0, - xref_start; - - - DEBUG_assert(pdf && (pdf->filepos != -1)); - - // pages - const int pages_obj = 1; - pdf->xref[0] = pdf->filepos; // now fix it - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<pagessize); - for (iA = 0; iA < pdf->pagessize; iA ++) - cfPDFOutPrintF(pdf, "%d 0 R ", pdf->pages[iA]); - cfPDFOutPrintF(pdf, - "]\n" - ">>\n" - "endobj\n"); - - // rootdict - root_obj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<>\n" - "endobj\n", - root_obj, pages_obj); - - // info - if (pdf->kvsize) - { - info_obj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<<\n", - info_obj); - for (iA = 0; iA < pdf->kvsize; iA++) - { - cfPDFOutPrintF(pdf, " /%s ", pdf->kv[iA].key); - cfPDFOutputString(pdf, pdf->kv[iA].value, -1); - cfPDFOutPrintF(pdf, "\n"); - } - cfPDFOutPrintF(pdf, - ">>\n" - "endobj\n"); - } - // TODO: some return-value checking (??) - - // write xref - xref_start = pdf->filepos; - cfPDFOutPrintF(pdf, - "xref\n" - "%d %d\n" - "%010d 65535 f \n", - 0, pdf->xrefsize + 1, 0); - for (iA = 0; iA < pdf->xrefsize; iA ++) - cfPDFOutPrintF(pdf, "%010ld 00000 n \n", - pdf->xref[iA]); - cfPDFOutPrintF(pdf, - "trailer\n" - "<<\n" - " /Size %d\n" - " /Root %d 0 R\n", - pdf->xrefsize + 1, - root_obj); - if (info_obj) - cfPDFOutPrintF(pdf, " /Info %d 0 R\n", info_obj); - cfPDFOutPrintF(pdf, - ">>\n" - "startxref\n" - "%d\n" - "%%%%EOF\n", - xref_start); - - // set to done - pdf->filepos = -1; - for (iA = 0; iA < pdf->kvsize; iA ++) - { - free(pdf->kv[iA].key); - free(pdf->kv[iA].value); - } - pdf->kvsize = 0; -} -// }}} - - -// -// 'cfPDFOutFree()' - Free memory of a cf_pdf_out_t structure -// - -void -cfPDFOutFree(cf_pdf_out_t *pdf) // {{{ -{ - if (pdf) - { - DEBUG_assert(pdf->kvsize == 0); // otherwise: finish_pdf has not been called - - free(pdf->kv); - free(pdf->pages); - free(pdf->xref); - free(pdf); - } -} -// }}} - - -static void -pdf_out_outfn(const char *buf, - int len, - void *context) // {{{ -{ - cf_pdf_out_t *pdf = (cf_pdf_out_t *)context; - - if (fwrite(buf, 1, len, stdout) != len) - { - perror("Short write"); - DEBUG_assert(0); - return; - } - pdf->filepos += len; -} -// }}} - - -// -// 'cfPDFOutWriteFont()' - Writes the font emb including descriptor to the PDF -// and returns the object number. -// - -int -cfPDFOutWriteFont(cf_pdf_out_t *pdf, - _cf_fontembed_emb_params_t *emb) // {{{ -{ - DEBUG_assert(pdf); - DEBUG_assert(emb); - - _cf_fontembed_emb_pdf_font_descr_t *fdes = _cfFontEmbedEmbPDFFontDescr(emb); - if (!fdes) - { - if (emb->outtype == _CF_FONTEMBED_EMB_FMT_STDFONT) - { // std-14 font - const int f_obj = cfPDFOutAddXRef(pdf); - char *res = _cfFontEmbedEmbPDFSimpleStdFont(emb); - if (!res) - return (0); - - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "%s" - "endobj\n", - f_obj, - res); - free(res); - return (f_obj); - } - return (0); - } - - const int ff_obj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<outtype == _CF_FONTEMBED_EMB_FMT_TTF) - cfPDFOutPrintF(pdf, " /Length1 %d 0 R\n", - ff_obj + 2); - else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_T1) // TODO - cfPDFOutPrintF(pdf, - " /Length1 ?\n" - " /Length2 ?\n" - " /Length3 ?\n"); - cfPDFOutPrintF(pdf, - ">>\n" - "stream\n"); - long streamsize = -pdf->filepos; - const int outlen = _cfFontEmbedEmbEmbed(emb, pdf_out_outfn, pdf); - streamsize += pdf->filepos; - cfPDFOutPrintF(pdf,"\nendstream\n" - "endobj\n"); - - const int l0_obj = cfPDFOutAddXRef(pdf); - DEBUG_assert(l0_obj == ff_obj + 1); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "%ld\n" - "endobj\n", - l0_obj, streamsize); - - if (emb->outtype == _CF_FONTEMBED_EMB_FMT_TTF) - { - const int l1_obj = cfPDFOutAddXRef(pdf); - DEBUG_assert(l1_obj == ff_obj + 2); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "%d\n" - "endobj\n", - l1_obj, outlen); - } - - const int fd_obj = cfPDFOutAddXRef(pdf); - char *res = _cfFontEmbedEmbPDFSimpleFontDescr(emb, fdes, ff_obj); - if (!res) - { - free(fdes); - return (0); - } - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "%s" - "endobj\n", - fd_obj, res); - free(res); - - _cf_fontembed_emb_pdf_font_widths_t *fwid = _cfFontEmbedEmbPDFFontWidths(emb); - if (!fwid) - { - free(fdes); - return (0); - } - const int f_obj = cfPDFOutAddXRef(pdf); - res = _cfFontEmbedEmbPDFSimpleFont(emb, fdes, fwid, fd_obj); - if (!res) - { - free(fwid); - free(fdes); - return (0); - } - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "%s" - "endobj\n", - f_obj,res); - free(res); - free(fwid); - - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) - { - res = _cfFontEmbedEmbPDFSimpleCIDFont(emb, fdes->fontname, f_obj); - if (!res) - { - free(fdes); - return (0); - } - const int cf_obj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "%s" - "endobj\n", - cf_obj, res); - free(res); - free(fdes); - return (cf_obj); - } - - free(fdes); - return (f_obj); -} -// }}} diff --git a/cupsfilters/pdfutils.h b/cupsfilters/pdfutils.h deleted file mode 100644 index 96258d8e5..000000000 --- a/cupsfilters/pdfutils.h +++ /dev/null @@ -1,111 +0,0 @@ -// -// PDF file output routines for libcupsfilters. -// -// Copyright 2008 by Tobias Hoffmann. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFUTILS_H_ -# define _CUPS_FILTERS_PDFUTILS_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Include necessary headers... -// - -#include -#include - - -// -// Types and structures... -// - -struct cf_keyval_t -{ - char *key, *value; -}; - -typedef struct -{ - long filepos; - - int pagessize, pagesalloc; - int *pages; - - int xrefsize, xrefalloc; - long *xref; - - int kvsize, kvalloc; - struct cf_keyval_t *kv; -} cf_pdf_out_t; - - -// -// Prototypes... -// - -// allocates a new cf_pdf_out_t structure -// returns NULL on error - -cf_pdf_out_t *cfPDFOutNew(); -void cfPDFOutFree(cf_pdf_out_t *pdf); - -// start outputting a pdf -// returns false on error - -int cfPDFOutBeginPDF(cf_pdf_out_t *pdf); -void cfPDFOutFinishPDF(cf_pdf_out_t *pdf); - -// General output routine for our pdf. -// Keeps track of characters actually written out - -void cfPDFOutPrintF(cf_pdf_out_t *pdf, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); - -// write out an escaped pdf string: e.g. (Text \(Test\)\n) -// > len == -1: use strlen(str) - -void cfPDFOutputString(cf_pdf_out_t *pdf, const char *str, int len); -void cfPDFOutputHexString(cf_pdf_out_t *pdf, const char *str, int len); - -// Format the broken up timestamp according to -// pdf requirements for /CreationDate -// NOTE: uses statically allocated buffer - -const char *cfPDFOutToPDFDate(struct tm *curtm); - -// begin a new object at current point of the -// output stream and add it to the xref table. -// returns the obj number. - -int cfPDFOutAddXRef(cf_pdf_out_t *pdf); - -// adds page dictionary >obj to the global Pages tree -// returns false on error - -int cfPDFOutAddPage(cf_pdf_out_t *pdf, int obj); - -// add a >key,>val pair to the document's Info dictionary -// returns false on error - -int cfPDFOutAddKeyValue(cf_pdf_out_t *pdf, const char *key, const char *val); - -// Writes the font >emb including descriptor to the pdf -// and returns the object number. -// On error 0 is returned. - -int cfPDFOutWriteFont(cf_pdf_out_t *pdf, - struct _cf_fontembed_emb_params_s *emb); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_PDFUTILS_H_ diff --git a/cupsfilters/pwgtopdf.cxx b/cupsfilters/pwgtopdf.cxx deleted file mode 100644 index e0d5564d5..000000000 --- a/cupsfilters/pwgtopdf.cxx +++ /dev/null @@ -1,1826 +0,0 @@ -// -// PWG/Apple Raster to PDF filter function for libcupsfilters. -// -// Copyright 2010 by Neil 'Superna' Armstrong -// Copyright 2012 by Tobias Hoffmann -// Copyright 2014-2022 by Till Kamppeter -// Copyright 2017 by Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#include "filter.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // ntohl - -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef USE_LCMS1 -#include -#define cmsColorSpaceSignature icColorSpaceSignature -#define cmsSetLogErrorHandler cmsSetErrorHandler -#define cmsSigXYZData icSigXYZData -#define cmsSigLuvData icSigLuvData -#define cmsSigLabData icSigLabData -#define cmsSigYCbCrData icSigYCbCrData -#define cmsSigYxyData icSigYxyData -#define cmsSigRgbData icSigRgbData -#define cmsSigHsvData icSigHsvData -#define cmsSigHlsData icSigHlsData -#define cmsSigCmyData icSigCmyData -#define cmsSig3colorData icSig3colorData -#define cmsSigGrayData icSigGrayData -#define cmsSigCmykData icSigCmykData -#define cmsSig4colorData icSig4colorData -#define cmsSig2colorData icSig2colorData -#define cmsSig5colorData icSig5colorData -#define cmsSig6colorData icSig6colorData -#define cmsSig7colorData icSig7colorData -#define cmsSig8colorData icSig8colorData -#define cmsSig9colorData icSig9colorData -#define cmsSig10colorData icSig10colorData -#define cmsSig11colorData icSig11colorData -#define cmsSig12colorData icSig12colorData -#define cmsSig13colorData icSig13colorData -#define cmsSig14colorData icSig14colorData -#define cmsSig15colorData icSig15colorData -#define cmsSaveProfileToMem _cmsSaveProfileToMem -#else -#include -#endif - -#define DEFAULT_PDF_UNIT 72 // 1/72 inch - -#define PRE_COMPRESS - - -// Compression method for providing data to PCLm Streams. -typedef enum compression_method_e -{ - DCT_DECODE = 0, - FLATE_DECODE, - RLE_DECODE -} compression_method_t; - -// Color conversion function -typedef unsigned char *(*convert_function)(unsigned char *src, - unsigned char *dst, - unsigned int pixels); - -// Bit conversion function -typedef unsigned char *(*bit_convert_function)(unsigned char *src, - unsigned char *dst, - unsigned int pixels); - -typedef struct pwgtopdf_doc_s // **** Document information **** -{ - cmsHPROFILE colorProfile = NULL; // ICC Profile to be applied to - // PDF - int cm_disabled = 0; // Flag raised if color - // management is disabled - convert_function conversion_function; // Raster color conversion - // function - bit_convert_function bit_function; // Raster bit function - FILE *outputfp; // Temporary file, if any - cf_logfunc_t logfunc; // Logging function, NULL for no - // logging - void *logdata; // User data for logging - // function, can be NULL - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled - // function, can be NULL -} pwgtopdf_doc_t; - -// PDF color conversion function -typedef void (*pdf_convert_function)(struct pdf_info * info, - pwgtopdf_doc_t *doc); - - -struct pdf_info // **** PDF **** -{ - pdf_info() - : pagecount(0), - width(0), height(0), - line_bytes(0), - bpp(0), bpc(0), - pclm_num_strips(0), - pclm_strip_height_preferred(16), // default strip height - pclm_strip_height(0), - pclm_strip_height_supported(1, 16), - pclm_compression_method_preferred(0), - pclm_source_resolution_supported(0), - pclm_source_resolution_default(""), - pclm_raster_back_side(""), - pclm_strip_data(0), - render_intent(""), - color_space(CUPS_CSPACE_K), - page_width(0), page_height(0), - outformat(CF_FILTER_OUT_FORMAT_PDF) - { - } - - QPDF pdf; - QPDFObjectHandle page; - unsigned pagecount; - unsigned width; - unsigned height; - unsigned line_bytes; - unsigned bpp; - unsigned bpc; - unsigned pclm_num_strips; - unsigned pclm_strip_height_preferred; - std::vector pclm_strip_height; - std::vector pclm_strip_height_supported; - std::vector pclm_compression_method_preferred; - std::vector pclm_source_resolution_supported; - std::string pclm_source_resolution_default; - std::string pclm_raster_back_side; - std::vector< PointerHolder > pclm_strip_data; - std::string render_intent; - cups_cspace_t color_space; - PointerHolder page_data; - double page_width,page_height; - cf_filter_out_format_t outformat; -}; - - -// -// Bit conversion functions -// - -static unsigned char * -invert_bits(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - unsigned int i; - - // Invert black to grayscale... - for (i = pixels, dst = src; i > 0; i --, dst ++) - *dst = ~*dst; - - return (dst); -} - - -static unsigned char * -no_bit_conversion(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - return (src); -} - - -// -// Color conversion functions -// - -static unsigned char * -rgb_to_cmyk(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -white_to_cmyk(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageWhiteToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_rgb(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageCMYKToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -white_to_rgb(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageWhiteToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_white(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageRGBToWhite(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_white(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageCMYKToWhite(src, dst, pixels); - return (dst); -} - - -static unsigned char * -no_color_conversion(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - return (src); -} - - -// -// 'split_strings()' - Split a string to a vector of strings given some -// delimiters -// -// O - std::vector of std::string after splitting -// I - input string to be split -// I - string containing delimiters -// - -static std::vector -split_strings(std::string const &str, - std::string delimiters = ",") -{ - std::vector vec(0); - std::string value = ""; - bool push_flag = false; - - for (size_t i = 0; i < str.size(); i ++) - { - if (push_flag && !(value.empty())) - { - vec.push_back(value); - push_flag = false; - value.clear(); - } - - if (delimiters.find(str[i]) != std::string::npos) - push_flag = true; - else - value += str[i]; - } - if (!value.empty()) - vec.push_back(value); - return (vec); -} - - -// -// 'num_digits()' - Calculates the number of digits in an integer -// -// O - number of digits in the input integer -// I - the integer whose digits needs to be calculated -// - -static int -num_digits(int n) -{ - if (n == 0) - return (1); - int digits = 0; - while (n) - { - ++digits; - n /= 10; - } - return (digits); -} - - -// -// 'int_to_fwstring()' - Convert a number to fixed width string by padding -// with zeroes -// O - converted string -// I - the integee which needs to be converted to string -// I - width of string required -// - -static std::string -int_to_fwstring(int n, - int width) -{ - int num_zeroes = width - num_digits(n); - if (num_zeroes < 0) - num_zeroes = 0; - return (std::string(num_zeroes, '0') + QUtil::int_to_string(n)); -} - - -static int -create_pdf_file(struct pdf_info * info, - const cf_filter_out_format_t &outformat) -{ - try - { - info->pdf.emptyPDF(); - info->outformat = outformat; - } - catch (...) - { - return (1); - } - return (0); -} - - -static QPDFObjectHandle -make_real_box(double x1, - double y1, - double x2, - double y2) -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - ret.appendItem(QPDFObjectHandle::newReal(x1)); - ret.appendItem(QPDFObjectHandle::newReal(y1)); - ret.appendItem(QPDFObjectHandle::newReal(x2)); - ret.appendItem(QPDFObjectHandle::newReal(y2)); - return (ret); -} - - -static QPDFObjectHandle -make_integer_box(int x1, - int y1, - int x2, - int y2) -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - ret.appendItem(QPDFObjectHandle::newInteger(x1)); - ret.appendItem(QPDFObjectHandle::newInteger(y1)); - ret.appendItem(QPDFObjectHandle::newInteger(x2)); - ret.appendItem(QPDFObjectHandle::newInteger(y2)); - return (ret); -} - - -// -// PDF color conversion functons... -// - -static void -modify_pdf_color(struct pdf_info *info, - int bpp, - int bpc, - convert_function fn, - pwgtopdf_doc_t *doc) -{ - unsigned old_bpp = info->bpp; - unsigned old_bpc = info->bpc; - double old_ncolor = old_bpp / old_bpc; - - unsigned old_line_bytes = info->line_bytes; - - double new_ncolor = bpp / bpc; - - info->line_bytes = (unsigned)old_line_bytes * (new_ncolor / old_ncolor); - info->bpp = bpp; - info->bpc = bpc; - doc->conversion_function = fn; -} - - -static void -convert_pdf_no_conversion(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - doc->conversion_function = no_color_conversion; - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_cmyk_8_to_white_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 8, 8, cmyk_to_white, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_rgb_8_to_white_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 8, 8, rgb_to_white, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_cmyk_8_to_rgb_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 24, 8, cmyk_to_rgb, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_white_8_to_rgb_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 24, 8, white_to_rgb, doc); - doc->bit_function = invert_bits; -} - - -static void -convert_pdf_rgb_8_to_cmyk_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 32, 8, rgb_to_cmyk, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_white_8_to_cmyk_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 32, 8, white_to_cmyk, doc); - doc->bit_function = invert_bits; -} - - -static void -convert_pdf_invert_colors(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - doc->conversion_function = no_color_conversion; - doc->bit_function = invert_bits; -} - - -// -// Create an '/ICCBased' array and embed a previously -// set ICC Profile in the PDF -// - -static QPDFObjectHandle -embed_icc_profile(QPDF &pdf, - pwgtopdf_doc_t *doc) -{ - if (doc->colorProfile == NULL) - return (QPDFObjectHandle::newNull()); - - // Return handler - QPDFObjectHandle ret; - // ICCBased array - QPDFObjectHandle array = QPDFObjectHandle::newArray(); - // Profile stream dictionary - QPDFObjectHandle iccstream; - - std::map dict; - std::map streamdict; - std::string n_value = ""; - std::string alternate_cs = ""; - PointerHolderph; - -#ifdef USE_LCMS1 - size_t profile_size; -#else - unsigned int profile_size; -#endif - - cmsColorSpaceSignature css = cmsGetColorSpace(doc->colorProfile); - - // Write color component # for /ICCBased array in stream dictionary - switch (css) - { - case cmsSigGrayData: - n_value = "1"; - alternate_cs = "/DeviceGray"; - break; - case cmsSigRgbData: - n_value = "3"; - alternate_cs = "/DeviceRGB"; - break; - case cmsSigCmykData: - n_value = "4"; - alternate_cs = "/DeviceCMYK"; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Failed to embed ICC Profile."); - return (QPDFObjectHandle::newNull()); - } - - streamdict["/Alternate"] = QPDFObjectHandle::newName(alternate_cs); - streamdict["/N"] = QPDFObjectHandle::newName(n_value); - - // Read profile into memory - cmsSaveProfileToMem(doc->colorProfile, NULL, &profile_size); - unsigned char *buff = - (unsigned char *)calloc(profile_size, sizeof(unsigned char)); - cmsSaveProfileToMem(doc->colorProfile, buff, &profile_size); - - // Write ICC profile buffer into PDF - ph = new Buffer(buff, profile_size); - iccstream = QPDFObjectHandle::newStream(&pdf, ph); - iccstream.replaceDict(QPDFObjectHandle::newDictionary(streamdict)); - - array.appendItem(QPDFObjectHandle::newName("/ICCBased")); - array.appendItem(iccstream); - - // Return a PDF object reference to an '/ICCBased' array - ret = pdf.makeIndirectObject(array); - - free(buff); - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: ICC Profile embedded in PDF."); - - return (ret); -} - - -static QPDFObjectHandle -embed_srgb_profile(QPDF &pdf, - pwgtopdf_doc_t *doc) -{ - QPDFObjectHandle iccbased_reference; - - // Create an sRGB profile from lcms - doc->colorProfile = cmsCreate_sRGBProfile(); - // Embed it into the profile - iccbased_reference = embed_icc_profile(pdf, doc); - - return (iccbased_reference); -} - - -// -// Calibration function for non-Lab PDF color spaces -// Requires white point data, and if available, gamma or matrix numbers. -// -// Output: -// [/'color_space' -// << /Gamma ['gamma[0]'...'gamma[n]'] -// /WhitePoint ['wp[0]' 'wp[1]' 'wp[2]'] -// /Matrix ['matrix[0]'...'matrix[n*n]'] -// >> -// ] -// - -static QPDFObjectHandle -get_calibration_array(const char *color_space, - double wp[], - double gamma[], - double matrix[], - double bp[]) -{ - // Check for invalid input - if ((!strcmp("/CalGray", color_space) && matrix != NULL) || - wp == NULL) - return (QPDFObjectHandle()); - - QPDFObjectHandle ret; - std::string csString = color_space; - std::string colorSpaceArrayString = ""; - - char gamma_str[128]; - char bp_str[256]; - char wp_str[256]; - char matrix_str[512]; - - // Convert numbers into string data for /Gamma, /WhitePoint, and/or /Matrix - - // WhitePoint - snprintf(wp_str, sizeof(wp_str), "/WhitePoint [%g %g %g]", - wp[0], wp[1], wp[2]); - - // Gamma - if (!strcmp("/CalGray", color_space) && gamma != NULL) - snprintf(gamma_str, sizeof(gamma_str), "/Gamma %g", - gamma[0]); - else if (!strcmp("/CalRGB", color_space) && gamma != NULL) - snprintf(gamma_str, sizeof(gamma_str), "/Gamma [%g %g %g]", - gamma[0], gamma[1], gamma[2]); - else - gamma_str[0] = '\0'; - - // BlackPoint - if (bp != NULL) - snprintf(bp_str, sizeof(bp_str), "/BlackPoint [%g %g %g]", - bp[0], bp[1], bp[2]); - else - bp_str[0] = '\0'; - - // Matrix - if (!strcmp("/CalRGB", color_space) && matrix != NULL) - { - snprintf(matrix_str, sizeof(matrix_str), - "/Matrix [%g %g %g %g %g %g %g %g %g]", - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5], - matrix[6], matrix[7], matrix[8]); - } - else - matrix_str[0] = '\0'; - - // Write array string... - colorSpaceArrayString = "[" + csString + " <<" + gamma_str + " " + wp_str + - " " + matrix_str + " " + bp_str + " >>]"; - - ret = QPDFObjectHandle::parse(colorSpaceArrayString); - - return (ret); -} - - -static QPDFObjectHandle -get_cal_rgb_array(double wp[3], - double gamma[3], - double matrix[9], - double bp[3]) -{ - QPDFObjectHandle ret = get_calibration_array("/CalRGB", wp, gamma, matrix, - bp); - return (ret); -} - - -static QPDFObjectHandle -get_cal_gray_array(double wp[3], - double gamma[1], - double bp[3]) -{ - QPDFObjectHandle ret = get_calibration_array("/CalGray", wp, gamma, 0, bp); - return (ret); -} - - -// -// 'make_pclm_strips()' - Return an std::vector of QPDFObjectHandle, each -// containing the stream data of the various strips -// which make up a PCLm page. -// -// O - std::vector of QPDFObjectHandle -// I - QPDF object -// I - number of strips per page -// I - std::vector of PointerHolder containing data for each strip -// I - strip width -// I - strip height -// I - color space -// I - bits per component -// I - document information -// - -static std::vector -make_pclm_strips(QPDF &pdf, - unsigned num_strips, - std::vector< PointerHolder > &strip_data, - std::vector &compression_methods, - unsigned width, std::vector& strip_height, - cups_cspace_t cs, - unsigned bpc, - pwgtopdf_doc_t *doc) -{ - std::vector ret(num_strips); - for (size_t i = 0; i < num_strips; i ++) - ret[i] = QPDFObjectHandle::newStream(&pdf); - - // Strip stream dictionary - std::map dict; - - dict["/Type"] = QPDFObjectHandle::newName("/XObject"); - dict["/Subtype"] = QPDFObjectHandle::newName("/Image"); - dict["/Width"] = QPDFObjectHandle::newInteger(width); - dict["/BitsPerComponent"] = QPDFObjectHandle::newInteger(bpc); - - J_COLOR_SPACE color_space; - unsigned components; - // Write "/ColorSpace" dictionary based on raster input - switch(cs) - { - case CUPS_CSPACE_K: - case CUPS_CSPACE_SW: - dict["/ColorSpace"] = QPDFObjectHandle::newName("/DeviceGray"); - color_space = JCS_GRAYSCALE; - components = 1; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - dict["/ColorSpace"] = QPDFObjectHandle::newName("/DeviceRGB"); - color_space = JCS_RGB; - components = 3; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return (std::vector(num_strips, QPDFObjectHandle())); - } - - // - // We deliver already compressed content (instead of letting QPDFWriter - // do it) to avoid using excessive memory. For that we first get preferred - // compression method to pre-compress content for strip streams. - // - // Use the compression method with highest priority of the available methods - // __________________ - // Priority | Method - // ------------------ - // 0 | DCT - // 1 | FLATE - // 2 | RLE - // ------------------ - // - - compression_method_t compression = compression_methods.front(); - for (std::vector::iterator it = - compression_methods.begin(); - it != compression_methods.end(); ++it) - compression = compression > *it ? compression : *it; - - // write compressed stream data - for (size_t i = 0; i < num_strips; i ++) - { - dict["/Height"] = QPDFObjectHandle::newInteger(strip_height[i]); - ret[i].replaceDict(QPDFObjectHandle::newDictionary(dict)); - Pl_Buffer psink("psink"); - if (compression == FLATE_DECODE) - { - Pl_Flate pflate("pflate", &psink, Pl_Flate::a_deflate); - pflate.write(strip_data[i]->getBuffer(), strip_data[i]->getSize()); - pflate.finish(); - ret[i].replaceStreamData(PointerHolder(psink.getBuffer()), - QPDFObjectHandle::newName("/FlateDecode"), - QPDFObjectHandle::newNull()); - } - else if (compression == RLE_DECODE) - { - Pl_RunLength prle("prle", &psink, Pl_RunLength::a_encode); - prle.write(strip_data[i]->getBuffer(), strip_data[i]->getSize()); - prle.finish(); - ret[i].replaceStreamData(PointerHolder(psink.getBuffer()), - QPDFObjectHandle::newName("/RunLengthDecode"), - QPDFObjectHandle::newNull()); - } - else if (compression == DCT_DECODE) - { - Pl_DCT pdct("pdct", &psink, width, strip_height[i], components, - color_space); - pdct.write(strip_data[i]->getBuffer(), strip_data[i]->getSize()); - pdct.finish(); - ret[i].replaceStreamData(PointerHolder(psink.getBuffer()), - QPDFObjectHandle::newName("/DCTDecode"), - QPDFObjectHandle::newNull()); - } - } - return (ret); -} - - -static QPDFObjectHandle -make_image(QPDF &pdf, - PointerHolder page_data, - unsigned width, - unsigned height, - std::string render_intent, - cups_cspace_t cs, - unsigned bpc, - pwgtopdf_doc_t *doc) -{ - QPDFObjectHandle ret = QPDFObjectHandle::newStream(&pdf); - - QPDFObjectHandle icc_ref; - - int use_blackpoint = 0; - std::map dict; - - dict["/Type"] = QPDFObjectHandle::newName("/XObject"); - dict["/Subtype"] = QPDFObjectHandle::newName("/Image"); - dict["/Width"] = QPDFObjectHandle::newInteger(width); - dict["/Height"] = QPDFObjectHandle::newInteger(height); - dict["/BitsPerComponent"] = QPDFObjectHandle::newInteger(bpc); - - if (!doc->cm_disabled) - { - // Write rendering intent into the PDF based on raster settings - if (render_intent == "Perceptual") - dict["/Intent"] = QPDFObjectHandle::newName("/Perceptual"); - else if (render_intent == "Absolute") - dict["/Intent"] = QPDFObjectHandle::newName("/AbsoluteColorimetric"); - else if (render_intent == "Relative") - dict["/Intent"] = QPDFObjectHandle::newName("/RelativeColorimetric"); - else if (render_intent == "Saturation") - dict["/Intent"] = QPDFObjectHandle::newName("/Saturation"); - else if (render_intent == "RelativeBpc") - { - // Enable blackpoint compensation - dict["/Intent"] = QPDFObjectHandle::newName("/RelativeColorimetric"); - use_blackpoint = 1; - } - } - - // Write "/ColorSpace" dictionary based on raster input - if (doc->colorProfile != NULL && !doc->cm_disabled) - { - icc_ref = embed_icc_profile(pdf, doc); - - if (!icc_ref.isNull()) - dict["/ColorSpace"] = icc_ref; - } - else if (!doc->cm_disabled) - { - switch (cs) - { - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - // For right now, DeviceN will use /DeviceCMYK in the PDF - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceCMYK"); - break; - case CUPS_CSPACE_K: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceGray"); - break; - case CUPS_CSPACE_SW: - if (use_blackpoint) - dict["/ColorSpace"]=get_cal_gray_array(cfCmWhitePointSGray(), - cfCmGammaSGray(), - cfCmBlackPointDefault()); - else - dict["/ColorSpace"]=get_cal_gray_array(cfCmWhitePointSGray(), - cfCmGammaSGray(), 0); - break; - case CUPS_CSPACE_CMYK: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceCMYK"); - break; - case CUPS_CSPACE_RGB: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceRGB"); - break; - case CUPS_CSPACE_SRGB: - icc_ref = embed_srgb_profile(pdf, doc); - if (!icc_ref.isNull()) - dict["/ColorSpace"]=icc_ref; - else - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceRGB"); - break; - case CUPS_CSPACE_ADOBERGB: - if (use_blackpoint) - dict["/ColorSpace"]=get_cal_rgb_array(cfCmWhitePointAdobeRGB(), - cfCmGammaAdobeRGB(), - cfCmMatrixAdobeRGB(), - cfCmBlackPointDefault()); - else - dict["/ColorSpace"]=get_cal_rgb_array(cfCmWhitePointAdobeRGB(), - cfCmGammaAdobeRGB(), - cfCmMatrixAdobeRGB(), 0); - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return (QPDFObjectHandle()); - } - } - else if (doc->cm_disabled) - { - switch(cs) - { - case CUPS_CSPACE_K: - case CUPS_CSPACE_SW: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceGray"); - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceRGB"); - break; - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - case CUPS_CSPACE_CMYK: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceCMYK"); - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return (QPDFObjectHandle()); - } - } - else - return (QPDFObjectHandle()); - - ret.replaceDict(QPDFObjectHandle::newDictionary(dict)); - -#ifdef PRE_COMPRESS - // we deliver already compressed content (instead of letting QPDFWriter - // do it), to avoid using excessive memory - Pl_Buffer psink("psink"); - Pl_Flate pflate("pflate", &psink, Pl_Flate::a_deflate); - - pflate.write(page_data->getBuffer(), page_data->getSize()); - pflate.finish(); - - ret.replaceStreamData(PointerHolder(psink.getBuffer()), - QPDFObjectHandle::newName("/FlateDecode"), - QPDFObjectHandle::newNull()); -#else - ret.replaceStreamData(page_data, QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); -#endif - - return (ret); -} - - -static int -finish_page(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - // Finish previous PDF Page - if (!info->page_data.getPointer()) - return (0); - - QPDFObjectHandle image = make_image(info->pdf, info->page_data, - info->width, info->height, - info->render_intent, - info->color_space, info->bpc, doc); - if (!image.isInitialized()) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load image data"); - return (1); - } - - // add it - info->page.getKey("/Resources").getKey("/XObject").replaceKey("/I",image); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // Finish previous PCLm page - if (info->pclm_num_strips == 0) - return (0); - - for (size_t i = 0; i < info->pclm_strip_data.size(); i ++) - if (!info->pclm_strip_data[i].getPointer()) - return (0); - - std::vector strips = - make_pclm_strips(info->pdf, info->pclm_num_strips, info->pclm_strip_data, - info->pclm_compression_method_preferred, info->width, - info->pclm_strip_height, info->color_space, info->bpc, - doc); - for (size_t i = 0; i < info->pclm_num_strips; i ++) - if (!strips[i].isInitialized()) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load strip data"); - return (1); - } - - // Add it - for (size_t i = 0; i < info->pclm_num_strips; i ++) - info->page.getKey("/Resources").getKey("/XObject") - .replaceKey("/Image" + - int_to_fwstring(i, num_digits(info->pclm_num_strips - 1)), - strips[i]); - } - - // Draw it - std::string content; - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - content.append(QUtil::double_to_string(info->page_width) + " 0 0 " + - QUtil::double_to_string(info->page_height) + " 0 0 cm\n"); - content.append("/I Do\n"); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - std::string res = info->pclm_source_resolution_default; - - // resolution is in dpi, so remove the last three characters from - // resolution string to get resolution integer - unsigned resolution_integer = std::stoi(res.substr(0, res.size() - 3)); - double d = (double)DEFAULT_PDF_UNIT / resolution_integer; - content.append(QUtil::double_to_string(d) + " 0 0 " + - QUtil::double_to_string(d) + " 0 0 cm\n"); - unsigned yAnchor = info->height; - for (unsigned i = 0; i < info->pclm_num_strips; i ++) - { - yAnchor -= info->pclm_strip_height[i]; - content.append("/P <> BDC q\n"); - content.append(QUtil::int_to_string(info->width) + " 0 0 " + - QUtil::int_to_string(info->pclm_strip_height[i]) + - " 0 " + QUtil::int_to_string(yAnchor) + " cm\n"); - content.append("/Image" + - int_to_fwstring(i, - num_digits(info->pclm_num_strips - 1)) + - " Do Q\n"); - } - } - - QPDFObjectHandle page_contents = info->page.getKey("/Contents"); - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - page_contents.replaceStreamData(content, QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - page_contents.getArrayItem(0).replaceStreamData(content, - QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); - - // bookkeeping - info->page_data = PointerHolder(); - info->pclm_strip_data.clear(); - - return (0); -} - - -// -// Perform modifications to PDF if color space conversions are needed -// - -static int -prepare_pdf_page(struct pdf_info *info, - unsigned width, - unsigned height, - unsigned bpl, - unsigned bpp, - unsigned bpc, - std::string render_intent, - cups_cspace_t color_space, - pwgtopdf_doc_t *doc) -{ - -#define IMAGE_CMYK_8 (bpp == 32 && bpc == 8) -#define IMAGE_CMYK_16 (bpp == 64 && bpc == 16) -#define IMAGE_RGB_8 (bpp == 24 && bpc == 8) -#define IMAGE_RGB_16 (bpp == 48 && bpc == 16) -#define IMAGE_WHITE_1 (bpp == 1 && bpc == 1) -#define IMAGE_WHITE_8 (bpp == 8 && bpc == 8) -#define IMAGE_WHITE_16 (bpp == 16 && bpc == 16) - - int error = 0; - pdf_convert_function fn = convert_pdf_no_conversion; - cmsColorSpaceSignature css; - - // Register available raster information into the PDF - info->width = width; - info->height = height; - info->line_bytes = bpl; - info->bpp = bpp; - info->bpc = bpc; - info->render_intent = render_intent; - info->color_space = color_space; - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - info->pclm_num_strips = - (height / info->pclm_strip_height_preferred) + - (height % info->pclm_strip_height_preferred ? 1 : 0); - info->pclm_strip_height.resize(info->pclm_num_strips); - info->pclm_strip_data.resize(info->pclm_num_strips); - for (size_t i = 0; i < info->pclm_num_strips; i ++) - { - info->pclm_strip_height[i] = - info->pclm_strip_height_preferred < height ? - info->pclm_strip_height_preferred : height; - height -= info->pclm_strip_height[i]; - } - } - - // Invert grayscale by default - if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_invert_colors; - - if (doc->colorProfile != NULL) - { - css = cmsGetColorSpace(doc->colorProfile); - - // Convert image and PDF color space to an embedded ICC Profile color - // space - switch (css) - { - // Convert PDF to Grayscale when using a gray profile - case cmsSigGrayData: - if (color_space == CUPS_CSPACE_CMYK) - fn = convert_pdf_cmyk_8_to_white_8; - else if (color_space == CUPS_CSPACE_RGB) - fn = convert_pdf_rgb_8_to_white_8; - else - fn = convert_pdf_invert_colors; - info->color_space = CUPS_CSPACE_K; - break; - // Convert PDF to RGB when using an RGB profile - case cmsSigRgbData: - if (color_space == CUPS_CSPACE_CMYK) - fn = convert_pdf_cmyk_8_to_rgb_8; - else if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_white_8_to_rgb_8; - info->color_space = CUPS_CSPACE_RGB; - break; - // Convert PDF to CMYK when using an RGB profile - case cmsSigCmykData: - if (color_space == CUPS_CSPACE_RGB) - fn = convert_pdf_rgb_8_to_cmyk_8; - else if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_white_8_to_cmyk_8; - info->color_space = CUPS_CSPACE_CMYK; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to convert PDF from profile."); - doc->colorProfile = NULL; - error = 1; - } - } - else if (!doc->cm_disabled) - { - // Perform conversion of an image color space - switch (color_space) - { - // Convert image to CMYK - case CUPS_CSPACE_CMYK: - if (IMAGE_RGB_8) - fn = convert_pdf_rgb_8_to_cmyk_8; - else if (IMAGE_RGB_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_WHITE_8) - fn = convert_pdf_white_8_to_cmyk_8; - else if (IMAGE_WHITE_16) - fn = convert_pdf_no_conversion; - break; - // Convert image to RGB - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - if (IMAGE_CMYK_8) - fn = convert_pdf_cmyk_8_to_rgb_8; - else if (IMAGE_CMYK_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_WHITE_8) - fn = convert_pdf_white_8_to_rgb_8; - else if (IMAGE_WHITE_16) - fn = convert_pdf_no_conversion; - break; - // Convert image to Grayscale - case CUPS_CSPACE_SW: - case CUPS_CSPACE_K: - if (IMAGE_CMYK_8) - fn = convert_pdf_cmyk_8_to_white_8; - else if (IMAGE_CMYK_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_RGB_8) - fn = convert_pdf_rgb_8_to_white_8; - else if (IMAGE_RGB_16) - fn = convert_pdf_no_conversion; - break; - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - // No conversion for right now - fn = convert_pdf_no_conversion; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - error = 1; - break; - } - } - - if (!error) - fn(info, doc); - - return (error); -} - - -static int -add_pdf_page(struct pdf_info *info, - int pagen, - unsigned width, - unsigned height, - int bpp, - int bpc, - int bpl, - std::string render_intent, - cups_cspace_t color_space, - unsigned xdpi, - unsigned ydpi, - pwgtopdf_doc_t *doc) -{ - try - { - if (finish_page(info, doc)) // any active - return (1); - - prepare_pdf_page(info, width, height, bpl, bpp, - bpc, render_intent, color_space, doc); - - if (info->height > (std::numeric_limits::max() / - info->line_bytes)) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Page too big"); - return (1); - } - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - info->page_data = - PointerHolder(new Buffer(info->line_bytes * info->height)); - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // reserve space for PCLm strips - for (size_t i = 0; i < info->pclm_num_strips; i ++) - info->pclm_strip_data[i] = - PointerHolder(new Buffer(info->line_bytes * - info->pclm_strip_height[i])); - } - - QPDFObjectHandle page = QPDFObjectHandle::parse( - "<<" - " /Type /Page" - " /Resources <<" - " /XObject << >> " - " >>" - " /MediaBox null " - " /Contents null " - ">>"); - - // Convert to pdf units - info->page_width = ((double)info->width / xdpi) * DEFAULT_PDF_UNIT; - info->page_height = ((double)info->height / ydpi) * DEFAULT_PDF_UNIT; - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - page.replaceKey("/Contents", QPDFObjectHandle::newStream(&info->pdf)); - // data will be provided later - page.replaceKey("/MediaBox", make_real_box(0, 0, info->page_width, - info->page_height)); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - page.replaceKey("/Contents", - QPDFObjectHandle::newArray(std::vector - (1, QPDFObjectHandle::newStream - (&info->pdf)))); - - // box with dimensions rounded off to the nearest integer - page.replaceKey("/MediaBox", - make_integer_box(0, 0, info->page_width + 0.5, - info->page_height + 0.5)); - } - - info->page = info->pdf.makeIndirectObject(page); // we want to keep a - // reference - info->pdf.addPage(info->page, false); - } - catch (std::bad_alloc &ex) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to allocate page data"); - return (1); - } - catch (...) - { - return (1); - } - - return (0); -} - - -static int -close_pdf_file(struct pdf_info * info, - pwgtopdf_doc_t *doc) -{ - try - { - if (finish_page(info, doc)) // any active - return (1); - QPDFWriter output(info->pdf, NULL); - output.setOutputFile("pdf", doc->outputfp, false); - //output.setMinimumPDFVersion("1.4"); - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - output.setPCLm(true); - output.write(); - } - catch (...) - { - return (1); - } - - return (0); -} - - -static void -pdf_set_line(struct pdf_info * info, - unsigned line_n, - unsigned char *line, - pwgtopdf_doc_t *doc) -{ - if (line_n > info->height) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Bad line %d", line_n); - return; - } - - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // copy line data into appropriate pclm strip - size_t strip_num = line_n / info->pclm_strip_height_preferred; - unsigned line_strip = line_n - - strip_num * info->pclm_strip_height_preferred; - memcpy(((info->pclm_strip_data[strip_num])->getBuffer() + - (line_strip*info->line_bytes)), line, info->line_bytes); - } - else - memcpy((info->page_data->getBuffer() + (line_n * info->line_bytes)), - line, info->line_bytes); -} - - -static int -convert_raster(cups_raster_t *ras, - unsigned width, - unsigned height, - int bpp, - int bpl, - struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - // We should be at raster start - int i; - unsigned cur_line = 0; - unsigned char *PixelBuffer, *ptr = NULL, *buff; - - PixelBuffer = (unsigned char *)malloc(bpl); - buff = (unsigned char *)malloc(info->line_bytes); - - do - { - // Read raster data... - cupsRasterReadPixels(ras, PixelBuffer, bpl); - -#if !ARCH_IS_BIG_ENDIAN - if (info->bpc == 16) - { - // Swap byte pairs for endianess (cupsRasterReadPixels() switches - // from Big Endian back to the system's Endian) - for (i = bpl, ptr = PixelBuffer; i > 0; i -= 2, ptr += 2) - { - unsigned char swap = *ptr; - *ptr = *(ptr + 1); - *(ptr + 1) = swap; - } - } -#endif // !ARCH_IS_BIG_ENDIAN - - // perform bit operations if necessary - doc->bit_function(PixelBuffer, ptr, bpl); - - // write lines and color convert when necessary - pdf_set_line(info, cur_line, doc->conversion_function(PixelBuffer, - buff, width), - doc); - ++cur_line; - } - while (cur_line < height); - - free(buff); - free(PixelBuffer); - - return (0); -} - - -static int -set_profile(const char *path, - pwgtopdf_doc_t *doc) -{ - if (path != NULL) - doc->colorProfile = cmsOpenProfileFromFile(path, "r"); - - if (doc->colorProfile != NULL) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Load profile successful."); - return (0); - } - else - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load profile."); - return (1); - } -} - - -int // O - Error status -cfFilterPWGToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters (outformat) -{ - int i; - char *t; - pwgtopdf_doc_t doc; // Document information - FILE *outputfp; // Output data stream - cf_filter_out_format_t outformat; // Output format - int Page, empty = 1; - cf_cm_calibration_t cm_calibrate; // Status of CUPS color management - // ("on" or "off") - struct pdf_info pdf; - cups_raster_t *ras; // Raster stream for printing - cups_page_header2_t header; // Page header from file - ipp_t *printer_attrs = data->printer_attrs; // Printer attributes from - // printer data - ipp_attribute_t *ipp_attr; // Printer attribute - const char *profile_name = NULL; // IPP Profile Name - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int total_attrs; - char buf[1024]; - const char *kw; - - - (void)inputseekable; - - if (parameters) - { - outformat = *(cf_filter_out_format_t *)parameters; - if (outformat != CF_FILTER_OUT_FORMAT_PCLM) - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - else - { - t = data->final_content_type; - if (t) - { - if (strcasestr(t, "pclm")) - outformat = CF_FILTER_OUT_FORMAT_PCLM; - else if (strcasestr(t, "pdf")) - outformat = CF_FILTER_OUT_FORMAT_PDF; - else - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - else - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: OUTFORMAT=\"%s\"", - outformat == CF_FILTER_OUT_FORMAT_PDF ? "PDF" : "PCLM"); - - // - // Open the output data stream specified by the outputfd... - // - - if ((outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to open output data stream."); - } - - return (1); - } - - doc.outputfp = outputfp; - // Logging function - doc.logfunc = log; - doc.logdata = ld; - // Job-is-canceled function - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - // support the CUPS "cm-calibration" option - cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (outformat == CF_FILTER_OUT_FORMAT_PCLM || - cm_calibrate == CF_CM_CALIBRATION_ENABLED) - doc.cm_disabled = 1; - else - doc.cm_disabled = cfCmIsPrinterCmDisabled(data); - - if (outformat == CF_FILTER_OUT_FORMAT_PCLM && printer_attrs == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: PCLm output: No printer IPP attributes are supplied, PCLm output not possible."); - return (1); - } - - // Transform - ras = cupsRasterOpen(inputfd, CUPS_RASTER_READ); - - // Process pages as needed... - Page = 0; - - // Get PCLm parameters from printer IPP attributes - if (outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, "PCLm-related printer IPP attributes:"); - total_attrs = 0; - ipp_attr = ippFirstAttribute(printer_attrs); - while (ipp_attr) - { - if (strncmp(ippGetName(ipp_attr), "pclm-", 5) == 0) - { - total_attrs ++; - ippAttributeString(ipp_attr, buf, sizeof(buf)); - log(ld, CF_LOGLEVEL_DEBUG, " Attr: %s",ippGetName(ipp_attr)); - log(ld, CF_LOGLEVEL_DEBUG, " Value: %s", buf); - for (i = 0; i < ippGetCount(ipp_attr); i ++) - if ((kw = ippGetString(ipp_attr, i, NULL)) != NULL) - log(ld, CF_LOGLEVEL_DEBUG, " Keyword: %s", kw); - } - ipp_attr = ippNextAttribute(printer_attrs); - } - log(ld, CF_LOGLEVEL_DEBUG, " %d attributes", total_attrs); - } - - char *attr_name = (char *)"pclm-strip-height-preferred"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value %d", - attr_name, ippGetInteger(ipp_attr, 0)); - pdf.pclm_strip_height_preferred = ippGetInteger(ipp_attr, 0); - } - else - pdf.pclm_strip_height_preferred = 16; // default strip height - - attr_name = (char *)"pclm-strip-height-supported"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\"", - attr_name); - pdf.pclm_strip_height_supported.clear(); // remove default value = 16 - for (int i = 0; i < ippGetCount(ipp_attr); i ++) - pdf.pclm_strip_height_supported.push_back(ippGetInteger(ipp_attr, i)); - } - - attr_name = (char *)"pclm-raster-back-side"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, ippGetString(ipp_attr, 0, NULL)); - pdf.pclm_raster_back_side = ippGetString(ipp_attr, 0, NULL); - } - - attr_name = (char *)"pclm-source-resolution-supported"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - pdf.pclm_source_resolution_supported = split_strings(buf, ","); - } - - attr_name = (char *)"pclm-source-resolution-default"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - pdf.pclm_source_resolution_default = buf; - } - else if (pdf.pclm_source_resolution_supported.size() > 0) - { - pdf.pclm_source_resolution_default = - pdf.pclm_source_resolution_supported[0]; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" missing, taking first item of \"pclm-source-resolution-supported\" as default resolution", - attr_name); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: PCLm output: Printer IPP attributes do not contain printer resolution information for PCLm."); - return (1); - } - - attr_name = (char *)"pclm-compression-method-preferred"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - std::vector vec = split_strings(buf, ","); - - // get all compression methods supported by the printer - for (std::vector::iterator it = vec.begin(); - it != vec.end(); ++it) - { - std::string compression_method = *it; - for (char& x: compression_method) - x = tolower(x); - if (compression_method == "flate") - pdf.pclm_compression_method_preferred.push_back(FLATE_DECODE); - else if (compression_method == "rle") - pdf.pclm_compression_method_preferred.push_back(RLE_DECODE); - else if (compression_method == "jpeg") - pdf.pclm_compression_method_preferred.push_back(DCT_DECODE); - } - } - // If the compression methods is none of the above or is erreneous - // use FLATE as compression method and show a warning. - if (pdf.pclm_compression_method_preferred.empty()) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "(pwgtopclm) Unable parse Printer attribute \"%s\". " - "Using FLATE for encoding image streams.", attr_name); - pdf.pclm_compression_method_preferred.push_back(FLATE_DECODE); - } - } - - while (cupsRasterReadHeader2(ras, &header)) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Job canceled"); - break; - } - - if (empty) - { - empty = 0; - // We have a valid input page, so create PDF file - if (create_pdf_file(&pdf, outformat) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Unable to create PDF file"); - return (1); - } - } - - // Write a status message with the page number - Page ++; - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPWGToPDF: Starting page %d.", Page); - - // Update rendering intent with user settings or the default - cfGetPrintRenderIntent(data, header.cupsRenderingIntent, - sizeof(header.cupsRenderingIntent)); - - // Use "profile=profile_name.icc" to embed 'profile_name.icc' into the PDF - // for testing. Forces color management to enable. - if (outformat == CF_FILTER_OUT_FORMAT_PDF && - (profile_name = cupsGetOption("profile", data->num_options, - data->options)) != NULL) - { - set_profile(profile_name, &doc); - doc.cm_disabled = 0; - } - if (doc.colorProfile != NULL) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: TEST ICC Profile specified (color " - "management forced ON): \n[%s]", profile_name); - - // Add a new page to PDF file - if (add_pdf_page(&pdf, Page, header.cupsWidth, header.cupsHeight, - header.cupsBitsPerPixel, header.cupsBitsPerColor, - header.cupsBytesPerLine, header.cupsRenderingIntent, - header.cupsColorSpace, header.HWResolution[0], - header.HWResolution[1], &doc) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Unable to start new PDF page"); - return (1); - } - - // Write the bit map into the PDF file - if (convert_raster(ras, header.cupsWidth, header.cupsHeight, - header.cupsBitsPerPixel, header.cupsBytesPerLine, - &pdf, &doc) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Failed to convert page bitmap"); - return (1); - } - } - - if (empty) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Input is empty, outputting empty file."); - cupsRasterClose(ras); - return (0); - } - - close_pdf_file(&pdf, &doc); // output to outputfp - - if (doc.colorProfile != NULL) - cmsCloseProfile(doc.colorProfile); - - cupsRasterClose(ras); - fclose(outputfp); - - return (Page == 0); -} diff --git a/cupsfilters/pwgtoraster.c b/cupsfilters/pwgtoraster.c deleted file mode 100644 index 1ea9fbd88..000000000 --- a/cupsfilters/pwgtoraster.c +++ /dev/null @@ -1,2657 +0,0 @@ -// -// PWG/Apple Raster to CUPS/PWG/Apple Raster filter function for libcupsfilters. -// -// Copyright (c) 2008-2011 BBR Inc. All rights reserved. -// Copyright (c) 2012-2022 by Till Kamppeter -// Copyright (c) 2019 by Tanmay Anand. -// Modified 2021 by Pratyush Ranjan. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "colormanager.h" -#include "image.h" -#include "bitmap.h" -#include "filter.h" -#include "ipp.h" -#include -#include - -#define USE_CMS - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef USE_LCMS1 -#include -#define cmsColorSpaceSignature icColorSpaceSignature -#define cmsSetLogErrorHandler cmsSetErrorHandler -#define cmsToneCurve LPGAMMATABLE -#define cmsSigXYZData icSigXYZData -#define cmsSigLuvData icSigLuvData -#define cmsSigLabData icSigLabData -#define cmsSigYCbCrData icSigYCbCrData -#define cmsSigYxyData icSigYxyData -#define cmsSigRgbData icSigRgbData -#define cmsSigHsvData icSigHsvData -#define cmsSigHlsData icSigHlsData -#define cmsSigCmyData icSigCmyData -#define cmsSig3colorData icSig3colorData -#define cmsSigGrayData icSigGrayData -#define cmsSigCmykData icSigCmykData -#define cmsSig4colorData icSig4colorData -#define cmsSig2colorData icSig2colorData -#define cmsSig5colorData icSig5colorData -#define cmsSig6colorData icSig6colorData -#define cmsSig7colorData icSig7colorData -#define cmsSig8colorData icSig8colorData -#define cmsSig9colorData icSig9colorData -#define cmsSig10colorData icSig10colorData -#define cmsSig11colorData icSig11colorData -#define cmsSig12colorData icSig12colorData -#define cmsSig13colorData icSig13colorData -#define cmsSig14colorData icSig14colorData -#define cmsSig15colorData icSig15colorData -#else -#include -#endif - -#define MAX_BYTES_PER_PIXEL 32 - -typedef struct cms_profile_s -{ - // for color profiles - cmsHPROFILE colorProfile; - cmsHPROFILE outputColorProfile; - cmsHTRANSFORM colorTransform; - cmsCIEXYZ D65WhitePoint; - int renderingIntent; - int cm_disabled; - cf_cm_calibration_t cm_calibrate; -} cms_profile_t; - -typedef struct pwgtoraster_doc_s -{ // **** Document information **** - cf_filter_data_t *data; - bool page_size_requested; - int bi_level; - bool allocLineBuf; - unsigned int bitspercolor; - unsigned int outputNumColors; - unsigned int bitmapoffset[2]; - cups_page_header2_t inheader; - cups_page_header2_t outheader; - cups_file_t *inputfp; // Temporary file, if any - FILE *outputfp; // Temporary file, if any - // margin swapping - bool swap_margin_x; - bool swap_margin_y; - unsigned int nplanes; - unsigned int nbands; - unsigned int bytesPerLine; // number of bytes per line - // Note: When CUPS_ORDER_BANDED, - // cupsBytesPerLine = bytesPerLine * cupsNumColors - cms_profile_t color_profile; -} pwgtoraster_doc_t; - -typedef unsigned char *(*convert_cspace_func)(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc); -typedef unsigned char *(*convert_line_func)(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace); - -typedef struct conversion_function_s -{ - convert_cspace_func convertCSpace; // Function for conversion of colorspaces - convert_line_func convertLineOdd; // Function to modify raster data of a line - convert_line_func convertLineEven; -} conversion_function_t; - - -static cmsCIExyY -adobergb_wp_cms() -{ - double *xyY = cfCmWhitePointAdobeRGB(); - cmsCIExyY wp; - - wp.x = xyY[0]; - wp.y = xyY[1]; - wp.Y = xyY[2]; - - return (wp); -} - - -static cmsCIExyY -sgray_wp_cms() -{ - double *xyY = cfCmWhitePointSGray(); - cmsCIExyY wp; - - wp.x = xyY[0]; - wp.y = xyY[1]; - wp.Y = xyY[2]; - - return (wp); -} - - -static cmsCIExyYTRIPLE -adobergb_matrix_cms() -{ - cmsCIExyYTRIPLE m; - - double *matrix = cfCmMatrixAdobeRGB(); - - m.Red.x = matrix[0]; - m.Red.y = matrix[1]; - m.Red.Y = matrix[2]; - m.Green.x = matrix[3]; - m.Green.y = matrix[4]; - m.Green.Y = matrix[5]; - m.Blue.x = matrix[6]; - m.Blue.y = matrix[7]; - m.Blue.Y = matrix[8]; - - return (m); -} - - -static cmsHPROFILE -adobergb_profile() -{ - cmsHPROFILE adobergb; - - cmsCIExyY wp; - cmsCIExyYTRIPLE primaries; - -#if USE_LCMS1 - cmsToneCurve Gamma = cmsBuildGamma(256, 2.2); - cmsToneCurve Gamma3[3]; -#else - cmsToneCurve *Gamma = cmsBuildGamma(NULL, 2.2); - cmsToneCurve *Gamma3[3]; -#endif - Gamma3[0] = Gamma3[1] = Gamma3[2] = Gamma; - - // Build AdobeRGB profile - primaries = adobergb_matrix_cms(); - wp = adobergb_wp_cms(); - adobergb = cmsCreateRGBProfile(&wp, &primaries, Gamma3); - - return (adobergb); -} - -static cmsHPROFILE -sgray_profile() -{ - cmsHPROFILE sgray; - - cmsCIExyY wp; - -#if USE_LCMS1 - cmsToneCurve Gamma = cmsBuildGamma(256, 2.2); -#else - cmsToneCurve *Gamma = cmsBuildGamma(NULL, 2.2); -#endif - // Build sGray profile - wp = sgray_wp_cms(); - sgray = cmsCreateGrayProfile(&wp, Gamma); - - return (sgray); -} - - -#ifdef USE_LCMS1 -static int -lcms_error_handler(int ErrorCode, - const char *ErrorText) -{ - return (1); -} -#else -static void -lcms_error_handler(cmsContext contextId, - cmsUInt32Number ErrorCode, - const char *ErrorText) -{ - return; -} -#endif - - -static int -parse_opts(cf_filter_out_format_t outformat, - pwgtoraster_doc_t *doc) -{ - int num_options = 0; - cups_option_t *options = NULL; - char *profile = NULL; - const char *val; - cf_filter_data_t *data = doc->data; - cf_logfunc_t log = data->logfunc; - void *ld = data ->logdata; - cups_cspace_t cspace = (cups_cspace_t)(-1); - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - // Did the user explicitly request a certain page size? If not, overtake - // the page size(s) from the input pages - doc->page_size_requested = - (cupsGetOption("PageSize", num_options, options) || - cupsGetOption("media", num_options, options) || - cupsGetOption("media-size", num_options, options) || - cupsGetOption("media-col", num_options, options)); - - // We can directly create CUPS Raster, PWG Raster, and Apple Raster - cfRasterPrepareHeader(&(doc->outheader), data, outformat, - outformat, 0, &cspace); - - if (doc->outheader.Duplex) - { - int backside; - - // analyze options relevant to Duplex - // APDuplexRequiresFlippedMargin - enum - { - FM_NO, - FM_FALSE, - FM_TRUE - } flippedMargin; - - backside = cfGetBackSideOrientation(data); - - if (backside >= 0) - { - flippedMargin = (backside & 16 ? FM_TRUE : - (backside & 8 ? FM_FALSE : - FM_NO)); - backside &= 7; - - if (backside == CF_BACKSIDE_MANUAL_TUMBLE && doc->outheader.Tumble) - { - doc->swap_margin_x = doc->swap_margin_y = true; - if (flippedMargin == FM_TRUE) - doc->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_ROTATED && !doc->outheader.Tumble) - { - doc->swap_margin_x = doc->swap_margin_y = true; - if (flippedMargin == FM_TRUE) - doc->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_FLIPPED) - { - if (doc->outheader.Tumble) - doc->swap_margin_x = doc->swap_margin_y = true; - if (flippedMargin == FM_FALSE) - doc->swap_margin_y = !doc->swap_margin_y; - } - } - } - - // support the CUPS "cm-calibration" option - doc->color_profile.cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (doc->color_profile.cm_calibrate == CF_CM_CALIBRATION_ENABLED) - doc->color_profile.cm_disabled = 1; - else - doc->color_profile.cm_disabled = cfCmIsPrinterCmDisabled(data); - - if (!doc->color_profile.cm_disabled) - cfCmGetPrinterIccProfile - (data, - cfRasterColorSpaceString(doc->outheader.cupsColorSpace), - doc->outheader.MediaType, - doc->outheader.HWResolution[0], doc->outheader.HWResolution[1], - &profile); - - if (profile != NULL) - { - doc->color_profile.colorProfile = cmsOpenProfileFromFile(profile, "r"); - free(profile); - } - - doc->outheader.cupsRenderingIntent[0] = '\0'; - cfGetPrintRenderIntent(data, doc->outheader.cupsRenderingIntent, - sizeof(doc->outheader.cupsRenderingIntent)); - if (strcasecmp(doc->outheader.cupsRenderingIntent, "Perceptual") == 0) - doc->color_profile.renderingIntent = INTENT_PERCEPTUAL; - else if (strcasecmp(doc->outheader.cupsRenderingIntent, "Relative") == 0) - doc->color_profile.renderingIntent = INTENT_RELATIVE_COLORIMETRIC; - else if (strcasecmp(doc->outheader.cupsRenderingIntent, "Saturation") == 0) - doc->color_profile.renderingIntent = INTENT_SATURATION; - else if (strcasecmp(doc->outheader.cupsRenderingIntent, "Absolute") == 0) - doc->color_profile.renderingIntent = INTENT_ABSOLUTE_COLORIMETRIC; - - if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL && - !strncasecmp(val, "bi-level", 8)) - doc->bi_level = 1; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Page size %s: %s", - doc->page_size_requested ? "requested" : "default", - doc->outheader.cupsPageSizeName); - - if (num_options) - cupsFreeOptions(num_options, options); - - return (0); -} - - -static unsigned char * -reverse_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *p = src; - - for (unsigned int j = 0; j < size; j ++, p ++) - *p = ~*p; - - return (src); -} - - -static unsigned char * -reverse_line_swap_byte(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 1; - unsigned char *dp = dst; - - for (unsigned int j = 0; j < size; j ++, bp --, dp ++) - *dp = ~*bp; - - return (dst); -} - - -static unsigned char * -reverse_line_swap_bit(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - dst = cfReverseOneBitLineSwap(src, dst, pixels, size); - return (dst); -} - - -static unsigned char * -rgb_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_cmyk_line_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + (pixels - 1) * 3; - unsigned char *dp = dst; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 4) - cfImageRGBToCMYK(bp, dp, 1); - - return (dst); -} - - -static unsigned char * -rgb_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_cmy_line_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 3; - unsigned char *dp = dst; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 3) - cfImageRGBToCMY(bp, dp, 1); - - return (dst); -} - - -static unsigned char * -rgb_to_kcmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src; - unsigned char *dp = dst; - unsigned char d; - - cfImageRGBToCMYK(src, dst, pixels); - // CMYK to KCMY - for (unsigned int i = 0; i < pixels; i ++, bp += 3, dp += 4) - { - d = dp[3]; - dp[3] = dp[2]; - dp[2] = dp[1]; - dp[1] = dp[0]; - dp[0] = d; - } - return (dst); -} - - -static unsigned char *rgb_to_kcmy_line_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + (pixels - 1) * 3; - unsigned char *dp = dst; - unsigned char d; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 4) - { - cfImageRGBToCMYK(bp, dp, 1); - // CMYK to KCMY - d = dp[3]; - dp[3] = dp[2]; - dp[2] = dp[1]; - dp[1] = dp[0]; - dp[0] = d; - } - return (dst); -} - - -static unsigned char *line_no_op(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - // do nothing - return (src); -} - - -static unsigned char *line_swap_24(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 3; - unsigned char *dp = dst; - - for (unsigned int i = 0; i < pixels; i++, bp -= 3, dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - - return (dst); -} - - -static unsigned char * -line_swap_byte(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - unsigned char *bp = src + size - 1; - unsigned char *dp = dst; - - for (unsigned int j = 0; j < size; j++, bp --, dp ++) - *dp = *bp; - - return (dst); -} - - -static unsigned char * -line_swap_bit(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - dst = cfReverseOneBitLine(src, dst, pixels, size); - return (dst); -} - - -typedef struct func_table_s -{ - enum cups_cspace_e cspace; - unsigned int bitsPerPixel; - unsigned int bitsPerColor; - convert_line_func convertLine; - bool allocLineBuf; - convert_line_func convertLineSwap; - bool allocLineBufSwap; -} func_table_t; - - -static func_table_t specialCaseFuncs[] = -{ - {CUPS_CSPACE_K, 8, 8, reverse_line, false, reverse_line_swap_byte, true}, - {CUPS_CSPACE_K, 1, 1, reverse_line, false, reverse_line_swap_bit, true}, - {CUPS_CSPACE_GOLD, 8, 8, reverse_line, false, reverse_line_swap_byte, true}, - {CUPS_CSPACE_GOLD, 1, 1, reverse_line, false, reverse_line_swap_bit, true}, - {CUPS_CSPACE_SILVER, 8, 8, reverse_line, false, reverse_line_swap_byte, true}, - {CUPS_CSPACE_SILVER, 1, 1, reverse_line, false, reverse_line_swap_bit, true}, - {CUPS_CSPACE_CMYK, 32, 8, rgb_to_cmyk_line, true, rgb_to_cmyk_line_swap,true}, - {CUPS_CSPACE_KCMY, 32,8, rgb_to_kcmy_line, true, rgb_to_kcmy_line_swap, true}, - {CUPS_CSPACE_CMY, 24, 8, rgb_to_cmy_line, true, rgb_to_cmy_line_swap, true}, - {CUPS_CSPACE_RGB, 24, 8, line_no_op, false, line_swap_24, true}, - {CUPS_CSPACE_SRGB, 24, 8, line_no_op, false, line_swap_24, true}, - {CUPS_CSPACE_ADOBERGB, 24, 8, line_no_op, false, line_swap_24, true}, - {CUPS_CSPACE_W, 8, 8, line_no_op, false, line_swap_byte, true}, - {CUPS_CSPACE_W, 1, 1, line_no_op, false, line_swap_bit, true}, - {CUPS_CSPACE_SW, 8, 8, line_no_op, false, line_swap_byte, true}, - {CUPS_CSPACE_SW, 1, 1, line_no_op, false, line_swap_bit, true}, - {CUPS_CSPACE_WHITE, 8, 8, line_no_op, false, line_swap_byte, true}, - {CUPS_CSPACE_WHITE, 1, 1, line_no_op, false, line_swap_bit, true}, - {CUPS_CSPACE_RGB, 0, 0, NULL, false, NULL, false} // end mark -}; - - -static unsigned char * -convert_cspace_none(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t *doc) -{ - return (src); -} - - -static unsigned char * -convert_cspace_with_profiles(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t *doc) -{ - cmsDoTransform(doc->color_profile.colorTransform, src, pixelBuf, 1); - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_xyz_8(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t *doc) -{ - double alab[3]; - - cmsDoTransform(doc->color_profile.colorTransform, src, alab, 1); - - cmsCIELab lab; - cmsCIEXYZ xyz; - - lab.L = alab[0]; - lab.a = alab[1]; - lab.b = alab[2]; - - cmsLab2XYZ(&(doc->color_profile.D65WhitePoint), &xyz, &lab); - pixelBuf[0] = 231.8181 * xyz.X + 0.5; - pixelBuf[1] = 231.8181 * xyz.Y + 0.5; - pixelBuf[2] = 231.8181 * xyz.Z + 0.5; - - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_xyz_16(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t *doc) -{ - double alab[3]; - unsigned short *sd = (unsigned short *)pixelBuf; - - cmsDoTransform(doc->color_profile.colorTransform, src, alab, 1); - - cmsCIELab lab; - cmsCIEXYZ xyz; - - lab.L = alab[0]; - lab.a = alab[1]; - lab.b = alab[2]; - - cmsLab2XYZ(&(doc->color_profile.D65WhitePoint),&xyz,&lab); - sd[0] = 59577.2727 * xyz.X + 0.5; - sd[1] = 59577.2727 * xyz.Y + 0.5; - sd[2] = 59577.2727 * xyz.Z + 0.5; - - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_lab_8(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t *doc) -{ - double lab[3]; - - cmsDoTransform(doc->color_profile.colorTransform, src, lab, 1); - pixelBuf[0] = 2.55 * lab[0] + 0.5; - pixelBuf[1] = lab[1] + 128.5; - pixelBuf[2] = lab[2] + 128.5; - - return (pixelBuf); -} - - -static unsigned char * -convert_cspace_lab_16(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t *doc) -{ - double lab[3]; - - cmsDoTransform(doc->color_profile.colorTransform, src, lab, 1); - - unsigned short *sd = (unsigned short *)pixelBuf; - sd[0] = 655.35 * lab[0] + 0.5; - sd[1] = 256 * (lab[1] + 128) + 0.5; - sd[2] = 256 * (lab[2] + 128) + 0.5; - - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_rgba(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - unsigned char *dp = pixelBuf; - - for (int i = 0; i < 3; i ++) - *dp++ = *src++; - - *dp = 255; - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_rgbw(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - unsigned char cmyk[4]; - unsigned char *dp = pixelBuf; - - cfImageRGBToCMYK(src, cmyk, 1); - for (int i = 0; i < 4; i++) - *dp++ = ~cmyk[i]; - - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_cmyk(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - cfImageRGBToCMYK(src, pixelBuf, 1); - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_cmy(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - cfImageRGBToCMY(src, pixelBuf, 1); - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_ymc(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - cfImageRGBToCMY(src, pixelBuf, 1); - // swap C and Y - unsigned char d = pixelBuf[0]; - pixelBuf[0] = pixelBuf[2]; - pixelBuf[2] = d; - - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_kcmy(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - cfImageRGBToCMYK(src, pixelBuf, 1); - unsigned char d = pixelBuf[3]; - pixelBuf[3] = pixelBuf[2]; - pixelBuf[2] = pixelBuf[1]; - pixelBuf[1] = pixelBuf[0]; - pixelBuf[0] = d; - - return (pixelBuf); -} - - -static unsigned char * -rgb_8_to_kcmycm_temp(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - return (cfRGB8toKCMYcm(src, pixelBuf, x, y)); -} - - -static unsigned char * -rgb_8_to_ymck(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t* doc) -{ - cfImageRGBToCMYK(src, pixelBuf, 1); - // swap C and Y - unsigned char d = pixelBuf[0]; - pixelBuf[0] = pixelBuf[2]; - pixelBuf[2] = d; - - return (pixelBuf); -} - - -static unsigned char * -w_8_to_k_8(unsigned char *src, - unsigned char *pixelBuf, - unsigned int x, - unsigned int y, - pwgtoraster_doc_t *doc) -{ - *pixelBuf = ~(*src); - return (pixelBuf); -} - - -static unsigned char * -convert_line_chunked(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - // Assumed that BitsPerColor is 8 - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + i * (doc->outputNumColors), pixelBuf1, i, row, - doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->outheader.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, 0, i, pb, doc->outheader.cupsNumColors, - doc->outheader.cupsBitsPerColor, - doc->outheader.cupsColorOrder); - } - - return (dst); -} - - -static unsigned char * -convert_line_chunked_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t* doc, - convert_cspace_func convertCSpace) -{ - // Assumed that BitsPerColor is 8 - for (unsigned int i = 0; i < pixels; i++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + (pixels - i - 1) * doc->outputNumColors, - pixelBuf1, i, row, doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->outheader.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, 0, i, pb, doc->outheader.cupsNumColors, - doc->outheader.cupsBitsPerColor, - doc->outheader.cupsColorOrder); - } - - return (dst); -} - - -static unsigned char * -convert_line_plane(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - // Assumed that BitsPerColor is 8 - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + i * doc->outputNumColors, pixelBuf1, i, row, doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->outheader.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, plane, i, pb, doc->outheader.cupsNumColors, - doc->outheader.cupsBitsPerColor, - doc->outheader.cupsColorOrder); - } - - return (dst); -} - - -static unsigned char * -convert_line_plane_swap(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int plane, - unsigned int pixels, - unsigned int size, - pwgtoraster_doc_t *doc, - convert_cspace_func convertCSpace) -{ - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - - pb = convertCSpace(src + (pixels - i - 1) * doc->outputNumColors, - pixelBuf1, i, row, doc); - pb = cfConvertBits(pb, pixelBuf2, i, row, doc->outheader.cupsNumColors, - doc->bitspercolor); - cfWritePixel(dst, plane, i, pb, doc->outheader.cupsNumColors, - doc->outheader.cupsBitsPerColor, - doc->outheader.cupsColorOrder); - } - - return (dst); -} - - -// Handle special cases which appear in the Gutenprint driver -static bool -select_special_case(pwgtoraster_doc_t* doc, - conversion_function_t* convert) -{ - int i; - - for (i = 0; specialCaseFuncs[i].bitsPerPixel > 0; i ++) - { - if (doc->outheader.cupsColorSpace == specialCaseFuncs[i].cspace && - doc->outheader.cupsBitsPerPixel == specialCaseFuncs[i].bitsPerPixel && - doc->outheader.cupsBitsPerColor == specialCaseFuncs[i].bitsPerColor) - { - convert->convertLineOdd = specialCaseFuncs[i].convertLine; - convert->convertLineEven = specialCaseFuncs[i].convertLine; - doc->allocLineBuf = specialCaseFuncs[i].allocLineBuf; - return (true); // found - } - } - - return (false); -} - - -static unsigned int -get_cms_color_space_type(cmsColorSpaceSignature cs) -{ - switch (cs) - { - case cmsSigXYZData: - return (PT_XYZ); - break; - case cmsSigLabData: - return (PT_Lab); - break; - case cmsSigLuvData: - return (PT_YUV); - break; - case cmsSigYCbCrData: - return (PT_YCbCr); - break; - case cmsSigYxyData: - return (PT_Yxy); - break; - case cmsSigRgbData: - return (PT_RGB); - break; - case cmsSigGrayData: - return (PT_GRAY); - break; - case cmsSigHsvData: - return (PT_HSV); - break; - case cmsSigHlsData: - return (PT_HLS); - break; - case cmsSigCmykData: - return (PT_CMYK); - break; - case cmsSigCmyData: - return (PT_CMY); - break; - case cmsSig2colorData: - case cmsSig3colorData: - case cmsSig4colorData: - case cmsSig5colorData: - case cmsSig6colorData: - case cmsSig7colorData: - case cmsSig8colorData: - case cmsSig9colorData: - case cmsSig10colorData: - case cmsSig11colorData: - case cmsSig12colorData: - case cmsSig13colorData: - case cmsSig14colorData: - case cmsSig15colorData: - default: - break; - } - - return (PT_RGB); -} - - -// select convertLine function -static int select_convert_func(cups_raster_t *raster, - pwgtoraster_doc_t* doc, - conversion_function_t *convert) -{ - cf_logfunc_t log = doc->data->logfunc; - void *ld = doc->data->logdata; - - doc->bitspercolor = doc->outheader.cupsBitsPerColor; - - if ((doc->color_profile.colorProfile == NULL || - doc->color_profile.outputColorProfile == - doc->color_profile.colorProfile) && - (doc->outheader.cupsColorOrder == CUPS_ORDER_CHUNKED || - doc->outheader.cupsNumColors == 1)) - { - if (select_special_case(doc, convert)) - return (0); - } - - switch (doc->outheader.cupsColorOrder) - { - case CUPS_ORDER_BANDED: - case CUPS_ORDER_PLANAR: - if (doc->outheader.cupsNumColors > 1) - { - convert->convertLineEven = convert_line_plane_swap; - convert->convertLineOdd = convert_line_plane; - break; - } - default: - case CUPS_ORDER_CHUNKED: - convert->convertLineEven = convert_line_chunked_swap; - convert->convertLineOdd = convert_line_chunked; - break; - } - convert->convertLineEven = convert->convertLineOdd; - doc->allocLineBuf = true; - - if (doc->color_profile.colorProfile != NULL && - doc->color_profile.outputColorProfile != - doc->color_profile.colorProfile) - { - unsigned int bytes; - - switch (doc->outheader.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - if (doc->outheader.cupsBitsPerColor == 8) - convert->convertCSpace = convert_cspace_lab_8; - else - // 16 bits - convert->convertCSpace = convert_cspace_lab_16; - bytes = 0; // double - break; - case CUPS_CSPACE_CIEXYZ: - if (doc->outheader.cupsBitsPerColor == 8) - convert->convertCSpace = convert_cspace_xyz_8; - else - // 16 bits - convert->convertCSpace = convert_cspace_xyz_16; - bytes = 0; // double - break; - default: - convert->convertCSpace = convert_cspace_with_profiles; - bytes = doc->outheader.cupsBitsPerColor / 8; - break; - } - doc->bitspercolor = 0; // convert bits in convertCSpace - if (doc->color_profile.outputColorProfile == NULL) - doc->color_profile.outputColorProfile = cmsCreate_sRGBProfile(); - unsigned int dcst = - get_cms_color_space_type(cmsGetColorSpace(doc->color_profile.colorProfile)); - if ((doc->color_profile.colorTransform = - cmsCreateTransform(doc->color_profile.outputColorProfile, - COLORSPACE_SH(PT_RGB) | CHANNELS_SH(3) | - BYTES_SH(1), - doc->color_profile.colorProfile, - COLORSPACE_SH(dcst) | - CHANNELS_SH(doc->outheader.cupsNumColors) | - BYTES_SH(bytes), - doc->color_profile.renderingIntent,0)) == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Can't create color transform."); - return (1); - } - } - else - { - // select convertCSpace function - switch (doc->outheader.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - case CUPS_CSPACE_CIEXYZ: - convert->convertCSpace = convert_cspace_none; - break; - case CUPS_CSPACE_CMY: - convert->convertCSpace = rgb_8_to_cmy; - break; - case CUPS_CSPACE_YMC: - convert->convertCSpace = rgb_8_to_ymc; - break; - case CUPS_CSPACE_CMYK: - convert->convertCSpace = rgb_8_to_cmyk; - break; - case CUPS_CSPACE_KCMY: - convert->convertCSpace = rgb_8_to_kcmy; - break; - case CUPS_CSPACE_KCMYcm: - if (doc->outheader.cupsBitsPerColor > 1) - convert->convertCSpace = rgb_8_to_kcmy; - else - convert->convertCSpace = rgb_8_to_kcmycm_temp; - break; - case CUPS_CSPACE_GMCS: - case CUPS_CSPACE_GMCK: - case CUPS_CSPACE_YMCK: - convert->convertCSpace = rgb_8_to_ymck; - break; - case CUPS_CSPACE_RGBW: - convert->convertCSpace = rgb_8_to_rgbw; - break; - case CUPS_CSPACE_RGBA: - convert->convertCSpace = rgb_8_to_rgba; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - convert->convertCSpace = convert_cspace_none; - break; - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - case CUPS_CSPACE_WHITE: - convert->convertCSpace = convert_cspace_none; - break; - case CUPS_CSPACE_K: - case CUPS_CSPACE_GOLD: - case CUPS_CSPACE_SILVER: - convert->convertCSpace = w_8_to_k_8; - break; - default: - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Specified ColorSpace is not supported"); - return (1); - break; - } - } - - if (doc->outheader.cupsBitsPerColor == 1 && - (doc->outheader.cupsNumColors == 1 || - doc->outheader.cupsColorSpace == CUPS_CSPACE_KCMYcm )) - doc->bitspercolor = 0; // Do not convert the bits - - return (0); -} - - -static bool -out_page(pwgtoraster_doc_t *doc, - int pageNo, - cups_raster_t *inras, - cups_raster_t *outras, - conversion_function_t *convert) -{ - int i, j; - cf_filter_data_t *data = doc->data; - float paperdimensions[2], // Physical size of the paper - margins[4]; // Physical margins of print - float swap; - int imageable_area_fit = 0; - int overspray_duplicate_after_pixels = INT_MAX; - int next_overspray_duplicate = 0; - int next_line_read = 0; - unsigned int y = 0, yin = 0; - unsigned char *bp = NULL; - convert_line_func convertLine; - unsigned char *lineBuf = NULL; - unsigned char *dp; - unsigned int inlineoffset, // Offset where to start in input line (bytes) - inlinesize; // How many bytes to take from input line - int input_color_mode; - int color_mode_needed; - bool ret = true; - unsigned char *line = NULL; - unsigned char *lineavg = NULL; - unsigned char *pagebuf = NULL; - unsigned int res_down_factor[2]; - unsigned int res_up_factor[2]; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - - if (iscanceled && iscanceled(icd)) - { - // Canceled - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Job canceled on input page %d", pageNo); - return (false); - } - - if (!cupsRasterReadHeader2(inras, &(doc->inheader))) - { - // Done - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Job completed"); - return (false); - } - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Input page %d", pageNo); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: HWResolution = [ %d %d ]", - doc->inheader.HWResolution[0], doc->inheader.HWResolution[1]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: PageSize = [ %d %d ]", - doc->inheader.PageSize[0], doc->inheader.PageSize[1]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsWidth = %d", - doc->inheader.cupsWidth); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsHeight = %d", - doc->inheader.cupsHeight); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBitsPerColor = %d", - doc->inheader.cupsBitsPerColor); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBitsPerPixel = %d", - doc->inheader.cupsBitsPerPixel); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBytesPerLine = %d", - doc->inheader.cupsBytesPerLine); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsColorOrder = %d", - doc->inheader.cupsColorOrder); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsColorSpace = %d", - doc->inheader.cupsColorSpace); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsCompression = %d", - doc->inheader.cupsCompression); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsPageSizeName = %s", - doc->inheader.cupsPageSizeName); - } - - if (!doc->page_size_requested) - { - doc->outheader.PageSize[0] = doc->inheader.PageSize[0]; - doc->outheader.PageSize[1] = doc->inheader.PageSize[1]; - } - - memset(paperdimensions, 0, sizeof(paperdimensions)); - memset(margins, 0, sizeof(margins)); - if (data->printer_attrs) - { - // Find dimensions/margins of requested page size - if (cfGetPageDimensions(data->printer_attrs, data->job_attrs, - data->num_options, data->options, - &(doc->outheader), 0, - &(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), NULL, NULL) == 0) - { - // Find dimensions/margins for input page size - cfGetPageDimensions(data->printer_attrs, NULL, 0, NULL, - &(doc->inheader), 0, - &(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), NULL, NULL); - } - if (doc->outheader.ImagingBoundingBox[3] == 0) - for (i = 0; i < 4; i ++) - margins[i] = 0.0; - } - else - { - for (i = 0; i < 2; i ++) - paperdimensions[i] = doc->outheader.PageSize[i]; - if (doc->outheader.cupsImagingBBox[3] > 0.0) - { - // Set margins if we have a bounding box defined ... - margins[0] = doc->outheader.cupsImagingBBox[0]; - margins[1] = doc->outheader.cupsImagingBBox[1]; - margins[2] = paperdimensions[0] - doc->outheader.cupsImagingBBox[2]; - margins[3] = paperdimensions[1] - doc->outheader.cupsImagingBBox[3]; - } - else - // ... otherwise use zero margins - for (i = 0; i < 4; i ++) - margins[i] = 0.0; - } - - if (doc->outheader.Duplex && (pageNo & 1) == 0) - { - // backside: change margin if needed - if (doc->swap_margin_x) - { - swap = margins[2]; margins[2] = margins[0]; margins[0] = swap; - } - if (doc->swap_margin_y) - { - swap = margins[3]; margins[3] = margins[1]; margins[1] = swap; - } - } - - if (imageable_area_fit == 0) - { - doc->bitmapoffset[0] = margins[0] / 72.0 * doc->outheader.HWResolution[0]; - doc->bitmapoffset[1] = margins[3] / 72.0 * doc->outheader.HWResolution[1]; - } - else - { - doc->bitmapoffset[0] = 0; - doc->bitmapoffset[1] = 0; - } - - // Write page header - doc->outheader.cupsWidth = ((paperdimensions[0] - margins[0] - margins[2]) / - 72.0 * doc->outheader.HWResolution[0]) + 0.5; - doc->outheader.cupsHeight = ((paperdimensions[1] - margins[1] - margins[3]) / - 72.0 * doc->outheader.HWResolution[1]) + 0.5; - - for (i = 0; i < 2; i ++) - { - doc->outheader.cupsPageSize[i] = paperdimensions[i]; - doc->outheader.PageSize[i] = - (unsigned int)(doc->outheader.cupsPageSize[i] + 0.5); - doc->outheader.Margins[i] = margins[i] + 0.5; - } - - if (doc->outheader.ImagingBoundingBox[3] != 0) - { - doc->outheader.cupsImagingBBox[0] = margins[0]; - doc->outheader.cupsImagingBBox[1] = margins[1]; - doc->outheader.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; - doc->outheader.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; - - for (i = 0; i < 4; i ++) - doc->outheader.ImagingBoundingBox[i] = - (unsigned int)(doc->outheader.cupsImagingBBox[i] + 0.5); - } - - doc->bytesPerLine = doc->outheader.cupsBytesPerLine = - (doc->outheader.cupsBitsPerPixel * - doc->outheader.cupsWidth + 7) / 8; - if (doc->outheader.cupsColorOrder == CUPS_ORDER_BANDED) - doc->outheader.cupsBytesPerLine *= doc->outheader.cupsNumColors; - - if (!cupsRasterWriteHeader2(outras, &(doc->outheader))) - { - if (log) log(ld,CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Can't write page %d header", pageNo); - return (false); - } - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Output page %d", pageNo); - if (doc->outheader.ImagingBoundingBox[3] > 0) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Duplex = %d", doc->outheader.Duplex); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: HWResolution = [ %d %d ]", - doc->outheader.HWResolution[0], doc->outheader.HWResolution[1]); - if (doc->outheader.ImagingBoundingBox[3] > 0) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: ImagingBoundingBox = [ %d %d %d %d ]", - doc->outheader.ImagingBoundingBox[0], - doc->outheader.ImagingBoundingBox[1], - doc->outheader.ImagingBoundingBox[2], - doc->outheader.ImagingBoundingBox[3]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Margins = [ %d %d ]", - doc->outheader.Margins[0], doc->outheader.Margins[1]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: ManualFeed = %d", doc->outheader.ManualFeed); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: MediaPosition = %d", - doc->outheader.MediaPosition); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: NumCopies = %d", - doc->outheader.NumCopies); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Orientation = %d", - doc->outheader.Orientation); - } - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: PageSize = [ %d %d ]", - doc->outheader.PageSize[0], doc->outheader.PageSize[1]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsWidth = %d", doc->outheader.cupsWidth); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsHeight = %d", doc->outheader.cupsHeight); - if (doc->outheader.ImagingBoundingBox[3] > 0) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsMediaType = %d", - doc->outheader.cupsMediaType); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBitsPerColor = %d", - doc->outheader.cupsBitsPerColor); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBitsPerPixel = %d", - doc->outheader.cupsBitsPerPixel); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBytesPerLine = %d", - doc->outheader.cupsBytesPerLine); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsColorOrder = %d", - doc->outheader.cupsColorOrder); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsColorSpace = %d", - doc->outheader.cupsColorSpace); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsCompression = %d", - doc->outheader.cupsCompression); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsPageSizeName = %s", - doc->outheader.cupsPageSizeName); - } - - // - // write page image - // - - if (iscanceled && iscanceled(icd)) - // Canceled - return (false); - - // - // Pre-conversion of resolution and color space - // - // This is needed if we work with a driver (especially Gutenprint) - // handling very high resolutions due to the high resolution (high - // ink nozzle density of printer) used for dithering but do not want - // to overload clients be requiring them sending high-resolution job - // data. So we can tell the client to send a lower resolution while - // we let the driver use a higher resolution. Also color spaces - // clients can produce are usually more limited than what drivers - // work with. - // - // We also need this feature for retro-fitting CUPS Raster drivers - // which have the driver's/the printers's capabilities defined in - // PPD files (handled by ppdFilterCUPSWrapper() in libppd): - // - // The resolution and color space/depth/order for the CUPS Raster - // data to be fed into the filter is not simply defined by options - // and choices with intuitive names, but by the PostScript code - // attached to the choices of the options in the PPD which are - // selected for the job, like "<> - // setpagedevice" or "<> setpagedevice". The CUPS filters collect all - // of the selected ones and interpret them with a mini PostScript - // interpreter (ppdRasterInterpretPPD() in libppd) to generate the - // CUPS Raster header (data structure describing the raster format) - // for the page. - // - // Unfortunately, resolutions are not always defined in the - // “Resolution” option (for example in “Print Quality” instead) or - // the resolution values do not correspond with the human-readable - // choice names, same for color spaces not always defined in - // “ColorModel”. As with this the information the Printer - // Application has from the PPD often does not reflect the driver’s - // actual requirements, this filter function pre-converts - // resolutions and color spaces as needed. - // - - // Check for needed resolution pre-conversions - for (i = 0; i < 2; i ++) - { - res_down_factor[i] = 1; - res_up_factor[i] = 1; - - if (doc->outheader.HWResolution[i] == doc->inheader.HWResolution[i]) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: %s resolution: %d dpi", - i == 0 ? "Horizontal" : "Vertical", doc->inheader.HWResolution[i]); - continue; - } - - if (doc->outheader.HWResolution[i] > doc->inheader.HWResolution[i]) - { - if (doc->outheader.HWResolution[i] % doc->inheader.HWResolution[i]) - { - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: %s output resolution %d dpi is not an integer multiple of %s input resolution %d dpi", - i == 0 ? "Horizontal" : "Vertical", doc->outheader.HWResolution[i], - i == 0 ? "horizontal" : "vertical", doc->inheader.HWResolution[i]); - return (false); - } - else - { - res_up_factor[i] = - doc->outheader.HWResolution[i] / doc->inheader.HWResolution[i]; - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: %s input resolution: %d dpi; %s output resolution: %d dpi -> Raising by factor %d", - i == 0 ? "Horizontal" : "Vertical", doc->inheader.HWResolution[i], - i == 0 ? "Horizontal" : "Vertical", doc->outheader.HWResolution[i], - res_up_factor[i]); - } - } - else if (doc->outheader.HWResolution[i] < doc->inheader.HWResolution[i]) - { - if (doc->inheader.HWResolution[i] % doc->outheader.HWResolution[i]) - { - log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: %s input resolution %d dpi is not an integer multiple of %s output resolution %d dpi", - i == 0 ? "Horizontal" : "Vertical", doc->inheader.HWResolution[i], - i == 0 ? "horizontal" : "vertical", doc->outheader.HWResolution[i]); - return (false); - } - else - { - res_down_factor[i] = - doc->inheader.HWResolution[i] / doc->outheader.HWResolution[i]; - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: %s input resolution: %d dpi; %s output resolution: %d dpi -> Reducing by factor %d", - i == 0 ? "Horizontal" : "Vertical", doc->inheader.HWResolution[i], - i == 0 ? "Horizontal" : "Vertical", doc->outheader.HWResolution[i], - res_down_factor[i]); - } - } - } - - // Determine the input color space we have - if (doc->inheader.cupsNumColors == 3) - { - input_color_mode = 2; - inlineoffset = doc->bitmapoffset[0] * 3; - inlinesize = doc->outheader.cupsWidth * 3; - } - else if (doc->inheader.cupsNumColors == 1 && - doc->inheader.cupsBitsPerColor == 8) - { - input_color_mode = 1; - inlineoffset = doc->bitmapoffset[0]; - inlinesize = doc->outheader.cupsWidth; - } - else if (doc->inheader.cupsNumColors == 1 && - doc->inheader.cupsBitsPerColor == 1) - { - input_color_mode = 0; - inlineoffset = doc->bitmapoffset[0] / 8; // Round down - inlinesize = (doc->outheader.cupsWidth + 7) / 8; // Round up - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Unsupported input color space: Number of colors: %d; Bits per color: %d.", - doc->inheader.cupsNumColors, doc->inheader.cupsBitsPerColor); - return (false); - } - - // Determine which input color space we need to obtain the output color - // space - switch (doc->outheader.cupsColorSpace) - { - case CUPS_CSPACE_W: // Gray - case CUPS_CSPACE_K: // Black - case CUPS_CSPACE_SW: // sGray - if (doc->outheader.cupsBitsPerColor == 1) - color_mode_needed = 0; - else - color_mode_needed = 1; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_CMYK: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_RGBW: - default: - color_mode_needed = 2; - break; - } - - if (input_color_mode != color_mode_needed) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Need to pre-convert input from %s to %s", - (input_color_mode == 0 ? "1-bit mono" : - (input_color_mode == 1 ? "8-bit gray" : - "8-bit RGB")), - (color_mode_needed == 0 ? "1-bit mono" : - (color_mode_needed == 1 ? "8-bit gray" : - "8-bit RGB"))); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Input color mode: %s", - (input_color_mode == 0 ? "1-bit mono" : - (input_color_mode == 1 ? "8-bit gray" : - "8-bit RGB"))); - } - - // Conversion line buffer (if needed by the conversion function - if (doc->allocLineBuf) - lineBuf = (unsigned char *)calloc(doc->bytesPerLine, sizeof(unsigned char)); - - // Switch conversion functions for even and odd pages - if ((pageNo & 1) == 0) - convertLine = convert->convertLineEven; - else - convertLine = convert->convertLineOdd; - - // - // Input line buffer (with space for raising horizontal resolution - // and overspray stretch if needed) - // - // Note that the input lines can get stretched when the driver defines - // paper dimensions larger than the physical paper size for - // overspraying on borderless printouts. Therefore we allocate the - // maximum of the input line size (multiplied by a resolution - // multiplier) and inlineoffset + inlinesize, to be sure to have - // enough space. - // - - i = doc->inheader.cupsBytesPerLine * res_up_factor[0]; - j = inlineoffset + inlinesize; - if (j > i) - i = j; - line = - (unsigned char *)calloc(i, sizeof(unsigned char)); - - // Input line averaging buffer (to reduce resolution) - if (res_down_factor[1] > 1 && input_color_mode > 0) - lineavg = - (unsigned char *)calloc(doc->inheader.cupsBytesPerLine * - res_down_factor[1], - sizeof(unsigned char)); - - // Input page buffer for color ordered in planes (if needed) - if (doc->nplanes > 1) - pagebuf = (unsigned char *)calloc(doc->outheader.cupsHeight * inlinesize, - sizeof(unsigned char)); - - // - // Overspray stretch of the input image If the output page - // dimensions are larger than the input page dimensions we have most - // probably a page size from the driver where the page dimensions are - // defined larger than the physical paper to do an overspray when - // printing borderless, to avoid narrow white margins on one side if - // the paper is not exactly aligned in the printer. The HPLIP driver - // hpcups does this for example on inkjet printers. - // - // To fill this larger raster space we need to stretch the original - // image slightly, and to do so, we will simply repeat a line or a - // column in regular intervals. - // - // To keep aspect ratio we will find the (horizontal or vertical) - // dimension with the higher stretch percentage and stretch by this - // in both dimensions. - // - // Here we calculate the number of lines/columns after which we need - // to repeat one line/column. - // - // This facility also fixes rounding errors which lead to the input - // raster to be a few pixels too small for the output. - // - - if (doc->outheader.PageSize[0] > doc->inheader.PageSize[0] || - doc->outheader.PageSize[1] > doc->inheader.PageSize[1]) - { - int extra_points, - min_overspray_duplicate_after_pixels = INT_MAX; - - if (doc->outheader.PageSize[0] >= 2 * doc->inheader.PageSize[0] || - doc->outheader.PageSize[1] >= 2 * doc->inheader.PageSize[1]) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Difference between input and output page dimensions too large, probably the input has a wrong page size"); - goto out; - } - - for (i = 0; i < 2; i ++) - { - extra_points = doc->outheader.PageSize[i] - doc->inheader.PageSize[i]; - if (extra_points > 0) - { - overspray_duplicate_after_pixels = doc->inheader.PageSize[i] / - extra_points; - if (overspray_duplicate_after_pixels < - min_overspray_duplicate_after_pixels) - min_overspray_duplicate_after_pixels = - overspray_duplicate_after_pixels; - } - } - overspray_duplicate_after_pixels = min_overspray_duplicate_after_pixels; - next_overspray_duplicate = overspray_duplicate_after_pixels; - if (abs(doc->outheader.PageSize[0] - doc->inheader.PageSize[0]) > 2 || - abs(doc->outheader.PageSize[1] - doc->inheader.PageSize[1]) > 2) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Output page dimensions are larger for borderless printing with overspray, inserting one extra pixel after each %d pixels", - overspray_duplicate_after_pixels); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Output page dimensions are larger than input page dimensions due to rounding error, inserting one extra pixel after each %d pixels", - overspray_duplicate_after_pixels); - } - } - - // Skip upper border - for (y = 0, yin = 0; y < doc->bitmapoffset[1]; y ++) - if (y % res_up_factor[1] == 0) - for (i = 0; i < res_down_factor[1]; i ++) - if (yin < doc->inheader.cupsHeight) - { - // Read input pixel line - if (cupsRasterReadPixels(inras, line, - doc->inheader.cupsBytesPerLine) != - doc->inheader.cupsBytesPerLine) - { - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Unable to read line %d for page %d.", - yin + 1, pageNo); - ret = false; - goto out; - } - yin ++; - } - - // Convert the page from PWG/Apple Raster to CUPS/PWG/Apple Raster - - // We will be able to stream per-line if the color order in the destination - // raster stream is chunked or banded and stream per-page if the colors are - // arranged in planes - next_line_read = 0; - next_overspray_duplicate = overspray_duplicate_after_pixels; - for (unsigned int plane = 0; plane < doc->nplanes; plane ++) - { - for (y = doc->bitmapoffset[1]; - y < doc->bitmapoffset[1] + doc->outheader.cupsHeight; y ++) - { - if (plane == 0) - { - // First plane or no planes - - if (next_overspray_duplicate != 0) - { - if (next_line_read == 0) - { - for (i = 0; i < res_down_factor[1]; i ++) - { - if (yin < doc->inheader.cupsHeight) - { - // Read input pixel line - if (cupsRasterReadPixels(inras, line, - doc->inheader.cupsBytesPerLine) != - doc->inheader.cupsBytesPerLine) - { - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Unable to read line %d for page %d.", - yin + 1, pageNo); - ret = false; - goto out; - } - yin ++; - } - else - // White lines to fill the rest of the page - memset(line, 255, doc->inheader.cupsBytesPerLine); - - // Collect lines for averaging (when reducing vertical resolution) - if (res_down_factor[1] > 1 && input_color_mode > 0) - memcpy(lineavg + i * doc->inheader.cupsBytesPerLine, - line, doc->inheader.cupsBytesPerLine); - } - - // Calculate average of res_down_factor[1] input lines for reducing - // resolution (only for 8-bit color modes, on 1-bit only last line - // of the group is used) - if (res_down_factor[1] > 1 && input_color_mode > 0) - for (i = 0; i < doc->inheader.cupsBytesPerLine; i ++) - { - int val = 0; - for (j = 0; j < res_down_factor[1]; j ++) - val += (int)*(lineavg + j * doc->inheader.cupsBytesPerLine + - i); - line[i] = (unsigned char)(val / res_down_factor[1]); - } - - // Converting pixel lines for horizontal resolution - if (res_up_factor[0] > 1) - { - // Repeat every pixel in the line res_up_factor[0] times - if (input_color_mode == 0) - { - unsigned char *src = line + doc->inheader.cupsBytesPerLine - 1, - *dst = line + doc->inheader.cupsBytesPerLine * - res_up_factor[0] - 1; - unsigned char byte = 0, mask = 0x01; - while (src >= line) - { - byte = *(src --); - for (i = 0; i < 8; i ++) - { - for (j = 0; j < res_up_factor[0]; j ++) - { - if (mask == 0x01) - *dst = 0; - if (byte & 0x01) - *dst |= mask; - mask <<= 1; - if (mask == 0) - { - dst --; - mask = 0x01; - } - } - byte >>= 1; - } - } - } - else if (input_color_mode == 1) - { - unsigned char *src = line + doc->inheader.cupsBytesPerLine - 1, - *dst = line + - (doc->inheader.cupsBytesPerLine - 1) * - res_up_factor[0]; - while (src >= line) - { - for (i = 0; i < res_up_factor[0]; i ++) - *(dst + i) = *src; - src -= 1; - dst -= res_up_factor[0]; - } - } - else if (input_color_mode == 2) - { - unsigned char *src = line + doc->inheader.cupsBytesPerLine - 3, - *dst = line + - (doc->inheader.cupsBytesPerLine - 3) * - res_up_factor[0]; - while (src >= line) - { - for (i = 0; i < res_up_factor[0]; i ++) - for (j = 0; j < 3; j ++) - *(dst + 3 * i + j) = *(src + j); - src -= 3; - dst -= 3 * res_up_factor[0]; - } - } - } - else if (res_down_factor[0] > 1) - { - // Reduce the number of pixels per line to 1/res_down_factor[0] - if (input_color_mode == 0) - { - // Grab the last of every group of res_down_factor[0] pixels - unsigned char *src = line, *dst = line; - unsigned char byte = 0, mask = 0x80; - j = 0; - while (src < line + doc->inheader.cupsBytesPerLine) - { - byte = *(src ++); - for (i = 0; i < 8; i ++) - { - j ++; - if (j == res_down_factor[0]) - { - if (mask == 0x80) - *dst = 0; - if (byte & 0x80) - *dst |= mask; - mask >>= 1; - if (mask == 0) - { - dst ++; - mask = 0x80; - } - j = 0; - } - byte <<= 1; - } - } - } - else if (input_color_mode == 1) - { - // Average every group of res_down_factor[0] pixels - unsigned char *src = line, *dst = line; - int val = 0; - j = 0; - while (j || src < line + doc->inheader.cupsBytesPerLine) - { - if (src < line + doc->inheader.cupsBytesPerLine) - val += (int)*(src ++); - else - val += 255; - j ++; - if (j == res_down_factor[0]) - { - *(dst ++) = (unsigned char)(val / res_down_factor[0]); - val = 0; - j = 0; - } - } - } - else if (input_color_mode == 2) - { - // Average every group of res_down_factor[0] pixels - unsigned char *src = line, *dst = line; - int val[3]; - for (i = 0; i < 3; i ++) - val[i] = 0; - i = 0; - j = 0; - while (j || src < line + doc->inheader.cupsBytesPerLine) - { - if (src < line + doc->inheader.cupsBytesPerLine) - val[i ++] += (int)*(src ++); - else - val[i ++] += 255; - if (i == 3) - { - j ++; - if (j == res_down_factor[0]) - { - for (i = 0; i < 3; i ++) - { - *(dst ++) = (unsigned char)(val[i] / - res_down_factor[0]); - val[i] = 0; - } - j = 0; - } - i = 0; - } - } - } - } - - // Stretching pixel lines for horizontal overspray - if (overspray_duplicate_after_pixels < INT_MAX) - { - // Repeat one pixel after each overspray_duplicate_after_pixels - // pixels - unsigned char *buf = - (unsigned char *)calloc(inlinesize, sizeof(unsigned char)); - unsigned char *src = line + inlineoffset, - *dst = buf; - if (input_color_mode == 0) - { - unsigned char srcmask = 0x80, dstmask = 0x80; - i = overspray_duplicate_after_pixels; - while (dst < buf + inlinesize) - { - if (*src & srcmask) - *dst |= dstmask; - dstmask >>= 1; - if (dstmask == 0) - { - dst ++; - dstmask = 0x80; - } - if (i == 0) - i = overspray_duplicate_after_pixels; - else - { - srcmask >>= 1; - if (srcmask == 0) - { - src ++; - srcmask = 0x80; - } - i --; - } - } - } - else if (input_color_mode == 1) - { - i = overspray_duplicate_after_pixels; - while (dst < buf + inlinesize) - { - *dst = *src; - dst ++; - if (i == 0) - i = overspray_duplicate_after_pixels; - else - { - src ++; - i --; - } - } - } - else if (input_color_mode == 2) - { - i = overspray_duplicate_after_pixels; - while (dst < buf + inlinesize - 2) - { - for (j = 0; j < 3; j ++) - *(dst + j) = *(src + j); - dst += 3; - if (i == 0) - i = overspray_duplicate_after_pixels; - else - { - src += 3; - i --; - } - } - } - memcpy(line + inlineoffset, buf, inlinesize); - free(buf); - } - next_line_read = res_up_factor[1]; - } - next_line_read --; - next_overspray_duplicate --; - } - else - next_overspray_duplicate = overspray_duplicate_after_pixels; - - // Pointer to the part of the input line we will use - bp = line + inlineoffset; - - // Save input line for the other planes - if (doc->nplanes > 1) - memcpy(pagebuf + (y - doc->bitmapoffset[1]) * inlinesize, - bp, inlinesize); - } - else - { - // Further planes - - // Pointer to input line in page buffer - bp = pagebuf + (y - doc->bitmapoffset[1]) * inlinesize; - } - - // Pre-convert into the color mode needed to convert to the final - // color space - unsigned char *preBuf1 = NULL, *preBuf2 = NULL; - if (input_color_mode != color_mode_needed) - { - if (input_color_mode == 2) // 8-bit RGB - { - preBuf1 = (unsigned char *)calloc(doc->outheader.cupsWidth, - sizeof(unsigned char)); - cfImageRGBToWhite(bp, preBuf1, doc->outheader.cupsWidth); - bp = preBuf1; - } - else if (input_color_mode == 0) // 1-bit mono - { - preBuf1 = (unsigned char *)calloc(doc->outheader.cupsWidth, - sizeof(unsigned char)); - cfOneBitToGrayLine(bp, preBuf1, doc->outheader.cupsWidth); - bp = preBuf1; - } - // We are always on color mode 1 (8-bit gray) at this point - if (color_mode_needed == 2) // 8-bit RGB - { - preBuf2 = (unsigned char *)calloc(doc->outheader.cupsWidth * 3, - sizeof(unsigned char)); - cfImageWhiteToRGB(bp, preBuf2, doc->outheader.cupsWidth); - bp = preBuf2; - } - else if (color_mode_needed == 0) // 1-bit mono - { - preBuf2 = (unsigned char *)calloc((doc->outheader.cupsWidth + 7) / 8, - sizeof(unsigned char)); - cfOneBitLine(bp, preBuf2, doc->outheader.cupsWidth, - y - doc->bitmapoffset[1], doc->bi_level); - bp = preBuf2; - } - } - - // Convert the line into the destination format and put it out - for (unsigned int band = 0; band < doc->nbands; band ++) - { - dp = convertLine(bp, lineBuf, y - doc->bitmapoffset[1], - plane + band, doc->outheader.cupsWidth, - doc->bytesPerLine, doc, convert->convertCSpace); - cupsRasterWritePixels(outras, dp, doc->bytesPerLine); - } - - // Clean up from pre-conversion - if (preBuf1) - free(preBuf1); - if (preBuf2) - free(preBuf2); - } - } - - // Read remaining input pixel lines - for (; yin < doc->inheader.cupsHeight; yin ++) - if (cupsRasterReadPixels(inras, line, - doc->inheader.cupsBytesPerLine) != - doc->inheader.cupsBytesPerLine) - { - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Unable to read line %d for page %d.", - yin + 1, pageNo); - ret = false; - goto out; - } - - out: - // Clean up - free(line); - if (res_down_factor[1] > 1 && input_color_mode > 0) - free(lineavg); - if (doc->nplanes > 1) - free(pagebuf); - if (doc->allocLineBuf) - free(lineBuf); - - return (ret); -} - - -static int -set_color_profile(pwgtoraster_doc_t *doc, - cf_logfunc_t log, - void *ld) -{ - if (doc->outheader.cupsBitsPerColor != 8 && - doc->outheader.cupsBitsPerColor != 16) - { - // color Profile is not supported - return (0); - } - - // set output color profile - switch (doc->outheader.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - if (doc->color_profile.colorProfile == NULL) - { - cmsCIExyY wp; -#ifdef USE_LCMS1 - cmsWhitePointFromTemp(6504, &wp); // D65 White point -#else - cmsWhitePointFromTemp(&wp, 6504); // D65 White point -#endif - doc->color_profile.colorProfile = cmsCreateLab4Profile(&wp); - } - break; - case CUPS_CSPACE_CIEXYZ: - if (doc->color_profile.colorProfile == NULL) - { - // transform color space via CIELab - cmsCIExyY wp; -#ifdef USE_LCMS1 - cmsWhitePointFromTemp(6504, &wp); // D65 White point -#else - cmsWhitePointFromTemp(&wp, 6504); // D65 White point -#endif - cmsxyY2XYZ(&(doc->color_profile.D65WhitePoint),&wp); - doc->color_profile.colorProfile = cmsCreateLab4Profile(&wp); - } - break; - case CUPS_CSPACE_SRGB: - doc->color_profile.colorProfile = cmsCreate_sRGBProfile(); - break; - case CUPS_CSPACE_ADOBERGB: - doc->color_profile.colorProfile = adobergb_profile(); - break; - case CUPS_CSPACE_SW: - doc->color_profile.colorProfile = sgray_profile(); - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_K: - case CUPS_CSPACE_W: - case CUPS_CSPACE_WHITE: - case CUPS_CSPACE_GOLD: - case CUPS_CSPACE_SILVER: - // We can set specified profile to output profile - doc->color_profile.outputColorProfile = - doc->color_profile.colorProfile; - break; - case CUPS_CSPACE_CMYK: - case CUPS_CSPACE_KCMY: - case CUPS_CSPACE_KCMYcm: - case CUPS_CSPACE_YMCK: - case CUPS_CSPACE_RGBA: - case CUPS_CSPACE_RGBW: - case CUPS_CSPACE_GMCK: - case CUPS_CSPACE_GMCS: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_YMC: - // use standard RGB - doc->color_profile.outputColorProfile = NULL; - break; - default: - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Specified ColorSpace is not supported"); - return (1); - } - - return (0); -} - - -int -cfFilterPWGToRaster(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data,// I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - cf_filter_out_format_t outformat; - pwgtoraster_doc_t doc; - int i; - const char *val; - cups_raster_t *inras = NULL, - *outras = NULL; - conversion_function_t convert; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int ret = 0; - - - (void)inputseekable; - (void)parameters; - - val = data->final_content_type; - if (val) - { - if (strcasestr(val, "pwg")) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - else if (strcasestr(val, "urf")) - outformat = CF_FILTER_OUT_FORMAT_APPLE_RASTER; - else - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - } - else - outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - "Apple Raster"))); - - cmsSetLogErrorHandler(lcms_error_handler); - - // - // Open the input data stream specified by inputfd ... - // - - if ((inras = cupsRasterOpen(inputfd, CUPS_RASTER_READ)) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Unable to open input data stream."); - } - - return (1); - } - - // - // Prepare output document header - // - - // Initialize data structure - memset(&doc, 0, sizeof(doc)); - doc.data = data; - doc.color_profile.renderingIntent = INTENT_PERCEPTUAL; - - // Parse the options - if (parse_opts(outformat, &doc) == 1) - return (1); - - doc.outheader.NumCopies = data->copies; - doc.outheader.MirrorPrint = CUPS_FALSE; - doc.outheader.Orientation = CUPS_ORIENT_0; - - if (doc.outheader.cupsBitsPerColor != 1 && - doc.outheader.cupsBitsPerColor != 2 && - doc.outheader.cupsBitsPerColor != 4 && - doc.outheader.cupsBitsPerColor != 8 && - doc.outheader.cupsBitsPerColor != 16) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Specified color format is not supported."); - ret = 1; - goto out; - } - - if (doc.outheader.cupsColorOrder == CUPS_ORDER_PLANAR) - doc.nplanes = doc.outheader.cupsNumColors; - else - doc.nplanes = 1; - if (doc.outheader.cupsColorOrder == CUPS_ORDER_BANDED) - doc.nbands = doc.outheader.cupsNumColors; - else - doc.nbands = 1; - - // - // Check color space and set color profile - // - - switch (doc.outheader.cupsColorSpace) - { - case CUPS_CSPACE_CIELab: - case CUPS_CSPACE_ICC1: - case CUPS_CSPACE_ICC2: - case CUPS_CSPACE_ICC3: - case CUPS_CSPACE_ICC4: - case CUPS_CSPACE_ICC5: - case CUPS_CSPACE_ICC6: - case CUPS_CSPACE_ICC7: - case CUPS_CSPACE_ICC8: - case CUPS_CSPACE_ICC9: - case CUPS_CSPACE_ICCA: - case CUPS_CSPACE_ICCB: - case CUPS_CSPACE_ICCC: - case CUPS_CSPACE_ICCD: - case CUPS_CSPACE_ICCE: - case CUPS_CSPACE_ICCF: - case CUPS_CSPACE_CIEXYZ: - if (doc.outheader.cupsColorOrder != CUPS_ORDER_CHUNKED || - (doc.outheader.cupsBitsPerColor != 8 && - doc.outheader.cupsBitsPerColor != 16)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Specified color format is not supported."); - ret = 1; - goto out; - } - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_YMC: - case CUPS_CSPACE_CMYK: - case CUPS_CSPACE_KCMY: - case CUPS_CSPACE_KCMYcm: - case CUPS_CSPACE_YMCK: - case CUPS_CSPACE_RGBA: - case CUPS_CSPACE_RGBW: - case CUPS_CSPACE_GMCK: - case CUPS_CSPACE_GMCS: - doc.outputNumColors = 3; - break; - case CUPS_CSPACE_K: - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - case CUPS_CSPACE_WHITE: - case CUPS_CSPACE_GOLD: - case CUPS_CSPACE_SILVER: - // set paper color white - doc.outputNumColors = 1; - break; - default: - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Specified ColorSpace is not supported."); - ret = 1; - goto out; - } - if (!(doc.color_profile.cm_disabled)) - { - if (set_color_profile(&doc, log, ld) == 1) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Cannot set color profile."); - ret = 1; - goto out; - } - } - - // - // Open output raster stream - // - - if ((outras = cupsRasterOpen(outputfd, (outformat == - CF_FILTER_OUT_FORMAT_CUPS_RASTER ? - CUPS_RASTER_WRITE : - (outformat == - CF_FILTER_OUT_FORMAT_PWG_RASTER ? - CUPS_RASTER_WRITE_PWG : - (outformat == - CF_FILTER_OUT_FORMAT_APPLE_RASTER ? - CUPS_RASTER_WRITE_APPLE : - CUPS_RASTER_WRITE))))) == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Can't open output raster stream."); - ret = 1; - goto out; - } - - // - // Select conversion function - // - - memset(&convert, 0, sizeof(conversion_function_t)); - if (select_convert_func(outras, &doc, &convert) == 1) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToRaster: Unable to select color conversion function."); - ret = 1; - goto out; - } - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Output page header"); - if (doc.outheader.ImagingBoundingBox[3] > 0) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Duplex = %d", doc.outheader.Duplex); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: HWResolution = [ %d %d ]", - doc.outheader.HWResolution[0], doc.outheader.HWResolution[1]); - if (doc.outheader.ImagingBoundingBox[3] > 0) - { - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: ImagingBoundingBox = [ %d %d %d %d ]", - doc.outheader.ImagingBoundingBox[0], - doc.outheader.ImagingBoundingBox[1], - doc.outheader.ImagingBoundingBox[2], - doc.outheader.ImagingBoundingBox[3]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Margins = [ %d %d ]", - doc.outheader.Margins[0], doc.outheader.Margins[1]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: ManualFeed = %d", doc.outheader.ManualFeed); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: MediaPosition = %d", doc.outheader.MediaPosition); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: NumCopies = %d", doc.outheader.NumCopies); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: Orientation = %d", doc.outheader.Orientation); - } - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: PageSize = [ %d %d ]", - doc.outheader.PageSize[0], doc.outheader.PageSize[1]); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsWidth = %d", doc.outheader.cupsWidth); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsHeight = %d", doc.outheader.cupsHeight); - if (doc.outheader.ImagingBoundingBox[3] > 0) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsMediaType = %d", - doc.outheader.cupsMediaType); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBitsPerColor = %d", - doc.outheader.cupsBitsPerColor); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBitsPerPixel = %d", - doc.outheader.cupsBitsPerPixel); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsBytesPerLine = %d", - doc.outheader.cupsBytesPerLine); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsColorOrder = %d", - doc.outheader.cupsColorOrder); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsColorSpace = %d", - doc.outheader.cupsColorSpace); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsCompression = %d", - doc.outheader.cupsCompression); - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: cupsPageSizeName = %s", - doc.outheader.cupsPageSizeName); - } - - // - // Print the pages - // - - i = 0; - while (out_page(&doc, i + 1, inras, outras, &convert)) - i ++; - if (i == 0) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToRaster: No page printed, outputting empty file."); - - out: - - // - // Close the streams - // - - if (inras) - cupsRasterClose(inras); - close(inputfd); - if (outras) - cupsRasterClose(outras); - close(outputfd); - - // - // Clean up - // - - if (doc.color_profile.colorProfile != NULL) - cmsCloseProfile(doc.color_profile.colorProfile); - if (doc.color_profile.outputColorProfile != NULL && - doc.color_profile.outputColorProfile != doc.color_profile.colorProfile) - cmsCloseProfile(doc.color_profile.outputColorProfile); - if (doc.color_profile.colorTransform != NULL) - cmsDeleteTransform(doc.color_profile.colorTransform); - - return (ret); -} diff --git a/cupsfilters/raster.c b/cupsfilters/raster.c deleted file mode 100644 index 62961129e..000000000 --- a/cupsfilters/raster.c +++ /dev/null @@ -1,1841 +0,0 @@ -// -// Functions to handle CUPS/PWG Raster headers for libcupsfilters. -// -// Copyright 2013-2022 by Till Kamppeter. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfRasterColorSpaceString() - Return strings for CUPS color spaces -// cfRasterPrepareHeader() - Prepare a Raster header for a job -// cfRasterSetColorSpace() - Find best color space for print-color-mode -// and print-quality setting -// - -// -// Include necessary headers. -// - -#include -#include -#include "raster.h" -#include "filter.h" -#include "driver.h" -#include "ipp.h" -#include -#include -#include -#include - -// -// Local functions -// - -static int raster_base_header(cups_page_header2_t *h, cf_filter_data_t *data, - int pwg_raster); - -// -// '_strlcpy()' - Safely copy two strings. -// - -static size_t // O - Length of string -_strlcpy(char *dst, // O - Destination string - const char *src, // I - Source string - size_t size) // I - Size of destination string buffer -{ - size_t srclen; // Length of source string - - - // - // Figure out how much room is needed... - // - - size --; - - srclen = strlen(src); - - // - // Copy the appropriate amount... - // - - if (srclen > size) - srclen = size; - - memcpy(dst, src, srclen); - dst[srclen] = '\0'; - - return (srclen); -} - - -// -// 'cfRasterColorSpaceString()' - Return the color space name for a -// cupsColorSpace value. - -const char * -cfRasterColorSpaceString(cups_cspace_t cspace) // I - cupsColorSpace value -{ - static const char * const cups_color_spaces[] = - { // Color spaces - "W", - "RGB", - "RGBA", - "K", - "CMY", - "YMC", - "CMYK", - "YMCK", - "KCMY", - "KCMYcm", - "GMCK", - "GMCS", - "WHITE", - "GOLD", - "SILVER", - "CIEXYZ", - "CIELab", - "RGBW", - "SW", - "SRGB", - "ADOBERGB", - "21", - "22", - "23", - "24", - "25", - "26", - "27", - "28", - "29", - "30", - "31", - "ICC1", - "ICC2", - "ICC3", - "ICC4", - "ICC5", - "ICC6", - "ICC7", - "ICC8", - "ICC9", - "ICCA", - "ICCB", - "ICCC", - "ICCD", - "ICCE", - "ICCF", - "47", - "DEVICE1", - "DEVICE2", - "DEVICE3", - "DEVICE4", - "DEVICE5", - "DEVICE6", - "DEVICE7", - "DEVICE8", - "DEVICE9", - "DEVICEA", - "DEVICEB", - "DEVICEC", - "DEVICED", - "DEVICEE", - "DEVICEF" - }; - - if (cspace < CUPS_CSPACE_W || cspace > CUPS_CSPACE_DEVICEF) - return ("Unknown"); - else - return (cups_color_spaces[cspace]); -} - - -// -// 'cfRasterPrepareHeader() - This function creates a CUPS/PWG Raster -// header for Raster output based on the -// printer and job properties supplied to -// the calling filter functions, printer -// properties via printer IPP attributes -// and job properties via CUPS option list -// and job IPP attributesor optionally a -// sample header. For PWG and Apple Raster -// output the color space and depth is -// auto-selected based on available options -// listed in the urf-supported and -// pwg-raster-document-type-supported -// printer IPP attributes and the settings -// of print-color-mode ("ColorModel") and -// print-quality ("cupsPrintQuality") job -// attributes/options. -// - -int // O - 0 on success, - // -1 on error -cfRasterPrepareHeader(cups_page_header2_t *h, // I - Raster header - cf_filter_data_t *data, // I - Job and printer data - cf_filter_out_format_t final_outformat, - // I - Job output format - // (determines color space, - // and resolution) - cf_filter_out_format_t header_outformat, - // I - This filter's output - // format (determines - // header format) - int no_high_depth, // I - Suppress use of - // > 8 bit per color - cups_cspace_t *cspace) // IO - Color space we want to - // use, -1 for auto, we - // return color space - // actually used, -1 if - // no suitable color space - // found. -{ - int i; - ipp_t *printer_attrs, *job_attrs; - int num_options = 0; - cups_option_t *options = NULL; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - int pwgraster = 0, - appleraster = 0, - cupsraster = 0, - pclm = 0; - int cupsrasterheader = 1; - const char *p; - ipp_attribute_t *attr; - const char *cspaces_available = NULL, *color_mode = NULL, *quality = NULL; - int hi_depth; - char valuebuffer[2048]; - int res = 1; - int xres = -1; int yres = -1; - float margins[4]; - float dimensions[2]; - char size_name_buf[IPP_MAX_NAME + 1]; - - - if (final_outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER) - pwgraster = 1; - else if (final_outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - appleraster = 1; - else if (final_outformat == CF_FILTER_OUT_FORMAT_PCLM) - pclm = 1; - else - cupsraster = 1; - - if (cupsraster) - { - pwgraster = 0; - p = cupsGetOption("media-class", num_options, options); - if (p == NULL) - p = cupsGetOption("MediaClass", num_options, options); - if (p != NULL) - { - if (strcasestr(p, "pwg")) - { - pwgraster = 1; - cupsraster = 0; - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "PWG Raster output requested (via \"MediaClass\"/\"media-class\" option)"); - } - else - pwgraster = 0; - } - } - - if (header_outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || - header_outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) - cupsrasterheader = 0; - - printer_attrs = data->printer_attrs; - job_attrs = data->job_attrs; - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - printer_attrs = data->printer_attrs; - job_attrs = data->job_attrs; - - // These values will be used in case we don't find supported resolutions - // for given output format - if ((attr = ippFindAttribute(printer_attrs, "printer-resolution-default", - IPP_TAG_RESOLUTION)) != NULL) - { - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - const char *p = valuebuffer; - xres = atoi(p); - if ((p = strchr(p, 'x')) != NULL) - yres = atoi(p + 1); - else - yres = xres; - } - // Finding supported resolution for given output format - if (pwgraster) - { - if ((attr = ippFindAttribute(printer_attrs, - "pwg-raster-document-resolution-supported", - IPP_TAG_RESOLUTION)) != NULL) - { - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - const char *p = valuebuffer; - xres = atoi(p); - if ((p = strchr(p, 'x')) != NULL) - yres = atoi(p + 1); - else - yres = xres; - } - } - else if (appleraster) - { - if ((attr = ippFindAttribute(printer_attrs, "urf-supported", - IPP_TAG_KEYWORD))!=NULL) - { - for (int i = 0; i < ippGetCount(attr); i ++) - { - const char *p = ippGetString(attr, i, NULL); - if (strncasecmp(p, "RS", 2)) - continue; - int lo; int hi; - lo = atoi(p + 2); - if (lo == 0) - lo = -1; - p = strchr(p, '-'); - if (p) - hi = atoi(p + 1); - else - hi = lo; - xres = hi; - yres = hi; - } - } - } - else if (pclm) - { - if ((attr = ippFindAttribute(printer_attrs, - "pclm-source-resolution-default", - IPP_TAG_RESOLUTION)) != NULL) - { - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - const char *p = valuebuffer; - xres = atoi(p); - if ((p = strchr(p, 'x')) != NULL) - yres = atoi(p + 1); - else - yres = xres; - } - } - - if (log) - { - if (*cspace == -1) - log(ld, CF_LOGLEVEL_DEBUG, - "Color space requested: Default"); - else - log(ld, CF_LOGLEVEL_DEBUG, - "Color space requested: #%d", *cspace); - log(ld, CF_LOGLEVEL_DEBUG, - "Final output format: %s", - appleraster ? "Apple Raster" : - (pwgraster ? "PWG Raster" : - (pclm ? "PCLm" : "CUPS Raster"))); - } - - if (data->header) - *h = *(data->header); // Copy sample header - else - raster_base_header(h, data, 1 - cupsrasterheader); - if (cfGetPageDimensions(data->printer_attrs, data->job_attrs, - data->num_options, data->options, h, 0, - &(dimensions[0]), &(dimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), size_name_buf, - NULL) < 0) - { - pwg_media_t *pwg_media; - - for (i = 0; i < 2; i ++) - dimensions[i] = h->cupsPageSize[i]; - margins[0] = h->cupsImagingBBox[0]; - margins[1] = h->cupsImagingBBox[1]; - margins[2] = dimensions[0] - h->cupsImagingBBox[2]; - margins[3] = dimensions[1] - h->cupsImagingBBox[3]; - pwg_media = pwgMediaForSize(dimensions[0] / 72 * 2540, - dimensions[1] / 72 * 2540); - if (pwg_media) - { - p = (pwg_media->ppd ? pwg_media->ppd : - (pwg_media->legacy ? pwg_media->legacy : pwg_media->pwg)); - if (p) - _strlcpy(h->cupsPageSizeName, p, sizeof(h->cupsPageSizeName)); - } - } - else if (size_name_buf[0]) - _strlcpy(h->cupsPageSizeName, size_name_buf, sizeof(h->cupsPageSizeName)); - - if (!cupsrasterheader) - memset(margins, 0, sizeof(margins)); - - if (printer_attrs && - ((pwgraster && - (attr = ippFindAttribute(printer_attrs, - "pwg-raster-document-type-supported", - IPP_TAG_ZERO)) != NULL) || - (appleraster && - (attr = ippFindAttribute(printer_attrs, - "urf-supported", - IPP_TAG_ZERO)) != NULL))) - { - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - cspaces_available = valuebuffer; - if ((color_mode = cupsGetOption("print-color-mode", num_options, - options)) == NULL) - color_mode = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "print-color-mode"); - if ((quality = cupsGetOption("print-quality", num_options, - options)) == NULL) - quality = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "print-quality"); - hi_depth = (!no_high_depth && quality && - (!strcasecmp(quality, "high") || !strcmp(quality, "5"))) ? - 1 : 0; - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "Color mode requested: %s; color depth requested: %s", - color_mode, hi_depth ? "High" : "Standard"); - log(ld, CF_LOGLEVEL_DEBUG, - "Determining best color space/depth ..."); - } - res = cfRasterSetColorSpace(h, cspaces_available, color_mode, - cspace, &hi_depth); - } - else if (pclm) - { - // Available color spaces are always SRGB 8 and SGray 8 - cspaces_available = "srgb_8,sgray_8"; - if ((color_mode = cupsGetOption("print-color-mode", num_options, - options)) == NULL) - color_mode = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "print-color-mode"); - hi_depth = 0; - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "For PCLm color mode is always SRGB/SGray 8-bit."); - res = cfRasterSetColorSpace(h, cspaces_available, color_mode, - cspace, &hi_depth); - } - - if (res != 1) - { - // cfRasterSetColorSpace() was called - if (res < 0) - { - // failed - if (log) - { - log(ld, CF_LOGLEVEL_ERROR, - "Unable to set color space/depth for Raster output!"); - if (*cspace < 0) - log(ld, CF_LOGLEVEL_ERROR, - "Did not find a valid color space!"); - else - log(ld, CF_LOGLEVEL_ERROR, - "Requested color space #%d not a valid PWG/Apple Raster color space!", - *cspace); - } - return (-1); - } - else - // succeeded - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "Using color space #%d with %s color depth", - *cspace, hi_depth ? "high" : "standard"); - } - - if ((h->HWResolution[0] == 100) && (h->HWResolution[1] == 100)) - { - // No resolution set in header - if (xres != -1) - { - h->HWResolution[0] = xres; - h->HWResolution[1] = yres; - } - else - { - h->HWResolution[0] = 300; - h->HWResolution[1] = 300; - } - h->cupsWidth = h->HWResolution[0] * h->PageSize[0] / 72; - h->cupsHeight = h->HWResolution[1] * h->PageSize[1] / 72; - } - - // Make all page geometry fields in the header consistent - if (cupsrasterheader) - { - h->cupsWidth = ((dimensions[0] - margins[0] - margins[2]) / - 72.0 * h->HWResolution[0]) + 0.5; - h->cupsHeight = ((dimensions[1] - margins[1] - margins[3]) / - 72.0 * h->HWResolution[1]) + 0.5; - } - else - { - h->cupsWidth = (dimensions[0] / - 72.0 * h->HWResolution[0]) + 0.5; - h->cupsHeight = (dimensions[1] / - 72.0 * h->HWResolution[1]) + 0.5; - } - for (i = 0; i < 2; i ++) - { - h->cupsPageSize[i] = dimensions[i]; - h->PageSize[i] = (unsigned int)(h->cupsPageSize[i] + 0.5); - if (cupsrasterheader) - h->Margins[i] = margins[i] + 0.5; - else - h->Margins[i] = 0; - } - if (cupsrasterheader) - { - h->cupsImagingBBox[0] = margins[0]; - h->cupsImagingBBox[1] = margins[1]; - h->cupsImagingBBox[2] = dimensions[0] - margins[2]; - h->cupsImagingBBox[3] = dimensions[1] - margins[3]; - for (i = 0; i < 4; i ++) - h->ImagingBoundingBox[i] = - (unsigned int)(h->cupsImagingBBox[i] + 0.5); - } - else - for (i = 0; i < 4; i ++) - { - h->cupsImagingBBox[i] = 0.0; - h->ImagingBoundingBox[i] = 0; - } - h->cupsBytesPerLine = (h->cupsBitsPerPixel * h->cupsWidth + 7) / 8; - if (h->cupsColorOrder == CUPS_ORDER_BANDED) - h->cupsBytesPerLine *= h->cupsNumColors; - - // Mark header as PWG Raster if it is not CUPS Raster - if (!cupsrasterheader) - strcpy(h->MediaClass, "PwgRaster"); - - cupsFreeOptions(num_options, options); - - return (0); -} - - -// -// 'cfRasterSetColorSpace() - Update a given CUPS/PWG Raster header to -// the desired color mode, color space, and -// color depth. We supply one of the -// printer IPP attributes urf-supported or -// pwg-raster-document-type-supported as -// they contain a list of all valid combos -// of color space and color depth supported -// by the printer, tell the -// print-color-mode attribute setting for -// this job and request a color space and -// optionally high color depth. Then it is -// checked first whether the requested -// color space is available and if not we -// fall back to the base color space -// (usually sGray or sRGB). Then knowing -// the color space we will use, we check -// whether in this color space more than -// one color depth is supported, we chooce -// the lowest, and if high color depth is -// requested, the highest. -// - -int // O - 0 on success, - // -1 on error -cfRasterSetColorSpace(cups_page_header2_t *h, // I - Raster header - const char *available, // I - Available color spaces - // from IPP attribute - // urf-supported or - // pwg-raster-document-type-supported - const char *color_mode, // I - print-color-mode IPP - // attribute setting - cups_cspace_t *cspace, // IO - Color space we want to - // use, -1 for auto, we - // return color space - // actually used, -1 if - // no suitable color space - // found. - int *high_depth) // IO - Do we want to print in - // high color depth? We - // reset to 0 if high - // quality not supported - // in the color space - // used. -{ - int min_depth = 999; - int max_depth = 0; - int best_depth = -1; - int num_colors; - int cspace_fallback = 0; // 0: originally requested color space - // 1: sRGB for color, sGray for mono - // 2: sRGB for mono - const char *p, *q; - - // Range-check - if (!h || !available || !cspace) - return (-1); - - if (*cspace != -1 && *cspace != CUPS_CSPACE_SW && - *cspace != CUPS_CSPACE_SRGB && *cspace != CUPS_CSPACE_ADOBERGB && - *cspace != CUPS_CSPACE_W && *cspace != CUPS_CSPACE_K && - *cspace != CUPS_CSPACE_RGB && *cspace != CUPS_CSPACE_CMYK) - return (-1); - - // True Bi-Level only available in PWG Raster. - // List of properties in pwg-raster-document-type-supported IPP attribute - // is lower-case-only whereas urf-supported for Apple Raster is upper-case- - // only - if (islower(available[0]) && - (p = strstr(available, "black_1")) != NULL && - !isdigit(*(p + 7)) && - (!strcmp(color_mode, "bi-level") || - !strcmp(color_mode, "process-bi-level"))) - { - // Set parameters for bi-level, there is only one color space and color - // depth - *cspace = CUPS_CSPACE_K; - best_depth = 1; - num_colors = 1; - // No high color depth in bi-level - if (high_depth) - *high_depth = 0; - } - else - { - // Any other color space - for (;;) - { // Loop through fallbacks to default if requested color space - // not supported - if (*cspace != -1) - { // Skip if no color space specified - for (p = available; p; p = q) - { - int n, dmin, dmax; - // Check whether requested color space is supported - if ((*cspace == CUPS_CSPACE_SW && - (((q = strchr(p, 'W')) != NULL && - (q == available || *(q - 1) == ',') && - (q ++) && - isdigit(*q)) || - ((q = strstr(p, "sgray_")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 6) && - isdigit(*q))) && - (num_colors = 1)) || - (*cspace == CUPS_CSPACE_W && - (((q = strstr(p, "DEVW")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 4) && - isdigit(*q)) || - ((q = strstr(p, "black_")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 6) && - isdigit(*q))) && - (num_colors = 1)) || - (*cspace == CUPS_CSPACE_SRGB && - (((q = strstr(p, "SRGB")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 4) && - isdigit(*q)) || - ((q = strstr(p, "srgb_")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 5) && - isdigit(*q))) && - (num_colors = 3)) || - (*cspace == CUPS_CSPACE_ADOBERGB && - (((q = strstr(p, "ADOBERGB")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 8) && - isdigit(*q)) || - ((q = strstr(p, "adobe-rgb_")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 10) && - isdigit(*q))) && - (num_colors = 3)) || - (*cspace == CUPS_CSPACE_RGB && - (((q = strstr(p, "DEVRGB")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 6) && - isdigit(*q)) || - ((q = strstr(p, "rgb_")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 4) && - isdigit(*q))) && - (num_colors = 3)) || - (*cspace == CUPS_CSPACE_CMYK && - (((q = strstr(p, "DEVCMYK")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 7) && - isdigit(*q)) || - ((q = strstr(p, "cmyk_")) != NULL && - (q == available || *(q - 1) == ',') && - (q += 5) && - isdigit(*q))) && - (num_colors = 4))) - { - // Color space supported, check color depth values - n = sscanf(q, "%d-%d", &dmin, &dmax); - if (isupper(available[0])) - // urf-supported specifies bits per pixel, we need bits per - // color - dmin = dmin / num_colors; - if (dmin < min_depth) - min_depth = dmin; - if (n == 2) - { - if (isupper(available[0])) - // urf-supported specifies bits per pixel, we need bits per - // color - dmax = dmax / num_colors; - if (dmax > max_depth) - max_depth = dmax; - } - else - { - if (dmin > max_depth) - max_depth = dmin; - } - // Select depth depending on whether we want to have high or - // standard color depth - if (high_depth && *high_depth) - best_depth = max_depth; - else - best_depth = min_depth; - } - else - // Advance to the next color space entry - if (q && *q != '\0') - q ++; - } - if (best_depth > 0) - { - // The requested color space is supported, so quit the fallback - // loop - if (high_depth && *high_depth && min_depth == max_depth) - // We requested high color depth but our color space is only - // supported with a single color depth, reset request to tell - // that we did not find a higher color depth - *high_depth = 0; - break; - } - } - // Arrived here, the requested color depth is not supported, try next - // fallback level - cspace_fallback ++; - if (cspace_fallback > 2) - { - // Gone through all fallbacks and still no suitable color space? - // Quit finally - *cspace = -1; - return (-1); - } - // Fallback 1: Suitable color space for the requested color mode - // Fallback 2: Color always (if printer does not advertise mono mode or - // sRGB if DeviceRGB is requested but only sRGB available) - // AdobeRGB instead of sRGB only if available in 16 bit per color and - // high color depth is requested - if ((cspace_fallback == 1 && - (!strcasecmp(color_mode, "auto") || - strcasestr(color_mode, "color") || - strcasestr(color_mode, "rgb") || - strcasestr(color_mode, "cmy"))) || - cspace_fallback == 2) - { - if (strcasestr(color_mode, "adobe") || - (high_depth && *high_depth && - (strstr(available, "ADOBERGB24-48") || - strstr(available, "ADOBERGB48") || - strstr(available, "adobe-rgb_16")))) - *cspace = CUPS_CSPACE_ADOBERGB; - else if (strcasestr(available, "cmyk") && - strcasestr(color_mode, "cmy")) - *cspace = CUPS_CSPACE_CMYK; - else if ((strcasestr(available, "srgb") && - !strcasestr(color_mode, "device")) || - cspace_fallback == 2) - *cspace = CUPS_CSPACE_SRGB; - else - *cspace = CUPS_CSPACE_RGB; - } - else - { - if (!strcasestr(color_mode, "device")) - *cspace = CUPS_CSPACE_SW; - else - *cspace = CUPS_CSPACE_W; - } - } - } - - // Success, update the raster header - h->cupsBitsPerColor = best_depth; - h->cupsBitsPerPixel = best_depth * num_colors; - h->cupsColorSpace = *cspace; - h->cupsNumColors = num_colors; - h->cupsBytesPerLine = (h->cupsWidth * h->cupsBitsPerPixel + 7) / 8; - - return (0); -} - - -static int // O - -1 on error, 0 on success -raster_base_header(cups_page_header2_t *h, // O - Raster header - cf_filter_data_t *data, // I - Filter data - int pwg_raster) // I - 1 if PWG/Apple Raster -{ - int i; // Looping var - char *ptr, // Pointer into string - s[255]; // Temporary string - const char *val, // Pointer into value - *media; // media option - char *media_source, // Media source - *media_type; // Media type - pwg_media_t *size_found; // page size found for given name - int num_options = 0; // Number of options - cups_option_t *options = NULL; // Options - ipp_attribute_t *attr; - - - // - // Range check input... - // - - if (!h) - return (-1); - - // - // Join the IPP attributes and the CUPS options in a single list - // - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - // - // Check if the supplied "media" option is a comma-separated list of any - // combination of page size ("media"), media source ("media-position"), - // and media type ("media-type") and if so, extract media type and source. - // Media size will be handled separately. - // - - media_source = NULL; - media_type = NULL; - if ((media = cupsGetOption("media", num_options, options)) != NULL) - { - // - // Loop through the option string, separating it at commas and setting each - // individual option. - // - // For PageSize, we also check for an empty option value since some versions - // of MacOS X use it to specify auto-selection of the media based solely on - // the size. - // - - for (val = media; *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 ++; - - // - // Identify it... - // - - size_found = NULL; - if ((size_found = pwgMediaForPWG(s)) == NULL) - if ((size_found = pwgMediaForPPD(s)) == NULL) - if ((size_found = pwgMediaForLegacy(s)) == NULL) - { - if (strcasestr(s, "tray") || - strcasestr(s, "feed") || - strcasestr(s, "capacity") || - strcasestr(s, "upper") || - strcasestr(s, "top") || - strcasestr(s, "middle") || - strcasestr(s, "lower") || - strcasestr(s, "bottom") || - strcasestr(s, "left") || - strcasestr(s, "right") || - strcasestr(s, "side") || - strcasestr(s, "roll") || - strcasestr(s, "main")) - { - if (media_source == NULL) - media_source = strdup(s); - } - else - media_type = strdup(s); - } - } - } - - // - // Initialize header - // - - memset(h, 0, sizeof(cups_page_header2_t)); - - // - // Fill in the items using printer and job IPP attributes and options - // - - if (pwg_raster) - strcpy(h->MediaClass, "PwgRaster"); - else if ((val = cupsGetOption("media-class", num_options, options)) != NULL || - (val = cupsGetOption("MediaClass", num_options, options)) != NULL) - _strlcpy(h->MediaClass, val, sizeof(h->MediaClass)); - else - strcpy(h->MediaClass, ""); - if (strcasecmp(h->MediaClass, "PwgRaster") == 0) - pwg_raster = 1; - - if ((val = cupsGetOption("media-color", num_options, options)) != NULL || - (val = cupsGetOption("MediaColor", num_options, options)) != NULL) - _strlcpy(h->MediaColor, val, sizeof(h->MediaColor)); - else - h->MediaColor[0] = '\0'; - - if ((val = cupsGetOption("media-type", num_options, options)) != NULL || - (val = cupsGetOption("MediaType", num_options, options)) != NULL || - (val = media_type) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "media-type")) != NULL) - _strlcpy(h->MediaType, val, sizeof(h->MediaType)); - else - h->MediaType[0] = '\0'; - - if ((val = cupsGetOption("print-content-optimize", num_options, - options)) != NULL || - (val = cupsGetOption("output-type", num_options, options)) != NULL || - (val = cupsGetOption("OutputType", num_options, options)) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "print-content-optimize")) != NULL) - { - if (!strncasecmp(val, "auto", 4)) - _strlcpy(h->OutputType, "automatic", - sizeof(h->OutputType)); - else if (!strcasecmp(val, "graphics") || - !strcasecmp(val, "graphic")) - _strlcpy(h->OutputType, "graphics", sizeof(h->OutputType)); - else if (!strcasecmp(val, "photo")) - _strlcpy(h->OutputType, "photo", sizeof(h->OutputType)); - else if (!strcasecmp(val, "text")) - _strlcpy(h->OutputType, "text", sizeof(h->OutputType)); - else if (!strcasecmp(val, "text-and-graphics") || - !strcasecmp(val, "text-and-graphic") || - !strcasecmp(val, "TextAndGraphics") || - !strcasecmp(val, "TextAndGraphic")) - _strlcpy(h->OutputType, "text-and-graphics", - sizeof(h->OutputType)); - else if (!pwg_raster) - _strlcpy(h->OutputType, val, sizeof(h->OutputType)); - } - else - _strlcpy(h->OutputType, "automatic", sizeof(h->OutputType)); - - if (pwg_raster) - { - // Set "reserved" fields to 0 - h->AdvanceDistance = 0; - h->AdvanceMedia = CUPS_ADVANCE_NONE; - h->Collate = CUPS_FALSE; - } - else - { - // TODO - Support for advance distance and advance media - h->AdvanceDistance = 0; - h->AdvanceMedia = CUPS_ADVANCE_NONE; - if ((val = cupsGetOption("Collate", num_options, options)) != NULL || - (val = cupsGetOption("multiple-document-handling", - num_options, options)) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "multiple-document-handling")) != - NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes") || - !strcasecmp(val, "separate-documents-collated-copies")) - h->Collate = CUPS_TRUE; - else - h->Collate = CUPS_FALSE; - } - else - h->Collate = CUPS_FALSE; - } - - h->CutMedia = CUPS_CUT_NONE; - - h->Tumble = CUPS_FALSE; - if ((val = cupsGetOption("sides", num_options, options)) != NULL || - (val = cupsGetOption("Duplex", num_options, options)) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "sides")) != NULL) - { - if (!strcasecmp(val, "None") || !strcasecmp(val, "Off") || - !strcasecmp(val, "False") || !strcasecmp(val, "No") || - !strcasecmp(val, "one-sided") || !strcasecmp(val, "OneSided")) - h->Duplex = CUPS_FALSE; - else if (!strcasecmp(val, "On") || - !strcasecmp(val, "True") || !strcasecmp(val, "Yes") || - !strncasecmp(val, "two-sided", 9) || - !strncasecmp(val, "TwoSided", 8) || - !strncasecmp(val, "Duplex", 6)) - { - h->Duplex = CUPS_TRUE; - if (!strncasecmp(val, "DuplexTumble", 12) || - strcasestr(val, "short-edge")) - h->Tumble = CUPS_TRUE; - if (!strncasecmp(val, "DuplexNoTumble", 14) || - strcasestr(val, "long-edge")) - h->Tumble = CUPS_FALSE; - } - else - h->Duplex = CUPS_FALSE; - } - else - h->Duplex = CUPS_FALSE; - - if ((val = cupsGetOption("printer-resolution", num_options, - options)) != NULL || - (val = cupsGetOption("Resolution", num_options, options)) != NULL) - { - int xres, // X resolution - yres; // Y resolution - char *ptr; // Pointer into value - - xres = yres = strtol(val, (char **)&ptr, 10); - if (ptr > val && xres > 0) - { - if (*ptr == 'x') - yres = strtol(ptr + 1, (char **)&ptr, 10); - } - - if (ptr > val && xres > 0 && yres > 0 && ptr && - (*ptr == '\0' || - !strcasecmp(ptr, "dpi") || - !strcasecmp(ptr, "dpc") || - !strcasecmp(ptr, "dpcm"))) - { - if (!strcasecmp(ptr, "dpc") || - !strcasecmp(ptr, "dpcm")) - { - xres = xres * 254 / 100; - yres = yres * 254 / 100; - } - h->HWResolution[0] = xres; - h->HWResolution[1] = yres; - } - else - { - h->HWResolution[0] = 100; // Resolution invalid - h->HWResolution[1] = 100; - } - } - else - { - h->HWResolution[0] = 100; // Resolution invalid - h->HWResolution[1] = 100; - } - - // Resolution from IPP attrs - if (h->HWResolution[0] == 100 && h->HWResolution[1] == 100) - { - int x = 0, y = 0; - cfIPPAttrResolutionForPrinter(data->printer_attrs, data->job_attrs, - NULL, &x, &y); - if (x && y) - { - h->HWResolution[0] = x; - h->HWResolution[1] = y; - } - } - - // TODO - Support for insert sheets - h->InsertSheet = CUPS_FALSE; - - // TODO - Support for jog - h->Jog = CUPS_JOG_NONE; - - if ((val = cupsGetOption("feed-orientation", num_options, - options)) != NULL || - (val = cupsGetOption("feed-direction", num_options, options)) != NULL || - (val = cupsGetOption("LeadingEdge", num_options, options)) != NULL) - { - if (!strcasecmp(val, "ShortEdgeFirst")) - h->LeadingEdge = CUPS_EDGE_TOP; - else if (!strcasecmp(val, "LongEdgeFirst")) - h->LeadingEdge = CUPS_EDGE_RIGHT; - } - else - h->LeadingEdge = CUPS_EDGE_TOP; - - // TODO - Support for manual feed - h->ManualFeed = CUPS_FALSE; - - if ((val = cupsGetOption("media-position", num_options, options)) != NULL || - (val = cupsGetOption("MediaPosition", num_options, options)) != NULL || - (val = cupsGetOption("media-source", num_options, options)) != NULL || - (val = cupsGetOption("MediaSource", num_options, options)) != NULL || - (val = cupsGetOption("InputSlot", num_options, options)) != NULL || - (val = media_source) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "media-source")) != NULL) - { - if (!strncasecmp(val, "Auto", 4) || - !strncasecmp(val, "Default", 7)) - h->MediaPosition = 0; - else if (!strcasecmp(val, "Main")) - h->MediaPosition = 1; - else if (!strcasecmp(val, "Alternate")) - h->MediaPosition = 2; - else if (!strcasecmp(val, "LargeCapacity") || - !strcasecmp(val, "large-capacity")) - h->MediaPosition = 3; - else if (!strcasecmp(val, "Manual")) - h->MediaPosition = 4; - else if (!strcasecmp(val, "Envelope")) - h->MediaPosition = 5; - else if (!strcasecmp(val, "Disc")) - h->MediaPosition = 6; - else if (!strcasecmp(val, "Photo")) - h->MediaPosition = 7; - else if (!strcasecmp(val, "Hagaki")) - h->MediaPosition = 8; - else if (!strcasecmp(val, "MainRoll") || - !strcasecmp(val, "main-roll")) - h->MediaPosition = 9; - else if (!strcasecmp(val, "AlternateRoll") || - !strcasecmp(val, "alternate-roll")) - h->MediaPosition = 10; - else if (!strcasecmp(val, "Top")) - h->MediaPosition = 11; - else if (!strcasecmp(val, "Middle")) - h->MediaPosition = 12; - else if (!strcasecmp(val, "Bottom")) - h->MediaPosition = 13; - else if (!strcasecmp(val, "Side")) - h->MediaPosition = 14; - else if (!strcasecmp(val, "Left")) - h->MediaPosition = 15; - else if (!strcasecmp(val, "Right")) - h->MediaPosition = 16; - else if (!strcasecmp(val, "Center")) - h->MediaPosition = 17; - else if (!strcasecmp(val, "Rear")) - h->MediaPosition = 18; - else if (!strcasecmp(val, "ByPassTray") || - !strcasecmp(val, "bypass-tray")) - h->MediaPosition = 19; - else if (!strcasecmp(val, "Tray1") || - !strcasecmp(val, "tray-1")) - h->MediaPosition = 20; - else if (!strcasecmp(val, "Tray2") || - !strcasecmp(val, "tray-2")) - h->MediaPosition = 21; - else if (!strcasecmp(val, "Tray3") || - !strcasecmp(val, "tray-3")) - h->MediaPosition = 22; - else if (!strcasecmp(val, "Tray4") || - !strcasecmp(val, "tray-4")) - h->MediaPosition = 23; - else if (!strcasecmp(val, "Tray5") || - !strcasecmp(val, "tray-5")) - h->MediaPosition = 24; - else if (!strcasecmp(val, "Tray6") || - !strcasecmp(val, "tray-6")) - h->MediaPosition = 25; - else if (!strcasecmp(val, "Tray7") || - !strcasecmp(val, "tray-7")) - h->MediaPosition = 26; - else if (!strcasecmp(val, "Tray8") || - !strcasecmp(val, "tray-8")) - h->MediaPosition = 27; - else if (!strcasecmp(val, "Tray9") || - !strcasecmp(val, "tray-9")) - h->MediaPosition = 28; - else if (!strcasecmp(val, "Tray10") || - !strcasecmp(val, "tray-10")) - h->MediaPosition = 29; - else if (!strcasecmp(val, "Tray11") || - !strcasecmp(val, "tray-11")) - h->MediaPosition = 30; - else if (!strcasecmp(val, "Tray12") || - !strcasecmp(val, "tray-12")) - h->MediaPosition = 31; - else if (!strcasecmp(val, "Tray13") || - !strcasecmp(val, "tray-13")) - h->MediaPosition = 32; - else if (!strcasecmp(val, "Tray14") || - !strcasecmp(val, "tray-14")) - h->MediaPosition = 33; - else if (!strcasecmp(val, "Tray15") || - !strcasecmp(val, "tray-15")) - h->MediaPosition = 34; - else if (!strcasecmp(val, "Tray16") || - !strcasecmp(val, "tray-16")) - h->MediaPosition = 35; - else if (!strcasecmp(val, "Tray17") || - !strcasecmp(val, "tray-17")) - h->MediaPosition = 36; - else if (!strcasecmp(val, "Tray18") || - !strcasecmp(val, "tray-18")) - h->MediaPosition = 37; - else if (!strcasecmp(val, "Tray19") || - !strcasecmp(val, "tray-19")) - h->MediaPosition = 38; - else if (!strcasecmp(val, "Tray20") || - !strcasecmp(val, "tray-20")) - h->MediaPosition = 39; - else if (!strcasecmp(val, "Roll1") || - !strcasecmp(val, "roll-1")) - h->MediaPosition = 40; - else if (!strcasecmp(val, "Roll2") || - !strcasecmp(val, "roll-2")) - h->MediaPosition = 41; - else if (!strcasecmp(val, "Roll3") || - !strcasecmp(val, "roll-3")) - h->MediaPosition = 42; - else if (!strcasecmp(val, "Roll4") || - !strcasecmp(val, "roll-4")) - h->MediaPosition = 43; - else if (!strcasecmp(val, "Roll5") || - !strcasecmp(val, "roll-5")) - h->MediaPosition = 44; - else if (!strcasecmp(val, "Roll6") || - !strcasecmp(val, "roll-6")) - h->MediaPosition = 45; - else if (!strcasecmp(val, "Roll7") || - !strcasecmp(val, "roll-7")) - h->MediaPosition = 46; - else if (!strcasecmp(val, "Roll8") || - !strcasecmp(val, "roll-8")) - h->MediaPosition = 47; - else if (!strcasecmp(val, "Roll9") || - !strcasecmp(val, "roll-9")) - h->MediaPosition = 48; - else if (!strcasecmp(val, "Roll10") || - !strcasecmp(val, "roll-10")) - h->MediaPosition = 49; - } - else - h->MediaPosition = 0; // Auto - - if ((val = cupsGetOption("media-weight", num_options, options)) != NULL || - (val = cupsGetOption("MediaWeight", num_options, options)) != NULL || - (val = cupsGetOption("media-weight-metric", num_options, - options)) != NULL || - (val = cupsGetOption("MediaWeightMetric", num_options, options)) != NULL) - h->MediaWeight = atol(val); - else - h->MediaWeight = 0; - - if (pwg_raster) - { - // Set "reserved" fields to 0 - h->MirrorPrint = CUPS_FALSE; - h->NegativePrint = CUPS_FALSE; - } - else - { - if ((val = cupsGetOption("mirror-print", num_options, options)) != NULL || - (val = cupsGetOption("MirrorPrint", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes")) - h->MirrorPrint = CUPS_TRUE; - else if (!strcasecmp(val, "false") || - !strcasecmp(val, "off") || - !strcasecmp(val, "no")) - h->MirrorPrint = CUPS_FALSE; - else - h->MirrorPrint = CUPS_FALSE; - } - if ((val = cupsGetOption("negative-print", num_options, options)) != NULL || - (val = cupsGetOption("NegativePrint", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes")) - h->NegativePrint = CUPS_TRUE; - else if (!strcasecmp(val, "false") || - !strcasecmp(val, "off") || - !strcasecmp(val, "no")) - h->NegativePrint = CUPS_FALSE; - else - h->NegativePrint = CUPS_FALSE; - } - } - - i = 0; - if ((val = cupsGetOption("copies", num_options, options)) != NULL || - (val = cupsGetOption("Copies", num_options, options)) != NULL || - (val = cupsGetOption("num-copies", num_options, options)) != NULL || - (val = cupsGetOption("NumCopies", num_options, options)) != NULL || - cfIPPAttrIntValForPrinter(data->printer_attrs, data->job_attrs, - "copies", &i) == 1) - { - if (val) - h->NumCopies = atol(val); - else if (i) - h->NumCopies = i; - } - else - h->NumCopies = 1; // 0 = Printer default - - if ((val = cupsGetOption("orientation-requested", num_options, - options)) != NULL || - (val = cupsGetOption("OrientationRequested", num_options, - options)) != NULL || - (val = cupsGetOption("Orientation", num_options, options)) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "orientation-requested")) != NULL) - { - if (!strcasecmp(val, "Portrait") || - !strcasecmp(val, "3") || - !strcasecmp(val, "0")) - h->Orientation = CUPS_ORIENT_0; - else if (!strcasecmp(val, "Landscape") || - !strcasecmp(val, "4")) - h->Orientation = CUPS_ORIENT_90; - else if (!strcasecmp(val, "reverse-portrait") || - !strcasecmp(val, "ReversePortrait") || - !strcasecmp(val, "5")) - h->Orientation = CUPS_ORIENT_180; - else if (!strcasecmp(val, "reverse-landscape") || - !strcasecmp(val, "ReverseLandscape") || - !strcasecmp(val, "6")) - h->Orientation = CUPS_ORIENT_270; - else - h->Orientation = CUPS_ORIENT_0; - } - else - h->Orientation = CUPS_ORIENT_0; - - if (pwg_raster) - { - // Set "reserved" fields to 0 - h->OutputFaceUp = CUPS_FALSE; - } - else - { - if ((val = cupsGetOption("OutputFaceUp", num_options, options)) != NULL || - (val = cupsGetOption("output-face-up", num_options, options)) != NULL || - (val = cupsGetOption("OutputBin", num_options, options)) != NULL || - (val = cupsGetOption("output-bin", num_options, options)) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "output-bin")) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes") || !strcasecmp(val, "face-up") || - !strcasecmp(val, "FaceUp")) - h->OutputFaceUp = CUPS_TRUE; - else if (!strcasecmp(val, "false") || !strcasecmp(val, "off") || - !strcasecmp(val, "no") || !strcasecmp(val, "face-down") || - !strcasecmp(val, "FaceDown")) - h->OutputFaceUp = CUPS_FALSE; - else - h->OutputFaceUp = CUPS_FALSE; - } - } - - if (pwg_raster) - { - // Set "reserved" fields to 0 - h->Separations = CUPS_FALSE; - h->TraySwitch = CUPS_FALSE; - } - else - { - if ((val = cupsGetOption("separations", num_options, options)) != NULL || - (val = cupsGetOption("Separations", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes")) - h->Separations = CUPS_TRUE; - else if (!strcasecmp(val, "false") || - !strcasecmp(val, "off") || - !strcasecmp(val, "no")) - h->Separations = CUPS_FALSE; - else - h->Separations = CUPS_FALSE; - } - if ((val = cupsGetOption("tray-switch", num_options, options)) != NULL || - (val = cupsGetOption("TraySwitch", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes")) - h->TraySwitch = CUPS_TRUE; - else if (!strcasecmp(val, "false") || - !strcasecmp(val, "off") || - !strcasecmp(val, "no")) - h->TraySwitch = CUPS_FALSE; - else - h->TraySwitch = CUPS_FALSE; - } - } - - if ((val = cupsGetOption("Tumble", num_options, options)) != NULL) - { - if (!strcasecmp(val, "Off") || !strcasecmp(val, "False") || - !strcasecmp(val, "No")) - h->Tumble = CUPS_FALSE; - else if (!strcasecmp(val, "On") || !strcasecmp(val, "True") || - !strcasecmp(val, "Yes")) - h->Tumble = CUPS_TRUE; - } - - // TODO - Support for MediaType number - h->cupsMediaType = 0; - - // Only for CUPS Raster, if we do not have a sample header from a PPD file - if (pwg_raster == 0 && - ((val = cupsGetOption("pwg-raster-document-type", num_options, - options)) != NULL || - (val = cupsGetOption("PwgRasterDocumentType", num_options, - options)) != NULL || - (val = cupsGetOption("color-space", num_options, options)) != NULL || - (val = cupsGetOption("ColorSpace", num_options, options)) != NULL || - (val = cupsGetOption("color-model", num_options, options)) != NULL || - (val = cupsGetOption("ColorModel", num_options, options)) != NULL || - (val = cupsGetOption("print-color-mode", num_options, options)) != - NULL || - (val = cupsGetOption("output-mode", num_options, options)) != NULL || - (val = cupsGetOption("OutputMode", num_options, options)) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "print-color-mode")) != NULL)) - { - int bitspercolor, // Bits per color - bitsperpixel, // Bits per pixel - colorspace, // CUPS/PWG raster color space - numcolors; // Number of colorants - const char *ptr; // Pointer into value - - ptr = NULL; - numcolors = 0; - bitspercolor = 8; - if (!strncasecmp(val, "AdobeRgb", 8)) - { - if (*(val + 8) == '_' || *(val + 8) == '-') - ptr = val + 9; - colorspace = 20; - numcolors = 3; - } - else if (!strncasecmp(val, "adobe-rgb", 9)) - { - if (*(val + 9) == '_' || *(val + 9) == '-') - ptr = val + 10; - colorspace = 20; - numcolors = 3; - } - else if (!strcasecmp(val, "auto-monochrome")) - { - colorspace = 18; - numcolors = 1; - } - else if (!strcasecmp(val, "bi-level") || - !strcasecmp(val, "process-bi-level")) - { - bitspercolor = 1; - colorspace = 3; - numcolors = 1; - } - else if (!strncasecmp(val, "Black", 5)) - { - if (*(val + 5) == '_' || *(val + 5) == '-') - ptr = val + 6; - bitspercolor = 1; - colorspace = 3; - numcolors = 1; - } - else if (!strcasecmp(val, "process-monochrome")) - { - colorspace = 18; - numcolors = 1; - } - else if (!strncasecmp(val, "Monochrome", 10)) - { - if (*(val + 10) == '_' || *(val + 10) == '-') - ptr = val + 11; - colorspace = 18; - numcolors = 1; - } - else if (!strncasecmp(val, "Mono", 4)) - { - if (*(val + 4) == '_' || *(val + 4) == '-') - ptr = val + 5; - colorspace = 18; - numcolors = 1; - } - else if (!strcasecmp(val, "color")) - { - colorspace = 19; - numcolors = 3; - } - else if (!strncasecmp(val, "Cmyk", 4)) - { - if (*(val + 4) == '_' || *(val + 4) == '-') - ptr = val + 5; - colorspace = 6; - numcolors = 4; - } - else if (!strncasecmp(val, "Cmy", 3)) - { - if (*(val + 3) == '_' || *(val + 3) == '-') - ptr = val + 4; - colorspace = 4; - numcolors = 3; - } - else if (!strncasecmp(val, "Device", 6)) - { - ptr = val + 6; - numcolors = strtol(ptr, (char **)&ptr, 10); - if (*ptr == '_' || *ptr == '-') - { - ptr ++; - colorspace = 47 + numcolors; - } - else - { - numcolors = 0; - ptr = NULL; - } - } - else if (!strncasecmp(val, "Sgray", 5)) - { - if (*(val + 5) == '_' || *(val + 5) == '-') - ptr = val + 6; - colorspace = 18; - numcolors = 1; - } - else if (!strncasecmp(val, "Gray", 4)) - { - if (*(val + 4) == '_' || *(val + 4) == '-') - ptr = val + 5; - colorspace = 18; - numcolors = 1; - } - else if (!strncasecmp(val, "Srgb", 4)) - { - if (*(val + 4) == '_' || *(val + 4) == '-') - ptr = val + 5; - colorspace = 19; - numcolors = 3; - } - else if (!strncasecmp(val, "Rgbw", 4)) - { - if (*(val + 4) == '_' || *(val + 4) == '-') - ptr = val + 5; - colorspace = 17; - numcolors = 4; - } - else if (!strncasecmp(val, "Rgb", 3)) - { - if (*(val + 3) == '_' || *(val + 3) == '-') - ptr = val + 4; - colorspace = 1; - numcolors = 3; - } - else if (!strcasecmp(val, "auto")) - { - // Let "auto" not look like an error - colorspace = 19; - numcolors = 3; - } - if (numcolors > 0) - { - if (ptr) - bitspercolor = strtol(ptr, (char **)&ptr, 10); - bitsperpixel = bitspercolor * numcolors; - // In 1-bit-per-color RGB modes we add a forth bit to each pixel - // to align the pixels with bytes - if (bitsperpixel == 3 && - strcasestr(val, "Rgb")) - bitsperpixel = 4; - h->cupsBitsPerColor = bitspercolor; - h->cupsBitsPerPixel = bitsperpixel; - h->cupsColorSpace = colorspace; - h->cupsNumColors = numcolors; - } - else - { - h->cupsBitsPerColor = 8; - h->cupsBitsPerPixel = 24; - h->cupsColorSpace = 19; - h->cupsNumColors = 3; - } - } - else - { - h->cupsBitsPerColor = 8; - h->cupsBitsPerPixel = 24; - h->cupsColorSpace = 19; - h->cupsNumColors = 3; - } - - // TODO - Support for color orders 1 (banded) and 2 (planar) - h->cupsColorOrder = 0; - - // TODO - Support for these parameters - h->cupsCompression = 0; - h->cupsRowCount = 0; - h->cupsRowFeed = 0; - h->cupsRowStep = 0; - - // TODO - Support for cupsBorderlessScalingFactor - h->cupsBorderlessScalingFactor = 0.0; - - // TODO - Support for custom values in CUPS Raster mode - for (i = 0; i < 16; i ++) - { - h->cupsInteger[i] = 0; - h->cupsReal[i] = 0.0; - memset(h->cupsString[i], 0, 64); - } - - if (pwg_raster) - { - - if ((val = cupsGetOption("job-impressions", num_options, - options)) != NULL || - (val = cupsGetOption("JobImpressions", num_options, options)) != NULL || - (val = cupsGetOption("Impressions", num_options, options)) != NULL) - { - int impressions = atoi(val); - if (impressions >= 0) - h->cupsInteger[0] = impressions; - } - - // Printer property, command line options only for development and - // debugging - if ((val = cupsGetOption("pwg-raster-document-sheet-back", num_options, - options)) != NULL || - (val = cupsGetOption("back-side-orientation", num_options, - options)) != NULL || - (data->printer_attrs && - ((attr = ippFindAttribute(data->printer_attrs, "urf-supported", - IPP_TAG_ZERO)) != NULL || - (attr = ippFindAttribute(data->printer_attrs, - "pwg-raster-document-sheet-back", - IPP_TAG_ZERO)) != NULL || - (attr = ippFindAttribute(data->printer_attrs, - "pclm-raster-back-side", - IPP_TAG_ZERO)) != NULL))) - { - if (val == NULL) - { - if (ippGetCount(attr) > 1) // urf-supported - { - for (i = 0; i < ippGetCount(attr); i ++) - { - val = ippGetString(attr, i, NULL); - if (strncmp(val, "DM", 2) == 0) // Duplex mode field - break; - } - if (i == ippGetCount(attr)) // Not found, no duplex - val = "DM1"; - } - else // pwg-raster-document-sheet-back/pclm-raster-back-side - val = ippGetString(attr, 0, NULL); - } - // Set CrossFeedTransform and FeedTransform - if (h->Duplex == CUPS_FALSE) - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - else if (h->Duplex == CUPS_TRUE) - { - if (h->Tumble == CUPS_FALSE) - { - if (!strcasecmp(val, "Flipped") || - !strcasecmp(val, "DM2")) - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = -1; - } - else if (!strncasecmp(val, "Manual", 6) || - !strcasecmp(val, "DM4")) - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - else if (!strcasecmp(val, "Normal") || - !strcasecmp(val, "DM1")) - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - else if (!strcasecmp(val, "Rotated") || - !strcasecmp(val, "DM3")) - { - h->cupsInteger[1] = -1; - h->cupsInteger[2] = -1; - } - else - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - } - else - { - if (!strcasecmp(val, "Flipped") || - !strcasecmp(val, "DM2")) - { - h->cupsInteger[1] = -1; - h->cupsInteger[2] = 1; - } - else if (!strncasecmp(val, "Manual", 6) || - !strcasecmp(val, "DM4")) - { - h->cupsInteger[1] = -1; - h->cupsInteger[2] = -1; - } - else if (!strcasecmp(val, "Normal") || - !strcasecmp(val, "DM1")) - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - else if (!strcasecmp(val, "Rotated") || - !strcasecmp(val, "DM3")) - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - else - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - } - } - else - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - } - else - { - h->cupsInteger[1] = 1; - h->cupsInteger[2] = 1; - } - - // TODO - Support for ImageBoxLeft, ImageBoxTop, ImageBoxRight, and - // ImageBoxBottom (h->cupsInteger[3..6]), leave on 0 for now - - if ((val = cupsGetOption("alternate-primary", num_options, - options)) != NULL || - (val = cupsGetOption("AlternatePrimary", num_options, - options)) != NULL) - { - int alternateprimary = atoi(val); // SRGB value for black - // pixels - h->cupsInteger[7] = alternateprimary; - } - - if ((val = cupsGetOption("print-quality", num_options, options)) != NULL || - (val = cupsGetOption("PrintQuality", num_options, options)) != NULL || - (val = cupsGetOption("Quality", num_options, options)) != NULL) - { - int quality = atoi(val); // print-quality value - - if (!quality || - (quality >= IPP_QUALITY_DRAFT && quality <= IPP_QUALITY_HIGH)) - h->cupsInteger[8] = quality; - } - - // Leave "reserved" fields (h->cupsInteger[9..13]) on 0 - - if ((val = cupsGetOption("vendor-identifier", num_options, - options)) != NULL || - (val = cupsGetOption("VendorIdentifier", num_options, - options)) != NULL) - { - int vendorid = atoi(val); // USB ID of manufacturer - h->cupsInteger[14] = vendorid; - } - - if ((val = cupsGetOption("vendor-length", num_options, - options)) != NULL || - (val = cupsGetOption("VendorLength", num_options, - options)) != NULL) - { - int vendorlength = atoi(val); // How many bytes of vendor - // data? - if (vendorlength > 0 && vendorlength <= 1088) - { - h->cupsInteger[15] = vendorlength; - if ((val = cupsGetOption("vendor-data", num_options, - options)) != NULL || - (val = cupsGetOption("VendorData", num_options, - options)) != NULL) - // TODO - How to enter binary data here? - _strlcpy((char *)&(h->cupsReal[0]), val, 1088); - } - } - } - - // Set "reserved" fields to 0 - memset(h->cupsMarkerType, 0, 64); - - if ((val = cupsGetOption("print-rendering-intent", num_options, - options)) != NULL || - (val = cupsGetOption("PrintRenderingIntent", num_options, - options)) != NULL || - (val = cupsGetOption("RenderingIntent", num_options, - options)) != NULL || - (val = cfIPPAttrEnumValForPrinter(data->printer_attrs, - data->job_attrs, - "print-rendering-intent")) != NULL) - { - if (!strcmp(val, "absolute")) - _strlcpy(h->cupsRenderingIntent, "Absolute", - sizeof(h->cupsRenderingIntent)); - else if (!strcmp(val, "automatic")) - _strlcpy(h->cupsRenderingIntent, "Automatic", - sizeof(h->cupsRenderingIntent)); - else if (!strcmp(val, "perceptual")) - _strlcpy(h->cupsRenderingIntent, "Perceptual", - sizeof(h->cupsRenderingIntent)); - else if (!strcmp(val, "relative")) - _strlcpy(h->cupsRenderingIntent, "Relative", - sizeof(h->cupsRenderingIntent)); - else if (!strcmp(val, "relative-bpc") || - !strcmp(val, "RelativeBpc")) - _strlcpy(h->cupsRenderingIntent, "RelativeBpc", - sizeof(h->cupsRenderingIntent)); - else if (!strcmp(val, "saturation")) - _strlcpy(h->cupsRenderingIntent, "Saturation", - sizeof(h->cupsRenderingIntent)); - } - else - h->cupsRenderingIntent[0] = '\0'; - - if (media_source != NULL) - free(media_source); - if (media_type != NULL) - free(media_type); - cupsFreeOptions(num_options, options); - - return (0); -} diff --git a/cupsfilters/raster.h b/cupsfilters/raster.h deleted file mode 100644 index 9c987258d..000000000 --- a/cupsfilters/raster.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// Functions to handle CUPS/PWG Raster headers for libcupsfilters. -// -// Copyright 2013-2022 by Till Kamppeter. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_RASTER_H_ -# define _CUPS_FILTERS_RASTER_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Include necessary headers... -// - -# include "filter.h" -# include -# include -# include -# include - -# if defined(WIN32) || defined(__EMX__) -# include -# else -# include -# include -# endif // WIN32 || __EMX__ - -# include -# include - - -// -// Prototypes... -// - -extern const char *cfRasterColorSpaceString(cups_cspace_t cspace); -extern int cfRasterPrepareHeader(cups_page_header2_t *h, - cf_filter_data_t *data, - cf_filter_out_format_t - final_outformat, - cf_filter_out_format_t - header_outformat, - int no_high_depth, - cups_cspace_t *cspace); -extern int cfRasterSetColorSpace(cups_page_header2_t *h, - const char *available, - const char *color_mode, - cups_cspace_t *cspace, - int *high_depth); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_CUPS_FILTERS_RASTER_H_ diff --git a/cupsfilters/rastertopwg.c b/cupsfilters/rastertopwg.c deleted file mode 100644 index 298a13228..000000000 --- a/cupsfilters/rastertopwg.c +++ /dev/null @@ -1,607 +0,0 @@ -// -// CUPS Raster to PWG/Apple Raster format filter for libcupsfilters. -// -// Copyright © 2011, 2014-2017 Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... - - -#include "filter.h" -#include "raster.h" -#include "ipp.h" -#include -#include -#include -#include - - -// -// 'cfFilterRasterToPWG()' - Filter function to convert CUPS Raster -// into PWG or Apple Raster - -int // O - Exit status -cfFilterRasterToPWG(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - cups_raster_t *inras; // Input raster stream - cups_raster_t *outras; // Output raster stream - cups_page_header2_t inheader, // Input raster page header - outheader; // Output raster page header - unsigned y; // Current line - unsigned char *line; // Line buffer - unsigned page = 0, // Current page - page_width, // Actual page width - page_height, // Actual page height - page_top, // Top margin - page_bottom, // Bottom margin - page_left, // Left margin - page_right, // Right margin - linesize, // Bytes per line - lineoffset; // Offset into line - int tmp; - unsigned char white; // White pixel - int back; // Back side orientation - char buf[64]; - int width, - height, - left, - right, - bottom, - top; - int num_options = 0;// Number of options - cups_option_t *options = NULL;// Options - const char *val; // Option value - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - int res = 0; - - - val = data->final_content_type; - if (val) - { - if (strcasestr(val, "pwg") || strcasestr(val, "pclm")) - outras = cupsRasterOpen(outputfd, CUPS_RASTER_WRITE_PWG); - else if (strcasestr(val, "urf")) - outras = cupsRasterOpen(outputfd, CUPS_RASTER_WRITE_APPLE); - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Invalid output format specified. Only PWG Raster, Apple Raster/URF, and PCLm are supported."); - - close(inputfd); - close(outputfd); - return (1); - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "cfFilterRasterToPWG: Output format not specified, defaulting to PWG Raster."); - - outras = cupsRasterOpen(outputfd, CUPS_RASTER_WRITE_PWG); - } - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - inras = cupsRasterOpen(inputfd, CUPS_RASTER_READ); - - while (cupsRasterReadHeader2(inras, &inheader)) - { - if (iscanceled && iscanceled(icd)) - { - // Canceled - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Job canceled on input page %d", page + 1); - } - - // - // Show page device dictionary... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Duplex = %d", inheader.Duplex); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: HWResolution = [ %d %d ]", - inheader.HWResolution[0], inheader.HWResolution[1]); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: ImagingBoundingBox = [ %d %d %d %d ]", - inheader.ImagingBoundingBox[0], - inheader.ImagingBoundingBox[1], - inheader.ImagingBoundingBox[2], - inheader.ImagingBoundingBox[3]); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Margins = [ %d %d ]", - inheader.Margins[0], inheader.Margins[1]); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: ManualFeed = %d", inheader.ManualFeed); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: MediaPosition = %d", - inheader.MediaPosition); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: NumCopies = %d", inheader.NumCopies); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Orientation = %d", inheader.Orientation); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: PageSize = [ %d %d ]", - inheader.PageSize[0], inheader.PageSize[1]); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsWidth = %d", inheader.cupsWidth); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsHeight = %d", inheader.cupsHeight); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsMediaType = %d", - inheader.cupsMediaType); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsBitsPerColor = %d", - inheader.cupsBitsPerColor); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsBitsPerPixel = %d", - inheader.cupsBitsPerPixel); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsBytesPerLine = %d", - inheader.cupsBytesPerLine); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsColorOrder = %d", - inheader.cupsColorOrder); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsColorSpace = %d", - inheader.cupsColorSpace); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: cupsCompression = %d", - inheader.cupsCompression); - - // - // Compute the real raster size... - // - - page ++; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: %d %d\n", page, inheader.NumCopies); - - page_width = (unsigned)(inheader.cupsPageSize[0] * - inheader.HWResolution[0] / 72.0); - if (page_width < inheader.cupsWidth && - page_width >= inheader.cupsWidth - 1) - page_width = (unsigned)inheader.cupsWidth; - page_height = (unsigned)(inheader.cupsPageSize[1] * - inheader.HWResolution[1] / 72.0); - if (page_height < inheader.cupsHeight && - page_height >= inheader.cupsHeight - 1) - page_height = (unsigned)inheader.cupsHeight; - page_left = (unsigned)(inheader.cupsImagingBBox[0] * - inheader.HWResolution[0] / 72.0); - page_bottom = (unsigned)(inheader.cupsImagingBBox[1] * - inheader.HWResolution[1] / 72.0); - tmp = (int)(page_height - page_bottom - inheader.cupsHeight); - if (tmp < 0 && tmp >= -1) // Rounding error - page_top = 0; - else - page_top = (unsigned)tmp; - tmp = (int)(page_width - page_left - inheader.cupsWidth); - if (tmp < 0 && tmp >= -1) // Rounding error - page_right = 0; - else - page_right = (unsigned)tmp; - linesize = (page_width * inheader.cupsBitsPerPixel + 7) / 8; - lineoffset = page_left * inheader.cupsBitsPerPixel / 8; // Round down - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: In pixels: Width: %u Height: %u Left: %u Right: %u Top: %u Bottom: %u", - page_width, page_height, - page_left, page_right, page_top, page_bottom); - if (page_left > page_width || page_top > page_height || - page_bottom > page_height || page_right > page_width) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Unsupported raster data."); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Bad bottom/left/top margin on page %d.", page); - res = 1; - goto fail; - } - - switch (inheader.cupsColorSpace) - { - case CUPS_CSPACE_W : - case CUPS_CSPACE_RGB : - case CUPS_CSPACE_SW : - case CUPS_CSPACE_SRGB : - case CUPS_CSPACE_ADOBERGB : - white = 255; - break; - - case CUPS_CSPACE_K : - case CUPS_CSPACE_CMYK : - case CUPS_CSPACE_DEVICE1 : - case CUPS_CSPACE_DEVICE2 : - case CUPS_CSPACE_DEVICE3 : - case CUPS_CSPACE_DEVICE4 : - case CUPS_CSPACE_DEVICE5 : - case CUPS_CSPACE_DEVICE6 : - case CUPS_CSPACE_DEVICE7 : - case CUPS_CSPACE_DEVICE8 : - case CUPS_CSPACE_DEVICE9 : - case CUPS_CSPACE_DEVICEA : - case CUPS_CSPACE_DEVICEB : - case CUPS_CSPACE_DEVICEC : - case CUPS_CSPACE_DEVICED : - case CUPS_CSPACE_DEVICEE : - case CUPS_CSPACE_DEVICEF : - white = 0; - break; - - default : - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Unsupported raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unsupported cupsColorSpace %d on page %d.", - inheader.cupsColorSpace, page); - res = 1; - goto fail; - } - - if (inheader.cupsColorOrder != CUPS_ORDER_CHUNKED) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Unsupported raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unsupported cupsColorOrder %d on page %d.", - inheader.cupsColorOrder, page); - res = 1; - goto fail; - } - - if (inheader.cupsBitsPerPixel != 1 && - inheader.cupsBitsPerColor != 8 && inheader.cupsBitsPerColor != 16) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Unsupported raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unsupported cupsBitsPerColor %d on page %d.", - inheader.cupsBitsPerColor, page); - res = 1; - goto fail; - } - - memcpy(&outheader, &inheader, sizeof(outheader)); - outheader.cupsWidth = page_width; - outheader.cupsHeight = page_height; - outheader.cupsBytesPerLine = linesize; - - outheader.cupsInteger[14] = 0; // VendorIdentifier - outheader.cupsInteger[15] = 0; // VendorLength - - if ((val = cupsGetOption("print-content-optimize", num_options, - options)) != NULL) - { - if (!strcmp(val, "automatic")) - strncpy(outheader.OutputType, "Automatic", - sizeof(outheader.OutputType)); - else if (!strcmp(val, "graphics") || - !strcmp(val, "graphic")) - strncpy(outheader.OutputType, "Graphics", sizeof(outheader.OutputType)); - else if (!strcmp(val, "photo")) - strncpy(outheader.OutputType, "Photo", sizeof(outheader.OutputType)); - else if (!strcmp(val, "text")) - strncpy(outheader.OutputType, "Text", sizeof(outheader.OutputType)); - else if (!strcmp(val, "text-and-graphics") || - !strcmp(val, "text-and-graphic")) - strncpy(outheader.OutputType, "TextAndGraphics", - sizeof(outheader.OutputType)); - else - { - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unsupported print-content-optimize value."); - outheader.OutputType[0] = '\0'; - } - } - - if ((val = cupsGetOption("print-quality", - num_options, options)) != NULL) - { - unsigned quality = (unsigned)atoi(val); // print-quality value - - if (quality >= IPP_QUALITY_DRAFT && quality <= IPP_QUALITY_HIGH) - outheader.cupsInteger[8] = quality; - else - { - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unsupported print-quality %d.", quality); - outheader.cupsInteger[8] = 0; - } - } - - // Update rendering intent with user settings or the default - outheader.cupsRenderingIntent[0] = '\0'; - cfGetPrintRenderIntent(data, outheader.cupsRenderingIntent, - sizeof(outheader.cupsRenderingIntent)); - - // First try to use the input page size name for the output page, - // check whether this size is supported by theprinter - buf[0] = '\0'; - if (inheader.cupsPageSizeName[0]) - { - // Take only a page size name for a page size the printer actually - // supports - snprintf(buf, sizeof(buf), "%.63s", inheader.cupsPageSizeName); - cfGenerateSizes(data->printer_attrs, CF_GEN_SIZES_SEARCH, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - buf, NULL); - } - if (buf[0]) - snprintf(outheader.cupsPageSizeName, - sizeof(outheader.cupsPageSizeName), - "%.63s", buf); - else - { - // No name found, find the printer's page size by the size dimensions - // and margins - width = (int)(2540.0 * inheader.cupsPageSize[0] / 72.0); - height = (int)(2540.0 * inheader.cupsPageSize[1] / 72.0); - left = (int)(2540.0 * inheader.ImagingBoundingBox[0] / 72.0); - bottom = (int)(2540.0 * inheader.ImagingBoundingBox[1] / 72.0); - right = width - (int)(2540.0 * inheader.ImagingBoundingBox[2] / 72.0); - top = height - (int)(2540.0 * inheader.ImagingBoundingBox[3] / 72.0); - cfGenerateSizes(data->printer_attrs, CF_GEN_SIZES_SEARCH, NULL, NULL, - &width, &height, &left, &bottom, &right, &top, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - buf, NULL); - if (buf[0]) - snprintf(outheader.cupsPageSizeName, - sizeof(outheader.cupsPageSizeName), - "%.63s", buf); - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Page size %.2fx%.2f not supported by printer.", - inheader.cupsPageSize[0], inheader.cupsPageSize[1]); - outheader.cupsPageSizeName[0] = '\0'; - } - } - - if (inheader.Duplex && !(page & 1) && - (back = cfGetBackSideOrientation(data)) > 0 && - (back &= 7) != CF_BACKSIDE_NORMAL) - { - outheader.Duplex = CUPS_TRUE; - outheader.Tumble = inheader.Tumble; - - if (back == CF_BACKSIDE_FLIPPED) - { - if (inheader.Tumble) - { - outheader.cupsInteger[1] = ~0U;// CrossFeedTransform - outheader.cupsInteger[2] = 1; // FeedTransform - - outheader.cupsInteger[3] = page_right; - // ImageBoxLeft - outheader.cupsInteger[4] = page_top; - // ImageBoxTop - outheader.cupsInteger[5] = page_width - page_left; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_bottom; - // ImageBoxBottom - } - else - { - outheader.cupsInteger[1] = 1; // CrossFeedTransform - outheader.cupsInteger[2] = ~0U;// FeedTransform - - outheader.cupsInteger[3] = page_left; - // ImageBoxLeft - outheader.cupsInteger[4] = page_bottom; - // ImageBoxTop - outheader.cupsInteger[5] = page_left + inheader.cupsWidth; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_top; - // ImageBoxBottom - } - } - else if (back == CF_BACKSIDE_MANUAL_TUMBLE) - { - if (inheader.Tumble) - { - outheader.cupsInteger[1] = ~0U;// CrossFeedTransform - outheader.cupsInteger[2] = ~0U;// FeedTransform - - outheader.cupsInteger[3] = page_width - page_left - - inheader.cupsWidth; - // ImageBoxLeft - outheader.cupsInteger[4] = page_bottom; - // ImageBoxTop - outheader.cupsInteger[5] = page_width - page_left; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_top; - // ImageBoxBottom - } - else - { - outheader.cupsInteger[1] = 1; // CrossFeedTransform - outheader.cupsInteger[2] = 1; // FeedTransform - - outheader.cupsInteger[3] = page_left; - // ImageBoxLeft - outheader.cupsInteger[4] = page_top; - // ImageBoxTop - outheader.cupsInteger[5] = page_left + inheader.cupsWidth; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_bottom; - // ImageBoxBottom - } - } - else if (back == CF_BACKSIDE_ROTATED) - { - if (inheader.Tumble) - { - outheader.cupsInteger[1] = ~0U;// CrossFeedTransform - outheader.cupsInteger[2] = ~0U;// FeedTransform - - outheader.cupsInteger[3] = page_right; - // ImageBoxLeft - outheader.cupsInteger[4] = page_bottom; - // ImageBoxTop - outheader.cupsInteger[5] = page_width - page_left; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_top; - // ImageBoxBottom - } - else - { - outheader.cupsInteger[1] = 1; // CrossFeedTransform - outheader.cupsInteger[2] = 1; // FeedTransform - - outheader.cupsInteger[3] = page_left; - // ImageBoxLeft - outheader.cupsInteger[4] = page_top; - // ImageBoxTop - outheader.cupsInteger[5] = page_left + inheader.cupsWidth; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_bottom; - // ImageBoxBottom - } - } - else - { - // - // Unsupported value... - // - - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unsupported back side orientation value."); - - outheader.cupsInteger[1] = 1; // CrossFeedTransform - outheader.cupsInteger[2] = 1; // FeedTransform - - outheader.cupsInteger[3] = page_left; - // ImageBoxLeft - outheader.cupsInteger[4] = page_top; - // ImageBoxTop - outheader.cupsInteger[5] = page_left + inheader.cupsWidth; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_bottom; - // ImageBoxBottom - } - } - else - { - outheader.Duplex = inheader.Duplex; - outheader.Tumble = inheader.Tumble; - - outheader.cupsInteger[1] = 1; // CrossFeedTransform - outheader.cupsInteger[2] = 1; // FeedTransform - - outheader.cupsInteger[3] = page_left; - // ImageBoxLeft - outheader.cupsInteger[4] = page_top; - // ImageBoxTop - outheader.cupsInteger[5] = page_left + inheader.cupsWidth; - // ImageBoxRight - outheader.cupsInteger[6] = page_height - page_bottom; - // ImageBoxBottom - } - - if (!cupsRasterWriteHeader2(outras, &outheader)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Error sending raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unable to write header for page %d.", page); - res = 1; - goto fail; - } - - // - // Copy raster data... - // - - if (linesize < inheader.cupsBytesPerLine) - linesize = inheader.cupsBytesPerLine; - - if ((lineoffset + inheader.cupsBytesPerLine) > linesize) - lineoffset = linesize - inheader.cupsBytesPerLine; - - line = malloc(linesize); - - memset(line, white, linesize); - for (y = page_top; y > 0; y --) - if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Error sending raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unable to write line %d for page %d.", - page_top - y + 1, page); - res = 1; - goto fail; - } - - for (y = inheader.cupsHeight; y > 0; y --) - { - if (cupsRasterReadPixels(inras, line + lineoffset, - inheader.cupsBytesPerLine) != - inheader.cupsBytesPerLine) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Error sending raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unable to read line %d for page %d.", - inheader.cupsHeight - y + page_top + 1, page); - res = 1; - goto fail; - } - - if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Error sending raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unable to write line %d for page %d.", - inheader.cupsHeight - y + page_top + 1, page); - res = 1; - goto fail; - } - } - - memset(line, white, linesize); - for (y = page_bottom; y > 0; y --) - if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterRasterToPWG: Error sending raster data."); - if (log) log(ld,CF_LOGLEVEL_DEBUG, - "cfFilterRasterToPWG: Unable to write line %d for page %d.", - page_bottom - y + page_top + inheader.cupsHeight + 1, - page); - res = 1; - goto fail; - } - - free(line); - } - - fail: - - cupsRasterClose(inras); - close(inputfd); - - cupsRasterClose(outras); - close(outputfd); - - cupsFreeOptions(num_options, options); - - return (res); -} diff --git a/cupsfilters/rgb.c b/cupsfilters/rgb.c deleted file mode 100644 index 5a2b88cbf..000000000 --- a/cupsfilters/rgb.c +++ /dev/null @@ -1,428 +0,0 @@ -// -// RGB color separation code for libcupsfilters. -// -// Copyright 2007 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// cfRGBDelete() - Delete a color separation. -// cfRGBDoGray() - Do a grayscale separation... -// cfRGBDoRGB() - Do a RGB separation... -// cfRGBNew() - Create a new RGB color separation. -// - -// -// Include necessary headers. -// - -#include "driver.h" - - -// -// 'cfRGBDelete()' - Delete a color separation. -// - -void -cfRGBDelete(cf_rgb_t *rgbptr) // I - Color separation -{ - if (rgbptr == NULL) - return; - - free(rgbptr->colors[0][0][0]); - free(rgbptr->colors[0][0]); - free(rgbptr->colors[0]); - free(rgbptr->colors); - free(rgbptr); -} - - -// -// 'cfRGBDoGray()' - Do a grayscale separation... -// - -void -cfRGBDoGray(cf_rgb_t *rgbptr,// I - Color separation - const unsigned char *input, // I - Input grayscale pixels - unsigned char *output,// O - Output Device-N pixels - int num_pixels) - // I - Number of pixels -{ - int i; // Looping var - int lastgray; // Previous grayscale - int xs, ys, zs, // Current RGB row offsets - g, gi, gm0, gm1;// Current gray index and - // multipliers ... - const unsigned char *color; // Current color data - int tempg; // Current separation color - int rgbsize; // Separation data size - - - // - // Range check input... - // - - if (!rgbptr || !input || !output || num_pixels <= 0) - return; - - // - // Initialize variables used for the duration of the separation... - // - - lastgray = -1; - rgbsize = rgbptr->num_channels; - xs = rgbptr->cube_size * rgbptr->cube_size * rgbptr->num_channels; - ys = rgbptr->cube_size * rgbptr->num_channels; - zs = rgbptr->num_channels; - - // - // Loop through it all... - // - - while (num_pixels > 0) - { - // - // See if the next pixel is a cached value... - // - - num_pixels --; - - g = cf_srgb_lut[*input++]; - - if (g == lastgray) - { - // - // Copy previous color and continue... - // - - memcpy(output, output - rgbptr->num_channels, rgbsize); - - output += rgbptr->num_channels; - continue; - } - else if (g == 0x00 && rgbptr->cache_init) - { - // - // Copy black color and continue... - // - - memcpy(output, rgbptr->black, rgbsize); - - output += rgbptr->num_channels; - continue; - } - else if (g == 0xff && rgbptr->cache_init) - { - // - // Copy white color and continue... - // - - memcpy(output, rgbptr->white, rgbsize); - - output += rgbptr->num_channels; - continue; - } - - // - // Nope, figure this one out on our own... - // - - gi = rgbptr->cube_index[g]; - gm0 = rgbptr->cube_mult[g]; - gm1 = 256 - gm0; - - color = rgbptr->colors[gi][gi][gi]; - - for (i = 0; i < rgbptr->num_channels; i ++, color ++) - { - tempg = (color[0] * gm0 + color[xs + ys + zs] * gm1) / 256; - - if (tempg > 255) - *output++ = 255; - else if (tempg < 0) - *output++ = 0; - else - *output++ = tempg; - } - } -} - - -// -// 'cfRGBDoRGB()' - Do a RGB separation... -// - -void -cfRGBDoRGB(cf_rgb_t *rgbptr, // I - Color separation - const unsigned char *input, // I - Input RGB pixels - unsigned char *output, // O - Output Device-N pixels - int num_pixels) - // I - Number of pixels -{ - int i; // Looping var - int rgb, // Current RGB color - lastrgb; // Previous RGB color - int r, ri, rm0, rm1, rs, - // Current red index, multipliexs, - // and row offset - g, gi, gm0, gm1, gs, - // Current green ... - b, bi, bm0, bm1, bs; - // Current blue ... - const unsigned char *color; // Current color data - int tempr, // Current separation colors - tempg, // ... - tempb ; // ... - int rgbsize; // Separation data size - - - // - // Range check input... - // - - if (!rgbptr || !input || !output || num_pixels <= 0) - return; - - // - // Initialize variables used for the duration of the separation... - // - - lastrgb = -1; - rgbsize = rgbptr->num_channels; - rs = rgbptr->cube_size * rgbptr->cube_size * rgbptr->num_channels; - gs = rgbptr->cube_size * rgbptr->num_channels; - bs = rgbptr->num_channels; - - // - // Loop through it all... - // - - while (num_pixels > 0) - { - // - // See if the next pixel is a cached value... - // - - num_pixels --; - - r = cf_srgb_lut[*input++]; - g = cf_srgb_lut[*input++]; - b = cf_srgb_lut[*input++]; - rgb = (((r << 8) | g) << 8) | b; - - if (rgb == lastrgb) - { - // - // Copy previous color and continue... - // - - memcpy(output, output - rgbptr->num_channels, rgbsize); - - output += rgbptr->num_channels; - continue; - } - else if (rgb == 0x000000 && rgbptr->cache_init) - { - // - // Copy black color and continue... - // - - memcpy(output, rgbptr->black, rgbsize); - - output += rgbptr->num_channels; - continue; - } - else if (rgb == 0xffffff && rgbptr->cache_init) - { - // - // Copy white color and continue... - // - - memcpy(output, rgbptr->white, rgbsize); - - output += rgbptr->num_channels; - continue; - } - - // - // Nope, figure this one out on our own... - // - - ri = rgbptr->cube_index[r]; - rm0 = rgbptr->cube_mult[r]; - rm1 = 256 - rm0; - - gi = rgbptr->cube_index[g]; - gm0 = rgbptr->cube_mult[g]; - gm1 = 256 - gm0; - - bi = rgbptr->cube_index[b]; - bm0 = rgbptr->cube_mult[b]; - bm1 = 256 - bm0; - - color = rgbptr->colors[ri][gi][bi]; - - for (i = rgbptr->num_channels; i > 0; i --, color ++) - { - tempb = (color[0] * bm0 + color[bs] * bm1) / 256; - tempg = tempb * gm0; - tempb = (color[gs] * gm0 + color[gs + bs] * bm1) / 256; - tempg = (tempg + tempb * gm1) / 256; - - tempr = tempg * rm0; - - tempb = (color[rs] * bm0 + color[rs + bs] * bm1) / 256; - tempg = tempb * gm0; - tempb = (color[rs + gs] * bm0 + color[rs + gs + bs] * bm1) / 256; - tempg = (tempg + tempb * gm1) / 256; - - tempr = (tempr + tempg * rm1) / 256; - - if (tempr > 255) - *output++ = 255; - else if (tempr < 0) - *output++ = 0; - else - *output++ = tempr; - } - } -} - - -// -// 'cfRGBNew()' - Create a new RGB color separation. -// - -cf_rgb_t * // O - New color separation or NULL -cfRGBNew(int num_samples, // I - Number of samples - cf_sample_t *samples, // I - Samples - int cube_size, // I - Size of LUT cube - int num_channels) // I - Number of color components -{ - cf_rgb_t *rgbptr; // New color separation - int i; // Looping var - int r, g, b; // Current RGB - int tempsize; // Sibe of main arrays - unsigned char *tempc; // Pointer for C arrays - unsigned char **tempb ; // Pointer for Z arrays - unsigned char ***tempg; // Pointer for Y arrays - unsigned char ****tempr; // Pointer for X array - unsigned char rgb[3]; // Temporary RGB value - - - // - // Range-check the input... - // - - if (!samples || num_samples != (cube_size * cube_size * cube_size) || - num_channels <= 0 || num_channels > CF_MAX_RGB) - return (NULL); - - // - // Allocate memory for the separation... - // - - if ((rgbptr = calloc(1, sizeof(cf_rgb_t))) == NULL) - return (NULL); - - // - // Allocate memory for the samples and the LUT cube... - // - - tempsize = cube_size * cube_size * cube_size; // FUTURE: num_samples < cs^3 - - tempc = calloc(tempsize, num_channels); - tempb = calloc(tempsize, sizeof(unsigned char *)); - tempg = calloc(cube_size * cube_size, sizeof(unsigned char **)); - tempr = calloc(cube_size, sizeof(unsigned char ***)); - - if (tempc == NULL || tempb == NULL || tempg == NULL || tempr == NULL) - { - free(rgbptr); - - if (tempc) - free(tempc); - - if (tempb) - free(tempb); - - if (tempg) - free(tempg); - - if (tempr) - free(tempr); - - return (NULL); - } - - // - // Fill in the arrays... - // - - for (i = 0, r = 0; r < cube_size; r ++) - { - tempr[r] = tempg + r * cube_size; - - for (g = 0; g < cube_size; g ++) - { - tempr[r][g] = tempb + i; - - for (b = 0; b < cube_size; b ++, i ++) - tempr[r][g][b] = tempc + i * num_channels; - } - } - - for (i = 0; i < num_samples; i ++) - { - r = samples[i].rgb[0] * (cube_size - 1) / 255; - g = samples[i].rgb[1] * (cube_size - 1) / 255; - b = samples[i].rgb[2] * (cube_size - 1) / 255; - - memcpy(tempr[r][g][b], samples[i].colors, num_channels); - } - - rgbptr->cube_size = cube_size; - rgbptr->num_channels = num_channels; - rgbptr->colors = tempr; - - // - // Generate the lookup tables for the cube indices and multipliers... - // - - for (i = 0; i < 256; i ++) - { - rgbptr->cube_index[i] = i * (cube_size - 1) / 256; - - if (i == 0) - rgbptr->cube_mult[i] = 256; - else - rgbptr->cube_mult[i] = 255 - ((i * (cube_size - 1)) & 255); - } - - // - // Generate the black and white cache values for the separation... - // - - rgb[0] = 0; - rgb[1] = 0; - rgb[2] = 0; - - cfRGBDoRGB(rgbptr, rgb, rgbptr->black, 1); - - rgb[0] = 255; - rgb[1] = 255; - rgb[2] = 255; - - cfRGBDoRGB(rgbptr, rgb, rgbptr->white, 1); - - rgbptr->cache_init = 1; - - // - // Return the separation... - // - - return (rgbptr); -} diff --git a/cupsfilters/srgb.c b/cupsfilters/srgb.c deleted file mode 100644 index 21e99ec2f..000000000 --- a/cupsfilters/srgb.c +++ /dev/null @@ -1,69 +0,0 @@ -// -// sRGB lookup tables for libcupsfilters. -// -// Copyright 2007 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers. -// - -#include "driver.h" - - -// -// sRGB gamma lookup table. -// - -const unsigned char cf_srgb_lut[256] = -{ - 0, 20, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 65, 68, - 70, 72, 74, 76, 78, 80, 81, 83, 85, 87, 88, 90, 91, 93, 94, - 96, 97, 99, 100, 102, 103, 104, 106, 107, 108, 109, 111, 112, 113, 114, - 115, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128, 129, 130, 131, - 132, 133, 134, 135, 136, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, - 146, 147, 147, 148, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 158, - 158, 159, 160, 161, 162, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, - 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, - 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, - 190, 191, 192, 192, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, - 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207, 208, 208, - 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 216, 216, 217, - 217, 218, 218, 219, 219, 220, 220, 221, 222, 222, 223, 223, 224, 224, 225, - 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, - 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 239, 240, - 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, - 248, 248, 249, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, - 255 -}; - - -// -// sRGB gamma lookup table (inverted output to map to CMYK...) -// - -const unsigned char cf_scmy_lut[256] = -{ - 255, 235, 227, 222, 217, 213, 209, 206, 203, 200, 197, 194, 192, 190, 187, - 185, 183, 181, 179, 177, 175, 174, 172, 170, 168, 167, 165, 164, 162, 161, - 159, 158, 156, 155, 153, 152, 151, 149, 148, 147, 146, 144, 143, 142, 141, - 140, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 127, 126, 125, 124, - 123, 122, 121, 120, 119, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, - 109, 108, 108, 107, 106, 105, 104, 103, 102, 102, 101, 100, 99, 98, 97, - 97, 96, 95, 94, 93, 93, 92, 91, 90, 90, 89, 88, 87, 87, 86, - 85, 84, 84, 83, 82, 81, 81, 80, 79, 79, 78, 77, 77, 76, 75, - 74, 74, 73, 72, 72, 71, 70, 70, 69, 68, 68, 67, 66, 66, 65, - 65, 64, 63, 63, 62, 61, 61, 60, 59, 59, 58, 58, 57, 56, 56, - 55, 55, 54, 53, 53, 52, 52, 51, 50, 50, 49, 49, 48, 47, 47, - 46, 46, 45, 45, 44, 43, 43, 42, 42, 41, 41, 40, 39, 39, 38, - 38, 37, 37, 36, 36, 35, 35, 34, 33, 33, 32, 32, 31, 31, 30, - 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, 23, - 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, 16, 15, - 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, - 7, 7, 6, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, - 0 -}; diff --git a/cupsfilters/test1284.c b/cupsfilters/test1284.c deleted file mode 100644 index 26969a096..000000000 --- a/cupsfilters/test1284.c +++ /dev/null @@ -1,75 +0,0 @@ -// -// IEEE-1284 support functions test program for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1997-2006 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// main() - Test the device-ID functions. -// - -// -// Include necessary headers. -// - -#include -#include -#include -#include -#ifdef WIN32 -# include -#else -# include -# include -#endif // WIN32 - -#include - - -// -// 'main()' - Test the device-ID functions. -// - -int // O - Exit status -main(int argc, // I - Number of command-line args - char *argv[]) // I - Command-line arguments -{ - int i, // Looping var - fd; // File descriptor - char device_id[1024], // 1284 device ID string - make_model[1024], // make-and-model string - uri[1024]; // URI string - - - if (argc < 2) - { - puts("Usage: test1284 device-file [... device-file-N]"); - exit(1); - } - - for (i = 1; i < argc; i ++) - { - if ((fd = open(argv[i], O_RDWR)) < 0) - { - perror(argv[i]); - return (errno); - } - - printf("%s:\n", argv[i]); - - cfIEEE1284GetDeviceID(fd, device_id, sizeof(device_id), make_model, - sizeof(make_model), "test", uri, sizeof(uri)); - - printf(" device_id=\"%s\"\n", device_id); - printf(" make_model=\"%s\"\n", make_model); - printf(" uri=\"%s\"\n", uri); - - close(fd); - } - - return (0); -} diff --git a/cupsfilters/testcmyk.c b/cupsfilters/testcmyk.c deleted file mode 100644 index 5ab5d8f56..000000000 --- a/cupsfilters/testcmyk.c +++ /dev/null @@ -1,431 +0,0 @@ -// -// Test for the CMYK color separation code for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2006 by Easy Software Products, All Rights Reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// test_gray() - Test grayscale separations... -// test_rgb() - Test color separations... -// main() - Do color separation tests. -// - -// -// Include necessary headers. -// - -#include -#include -#include -#include "driver.h" -#include - -cf_logfunc_t logfunc = cfCUPSLogFunc; // Log function -void *ld = NULL; // Log function data - -void test_gray(int num_comps, const char *basename); -void test_rgb(int num_comps, const char *basename); - - -// -// 'main()' - Do color separation tests. -// - -int // O - Exit status -main(int argc, // I - Number of command-line arguments - char *argv[]) // I - Command-line arguments -{ - // - // Make the test directory... - // - - mkdir("test", 0755); - - // - // Run tests for K, Kk, CMY, CMYK, CcMmYK, and CcMmYKk separations... - // - - test_rgb(1, "test/K-rgb"); - test_rgb(2, "test/Kk-rgb"); - test_rgb(3, "test/CMY-rgb"); - test_rgb(4, "test/CMYK-rgb"); - test_rgb(6, "test/CcMmYK-rgb"); - test_rgb(7, "test/CcMmYKk-rgb"); - - test_gray(1, "test/K-gray"); - test_gray(2, "test/Kk-gray"); - test_gray(3, "test/CMY-gray"); - test_gray(4, "test/CMYK-gray"); - test_gray(6, "test/CcMmYK-gray"); - test_gray(7, "test/CcMmYKk-gray"); - - // - // Return with no errors... - // - - return (0); -} - - -// -// 'test_gray()' - Test grayscale separations... -// - -void -test_gray(int num_comps, // I - Number of components - const char *basename) // I - Base filename of output -{ - int i; // Looping var - char filename[255]; // Output filename - char line[255]; // Line from PGM file - int width, height; // Width and height of test image - int x, y; // Current coordinate in image - int r, g, b; // Current RGB color - unsigned char input[7000]; // Line to separate - short output[48000], // Output separation data - *outptr; // Pointer in output - FILE *in; // Input PPM file - FILE *out[CF_MAX_CHAN]; - // Output PGM files - FILE *comp; // Composite output - cf_cmyk_t *cmyk; // Color separation - - - // - // Open the test image... - // - - in = fopen("image.pgm", "rb"); - while (fgets(line, sizeof(line), in) != NULL) - if (isdigit(line[0])) - break; - - sscanf(line, "%d%d", &width, &height); - - if (fgets(line, sizeof(line), in)); // Ignore return value of fgets() - - // - // Create the color separation... - // - - cmyk = cfCMYKNew(num_comps); - - switch (num_comps) - { - case 2 : // Kk - cfCMYKSetLtDk(cmyk, 0, 0.5, 1.0, logfunc, ld); - break; - - case 4 : - cfCMYKSetGamma(cmyk, 2, 1.0, 0.9, logfunc, ld); - cfCMYKSetBlack(cmyk, 0.5, 1.0, logfunc, ld); - break; - - case 6 : // CcMmYK - cfCMYKSetLtDk(cmyk, 0, 0.5, 1.0, logfunc, ld); - cfCMYKSetLtDk(cmyk, 2, 0.5, 1.0, logfunc, ld); - cfCMYKSetGamma(cmyk, 4, 1.0, 0.9, logfunc, ld); - cfCMYKSetBlack(cmyk, 0.5, 1.0, logfunc, ld); - break; - - case 7 : // CcMmYKk - cfCMYKSetLtDk(cmyk, 0, 0.5, 1.0, logfunc, ld); - cfCMYKSetLtDk(cmyk, 2, 0.5, 1.0, logfunc, ld); - cfCMYKSetGamma(cmyk, 4, 1.0, 0.9, logfunc, ld); - cfCMYKSetLtDk(cmyk, 5, 0.5, 1.0, logfunc, ld); - break; - } - - // - // Open the color separation files... - // - - for (i = 0; i < num_comps; i ++) - { - sprintf(filename, "%s%d.pgm", basename, i); - out[i] = fopen(filename, "wb"); - - fprintf(out[i], "P5\n%d %d 255\n", width, height); - } - - sprintf(filename, "%s.ppm", basename); - comp = fopen(filename, "wb"); - - fprintf(comp, "P6\n%d %d 255\n", width, height); - - // - // Read the image and do the separations... - // - - for (y = 0; y < height; y ++) - { - if (fread(input, width, 1, in)); // Ignore return value of fread() - - cfCMYKDoGray(cmyk, input, output, width); - - for (x = 0, outptr = output; x < width; x ++, outptr += num_comps) - { - for (i = 0; i < num_comps; i ++) - putc(255 - 255 * outptr[i] / 4095, out[i]); - - r = 4095; - g = 4095; - b = 4095; - - switch (num_comps) - { - case 1 : - r -= outptr[0]; - g -= outptr[0]; - b -= outptr[0]; - break; - case 2 : - r -= outptr[0]; - g -= outptr[0]; - b -= outptr[0]; - - r -= outptr[1] / 2; - g -= outptr[1] / 2; - b -= outptr[1] / 2; - break; - case 3 : - r -= outptr[0]; - g -= outptr[1]; - b -= outptr[2]; - break; - case 4 : - r -= outptr[0]; - g -= outptr[1]; - b -= outptr[2]; - - r -= outptr[3]; - g -= outptr[3]; - b -= outptr[3]; - break; - case 6 : - r -= outptr[0] + outptr[1] / 2; - g -= outptr[2] + outptr[3] / 3; - b -= outptr[4]; - - r -= outptr[5]; - g -= outptr[5]; - b -= outptr[5]; - break; - case 7 : - r -= outptr[0] + outptr[1] / 2; - g -= outptr[2] + outptr[3] / 3; - b -= outptr[4]; - - r -= outptr[5] + outptr[6] / 2; - g -= outptr[5] + outptr[6] / 2; - b -= outptr[5] + outptr[6] / 2; - break; - } - - if (r < 0) - putc(0, comp); - else - putc(255 * r / 4095, comp); - - if (g < 0) - putc(0, comp); - else - putc(255 * g / 4095, comp); - - if (b < 0) - putc(0, comp); - else - putc(255 * b / 4095, comp); - } - } - - for (i = 0; i < num_comps; i ++) - fclose(out[i]); - - fclose(comp); - fclose(in); - - cfCMYKDelete(cmyk); -} - - -// -// 'test_rgb()' - Test color separations... -// - -void -test_rgb(int num_comps, // I - Number of components - const char *basename) // I - Base filename of output -{ - int i; // Looping var - char filename[255]; // Output filename - char line[255]; // Line from PPM file - int width, height; // Width and height of test image - int x, y; // Current coordinate in image - int r, g, b; // Current RGB color - unsigned char input[7000]; // Line to separate - short output[48000], // Output separation data - *outptr; // Pointer in output - FILE *in; // Input PPM file - FILE *out[CF_MAX_CHAN]; - // Output PGM files - FILE *comp; // Composite output - cf_cmyk_t *cmyk; // Color separation - - - // - // Open the test image... - // - - in = fopen("image.ppm", "rb"); - while (fgets(line, sizeof(line), in) != NULL) - if (isdigit(line[0])) - break; - - sscanf(line, "%d%d", &width, &height); - - if (fgets(line, sizeof(line), in)); // Ignore return value of fgets() - - // - // Create the color separation... - // - - cmyk = cfCMYKNew(num_comps); - - cfCMYKSetBlack(cmyk, 0.5, 1.0, logfunc, ld); - - switch (num_comps) - { - case 2 : // Kk - cfCMYKSetLtDk(cmyk, 0, 0.5, 1.0, logfunc, ld); - break; - case 6 : // CcMmYK - cfCMYKSetGamma(cmyk, 0, 1.0, 0.8, logfunc, ld); - cfCMYKSetLtDk(cmyk, 0, 0.5, 1.0, logfunc, ld); - cfCMYKSetGamma(cmyk, 2, 1.0, 0.8, logfunc, ld); - cfCMYKSetLtDk(cmyk, 2, 0.5, 1.0, logfunc, ld); - break; - case 7 : // CcMmYKk - cfCMYKSetGamma(cmyk, 0, 1.0, 0.8, logfunc, ld); - cfCMYKSetLtDk(cmyk, 0, 0.5, 1.0, logfunc, ld); - cfCMYKSetGamma(cmyk, 2, 1.0, 0.8, logfunc, ld); - cfCMYKSetLtDk(cmyk, 2, 0.5, 1.0, logfunc, ld); - cfCMYKSetLtDk(cmyk, 5, 0.5, 1.0, logfunc, ld); - break; - } - - // - // Open the color separation files... - // - - for (i = 0; i < num_comps; i ++) - { - sprintf(filename, "%s%d.pgm", basename, i); - out[i] = fopen(filename, "wb"); - - fprintf(out[i], "P5\n%d %d 255\n", width, height); - } - - sprintf(filename, "%s.ppm", basename); - comp = fopen(filename, "wb"); - - fprintf(comp, "P6\n%d %d 255\n", width, height); - - // - // Read the image and do the separations... - // - - for (y = 0; y < height; y ++) - { - if (fread(input, width, 3, in)); // Ignore return value of fread() - - cfCMYKDoRGB(cmyk, input, output, width); - - for (x = 0, outptr = output; x < width; x ++, outptr += num_comps) - { - for (i = 0; i < num_comps; i ++) - putc(255 - 255 * outptr[i] / 4095, out[i]); - - r = 4095; - g = 4095; - b = 4095; - - switch (num_comps) - { - case 1 : - r -= outptr[0]; - g -= outptr[0]; - b -= outptr[0]; - break; - case 2 : - r -= outptr[0]; - g -= outptr[0]; - b -= outptr[0]; - - r -= outptr[1] / 2; - g -= outptr[1] / 2; - b -= outptr[1] / 2; - break; - case 3 : - r -= outptr[0]; - g -= outptr[1]; - b -= outptr[2]; - break; - case 4 : - r -= outptr[0]; - g -= outptr[1]; - b -= outptr[2]; - - r -= outptr[3]; - g -= outptr[3]; - b -= outptr[3]; - break; - case 6 : - r -= outptr[0] + outptr[1] / 2; - g -= outptr[2] + outptr[3] / 3; - b -= outptr[4]; - - r -= outptr[5]; - g -= outptr[5]; - b -= outptr[5]; - break; - case 7 : - r -= outptr[0] + outptr[1] / 2; - g -= outptr[2] + outptr[3] / 3; - b -= outptr[4]; - - r -= outptr[5] + outptr[6] / 2; - g -= outptr[5] + outptr[6] / 2; - b -= outptr[5] + outptr[6] / 2; - break; - } - - if (r < 0) - putc(0, comp); - else - putc(255 * r / 4095, comp); - - if (g < 0) - putc(0, comp); - else - putc(255 * g / 4095, comp); - - if (b < 0) - putc(0, comp); - else - putc(255 * b / 4095, comp); - } - } - - for (i = 0; i < num_comps; i ++) - fclose(out[i]); - - fclose(comp); - fclose(in); - - cfCMYKDelete(cmyk); -} diff --git a/cupsfilters/testdither.c b/cupsfilters/testdither.c deleted file mode 100644 index 5a1cc14d0..000000000 --- a/cupsfilters/testdither.c +++ /dev/null @@ -1,186 +0,0 @@ -// -// Dither test program for libcupsfilters. -// -// Try the following: -// -// testdither 0 255 > filename.ppm -// testdither 0 127 255 > filename.ppm -// testdither 0 85 170 255 > filename.ppm -// testdither 0 63 127 170 198 227 255 > filename.ppm -// testdither 0 210 383 > filename.ppm -// testdither 0 82 255 > filename.ppm -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// main() - Test dithering and output a PPM file. -// usage() - Show program usage... -// - -// -// Include necessary headers. -// - -#include "driver.h" -#include -#include -#include - -cf_logfunc_t logfunc = cfCUPSLogFunc; // Log function -void *ld = NULL; // Log function data - - -// -// Local functions... -// - -void usage(void); - - -// -// 'main()' - Test dithering and output a PPM file. -// - -int // O - Exit status -main(int argc, // I - Number of command-line arguments - char *argv[]) // I - Command-line arguments -{ - int x, y; // Current coordinate in image - short line[512]; // Line to dither - unsigned char pixels[512], // Dither pixels - *pixptr; // Pointer in line - int output; // Output pixel - cf_lut_t *lut; // Dither lookup table - cf_dither_t *dither; // Dither state - int nlutvals; // Number of lookup values - float lutvals[16]; // Lookup values - int pixvals[16]; // Pixel values - - - // - // See if we have lookup table values on the command-line... - // - - if (argc > 1) - { - // - // Yes, collect them... - // - - nlutvals = 0; - - for (x = 1; x < argc; x ++) - if (isdigit(argv[x][0]) && nlutvals < 16) - { - pixvals[nlutvals] = atoi(argv[x]); - lutvals[nlutvals] = atof(argv[x]) / 255.0; - nlutvals ++; - } - else - usage(); - - // - // See if we have at least 2 values... - // - - if (nlutvals < 2) - usage(); - } - else - { - // - // Otherwise use the default 2-entry LUT with values of 0 and 255... - // - - nlutvals = 2; - lutvals[0] = 0.0; - lutvals[1] = 1.0; - pixvals[0] = 0; - pixvals[1] = 255; - } - - // - // Create the lookup table and dither state... - // - - lut = cfLutNew(nlutvals, lutvals, logfunc, ld); - dither = cfDitherNew(512); - - // - // Put out the PGM header for a raw 256x256x8-bit grayscale file... - // - - puts("P5\n512\n512\n255"); - - // - // Dither 512 lines, which are written out in 256 image lines... - // - - for (y = 0; y < 512; y ++) - { - // - // Create the grayscale data for the current line... - // - - for (x = 0; x < 512; x ++) - line[x] = 4095 * ((y / 32) * 16 + x / 32) / 255; - - // - // Dither the line... - // - - cfDitherLine(dither, lut, line, 1, pixels); - - if (y == 0) - { - fputs("DEBUG: pixels =", stderr); - for (x = 0; x < 512; x ++) - fprintf(stderr, " %d", pixels[x]); - fputs("\n", stderr); - } - - // - // Add or set the output pixel values... - // - - for (x = 0, pixptr = pixels; x < 512; x ++, pixptr ++) - { - output = 255 - pixvals[*pixptr]; - - if (output < 0) - putchar(0); - else - putchar(output); - } - } - - // - // Free the dither state and lookup table... - // - - cfDitherDelete(dither); - cfLutDelete(lut); - - // - // Return with no errors... - // - - return (0); -} - - -// -// 'usage()' - Show program usage... -// - -void -usage(void) -{ - puts("Usage: testdither [val1 val2 [... val16]] >filename.ppm"); - exit(1); -} diff --git a/cupsfilters/testimage.c b/cupsfilters/testimage.c deleted file mode 100644 index 0afbdb422..000000000 --- a/cupsfilters/testimage.c +++ /dev/null @@ -1,87 +0,0 @@ -// -// Image library test program for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// main() - Main entry... -// - -// -// Include necessary headers... -// - -#include "image.h" - - -// -// 'main()' - Main entry... -// - -int // O - Exit status -main(int argc, // I - Number of command-line arguments - char *argv[]) // I - Command-line arguments -{ - cf_image_t *img; // Image to print - cf_icspace_t primary; // Primary image colorspace - FILE *out; // Output PPM/PGM file - cf_ib_t *line; // Line from file - int y, // Current line - width, // Width of image - height, // Height of image - depth; // Depth of image - - - if (argc != 3) - { - puts("Usage: testimage filename.ext filename.[ppm|pgm]"); - return (1); - } - - if (strstr(argv[2], ".ppm") != NULL) - primary = CF_IMAGE_RGB; - else - primary = CF_IMAGE_WHITE; - - img = cfImageOpen(argv[1], primary, CF_IMAGE_WHITE, 100, 0, NULL); - - if (!img) - { - perror(argv[1]); - return (1); - } - - out = fopen(argv[2], "wb"); - - if (!out) - { - perror(argv[2]); - cfImageClose(img); - return (1); - } - - width = cfImageGetWidth(img); - height = cfImageGetHeight(img); - depth = cfImageGetDepth(img); - line = calloc(width, depth); - - fprintf(out, "P%d\n%d\n%d\n255\n", - cfImageGetColorSpace(img) == CF_IMAGE_WHITE ? 5 : 6, - width, height); - - for (y = 0; y < height; y ++) - { - cfImageGetRow(img, 0, y, width, line); - fwrite(line, width, depth, out); - } - - cfImageClose(img); - fclose(out); - - return (0); -} diff --git a/cupsfilters/testpdf1.c b/cupsfilters/testpdf1.c deleted file mode 100644 index 757377268..000000000 --- a/cupsfilters/testpdf1.c +++ /dev/null @@ -1,64 +0,0 @@ -// -// PDF file output test program 1 for libcupsfilters. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "pdfutils.h" -#include "debug-internal.h" -#include - -int -main() -{ - cf_pdf_out_t *pdf; - - pdf = cfPDFOutNew(); - DEBUG_assert(pdf); - - cfPDFOutBeginPDF(pdf); - - // bad font - int font_obj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<>\n" - "endobj\n", - font_obj, "Courier"); - // test - const int PageWidth = 595, PageLength = 842; - int cobj = cfPDFOutAddXRef(pdf); - const char buf[] = "BT /a 10 Tf (abc) Tj ET"; - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<>\n" - "stream\n" - "%s\n" - "endstream\n" - "endobj\n", - cobj, strlen(buf), buf); - - int obj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<> >>\n" - ">>\n" - "endobj\n", - obj, PageWidth, PageLength, cobj, font_obj); - // TODO: into pdf-> - cfPDFOutAddPage(pdf, obj); - cfPDFOutFinishPDF(pdf); - - cfPDFOutFree(pdf); - - return (0); -} diff --git a/cupsfilters/testpdf2.c b/cupsfilters/testpdf2.c deleted file mode 100644 index a67cdb4b4..000000000 --- a/cupsfilters/testpdf2.c +++ /dev/null @@ -1,131 +0,0 @@ -// -// PDF file output test program 2 (fontembed) for libcupsfilters. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "pdfutils.h" -#include "config.h" -#include "debug-internal.h" -#include "cupsfilters/fontembed-private.h" - -#include - -static inline void -write_string(cf_pdf_out_t *pdf, - _cf_fontembed_emb_params_t *emb, - const char *str) // {{{ -{ - DEBUG_assert(pdf); - DEBUG_assert(emb); - int iA; - - if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) - { - putc('<', stdout); - for (iA = 0; str[iA]; iA ++) - { - const unsigned short gid = _cfFontEmbedEmbGet(emb, - (unsigned char)str[iA]); - fprintf(stdout, "%04x", gid); - } - putc('>', stdout); - pdf->filepos += 4 * iA + 2; - } - else - { - for (iA = 0; str[iA]; iA ++) - { - _cfFontEmbedEmbGet(emb, (unsigned char)str[iA]); - // TODO: pdf: otf_from_pdf_default_encoding - } - cfPDFOutputString(pdf, str, -1); - } -} -// }}} - - -int -main(int argc, - char *argv[]) -{ - cf_pdf_out_t *pdf; - - pdf = cfPDFOutNew(); - DEBUG_assert(pdf); - - cfPDFOutBeginPDF(pdf); - - // font, pt.1 - const char *fn = TESTFONT; - _cf_fontembed_otf_file_t *otf = NULL; - - if (argc == 2) - fn = argv[1]; - - otf = _cfFontEmbedOTFLoad(fn); - if (!otf) - { - printf("Font %s was not loaded, exiting.\n", fn); - return (1); - } - DEBUG_assert(otf); - _cf_fontembed_fontfile_t *ff = _cfFontEmbedFontFileOpenSFNT(otf); - _cf_fontembed_emb_params_t *emb = - _cfFontEmbedEmbNew(ff, - _CF_FONTEMBED_EMB_DEST_PDF16, - _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE | - _CF_FONTEMBED_EMB_C_TAKE_FONTFILE); - - // test - const int PageWidth = 595, PageLength = 842; - const int cobj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<>\n" - "stream\n", - cobj, cobj + 1); - long streamlen = -pdf->filepos; - cfPDFOutPrintF(pdf, "BT /a 10 Tf "); - write_string(pdf, emb, "Test"); - cfPDFOutPrintF(pdf, " Tj ET"); - - streamlen += pdf->filepos; - cfPDFOutPrintF(pdf, - "\nendstream\n" - "endobj\n"); - const int clobj = cfPDFOutAddXRef(pdf); - DEBUG_assert(clobj == cobj + 1); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "%ld\n" - "endobj\n", - clobj, streamlen); - - // font - int font_obj = cfPDFOutWriteFont(pdf, emb); - DEBUG_assert(font_obj); - - int obj = cfPDFOutAddXRef(pdf); - cfPDFOutPrintF(pdf, - "%d 0 obj\n" - "<> >>\n" - ">>\n" - "endobj\n", - obj, PageWidth, PageLength, cobj, font_obj); - // TODO: into pdf-> - cfPDFOutAddPage(pdf, obj); - cfPDFOutFinishPDF(pdf); - - cfPDFOutFree(pdf); - - _cfFontEmbedEmbClose(emb); - - return (0); -} diff --git a/cupsfilters/testrgb.c b/cupsfilters/testrgb.c deleted file mode 100644 index 613f272bd..000000000 --- a/cupsfilters/testrgb.c +++ /dev/null @@ -1,340 +0,0 @@ -// -// Test for the new RGB color separation code for libcupsfilters. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2006 by Easy Software Products, All Rights Reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// main() - Do color rgb tests. -// test_gray() - Test grayscale rgbs... -// test_rgb() - Test color rgbs... -// - -// -// Include necessary headers. -// - -#include -#include -#include -#include "driver.h" -#include - -#ifdef USE_LCMS1 -# include -#endif // USE_LCMS1 - - -void test_gray(cf_sample_t *samples, int num_samples, - int cube_size, int num_comps, const char *basename); -void test_rgb(cf_sample_t *samples, int num_samples, - int cube_size, int num_comps, - const char *basename); - - -// -// 'main()' - Do color rgb tests. -// - -int // O - Exit status -main(int argc, // I - Number of command-line arguments - char *argv[]) // I - Command-line arguments -{ - static cf_sample_t CMYK[] = // Basic 4-color sep - { - //{ r, g, b }, { C, M, Y, K } - { { 0, 0, 0 }, { 0, 0, 0, 255 } }, - { { 255, 0, 0 }, { 0, 255, 240, 0 } }, - { { 0, 255, 0 }, { 200, 0, 200, 0 } }, - { { 255, 255, 0 }, { 0, 0, 240, 0 } }, - { { 0, 0, 255 }, { 200, 200, 0, 0 } }, - { { 255, 0, 255 }, { 0, 200, 0, 0 } }, - { { 0, 255, 255 }, { 200, 0, 0, 0 } }, - { { 255, 255, 255 }, { 0, 0, 0, 0 } } - }; - - - // - // Make the test directory... - // - - mkdir("test", 0755); - - // - // Run tests for CMYK and CMYK separations... - // - - test_rgb(CMYK, 8, 2, 4, "test/rgb-cmyk"); - - test_gray(CMYK, 8, 2, 4, "test/gray-cmyk"); - - // - // Return with no errors... - // - - return (0); -} - - -// -// 'test_gray()' - Test grayscale rgbs... -// - -void -test_gray(cf_sample_t *samples, // I - Sample values - int num_samples, // I - Number of samples - int cube_size, // I - Cube size - int num_comps, // I - Number of components - const char *basename) // I - Base filename of output -{ - int i; // Looping var - char filename[255]; // Output filename - char line[255]; // Line from PPM file - int width, height; // Width and height of test image - int x, y; // Current coordinate in image - int r, g, b; // Current RGB color - unsigned char input[7000]; // Line to rgbarate - unsigned char output[48000], // Output rgb data - *outptr; // Pointer in output - FILE *in; // Input PPM file - FILE *out[CF_MAX_CHAN]; - // Output PGM files - FILE *comp; // Composite output - cf_rgb_t *rgb; // Color separation - - - // - // Open the test image... - // - - in = fopen("image.pgm", "rb"); - while (fgets(line, sizeof(line), in) != NULL) - if (isdigit(line[0])) - break; - - sscanf(line, "%d%d", &width, &height); - - if (fgets(line, sizeof(line), in)); // Ignore return value of fgets() - - // - // Create the color rgb... - // - - rgb = cfRGBNew(num_samples, samples, cube_size, num_comps); - - // - // Open the color rgb files... - // - - for (i = 0; i < num_comps; i ++) - { - sprintf(filename, "%s%d.pgm", basename, i); - out[i] = fopen(filename, "wb"); - - fprintf(out[i], "P5\n%d %d 255\n", width, height); - } - - sprintf(filename, "%s.ppm", basename); - comp = fopen(filename, "wb"); - - fprintf(comp, "P6\n%d %d 255\n", width, height); - - // - // Read the image and do the rgbs... - // - - for (y = 0; y < height; y ++) - { - if (fread(input, width, 1, in)); // Ignore return value of fread() - - cfRGBDoGray(rgb, input, output, width); - - for (x = 0, outptr = output; x < width; x ++, outptr += num_comps) - { - for (i = 0; i < num_comps; i ++) - putc(255 - outptr[i], out[i]); - - r = 255; - g = 255; - b = 255; - - r -= outptr[0]; - g -= outptr[1]; - b -= outptr[2]; - - r -= outptr[3]; - g -= outptr[3]; - b -= outptr[3]; - - if (num_comps > 4) - { - r -= outptr[4] / 2; - g -= outptr[5] / 2; - } - - if (num_comps > 6) - { - r -= outptr[6] / 2; - g -= outptr[6] / 2; - b -= outptr[6] / 2; - } - - if (r < 0) - putc(0, comp); - else - putc(r, comp); - - if (g < 0) - putc(0, comp); - else - putc(g, comp); - - if (b < 0) - putc(0, comp); - else - putc(b, comp); - } - } - - for (i = 0; i < num_comps; i ++) - fclose(out[i]); - - fclose(comp); - fclose(in); - - cfRGBDelete(rgb); -} - - -// -// 'test_rgb()' - Test color rgbs... -// - -void -test_rgb(cf_sample_t *samples, // I - Sample values - int num_samples, // I - Number of samples - int cube_size, // I - Cube size - int num_comps, // I - Number of components - const char *basename) // I - Base filename of output -{ - int i; // Looping var - char filename[255]; // Output filename - char line[255]; // Line from PPM file - int width, height; // Width and height of test image - int x, y; // Current coordinate in image - int r, g, b; // Current RGB color - unsigned char input[7000]; // Line to rgbarate - unsigned char output[48000], // Output rgb data - *outptr; // Pointer in output - FILE *in; // Input PPM file - FILE *out[CF_MAX_CHAN]; - // Output PGM files - FILE *comp; // Composite output - cf_rgb_t *rgb; // Color separation - - - // - // Open the test image... - // - - in = fopen("image.ppm", "rb"); - while (fgets(line, sizeof(line), in) != NULL) - if (isdigit(line[0])) - break; - - sscanf(line, "%d%d", &width, &height); - - if (fgets(line, sizeof(line), in)); // Ignore return value of fgets() - - // - // Create the color rgb... - // - - rgb = cfRGBNew(num_samples, samples, cube_size, num_comps); - - // - // Open the color rgb files... - // - - for (i = 0; i < num_comps; i ++) - { - sprintf(filename, "%s%d.pgm", basename, i); - out[i] = fopen(filename, "wb"); - - fprintf(out[i], "P5\n%d %d 255\n", width, height); - } - - sprintf(filename, "%s.ppm", basename); - comp = fopen(filename, "wb"); - - fprintf(comp, "P6\n%d %d 255\n", width, height); - - // - // Read the image and do the rgbs... - // - - for (y = 0; y < height; y ++) - { - if (fread(input, width, 3, in)); // Ignore return value of fread() - - cfRGBDoRGB(rgb, input, output, width); - - for (x = 0, outptr = output; x < width; x ++, outptr += num_comps) - { - for (i = 0; i < num_comps; i ++) - putc(255 - outptr[i], out[i]); - - r = 255; - g = 255; - b = 255; - - r -= outptr[0]; - g -= outptr[1]; - b -= outptr[2]; - - r -= outptr[3]; - g -= outptr[3]; - b -= outptr[3]; - - if (num_comps > 4) - { - r -= outptr[4] / 2; - g -= outptr[5] / 2; - } - - if (num_comps > 6) - { - r -= outptr[6] / 2; - g -= outptr[6] / 2; - b -= outptr[6] / 2; - } - - if (r < 0) - putc(0, comp); - else - putc(r, comp); - - if (g < 0) - putc(0, comp); - else - putc(g, comp); - - if (b < 0) - putc(0, comp); - else - putc(b, comp); - } - } - - for (i = 0; i < num_comps; i ++) - fclose(out[i]); - - fclose(comp); - fclose(in); - - cfRGBDelete(rgb); -} diff --git a/cupsfilters/texttopdf.c b/cupsfilters/texttopdf.c deleted file mode 100644 index ac0cae242..000000000 --- a/cupsfilters/texttopdf.c +++ /dev/null @@ -1,2548 +0,0 @@ -// -// Text to PDF filter function for libcupsfilters. -// -// Copyright 2008,2012 by Tobias Hoffmann. -// Copyright 2007 by Apple Inc. -// Copyright 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include "pdfutils.h" -#include "debug-internal.h" -#include "filter.h" -#include "raster.h" -#include "fontembed-private.h" -#include -#include -#include "fontconfig/fontconfig.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 - -#define PRETTY_OFF 0 -#define PRETTY_PLAIN 1 -#define PRETTY_CODE 2 -#define PRETTY_SHELL 3 -#define PRETTY_PERL 4 -#define PRETTY_HTML 5 - - -// -// Globals... -// - -static char *code_keywords[] = // List of known C/C++ keywords... - { - "and", - "and_eq", - "asm", - "auto", - "bitand", - "bitor", - "bool", - "break", - "case", - "catch", - "char", - "class", - "compl", - "const", - "const_cast", - "continue", - "default", - "delete", - "do", - "double", - "dynamic_cast", - "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", - "reinterpret_cast", - "return", - "short", - "signed", - "sizeof", - "static", - "static_cast", - "struct", - "switch", - "template", - "this", - "throw", - "true", - "try", - "typedef", - "typename", - "union", - "unsigned", - "virtual", - "void", - "volatile", - "while", - "xor", - "xor_eq" - }, - *sh_keywords[] = // List of known Boure/Korn/zsh/bash keywords... - { - "alias", - "bg", - "break", - "case", - "cd", - "command", - "continue", - "do", - "done", - "echo", - "elif", - "else", - "esac", - "eval", - "exec", - "exit", - "export", - "fc", - "fg", - "fi", - "for", - "function", - "getopts", - "if", - "in", - "jobs", - "kill", - "let", - "limit", - "newgrp", - "print", - "pwd", - "read", - "readonly", - "return", - "select", - "set", - "shift", - "test", - "then", - "time", - "times", - "trap", - "typeset", - "ulimit", - "umask", - "unalias", - "unlimit", - "unset", - "until", - "wait", - "whence" - "while", - }, - *csh_keywords[] = // List of known csh/tcsh keywords... - { - "alias", - "aliases", - "bg", - "bindkey", - "break", - "breaksw", - "builtins", - "case", - "cd", - "chdir", - "complete", - "continue", - "default", - "dirs", - "echo", - "echotc", - "else", - "end", - "endif", - "eval", - "exec", - "exit", - "fg", - "foreach", - "glob", - "goto", - "history", - "if", - "jobs", - "kill", - "limit", - "login", - "logout", - "ls", - "nice", - "nohup", - "notify", - "onintr", - "popd", - "pushd", - "pwd", - "rehash", - "repeat", - "set", - "setenv", - "settc", - "shift", - "source", - "stop", - "suspend", - "switch", - "telltc", - "then", - "time", - "umask", - "unalias", - "unbindkey", - "unhash", - "unlimit", - "unset", - "unsetenv", - "wait", - "where", - "which", - "while" - }, - *perl_keywords[] = // List of known perl keywords... - { - "abs", - "accept", - "alarm", - "and", - "atan2", - "bind", - "binmode", - "bless", - "caller", - "chdir", - "chmod", - "chomp", - "chop", - "chown", - "chr", - "chroot", - "closdir", - "close", - "connect", - "continue", - "cos", - "crypt", - "dbmclose", - "dbmopen", - "defined", - "delete", - "die", - "do", - "dump", - "each", - "else", - "elsif", - "endgrent", - "endhostent", - "endnetent", - "endprotoent", - "endpwent", - "endservent", - "eof", - "eval", - "exec", - "exists", - "exit", - "exp", - "fcntl", - "fileno", - "flock", - "for", - "foreach", - "fork", - "format", - "formline", - "getc", - "getgrent", - "getgrgid", - "getgrnam", - "gethostbyaddr", - "gethostbyname", - "gethostent", - "getlogin", - "getnetbyaddr", - "getnetbyname", - "getnetent", - "getpeername", - "getpgrp", - "getppid", - "getpriority", - "getprotobyname", - "getprotobynumber", - "getprotoent", - "getpwent", - "getpwnam", - "getpwuid", - "getservbyname", - "getservbyport", - "getservent", - "getsockname", - "getsockopt", - "glob", - "gmtime", - "goto", - "grep", - "hex", - "if", - "import", - "index", - "int", - "ioctl", - "join", - "keys", - "kill", - "last", - "lc", - "lcfirst", - "length", - "link", - "listen", - "local", - "localtime", - "log", - "lstat", - "map", - "mkdir", - "msgctl", - "msgget", - "msgrcv", - "msgsend", - "my", - "next", - "no", - "not", - "oct", - "open", - "opendir", - "or", - "ord", - "pack", - "package", - "pipe", - "pop", - "pos", - "print", - "printf", - "push", - "quotemeta", - "rand", - "read", - "readdir", - "readlink", - "recv", - "redo", - "ref", - "rename", - "require", - "reset", - "return", - "reverse", - "rewinddir", - "rindex", - "rmdir", - "scalar", - "seek", - "seekdir", - "select", - "semctl", - "semget", - "semop", - "send", - "setgrent", - "sethostent", - "setnetent", - "setpgrp", - "setpriority", - "setprotoent", - "setpwent", - "setservent", - "setsockopt", - "shift", - "shmctl", - "shmget", - "shmread", - "shmwrite", - "shutdown", - "sin", - "sleep", - "socket", - "socketpair", - "sort", - "splice", - "split", - "sprintf", - "sqrt", - "srand", - "stat", - "study", - "sub", - "substr", - "symlink", - "syscall", - "sysread", - "sysseek", - "system", - "syswrite", - "tell", - "telldir", - "tie", - "tied", - "time", - "times" - "times", - "truncate", - "uc", - "ucfirst", - "umask", - "undef", - "unless", - "unlink", - "unpack", - "unshift", - "untie", - "until", - "use", - "utime", - "values", - "vec", - "wait", - "waitpid", - "wantarray", - "warn", - "while", - "write" - }; - - -// -// Types... -// - -typedef struct // **** Character/attribute structure... **** -{ - unsigned short ch, // Character - attr; // Any attributes -} lchar_t; - -typedef struct texttopdf_doc_s -{ - int NumFonts; // Number of fonts to use - _cf_fontembed_emb_params_t *Fonts[256][4]; // Fonts to use - unsigned short Chars[256]; // Input char to unicode - unsigned char Codes[65536]; // Unicode glyph mapping to font - int Widths[256]; // Widths of each font - int Directions[256];// Text directions for each font - cf_pdf_out_t *pdf; - int FontResource; // Object number of font resource dictionary - float FontScaleX, FontScaleY; // The font matrix - lchar_t *Title, *Date; // The title and date strings - - cups_page_header2_t h; // CUPS Raster page header, to - // accommodate results of command - // line/IPP attribute parsing - cf_filter_texttopdf_parameter_t env_vars; - int NumKeywords; - float PageLeft, // Left margin - PageRight, // Right margin - PageBottom, // Bottom margin - PageTop, // Top margin - PageWidth, // Total page width - PageLength; - int NumPages; - 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 - float CharsPerInch, // Number of character columns per inch - LinesPerInch; // Number of lines per inch - int UTF8; - char **Keywords; // List of known keywords... - - int Orientation, // 0 = portrait, 1 = landscape, etc. - Duplex, // Duplexed? - LanguageLevel, // Language level of printer - ColorDevice; - lchar_t **Page; -} texttopdf_doc_t; - - -// -// Local functions... -// - -static _cf_fontembed_emb_params_t *font_load(const char *font, int fontwidth, - cf_logfunc_t log, void *ld); -static _cf_fontembed_emb_params_t *font_std(const char *name); -static int compare_keywords(const void *k1, const void *k2); -static int get_utf8(FILE *fp); -static void write_line(int row, lchar_t *line, texttopdf_doc_t *doc); -static void write_string(int col, int row, int len, lchar_t *s, - texttopdf_doc_t *doc); -static lchar_t *make_wide(const char *buf, texttopdf_doc_t *doc); -static void write_font_str(float x,float y,int fontid, lchar_t *str, - int len, texttopdf_doc_t *doc); -static void write_pretty_header(); -static int write_prolog(const char *title, const char *user, - const char *classification, const char *label, - texttopdf_doc_t *doc, - cf_logfunc_t log, void *ld); -static void write_page(texttopdf_doc_t *doc); -static void write_epilogue(texttopdf_doc_t *doc); - - -// -// 'cfFilterTextToPDF()' - Main entry for text to PDF filter. -// - -int // O - Exit status -cfFilterTextToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, - // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - texttopdf_doc_t doc; - int i, // Looping var - empty, // Is the input empty? - ch, // Current char from file - lastch, // Previous char from file - attr, // Current attribute - line, // Current line - column, // Current column - page_column; // Current page column - - const char *val; // Option value - char keyword[64], // Keyword string - *keyptr; // Pointer into string - int keycol; // Column where keyword starts - enum {NLstyl = -1, NoCmnt, SNTXstyl} - cmntState; // Inside a comment - enum {StrBeg = -1, NoStr, StrEnd} - strState; // Inside a dbl-quoted string - - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - FILE *fp; // Print file - int stdoutbackupfd; // The "real" stdout is backupped here while - // stdout is redirected - int ret = 0; // Return value - cups_cspace_t cspace = (cups_cspace_t)(-1); - - - // - // Make sure status messages are not buffered... - // - - doc.UTF8 = 1; // Use UTF-8 encoding? - doc.WrapLines = 1; // Wrap text in lines - doc.SizeLines = 60; // Number of lines on a page - doc.SizeColumns = 80; // Number of columns on a line - doc.PageColumns = 1; // Number of columns on a page - doc.ColumnGutter = 0; // Number of characters between text columns - doc.ColumnWidth = 80; // Width of each column - doc.PrettyPrint = 0; // Do pretty code formatting - doc.Copies = 1; // Number of copies - doc.Page = NULL; // Page characters - doc.NumPages = 0; // Number of pages in document - doc.CharsPerInch = 10; // Number of character columns per inch - doc.LinesPerInch = 6; // Number of lines per inch - doc.NumKeywords = 0; // Number of known keywords - doc.Keywords = NULL; // List of known keywords - doc.Orientation = 0; // 0 = portrait, 1 = landscape, etc. - doc.Duplex = 0; // Duplexed? - doc.LanguageLevel = 1; // Language level of printer - doc.ColorDevice = 1; // Do color text? - doc.PageLeft = 18.0f; // Left margin - doc.PageRight = 594.0f; // Right margin - doc.PageBottom = 36.0f; // Bottom margin - doc.PageTop = 756.0f; // Top margin - doc.PageWidth = 612.0f; // Total page width - doc.PageLength = 792.0f; // Total page length - doc.pdf = NULL; - - if (parameters) - doc.env_vars = *((cf_filter_texttopdf_parameter_t *)parameters); - else - { - doc.env_vars.data_dir = CUPS_DATADIR; - doc.env_vars.char_set = NULL; - doc.env_vars.content_type = NULL; - doc.env_vars.classification = NULL; - } - - setbuf(stderr, NULL); - - // - // Open the input data stream specified by the inputfd... - // - - if ((fp = fdopen(inputfd, "rb")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "textopdf: Unable to open input data stream."); - } - return (1); - } - - // - // Redirect stdout to the outputfd (the PDF output strem of this filter - // function) - // - - if (outputfd != 1) - { - stdoutbackupfd = dup(1); - dup2(outputfd, 1); - close(outputfd); - } - - // - // Process command-line options and write the prolog... - // - - if ((val = cupsGetOption("prettyprint", - data->num_options, data->options)) != NULL && - strcasecmp(val, "no") && strcasecmp(val, "off") && - strcasecmp(val, "false")) - { - doc.PageLeft = 72.0f; - doc.PageRight = doc.PageWidth - 36.0f; - doc.PageBottom = doc.PageBottom > 36.0f ? doc.PageBottom : 36.0f; - doc.PageTop = doc.PageLength - 36.0f; - doc.CharsPerInch = 12; - doc.LinesPerInch = 8; - - if ((val = doc.env_vars.content_type) == NULL) - { - doc.PrettyPrint = PRETTY_PLAIN; - doc.NumKeywords = 0; - doc.Keywords = NULL; - } - else if (strcasecmp(val, "application/x-cshell") == 0) - { - doc.PrettyPrint = PRETTY_SHELL; - doc.NumKeywords = sizeof(csh_keywords) / sizeof(csh_keywords[0]); - doc.Keywords = csh_keywords; - } - else if (strcasecmp(val, "application/x-csource") == 0) - { - doc.PrettyPrint = PRETTY_CODE; - doc.NumKeywords = sizeof(code_keywords) / sizeof(code_keywords[0]); - doc.Keywords = code_keywords; - } - else if (strcasecmp(val, "application/x-perl") == 0) - { - doc.PrettyPrint = PRETTY_PERL; - doc.NumKeywords = sizeof(perl_keywords) / sizeof(perl_keywords[0]); - doc.Keywords = perl_keywords; - } - else if (strcasecmp(val, "application/x-shell") == 0) - { - doc.PrettyPrint = PRETTY_SHELL; - doc.NumKeywords = sizeof(sh_keywords) / sizeof(sh_keywords[0]); - doc.Keywords = sh_keywords; - } - else - { - doc.PrettyPrint = PRETTY_PLAIN; - doc.NumKeywords = 0; - doc.Keywords = NULL; - } - } - - cfRasterPrepareHeader(&(doc.h), data, CF_FILTER_OUT_FORMAT_CUPS_RASTER, - CF_FILTER_OUT_FORMAT_CUPS_RASTER, 0, &cspace); - doc.Orientation = doc.h.Orientation; - doc.Duplex = doc.h.Duplex; - doc.ColorDevice = doc.h.cupsNumColors <= 1 ? 0 : 1; - doc.PageWidth = doc.h.cupsPageSize[0] != 0.0 ? doc.h.cupsPageSize[0] : - (float)doc.h.PageSize[0]; - doc.PageLength = doc.h.cupsPageSize[1] != 0.0 ? doc.h.cupsPageSize[1] : - (float)doc.h.PageSize[1]; - doc.PageLeft = doc.h.cupsImagingBBox[0] != 0.0 ? doc.h.cupsImagingBBox[0] : - (float)doc.h.ImagingBoundingBox[0]; - doc.PageBottom = doc.h.cupsImagingBBox[1] != 0.0 ? - doc.h.cupsImagingBBox[1] : (float)doc.h.ImagingBoundingBox[1]; - doc.PageRight = doc.h.cupsImagingBBox[2] != 0.0 ? doc.h.cupsImagingBBox[2] : - (float)doc.h.ImagingBoundingBox[2]; - doc.PageTop = doc.h.cupsImagingBBox[3] != 0.0 ? doc.h.cupsImagingBBox[3] : - (float)doc.h.ImagingBoundingBox[3]; - doc.Copies = doc.h.NumCopies; - - // Check whether we do borderless printing with overspray and let text only - // get printed on the actual media size - if (doc.h.cupsPageSizeName[0] != '\0') - { - // The page size name in te header corresponds to the actual size of - // the media, so find the size dimensions - pwg_media_t *size_found = NULL; - strncpy(keyword, doc.h.cupsPageSizeName, sizeof(keyword)); - if ((keyptr = strchr(keyword, '.')) != NULL) - *keyptr = '\0'; - if ((size_found = pwgMediaForPPD(keyword)) != NULL || - (size_found = pwgMediaForLegacy(keyword)) != NULL || - (size_found = pwgMediaForPWG(keyword)) != NULL) - { - // Dimensions in PostScript points - float w = size_found->width / 2540.0 * 72.0; - float l = size_found->length / 2540.0 * 72.0; - if (w < doc.PageWidth) - { - // Width in header > actual media width => overspray - // As the overspray is to cover tolerances in paper traction - // and the paper can be mis-aligned to any size, we let - // margins be at least double the overspray width on each - // side (not dividing by 2) - float margin_needed = doc.PageWidth - w; - if (doc.PageLeft < margin_needed) - doc.PageLeft = margin_needed; - if (doc.PageWidth - doc.PageRight < margin_needed) - doc.PageRight = doc.PageWidth - margin_needed; - } - if (l < doc.PageLength) - { - // Length in header > actual media length => overspray - // As the overspray is to cover tolerances in paper traction - // and the paper can be mis-aligned to any size, we let - // margins be at least double the overspray width on each - // side (not dividing by 2) - float margin_needed = doc.PageLength - l; - if (doc.PageBottom < margin_needed) - doc.PageBottom = margin_needed; - if (doc.PageLength - doc.PageTop < margin_needed) - doc.PageTop = doc.PageLength - margin_needed; - } - } - } - - if ((val = cupsGetOption("wrap", data->num_options, data->options)) == NULL) - doc.WrapLines = 1; - else - doc.WrapLines = !strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes"); - - if ((val = cupsGetOption("columns", data->num_options, - data->options)) != NULL) - { - doc.PageColumns = atoi(val); - - if (doc.PageColumns < 1) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad columns value %d", doc.PageColumns); - ret = 1; - goto out; - } - } - - if ((val = cupsGetOption("cpi", data->num_options, data->options)) != NULL) - { - doc.CharsPerInch = atof(val); - - if (doc.CharsPerInch <= 0.0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad cpi value %f", doc.CharsPerInch); - ret = 1; - goto out; - } - } - - if ((val = cupsGetOption("lpi", data->num_options, data->options)) != NULL) - { - doc.LinesPerInch = atof(val); - - if (doc.LinesPerInch <= 0.0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad lpi value %f", doc.LinesPerInch); - ret = 1; - goto out; - } - } - - if (doc.PrettyPrint) - doc.PageTop -= 216.0f / doc.LinesPerInch; - - // - // Allocate memory for the page... - // - - doc.SizeColumns = (doc.PageRight - doc.PageLeft) / 72.0 * doc.CharsPerInch; - doc.SizeLines = (doc.PageTop - doc.PageBottom) / 72.0 * doc.LinesPerInch; - - // - // Enforce minimum size... - // - - if (doc.SizeColumns < 1) - doc.SizeColumns = 1; - if (doc.SizeLines < 1) - doc.SizeLines = 1; - - if (doc.SizeLines >= INT_MAX / doc.SizeColumns / sizeof(lchar_t)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, "cfFilterTextToPDF: Bad page size"); - ret = 1; - goto out; - } - - doc.Page = calloc(sizeof(lchar_t *), doc.SizeLines); - if (!doc.Page) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: cannot allocate memory for page"); - ret = 1; - goto out; - } - - doc.Page[0] = calloc(sizeof(lchar_t), doc.SizeColumns * doc.SizeLines); - if (!doc.Page[0]) - { - free(doc.Page); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: cannot allocate memory for page"); - ret = 1; - goto out; - } - - for (i = 1; i < doc.SizeLines; i ++) - doc.Page[i] = doc.Page[0] + i * doc.SizeColumns; - - doc.Copies = data->copies; - - // - // Read text from the specified source and print it... - // - - empty = 1; - lastch = 0; - column = 0; - line = 0; - page_column = 0; - attr = 0; - keyptr = keyword; - keycol = 0; - cmntState = NoCmnt; - strState = NoStr; - - while ((ch = get_utf8(fp)) >= 0) - { - if (empty) - { - // Found the first valid character, write file header - empty = 0; - ret = write_prolog(data->job_title, data->job_user, - doc.env_vars.classification, - cupsGetOption("page-label", data->num_options, - data->options), - &doc, log, ld); - if (ret) - goto out; - } - - // - // 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 (doc.PrettyPrint && keyptr > keyword) - { - *keyptr = '\0'; - keyptr = keyword; - - if (bsearch(&keyptr, doc.Keywords, doc.NumKeywords, sizeof(char *), - compare_keywords)) - { - // - // Put keywords in boldface... - // - - i = page_column * (doc.ColumnWidth + doc.ColumnGutter); - - while (keycol < column) - { - doc.Page[line][keycol + i].attr |= ATTR_BOLD; - keycol ++; - } - } - } - - column = (column + 8) & ~7; - - if (column >= doc.ColumnWidth && doc.WrapLines) - { // Wrap text to margins - line ++; - column = 0; - - if (line >= doc.SizeLines) - { - page_column ++; - line = 0; - - if (page_column >= doc.PageColumns) - { - write_page(&doc); - page_column = 0; - } - } - } - - keycol = column; - - attr &= ~ATTR_BOLD; - break; - - case 0x0d : // CR -#ifndef __APPLE__ - // - // All but MacOS/Darwin treat CR as was intended by ANSI - // folks, namely to move to column 0/1. Some programs still - // use this to do boldfacing and underlining... - // - - column = 0; - break; -#else - // - // MacOS/Darwin still need to treat CR as a line ending. - // - - { - int nextch; - if ((nextch = getc(fp)) != 0x0a) - ungetc(nextch, fp); - else - ch = nextch; - } -#endif // !__APPLE__ - - case 0x0a : // LF - output current line - if (doc.PrettyPrint && keyptr > keyword) - { - *keyptr = '\0'; - keyptr = keyword; - - if (bsearch(&keyptr, doc.Keywords, doc.NumKeywords, sizeof(char *), - compare_keywords)) - { - // - // Put keywords in boldface... - // - - i = page_column * (doc.ColumnWidth + doc.ColumnGutter); - - while (keycol < column) - { - doc.Page[line][keycol + i].attr |= ATTR_BOLD; - keycol ++; - } - } - } - - line ++; - column = 0; - keycol = 0; - - if (cmntState == NLstyl) - cmntState = NoCmnt; - - if (!cmntState && !strState) - attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | - ATTR_BLUE); - - if (line >= doc.SizeLines) - { - page_column ++; - line = 0; - - if (page_column >= doc.PageColumns) - { - write_page(&doc); - page_column = 0; - } - } - break; - - case 0x0b : // VT - move up 1 line - if (line > 0) - line --; - - keyptr = keyword; - keycol = column; - - if (cmntState == NLstyl) - cmntState = NoCmnt; - - if (!cmntState && !strState) - attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | - ATTR_BLUE); - break; - - case 0x0c : // FF - eject current page... - if (doc.PrettyPrint && keyptr > keyword) - { - *keyptr = '\0'; - keyptr = keyword; - - if (bsearch(&keyptr, doc.Keywords, doc.NumKeywords, sizeof(char *), - compare_keywords)) - { - // - // Put keywords in boldface... - // - - i = page_column * (doc.ColumnWidth + doc.ColumnGutter); - - while (keycol < column) - { - doc.Page[line][keycol + i].attr |= ATTR_BOLD; - keycol ++; - } - } - } - - page_column ++; - column = 0; - keycol = 0; - line = 0; - - if (cmntState == NLstyl) - cmntState = NoCmnt; - - if (!cmntState && !strState) - attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | - ATTR_BLUE); - - if (page_column >= doc.PageColumns) - { - write_page(&doc); - page_column = 0; - } - break; - - case 0x1b : // Escape sequence - ch = get_utf8(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 < (doc.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 (doc.PrettyPrint > PRETTY_PLAIN) - { - // - // Do highlighting of C/C++ keywords, preprocessor commands, - // and comments... - // - - if (ch == ' ' && (attr & ATTR_BOLD)) - { - // - // Stop bolding preprocessor command... - // - - attr &= ~ATTR_BOLD; - } - else if (!(isalnum(ch & 255) || ch == '_') && keyptr > keyword) - { - // - // Look for a keyword... - // - - *keyptr = '\0'; - keyptr = keyword; - - if (bsearch(&keyptr, doc.Keywords, doc.NumKeywords, - sizeof(char *), compare_keywords)) - { - // - // Put keywords in boldface... - // - - i = page_column * (doc.ColumnWidth + doc.ColumnGutter); - - while (keycol < column) - { - doc.Page[line][keycol + i].attr |= ATTR_BOLD; - keycol ++; - } - } - } - - // - // Look for Syntax-transition Starts... - // - - if (!cmntState && !strState) - { - if ((isalnum(ch & 255) || ch == '_')) - { - // - // 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 != '\\') - { - // - // Start a dbl-quote string constant... - // - - strState = StrBeg; - attr = ATTR_BLUE; - } - else if (ch == '*' && lastch == '/' && - doc.PrettyPrint != PRETTY_SHELL) - { - // - // Start a C-style comment... - // - - cmntState = SNTXstyl; - attr = ATTR_ITALIC | ATTR_GREEN; - } - else if (ch == '/' && lastch == '/' && - doc.PrettyPrint == PRETTY_CODE) - { - // - // Start a C++-style comment... - // - - cmntState = NLstyl; - attr = ATTR_ITALIC | ATTR_GREEN; - } - else if (ch == '#' && doc.PrettyPrint != PRETTY_CODE) - { - // - // Start a shell-style comment... - // - - cmntState = NLstyl; - attr = ATTR_ITALIC | ATTR_GREEN; - } - else if (ch == '#' && column == 0 && - doc.PrettyPrint == PRETTY_CODE) - { - // - // Start a preprocessor command... - // - - attr = ATTR_BOLD | ATTR_RED; - } - } - } - - if (column >= doc.ColumnWidth && doc.WrapLines) - { // Wrap text to margins - column = 0; - line ++; - - if (line >= doc.SizeLines) - { - page_column ++; - line = 0; - - if (page_column >= doc.PageColumns) - { - write_page(&doc); - page_column = 0; - } - } - } - - // - // Add text to the current column & line... - // - - if (column < doc.ColumnWidth) - { - i = column + page_column * (doc.ColumnWidth + doc.ColumnGutter); - - if (doc.PrettyPrint) - doc.Page[line][i].attr = attr; - - if (ch == ' ' && doc.Page[line][i].ch) - ch = doc.Page[line][i].ch; - else if (ch == doc.Page[line][i].ch) - doc.Page[line][i].attr |= ATTR_BOLD; - else if (doc.Page[line][i].ch == '_') - doc.Page[line][i].attr |= ATTR_UNDERLINE; - else if (ch == '_') - { - doc.Page[line][i].attr |= ATTR_UNDERLINE; - - if (doc.Page[line][i].ch) - ch = doc.Page[line][i].ch; - } - else - doc.Page[line][i].attr = attr; - - doc.Page[line][i].ch = ch; - } - - if (doc.PrettyPrint) - { - if ((ch == '{' || ch == '}') && !cmntState && !strState && - column < doc.ColumnWidth) - { - // - // Highlight curley braces... - // - - doc.Page[line][column].attr |= ATTR_BOLD; - } - else if ((ch == '/' || ch == '*') && lastch == '/' && - column < doc.ColumnWidth && - doc.PrettyPrint != PRETTY_SHELL) - { - // - // Highlight first comment character... - // - - doc.Page[line][column - 1].attr = attr; - } - else if (ch == '\"' && lastch != '\\' && !cmntState && - strState == StrEnd) - { - // - // End a dbl-quote string constant... - // - - strState = NoStr; - attr &= ~ATTR_BLUE; - } - else if (ch == '/' && lastch == '*' && cmntState) - { - // - // End a C-style comment... - // - - cmntState = NoCmnt; - attr &= ~(ATTR_ITALIC | ATTR_GREEN); - } - - if (strState == StrBeg) - strState = StrEnd; - } - - column ++; - break; - } - - // - // Save this character for the next cycle. - // - - lastch = ch; - } - - // Do not write anything if the input file is empty - if (empty) - { - if(log) log(ld, CF_LOGLEVEL_DEBUG, - "Input is empty, outputting empty file"); - goto out; - } - - // - // Write any remaining page data... - // - - if (line > 0 || page_column > 0 || column > 0) - write_page(&doc); - - // - // Write the epilog and return... - // - - write_epilogue(&doc); - - out: - - // - // Close input data stream - // - - if (fp != stdin) - fclose(fp); - - // - // Flush and close output data stream - // - - fflush(stdout); - close(1); - - // - // Re-activate stdout output - // - - if (outputfd != 1) - { - dup2(stdoutbackupfd, 1); - close(stdoutbackupfd); - } - - // - // Clean up - // - - if (doc.Page) - { - free(doc.Page[0]); - free(doc.Page); - } - - return (ret); -} - - -static _cf_fontembed_emb_params_t * -font_load(const char *font, - int fontwidth, - cf_logfunc_t log, - void *ld) -{ - _cf_fontembed_otf_file_t *otf; - - FcPattern *pattern; - FcFontSet *candidates; - FcChar8 *fontname = NULL; - FcResult result; - int i; - - if ((font[0] == '/') || (font[0] == '.')) - { - candidates = NULL; - fontname = (FcChar8 *)strdup(font); - } - else - { - FcInit(); - pattern = FcNameParse ((const FcChar8 *)font); - FcPatternAddInteger(pattern, FC_SPACING, FC_MONO); - // guide fc, in case substitution becomes necessary - FcConfigSubstitute (0, pattern, FcMatchPattern); - FcDefaultSubstitute (pattern); - - // Receive a sorted list of fonts matching our pattern - candidates = FcFontSort (0, pattern, FcFalse, 0, &result); - FcPatternDestroy (pattern); - - if (candidates) - { - // In the list of fonts returned by FcFontSort() - // find the first one that is both in TrueType format and monospaced - for (i = 0; i < candidates->nfont; i ++) - { - FcChar8 *fontformat = NULL; // TODO? or just try? - int spacing = 0; // sane default, as FC_MONO == 100 - FcPatternGetString(candidates->fonts[i], FC_FONTFORMAT, 0, &fontformat); - FcPatternGetInteger(candidates->fonts[i], FC_SPACING, 0, &spacing); - - if ((fontformat) && ((spacing == FC_MONO) || (fontwidth == 2))) - { - // check for monospace or double width fonts - if (strcmp((const char *)fontformat, "TrueType") == 0) - { - fontname = - FcPatternFormat(candidates->fonts[i], - (const FcChar8 *)"%{file|cescape}/%{index}"); - break; - } - else if (strcmp((const char *)fontformat, "CFF") == 0) - { - fontname = - FcPatternFormat (candidates->fonts[i], - (const FcChar8 *)"%{file|cescape}"); - // TTC only possible with non-cff glyphs! - break; - } - } - } - FcFontSetDestroy (candidates); - } - } - - if (!fontname) - { - // TODO: try /usr/share/fonts/*/*/%s.ttf - if(log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: No viable font found."); - return (NULL); - } - - otf = _cfFontEmbedOTFLoad((const char *)fontname); - free(fontname); - if (!otf) - return (NULL); - - _cf_fontembed_fontfile_t *ff = _cfFontEmbedFontFileOpenSFNT(otf); - DEBUG_assert(ff); - _cf_fontembed_emb_params_t *emb = - _cfFontEmbedEmbNew(ff, - _CF_FONTEMBED_EMB_DEST_PDF16, - _CF_FONTEMBED_EMB_C_FORCE_MULTIBYTE | - _CF_FONTEMBED_EMB_C_TAKE_FONTFILE); - DEBUG_assert(emb); - DEBUG_assert(emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE); - - return (emb); -} - -static _cf_fontembed_emb_params_t * -font_std(const char *name) -{ - _cf_fontembed_fontfile_t *ff = _cfFontEmbedFontFileOpenStd(name); - DEBUG_assert(ff); - _cf_fontembed_emb_params_t *emb = - _cfFontEmbedEmbNew(ff, - _CF_FONTEMBED_EMB_DEST_PDF16, - _CF_FONTEMBED_EMB_C_TAKE_FONTFILE); - DEBUG_assert(emb); - - return (emb); -} - - -// -// '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))); -} - - -// -// 'get_utf8()' - Get a UTF-8 encoded wide character... -// - -static int // O - Character or -1 on error -get_utf8(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) // 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); - } -} - - -// -// 'write_epilogue()' - Write the PDF file epilogue. -// - -static void -write_epilogue(texttopdf_doc_t *doc) -{ - static char *names[] = // Font names - { "FN","FB","FI","FBI" }; - int i,j; - - // embed fonts - for (i = doc->PrettyPrint ? 3 : 1; i >= 0; i --) - { - for (j = 0; j < doc->NumFonts; j ++) - { - _cf_fontembed_emb_params_t *emb = doc->Fonts[j][i]; - if (emb->font->fobj) // already embedded - continue; - if ((!emb->subset) || - (_cfFontEmbedBitsUsed(emb->subset, emb->font->sfnt->numGlyphs))) - { - emb->font->fobj = cfPDFOutWriteFont(doc->pdf,emb); - DEBUG_assert(emb->font->fobj); - } - } - } - - // - // Create the global fontdict - // - - // now fix FontResource - doc->pdf->xref[doc->FontResource - 1] = doc->pdf->filepos; - cfPDFOutPrintF(doc->pdf,"%d 0 obj\n" - "<<\n", - doc->FontResource); - - for (i = doc->PrettyPrint ? 3 : 1; i >= 0; i --) - { - for (j = 0; j < doc->NumFonts; j ++) - { - _cf_fontembed_emb_params_t *emb = doc->Fonts[j][i]; - if (emb->font->fobj) // used - cfPDFOutPrintF(doc->pdf, " /%s%02x %d 0 R\n", names[i], j, - emb->font->fobj); - } - } - - cfPDFOutPrintF(doc->pdf,">>\n" - "endobj\n"); - - cfPDFOutFinishPDF(doc->pdf); - - cfPDFOutFree(doc->pdf); -} - - -// -// {{{ 'write_page()' - Write a page of text. -// - -static void -write_page(texttopdf_doc_t *doc) -{ - int line; // Current line - - int content = cfPDFOutAddXRef(doc->pdf); - cfPDFOutPrintF(doc->pdf,"%d 0 obj\n" - "<>\n" - "stream\n" - "q\n", - content, content + 1); - long size = -((doc->pdf->filepos) - 2); - - (doc->NumPages) ++; - if (doc->PrettyPrint) - write_pretty_header(doc); - - for (line = 0; line < doc->SizeLines; line ++) - write_line(line, doc->Page[line], doc); - - size+= ((doc->pdf->filepos) + 2); - cfPDFOutPrintF(doc->pdf,"Q\n" - "endstream\n" - "endobj\n"); - - int len_obj = cfPDFOutAddXRef(doc->pdf); - DEBUG_assert(len_obj == content + 1); - cfPDFOutPrintF(doc->pdf,"%d 0 obj\n" - "%ld\n" - "endobj\n", - len_obj,size); - - int obj = cfPDFOutAddXRef(doc->pdf); - cfPDFOutPrintF(doc->pdf,"%d 0 obj\n" - "<>\n" - ">>\n" - "endobj\n", - obj,doc->PageWidth, doc->PageLength, content, - doc->FontResource); - cfPDFOutAddPage(doc->pdf,obj); - - memset(doc->Page[0], 0, - sizeof(lchar_t) * (doc->SizeColumns) * (doc->SizeLines)); -} -// }}} - - -// -// {{{'write_prolog()' - Write the PDF file prolog with options. -// - -static int -write_prolog(const char *title, // I - Title of job - const char *user, // I - Username - const char *classification,// I - Classification - const char *label, // I - Page label - texttopdf_doc_t *doc, - cf_logfunc_t log, - void *ld) -{ - int i, j, k; // Looping vars - const char *charset; // Character set string - char filename[1024]; // Glyph filenames - FILE *fp; // Glyph files - const char *datadir; // CUPS_DATADIR environment variable - char line[1024], // Line from file - *lineptr, // Pointer into line - *valptr; // Pointer to value in line - int start, end; // Start and end values for range - time_t curtime; // Current time - struct tm *curtm; // Current date - char curdate[255]; // Current date (text format) - int num_fonts = 0; // Number of unique fonts - _cf_fontembed_emb_params_t *fonts[1024]; // Unique fonts - char *fontnames[1024]; // Unique fonts -#if 0 - static char *names[] = // Font names - { - "FN", "FB", "FI" - /* - "cupsNormal", - "cupsBold", - "cupsItalic" - */ - }; -#endif - - - // - // Get the data directory... - // - - datadir = doc->env_vars.data_dir; - - // - // Adjust margins as necessary... - // - - if (classification || label) - { - // - // Leave room for labels... - // - - doc->PageBottom += 36; - doc->PageTop -= 36; - } - - if (doc->PageColumns > 1) - { - doc->ColumnGutter = doc->CharsPerInch / 2; - doc->ColumnWidth = (doc->SizeColumns - doc->ColumnGutter * - (doc->PageColumns - 1)) / - doc->PageColumns; - } - else - doc->ColumnWidth = doc->SizeColumns; - - // - // {{{ Output the PDF header... - // - - DEBUG_assert(!(doc->pdf)); - doc->pdf = cfPDFOutNew(); - DEBUG_assert(doc->pdf); - - cfPDFOutBeginPDF(doc->pdf); - cfPDFOutPrintF(doc->pdf,"%%cupsRotation: %d\n", - (doc->Orientation & 3) * 90); // TODO? - - cfPDFOutAddKeyValue(doc->pdf, "Creator", "texttopdf/" PACKAGE_VERSION); - - curtime = time(NULL); - curtm = localtime(&curtime); - strftime(curdate, sizeof(curdate), "%c", curtm); - - cfPDFOutAddKeyValue(doc->pdf, "CreationDate", cfPDFOutToPDFDate(curtm)); - cfPDFOutAddKeyValue(doc->pdf, "Title", title); - cfPDFOutAddKeyValue(doc->pdf, "Author", user); // was(PostScript): /For - // }}} - - // - // {{{ Initialize globals... - // - - doc->NumFonts = 0; - memset(doc->Fonts, 0, sizeof(doc->Fonts)); - memset(doc->Chars, 0, sizeof(doc->Chars)); - memset(doc->Codes, 0, sizeof(doc->Codes)); - // }}} - - // - // Get the output character set... - // - - charset = doc->env_vars.char_set; - if (charset != NULL && strcmp(charset, "us-ascii") != 0) // {{{ - { - snprintf(filename, sizeof(filename), "%s/charsets/pdf.%s", datadir, - charset); - - if ((fp = fopen(filename, "r")) == NULL) - { - // - // Can't open charset file! - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Unable to open %s: %s", - filename, strerror(errno)); - return (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); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad charset file %s", filename); - return (1); - } - - if (strncmp(line, "charset", 7) != 0) - { - // - // Bad format/not a charset file! - // - - fclose(fp); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad charset file %s", filename); - return (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 & 255); lineptr ++); - // Skip whitespace - - if (strcmp(lineptr, "utf8") == 0) // {{{ - { - // - // UTF-8 (Unicode) text... - // - - doc->UTF8 = 1; - - // - // Read the font descriptions... - // - - doc->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 & 255)) - lineptr ++; - - valptr = lineptr; - - while (!isspace(*lineptr & 255) && *lineptr) - lineptr ++; - - if (!*lineptr) - { - // - // Can't have a font without all required values... - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad font description line: %s", valptr); - return (1); - } - - *lineptr++ = '\0'; - - if (strcmp(valptr, "ltor") == 0) - doc->Directions[doc->NumFonts] = 1; - else if (strcmp(valptr, "rtol") == 0) - doc->Directions[doc->NumFonts] = -1; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad text direction %s", valptr); - return (1); - } - - // - // Got the direction, now get the width... - // - - while (isspace(*lineptr & 255)) - lineptr ++; - - valptr = lineptr; - - while (!isspace(*lineptr & 255) && *lineptr) - lineptr ++; - - if (!*lineptr) - { - // - // Can't have a font without all required values... - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad font description line: %s", valptr); - return (1); - } - - *lineptr++ = '\0'; - - if (strcmp(valptr, "single") == 0) - doc->Widths[doc->NumFonts] = 1; - else if (strcmp(valptr, "double") == 0) - doc->Widths[doc->NumFonts] = 2; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad text width %s", valptr); - return (1); - } - - // - // Get the fonts... - // - - for (i = 0; *lineptr && i < 4; i ++) - { - while (isspace(*lineptr & 255)) - lineptr ++; - - valptr = lineptr; - - while (!isspace(*lineptr & 255) && *lineptr) - lineptr ++; - - if (*lineptr) - *lineptr++ = '\0'; - - if (lineptr > valptr) - { - // search for duplicates - for (k = 0; k < num_fonts; k ++) - if (strcmp(valptr, fontnames[k]) == 0) - { - doc->Fonts[doc->NumFonts][i] = fonts[k]; - break; - } - - if (k == num_fonts) // not found - { - fonts[num_fonts] = doc->Fonts[doc->NumFonts][i] = - font_load(valptr, doc->Widths[doc->NumFonts], log, ld); - if (!fonts[num_fonts]) // font missing/corrupt, replace by first - { - if(log) log(ld, CF_LOGLEVEL_WARN, - "cfFilterTextToPDF: Ignored bad font \"%s\"",valptr); - break; - } - fontnames[num_fonts++] = strdup(valptr); - } - } - } - - // ignore complete range, when the first font is not available - if (i == 0) - continue; - - // - // Fill in remaining fonts as needed... - // - - for (j = i; j < 4; j ++) - doc->Fonts[doc->NumFonts][j] = doc->Fonts[doc->NumFonts][0]; - - // - // Define the character mappings... - // - - for (i = start; i <= end; i ++) - doc->Codes[i] = doc->NumFonts; - - // - // Move to the next font, stopping if needed... - // - - doc->NumFonts ++; - if (doc->NumFonts >= 256) - break; - } - - fclose(fp); - } // }}} - else // {{{ - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF: Bad charset type %s", lineptr); - return (1); - } // }}} - } // }}} - else // {{{ Standard ASCII - { - // - // Standard ASCII output just uses Courier, Courier-Bold, and - // possibly Courier-Oblique. - // - - doc->NumFonts = 1; - - doc->Fonts[0][ATTR_NORMAL] = font_std("Courier"); - doc->Fonts[0][ATTR_BOLD] = font_std("Courier-Bold"); - doc->Fonts[0][ATTR_ITALIC] = font_std("Courier-Oblique"); - doc->Fonts[0][ATTR_BOLDITALIC] = font_std("Courier-BoldOblique"); - - doc->Widths[0] = 1; - doc->Directions[0] = 1; - - // - // Define US-ASCII characters... - // - - for (i = 32; i < 127; i ++) - { - doc->Chars[i] = i; - doc->Codes[i] = doc->NumFonts - 1; - } - } - // }}} - - if (doc->NumFonts == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToPDF:No usable font available"); - return (1); - } - - doc->FontScaleX = 120.0 / (doc->CharsPerInch); - doc->FontScaleY = 68.0 / (doc->LinesPerInch); - - // allocate now, for pages to use. will be fixed in epilogue - doc->FontResource = cfPDFOutAddXRef(doc->pdf); - - if (doc->PrettyPrint) - { - doc->Date = make_wide(curdate, doc); - doc->Title = make_wide(title, doc); - } - - return (0); -} -// }}} - - -// -// {{{ '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 - texttopdf_doc_t *doc) -{ - int i; // Looping var - int col,xcol,xwid; // Current column - int attr; // Current attribute - int font, // Font to use - lastfont, // Last font - mono; // Monospaced? - lchar_t *start; // First character in sequence - - - xcol = 0; - for (col = 0, start = line; col < doc->SizeColumns;) - { - while (col < doc->SizeColumns && (line->ch == ' ' || line->ch == 0)) - { - col ++; - xcol ++; - line ++; - } - - if (col >= doc->SizeColumns) - break; - - if (doc->NumFonts == 1) - { - // - // All characters in a single font - assume monospaced and single width... - // - - attr = line->attr; - start = line; - - while (col < doc->SizeColumns && line->ch != 0 && attr == line->attr) - { - col ++; - line ++; - } - - write_string(col - (line - start), row, line - start, start, doc); - } - else - { - // - // Multiple fonts; break up based on the font... - // - - attr = line->attr; - start = line; - xwid = 0; - if (doc->UTF8) - lastfont = doc->Codes[line->ch]; - else - lastfont = doc->Codes[doc->Chars[line->ch]]; - //mono = strncmp(Fonts[lastfont][0], "Courier", 7) == 0; - mono = 1; // TODO - - col ++; - xwid += (doc->Widths[lastfont]); - line ++; - - if (mono) - { - while (col < doc->SizeColumns && line->ch != 0 && attr == line->attr) - { - if (doc->UTF8) - font = doc->Codes[line->ch]; - else - font = doc->Codes[doc->Chars[line->ch]]; - if (//strncmp(Fonts[font][0], "Courier", 7) != 0 ||*/ // TODO - font != lastfont) - break; - - col ++; - xwid += (doc->Widths[lastfont]); - line ++; - } - } - - if (doc->Directions[lastfont] > 0) - { - write_string(xcol, row, line - start, start,doc); - xcol += xwid; - } - else - { - // - // Do right-to-left text... ; assume no font change without direction - // change - // - - while (col < doc->SizeColumns && line->ch != 0 && attr == line->attr) - { - if (doc->UTF8) - font = doc->Codes[line->ch]; - else - font = doc->Codes[doc->Chars[line->ch]]; - if (doc->Directions[font] > 0 && - !ispunct(line->ch & 255) && !isspace(line->ch & 255)) - break; - - col ++; - xwid += doc->Widths[lastfont]; - line ++; - } - - for (i = 1; start < line; i ++, start ++) - if (!isspace(start->ch & 255)) - { - xwid-=(doc->Widths[lastfont]); - write_string(xcol + xwid, row, 1, start, doc); - } - else - xwid--; - } - } - } -} -// }}} - - -static lchar_t -*make_wide(const char *buf, - texttopdf_doc_t *doc) - // {{{ - convert to lchar_t -{ - const unsigned char *utf8; // UTF8 text - lchar_t *ret,*out; - - - // This is enough, utf8 chars will only require less space - out = ret = malloc((strlen(buf) + 1) * sizeof(lchar_t)); - - utf8 = (const unsigned char *)buf; - while (*utf8) - { - out->attr = 0; - - if (*utf8 < 0xc0 || !(doc->UTF8)) - out->ch = *utf8 ++; - else if ((*utf8 & 0xe0) == 0xc0) - { - // - // Two byte character... - // - - out->ch = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f); - utf8 += 2; - } - else - { - // - // Three byte character... - // - - out->ch = ((((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)) << 6) | - (utf8[2] & 0x3f); - utf8 += 3; - } - - out++; - } - out->ch = out->attr = 0; - return (ret); -} -// }}} - - -// -// {{{ '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 - texttopdf_doc_t *doc) -{ - float x, y; // Position of text - unsigned attr; // Character attributes - - - // - // Position the text and set the font... - // - - if (doc->Duplex && (doc->NumPages & 1) == 0) - { - x = doc->PageWidth - doc->PageRight; - y = doc->PageTop; - } - else - { - x = doc->PageLeft; - y = doc->PageTop; - } - - x += (float)col * 72.0f / (float)(doc->CharsPerInch); - y -= (float)(row + 0.843) * 72.0f / (float)(doc->LinesPerInch); - - attr = s->attr; - - if (attr & ATTR_RAISED) - y += 36.0 / (float)(doc->LinesPerInch); - else if (attr & ATTR_LOWERED) - y -= 36.0 / (float)(doc->LinesPerInch); - - if (attr & ATTR_UNDERLINE) - cfPDFOutPrintF(doc->pdf,"q 0.5 w 0 g %.3f %.3f m %.3f %.3f l S Q ", - x, y - 6.8 / (doc->LinesPerInch), - x + (float)len * 72.0 / (float)(doc->CharsPerInch), - y - 6.8 / (doc->LinesPerInch)); - - if (doc->PrettyPrint) - { - if (doc->ColorDevice) - { - if (attr & ATTR_RED) - cfPDFOutPrintF(doc->pdf, "0.5 0 0 rg\n"); - else if (attr & ATTR_GREEN) - cfPDFOutPrintF(doc->pdf, "0 0.5 0 rg\n"); - else if (attr & ATTR_BLUE) - cfPDFOutPrintF(doc->pdf, "0 0 0.5 rg\n"); - else - cfPDFOutPrintF(doc->pdf, "0 g\n"); - } - else - { - if ((attr & ATTR_RED) || (attr & ATTR_GREEN) || (attr & ATTR_BLUE)) - cfPDFOutPrintF(doc->pdf, "0.2 g\n"); - else - cfPDFOutPrintF(doc->pdf, "0 g\n"); - } - } - else - cfPDFOutPrintF(doc->pdf, "0 g\n"); - - write_font_str(x, y, attr & ATTR_FONT, s, len, doc); -} -// }}} - - -// {{{ show >len characters from >str, using the right font(s) at >x,>y -static void -write_font_str(float x, - float y, - int fontid, - lchar_t *str, - int len, - texttopdf_doc_t *doc) -{ - unsigned short ch; // Current character - static char *names[] = // Font names - { "FN","FB","FI","FBI" }; - - - if (len == -1) - for (len = 0; str[len].ch; len++); - - cfPDFOutPrintF(doc->pdf, "BT\n"); - - if (x == (int)x) - cfPDFOutPrintF(doc->pdf," %.0f ", x); - else - cfPDFOutPrintF(doc->pdf," %.3f ", x); - - if (y == (int)y) - cfPDFOutPrintF(doc->pdf, "%.0f Td\n", y); - else - cfPDFOutPrintF(doc->pdf, "%.3f Td\n", y); - - int lastfont, font; - - // split on font boundary - while (len > 0) - { - // - // Write a hex string... - // - - if (doc->UTF8) - lastfont = doc->Codes[str->ch]; - else - lastfont = doc->Codes[doc->Chars[str->ch]]; - - _cf_fontembed_emb_params_t *emb = doc->Fonts[lastfont][fontid]; - _cf_fontembed_otf_file_t *otf = emb->font->sfnt; - - if (otf) // TODO? - { - cfPDFOutPrintF(doc->pdf," %.3f Tz\n", - doc->FontScaleX * 600.0 / - (_cfFontEmbedOTFGetWidth(otf, 4) * 1000.0 / - otf->unitsPerEm) * 100.0 / (doc->FontScaleY)); // TODO? - // gid == 4 is usually '!', the char after space. We just need "the" - // width for the monospaced font. gid == 0 is bad, and space might also - // be bad. - } - else - { - cfPDFOutPrintF(doc->pdf," %.3f Tz\n", - doc->FontScaleX*100.0/(doc->FontScaleY)); // TODO? - } - - cfPDFOutPrintF(doc->pdf," /%s%02x %.3f Tf <", - names[fontid],lastfont,doc->FontScaleY); - - while (len > 0) - { - if (doc->UTF8) - ch = str->ch; - else - ch = doc->Chars[str->ch]; - - font = doc->Codes[ch]; - if (lastfont != font) // only possible, when not used via - // write_string (e.g. utf-8filename.txt in - // prettyprint) - break; - if (otf) // TODO - { - const unsigned short gid = _cfFontEmbedEmbGet(emb, ch); - cfPDFOutPrintF(doc->pdf, "%04x", gid); - } - else // std 14 font with 7-bit us-ascii uses single byte encoding, TODO - cfPDFOutPrintF(doc->pdf, "%02x", ch); - - len --; - str ++; - } - - cfPDFOutPrintF(doc->pdf,"> Tj\n"); - } - cfPDFOutPrintF(doc->pdf,"ET\n"); -} -// }}} - - -static float -string_width_x(lchar_t *str, - texttopdf_doc_t * doc) -{ - int len; - - - for (len = 0; str[len].ch; len ++); - - return ((float)len * 72.0 / (float)(doc->CharsPerInch)); -} - - -static void -write_pretty_header(texttopdf_doc_t *doc) // {{{ -{ - float x, y; - cfPDFOutPrintF(doc->pdf,"q\n" - "0.9 g\n"); - - if (doc->Duplex && (doc->NumPages & 1) == 0) - { - x = doc->PageWidth - doc->PageRight; - y = doc->PageTop + 72.0f / (doc->LinesPerInch); - } - else - { - x = doc->PageLeft; - y = doc->PageTop + 72.0f / (doc->LinesPerInch); - } - - cfPDFOutPrintF(doc->pdf, "1 0 0 1 %.3f %.3f cm\n", x, y); // translate - cfPDFOutPrintF(doc->pdf, "0 0 %.3f %.3f re f\n", - doc->PageRight - doc->PageLeft, 144.0f / (doc->LinesPerInch)); - cfPDFOutPrintF(doc->pdf, "0 g 0 G\n"); - - if (doc->Duplex && (doc->NumPages & 1) == 0) - { - x = doc->PageRight - doc->PageLeft - 36.0f / - doc->LinesPerInch - string_width_x(doc->Title, doc); - y = (0.5f + 0.157f) * 72.0f / doc->LinesPerInch; - } - else - { - x = 36.0f / doc->LinesPerInch; - y = (0.5f + 0.157f) * 72.0f / doc->LinesPerInch; - } - write_font_str(x, y, ATTR_BOLD, doc->Title, -1, doc); - - x = (-string_width_x(doc->Date, doc) + doc->PageRight - doc->PageLeft) * 0.5; - write_font_str(x, y, ATTR_BOLD, doc->Date, -1, doc); - - // convert pagenumber to string - char tmp[20]; - tmp[19] = 0; - snprintf(tmp, 19, "%d", doc->NumPages); - lchar_t *pagestr = make_wide(tmp, doc); - - if (doc->Duplex && (doc->NumPages & 1) == 0) - x = 36.0f / doc->LinesPerInch; - else - x = doc->PageRight - doc->PageLeft - - 36.0f / doc->LinesPerInch - string_width_x(pagestr, doc); - write_font_str(x, y, ATTR_BOLD, pagestr, -1, doc); - free(pagestr); - - cfPDFOutPrintF(doc->pdf, "Q\n"); -} -// }}} diff --git a/cupsfilters/texttotext.c b/cupsfilters/texttotext.c deleted file mode 100644 index feea20f19..000000000 --- a/cupsfilters/texttotext.c +++ /dev/null @@ -1,1246 +0,0 @@ -// -// Text to Text (for text-only printers) filter function for libcupsfilters. -// -// Filter function to print text files on text-only printers. The -// filter has several configuration options so that it should work -// with most printer models. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1997-2006 by Easy Software Products. -// Copyright 2011-2016 by Till Kamppeter -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ipp.h" -#include "filter.h" - - -// -// Type definitions -// - -typedef enum overlong_line_e -{ - TRUNCATE = 0, - WORDWRAP = 1, - WRAPATWIDTH = 2 -} overlong_line_t; - -typedef enum newline_char_e -{ - LF = 0, - CR = 1, - CRLF = 2 -} newline_char_t; - - -// -// Local functions... -// - -static int is_true(const char *value); -static int is_false(const char *value); -static int check_range(char *page_ranges, int even_pages, - int odd_pages, int page); - -int -cfFilterTextToText(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - int i, j; // Looping vars - char *p; - int exit_status = 0; // Exit status - int fd; // Copy file descriptor - - int num_copies; // Number of copies - int num_options; // Number of options - cups_option_t *options; // Options - const char *val; // Option value - int num_lines = 66, // Lines per page - num_columns = 80; // Characters per line - int page_left = 0, // Page margins - page_right = 0, - page_top = 0, - page_bottom = 0, - num_lines_per_inch = 0, - num_chars_per_inch = 0; - int text_width, // Width of the text area on the page - text_height; // Height of the text area on the - // page - char encoding[64]; // The printer'a encoding, to which - // the incoming UTF-8 is converted, - // must be 1-byte-per-character - overlong_line_t overlong_lines = WRAPATWIDTH; - // How to treat overlong lines - int tab_width = 8; // Turn tabs to spaces with given - // width - int pagination = 1; // Paginate text to allow margins - // and page management - int send_ff = 1; // Send form-feed char at the end of - // each page - newline_char_t newline_char = CRLF; // Character to send at end of line - char *newline_char_str; - char *page_ranges = NULL; // Selection of pages to print - int even_pages = 1; // Print the even pages - int odd_pages = 1; // Print the odd pages - int reverse_order = 0; // Ouput pages in reverse order? - int collate = 1; // Collate multiple copies? - int page_size; // Number of bytes needed for a page, - // to allocate the memory for the - // output page buffer - char *out_page = NULL; // Output page buffer - cups_array_t *page_array = NULL; // Array to hold the output pages - // for collated copies and reverse - // output order - iconv_t cd; // Conversion descriptor, describes - // between which encodings iconv - // should convert - char outbuf[4096]; // Output buffer for iconv - char inbuf[2048]; // Input buffer for iconv - size_t outsize; // Space left in outbuf - size_t insize = 0; // Bytes still to be converted in - // inbuf - char *outptr = outbuf; // Pointer for next converted - // character to be dropped - char *inptr = inbuf; // Pointer to next character to be - // converted - size_t nread; // Number of bytes read from file - size_t nconv; // -1 on conversion error - int incomplete_char = 0; // Was last character in input buffer - // incomplete - int result = 0; // Conversion result (-1 on error) - char *procptr, // Pointer into conversion output - // to indicate next byte to process - // for output page creation - *destptr; // Pointer into output page buffer - // where next character will be put - int page, // Number of current output page - line, column; // Character coordiantes on output - // page - int page_empty; // Is the current output page still - // empty (no visible characters)? - int previous_is_cr; // Is the previous character processed - // a Carriage Return? - int new_line_started = 0; // Is the proceeding of starting a - // new line (left margin, new page's - // top margin, wrapped word) already - // done for this line? - int skip_rest_of_line = 0; // Are we truncating an overlong - // line? - int skip_spaces = 0; // Are we skipping spaces at a line - // break when word-wrapping? - char *wrapped_word = NULL; // Section of a word wrapped over - // to the next line - int num_pages = 0; // Number of pages which get actually - // printed - - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *ipp; - ipp_t *media_col_entry; - float paperdimensions[2]; - char buf[2048]; - - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - cups_file_t *outputfp; - - - // - // Make sure status messages are not buffered... - // - - setbuf(stderr, NULL); - - if ((outputfp = cupsFileOpenFd(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterBannerToPDF: Unable to open output data stream."); - } - return (1); - } - - // - // Number of copies - // - - num_copies = data->copies; - if (num_copies < 1) - num_copies = 1; - - // - // Parse the options and IPP attributes - // - - num_options = data->num_options; - options = data->options; - - // Number of lines and columns per inch This is mainly for - // development and debugging. With these set and the page size given - // as width and length dimension, the number of lines and columns on - // the page gets calculated. A way for using custom sizes. With - // these not set, the media-col-database entry for the page size is - // looked up and if it has the extra integer entries "num-lines" and - // "num-columns", the numbers here are used. - if ((val = cupsGetOption("NumLinesPerInch", - num_options, options)) != NULL || - (val = cupsGetOption("num-lines-per-inch", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "num-lines-per-inch", - IPP_TAG_INTEGER)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "num-lines-per-inch-default", - IPP_TAG_INTEGER)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - num_lines_per_inch = atoi(val); - if (log) - { - if (num_lines_per_inch <= 0) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Number of lines per inch not set, if page dimensions are given, find number of lines per page via pre-defined page sizes"); - else - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Number of lines per inch: %d", - num_lines_per_inch); - } - } - - if ((val = cupsGetOption("NumCharsPerInch", - num_options, options)) != NULL || - (val = cupsGetOption("num-chars-per-inch", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs,"num-chars-per-inch", - IPP_TAG_INTEGER)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "num-chars-per-inch-default", - IPP_TAG_INTEGER)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - num_chars_per_inch = atoi(val); - if (log) - { - if (num_chars_per_inch <= 0) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Number of charcters per inch not set, if page dimensions are given, find number of characters per line via pre-defined page sizes"); - else - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Number of characters per inch = %d", - num_chars_per_inch); - } - } - - cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, - NULL, 0, - &(paperdimensions[0]), &(paperdimensions[1]), - NULL, NULL, NULL, NULL, NULL, &media_col_entry); - if (media_col_entry) - { - // We have a media-col-database and the requested/default page size - // is contained in it (= size is supported), check whether we have - // a number of lines/columns registered via extra interger entries - // "num-columns" and "num-lines". - num_columns = ippGetInteger(ippFindAttribute(media_col_entry, - "num-columns", - IPP_TAG_ZERO), 0); - num_lines = ippGetInteger(ippFindAttribute(media_col_entry, - "num-lines", - IPP_TAG_ZERO), 0); - } - - if (num_chars_per_inch > 0 && paperdimensions[0] > 0) - num_columns = (int)((paperdimensions[0] / 72.0) * (num_chars_per_inch)); - if (num_lines_per_inch > 0 && paperdimensions[1] > 0) - num_lines = (int)((paperdimensions[1] / 72.0) * (num_lines_per_inch)); - - if (num_lines <= 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid number of lines %d, using default: 66", - num_lines); - num_lines = 66; - } - if (num_columns <= 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid number of columns %d, using default: 80", - num_columns); - num_columns = 80; - } - - // Direct specification of number of lines/columns, mainly for debugging - // and development - if ((val = cupsGetOption("PageHeight", num_options, options)) != NULL || - (val = cupsGetOption("page-height", num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "page-height", IPP_TAG_INTEGER)) != - NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - i = atoi(val); - if (i > 0) - num_lines = i; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid number of lines %d, using default value: %d", - i, num_lines); - } - if ((val = cupsGetOption("PageWidth", num_options, options)) != NULL || - (val = cupsGetOption("page-width", num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "page-width", IPP_TAG_INTEGER)) != - NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - i = atoi(val); - if (i > 0) - num_columns = i; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid number of columns %d, using default value: %d", - i, num_columns); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Lines per page: %d; Characters per line: %d", - num_lines, num_columns); - - if ((val = cupsGetOption("PageLeft", - num_options, options)) != NULL || - (val = cupsGetOption("page-left", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "page-left", - IPP_TAG_INTEGER)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "page-left-default", - IPP_TAG_INTEGER)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - { - if (!strncasecmp(val, "Custom", 6)) - val += 7; - page_left = atoi(val); - } - if (page_left < 0 || page_left > num_columns - 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Left margin not set or invalid, use 0 columns"); - page_left = 0; - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Left margin: %d columns", - page_left); - } - - if ((val = cupsGetOption("PageRight", - num_options, options)) != NULL || - (val = cupsGetOption("page-right", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "page-right", - IPP_TAG_INTEGER)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "page-right-default", - IPP_TAG_INTEGER)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - { - if (!strncasecmp(val, "Custom", 6)) - val += 7; - page_right = atoi(val); - } - if (page_right < 0 || page_right > num_columns - 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Right margin not set or invalid, use 0 columns"); - page_right = 0; - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Right margin: %d columns", - page_right); - } - - if ((val = cupsGetOption("PageTop", - num_options, options)) != NULL || - (val = cupsGetOption("page-top", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "page-top", - IPP_TAG_INTEGER)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "page-top-default", - IPP_TAG_INTEGER)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - { - if (!strncasecmp(val, "Custom", 6)) - val += 7; - page_top = atoi(val); - } - if (page_top < 0 || page_top > num_lines - 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Top margin not set or invalid, use 0 lines"); - page_top = 0; - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Top margin: %d lines", - page_top); - } - - if ((val = cupsGetOption("PageBottom", - num_options, options)) != NULL || - (val = cupsGetOption("page-bottom", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "page-bottom", - IPP_TAG_INTEGER)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "page-bottom-default", - IPP_TAG_INTEGER)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - { - if (!strncasecmp(val, "Custom", 6)) - val += 7; - page_bottom = atoi(val); - } - if (page_bottom < 0 || page_bottom > num_lines - 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Bottom margin not set or invalid, use 0 lines"); - page_bottom = 0; - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Bottom margin: %d lines", - page_bottom); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Margins: Left (Columns): %d; Right (Columns): %d; Top (Lines): %d; Bottom (Lines): %d", - page_left, page_right, page_top, page_bottom); - - text_width = num_columns - page_left - page_right; - text_height = num_lines - page_top - page_bottom; - if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterTextToText: Text area: Lines per page: %d; Characters per line: %d", - text_height, text_width); - - strcpy(encoding, "ASCII//IGNORE"); - if ((val = cupsGetOption("PrinterEncoding", - num_options, options)) != NULL || - (val = cupsGetOption("printer-encoding", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "printer-encoding", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "printer-encoding-default", - IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - { - if (!strncasecmp(val, "Custom", 6)) - val += 7; - if (val[0] != '\0') - { - snprintf(encoding, sizeof(encoding), "%.55s//IGNORE", val); - for (p = encoding; *p; p ++) - *p = toupper(*p); - } - } - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Output encoding: %s", encoding); - - if ((val = cupsGetOption("OverLongLines", - num_options, options)) != NULL || - (val = cupsGetOption("over-long-lines", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "over-long-lines", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "over-long-lines-default", - IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (!strcasecmp(val, "truncate")) - overlong_lines = TRUNCATE; - else if (!strcasecmp(val, "word-wrap") || - !strcasecmp(val, "WordWrap")) - overlong_lines = WORDWRAP; - else if (!strcasecmp(val, "wrap-at-width") || - !strcasecmp(val, "WrapAtWidth")) - overlong_lines = WRAPATWIDTH; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid value for over-long-lines: %s, using default value", - val); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Handling of overlong lines: %s", - (overlong_lines == TRUNCATE ? "Truncate at maximum width" : - (overlong_lines == WORDWRAP ? "Word-wrap" : - "Wrap exactly at maximum width"))); - - if ((val = cupsGetOption("TabWidth", - num_options, options)) != NULL || - (val = cupsGetOption("tab-width", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "tab-width", - IPP_TAG_INTEGER)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "tab-width-default", - IPP_TAG_INTEGER)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val && val[0]) - { - if (!strncasecmp(val, "Custom", 6)) - val += 7; - tab_width = atoi(val); - } - if (log) - { - if (tab_width <= 0 || tab_width > num_columns - 1) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Tab width not set or invalid, use default"); - else - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Tab width: %d columns", - tab_width); - } - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Tab width: %d", tab_width); - - if ((val = cupsGetOption("Pagination", - num_options, options)) != NULL || - (val = cupsGetOption("pagination", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "pagination", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "pagination-default", - IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (is_true(val)) - pagination = 1; - else if (is_false(val)) - pagination = 0; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid value for pagination: %s, using default value", - val); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Pagination (Print in defined pages): %s", - (pagination ? "Yes" : "No")); - - if ((val = cupsGetOption("SendFF", - num_options, options)) != NULL || - (val = cupsGetOption("send-ff", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "send-ff", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "send-ff-default", - IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (is_true(val)) - send_ff = 1; - else if (is_false(val)) - send_ff = 0; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid value for send-ff: %s, using default value", - val); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Send Form Feed character at end of page: %s", - (send_ff ? "Yes" : "No")); - - if ((val = cupsGetOption("NewlineCharacters", - num_options, options)) != NULL || - (val = cupsGetOption("newline-characters", - num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "newline-characters", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(printer_attrs, "newline-characters-default", - IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (!strcasecmp(val, "lf")) - newline_char = LF; - else if (!strcasecmp(val, "cr")) - newline_char = CR; - else if (!strcasecmp(val, "crlf")) - newline_char = CRLF; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid value for newline-characters: %s, using default value", - val); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Characters sent to make printer start a new line: %s", - (newline_char == LF ? "Line Feed (LF)" : - (newline_char == CR ? "Carriage Return (CR)" : - "Carriage Return (CR) and Line Feed (LF)"))); - - if ((val = cupsGetOption("page-ranges", num_options, options)) != - NULL || - (ipp = ippFindAttribute(job_attrs, "page-ranges", IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (val[0] != '\0') - page_ranges = strdup(val); - } - if (page_ranges) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Page selection: %s", page_ranges); - - if ((val = cupsGetOption("page-set", num_options, options)) != - NULL || - (ipp = ippFindAttribute(job_attrs, "page-set", IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (!strcasecmp(val, "even")) - { - even_pages = 1; - odd_pages = 0; - } - else if (!strcasecmp(val, "odd")) - { - even_pages = 0; - odd_pages = 1; - } - else if (!strcasecmp(val, "all")) - { - even_pages = 1; - odd_pages = 1; - } else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid value for page-set: %s, using default value", - val); - } - if (!even_pages || !odd_pages) - if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterTextToText: Print %s", - (even_pages ? "only the even pages" : - (odd_pages ? "only the odd pages" : - "no pages"))); - - if ((val = cupsGetOption("OutputOrder", num_options, options)) != - NULL || - (val = cupsGetOption("output-order", num_options, options)) != - NULL || - (ipp = ippFindAttribute(job_attrs,"output-order", IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (!strcasecmp(val, "reverse")) - reverse_order = 1; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid value for OutputOrder: %s, using default value", - val); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Print pages in reverse order: %s", - (reverse_order ? "Yes" : "No")); - - if ((val = cupsGetOption("Collate", num_options, options)) != NULL || - (ipp = ippFindAttribute(job_attrs, "collate", IPP_TAG_ZERO)) != NULL) - { - if (val == NULL) - { - ippAttributeString(ipp, buf, sizeof(buf)); - val = buf; - } - if (is_true(val)) - collate = 1; - else if (is_false(val)) - collate = 0; - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Invalid value for Collate: %s, using default value", - val); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterTextToText: Collate copies: %s", - (collate ? "Yes" : "No")); - - // Create a string to insert as the newline mark - if (newline_char == LF) - newline_char_str = "\n"; - else if (newline_char == CR) - newline_char_str = "\r"; - else if (newline_char == CRLF) - newline_char_str = "\r\n"; - - // Size of the output page in bytes (up to 4-byte-per-character) - // in the worst case, no blank lines at top and bottom, no blank right - // margin, 2 characters for newline (CR + LF) plus form feed plus closing - // zero - page_size = ((num_columns + 2) * num_lines + 2) * 4; - - // Allocate output page buffer - out_page = calloc(page_size, sizeof(char)); - - // Set conversion mode of iconv - cd = iconv_open(encoding, "UTF-8"); - if (cd == (iconv_t) -1) - { - // Something went wrong. - if (errno == EINVAL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToText: Conversion from UTF-8 to %s not available", - encoding); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToText: Error setting up conversion from UTF-8 to %s", - encoding); - } - goto error; - } - - // Open the input file - fd = inputfd; - if (fd < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToText: Unable to open input text file"); - goto error; - } - - // Create an array to hold the output pages for collated copies or - // reverse output order (only when printing paginated) - if (pagination && - ((num_copies != 1 && collate) || reverse_order)) - // Create page array - page_array = cupsArrayNew(NULL, NULL); - - // Main loop for reading the input file, converting the encoding, formatting - // the output, and printing the pages - destptr = out_page; - page_empty = 1; - page = 1; - line = 0; - column = 0; - previous_is_cr = 0; - insize = 0; - new_line_started = 0; - do { - // Reset input pointer - inptr = inbuf; - - // Mark the output buffer empty - outsize = sizeof(outbuf); - outptr = outbuf; - - // Read from the input file - nread = read (fd, inbuf + insize, sizeof (inbuf) - insize); - if (nread == 0) - { - // When we come here the file is completely read. - // This still could mean there are some unused - // characters in the inbuf, meaning that the file - // ends with an incomplete UTF-8 character. Log - // this fact. - if (insize > 0 && incomplete_char) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Input text file ends with incomplete UTF-8 character sequence, file possibly incomplete, but printing the successfully read part anyway"); - } - - // Now write out the byte sequence to get into the - // initial state if this is necessary. - iconv (cd, NULL, NULL, &outptr, &outsize); - } - - insize += nread; - - // Convert the incoming UTF-8-encoded text to the printer's encoding - if (insize > 0) // Do we have data to convert? - { - // Do the conversion. - nconv = iconv (cd, &inptr, &insize, &outptr, &outsize); - if (log && nconv == (size_t) -1) - log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: iconv() message: %s", strerror(errno)); - if (nconv == (size_t) -1 && insize > 0) - { - // Not everything went right. It might only be - // an unfinished byte sequence at the end of the - // output buffer. Or it is a real problem. - if (errno == EINVAL || errno == E2BIG) - { - // This is harmless. Simply move the unused - // bytes to the beginning of the buffer so that - // they can be used in the next round. - if (errno == EINVAL) - incomplete_char = 1; - memmove (inbuf, inptr, insize); - } - else - { - // We found an illegal UTF-8 byte sequence here, - // so error out at this point. - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterTextToText: Illegal UTF-8 sequence found. Input file perhaps not UTF-8-encoded"); - result = -1; - break; - } - } - } - - // Process the output to generate the output pages - if (outptr == outbuf) // End of input file - *(outptr ++) = '\0'; - for (procptr = outbuf; procptr < outptr; procptr ++) - { - if ((column >= text_width && // Current line is full - *procptr != '\n' && *procptr != '\r' && *procptr != '\f') || - // Next character is not newline or - // formfeed - (outbuf[0] == '\0' && column > 0)) // End of input file - { - if (overlong_lines == TRUNCATE && outbuf[0] != '\0') - skip_rest_of_line = 1; - else - { - if (overlong_lines == WORDWRAP && outbuf[0] != '\0') - { - if (*procptr > ' ') - { - *destptr = '\0'; - for (p = destptr - 1, i = column - 1; *p != ' ' && i >= 0; - p --, i--); - if (i >= 0 && i < column - 1) - { - wrapped_word = strdup(p + 1); - for (; *p == ' ' && i >= 0; p --, i--); - if (*p != ' ' && i >= 0) - destptr = p + 1; - else - { - free(wrapped_word); - wrapped_word = NULL; - } - } - } else - skip_spaces = 1; - } - // Remove trailing whitespace - while (destptr > out_page && *(destptr - 1) == ' ') - destptr --; - // Put newline character(s) - for (j = 0; newline_char_str[j]; j ++) - *(destptr ++) = newline_char_str[j]; - // Position cursor in next line - line ++; - column = 0; - new_line_started = 0; - } - } - if ((line >= text_height && // Current page is full - *procptr != '\f') || // Next character is not formfeed - outbuf[0] == '\0' ) // End of input file - { - // Do we actually print this page? - if (!pagination || - check_range(page_ranges, even_pages, odd_pages, page)) - { - // Finalize the page - if (pagination) - { - if (send_ff) - { - if (page_empty) - destptr = out_page; // Remove unneeded white space - // Send Form Feed - *(destptr ++) = '\f'; - } - else if (outbuf[0] != '\0') - { - // Fill up page with blank lines - for (i = 0; i < page_bottom; i ++) - for (j = 0; newline_char_str[j]; j ++) - *(destptr ++) = newline_char_str[j]; - } - } - // Allow to handle the finished page as a C string - *(destptr ++) = '\0'; - // Count pages which will actually get printed - num_pages ++; - if (!pagination) - { - // Log the page output (only once, when printing the first buffer - // load) - if (num_pages == 1) - { - if (log) log(ld, CF_LOGLEVEL_CONTROL, "PAGE: 1 1"); - } - cupsFilePuts(outputfp, out_page); - } - else if ((num_copies == 1 || !collate) && !reverse_order) - { - // Log the page output - if (log) log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", num_pages, num_copies); - cupsFilePuts(outputfp, out_page); - } - else - { - // Save the page in the page array - cupsArrayAdd(page_array, strdup(out_page)); - } - } - // Reset for next page - destptr = out_page; - page_empty = 1; - line = 0; - column = 0; - new_line_started = 0; - page ++; - } - if (outbuf[0] == '\0') // End of input file - break; - if (column == 0 && !new_line_started) // Start of new line - { - new_line_started = 1; - if (line == 0 && pagination) // Start of new page - for (i = 0; i < page_top; i ++) - for (j = 0; newline_char_str[j]; j ++) - *(destptr ++) = newline_char_str[j]; - for (i = 0; i < page_left; i ++) - *(destptr ++) = ' '; - // Did we wrap a word from the previous line? - if (wrapped_word) - { - for (p = wrapped_word; *p != '\0'; p ++, column ++) - *(destptr ++) = *p; - free(wrapped_word); - wrapped_word = NULL; - page_empty = 0; - skip_spaces = 0; - } - } - if (*procptr == '\r' || *procptr == '\n') // CR or LF - { - // Only write newline if we are not on the LF of a CR+LF - if (*procptr == '\r' || previous_is_cr == 0) - { - // Remove trailing whitespace - while (destptr > out_page && *(destptr - 1) == ' ') - destptr --; - // Put newline character(s) - for (j = 0; newline_char_str[j]; j ++) - *(destptr ++) = newline_char_str[j]; - // Position cursor in next line - line ++; - column = 0; - new_line_started = 0; - // Finished truncating an overlong line - skip_rest_of_line = 0; - skip_spaces = 0; - } - if (*procptr == '\r') - previous_is_cr = 1; - else - previous_is_cr = 0; - } - else - { - previous_is_cr = 0; - if (*procptr == '\t') // Tab character - { - if (!skip_rest_of_line && !skip_spaces) - { - *(destptr ++) = ' '; // Always at least one space - column ++; - // Add spaces to reach next multiple of the tab width - for (; column % tab_width != 0 && column < text_width; column ++) - *(destptr ++) = ' '; - } - } - else if (*procptr == '\f') // Form feed - { - // Skip to end of page - if (send_ff) - // Mark page full - line = text_height; - else if (pagination) - // Fill page with newlines - for (; line < text_height; line ++) - for (j = 0; newline_char_str[j]; j ++) - *(destptr ++) = newline_char_str[j]; - column = 0; - new_line_started = 0; - // Finished truncating an overlong line - skip_rest_of_line = 0; - skip_spaces = 0; - } - else if (*procptr == ' ') // Space - { - if (!skip_rest_of_line && !skip_spaces) - { - *(destptr ++) = *procptr; - column ++; - } - } - else if (*procptr > ' ' || *procptr < '\0') // Regular character - { - if (!skip_rest_of_line) - { - *(destptr ++) = *procptr; - column ++; - page_empty = 0; - skip_spaces = 0; - } - } - } - } - } - while (outbuf[0] != '\0'); // End of input file - - close(fd); - - if (iconv_close (cd) != 0) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterTextToText: Error closing iconv encoding conversion session"); - - // Error out on an illegal UTF-8 sequence in the input file - if (result < 0) - goto error; - - // Print out the page array if we created one - if (pagination && - ((num_copies != 1 && collate) || reverse_order)) - { - // If we print collated copies, the outer loop (i) goes through the - // copies, if we do not collate, the inner loop (j) goes through the - // copies. The other loop only runs for one cycle. - for (i = 0; i < (collate ? num_copies : 1); i ++) - for (page = (reverse_order ? num_pages : 1); - (reverse_order ? (page >= 1) : (page <= num_pages)); - page += (reverse_order ? -1 : 1)) - { - p = (char *)cupsArrayIndex(page_array, page - 1); - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterTextToText: %d %d", page, (collate ? 1 : num_copies)); - } - // Clean up - for (page = 0; page < num_pages; page ++) - { - p = (char *)cupsArrayIndex(page_array, page); - free(p); - } - cupsArrayDelete(page_array); - } - - // - // Cleanup and exit... - // - - error: - - if (outputfp) - cupsFileClose(outputfp); - - free(page_ranges); - free(out_page); - - return (exit_status); -} - - -// -// 'is_true()' - Check option value for boolean true -// - -static int -is_true(const char *value) -{ - if (!value) - return (0); - return ((strcasecmp(value, "yes") == 0) || - (strcasecmp(value, "on") == 0) || - (strcasecmp(value, "true") == 0) || - (strcasecmp(value, "1") == 0)); -} - - -// -// 'is_false()' - Check option value for boolean false -// - -static int -is_false(const char *value) -{ - if (!value) - return (0); - return ((strcasecmp(value, "no") == 0) || - (strcasecmp(value, "off") == 0) || - (strcasecmp(value, "false") == 0) || - (strcasecmp(value, "0") == 0)); -} - - -// -// 'check_range()' - Check to see if the current page is selected for -// printing. -// - -static int // O - 1 if selected, 0 otherwise -check_range(char *page_ranges, // I - Selection of pages to print - int even_pages, // I - Print the even pages - int odd_pages, // I - Print the odd pages - int page) // I - Page number -{ - const char *range; // Pointer into range string - int lower, upper; // Lower and upper page numbers - - // - // See if we only print even or odd pages... - // - - if (!odd_pages && (page & 1)) - return (0); - - if (!even_pages && !(page & 1)) - return (0); - - // - // page-ranges option - // - - if (!page_ranges || page_ranges[0] == '\0') - return (1); // No range, print all pages... - - for (range = page_ranges; *range != '\0';) - { - if (*range == '-') - { - lower = 1; - range ++; - upper = (int)strtol(range, (char **)&range, 10); - } - else - { - lower = (int)strtol(range, (char **)&range, 10); - - if (*range == '-') - { - range ++; - if (!isdigit(*range & 255)) - upper = 65535; - else - upper = (int)strtol(range, (char **)&range, 10); - } - else - upper = lower; - } - - if (page >= lower && page <= upper) - return (1); - - if (*range == ',') - range ++; - else - break; - } - - return (0); -} diff --git a/cupsfilters/universal.c b/cupsfilters/universal.c deleted file mode 100644 index f1566ce54..000000000 --- a/cupsfilters/universal.c +++ /dev/null @@ -1,342 +0,0 @@ -// -// Universal filter function for libcupsfilters. -// -// Converts from any input format into any output format, calling an -// auto-selected chain of filter functions. -// -// Copyright 2021 by Pranshu Kharkwal -// Copyright 2021-2022 by Till Kamppeter -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "config.h" -#include "filter.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int // O - Error status -cfFilterUniversal(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - char *input; - char *final_output; - char output[256]; - char input_super[16]; - char input_type[256]; - char output_super[16]; - char output_type[256]; - cf_filter_out_format_t *outformat; - cf_filter_filter_in_chain_t *filter, *next; - cf_filter_universal_parameter_t *universal_parameters; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - int ret = 0; - - universal_parameters = (cf_filter_universal_parameter_t *)parameters; - input = data->content_type; - if (input == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterUniversal: No input data format supplied."); - return (1); - } - - final_output = data->final_content_type; - if (final_output == NULL) - { - final_output = universal_parameters->actual_output_type; - if (final_output == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterUniversal: No output data format supplied."); - return (1); - } - } - - if (universal_parameters->actual_output_type) - strncpy(output, universal_parameters->actual_output_type, - sizeof(output) - 1); - else - strncpy(output, data->final_content_type, sizeof(output) - 1); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Converting from %s to %s", input, output); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Final output format for job: %s", - final_output); - - sscanf(input, "%15[^/]/%255s", input_super, input_type); - sscanf(output, "%15[^/]/%255s", output_super, output_type); - - cups_array_t *filter_chain; - filter_chain = cupsArrayNew(NULL, NULL); - - if (!strcasecmp(input_super, "image") && strcasecmp(input_type, "urf") && - strcasecmp(input_type, "pwg-raster")) - { - if (!strcasecmp(output_type, "vnd.cups-raster") || - !strcasecmp(output_type, "urf") || - !strcasecmp(output_type, "pwg-raster") || - !strcasecmp(output_type, "PCLm")) - { - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterImageToRaster; - filter->parameters = NULL; - filter->name = "imagetoraster"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - - if (!strcasecmp(output, "image/pwg-raster") || - !strcasecmp(output, "application/PCLm")) - { - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterRasterToPWG; - filter->parameters = NULL; - filter->name = "rastertopwg"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - - if (!strcasecmp(output, "application/PCLm")) - { - outformat = malloc(sizeof(cf_filter_out_format_t)); - *outformat = CF_FILTER_OUT_FORMAT_PCLM; - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterPWGToPDF; - filter->parameters = outformat; - filter->name = "pwgtopclm"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } - } - else if (!strcasecmp(output, "image/urf")) - { - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterRasterToPWG; - filter->parameters = NULL; - filter->name = "rastertopwg"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } - } - else - { - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterImageToPDF; - filter->parameters = NULL; - filter->name = "imagetopdf"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } - } - else - { -#ifdef HAVE_GHOSTSCRIPT - if (!strcasecmp(input, "application/postscript")) - { - outformat = malloc(sizeof(cf_filter_out_format_t)); - *outformat = CF_FILTER_OUT_FORMAT_PDF; - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterGhostscript; - filter->parameters = outformat; - filter->name = "ghostscript"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } - else -#endif // HAVE_GHOSTSCRIPT - if (!strcasecmp(input_super, "text") || - (!strcasecmp(input_super, "application") && input_type[0] == 'x')) - { - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - cf_filter_texttopdf_parameter_t* tparameters = - (cf_filter_texttopdf_parameter_t *) malloc(sizeof(cf_filter_texttopdf_parameter_t)); - *tparameters = universal_parameters->texttopdf_params; - filter->function = cfFilterTextToPDF; - filter->parameters = tparameters; - filter->name = "texttopdf"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", - filter->name); - } - else if (!strcasecmp(input, "image/urf") || - !strcasecmp(input, "image/pwg-raster")) - { - outformat = malloc(sizeof(cf_filter_out_format_t)); - *outformat = CF_FILTER_OUT_FORMAT_PDF; - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterPWGToPDF; - filter->parameters = outformat; - filter->name = "pwgtopdf"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } -#ifdef HAVE_GHOSTSCRIPT - else if (!strcasecmp(input_type, "vnd.adobe-reader-postscript")) - { - outformat = malloc(sizeof(cf_filter_out_format_t)); - *outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterGhostscript; - filter->parameters = outformat; - filter->name = "ghostscript"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - - outformat = malloc(sizeof(cf_filter_out_format_t)); - *outformat = CF_FILTER_OUT_FORMAT_PDF; - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterPWGToPDF; - filter->parameters = outformat; - filter->name = "pwgtopdf"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } -#endif // HAVE_GHOSTSCRIPT - else if (!strcasecmp(input, "application/vnd.cups-pdf-banner")) - { - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterBannerToPDF; - filter->parameters = - strdup(universal_parameters->bannertopdf_template_dir); - filter->name = "bannertopdf"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } - else if (!strcasestr(input_type, "pdf")) - { - // Input format is not PDF and unknown -> Error - ret = 1; - goto out; - } - } - if (strcasecmp(input_super, "image") || - (strcasecmp(output_type, "vnd.cups-raster") && - strcasecmp(output_type, "urf") && strcasecmp(output_type, "pwg-raster") && - strcasecmp(output_type, "PCLm")) || - !strcasecmp(input_type, "urf") || - !strcasecmp(input_type, "pwg-raster")) - { - if (strcasecmp(output_type, "pdf")) - { - if (strcasecmp(input_type, "vnd.cups-pdf")) - { - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterPDFToPDF; - filter->parameters = NULL; - filter->name = "pdftopdf"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } - -#if defined(HAVE_GHOSTSCRIPT) || defined(HAVE_POPPLER_PDFTOPS) - if (strcasecmp(output_type, "vnd.cups-pdf")) - { - if (!strcasecmp(output_type, "vnd.cups-raster") || - !strcasecmp(output_type, "urf") || - !strcasecmp(output_type, "pwg-raster") || - !strcasecmp(output_type, "PCLm")) - { -# ifdef HAVE_GHOSTSCRIPT - outformat = malloc(sizeof(cf_filter_out_format_t)); - *outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; - if (!strcasecmp(output_type, "pwg-raster")) - *outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - else if (!strcasecmp(output_type, "urf")) - *outformat = CF_FILTER_OUT_FORMAT_APPLE_RASTER; - else if(!strcasecmp(output_type, "PCLm")) - *outformat = CF_FILTER_OUT_FORMAT_PCLM; - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterGhostscript; - filter->parameters = outformat; - filter->name = "ghostscript"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", - filter->name); -# else -# ifdef HAVE_POPPLER_PDFTOPS - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterPDFToRaster; - filter->parameters = NULL; - filter->name = "pdftoraster"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", - filter->name); - if (!strcasecmp(output_type, "PCLm")) - { - outformat = malloc(sizeof(cf_filter_out_format_t)); - *outformat = CF_FILTER_OUT_FORMAT_PCLM; - filter = malloc(sizeof(cf_filter_filter_in_chain_t)); - filter->function = cfFilterPWGToPDF; - filter->parameters = outformat; - filter->name = "pwgtopclm"; - cupsArrayAdd(filter_chain, filter); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterUniversal: Adding %s to chain", filter->name); - } -# endif // HAVE_POPPLER_PDFTOPS -# endif // HAVE_GHOSTSCRIPT - } - else - { -#endif // HAVE_GHOSTSCRIPT || HAVE_POPPLER_PDFTOPS - // Output format is not PDF and unknown -> Error - ret = 1; - goto out; -#if defined(HAVE_GHOSTSCRIPT) || defined(HAVE_POPPLER_PDFTOPS) - } - } -#endif // HAVE_GHOSTSCRIPT || HAVE_POPPLER_PDFTOPS - } - } - - out: - - if (ret) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterUniversal: Unsupported combination of input and output formats: %s -> %s", - input, output); - } - else - // Do the dirty work ... - ret = cfFilterChain(inputfd, outputfd, inputseekable, data, filter_chain); - - for (filter = (cf_filter_filter_in_chain_t *)cupsArrayFirst(filter_chain); - filter; filter = next) - { - next = (cf_filter_filter_in_chain_t *)cupsArrayNext(filter_chain); - free(filter->parameters); - free(filter); - } - - return ret; -} diff --git a/data/banner-instr-append.txt b/data/banner-instr-append.txt deleted file mode 100644 index bf3321995..000000000 --- a/data/banner-instr-append.txt +++ /dev/null @@ -1,2 +0,0 @@ -%%#PDF-BANNER -%%Show printer-name printer-info printer-location printer-make-and-model job-id time-at-creation time-at-processing job-originating-user-name job-name job-originating-host-name job-billing job-uuid diff --git a/data/classified.pdf b/data/classified.pdf deleted file mode 100644 index cca46a0d9d633d0aa540d23be6d4f4565bb9b935..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 1191 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 diff --git a/data/default.pdf b/data/default.pdf deleted file mode 100644 index d07f089bfd04ba6bae3c44ee21029b1d728274e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 1057 zc-obd!H&}~5QbNK^izz4D(wm-jng>kqSZ>Y-CZGIsakG`gPXXim(-4I2f924XP$u@ z&%z7rl_%hWZIZN7_d*Uy{CVb|`7_R7KD!BUNYLQt?;n5A2qKWHdvtjT!#7NnW&?>u z3j_ee8(tZv?6abV%~&oAhOVxVHj2?L+I@fih#pT*PQRV}LQE7bwr@@_X@#v0jyo9l z)tpWX$w3S6A8H1}IW3uQU``bi25{feTUKy7kvsTcQ3T|IgmFelmcTf{;dp$JWf(sC z(5V#0CTke=*|$nIHQ3_dueO?}9a>tdsL-{moIiAUYWa+P<~dv3P8_uDJHB9AHY#Tt zVqazN3P0&e`>fZz(AI%7zUaGcoY4b;^gv*0zpRl8&T`6?1RUZhjNxo!O#Naw^srR4 z-tb(9QkCcD|Gu7*MU%7tN!yptj{2WcLn~RfgRx%Px$JXximg!uV-)olq=bwJtf6D0 z)DF(?h@Rn?9I*%=GaMzyEJ=@8@`N5C@tzq)dBv1Bx8z@$ZP`y#N&{JcTwaN_gtYUh z4OPZFOrr!12G{Ry?ERVhohC2u?yeVhwcN-rP%Cavo(crrvVOsxR=22RP7Rl$2V44y z1ymHl*8XA@+{;zK3ov|ZsR?p7eHY$=T1k84G#8~0m5P^K*bq?$jqWPDo&M!8WFxiN O@2q%LbwW+U3-lN1+$N_0 diff --git a/data/form-instr-append.txt b/data/form-instr-append.txt deleted file mode 100644 index 79fe47842..000000000 --- a/data/form-instr-append.txt +++ /dev/null @@ -1,3 +0,0 @@ -%%#PDF-BANNER -%%Font UseDefault -%%Font-size 11 diff --git a/data/form_english.pdf b/data/form_english.pdf deleted file mode 100644 index a92730b70fe12720e393d45aed58b2ec2a781d54..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 276117 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 z zBWibk_G;+&x&}?FhPg#Jaxsovf^$Es?RHMPbE$U&&bwiScg?o`!e9F{ayv;+I!W(4 z?W^CS7;j4y@A13bUT`;YaICv?nWsx&~iVKo_w;;evhhtU(%J$uSn*1 zAqQI@#nc|f20(Bj?D?p;&IC~RhAyzeG;bd!d=?Ijve>J24#3i-PV`4x+T>-;fR z0hnBVY4Ut&CsvW<}$rY6*i%GlxtmCGn;qk1BJ~4g`Wm8I(kXNz4j6xQR*L?p0Xm*M-<~@iDCu6OGU8tMNI8QjMbl*+@Gb% ziqh`3I&StlZVoz~6+R7>J~b(Wg|`O_?+<2p(@E#(_Uf&uJ*`cpfHVFh|L)6R;pku? z?@)$RA8AdWJ+TdSsSTCgR&wVBf7gp(>tsxAGR7(ele<-#Y%1-3RmUw>$1Pr`Q~zXx z(aD;NLxr(JWYz>_bqiB_3zPesGMj(ct3?`fg)N$k1bwlZP3ivh%`_`-;Dg1ea%*qDIA=2b?(o9`R zrwc2eg{^+?nXcc2^I4cK$y9rlv}ZUz|NM%}_ex^Yu9cIkX|mk>_y)`J0Umd) zdBc4JlBvZM*XMovJcfj>`v&|))qm~k_GOLLsQ=%#tk1@FiSbW?Jp~Qz)znX(IxjU- zm8=Es_!hlRcl(0A;~NMfSf4-U;1yRzNKG>~2al-hk=IukE(Z?75dDyF(JV`!|PR<@=2@S3bhYyC>H~?T--E+_^03gI)+qKHT`I z%%er}!pR#VKb#Tehl}ZevX?)EipuT0W_;8rL`1&Q_~?p9XGNWoH$;615tZ0!Wqi~y z1TBBZ_~`OSfueho$&pa8PduO^dF7)pQT3g*Q6G##GElc}Q&BOw%>j?~s9(5J*#*aiUDtXE7-9^$^`BRpn6vdioI7C#VJfHRSArl*VpD}3a6tYobN}?&O@j9+m1uGvk6R(=6s7V$5a;0d1;%xCZA%7Fh zDV${w-W=45r=4ilIbM&A?I&>3Jd@Fj?H;Qdc)Qkj zduzJp*t-v|Ohy6ICZ{XDsw~#Nn|gqOa#(NkKCW6r=gC0i*a?mJ13qs;gwQI(9R#&o zHP3xl3|Dp2FLTdJ+R#vWO=zW>;M1q+FW)NfFL}5zUz#hFa*942|F%3Ma(R!$MQ&ot zVarT=`-6V5l|fIV;$X$pRBC9x+oDo8)&;eB(gNQmexC`)t8w!}0!vy))%^SL| z2x_!OP2p)Lpi!h`Wx3l5g9fFaP+d?$x zj|Mm!8KA&=`^`a8l03fVOWd3)gBD5g9c-zl9X=yiMs*sa-iW6r86{j-Xc!rD z>OZ)lP&9h}u3fyfm+#s?3JSbDdO|~LMad#lNjrg%c|W!DOpx3NTFCV5P1r&UO*aPW z8JZWy{vx&euw~3!+Z%V6s|cnFD)X-A5j*g;jZVp%i^4YYm&Y$mOIrY1gViyPK}FHr zsR^4BuK&c@E0L1*gV*>huFHbjs;!%6VJ2;6z8Gwkn3VXzexOz5f&ucVaZQX4FNwJf zYI2-RjkM)T-L$1}aJ1^M72}L-($F$%51bjfCqmoV~M9veK@CLmw8pT(Dc{^QfJP=)7xG=2N2}z>cV& z+PMKcX6!F1ehSLHY_EI(FaahlZT>`uP!D{{WqQ_&ZQdh#hQCZUuc#N#?C= zRgGEP%mr#2wLHe3aw_EaQ<}%s#$9lU>E&@vCi#-`>YLng#0Bs^!(cWF zLH5<0XCWKo|L#=CF_2?ujk2-|$kCDPdpXNrK20zU&j)(&LrFzt-au}#`&^turreF! zLVR2wHu|84Yh*XS#dUFZze<>y3ry};wCTnJdWLH!^s7pqyd$5TNi+}3ycIk@{q&W2 zN%DBXv^3tY##>Qz+B|r?6^ZWR-6hJy>G$hO>?(N1HDUMYvM%+H5`g#4XM3+eS{iEN zh+7_$1&k!#UArKT{|1pR4?-hp^hq?@fPi*#4Qd_S-k1Ke%Exh_>JY1gb{{62 z{!-;6d--}A_r$Jc@C}aX2Z>3xykE0U!O^ljV?_ygD-Pa7*)KOR4AKXV5Wzq}F-Za7 z_-UQ!I?$1fg2Dw=xHkull?;xZ7`n_X;b>=nc?KA_y&Y?!>>~s<*L9X>Y!1SL0SQ;` zwkaqEjM>Y;E0kn-xtmN0NpQ2{hRDWHLAiC;%H52DM4Cdtsxe$peO+x-qY)6ny8tsDDlk1%BpS_wf5ik1 zd(|FgVYVP)A+wvqdd7v{yVt> zhx{hQFjs-g+%b#aF(dhoE&Ag`SXp2_m`pSS{hK$Se0()D&=B*`;SP9e#sq0Ee#oT0Bg!}sz|VC-|U}$}VJzc}+IX zd{fd$y4gtgH|DUuF~sRe{ppdz0L8*?W{DK!<;3{_a8b;4P+eY&dBBaivY}mw-{lW> z0{8p^Ye9+LDylI&G~2{}&K9mP;I|M=)f?Ht(2z)YI=+s+x_7?Vqgb61F0f z!?J#-E;0qpXA0b&JRSjgLwnLjzcIi6#`04=#vbF*37M&K%zkCwiUR%40?x1~%#iww zE{u*GV)aB;fj+HZ(!2!#iwpESIDwmjMlZ>0$MC$hP3!|bu3oV51seCTI7vdwD{!-t zN1y-Z^qw}}2pg!l;l&y)ucD+qU@=f95Mk5P_8RsbkG}T7emennHH_+M6Jw)T93d2o z;}B$~;-o!Q^Zez^Mt>q;IMVBAS;$-?qt!udw z*jv@*!~7NvGjxDp-mXF5C-H>Y2A#iSNa!%h=)uNPg10^9w}RA%ND_iR1B z6m%95C_2u_Nw2^o6X-4aSdr*V9c9#uU06I_J5BJ^9QATWfPCYNKbqeM%y@wP1}#&7 zcCQhcKXZ%Mf$KTIS&G3xRyZRkeFF!2Ew9^oe#*Qvb(8{jVfu9KG=cMdR!Dm{n~>Iz zgtY3)S%kEANJzUsi;#Ab71A_k5z-jxo4b43gd*x2UubH11#fos=nG<(NZ}qr#U)8Z zuo$Qlj;QZx!-B;?T}hI9^e53$jrJWTQmM8=*liJ zU>5R2SreG%LeEuBZ|?q+0R~(a?eq*VT+J@TPS;KoICmi7zPlnDoNRuqE~gEwk;qkl zalyQqgONdKJqw%FAyXu=c@&JdGxL+NFYLnL>6(d?vuho+3hZgSJsUsEfgZPb^zD`f zGV5v3bGAFX3ta9rzSuW&lV!szp2iof70w&S2?2J7Kj(4T4?X1syrgm# zF7^qCUEt_&b(+Pn9EQ?-+`}+v88b7euYoT&h>iVUjLKn_$+W4s8G8w_0CBOd%X%~S z&r!r?n$QAgMtxP+jrdq@>;GU_b{~Zvffgq*%4gwc+Ah!&8r;Lpvly2QGPKg(+)bLf zqeZ|^YCQUIo=k95&dgE#wAqEzwi733?|LMbY12Q=e0)_QgR}ecEaJa*Hyq5o_k#~q z8)oFBbk^u}s@DZp3Nv(+xdt>FR-%zUn+c!;Ni@7?lW3$PiH6P0X8=YHE77?4zX)jz zR!GyFMMz`hBO$GRHX)5Q4XDpNq&;MXH1FAjw9)FM?#34Yk+#+l4tEk0kzbMoP5(Hy=+7IbH5GL zg6+C=KQhO&Mx3NO`$>G1Jv;XY91MK^zVL2TqaN!?{31zVR_=c-s&NzRNxY=wc2@46 z2l!S7nCa8U)~vIFpTq;X|B8|?!Rj^O1H5j4br|bOyym)EIQRFLFJLvc@dbm`-r&tt zj@D1>E*DvVGya60TFk=8u1GGMpA9{_fK`IKPI!8E50RyKw-EgNA|KEWYLzBL_TxKu zWNDe74Xwv#VTa-L@nYELIOI(KjuqgBY@nt2^t7LN$4YY_LQl_T#Q~mEspwf+lNIi$ zYoLYtEX*+0F4De0yDS%+XZlh&QE*HgcjNT?O10JFF|spt+7RwlfD?Gkk@a!pvdY z%E9*ot?LJ~K@bh1=8ScSfWpfz%f ztfIT-;GOd7F`xa*eD^%H>g_K0e0Tros^0EbLt9n|>#pf~S``wv#%p^RqbK+J@kfJn&Eh&(F2u=So5yXW5|b7v}RK6na>Wgqqhcr@kt%?PGj_34!ImJroE@{+S2Yu zqG4-D>>3x}q~2blR?%>%Uyzw5eCgIm4-3rZGS5bjK zXTIBLujGLP(HG6dp)wNyyUhHjq+LdTh3=G0S(38O#8n#K)RnfePOag6eu3y;Z4*Gw%=0LOz_LwUatSk2yxKOj;({T#o05KQwDT!)TIGIlzmqI{lFd{R(NxJ)kpkqZR9vpPd^Q$3ZDCWJcAB!Ofzx7; zS_D2_K*&Edb#J&nHGk-Y@SY%`vW$9k(=6p)KpH%$7&`r^!SPedUG2UbIW5v$lX-VB zd^%UN_iXINn!RCwT*+pW&RhRhLZ=mM>ecSzY--?i2BbaLE2621oX*C^*1Sbd+d4b{t=!((Sz1Qz4Lv63?!Mm% zI*n6{XkKMg11E?YImyE0{|tcRXlnl8=`g~ZQ$Q{qHuP-d_J4#MJgFEusr{(Ity9T8 ztAT|M=RlACvy}W#$csV?ME6abultYwkK^X;%@;O4U~fRnmxx#o9OS z%9-`)#%qgs%gR<))4DF#Sd^0^k1lW0Fa6?ZHSsbtt~ED65{(=kk1flQV@IhGa`>)O zLyrCnnNgWhNB!cug%8$79o<}tl_WaW#)oRI>nbO&Yzhw@zR;^ziju@QYDM;XtizRi z$TX2c`Pbogz=iRktR0S8QN5c=MI@`_n$CvGt&1!7SkW}-m>1crUy7Dga?Fbhl~`v} z?y3G1c$rK17)&q!6IOfQ1N^HV8t&1^sL;DEhk+xSr;juNkv;+T@)4 z-#{%5n2O}PJn6F++pj~#K3&2F;Mnz`arW5RkUV)?#A5bf=mJmID5n?Z@zC4tp~7Fi zfi88lnu0SKVz90x!lRUq$ssTD2tB|+lbqTAmFwYAve2rM>`<%bS z?^)|$mtE;BH0`F>6@?akQ#&n^Cl7sI%Yvsbzq)({j1QC~`l(2aobnF4I2=$jol9-d zWBDLPA~83D5uQJ|{7ZK>s6#rlLD5b^UIyu0sHM$PAG}nFaZ;|bi!0L7{p#`);G2<| zMOp!#e00H5J#3QODzRPkG`$Iy--KPWxE?z>kp-os@VnDe=#w z#7iq%E91hou@{;;2#h|bs?xaf6Ze48t5@3A=8UH~JJX!gXyp&8tP8bNPpIxXp-MRM zvDOI_6Pyzslph{c9DYsHTce*~8lL{L?q$5Tl=nIZ#AMIwUzkx}Jg36a(|VlSdYtjS z04tm_MjIz&h!fg|`-Rv~=%mFgv%QgfL)Tla-?4NQ4y?hyMFiPI1nrNwHeAd5)F6Aa z!Gp~Pxtl9b_5%$WaCRPWP8%qHaQBA8j-WsxHz|>2tiVM@e+&c_)7sgOV zXq$}CuQIWm3<`-13Xg2f82W_$9w3mlf#aKwMd+96Fr0% zJLD|(80s(4bE>N6RCk?&`^!Bl$SW$yH|neYYGS$CTVsRw#s+o9u&W(SPCS;JczhM) zYOlgo`@&U4Bfjry`V=z*%5#JBaA&VL)VAUfe&u7()xwh#Ba#y%lM|y>eeG)ZAy;#j zJCr7Os7>w=9uKQdh*ph`R=pPutA71_Q0n=ho9DmpYWpZ=R+Q&fuynlAA!nsSX-bbF zR}+#I5|b6e%6{L~^e+S%T?jI{0QcA5TMTY*F-YG6^;eqOp*FQcc=gBMbv4yk)m^cw zgjm?s+P5zq+P+j^2kdICTMTGh3_7-a*VV-ELRh@eYCPQ8xago2(Lu7&psVGWEv+zH z`p#@=yV=*SMuc7M_vj#(=%8cKLGID8>KjcA3``6RO(4~sw;W2_a;R;~_gyVTUg);G z(0zGWdP{Urdvs7&H0)~el;`mjvxM)vnzO~BG>b!R7I1&8F)R5l z_t1hfaxgWIkb??gA48Jwv!SgQpo|iN7Bw2!X`=l&F{b9 z^3LZ!;ew3YA2}Js_SMUA{+!1C70S=nPQzEHX7is|_a>6f{z%%?imqqPKQj|PQSZOJ z;{SsN!ApUpAKP#07E$Q`K~JZQA>nRjut^eGONY5N=sz)T?SEt3H~)okoBtEzdj2QI z^_`({+p|3%>3Ww|(WvQTpDYGP?mwcAb^9?N`qt?(gFaBp`NlLJEKNUB^+#BQip~wc z*sij{L2{rr`ob`fIVp4=Dp)ku${@&+T5g>lTmvLkByH3MI#4Cp`S|@u>r>6Q0tP!O zeHJUOvNB#`R=F;w;8<6ytT8!KIU!U(2AJR%Ef!zQ7jt)EOFA$`^YsgwRsvIW$!V3? zr+g53*HbzBmG;cWKgUiC)qP%d7&ru1B?-#{6ST(SF5@MZl^P(=PRSY-^0kq)Oj*ai=NsNTQ2nI7o!)T%J!n@dn!k3=PgLr&3iTmvCuR}H5dp3 zmZbt$IEqlD%P9grSU$QC%!@#PC<5RH>V$dMuGh=yjdc0=XDv5O3(CjdwEz7Sp=mj+ z$L}%vyrAX4IsW))&Dz^nWV<1Gpl*Q^YS zgIN6nv%0!5M>5%9t?9N%7k`3C@>Ln^@Akj@6X?t9=Fw!0gMW+BAKeN`7fCi)4^rs> zG(er1kOe@Ep&hK3?dGA3qfI!`J7e?*+32;LXh8&>dg1LU2nYjAVy?*4`53Ej%vnH5 zT%2k)dLA-506@b4S)fkiRq?BNv=)P$%lnS;!?cim{OP|IP@Vk%e++`azb|K5kpdNl88R)@vH z;ugpJCR68yu$q@@)(>|XAZ{Kk4iuvyim<{azu%}4UHFQRre>VIF-M4!$dd|ONmGtQ zx&#o?PRZ{=qyxPSQuP}EG)E$Z*DK%Om@^vb(t6~xqD`17TA zGIhR)9wRAcpW%k{(ag9k04fZmqlPG=M=TiOV6l+6o3_6P5Qdk-tlBT>KZ3gq5I4FN z76-9n?gXe;#g64C4{Q0Qo^F>o@bC!`BjZmq2d^IAyh=f|0RO~#tl~lZ)lJ2%1UUw< zz}#DNAzAZ=%H?DW@w}0X$DOJQ<4BJOf#q9_8!%RP0+WE-@<#pQz|-gj5{vK1aa`Ij zM-J@;XyoYFvf<_9!=b0cpkp+6-%vG;_~b%H6mrC$)&!;l^2)~W)590sHWyf& zyyFbAkFt~UlIgV9xaQ9VY*QVU#HjYQtz zrTqqsHKpHWtWmV3#?^WnuO6n62%nZix4}6QiEqH=rCtsnCGoSNQ1Txbm}*x(O!Wkq zH^IvFk#|T0p=5)%=YVP)e2e#9WMU1j=#&2dB8%-Sy_|l8su8BTZz^|}8{!9X5dIZZ z!yCR$Y&T3JfKMVoNSe1fiuTR`raFO89oyQ)#Q`?t1h*OD*?}qf& z$WY*s19ezzd?T0u(k0ddYD}02KUd+qtKQ^bZIbi3oyiw17D2; zqjTmrvw@l8KtwAGD4AgEnnoDc^`fHw3AU~s$)uYI7qY3*eSA7CKd>MIps33 z(iF*L72{xii9|9q_%whheIjb3hAvR*1kB3-d22AC5*&I!6(3Cypf`v;CrBAw4zK_s z^zlW%?}Ho|;9?>nnM^l;=*(y<0tmjwt4#4a{Hi=IS@kgL>QwfY%Ysbh+ z49q+cW_}zTa%|wQ9KZ_0w8Jc5yVW=l(TWZZw{cyg#&yLXTO`2NbsdMUEs(C&0R(Me za+Wv|c_)C-X1qva=jm|K#Fd6SHitZqT0uyjkCoTo91;hWv8uJ9GFG*h#?DuCHBX61 z;R(e*Mx7q7?jt0pL9<}+1jvl_fb7RX4_&s=#@V$Qzj(=6ef`E{Xp9>%Fa=b?2p28>|N)NP<}QyJzhjzpUhwuoum36IIHu)xjU=s zf8eYK|L-{Kn*YFAcmD^@y5>J{*5>~+&U){^aMu4Ivfld-oYnn5aMop5`S5Vjq?M1_ zjMr-H3?085AtW!s${TGCc@edrkUT)+tnPgYma(co+*tX@4_vUp>kIH%6twp6;=B|` zjyD%FB9S8wd<<1;Tmv6|keIbxdE;_7;=rZ=Qa5_mK)$j zFTC7<)*W&1asymGLXK`hEh3jt|%MI|N8hi}ZfpNVHJ=}tp8_>FA2ejOP z)*Tqy^_7oaaJHCz89%{vx)Z8>C!3Sp3@&Q16N-O~n%D_PpAgnbf$lpeJ;29MiIckg z>ptbE-Z5EPHn4n?oueKbAn%*VU|P;LQ88AWoD&aV@vEmXxr2RYG9W%w{QLlwY%r-c zpeSa1sQ2bSJ5A0jR8-^j)T8_zQr&e#e!A0A<<5iawaVrrTHDwJ2lO&9&0A8(vY_1)h1NG&Y@fB4;Sy1g_(#m^^vzWR;T z>5JYo!Z&-#|4XBl7KHgp%(~sB)xAe%JxG=od{Cb~_~iFRj)#Fej_^&c$lsbFmuse`>X%Ia%$?T yE>^StpIU8j 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≷!Phh>1rHi?U=*B{rsEX!C&Tlud_@VQuY<_lm zuLvvQ`H2~ZD6Is9ql30N-$gHl|30U|f3Ap^ggV(JCE98jkNXGL8u7awf%8p4CQx;? z#yT(i=CCjt_(9IY!^G5d>iTS(gTku84{{!g{CMd=vrO~3FeSCTy1qTln&1aMm#GZ; zkJj!24gyb1SMU{&{C}15 zr~n&3 zCla7(hd`hGk8YH9Uv1^x?s<<(2=}^OOUPd#-@4S>x4uwYNR9&g*+R5?iIW*LaxHCP z&6R}}HqdNc9poc(r5>I#wKC;Mi%EhOn?>7FXcdl4MMc5E(_5d|(#zcmjT3Pa;s!+i zB6!AyL*NO!2PzXOh>Drn+QLo)%%O*JAQ@%%d;(!e;jil>0Z*AxPkS-JMlT5nM8TdC zkOUi;)eOW#h4qIGzj_KIpAV zrXE_gKB$42AbJyo0!6}wpqvm{_$E85rk=837f?2oFa?uft9%rM=K*6cXvC`w?B6|wW0heYyY zd1*elAZh3bDvhsTW)i6SF7R-qJiX)x{2)7gX6N*ifj< zzk6vekdaBE!A=LA8*B3YhU-<(Fi2|R-hv{gN*KyZCc7 zQS`$(DBS>L@<%fv(FT_hovwnq{*-`@!vG=4dSVSQjUNO;GTqNvMA1V^g9L+$e~u|$ zPf6e-unb)Cgm2e^Ywm9and(5M9e&&WW-vLSx@@igGRam2gaGg98lW%yAP|yi=hp1A zP^PV{@Tn0JvsjT=Th_>upHPR%FBqW`>I815~7be+hm!aDgC&V@i|-^)GQQ)5HX)?NVe*m2<8j z6Y05dEL%*8r$LLNk->@;oR;5{EmeWl#3$h5h$K(wy2cJ`3+hM0#C}j@edPQ(bclva z)q5;_5}3r~}lSW#LFvJF{bX*+)JT84~^P$hqzV+C_XMQUBmp(XNZqW+ zuePA-9(Y_rrGzSJXE)@=IlT--sZfcj>~^Cf%}sWPvxK5?MMaaKLvLm%RDpV60S4{< z*-u%~O+Wp9-9skX9dWEM;ZOH6gNg>YPp<=#t}qg+S;vZQYA7)qjxUP%2CQJDwu&mu zD5m~d!u!FMfGYWjRDu-=@uk?&PeEZYXOiW_ba3tAx-YPzrdAB8eG>RJ^Dkcc7vRNb&g0E=C&%eaU`!}v0z`C47~aD^L2(1LZ!rW0Aam7y~W4X2~Kso1n)7P-*yg|g{PgR?~#%6M2fYSBh|n>1$uFUBj% z3p5-ex7@Jb$vrnRHgVhV;0xEVl<}}|)S`*Zc|jO6_zx4|u&Q@xj9uNq4k_Dc;8IPKi_vyUwxRVEgCQ>eK|$k>$;a%DTm}RL-I2RshB;_= z1k5sv>JszDjxvU$$udIb* zi4o{?QPsiVfB_wL+Xvr6PW8<$~HjY2Ne*xgteN<4L;M%i?Zfl;yH0ZE0a z3z9>y=i1oxc8>gF3z8X8&QTOo=KVl$Fb=vwc#y_Asl|jWwPXH;!Ebg;n9KDE)Qj}q z4)R6Tx}wxlG_T*tf*J+Oykle*YT_CD|k?C%cmnYpd%sx}3zV(IUKB}nx3zP{lx zP@(yn{j?_}rNo^KzZXM2-m{$P2_Lmy^nZ%A$=kAWOT88h3iTWEOEB}J-;K1X-f7%V zk*HQ8Y=J?c&KvUNj^(YUKBG-G#vna8;hzrO%`|soi&ae2C=3=wQKK3N3KYhGUPhpy z2rv{tLo35c)yo~oOf0B779YBW1qBtmsuBy; zT!w0kJ6I^t*2wTo_pu9QYHPgtu|k|5lec%vF>=Ryas$PAp!*9|g)~ejlvpzM`sU^3 zDkaV%HEL>iTGnTEyMd;|4y4_2I9Z6S+cdC0bGcfv?&s$`@{0AmdaobA zpa@73#Yf(UY77XI7&jOci>Or9$yd)@&K0XR_pCm?P+RTCogLfoHZpCJy21A zkIG~{GMp0p%z<=RmpM*UCx)C|0_qzVsuM%*K>N+lAWHp@;P*@2rS%Opekj>5^E6D* z-JZYUsb?Z7u&uDc++SV1CCrzW71YL0GJmcv-Upo=z2$nVyQjbVOYY0&s(<3^kzv<< zrI>iBKuKDXYs=$mXMHN48~*fM+`{#FgYH7|rGe#X1#bAvx&56To0~T`w^Ezm``jvx zKc|`2@er|?Q!mo|J@E3o9_B|arw@W}$wiOwW%7cFtwaKSR@q~SpQ zfgE2yUmwcXOm|l~^-gqXN|nJqC>8j8fufTySQ+#s&EE(AQKrAsU8r(GSDLIZe%ibr z#LntQH627t zR%Vn`HR#Ix0u~W^;_?x`wEkuLWmZNAita6(Q07VK)SGA_t1oUGAvkm44^kg%I0SWQ zTYJ9wxHV4&rl;GU1o|#GiPxBqGcn8?tkDJ6_vqHN6S^S^BgEklZj!So0A*{Ye>jgXSTKUJhk$X;j1)Yzw{U(LVw?fLTcjF_D@Xd$$f6pNg5i7l7g|i) z@PpKQYEc8*oL&%x?^}q|+W-vApiICtJ&6bm%%m)ETe@EK7zUQHmtWB$Vk0oHf{o%| zBL(voA_g|iSX?+tuz2C<2IjvQcdbm|0NUn*=>?bZnTtW2kEGki;Xf=M!@xY!_2Tj7 zazNQGa=6Aa0WYBJOLDl6Wr4>dM>VhjJqu+M@o7Y0U~`Fr!VdatU;`^yV^LrtzLF>? zy68s(%LNAZTpp)ZE^t0Q={nk>Z{UR$aDNyf29|UKudIOEY&5!o>6wYYA_}T40tU9q zAz-sbsu?ga9f!g#7JS>rFtD5H1-J2DOF)|=mf&ud3)}&1&Rl{^Ef2gmhJh(B#Z{FH z{Dms}QM&(q{Qjjt*_r9G5AYF7M>nvcXhDAmeOq8){B{;5=HikUeQAT|KcotaopRbMv(JUCr;e?k{lL9$8Z#5f$s$9$IVgEY7%=CtR5+^EUwlIC z;=4k}C&w<-dlKVH@HePgab*+-5DybSs*gOfXM7v=9L5rB)Rq{c9+l8|W#niU)BPUr zmEkD%Mn4Z%=?>ieQkVaPtXMg=zV74UGBN#f?yCO6i*erJMQ1&TZz?&q0gqbLQ&nCp zrIVLd_b?}k@%BQFVynl0wMac@>5blWzd`Cwg3ppff>pOOgO*S5%WEXiW!8}pMCZ%C zaI0qBH+{{ay0-|vJ`9a;-OdkHh0g||sfGNZY9OI2pG3;D{=ITT(NMXkji*e#cb6Lj znk>CtsV;VQCs*lTvVmr; zsvu1<>rVKAj-^Iz*^z0jPi*~iops;^fx0<0x_KvTW+P8;7Lw3YCc>6MG1Z*Xb=%l2u=C%@asPV;*6mF zXlSz4%A|g1r9&=%aWFNdJb$wOX5qHl!M2CU>c9o1Wd%_42!zSvI*g{2ETk4>J>Y_} z4TYQyFD$Cor9_hxX|qmCE}B5?I6-x6LcouF%Q5Ihy-+?_25A7|{D`11kzBHHE-zYE zRQ_TDC!*5;fGrV(Wyo~NOYlt#jEX!hi9w3~j^;vEAF&~s{2?#cn92gXi4zyYo*e?{ z#1zYa8sypn>5s(<)vzC1!yE%ySofmv=?A;77(6GMs|9(Hf{|PjTg9pAd@RXbu;tDL zxxUCZoQHC~U?C#|f?u#PjN%3#Y>>(`wyu7z{7$|>1|T5yiuYgl=KTr=CQtjVug5Wy=UGO>^@HsP7FArEK zbe(NI*qF+o7YHm8Spvcag7Ot&%AdHvA4rV|fdEI5athFLdW;(NnDA4mswtstxTmtR z01ftp0etfX23!gnCdarl6o-{r03)+-sDCa~W`B&#{AfV{$|=c4V+zB^=K;)7)^_5j zMM$@hNS-j^I)dJW5s;90ibfKUNIkVJ&iV*W643cVvA{`zuR!c`HT(fD*l5g&1_(eB zS@?kgR5Gry%O5&dkOQQKh48>&OF=o6OT8yBVVnqf69W{@7k&!t5(aV_u(6nYpkWO| zcYB=IU}XM!jxFcviKKlP4Y{H7hhetBqF5yu~P4fx1w#L26( z!kEiE7{yD6o8Qx-|9jyFG<0Fte4tCf+3UksFqe9$i#M`XfY>)_i^JXUCg-vb^S)5{ zhjYY7R)kA47lupV!fWo!H=M2E_q`p=gj7zTnPPC0F#;Vx%P^4hHX9V#vT+$vvGGJ_ z3-aomjmAuk!6G?ln#3T(C5+_U$VNr-*&Mv;afs-IENW&WDwC`M8IhvbRMrX%mL{uR zlpe96mOem==$$>2FueX)-mj*7EM8j-FF%u)u${h%2|h*Oo0yhWYz%M*lJLQO?PdBpQjd~yp5!Mik?}HVak78=e;(Nm8lSTOu z?XdBbo(e3SV&+DES41~ZDxcCK7Vgmt$#PfT3hLC4x3N&J`(xO+(Z4nZxFD=nuq=02 zh|?~`?ebPRgY{)LQTc*QkgG`YgwLkIHLp<+=`jU9MPx)?3X8x-&ekUbge*LmKVafj zMekv9yh$#|Kt`trFnV)43$2Puwj`xxDu(k<4Ckw4e->u}hSL`11S2^MA{&9pVi+UP z06Gl};oQcC<$`9r6RV*4SfXu8bor4GTW02W4R^i3QAbfeT>* zIu%lvAv%HqoyjbqWCX~9U=>*aOaCk)3xQdY?Eg2?&MJ9ny%-kIlP?>|vI3 zSDviL;?rRB6@Vb$(D%L6Ci3Yi|vh9%BFXyu!CTWE%hf`fcR9R*kVVhW{k#(EJ!ZGvW}}-&Z@Ng5A|fg zOOiy!UoA&#U!h1(4D~-X&mmlIOLPvrKv`k@n&*^l3w?V(f+XO!3ELzUW_@=Ne%2Na zlF_AQT(?4#M{7Jt=24BBpeu7P!4kwEzv~Y_XA4Kc)X77i;?)|D_VC!LqmWsZ2MHik z4E}CV``K|Jo99kl36{=tF9EvHk$VAn92;jMtLUfv0IeGLs&oeI@&1m-{@qJUW&V|* zE5o5X0ekE+aV$EN<6#iIaX>AYrlLz||8K$^)0j^6 z0rq!C{%s&{hVL-pb4i7K28{fIz4VuC7yJOGRo^!#nit4f8ak?RceSF?A?;TIYSF=r zJ7eKVZ?3IiQsoj*%XCbB3B)mt%2OBR3F4SW<;C9Of2qCI^JZ^6LPL46H_u;cZ{6T! z0Jj6{Pzx`}_n{VE6sOz2vlaPb40r6jx*D3u`v9&dG%`=0THvXt{$h(OLmvTfi7w>{ z{GJHEOyL?N8TCttIA7kT24|kUH+5dTx4t%>ytmb%MA7fCQ$Gvg8s75%>(>OqydxWT zB3>>p_)a$fc;{XM_|-5^08{wo`u`iyMjYo`0r-Udwgr};XOQG(H^Msv+tDGP=qF^j z2DhTOS4Ka9i{wzt4(6o5HOf&^LHbA4{Uo>*9pShJDVL*9K)BVy19X1iz6Im5LHdrZ zU&&QF-+!gm`pxR-u<;z~U<^|jL;V!b2f7SG24isWS^r>bbu)?K^KIC51d5VLepfJu8|9!kqb`VepDhv%pLVY4Q zPZ-{b;yhQiMw#iLyJC(oph&>Z>b9|An*Vju4Iph=vEW<=L5{Lc{sXjmZeD&O*P%bp zq3~305TZ^zAVhr-UB`|)KZg*_WAXk6XcK5_{Gl^BkdbFmZE+R*lLv&TBdV?O=I0Qi zc}(81L7PBZjdBRn8PJyC6O7yNT9UdQ36hW>$X-n79uKi|Qe3eS8- z;__PE*@-F66!sc=ryO_ze)GT1<=`J~8nfZuW)s=6dP?%;usl{vk}z z8=xf%)WyGs3HptImP|mG-J>Bu0XEBjF7EeIfwBt}dOd%6T9O++buLbE{ST-%Sz*nN(Ek`V9Qr&dPni*(g2=SoA%)--e3Ahc$O?* zL``-CP){4rIi7RaUp*)bVB>RAO}B|1`ebZ_yOb~GXUt1$UziX?yo1lFhWoSMOseX8!*A5ORuLPQFEHUxM76g zjJ^DVW#3DE#)<~u8G26xr={zi#7oWx`ew%egt2frVQoiNI$!tpfp=1s@Ao$vP`|-D zwaXcY(60la2{IrRN_df@eOYMjUjFG8CVz-aUicBrn~64=8T}J9IrV8EK3(q|XfiYU z=Z{Th>bmY}pjvuC7|^xsd3?kITpK{97M#atF2Hq-fK1iW1-8iy{)yOTlA$<0QZRHO zX*g@b`Mre;EciBv{}K6v)OEeUzOF{$yB6Yf$^=YM_}|uae9WyylSQN3 z7c=%Ju&=I2!Q8z8N@a+BF=Ky@ZeK}df&0_-E`v5FUB;&_#y!}8v{^3>U$+>SxnXqs zVut<%%C0X9^i1bV0Lo^D{v6f5WKG1U5CtcqKuaM9{xud6Ca`^_nuseC1*bX$tR2C= zD8Rl}mIsEV`(MX@ln3_3jQojUE#(z(#^r%gBVaAjkzD3EQBc((U=zSg>TkA4G86wy zM9|^^TP(WF#QCB}H!x=4PtfKY<$<@;{qKM_GXsB)ZeSJVfe+L9?xUUgKHhdI?j1m; z@;$(xUW#im8r{H{aX%3Q(=!*Jc~S7xBGPc)g0XQ&1CH?LQ|irs0vpBwdSbjnlorhN zsqLGK(31ygtiRIl+-v~L4`ce&@Ml!ONoN}Kn<Z4dlv4R^voblAteetKLu%a_!Kex2!qGZtdyHfT?#qS-Y(?A zXTvZOv=D)kMYOg6ye9m&p!`fBikTD#;X5B!$0Kn)+a}uiwL(*$Sf`d(76J0VU79TA zGW202W^Z0U{xJSCqsxfV_w9rO@`0|CG{swih^c)HStDsNjO62PH5t!ey9qH87$49iAo?V#SwTYj8cUI zL4?kgn*cR$czBU{-%v)7y}AQylwf&iHP1eH$d^-xplaXf%bkup%qf1$E!ivPfthMb++bkl>j1UiE9i<-$QQ?n8^;dH*` zN6oT*qLG{vPT!8JX}Ie_%TaMQ$&LdxsV4`W0%W)->6OO%1r=n!PB^`=_|>NN&h1D} zmUPs+ZR%Uu%oDK8%EtUG<^U}7T)?b--J@C_%B4;0yy_vA>?1FYhmS3wqlUHZwss

zcRi{t9i7Q68hK1>^z1$%LafL?=r;qytbt)JES&k7#k>&1EQ>O08qv-}xn6B-w>F}g zX2;Y7z{ic-p`%=)=#-m!Hf`>@p7ISF+xhH1C3{eoG{8B9gw2$aeRj4t_N!5?o3bqC54IR)A(T0*s{Ry|u`}5NI#-aMW(*x$K}S$7e_N3*aHS9_pyD5ZoVLon zrrDm+o$)){rh+;VvNB4BiwX)*$c*W)ua<_=SIb{IuF&w9Q#wz-*F%dj%(58f2>}YJ zEM{2@Ge64gOzf0`a@`KEIYI2a?lCVp$R9qw3mpy0OHVY`&jt1wUQ<}`YJGd&_G~z( z=%kxEMzs4iC#Xl-9-Cydm?vPF`HcBl%mG;Di72yseAZMbW5fD(=zQzyzL){|h79Nk zt}K6M1-Q_N6j1TIK~7oQPuD$2OB&QuDx^T22)V!pkl~_)=%%ttpgUjp;8X71n(Z9T zAsuPxPX8tha}t($*mN3;xl4)797KSbFFs(CKEJGrfl3@2uG z`Ns7t{!@hA9*HZW+k;0g578|pI1b%>mTA?-zCRgUTx8dj>OwDF3Kc}pN*5Q!g&?$h z!D6_1;yQR=^FEG4aPpti~CvhIW)xt+kgYlWkcy*gSvT@v|5{dPs=|0rtn~0ak_cm zZX5wWl-5;RG!K_jV|Cr5R$ltUMjICHxE?;<1u(oHtmnJ|y~2tp+LNqgzr zY|TCt-FX=qzu2JVIt+7x^_*8M<}M7gKa;tm;s_HuoPDlFKlkgp_6OSm82D52d@InG zVJQZ#dDJce0AN}TRMC$12i^{i*buT`0T}@gC&f(ih^-#3zY3$7ecGY&?s7`kwzJat z8itvQVZIb|%ALs^bR5I{3NYW@nms2u$h-ZT$GY~jHM5c@z=w~Fb`l@ROF!Khlm+_J zH4i-{E8>G4Aj@%(1$0FwV~`h{@yW)uEJkHEV-Q+4(hZj)S*^CN_K#6C+Ibf#g+FIy z1n7iIjf8q?*0mpuMzTbJEUIu9gb7HcAFQ(Ft?om`LU}1Q%8E!ILMoR?zPlaCB5RFg zd5v^G7GnX1v9m^&#aO^*3bX1lRLH}rPx}{ z$piMMnjCbj{ia7Q$nqD+k^m3rFafDFNwI(li;7Xq*42hFqRBX9o8ZxIl6|ix7~1%=SGs`92$7Gn z$N||N@>VgSlP!o{nm)<~4M8T^=(buJs`uFvNW=2dmo^6d z4U7$%_u1s6jUhD-(Tdda9R2n+=$2%;ny7f!<0`b7C{BF~Jc>2#AJ}9UMQNj$?b#J6 zmq~xH*!)y~er?g-LGWtL@#>nRlCQvb=lZ&V^>05Y zZ~R>@9&s4B7GIM$-I00Ho#rprT^w(^Q|C!{bl&VvohRLuDVwT<5^ZlxT(C$|q0n%$ zfswLl;DtyB4B_I1-&i>;KsrL$J6oZj1ozjo!RIjBp=FkMbUW0v58bSB>gYz><@I1i z#$?oA9}3@DWkVQtcUU$T_2Dg;PJ3z>$6@tSYDp#AV@D2B*#11_5%Aby*Zm>v#-slH zn<98b%f&+;`?De?Ts}CU?1J2Q8o@V?0`5a#j~#K{GhjC!`197uQU*q+Fl|S*la3*j zsW6oR5uP!%2NCcK7S+NxH_#l&jb5|bR=Z0Xg>Y0pR$W-(ED=9L8I$>k;zyGw@(|*I zaaZq@1&fL?sXtlsWwZx!qiI#N``M09xmX!)pnZ?4YSE52d3;UekmuBeNW1U)+>aU^ zO^OZGsB^}Rq7&d-Lut;-^lW@G)bLb}nU)C3Lki}Ao$r|Md-A`JIdM?Z2g*Fb2F%MfSXuBo_)<4_Cg zft;TE!>(M8C-Gv;hwjxJPvWanE|b1;c<#`_p{AH%5~KS_UOs&wx0&g=mllzBv7W?x zMLR)l|RGZ?0a!NO-PG_xYC=%SiBQJJUaJkhEnkVaWa8SUEb?CO}uNk)ha~ zcnjJur}t6LkQ|}B6*C-lj#6~#t6a6ZD`(i3_fiu}V|(Jv{M^f(H}_X6=-wJ?Ba*HF zO1J`no)N{+?k4H_B@E;fUeTM$x4SC{TdsKjTXCy%xKvl}yj-65zn=MnJ!IYRYAE}y zNP)MuL#4u@%@&(2+{-VUy}6R!Mr;M- zL0Fb_GEw{1=T#2X3hxzsHotW*PcZv-B|Tony}UwU#jT1fDw|_hIkXXD&Gv1c4WGJ~ zH&+v69LrbS(sBp8W}@R}?>4Y2RiLbD)xMQOCep6-94q(P>|Kpg;@$R<6W(UcBhaZD zW>uf-S7_2Isem>D8a>*>ot28cI(|WN1|pN6%GVQMKYBwc@kZ z3eE7h-usRv#Kt@BBgek=X3}GH8Q#8|`;i2$j52;n3b5>}ky!CcG))*V_xsM$G z*1Hm=qgq6AfXtWC97yI`HXX{srbAg&QTpGg5`HKeVLZg9J07!U(=io=^I8DY7P)T1 z15}HnQ8C9J_{T<{i0%JSmK~h_MJ4p=Z ziVlLKFMX(Yuw@utS5cKsbtOw}vF@Nz!or;|&mN%G>Q;xjGKPA|RfaDZRCjVyRdE!> zhA`adooRO15^RT>_n_M@DG6w!4GjAfhsB|r&yo7W=ys?%)wNFJ@LaX3cko_RDkSV7 zvad!s#H8k_=^Xa0>$`_NbXLoI+qbVdE{yW1yRt-GigeibVRtSdIu6rwn@Yr_bPo@u zb{Cgazw5JW4hfs5=2zEeA(el4D82hxiIPPl|AOD6PA+BvHy{CFPp8MCIfCjgKySzQ{S(W(5;!4>qo&Dqi-qf^;)g zEpI}T^}%aqZiui0q$7k~Xu5Si!8vm|IDt~{voeod7ecrT(vQBOkffZ9 zzbB8Y9Bo);^_$}rlcQbV#;SH!<z)U)F`yo+jR)M7_I z1a<%uUgTW!egx9~^N^{Qb)f3kP|7$C4>Crrt9E6|0qd7JH@$OzIJ7~`#)(O{t~@lZ zLm#wZqZK>yVPfwf?UWQu*hIbD)_c%;T!+K=NcrmZm)cZ8)jOe-aU6CGk6zb)blX8) zgKp`UC7>N0^%ULG2u(6RI^rM$F+aj9=7*=oOxX{q3ASG%L|;-BYQndfmS%>th|f90@|ue%K=z=nOl2*_Y2k?MAy+KMjWxCVQ8JysjPuch!ji6@X$)$zsg#ZfePdR)~}RT`hwjapL(&6VHJ z^7K4vR-)%s@1%JWpGpM>m%|H*s1dq~AH#8jBQ(6;3yd^k!YykAvXb;b)z9@5H6n0g zdv$1unx|zY7CsH0>=qDML8jNQs$Zq$&FHPA#6Rrxt)Kl^2ModdYZOmL;C^o@5{?Be zpPc2gMlmS@w0xFZK(OWh`6IPF7sxIo?PeEZ`C&egy>GsR|I=VuWDyiq?+?Y97jOo9 zN07Y_9Zc-bS|Z=i!$_ph#%RtpL%YxUr1zdpgy9=Ff zDO!-lw?Xlb$o~37%@EBHISTw-%l%D!xTbwAZ5Py^Q@$&%ij7}M3rEXPpA>yPRK+9!Y*BEBWnu3-al?z6>HQgAgESz$J@VB5|`^@D8|SF54At zqj44vBN?j&7{P-qvRrm3CP5WcKYp%tFB*5ab0`Y0akS&p zJ7AZ^<83R4RCjpO+jeyP58;lRr-t8Q`Ws{EcGPlj^Q5*APJAI&o)|{@>*;@51iiA9H$#NfF(ZuK{r|52pY}-%- z1AoX>mGNBRdI59(9QG9W^KY}`}EUGLz z=^d#212PDH-9wa{L`@+%`iYC;$b=j>NQ%UzhniAd%r7l0E`{<;1q@LFZy6`|oV2B@CJs{KQoAaPCeEI+5Av7E@TVm^&w z)@L#!izZOeCj#|kLqlCI2?PKz4jbx;z%uoWp^B`NA0rET86IuME-J}RL|`8R z|M&rj3Zw&zz1Ymk7-oMv5k404TnzIElzBD`l=X)(jbcow=O-3IQwV3)g+kQWP)|5# z(5c%1x{0j1WUn33!!nDQePA&cV3;Q`ng56T{2%V~f4I;8;XeO|`}`m7^MAO{|0}pp zB-I)%+zJQ^uLofXid1~}`@_O)w)iLj)Sz9(NT|K$st4Ho0q_xEFDE$xi@OSm!6KUg zNG4c|#N1ri=^8PojxgCG*knVL4PMG6fo?)=P`wraZR&pDIcOY2{>UZ+*r)@%ve^pV zq=^6tN>n6@mM72O4X%N_^c6O_DtT>tx={>J36m{?o&GeLpT%~GO|C%LJPq2b;N0_! zJTVZ!@uU9|c|$HHqDzao1Tw{fm8X~-{{a@{DAy5P=f)*F_?SIs%YRCGZ3IiqJ!}(Q z;2bty%o@ivIZ+MPp>kXKFV@`(-gM{4lkSEqc+%akwmVJUbmz&N?y7jwo#!vsojPy2 zbLLHVl|1RrnJ3*v+QBRC3%|$fjd$65KJ0kS#7SVb+FhCE==P?l65VpeozSiG5&TGJ z2I{c)&r7+IPahzc@6tdQlRso4J?)w}4k4c+jMII$=(ENwB|pS;-|db~1IIWI%qcLF}gNFBj4ey^SJF6)25oI|<6GF5`l zCU6HCa4^L zCfOvdm{4(T-Ajtt*Rz*3XsfmbDxSpS*S9ofrEG{dC-|KSCC*&1sH3j3?%d+|P~ua= zGpyZ)<)*(cSfm1Wrr5NZEHbqF$FOYm%@zBuZ;-=M$L8RuB>nvMk~x<>u)IHJ8ok_( z%sZ}|xMvT#5Lt-HJ05{K3Oj7CZm-0B_3tlLT?*q~^J+V0AKG72#;xqlcS=+baLqo& zz-SWZHUGH_?>77KY@Zi5qAvV4Tj2t0r(j^T_r@RS?kpxPau18V_r?uLg{;u_(6`(- z-m+wg{nSmXu~q+A$EadOKI6&kf(s4pluaWp1cuJsdqZ}C;B1AbhWZ8~|G+yHKtGE~ zoXN_eVk&PSLQacIi?NtoZREA1=;3b((OvgWIxf(UC4hbTcZFw<9VWNiH(pAh{^?H-pL7Pby3fhq~vv z->ivU*G@_-%8_qcH(ANZO99?(Ekfv~zgb6j-8s2)QO=U4M+>3d_Q@$mUfYWfZSb;u zoq2Q9WM?BUv!b})8i`#uPpU7pPdv#D0#14izF8LZUwuT$gXvhZHUOuCDtS8 zLXj;-IsdH}6{bc_LTBEDbR+>^UT%1o$1j)SmN%{3d~MWb3{iGL@Sbs9n}KC_S0xoHn1dKsysPcixaN&cI|Yq?+;jpevjcKb?-T=(pzr(Mm<+57 zvSzvIejb$d7Y~;8&fMYNRcMyQ_6JBDtd%`q*?xJk)yo@)E_>WJ&c3;!mU-?_^T>63 z^JVxHnrk{v*cV;)*cA2PK+Pi+76)`5y@B@Nnz2uO%EKcA58tQE=;+ApaG^NsY7e^& zRJZ$PpO^k7k(gneI}~G+Nbi2xZnK5lapIOzzU~QMpLe?JGA!S z4Amo{`pcCSgcF0!?X6$3TmQC|;LC}>@@RJX?Z+=4uRcQQe?&R@ETH}JlQ)ddlY1YuH9kuV z>0sQpcXzx>*7?n^jR7S?&I z@aw{yD5Zd2_MV^_IaQG0o*|e$~aBL$h@A?ujk^Ti54X(w>*}#iaGK-mh=Cezx~q zJ45H0PR`I5io518o%Av!Ly77;FhqH&w1qK&{`BN)5eKKe?jCLqc1PeN=RF5SL{`Wu z%fbgLD^{tfYOu&EDkxn`OKF?8i-VH+mOTd@>^(%3%#QByfC&c9Zbw9v)~s>%+AX_m zmF!9tWmz@lmGH0Pa#<@8rS)#kM_nLpGo@{Ac24dtc5V(%_TEZs&6NIdICj9^!F2r^ zrH!&4Zbuz-bVQVl9h@9J_RDIBC>b0$?BU?1WN_Hd!{HAHd*{6lB2WfYYOgC3_@F=j z5gMYhf-Uh{eOYBiRS{WPStWZrm3bv7{A>D}U)@+<$^CS`r(-HnBHYH1tvCADX+ z3+UN4ZGF?}YgNM6ZfKplCM++LvihTj&Ux~Au`cg@2GiHSa^H8OefUkelS1fmLjK@m zLi`P%6zAML8tuNb>Wc(xUDNA2Pkz6e`%Fs5g%Xe}eD$Gl6Y*Q?*1YR+?tH#7^V?$2 zchx)yIlpW`biR|+n)C%5HFqA%=}YaJzvk1NlctSR`f~I9Nmgeg{^8SDENE+6``l0A zYe$l<&huHhN3sr1Z4bGsdw0dF&dP(kROb!I8RkFp*{xCZ`k`>Q+ed0cqhfwWUdF@% z-;>*x7GE`-$$wpVme%IBVr_a?t!AEcFny!&h8=~Hr=?SRcTkA`L*pQRnFc6G*niFcuk z>G*fe{^VmKU-XvgZjshKv*x4PSB>YnxGwFGiy!>B5BxT7 zjbp07u!w}RWa?7OB+g0#m)%z{iyGusRqTd3%3}GZm3F%{&QXSDV*#Cn$IU!=hB@)p_CvS zE!^?7MWMkst;$VQDXF%s4szA)IPF9GzD$XEcr*K8w#lj^2mc8(TTF5pzR^?jn~&>O zJ3niuDzDvtzmDAZ{D5lNh9${s6r+qZE*Ab75#F@JF|@MjsA5Xx0qPs^Sv^&M>22D2 z^<~?^ux@LeNmFAE-Myc@R?Ruoxz#@FxmW3wjFO4wZ(S>m=BJrTTAg|1D|knC;bGs; zy>p~=PfJuBn&jmkR#{xCXyPRroMriLL*V(>BAW6$PwsAOxtLE;2^;$`$JKo~;6V4UY(&KK0O;UwPh)7ODLo!*sIa~{6_q_E`5?2UiF zey*@>)7Fw3ub0nWS0*&MWB&qO6R&K-%OvqfkHr5xp_b^m)jhPgsL}7uTT;H_&84R| zrM`Z$_}&11>Oxwn$Pv{lbN<5BkL`$6pO-FlHIBUgsC$^SU|Q+1rn`67=lpwm<@!PG z-)%#0pKm&~Z)yJ}tE3LPUrd;1Z^kztp^(9MhjZS%(M{Sx3yDv1YH^x0GpM-QX=SNp zoYwLqhxoR1pT72Z&c4#GRB^S^OLHaWbrlH@ZGU%9{nGvH7pk3!aRy1^CZhZ64lX)a zry-xD_VLL;o2uM^*1;9?)&%Aj%&^~8R?&W8w*8FQcB$Ce4UGpUysvUP+!^k=YQ+gP zx&6M^)!)=kYJ3zbS#mRM5ySTHcLQb~Lwg^GJZKHL8=EIPFRCRl(zdp4>Ie0`8}lz7 zizFDXl&H{?-5FD~T74A?yXjOD^MO~e7#_9KaPJNO*ctpbUpU<1;?96$X|7dZcK%qli zt?%4br>ez_TPj1OBc5uj?oGQrCkJ2qmh`#fw8gGUozss+^WGi~n=F%>TKeWQZCb{J zshc#MZ&o#3%~Q$uK6d@cw;aJr_0ypX7YnaeEcKo`XZOQZ0h8t`FRs2otqhIrEm*T` zN|z$v;q9}!&X>8hbnVw~@*rwBZ5JekR_@=cnmRqMKyz62-_zmb#8NnUP-f)i6YUMcWeP@a;~ACw&P&b|@t_VUOD@z`dcg~u%=HT5qsZG7qNfUGOT=l80%U=0*kk#kyQ8sJX)jC^J%)PSI zTVv%%`LzaRvh7_r)+(#&t>0W57mgF|ePMB4^ts=ltbvEIj>@U6gR)VQFBc29nywMp zV|!|2h46yAt0bf3N(Mek4o{K2rlPMfy(Mss$%+l3-h5G4*DCkmU;QZ(Dtg!TH#M<> z2a{H7ogSPtGjLVfHL)GSlSOOQt>!oUmDHa)r%-KR(wglp6FMWs`Rt3+{%tw&WUCWx z;=pe0g>?~=jVBN^P91q!VEp%ig45fi&E5@dUmkt`Z0OF&?(!#@#L#C2>(V9{f740L z)jV}Z2!HtRHHJO2Q!Woak~p2L6|rsLQKAID``xBjHpRYE|H+!-F!`WDWZLQIr3-He zXXHB(tYQM?w!B&8alYoakg7ZO_g8luyl62ayDYxL6 zvrFa{Kb>_t%4M0%z2%-Ky_ zsw-AzX~*t*RQcsXlDEv6KOU%EH3})VHu0~K?_TNiEb^ahV%awV+Xc}gt%imd&5q@6 zJ@~Au^5G);{pF8V)y8b8c;v9r!CT>tO4L?IYvbK7H} zAYM#2(1w1{!oyq7`Ul5h{#~RHG)%trUWKEMi^hf5&!*zO#9_!#Vy{0X3_Fb-+ zs8P0cpHcLzpj6rOf{x4U`|@+TOMKpF4`|91lpUe>5u9)PkYCsBp6#x;9c zf2)1y2@!qpc7N7wy24eR69e~5n!l0ibKjY0b_AT=6}5=qq_nJVdcD4e?yE~nR&JLJ zi58jt%*`!W;I>9)>2j4t7wxhP1PUGhh<1pMo=o~J-neCzLr+W1Z%Z!*1ZjBGT`l;0 zqI>X-%&I^VK~rZ&et^=Wf7V7mjL9`Nt=IX=*Y|8;Qr2|e>QMZCJ@du$Vh*%S5sBY* z-|)r4yBf*XVr%x5X&hJ*vUPRL;tPgTO_QT54IN$*5Bxu@x?_xRP1m5?wr$(CZQHiH z?>6tYZQHhO+qP}Z^SqgyGx$zUGMSz1q*m>{s{UNHYW=vnRAD6&n=`Ne{~aHWn2{C}?UHfCAU66(%>Ij~i3F^%*a7YdmlmY*m=Qsu|P$csRex z-hZCD&H6nbUi2EKmv&fS|y;jLhzpBPhRN zyLZF4iXokP{MDO~+QTb|lWx<6CdCSJqYZM92boAlXaG2)d4Om)ug?`!7;tJW$+pyfe&NfgIfs3^=g1$mU4ayDTshdYQC z!@YzSv)gqR>AMNozFfuIW^GEh_5R_K+&}tQ&Gm8|lWX9adjY)WIYMcW z_3$7F8%`(&Bx^urwYy~X87-mr$gabBt7+17aF@~T5X5DSYcPD zpQA+KOi8^?Y@;nuZ)^py3UsJN1gV6Hg=bGqBAf`vdg& zpHx4u^u1qfInJ|okjk0izn5yZyBq>rNvcMIw98B6(-=~I)MYiuE1>bReG`Or_(PA_*XZs9`%kGVXL#k~T| zpvQ`ZJXsXS6o_f(M41W&3xXG=h>|B+Y4JFzWwW#jFM19J)&Q(p5`e3s5@nl&a;P$g znycRKcHaDQ2F!a$uFl`~;O{?kjlgf*%e^S*9D6>Eyw3)Ff?m`Z`Kc6;DiCO>nz5{) zA1=mZvNYpZC#sJirm5X9(tGzd>GebXko^Gl zJ&%)VwOi^mwRgCDxZ)G#;rs@FbeZs7cqAF5QJ_W)=F*h@Shb3qCk*mrrKOqHhj66N z5Gr*gBuYy^#ii^$v1^qZBAc7G-yHo=w(e_eZ69@?*8B9Xcc${zftnBXt}&rh*G}#z zbQyv-k_-0YlVrg%l@f-=84hSnFi4(bN@Z^rk~9Psk%faHOaG!c)+->u(-LzwCERns zBnwZ+zwy^S1;<+ZdFie&dKqX^{v_}^c<|lD|9k|Hymur0aWtrRf^L4#-kW6ZpH-ni zJ%$cJ`KKS^q}9!L^g_`>Gm6P7F0{h?V`0XUu_l6)moq*6bw7~%YkfAm>+}5;0`6wJ z*(+=nEG+I?jPwI{J(Cd(iFnb9mjl^XnFuJJ$nfVV>t7mmFlGRKDX6b23$j zN8GKpTLFLh`BeI>1X_uY!$*5}Ql-_+M=J{r*)Ia;8EMmOQyG;E$p9GIlY;jVS)0TP z>hQA5^#TJLX*qv}NY4b@4p=1UW^~fI>yzxEwb}Z$f4%wJ>>PPxvJjD~=F17Oao8X+ArDNCmut)th>uCQIx!u! z49f~DsTRa(HA8JE{A@Wr)0i%%>_@bySG`g3X3Nha*;cp^h@n$xvx-aK`*u~)iPv_T z^LeW`Yy#iSM&ZW+M|%7tnN`S3y9T87`;>_O&*s~y`i0(h(d^Ee3gVjiu36+L4m6py zlX9vQG+fe><(kH0j$TTz8%IKS-6q6;4G!CpV)i*G@TexQRLW!KS1DeJ1w)6WDrD5{ zIuk4jL1nXCx`CAv1S`-@2_s7k&CV0enZf_G(tX&MJf&~y@x3gpYx`*rO|+L5-DQjxgMNg^_Cs`&qhz_q`xUgm0KSyDw&eTVs#S;9H#w<@NvJR!l z`8K~0nHgzRX_Gugj!f?|a+S#*3lx#DLK_7=Nd3X<^ z=RSp&FS6|<{_%JpNS=wV)UtYTnBBu}BQ}@4z2>bs!M5qRtjOpv=l$M_Nn^e8Yc+DE z%r$CMXK_LHhzaCDE0bLiw}Q+{DH&t9j1rT86;n=7jA7x|mEN0A6f>7-23#}(tR2*~ z{cSKiRjf}-ds&+hmUmDN!E&vQ;vEp7Ve*qXd*UcJClJR}2#=d!RA8b~ z1xubcQNS{dBj&)|GPL~LaWPe>2U|W*5J(gvKU!G)_xeo{6#I)>&nXFwHKFldmHnj` zuNN8$sJ;v^gC0oMsfS7-+hFu)&#RRF&`~9 zDmjWbgW$ufCJ9)Cl0;c}f#ra8>QFdRuDBkt_PEI?f21Fy29kpO#;9(XR|&nR>Dkr0`tI*pY;E|<8e9h@NUlPj z6QS^kkpgTAm8w!o;6&J>0cNDWbHx3o3ueDKK=F1 zG^uX6;+KFl+%kHW`Z$hGN&ZIyN&F{E>ej)&lJ7;A5OUuKk5O|wC`LsB$_&0AZbb*} zn%v=2%BF*n+Y-(M?#3@L|rNF6_5~?9dG7}W(Bn4)( zRKG<=Q5%?y{}|`B1C1UJ8I%OBER+|vs{yn-jVnX1AG_W!mf9uR#zWn{DgB)}7xoKXK(}sRVT6jj zL%qrb!)Uz(X|e=HN-%ec%($T(-F#j6XK0{KM@?3g!=aRp5XxE!ft$2bw>OV3)z4R? zyFAZBuLEJv&9XZPe~kQ)b(#cF-+rQ;ee!t}=C+3`Fs`si&=yl2Zd; z^t8mO3KcAG@A7zuR=1AbLfyCs!Jc6QZUMRDEUsj`I-PK`29>d`ja^QTj{9@Tk`xSP z@b+lK-{X7OUdi-HUZ?%G`5Z;}FXNTgnu%92WpA_}-TT-3R{C2o?2;6GtvblxXZ%^x zgA=?xN5pD+Xi(*XCY1^c#qgig-Sy!ZK_hItf2 zoeCEmDY+F1)|PuuW2S^hyI83jQ)U7(ac40i$0%OmfEafxt?~SH@)BE}oVIR~pVI+4 z%I(gF=0x}JiBT!cldvZMa@-I1Vcu6;Ew|r}%^b*_B{{14i9eDDkq_KQIW62S4? zBGrZFg~SEFu?v&dN!N*)iQ)&!2eWs%_gqvkQGp{D9x6g7<@08RcN(EE-kA#mL!KmD)izBz+7%N`R=GgMIj= z9k6PW$Vc8MX_A>I9T>MLIOxvUTQFd0o47sn*(po4hYwbN0Z9cb@b>)#>8UDe3d%)< zqP>24T+YvNC>zCw4e5wXUS6f6KEwtPt!CF6o4vB5uYqoPN49?F%)e0x^s}@tZ8i5S z+TOi>e7zI>Vuh|b-#NMsM`){RD=KPxnmA&W4n*9U6!+@l^Gj_#MdMp?WJUIl^|NF^ zdVcLQsLQmcvZFcK-YM(lp784Q`TClVe#XV^cjd>md3_b>uH!&~1<0NB^!kgaUze>A zi4xnuFNPYe`0K>aKZ+gwL7aa>We;KjXuh1kBT=`hKUNC?vI+bJWgqnVPTXI-&9L2) z(Cdi~->4T?(#hR~6GsvGXP6b28=QZ-Z{XVf1EU5AmJ6ey9@i&#b>Ww z$BY50185)Hin|2X0o@{ArX_&wy|$@)baf3G{M~mo&3sWq1M-4I6&=Kt`bq5ar*-!> z5+U(TU`cKbB#2u$Upq_!Hy_Vn5gisKdcTu+2{*W!aOd(d;3}_9!OI?<5$%kNYapw& zn;gV>U(Oz-&P6l=c;3Q2V!Ov^IKrsbBG;8cp3$J?cR|O+bd5fvDAeHmq6!%QjO^vI zSd2`FWv1%zaxf1conbT*8tNVKm4=oMRni|TaNnYc1L=we?wO`}wevy&7OJ55PGnyb zbbKPz-Mwq>6=*3&h$2d@0I!1zppa)GbRKdpMbL>RI8>b3Ko#PI+mJuD0`Mk4_vpIV zUAPPzRrp=`+izG6Od)Vgly+MeVoqjl7Y^gXKp%T5W_YrYvk7+IM_Q5IVLupkQ4!?j z&2U8A?XC{OvlnzksO%7( zwSSO{zgZi^?5pXtPy_OYOk-X`z5QISAef?fMOJ`%BHj}oSuXs z6aX&lLkFzmCLibRtOs$ONZ>uU`FBxoUZP!F&T=OIun0fjS#>Tp%!$-R%;8B1+GUVq zeiUjiWDZVYP5RG8Ax2o}pUBS5zSB4TqTxud?gHD6^5c}qEIRaZL|V5wx1fbQNDY5enSB%@BTPenk4gqn<3R@$d>b!YOjO5%asWTV4 zChDs7r3Lj%vVB3^f>ERj5PSIK^9ZrXvg9xx-wD!yE|!l?199@=%spy>?;+{xEA(o- z;;mSVX>ut^i_mikL=^MxB(t;wQWU!o9mUr~4CD>$%ijIHe?ZG2qlXse8> z3lvIHlA9vV^AttuKMJ$b7K`HbsNYdN++}tJM9T=rW8k|%+sJrmf4jSvBErX{-u^u0 zvYZxwz=nSgu&|4Y>UCHYft2Bg%%f1yU-#j@qbb|tVDn!aJ)uPMZ2soHBK8cZ1R56> znJ1;v?bXXo0dE)HuX2+mh||nNte3@@#nheg(16UsTkdpeMFg>f<>MEB ziImYrmcvLJ_TW)0ky@SWf62@aM?(mkZUtcvy^!(XW5$893t9)X;BnsRVAKYtOv#HX zJAyW$*7-s;u?yuk9lIao^Fe3mMUfNH^1%2+7>)#3Ibs%V@VOhiBNZ0c)q;qz-i_Rm zh+Q*4(H`t?L&0xR?oBI81Vq9cF0q6~!pV}#?i;e$K+3P~V+jc4So-H-A*cvylfF>r z^UG>%1clta-gr;8)_70u>a1$^I3{bi_;#OO2I@EFM{3hEmC_xE(-}<;r$pvQ(tJq0 zMJW27W3_ypnzyIW^JQASe>KuXo-p&=EXwPkO_OW94VgB0d+(TX{`rAFoy|56evFZ< zd8}cXgtQS@bby*$hQ27bS!^%IXY*ls4kTmY1fX8g1(- ztS^i@aX8+O3|$w@B0;&TZGIYV8BWuAUiP z%H8=r@ip^ATFGO|f^i~nx8C31Hxz`bHW<3uuI?P{TnBeM(ek~XXinlG;OPT^4|U66 z(hWi%o-xV3oX15?5ig;xLss>Vo&M(@cZGWAoBTd8j5HhV$1QcO8;Ef%rZWs3hoNsC zDPFhR)ob^8eu>;9%`mUW3a-=IOaEj$)w;38Y026Qk!~@ZWcy?zYqEW21G8PWJ8o@w zB9E@NOsDge9!D$jb)Gz#GixzHUa@JgX|Oj6meuN(_JXPufI_7T2*u@#Rt;71Sak$~ zu&eHGzMs*V4ADeQdA=;sTB)wC+hVp}YdE#5(w(fO)tpS(3Rd3@59UU-QU|$020Fb6 zAqOsHE#_XxFoA24gi5guZbx&EfzKUn5KhrS2L*}bTWmHFtTv6WTxvkr0G+#-RLUSp zvpty5xN~_|bPg6BJUHaQEybm3g$SH0I_U(fR~iEhg0*WrKIq!_h+?Sy0!w9}C*OSr zXXjH7z0^6RY1C~-dnI6lwsl8aGbUqDgPw(PhwHfGTopxMJRK-sTQi|zW;?Gdk<>1+ zG$L4F7*a1;H?Oy#Vx`Sk#*!!=!;FO1A#xV1%AYa zhBAYn2*EZRMY#!P?BYvVl-L>Q0%bh^z_8M)tMdRjq|NDMw--}AG#j13$~3M%QgiK2jftpKzxTXuB^(Rc& zGfiqGjRz@>M~rw%44L}{w9J$~`mdY;w*RgIa4<5m{lBy9|BL$|W^d;rV(M({Wa;2y z@ARKg&d}DBURdzohg!zc$kfTu#nRqR*51w@>R*4r+?jwC>OVjJN06Xp;b0`7WoBR? zU}s?>;N)P`rI&Ouw6Qc6v@^FcC18M}7j!l@wR0ihU}l1%|97i@gMSmC=!Fd(Bup*M zEnNP|`zKU(F|}1A_;>X0>SC5Qrc4C?F&dQqC&z)~e{&p|**X6U!Qp>Y8gvsT?E)DP zL|?swQ_qQjGS!I*i-JJHMe(Vw!4a5?B!&ru(Y}0mnVXV3BwTEEF>{9w(U;qqz7Kjg z6j$rQYS^oTJ-W1E51?9i+?B#xRwr8u?0H!dPdhg}v(YiA7Na#!-tBK}^scB}M_s!H z0#AF_&}{7Oj9wL&>~wca&!NE>A|bkX&}m(2W|f(F*@53wnrpS(w&*W+lD|NARw085 znoUr7TNvb{!8X&d-8B)8h^QyGfERzbp^fR8NyBAPy+ps=|=Naga(0orXTi zkJ2ezl|sA{375Iz7xewUSi9BPh#y7zLmeC6TVTOO@1MGNk7Gp#(yZE z4S&lVs@C|2J5fHx-+s%cbD6GkMV`Vot@oV|y1t{r=iIEf!{!tgZ1z;1z3?R044qB?^X2~wMN21V7hwxSr+*ofG5qgi=6~2! zw={9FaMosH_*dKcKYpS9Z|Cbm{V%7D;6JBP35s6DUe(U>Ut|gX1NZ-gJ2S)ojyvQ3 zpVGs`!Nkh;Kk|Rv9hqsms*3Oa`q_2ct$Mn(xV){p>|?*$ouGA5(<8rLeItehfxT&< zo#HW^ODiqZK&vSfi*cA{kTc@gM+m}$F$gXPV`hW#x4$270OK&$(7*xXA&7(r21Et| zt~RP(`th^cN6MW*+ z@3B1;&*P!8hpx+E>9YF&)l%XQ{L2Q~d6I_|5wv-mCYb{;6{6Ad`7enZ z?9UMTogI2(e$+h?xBmL ztXI4*6j6@Wl?uEv5@4a_-_@+RN(kZ=licG5irfmq?)RJawYQ_!sBPT{-}BY)ET?85 zWmEGE#p`Ka@{Y!{#HKMoS&UU|CL94E)hg1J`jU(=CdV@GC&1r1?+*!5uZX?>>Zqen zi3CL(vg%F~7Cc`#Fl*0zxr|gaDfjpbnN6;pCp6bGjuff0NdVO!3Tn4zo}1~=pG-DJ zE}IalySH@6PJdYj^JUhsCD!4w7fu_s1X!rMd(>xvb@6ZJy)tkOcrSK)_*YJQZW-Oh z4jT1XvL{-np+&9(I&)|khRDE?9GBRwR=RUW*3zY%HVfmLv5ta7wnyE&`Ns^Cz{Ck^ zKmM$>MxKirV-XIA9OG&cB@gnTw!Co zESfe448^sMSvG%_aau+SR;wg{nFg*(npG=`9p?VgG)g{A6{RYgC#)}5pxxafcL= z4tK>QSS<+*--+jsWU+EQER%4x!hYP!81toi&604jOomVh*O^RmM?sRR1ki$~H}@%2 zgo4Qo4-%S`Wm^79GGjX?%FIXpYTLMLWKe-SC5S`^YsV~EVfTimOQ}jAf4T-^>%Obn z(cI!@izz#0Vy?;Y=h?dnbTYNhgew)V$jI0!U=(ucf*WTcJyv1t507NY+xJXYtXw13 z@`oP8(>PBrxol<+{#OBH$D83Xv^tNO>>EbM@!93v45Iaw?||jya`niS#^;Ga%*$Fi zK65`_e!AHt*Y=*4qgeY46W`C}81Yev#RLKmmS_S^x;D>bK7fQ-QJfRC1K8A0Q1$<(iR6L@kzk7Xy zULDP|Whsk{=)||%R(u~UrJ8vL*)44=ze*m>37Vd36j5CzPA|XvQSM0Fz$Q3zU9#{a zB;--_Bgz^6eD3mW^V;?$VG;Ox&IgrCPStkbc{(5|nKJ~x1Gt=V#5=_#L|A9!6{5DV zQ|~}2DO683M9w(uF;|J5m7R@k$ufr=>v4-ZwZucQ;I;ZW&_5sP4!Qj*FDBzLJlzPV zZufbv0)%+w%vP5bRZM3*-cm#?%N9kogcF7lwuM=4$-n%tIM~8hk>q;_rQaC$Ue7tg zP*h-YJ`<-O;~nQM9M1`Gp3?N-AZwJLP8QD%6ffgn6Bz>V6HJ+4+kw%JC4WlS&9bVF)tHLGo&j82?#-UDBVyDXJ_0YuFedLia zo3uEl^rB4G2o0Zw=F*(F@)J5#9asNX3n54*NgS&hN*F%zj1iHxwR0#dw<2JzHcdGy z7^|AWWPCvK&ISNMxTe`J)W_bnq!Xqlml@hXpnFFpfdWmOhAFLA(suw3?n)5}qgrEZ zITV3&4D&gM?dJkf6%@q;2;E#-NO_c~E%eHeka!+8^4`(l&e?hF86nA^*byX_=}qbO>MdLn-7D zQI;qf|9DVoeBtpHrqnRR0Cm8fwD~ezR-pH^L)GT4Ci4vh=dxL9Kzr!1)_5Kbvp(b` zqGPcvtP=}W0r;7b`kG9};(Ee9&y$(mFW7Sa&%1Py$YYwk+ILH_AxcdSf{tkqP>?h6 zYa|A*1wDP3fMRbwyeMN>$5Wfs0~(MaLj>v}@I|TRZ3s@f7XbMCjL2HyK5z@x5BCPA zQOdddNYY5doK<#c`?A+Fa~eXG$hjFeDHZY(q@=ugFib94j!j&@(;_!#`xj?SIC zvty@i0~ZnU^+J;o!d)fO(cyZKS^#~6U(?DIg2swtu+1p!anr=@8kXwmj;N1qe;UE+n;i)-pM zj}@%`VwstmQK6*3IkDQbl*iZQkOLr%l$_vLoq%f``ojS;!UJHaA(KkutT;tNDLM<$ zVW#$4LC_dB;28A5`^QgPTCicW(x(s_$a;&YdP~gg?Wk?Yf2vpc5ax74Z-uU)>-TL=dJOCWSRP%mVcNsL+DhCChn8&%SruD2349|1FXV5Q+-#^SNSEcaQ z252=?@p^$;z3n2B`~si`ykFg7SIvucLr;)g;`>16dgrUI(_{NR`C9iP=KdylqUriR z!G3BS*g6443+hvRuk#mXC=tSbU@O+5N|)C7nMOSKdV^c@?!{u??V%S#mD;qcl5dh{ z4j~Zc*^QSMWZN|2&RiGTQz*Ew&iC~Jal*Gu7D+5DK}cP1g0%=IaHC4WaFwA+O9O0c z0CZ)-xC6Xtmxc$F%-3-+?YU!Wh5#M2tU0vO53vG}>d0DGB_gGB>JKrg>+COR=9F1?QZb?vUYAx&XbRYt z06Z5?`nO>A;e@yTNY5VpY=1QXbf?5m=r`#H)WE-YE>{#-iJ75!ogEza)ph-!Cf(9& zBB$~68nm-%wpraD{G=#i4BX?w*uwWO5Y5ya1NxMWH=@~_ji{%%B-6YD33kwmd&qY_ z)YB+GF|&&v?n`p6SV$peEjh`03N%T3St)f%)>5GY_`a0uvLQDTQdEU=SB9PRl4rBtOj>_Pt$>`m3$4hqDQQxYK<@KJK}JnlwD*Db zwOMmIy9t1`KgX~R!4t)cWKz3I^bp$9oOh$%P}WIXAb8}nMSa4{tOQiOTpKpymQnlp z4sj`ChP{MqJToR>dmI;FN}g{ZLrl6Pl3Jv(k&QkiC!5smn=G!^AC!?Z`W~veXmsc|~!#T44+BMd*Ayi7jFvsTe4<;vVlm|0T{b8ecW~*UG1~}d2&Br4CdwdJ^5lexE zWs)A=O7y}>tKiyj1glI;%?=5|c2Y@V!khy_ z9LOih_#BmR0VY(g5C~U z?i2HKTCv9!97TWBI|JISF!xrT0&UV)Ksp;$b85KDgK_#Rm{4W*Nv4x2Id6wp;z zTQPO^tXGEjh%tlfn^@%8w8c2iyBQ^fXNMd^rZ|+l6b^Z}$dphi1I!(<0HMOlmei3) zO<3#za!}T19R8gUL1Z4IrQ7u(rjXdwDhrjkrx3J=h{|9uCYu*&rW#?GS|Ta-z^zjD zXH9nkd@dO3`v5)ZlH-&+=6Y$CiEZR1~TiF@6VbVAb*zX|I z86z>Mi6-jPhG6v9CCv~O+MMe#%U;_uaD$Nhj&tl5>V46^;bpVHP##mG`EvT2B_@7d z-K~YE5+aTvXlAf3K*ou9c_j%s$^kA*0}~Ns^PsDVqqvA9R*EZZfT9{}Y;qpOd=-n7 zp)yfWXdscYG%2z>(^zGe7%=$`R9J9PZMQ{TQO=1xbTE~pRUmtI{|DC>1-vA|5`d$? z`XP@>R`K0xj@&ORf$wh~5)yimKj&0fvTPAZ)D20l=$Gix=qQe@jlv9CTxK{7{f!C6 zn13==thc<_c^>g({tnrRAQzc(Nu0N00MUflT!~#gN`XwVK$4y#L7c0ksgCw?v>T>W zNJ^+VUWiI<1FJQWFzpym_zB|f#pGFGmkE_o3nXeu#1Yw8GmwKR@Q1so8H$G`3`&6e zRW}D@7lM@6nB8QbLmZxT@o&I8QTtfTD8qQXQ&DW^gL^}fP|-0Fic;%Mn${(7>~m~t z0&U9j#93+&vl1(W(g6xxye|Fh&88RlRA9Z&^4A~9@zxizcK$fRM@lbGK%)qe`)udzs5Xx~PwGt;rJ1VRx8Va!u z8sGg&VqWi0V)NU=mjeW;qK*I|`O9lG6xsFt#IMX>wOjp@`~mx%yM$jMU*4;ft0q5KGfD6p&sYBX z#Ob5CTf?v4tI(^lTdiNCpMSduf3bQ#`h@&Z`K0~Ce&Kd@yL7v-Tl-u8Blr#Yeb_gF zzt~&!Tm2LJ;r4WU$-m-P@vG#A^`d45)~$!FTfT_7v}?)=u9VJqxMEYAk?x=pDRFc* zt%@ueCKnlbn@-Rte^v#YfOxwb(ucuef`wV&zz1}(4%(dqu}~cl4%x+hU|XLRk?A5h zN=1Ii1?hTL1M z=~5%#<?NK)8IRh`^L`&t_)(#W5 zWar(b`iV(Rc8yBzM*T*bXX!;B12KAxm`1YFlx$cjgFptr`#Bv)Py;I*)imkKQ#C2Zj07V=gFlh zQG7AR9jx)u1ZENWGdUcAIq~OoHEMXEO%SZLe0}T{Fk8LR7|4dL!euoKD`dDa`VNES zfi92A>8XfVeNxg3x5Fztm{Hz{uiq0@qQBFsR%WO^1=eZm3Wb@L)1qAeoF+l@KG>$soJb`DtZsC~IjL|MgB?aYhYkWJghN}Cq zKbQyO!%v0+Npo`|fd>cc7@6iGhl0Mmg6aO5qNb!=+MvtcG=EJFX%sX&gmVLKbNj6c zg(%i)zNF}rxx)oy6l%+vQ?+m+sIi=gFy$@EQ?!Yun@#}Db+%n{KM{=0SuD4aTeA5f zsrgedwRYk8NBv+My?yE9093+vx!vc~tnI@n0-Ks?=Vw*(Y;u?qdlKH{v1+3^7^~>&BdB%s6Woq*7 z8$SLIKXBEbj){;FdttUG7ap4#~TF7-NWzh0F`6(gB=tPU8zQpJi2G9 zO0ns(hwx2h$rLLWhO+a~nuu z-jUGkPhODFeJt+-$wnZ|plSV>S)IvV)Y3;nbf{P9mc(iG(2~!Flt&3M&UB=q%^T8)%TF z00?FAH4=<|3n2O!88GMwJr$rS`ans=vxjXP4`}CNBkL|{B#9=}9hGT>A$|ug9n&U{ zO=Fo$&E>-MT3=Bs3c1pPKy4{NQv!AB#Bp++uX= zlk-zuN~C0DR9SOkKwv*WGc{FBpO#u&0fi%(*iL;MRq__{>2vBWyOp^=tDe<v z@E5Jix8?jwzpCHCAJ%(-K)*nFwac9!Q8^cxPomumtryfEa^K#0f9V%%7h=yw1HgMq zRyS!%Pg&J|lg}y#*R8x21$SlZj)Wa$)eAQBLTb+}=B2$C&MMGKS2{@XM@l1thE)Hg zPyX_JBdOc|` zZy)%4aaNF<)D@t?FO|;$MX@7aSBtT#a_HB(C*jI2P0Opg{57^Jc)u+(Q&n2bjO8A)ICkq~gi;TAoH z4p^FYN8{B#jCqLXprM~>loWPCSYO~Da;-Vbu{s^iNaBYRsg+GZN)Zu9R5@Xeuy4?? z9h3bSKfr`5>=el8(cr! zW%u1783|bRz>B78c+r?pq7hvIXB;etKPy#udJfVdm5>(DG14P15ahV;4j>xuqJG$G zSttV=RY#^A_p??Rk{UnXROfk~kC zp+*~2SYW6C3ucY3Pb+vKiIP%&`fR~E@?D!yBLZcDoV$Ze0mpqn ziG~^&t&@u)B48MEX=V)@(aIGOcTk~Z;mZ4(k)8EaMzd@V?y5Umtb*3y<^=|I@w0~D zJ+jpwpMVEm=R%T&k^JkErg}`P(J`F&vneE&hMm=UCHn!5u=p#8A|(3R+|;3ey_G%F zy}I$KFfqi36P~pnE#cDt!{f5>nawRueM;msbDi4$WxsFs7v6q6%nT_*mI3&Ay6kIC zeNGO9R6B-seeY*`0aCaGQA?^*dBQBtJjL9#vm7ve%aymUK(coolKQPo{&4B~6if@6 z?CeEw`)#CIsw}qRR8*2bXx!X%Y*KJvGF~8nYZD&^ z(*X>b>6-ZrT6hKiL$d)?BTXg9kkiNUireKq8pNDsOIfT|5MsO~j!6hNZ*?{;yL;Q9 z(%16AkOTnBk$`Li1vKSH2=xylZ-I#!QnL2Mx+{q;e*#eddsj`1MTK!u$6|a4mGZIv z-*DAKFYW{L)y3r?>LiC_>U&F&hWW+j zHrkYf{;(g|n%z02)aQ_)TNfWiT&51-Nk;_o(J00xjeDx?bxny2u`MZ*J1LU|PU;{)@sYOaI2$nm{r=Nwp)9h>_lijN9b!oNL+ zMiONC>dDMnT;KSc)5rI_WbB(lmw#9;tx_ z!O$a}0yLFpJCXa7Pq;G}5PaEr-dJlM^y#QQlg{EdWV)=nS1PfN2tM&r=Hfz`W?`3L{4H zObxLNg64%d6HQoo$LTXnNhX*P$m(EE#eic$ju^Rsc`V(uVHAk1(I_U z_Mb9Abd6IgPn&7Jxi+3+#G++3^ok2baIorLjANuYOp9D24_d^KOuT!e3GB$7mFLKi zS`Y@|C2I3`hQKOFMOb1Swy1%KL1DqRW+Dn&f7^!xk}71%yeTtaT45EhMO;^zWi)M+ zH`s-IB($v^&DQdUKtqDaxzY62)Wg|`rtOs{lx$jg^4qG*z7Gjxp08pp^5KX!D{>?g zAQqw48S_F0?l)}g*W=HH#^s?Gr{ZMqQy`ZfSm+whSUjdUziHpfvWM82L{h8W>z&z4 zZm!EeOQQ_FFl2SIy94U3%GW>%;U)@W^JMKs$$jX6^&SpAyMf^Sa$XAXw7lHV$rIgw zjdWrtc)_XFw}Ug?2y+9^bUcwp_ITZ08k>uxZj92p6`7x=KfiAoI#5*ER@0a)DZr%J zXQSI*0ZBa2{dZF-LxNW9^fLg1sFWrph9P>$bPS($o^a+RB{TT*6)bp|F=VKG@$ z$H(UnN;}0WPn74ANfb3QNF-wjrWCm>nq~<=p1iYmcr!>e5V13wq~M~+w)aCwJ z3-J(oO@>)MYZnDg#6=J9%8%%&Jxl&`UxA{Ino4cxNfIL|$#Y*X{nvO!oF6BG&zg0x z$OT2R+{fi>ENzZx1jMuB_S)Cj^n-(qlyf+9r*_toj4fikIZ+&N9sYqyX`ahMdF40vVKMH zN`oKpU;gURH0;x7((a|q7<-R;-}WEvkK_-)Z?5k$--^}1AG$M!eRB?%TiVtRmD+x7_5>eH{SHJtr$Qkh&~eD6dy`?Dz0f zL8w@`yK#9rF}Me}qC>4#ID_uO-EAddi7zmNsl%y_BX0Wnh6hLMi-wKnZ=IlL1_sR_ z9B%1uoxf&C>^{pw*%9-GK@m5M{%S_4@ymbr2`N6={>VaP3&4~I@gZs;w#8E4d>MV& zgBOB$BZyD0jAoA3j1s|&Ac}{*(WIDnN_$ciycTX=4|f3RWUF06McPjpILi#;oVwJd ziISb}amkMgQuE=jpZvMX=6nfrDPJ88it$xWFm@^nXb?tEg@%6vj6`#;I~uxmZUk7O z(!D=84rZhMAd_~3TAiDY4ajvAq&3NmM{601n>-Q&7|5{|$c($Jav7vO>nvcf4CG8I z8{xbm4={`l7GwhlslubYx`i5cCNoQ~twO>|nu6)mVw_Sw#5j{qSro!AR@=e^uq~m2 zg#Or~OUYTAk-dn-x#*Qp*5ozmMOCNtb+krc(GBjBA`VX09M}d2;KmGx>e#%7++Lgg zAw(~WHAwc0fz(8PFtNR!uQi5LJbK1oHvLVU`M!PQ@dxpRh+LkB_x$Cf$@Lq76@7a3 zhT-JtN4=fA_DIbDxMS;wVj3;ntm2KVdQE@2<$T^Si2VUYA5lLXAr(9NK}u}`FIUN< zwmtmjg~{~s|MOv@sB=X19WG#GoOeK!8?J=BoV0jZ*qw-gmuxGl&+W-9+b9@=x(y3H z+|LkwFLV_u=;=_Gxq8N7-?~;!Cm+R|ZROGrxi<)!vS#EEIsMUosh(ajdqT(UGLF#a z#0@a#-kQh))7zavVu>Zx(7C?Jsm$mSE+F{q7AeSCHQ}q5i+}vh!=ILOFPZFVM>v%z zh-A~?VtS|3q=B5prgY#CEM*sHoa|t%_$Nw zXM(}QHL2X^F{{hD?qDBwtcw6rDCaW92o&`=?Xs!nu8>_7K{DC+sNm}o#@J_}kiM7> zCqCy~Lndn(Vv0ni-_GKrnz8ej(K+zmQp@x$^~C?uE;F74qX}cbc-EMT~e9E0LQFo5E*MBx*Ny{c%yd%P(<61+;;%~*rpt+)N@x%@f&nf-M; z9&u#Iz?OWTIGsT3R(>x4*OA^??CM!)aI;^cZ{&~J_v9s##hNZ z?V~yyM5WWh%|@iFsXb}!!ptRuUW;Kv_-3K`EQt4lmF~h8>Y}qKxie^`wP!W-5YJZC zLC^W~JraJaS|qyvrAEyH0S~`coVsS>TiD4}LF{twy$2)TpNq`J zgi#~80i&K6?UCWBTcc+KtOGU}tNAnu4d_F{%*x?$JXA7Y>8jkTEHYezEy2;dCdLiq z+bVIw6?f!A(M`{ok1FDI5pvO^4efck0d|O2LGxixM9X-o*v_*-W8fZ4x6N!qJ|~LA z!F3K1{huJfn8Wn69e3irnFJzp)zQ)X{>6Hm!tnT^qgV-HkrBvuuXWHZ+a$PxkMS^t zSi26bDD#GUFw9TMyVRhby%Ik#^`Q1#nN?ff9NN>y_}u7K6&~U;OnPmH4Okmr_;Y7x zmY$Lv?Syiqc6C2tIwO$nlW_zZB0}b#0ueyEHb^c4VkIhLlhzzswl0in_!2xNR~q1B zNsETsOxG2=tuW#T0Ac_65;-fDM6MY%^zq+m%x!AJbS5rRvvl57(f5(>+bt}T{zezI zY0PFC!ny-PqUbt}LhIl!#Bm^%C^Sd^{K(-(#MXsTT`^)MS!gFo5nUX>DK|Mb%9|Uj zbvK+;v44shGO_vjG}btUDXUPH%#}A${a9f-u+H)peYC?&qBI+~;@hIp^HZGuQpxz7pVrYZ5npZ#&tY41q>cfQ6S-QmqP| zBHjdXe$L<)yrEUCrGQ&y36`uZtSnOj8FKd0eblY&)UlEcZ8zd6uhh?dX-7Nf2cZjo zoF6$)iN$n%x*MuwV=gDW9~Wo#`T%$Hhey{n3qwl;lG`;^j?oLp0T=3vHhL19{Zi_5MQdLRCjm2%Z!>1mr;2j6+_0g7>*Tj2%r z`%p^>_gxawB@lrTdmqr`AEKa*&;#N1ERK%FTU;D%+Yu%2nzX+?O-i9GL}wTmjfCrb zjnuk~;e5N#v;t!mhs+fo$Z$(<~uw<*L0$_ zBG$Di+uSrHJH#v;!gRg2hn0<|P|dUX%uf9SEfH4_6eg-;K__4@)YOG3PLC!{p4i@* zYp9s%Zhx{y&MGMR!zpPr6;9h4pO(C<0?|c@kx>;!IDLfED`{~Z)hO|aUjXDi$0gG> z#usFq8k*~yPUxV_lN5LHd(@itP(f#}YE%-o)uV%kCk0w} zP(?=WZ^=)lKP`0gdD=b|xGDN69}rd3J3VfjE0LP!sQC0Yg`lzx^W=@l+;9~)jiU#S zzw3@P_kYWG|3=r<%A-j|g(j>n(|xpTBZ>6U$f&(S!biN04og(OH~}3zrtUc7tahS? z;#xzfF36y*RV4cE#G_#QkoJ+j=+Jy-94;PD_*-ioIebjN%eT-5$uuWQS{YyMr)>cm_L3ljvvAgK4|V;(%KnP-OJ1| zQR(cN)IG#}`>1AjDMzMA!kFMXb9lJr)Jgd3;Yibk0 z_PM?0SPmBbU9I~}W!{FAMT^bf5?Sw+Q}itI=C z1U`LldG3^-x>f1DPR{IySNgLndTDAVsK++7|b`fi^w zb45h+jZf4CovMmpUdorY@BG7s(kI(4riY4kRx*1_$jVG9IdGr_lojQLa;P;<7#9k`p7yzp z9%)5`%DtqVs2TvQS9U4K6rp$ZcwyYObIxeBiejR9Fn7POS!o_mP3XU+KPoR(fp*=l zVOBmWVr=$8Gd}xO>R->&_bb^_rSNG|RPx-Fm*-#!Uxe1s(!PlFKk{mH`61j<)bkq6 zhna_9c|rZ#=>Q6m+uZx}mOWk;>x_vxsYhQAv@1;;5v;kXxj@zEUp2q0z7bwGb&;;{ zxNzJ{?lZ;RkVQl{Gc^JiqjJr{=N!d{0nP`z#13EV_tBV{y@@oYqfI%AO$_$#?dU&l zB6^K63{u8?RJoGzy=81hMU-b}*tMX&dX2l{w#Ufr>uE+9!nE21h9Tj7b&AFX*7v?n zH47Hmhv7br)7%xSZRie(;aHJ=O0f-%=N9kc-kV?Rbu$1k1_IS_Z|^#Hi}(C7m5`*` z{r+wlOY|KVZqzR1BHxC0H@ML@hRb1B#TQP^Kz&kn;GVG^ezZHvbGC(d_o5|E!q5&hP%s^v9gtRnyA7W&C>P=43LspgoI+I=~!UWYs7=N>EXQe+rB<7>cX2!B(|Ywx*p ziubm&NkDI%dGvkf$(9m>ucx(c4eh5bNVE2wl|IoV&dhSlrB40UmpV;5r-S;=`F(N7 zI+gh7-1=8nt?0AhqW7l5g-*9xBmJ;9UY>4v(uFSgB2|0tILjqQtr&$w-zWWx0tF%Y zf{)?yC#JLp(ZIvWjF@*H&Q8GTpE`6}_*a zOJe{bw*JxZ=}FLA`a!mfW8ku9uamn2wJn5FdBqLerLT1->3m_hf21jk(Y>zwD1FvF zZX`mj_-Gh=z@Eydc}Aiq-I^Kd?3l(Sv4;h2@-Q-m>BFmb7Ddn~a3=W1iIgu3OxGp7 z2IUo>@ULvxl}7U>X)$RDCxcWG&8=ar1Uu8{8AlC!wC4felwOs>1R|=i_pxGU#6vmX zYquEHCBJQFxcuxaWuTYzPF_jXC$;E zeBNbNheur1cs1{BSqaZyMFch{7X+XDB6Oq6i`v91-pmpb%D3x8|1y_h2QQx~@9yr$ z)EEwYQvyh*se=i9qgSMw`OxyP#3Nu&*FI~`hzNu1{5#@y)&fx>CV9<3fShG$H|%JF6IG5<++eNp;E$*s0vSJOn> zsa&`AJ#BkB4LVvoUQX~#^geLN@3ko{FX=3KxpHM0v?#FH^d;uQ%MT81y^j;~5+7c` zh9#-!wz^%Fr8UBj$3zc^k`DrLa)N(1(& zWdd8WNrl&-ITy6pP+L%2q&}iIRp-5m#sP=?1PuNY>o$ggXCkZ>zF?lWmk50pEv@F% zduu7CTL`AxaK6z?f^IVE^*lP9f;VH(6-j~UW&(!@`U#898NvQK*5uu3g&JQBTurud zuxdnnvFn?Nm`N`H&D@n8&icC5KCk@f3%M z;KP&{7n93vA_+S~t_@q}?Z78`5HyMm*}mdzP>it4H-q0*^VW7wgK8uNhA1zOneR@& zf76fM=2ha`EMC5bk+|)RqNa3ZPYu1UcNiRc!)zqm&3k)_>jJ+>(~bUj0jUXGexbS) z4v6U!dW5F4we=GBp8XXu({|~+aJu4-euOIh$Sal|_!V1T>hXuUR;w@MiJT=oL}!-RLKA5IaeNfCtq5V(Gj*zzI+#gyWBl5yH{j zWDmttpJ`ucuzd6OZug}a9}@x9kQ$$(MK-46zU9D~RF~GQazjEZ-TQMByX{A6{3^2Y zS?=e&Otw7B6F(<$FhV&v>rH^H&Heq=TrRR#F;Yrh2S(nz zs&mItVINp(BC;II++`T^Dyz30=cQ2|F@*`*hY}*_6TiBazvHUcPV;=QP2Ry^57+0y zGKx=k{{}`NKsg#(z6vhGesB|eB=6FSgaxv$qx+4qPE0!{oxm&(; zMtMf}p! zNgpxxyixGEqDVz$wUjSaot{BDT633U(O!k?u#;DBw>G`e24bhi_Xsfp^%ZY1yk*GE zrjr#dXyyyth1%&z_VnUB$Ki_<-|h1$_!b%>!UOE08fRXRnDra9ty0&I z>Nid6(4b(B>ZyP4ep3HMHoOPh>GN!#MuGVCN4}4pY>jS!J5+1@4+YvZu%vqOxMW~o zRJ#qOOL_!{8j6TXG>1l-v5Yx$Zbt z8sj6(9=2^zK5*}m1hBxR7Np!xof5P+tWC(pKd9{l0QX6tGr@xS$Sw;(QE~g~N$Wjz z3$vm9a)-tU&%nM_ueicP32{HG$&b<$-|F}pcnQj+b5|ZrPg5z-x z&QDZ}G}*4U9Cm==;O&VIEi2KB^3U*;Q5^_8yHHc_l_}I-4oqmhGS!TtRm^fjVvVEg zxI#M*CDm}I+{I>;@A+9aa0f!nE2PJhCd7pL!jp5NLZVon@*~Gm^}>w9GiPtR*rF-~ z3ZNc-oKaWIzO za}|iDLBXdg&{g*ZSOkyza!7=ES9b6ZZ8Hvv?vdT6m+^syGtzZd{!U_jM6Crf1+@@D zhmQSgyszgkrI&z(xa%&*QdjWFu_l$ZULw@IjqX*a_|GZ@cSr6NK=C%S)_2}K8ySv= zP>Z@9tQODfzJqQM&)Ie(J?-QhQ2;?JMu;kJ=xr8g{#aljP&qU8^7&ECczx@qDV$W! zc3s=TnZ|ZdQsn4y>sr&4^Y|}1Js;@j=kTj!DaoF3rVXQS*-@aI`2np?9e7hk>94NC zR-)w!3DMU39u@8P_Yk9uHz~L}f2-{rZt?|X(rNpPubWl6N{3oJ>Saf8H_T9xw=|Xo z<`@~~{1sZe-0W!EmGX~ypg#KbSNd(&FN^I>z`yhQNP4zw0(cbW+$vO4>Wl-Da=d%u<1PEL`8 z_%IL3fc+;iml^YR@7j6md4|9UrBR~&5WSg1!1?nLc1#&(IP*Njh8$ULD9XRDG35d7 zFXkVAz^$(pTC8FYsT*izjV!-%M2;ydsWcT%yGK=6gQt>KLXG?5yA#jnGu?IlO?A%P z;!{QPLk98mL9b_TCW9VJ?0QcPEHP!Dc*U3No3MN5ome`Z!|5)haQFOHyqh3Yh%czU054aN#J@DmrmZ`AT5)CRbT0(u1n5*fiEcCas7JOuV z?K974|9RWXH#=m{oL6iMmCd3XTPl-k*sZ-%b>LWjpnZywNP5841aPw=B~ot&bu-IMV51SJ<(4)UV}_?U-D8eDfmaaURY4X**8+mRD%`S}|U} z_ajaAmzWOw*7c}X%EtyiG_up_F~GmkX56df6Y-LbZ&b)HRSREzU_pl7m#+~GsSgZ$ zp2?=~&-q2r^D1B$w1fTODbocii$kZNuI02>imL|CMEJjWz=yt<;+9k3v5+`w(xiR2 zS2t1T!o8KO1?SI(o@u}-*Vz*Jbce=WZIb9LpJ}S31ha`dr23F+kA#F4bAZCWcUu~%lE!m^5MSIyv$ExT3b-V_3QNC_4++DZb^ldTJ zp<*dWq*b^>1}I%|F%>H~#B#9J8xy@Wf8}A&!OE8r_lulF?FvW+Qg25sW%w6urLGK;y_;>viC-zO&1aN~nFA7n6G6-|6-*-`!h=e(p$+u7NL}2Nk=t6|YxvHr%o8T&q2s3HdtfvIl=!r#6CcP% z7b6-{M47dZgZYq>Sq1A?c6#n=-Pa0FMMt=YV$Eu(Dnu$HI7i-8SIkK7JA0&=_q|D( zEvhaz^RdT`i*_Dms#ArKI}S(MUa;0iorwR-u{EN{rTq4RSJ3;(c1OIq8+jZBS+rA+ zEQMcSPZl#}W_}=0&RVm`@7Y`UN%_e#1LL6{@586EJ9w8PC@4}6_7zXI!eRz)P3;TV zLubG%H|8$UAQ?A3KC10F1Intq2`K#PTN?5FaZ5>p)$FR(z2t)@=2Lp7)#>t;<(9KX zi}<6M*_K)6cEA4U156Rg>7N{^;62aOvB&t0R3nuw%^jw3d(lt*U3u2+3CN=f3AYAK z`_5;we}%AwA88(GDD%wXvO&nV>6wlOL?%-ozFU*ZG^#x+sTFl8XxlxNCa6lut&wau zQLWGkM`sT?{n`w@OL^H}27FEnFW!|~5Hjk_wl5pJ=jeOfD04QG=|$+P)(GfvwCnge zg%;q)*j@SMwJLc^O)p>X^MOmN*`{c?rix| z|8%@!?0V*n3v3j3Lqmo6x?kzRGQ;WF7~*}cavf_BL5Q=Lvmetfs1tf&vVv`HoY_mV z6x$UuoX4soWpj<51jLG*i7S08eLCUoN=W;@q_Xgi%QCe~AIdGSNXMtCyc3V?=)xX< zEXPuu`sxD>cM9h@6@`55m{%x`OwTHSy^E}jzf%s);ny563x>xNaT3m`r=~kU_a35) zsyR8@Da9R>X7HgY{=mW1%lgxc;-w!(ZB5!sKS;j~{mPM98@BQ~Q6h1 zAqUmc6t}FECq2T;tF!RpgvHR=my3}f;xSC2eCW?Si$2c$Mbqg_2aC@c#d!7O#)=1@ zKC~HZ)`*c4PgKqHOk<3+YP@S4sy%&jPH{@n&EC_>Gi$PAU#(nszS0c?msY+x{)wCd z$7T0tm0@hf2)9-`)_K)#*ny_SmC24->SF%M9LdIWg+;DvGY=*^1{MsCvPl%ryBkdP zy0+5Ux4+MExvyJ8EGe(r-Ei6{_3P8g4uM+PBHOTYyDSRD!YF$kUome((9$H9hlfDNOR-Q|3&?!zBRt|EG2 zs{;oOzN}J)-~0Sxs~6LNzQuF^!4YuS-(v888JtCNJtBtESyMY(3m0bs7XLjm3v_d2 z7C0OMkVHUH05Av&0)Sv(@Xunitc73Mh|K~>lm zFN(rxHO@M!wgpG_UFRDc9%%F{G`hEZnKmz-E5r7H>CW>Bh3ALaaz{HJgHxkS3Z8#5 z_*il>NhM?k8U$putl5FgQ3V`}ZqBp0apPk2D*kJ2Bh$OcBApunjS`(PE`9s(ae`CV zN|dA`y)iwiQs6i4RR{rp?K%POpaYI02r1w>&p0wvqPKgI?lgyl??g6Ca zO7@y$Lzh;9VH~HY&r!*G`+nKVWT5|B(3CArWXp&tAkap{6a)kTK)~Su1Ox?uq7VQ$ z47QfTP%r=jM2heO1t7r0gg^k0(5)hxteNmXBATpO#6N8W_FtO00^ZuxPE-FUwhaYH zLWsJ6fj}St1O&o=YTH2AX4@upcav=cNoO}~d$X4C{|)$tvMM-YzpZgrM5s6goU`@! z84L+oFAG`EiHYue zoqtQ;uS05MBf;QwARq{~b}>>yfWd2N?U*$D#0sS2wTh&mHt=r>5*1i0OH^R3-nVia z^}c;ZK_MG$ljPr(ASEaqwWa{6GaHH$ud!L-jn5<3Kblq3JHvjJeO9U$=Udcbu( z*4q4**1*9~>t%@mfOMq!>_7bf*Yn?pgA6wewV@S)Xju@%x1J#(8!{LSOb0;`%R!NU z4>PGP1o0dM@edgU4%;CAJrRl0ZP5K8fq=ljhhcNtJOm{B`99=pL-<1?Df@kBH>XWQ z`cwNO+3@D!{Lv2oEBhsaA(;)C{Cm4VAl()-(pmtqO<(LyvkwLYSesXMr7=W{Lal!y z(IuO*HibrlM?#6~zXA^DKyWsqTdM?E>nLEYJYX&I6krVk(O%Xw(0T@5 z&mimBdQ}i?JzH-AwB812y%Eq_D|F!XR>146{Fm>|Q~&nfJZU8&XJY@H0pK475DeXd zhBF-*0lL2t05&loDWYL*;cNuh&`Zg|g$NR~dBBnX!#e^=jf0U8DN@`mDd_2~H2Jm{ z0{#0-0GpUHbR0jq4C8m0QLu|pwr;T(__6B?@lzwbq>TeTg=A6Ow!t7Kk<|$018CG>7&3$f(HYE!#6g45ES$$o4!qiC7rE3e~(2Yw&MLKJB%RMFYtzdi28lw z4MRymema%7xcnZU5xSMn2>p|NM$j)fjbPOG2^#{2{$%@Sfwy-3P0q|#{vzy8_7_3F z;4p&WKq=%m%3vhwKQNXgCg4RxM+y6rJxcH|c$6Tb2mgJW z4T3;_vdtzVPipYDK;O!#g#EbLQZgl&+BsOQKc@KhxQ0$z+YyUXFelw$Fj{{OA$`K| zBmn1PXSe33`lkj5EA|)0|XchCYQmGFfthmN>+~Oe~^`fz>z=pmuNe1 zI5~SV1`rgC>>6+c3PC<@Fw~C>pa=xnct8a}85B%rS6~E+tQ^rK$;v@sWaEK=pnzod zOuPhHdr%l`Q$GlJQ)@dco-|kLS$krM(~z{Tg9C9n`u1Z3b)2OG00#edpbRhqppl9o zs4@lv!YBY0APNd97z9uWM(j2M3V{%fhq$c$dlAwDaAgN`rL$ObTW1%0fHXt}jKE-s zCW-+o!cdCJ5Jd%;k`e@{K%7+JNEPJzLT`#EtWB51UJ$?2bi%?a+8E+rK^6W18B!8UZ$@{A7>d zw-p1F$r6ca7y6kSa9c-1RaY4km#QUYN?rY6WnWy?x4O@cV5a`Z#eKCRum?iFf8Z2G zJW8ZR(~hLZn3?rKX1H+*xY|6Fw8|Ve=~%gp;xN`sZM^hbt?J{#9h~($$$nPkwP^nz zoc_2HBGj%DuxU-lrawY3g)4j5vS>JSO#yv@&$?EoEDV-qV1|zniqbS8^>?iB}_F?JuK%;ttm!nxG60_1S)xQb}nt zpdeH3kOy(iC!F&p>s?AUCjf(Kz-knMHW!pg6daeVS?D!c(Q^21d1+##tNIRZ`-t`- z0M`TW8rATUz)m?UbvFNw%-Rn;r71%wX++d(c+9gT^x*D3z;V*)wIBWz@j(XBjZrul z8T@AQTRwS(bq3&y#4-jC3S$Z?mg@PfRit41>`L=pYy3}z<}FYdTwxp*6q;=Mi9UWC9oahRADwHo2bZ@UqH*sd{f2IZ zsf(0~W52m^ZvH@f{|K!t2M15`T+u%Z2?4PJ4FU1jx`6x=oB=?frJcF+FMX(cudlW) zi|*Op-Z|J3_BC5|5cg;GP^c(mIS6bkP1p}&PftmPf4E=dfSe2XiqEc;aJ!qawfi48P!Epg zDzs21h5E7r`@6R-KnsjVy%dRciRjxEDi_r&_xI_Bm!HI-UK<^jo$LbST}886-ET|X z-?uT_>MpE0;ZgGqT@DzTWp)aWRUc}WD~^q#);vOY?lC$~Yz=O0NeFIq5sbp2D@Tf0 z_jY4X2l}y*!%=^0PC(ean7~j7afSuse`h|x>YZ8Hj-tRN4I=k}g;}f`RAjwJjStj2 z#yeB2tWwXT0#s;mH|Y&{$F^WrIJv6|jM%$K<*ARB7dK5Q2AH4*;Dy%hXIGZ3b(9mb z!6x0$m+nzpMX2N-g1WM}HcZ)Yf)5N?a$u8x_$V6h;KWeN^ zu23kUuRH{II#&FC37%o7t&W=j5(3n(Wpp zo(gbXgS}}dI~5k7oWN%+!X_;eT$p*)azBhvu(}StpBXZ_X1b7PTU4MtcAPAYZy&4a z=2Dr5i?)IP*=zSe{CH&wlEg+fV*XPppU)u( zK_T0Ta>2T&dO-{8Q4@ts$}Cx!NIjLL*my`-mj#UF&U~tTA=oKf6nf~mE9q99fTDI` z2I}~8{iNo2w>6B)aC&V(ZKgd^HdDt(Jnd7(I?|8VV(cSpG?D2=@1pbhhQFrEQl|SJ zCSi`!PDF*|#?ZW4KL=+)cONF7xNYq}uxIL9&1X!-jU}y=UW$#frJ<;a!A~dHxh{?s zDHY;}AGS8QPwULp@*R47ox0RFowC|&vvQ+VcCB0jiZN+il@cIQC*Id7xr`(?f@Rdw zi-Zx5l<95V@4sO1by~87q^V&+Xzt)wa-i?R(+CUNL_J-fsFh8-Qk}p1T&>Hk66>VH z=EE@{%lS}{1>_{p9vzjpmQR&Em%H*m+n4OZ*GqCpd-tJ|XIoD6m8B$cPijFO_hBhB z6K8Bc^=(0}j(IUfbC%O&UVF;v+&%>QPMbT6U>}*$HEyMFZc4Y z4B(cdbekO&f);0nB2B|rlbv6Dw*?f z@9lB!CEvS9LM~_`VQLAdT~4jW!{8&=S7A-fgGiTTf5f|h8{W%=eYN-Yz7*K-4UTql zG0N6lS+Di(=RGrJmRR`xsbg#EOKZq(tKD8n;jocG^| z4#W!d#8f0V?}YADEtkn~&+eUkYjn9eW2V*Zb{xzv2}-82^~GDABh|s!Y0~$_dxuCwfq~eA2JPBabDfm(7iN5`mG;;Bk7rmnrzn;m@a=u81^wHQmC+WV7y=nae60w3oV z8#L92BUxl=5@_;5o6Zd5RBU%zaYNZ}KzH*EEf934wg1 z5wC=j5vG6>-ToqoIakY#be-|3%e$|a$!yJRKoh|_7?;@s|Cni^h4;Fx#j%k5gG`~Z zwWU8CV_^!7?yDwfL&OLrQ=?C7f@aorhFK+9-=yyrb!!>kT+qYF9)K(F6~$f~5(FGE zu-Vzor2yh9Bh~mJ&7QdS4nc8@MSYGi*Aay#MPAhl1zutBL(D2%2IOt1*>s8YI*{V% zx2?@D-WZEW@qyx=maeNP~{p zru*BxIfLl_V0EdCC;9o;xBJM#jrvr#8$DDaN4SJX6oVRX%@V};?KLn9>4(nV$W^}A zLEsb&ph}}fhd$glA%LHgM5qSq2~#RW1JYtXm3^&olK^%5Ic*6Jg5R}#VQxE|A&+ayTs=gF#4(j}CB zEp!5WVzrs2lN0`e$|J!#&c}M$UB=Ng=j+Q?rsz#UV!_QXdvHV$rBB@#w!wCncP6aw zs#_^*k%c%KQt6TD6Pelz%;M$$>-OLh!@A@j_p&BpWvHKU{*Q1dw)^$|Mh3})-jU}8r zHWu`BITYHy@N>z;+MUmoLa(u06j`MuHHjB?Ao`W^xpnFYWvPu?bXaV=R9*+{mT#uK zhFbz*&SmW zfAl({?4S`0+XiG`!3o0HM|PDC8WFb)E#8$#w;9j<1|>xXND&l(7(`O*W<=4k^lO_m z8o2gp)WoF_8$c5oEl9?wnYR6&1)^_cQ5&^9NJF~w8ACTzebbz7WJW5wA>(BpTDgVmor88u6jH&WHy<$C)bnlTg@dE(iSz`I5$QAGXlQ+9YJ3=#{Vg-9-b+suQ%~g13rlHTfG4d6(b=0z8*ik@Rmh5<@&5V0lVs90T2Qf>Gd*W1RslqOHa^V+cxhOJu z4o$uf|^#kF!*=v_MY86CY!-I6?LqM#-9)MsE^WfA# ze6FB}59r__bnqBDH>{($Cr@qX1m-?fES#U4a-f?-V3TE17G98lmg=LhFxrqX+L$nR zQ`Yw9QCjFXA``r5IbF;XIqWMr94kSc@1m{*%#bzBlgaq;n>W~-Hz=eUY_9X_xQ9#A z>NJ|K9d09CBsz0EHzmD$)Qai-d(X(Q-fIw)O(|Kn68h+ikfJ6f2;B3HzJ z^K#)yVwhCgP4l)wHY4>8qCjz;p&&bPVAaa~I?f9>gFj5>Xe=VaeB}%0Rt(%rpAAMibT-(=5Fk1Lp<_r`T1j!vLPRFs@7;HC!A!JY@C+-MO{)BWaEu zZMHrMN3lP;m%ye_c{5x)ugV9ZvNANRirg>H9C?RFWePrAZEf!#Zy07ylKB{M*!O+o zMC`s>?Re$*(}BS@%ivVAePC#%hkFR5zh@`Fj#N5uKWSa0tHoeq3%D_f7dh>bMeDxc zLJsCqI7BfcuoWpnqFp;}QF)z`;dV={#IIv)Qn5c39C+SBx*w^7Sf5wjNF&?7lrYn0mUW;T=MxxCdG^RXL9FVi?tiKF6e|l(qOLApeQEiu@ zcPw63@1^^W#pF0P#aLkwq zj+m@+<{QW}x-3BpqN}yDuU!iChl!#H3YCJ2) zZ%$^GleIvn-ee6Hf=a=Wlq%YbOq0*?8`#S^w`pX2vAISO04fEd+oT?~L-8t}5&X$d z*XYD5cc%$9msqDc)`RO(6kfJU#O{l&CRbska+!sSOfCk!G^Sf7>tR&wjJJarTSWHd z_9&p8HT>@tScEBKZ?p5`UaG*vzf|pmY`Qz1)v+a*AMn^Csc34}VX{R($l+;esA1Tj zoT6luE#Q=No1!o!@cgXYzYUP_eof0%_M~N(_zd^U#`dn9q-A(9p^@zVX=DF$X;1ES zo~0TU0^(P|f3dOEEkL%$c1D&q&Md%x-ZDGbnTIGVN}(VUBEBR+k&zZxefh0}fPjR6 zhkChl7x@|v0fFi!BQB!;G3_W_tAwhDP}t)#oHv4ZhtZhi$_WFDdYX+!9UGe77((LN z?o`vX+XdJ;uG+fJIZqVwD`>9uVO_-T8orfb1N`@m>QUq8KA(^Er#YRcSZ zG>tbv8m^c69?GK>lwBS*tTH!k#@gKdx@kFNM{QiRYq4$GN?6vL854Sb z*oYGHx|-1nV`;m2fi@gg4~@P&t=j8C6Z-UYyTkp%BIoU#9*64F%?6s{-6MFlMtBq! zSNp?S0A9v-8D5HZwF1R0o|i124ZR<)252%X-8W;TRK;ywvp7lIceAJxw2kX#G;_wO zok|4qz+S^?yfw+vj_v1z46p4P$-v$d5_l@Q>XViQ>5^2fcl#yW%Rn{L)VJ2NR5H)pn1km=R@ z^D~kn&ze6VK_}39gRp+tWwESbg|GVY>HcIV(_0V}Wd%nJ91p-Vn6D3R6k!g5qEnmI zHi>wVQ<|F4!)aF(|G6|}25?el3Dx}Je28U&3R9=;a#F70Mc%}&OI4n*mtRCKS=1*H z8A{Q~d|M*u4NYStl>L>@c5|Ytn_sHuecAz6%Q{)~qoVzZuQ+os-%h%eIPQE4FKF;N z^K3$jRR2Z7jA@?N3q;8QmQ(qykJpQ6f;T_0l=wULjl-CFSyVzRyN_YYz$+dn5!@?Z ztJdLC?b>d)HMI1viV8A{2Ox=VOKV26*9||e!72{69fB5Z8y-))&|V}nc6U%QW;Icr zP$rIKmusuSSUWB{#MH1vUsC${%gp5n*JAi<-ji<^^Tb*!o?v~>`z%FE=le9;81qsc5|> z54g*57@BwJ#*k7@8L2^6i4=Oe@i=M4gT7vJRwCqf&w>?PcI+d5cT|g2wfWMVWabZ0 z#6$#zS61pDe~XVjG2P-iHA#E9%`bk9(EF z=Q1L3sg~!b z`@a1yCPkq~V5iJxL6h27b%O#djJ5Wgb)?X!GsxtDrrqo?)dO`hBOiFBdQETn_>88JWBcC{&#_@*&ElA*7Y-p$wC4MI^FuDLD=l=@jGX zWN`GXe362*%zJ^a?SlFJv5JGD1IUJw}CowW4hwU)9+rb^;atJLnRyWT8VLGN0T9i?F6}iJ=L`xqubGT6R z(*5j7>@wZ9ljjN7(z6R}ClJp9$&jKr*z9P@g#4lkKS+}BzdijXyf;-g?uQ=DBd8nW zR|QWDr;v=51qG71-|jTl?qUvZxj$}E6LT~@6nUHWh6b||39bN*JXASm4bD~SDlX0l z*Y@sWOsFp;dxrjc(Ow91BFv$aX+sLWj~Phhh^VTZ*`Zl#;B~w8aE`3R>tMtg@-ikQ zeV2I^2wRsS5HtwFf7Pg8AOf*RCpu;!wjpb#vBUVnw#n2?w%Hz1c@6qB8(jyk%^AVywp8L57JC558 z?hanyW2!@M;Og3RXvgvc&_*7o))C6q#={9XEIK&BAGyj!b#vG0)M~%N@M5DOeIbs? zRI9nCD@f}!p|79Fry$|1h%S)&4A*5E`5g;ACa_V0uZ5LwEpb2jWeR$k97$SOZ-Qm` zMc+8WYE>o_zc)_@p#xqf7J$jma)RnFD-MtR`SH3 zG}<5SM z{P~eewn;BzDGXsS3GeHBo%GtD?=$M1hB@ThNZE&XgG~P=$&W`R?Us6ZG~E?vz9i-M z@<-87NeShA8#`hA*-L$kws*%D(hPgg>Va+UuG~yHQl=%3h!TP}mWW6t72GuFju zbGhC9TPj||w|)zO{JNhX!+s^YgTU;(OPRSMOyNz9{O(aVJ0*7zX*T3}+^kq(3pN z-mGeqjA;_!|5p*ZPP~(t{^RRO;uk||Bd_JA3Jhc6i-S7;9w9@$vXM6KC>{A}iM`m4 zk%|U|QpIo79LIGhWBuRp#~<9QIDVohxFh>Bc^T@m-(7hg?OVh3+-#37 zj?UsKhAGfL-nF?fr456QzuCH9-MnX~yF9!9A(O$+rV|ah@jq*sCKMko_~hA{y=+r- zQII3izOA%a&cz^@sReohPVNmoSWXxrzRg5?iNWs;-2fkEEPK#+1>2GJWORM9qq6N}Va-cm*1wk?iGH`NesB1t8yDOe64g?>};B zdRj`<6}6%EuZ-HE2ls*y7MgC!$nq7#F+a%JR;ZE1Rw z*M_#|X?5aE1o#3TLWNVqcOO;H7r-e3gKW})z)vZuG|(o}kI`fzf6xe%;3vT>tk+`F zhTBT1q^jir+U8XKc;AV{+o|jta*4T`lE7q<$(Pu+o;jMiR=ml}Kpcs( z7E8nz1EXIeG?PF+b*!CjnlO>!3RhL52aR%~xPbpD;-t-6H5#tKG=tI1SLxK9rt+V>7^#KB26I17Bw4Whc+3(l7?uhyUzeh>x}Lr!j3OG1K|^@y(e`E* zsq9Z1L#Av0vX0b(LbIR1jZH;U%xMEal7Ub`QNo$v$4g$qI-)WR>~-ZJ;(R%XtdsN?T-{rb=${ ze&+N>xg^JGWNqq^5mA3wbzz4(rwJX^ptMEZ9s^$sb5q_T-;eov2GSJbGb_KmO!{V5 zJ9=Kjga9UPWSg;?Y}$`07yL@!$UA0u9Ho(aYa`(iNFqcnuQops@{X<}m}#j1557jb zf!_#(E%Oz9twuDeS8WZde-q;?9DyJ|=nwQGYF3o%;Yhep;qAyeTJQVNI^~zm|KXG& za%_Gj7&(8-;Ad;)v7X=Rr+5qZV-t7KRL|Ke#g`9h?8i#`E$`f(cb=HXo(a|~YF)%_ zghzAC@1l2V&q+0;Dt;{!)PgN0VId(P{Gk85Oz=nX5xxX5dpk2rbCAYydA&dnXnX zkdxDY?z2Ke{L+hGAgKQY|25lRYeUXJHygk|j_)k%>P9U#;&`1@u-00vn(Cg*n;k~f z(5lwzzlnnyFg3)h6<=7A&K6c}+nfUv$$p{LseXI?5if}!hHWe$XtgO+b>+EQX?-FA zao}S!@Y~rm!35_|GPyaqoyCjxWvQJ+G;fcwoM2cW#`zm1i(ZU%^injf2e?LFN#B8MSA{&I?t^-+nAM8JcgoT@W|ak+-6Q-9OgCotG+-p zyJlBhRM!*6S^!oymw=#K8b74U^VMZhBy0@wGLqYCW!TREzbGAt#q+d_L3M0eO!d?Q zr2^Nu^@g6-cLQx$@+wAEa5M|hm~c4j{CiL+H{TE(8i8R(R*kSE%~}_a;^W~+8U&aq zEXqEShIw<6RF2;6WHXiJ68J9FpLuH#atUFYTL3_C+*Dy&0WqsRuW(|PGkMBDxf%`Z zw>=oQ;#$bWX$g(el4{a3{s1xI5?aXI`*6fSN=)l22{ADWyu7p`W`~S+`RHw9+rWLb zdbkp*+fleFZ*tUD8gfXG7-9E$*L}8~`cTd~H(>)%kd0RK$Bzjtr6!KgqDIC$jJdDqBrEd8iV=zYSY<}(6EH*1Zav%RBN&nL8E2d5J1 z2;h>aUF8SEnC=_N&cLjRe7;fPR;p1h^OzmL`SgWN0w~Ey!Kvk4Bqiw|9(TB_K3!p| zt3COTSo7z23_Xl2IZzQop>G&@&6jgo1_rN;w>b*nEyd3p z(_kXDC&%mFECj+vt}%JHB>PNKU_&>oujOJsybD+%cE-o`N?C4gp^4$>RILrxCer!> z=S>1D9CBj}SFD#or~g{@c>P3G+)ldRm06pi$O$ZG&X)lzLvuDxPH*wZEH679Pk``2 z(V1JXl9cFxu=*~FCb{8b;m;(T_e08MQl-Rva?vI{eUU{Gy7FxpA$d6Z1Q_BQ%fh;G z(W_KV>y2>!L`ZZj+A?Xj!3)BvRhVDCt0OQQ`?6!(99g`cZO_9((kdeq>6wf+7V4pa zxo7Q_HlYC01i#LZ32*kQVQ{Iv-{ow|g%Arhm`P3S$QD-w}kiVD4ZGG;q zUYea6wIlFo%tOeIjC34&Go!0j<>Gar6=MEwsgRE12p^l1j1hDOISP~Q-Th_DY09{X zTqpGQN3l6u-9}*}y|$xoldmrvfGC@mI=Q>B&Ba9PRNvZJ+Y+$nD$AjD_EzKdC)_?p z{)VbnBa9_OvI0?Z(SinT{GA?gSynX6V#Nv*&*wbK+Vf^DVn+2jdAkNWy6y6jQ)Hy} z`>9neetuvwsJxU)TzCYspZ&1Owe7g+>MoQaV60#~QlOog1rmneVoYY~*d-ndSfdE4 zp+1Ww+H8U{)kt(Yx0(6U!k1G{iB)-Y5yEc{e+PkZYYi-^^-!H9ubsQ zvo|YA=2vHZr*W(^lAJ+Ewpn<%XDY=9=}Y9NP*f-eSU02Cgz`penX7mQN24Nh&4t|E zfvzQU#e1xVf;~KITF5T6@W)eSAm;Nc6%$R>d;fPWrBntJaKi*!5@#R+Egdrt>t77nJ=9a%AqDW*}Jl_F7BYWddt_Bo^G9Gg~O zlDc)rdD6W1-Q>8cflExfaF$WPp08Uhjw)?$uzZi)t62Kf9>5(By4DQkN}bU2$~3xeJh=c)AOqn1 zrdM*cV!g53S!GQ*dN?9C)iK;CgBSzm)qPCOzTB87JH*_KJvyV6+ylHNN|;QU$@gb1 zSpo5;^KDJfg0?i=fgdp=zeLuc6U$WnCe7`>(9qw$-X7xmA0qUiyDh{wks57Z< zEV)^HySge)K*`r=8zxKObX+P*_ z%CGfdhUb>Ldx{`?JUyNF11eaO>7(04Ubb)t)Cy&cHZNu=SiZ6Sm1He~>`2I^K`eDr zwE4{v(Ow^S;H9T)ay>j#+R5`5ash7>R-xg0hjKcmuSlIEG1vL_i2Dm4-RG9kUP@sl z*7$0ldZ1TmD^&K=L&Q8EOFKd-yq#Mfx?}mNYa+w9^X?ugQC-X(Y!;MI2?|;!_)9Q+ zz5H2eBq`%p!HX@b4A;g0PLz9O)8$u$SHaS@B6D1=V9K5Xjk}q>PX>n4z$`WTA{F7) z1kQw4qTZ2_U71%JDYKg1;ei!3A~Z;PVyxW!)~8&&KCZ75p_O#jd5PuAQn?its?gh4 zuGgw;_v1wh5mL|ZMlz#F-IcWMAR*`zkpfqH6Uw(m5jwvRPu~e>j=d{Arps%hmG!$5 zbhY%%(df=hjrIiLetTEkF3(6nITGFs6-LM3Avl|w6lJ@F6A)+ZwRx6r0EZTGnwr3z z!1tUGxye?-d?I}##jJq=D`A6u02Mp`vA`?tgCJJgzL2Kn?sX0CQ6T06zu0rqJi2yR zU+8`?KWay8G`h}Sp$h|^%G3Z0qfKhrcpZCIp4a4~x3k~_=`ZW5(af@l8UX@gk>l^I zE1Z|0AfYb8B%>(F@?YM|i=kDfE@#*E27P%48+UPV+)N{jM@M}hVG>%I-zEA(<%i() zMLb=F!(v%L$pTwDc;!>~v4dkhk63W8Nsqw|2LnW11xh{6Jx^O=#bM>@VVdZaFr}L( zb`$ExNU1g=ZEGmCtU78oC6gzdwnsYAn}N(*C(~vf4oKblRctIn6UHx1f|qJ8aivV} zBnm=9Jl@1DirA{lc*5bR>6=Grewpefuogtzc1}}Yw72%-nw0G7~~ z_T-Hqk+w%})|k`=iPC^)Haiz$v!Gkh5i#b5I+Ag4Me0>UR8X@{S^jvQ{dbhYAKqv9GwP4$9Fc!R=|KH{^8L@q zKknWl{f2af|Ls)dpR4!h&E5aL7P0<%tM|{@|IsV_z2IHszdO=@&i~Jj*neM8lz;C- z{xu)c-}vxR{u@5!zgs9*%D?w9|6M*6lz;DY{xzT98)=8~?|jg|=0fxv7h`v9DsK@>u=r9!%qqQx?dELRfgUM0)4D~h5}7~80naMP_s*5r1xbkio8 zP?DRi1+CMh3@L*l#xnDs^PL$+A|L^nO>7MU7&vu^koaa2}Ip@@TyVVxIELpq^ zHNP~Yw6OGMX);PzMMuSJ>mHP$p|;JAeY&|4{y8WKKuU^T^zxYi+l7F?S?#CIoL&Quku70c{ zaO{+;+pc6<%f^*ew9F-^U%c4gQ@P_|-{9Z$J_fxT{OQjn;u!gDeU}KQTI*@8gu!G+ zpBv+?8>2dzIOs%ZHAd5W2!n2nz8J>0+`j(Y{@lUmL@MJOZlIgUsO~;{kwE{7W_U5^ zeuGCDIrKrVK`N0>CV)S!$qZx0fFFZOq*mk5bUK|%WT?{Vjbw%#j)5+#j(XWA!RRLp zYBTzh>0jL#E#OZs+U>VKpSx({3!D+5pTYnBwmMdQ9zm(qfJ`;QG) z!bhidZ2Le{ zGz~Tmv*X_at(KHmsThQR5&LIp z`OerR(?fzU<+tyOtyEDvUS2GpvD7N#z1UNN_kF1Qz--Gz!w(;yCrJ=3Rm#tp=7=5G z5vvvM%=*ZGtWc2+KgqXs;IT@w*dL|xA5WVaBZZjm7V13DixHKl^8$n~sE^$+Rx_Rp z0ZM_GlBQo93`HPbQh(jV8vXdn;4Rms^bNLL$8B2?_D1AFQr=6E3u_y`L{!=`3(u0B z>Yj&K1wzRxF66!3fAqE~#OS|EhHR7Kja&UO7Scyc&m2OqMEq3F^kW?ec>1nb7WFwA zF!K!c%Wc4$cug0K)oF)(#|(s{H)29c4l5K<8L-92wIMO0^4Eq$qTDGJV8?l$wk1uH z`s!PJW-^PTc`HROoMsgWC96>Nw0)zxFBW3Ld(Gv#;R4c!bcSqy`C?YD?HoRdK^F8;KG%a8jiy`H5Rv~gh!S9&DJc#))h_FqH_t(|a z5qc}tA;ejhh@Z)643S$0!JoVZ&oilypRZQ4RoZ{xbJbCbn&kE|J58RU90xg0th1Vk zk{5;)TGQ~Ha#mj%je9aM=(C;H-L<5AnhF`G8uoXxP9IAv5xAn#Z z2V5u=aFZNE*rsWH+7-V|Fto!p1VI%QouQ zc_!bLW`H1@oGJ9-8}iy%ou6WmT`@67DPhRF7Th=4eZd1wQz0 z0fl!MIX*Ebc-N});@@>?#L*rToOJo4{CA47%<>Z%wBKlJr!vBoR%DeV(^cWVf-PcC zVXm?(wraRR9>!3I$pT`lkOc%tM*!Ik!(ogPQzC4DzCdI#nS58)7kSv^OvI9&0I7LD z;X>>&VqjyE4y%1D%va0mmPvuY(_douh5CwSY*u;8+foOMSxk7gDF-r~O#zUCEx#ka z!pVq$XAKDy=RnRLqA)4Yv%005^hA&Y@-y-w4LxG_A?t_*Y1@3TL+@6wVNkN}Ou-gx ziAI(VYVpa${uM$Q;nUOKmud(vIndUcC`2|q_Xd6OozFS%;NOC!tc3I|ilgmgw@H7I z6ELzUj)`N2CdE1O0>4^R1ts!5Ek128xkK7Uer3OWjl$BO1BIIStPKN2G zzki}(wG>r%GE6`HeWk{+r3c!m3(o7jkrxQCD83lyqf8F*m2{UDn=OC`;*zNE6NOIq zKYDg#x>VKck1i&w%9o+^qr+^|-#^n3)c56xa%EgYn&51eP?u-Q~LY5 z@;kXcN{LkL0fztK>MbSbZFd6I{;{v|lr~flsI@50x3}B-k-1!i-dYsjVs>q}PdZUS zpym6n9XiEgr}ssN;nG#!YsjrYp-zQ`tC5Yp!Xivp`L96DJ;k)!iFB1ljfNGY+D+Ti z^ohNWkM#370?RE;tJrqqR}?U_MC@kz)IQHwdYR&PcJow}DL56Do38RjL(TwIaVqS3 zy2@9LjRs7+Jx~#)SQwu0qvlFhs42Vc9LEOJju* z({4^NVfE=M-$t{WX@|Xcp!7UtfqyMcsrK!GY`fK4nto$;-5w+zp)Am7Ihx(tV!{T} zu?&r7G>RS*HfcZ`Fem+} z^cz)y0ISjL=Dr%G6c=Wdft{(DwHmcJj%By^Qkuf%OuN~}g>A^d$~+#;-?Z0Do|G0- z6L@GP_EZyiYc-19+CqGFVxIG~OtvTQV~neJPkcR{*qfdJuGQ}6XUiEOL= z`>UxBvum9s=?&@v^}mkhZFQ%^zGb{O(mb{f)pRrnL5OmE}8^xj0%%5GF| zQ?xEVx?VC(`lN=yeCy)lY`Ya|2rRK?dRz4R*mUVH8YAp>KCdJ$%{xQ*lyq_2+!gEj zEu-FB&6uMZreIcZGZsGhzN8njMy_x&wy|KCtb8jV(gtHnk z5lfSvlvy<}e+2)EzK*V-3U=G7VMNl+nTs3FulA4S$hWO}O-rg_F`SHBlqiUJ%`1#+ zS7Lr(n-T}j^vtwGNJM#FU~D_9VgWwMZiSVD9bFeoBeZ80S@q5(ors_?Kd|i|yLgT! zYS=8pV_(MEP9t&R#X0HSSFGQFBoad3N%@{?9OM6%dHQ9D?|1X`!#s?wn?16Ba-#s# z$)+vIN4)DX0 z@0RIg=Yg5)bFtWy;Ng%9BRoekU2YU`J458>MgUk2Kbr7oYLD!S3{Q|;7~vywnB+zQ zF4yx)!a{-wH*4WP*fzAhPWy3VgtRkUx?+B3ydfVqM)>`IASzxgT>DO7v_TMmkMA=i z2O9?!emb#HnJ_rFuK%?V;cdrj#sGybQr;od9gv;dQS!G(p_BiOvcK_;qPg_`g)`RD zm2b`PQhR4n811jnq5om;gV$>B%FyqJ{wOg*qH*`j(AY-hybg&5X4xy`Il!k+=Q%RK z=eplI^IB{>rbJXkdtPB|d*2P_QH^sKXKmbI&$eK1++agL8R3`shl9fKuKnXK-afR0 z%MWklV9$?s=+s)w-o9D;xB>Ii-%1A=nrTVD-+}>J*GgKC1H-@1P`#uziWU;u7IJ8*Ovw!+H{krjRfJTwePSjQ2$`2*#CIw5a{K96+zp3?%3Fb!#a4boE&F2IEHZ}yEW+tS)GBWkIgkcJ6k)!8v2B7a`-&8& z&YQ`@LCVr+LUO`=mtQvF7!t&_acJ@I&7+m$^cyU|0s2Ayny1~Ad2j2VJ^Gk@ncmU0 z-rh#!$nu#Deux5>a6Nj?63k5O1?hb@<59?J?-zXS@SOFIE^E8K#C0AMzrU z74t9abKLa*c=JDW>%Wxa@?VXlaQUO7GyBS)b@%Vb{Pfhr14(#1rx3#}aEc2DU=z_g&^R`?Vdmknvato&^{+@3ua_-_@LPts$*LC7M9;!)oO1X>mn zc!$OQ*v%L>_NJ@MGbjHfAkY#I0{9fTK ztSwDuoL7snZo!3GR_X4I%`q(yY?+Ga~*VsA(&k-HL{+)aYtDX>MQLXZp@2L^1SZdY7 z-*U0Mi>E~?wvDs6Xj&kYam0jK?Bc}94}n1;*isVrbRF2;!__X4tah(hKl@aCB&@bf zrq$w|(RF$^ycPk+1aV92(zvHc)o3y;#Z&RYz=Yf@;s#IlPee0!xz^DwsV&+y6-|ww4wc$Jl`2)AU zW_%vrU3@pkKeR?o(%ZzI;?*v#vke7Fc(~f9YKrcbA0HE#Sp{2~#E70i3)plWNKk*S z!FVmi8p}rU)yYb^0P>FaoQ4L~!;@)&1rr2z7OtHPB>Y^j7~%lx=ZZY^&X7ALT^hG_ z#d^aznvp~84fXY5qKp|`VQ}m6>~J{NnT#;HwX6L_Zey& zndiy+eTmCs|13+iY#%S1#c@YLsEfHwaj;-tVqi)<#(r=(<#1pRNA}N3`#DYnoM}JD zX`pDob?`%u>>sIEGC#Pt$g`H*@aVob+;q#R9}NZY5)(HZ5P1f1za`(z!$ie>@Xolc z3w=*|+&LM46k3SmaWUUHh}UtW!hc}M3M4SfO}yLh9seq_+dP~&&q!4P2{>s9aW{tC zY1}!(3QpFknc)pMbZ|GB2G6)0#cdxHcQf&!>VLqHUjhxdbcd>CTrSK*V4vo04F3x& zTZ=dyWVKr-KJt+o0Z;|EhI9poKrU7IAMnV5k4RF^g&l&q-fWT+dAYMg@8kagOBPbU z!iTTOi3g<-8i5>(!tHMJA<2PT75*0%w>sC0`|-~g_lL3m&QnR;htm$Q_1=m`y=x=) zP^rnS3dosod|#S1M^p4vyntmznA#3; zq%4m6=e^Y??~JP?t4+*5-WiKV5(1LQVM2hc8%Z*ogAkCE#{E+g_fIhSHOC7EJh~Wh z{zawgD-J|X)H*S24XB#@gX*Z+5}fwHW)f^0S1i(#Ir1%7b1k%k<1$XAg+KlyaFjdu@;_Hcqom#2WuE`ZD>y@#R(jjg>!7#h2@D z$y_q7ewn;Z*)6Ei&+SPc{p~J6H-by!Eg`}^=HElPQI~w=YF~d4>mS)JEu_C`8GKts zeCTx%EgMA~xKrJK_aQL}9ivR3QPYh-_|xBwZXtgB8Aqza-gu3c=nW5GOlR01VgM0Uu`*)Ax5V*_PFKJ+L!dvdXh^in{lLj ze#U{*A6=j*yr|6DXK|1?R4l#HZ*frV3b}{ej7A3ze5D$s^#x|(f=^zfWEtZI=&gFn z#ujQ<2+CCkft?geVDFsZ^uwPGu2d_d8AWriR3Gi@wTDWqqYqw`NG_f6qUT6sc12c~ zg$(7n!WrF#J2o6nUvF_~IjBJBrIS~PS*y}Nvp&nORIxurgcU{g@@I%yA3O#vKDo)c zP)XamWFxED-JZ&-Q+QF4^}EH5cSD8lm2Oy2YUKiTuaKh`K6;mR@#N>nSr?aIQBOZy z-*?($@bL|LAxHVq3HOepJdlm1ASJXR8G$~)6Gx($<(;}@KaSG5wfgRGxh|)h>;lWd z8+AQ*>-<0k{4ppiN`G~S)k^XRB6tLmorrgaDUuKXWf(R@M5ILDYC=#QResqN=T?LI zzTo=49wKgfvS05h7A!F5f!{D5!8nyiW8VeFlE%7ZVG`h$C%Rom_VSCd{(3#9#0@e`xJ#G z!h?uFrlN=CHXUaD+VU$L@OKjzNLlmfj1Pjk-F_F21Nb^{@sCbuj)4uq?$lM^9fq-q zDD_SRiW31;?$%K#a{Z@1H?dk?Io4&Df+{lrHu1?O zI=A+?v=VWQm4WX_r*SxPDcO5<_jm${ZgYstMp-=*G8H{y%COA(Hm?mF@JR?{#sP@h zPx6D~SYF>3RNn_?oMT`^FaYoyhOvq2DY^IP9nT)QF$R4yy1O?hqsd?Vux@CARO_Q> zt8=mU+uQ^PBY=+XX-9e1c2yV#~B7C#mW~gFYghRU#R=! zmX@wG_-)v(u|2EY4*ud&W4BmFMvE!PAKKnfo~Kr70U8Fnp4)pfW98-KSLr_CpCjKV zORkT(TupN`Bn@?Vo)je}tLt3uZ5}BqpHuFwT8bBQ+J-m6lo4gsgcmg~ML_qL2wkZ&vZRx6Db^V@zqj?^zs6A+TWSgu3C%`FWV zlh8GcBbi33>lzaL!}}XsS|bVprM7NidAmiVto*z3_PM1e#Jsi(XFgxj5k~miGHWkjc1{Bl(ri?q6fCRr8RPeh z*aT4RR5P16k5{?&G(cwm4n6UntZ_>(g=>mu`s!T5k-uEn!m1a?PtRO-?kUz<$YHik z3)Xs6aE~A@SO;(?c}cLLMZ1b@SL`n-elpn6br7>t&Sv)zQ*pc?Th<_0NFkbvHZ8y= zPa!}N(!oAhbgM=O_a#*$fasUyl57;B6^jFEH42zOjCGp^Ayhht5{jei#sU7|mAT^F zR-K2%(u}H?%gw?y1s&$tEIIc98&xbFt~otZ&St;PL)Z{<5Mxqeqae}1I~Ae?0DK5M z!p8q#mnBTWOff8(1kLS&P(l@0K!R+MkY>Dod!bDL)i!-y{t5F8Mh@^oGV);f#Cy8x zm3b9g!Rzq5&W=G~k5YlmMLIhK9A=&SCrne+W)rpoH3YaHFEa!P)zpH3Zp0`+09-#T z652)DDvt2E1rh^E#qk2np4g(1$O5WbQo|MY{48XsAC41=a)S`0ZX~t z$u4{@qxczfydcfoCCgs+@N*gS&zRE%Y1S^y>t!pR%k2G(Nh(NN=c2JecJ>RIGoLY+ z3yRjfkjegx$u3B9bg{ph&Fc&7Zsw5hnHDMA94|R&1mZGkRXzUcLG#uEySq8fF0xVe z_@;yA-32xGax~mzU)AIL4w^Fx?C$5dV`UdM;HMul7r9$gl0#mrx^$XD+Hse43nXn! z?Q4+1f5E8TwR@b?yj|A%rA+o0%rAFq{>{-1XY##fBj-HSa6ouc>SoA=e9?vcn$DROD{a+JO;D&7`_@=Gms(b(^p ze_K?qElSBRwcN%1fa8WcqAqPwx_+sZE?GW~U3WzBZGHQE9XAw+CbdPa_cQYM~Ub|@c$-a6k)7^$4+)aD$;_ffI@SThz1@riB+9#K+0NKOuWXvg; zXLr9H2@gb{o4J?r3fdDz#FzP*f4W;k%h3pubt2+l`I%Gh+70Bm2g_a|;@kYpX?JVr zIaxT_J|dpi-<+?|jwhEH#q9ZVlSKF(YWTG`6l)c{$Lso=D-_xZX?GQCp-6ZOfAjf; zH6q&aC0b4-ypzAVcA=e^_Oeo~D_{SUF}u1eap41KjLQvq*}Pvs%c9<)=Kz@Ul@?O#?^j-cK+kmySA>i z>e|v3TC+dNoav0R4M^SK>VC%Yd4=e`&Zw;csheE0{&e*HSM*b7)S-Y>SJ&pVj?zy= z#i>zI0jb+uHO@QcKM~cVu0nQ4>kXByA76uoazXZUlS~j5Ln=($@0yh;yNWDxiHd10 zOgrS-oFp4XmZ_v-x(m~exN0QJz9P$XQ!$LfDrBQvEp*Qox~KTP8naxDG}(k^ncuoF zviIyPa^2HqKR3$+bzxNR)mY_bWyr2-k-5}`(Yj~1Cb#*LY*foZWYou<$9s0a=eqwb`}s4z=&<>gdo>$# zsljH!Jm+R+cdrUyU{O)6_@=|=2k+Imy zQtMsqJ++s;*1GZ)zwb!u8`s)>+RtBW^?k*M9Z4m*+V9u)ebWll({RmNpWF*zZ%z5^ z+Falm|5kKwPgKE?REn!cq2sc5qGx)ds*a>~xw_wTeEv@KUQg7ABdIjktRhEWqUfid z0wie86zDBDZ=xfh*ozSzZRvp>paO%)Ex4Bky_=~WxzpXuBsS{#T=avTEool8Fe&&&w$S5&9_{qqlm`Q#dlJUbN zpOx_g>xIUps+HQ@k4wev4<7XIOJqEhvQ6~X@nIeyIP-gsw7-bDau6o<4?JOVFC=dq zM+z59E^jx5ng3h>;LPti(*7c_lD%U|$Hl00kl&ZLtCUK~D{c3mTS`5#{5JYxk!`ub z8+Ly0A4eKW`>_>%F|M`gvMl?IV+UYD%nku5@o9 z*q~D;GASd4byty>ottTNSO%uM0zdB$47FzH>v)eD1Y=y!z&&O1z5fS3wuTx0x|%Od z&45ptu`k9Io}DJdF~S6AnSswzdkahgfk}o(FoBV@AlV?8{q=AYK}p${hqYV50uykv0 z2I%5+?Er{=UwhWDFU-=da|kF9Q}l5N=n+f~aIy)tbaU8sE%n^opVEsW9kYP-1Mfn; zR5I_U=RPJ2IgCLhi|E7#A}X_ixx>2pkJHUK5~_Rn3km0VV~UjCDcZyh5#^$72Z-x> z`e&2LuPL}e8j+ri8%*V;ch?dY541Aq)r4?ZYCgORas>;AU|zO4oOTnY}h)Zx$- z1}KKw%>{dHUdd%F+3oSPE+hKa&8PM0i#Fr_%trrPmb|02TDSN5N&2F6^uO<`b^ET1 zA=PFUlzil(m(dN8aVuSadQXvK7SM9Zt68eGp zq;PTNy}j-T$on3d)_+M{R^Qq1g!ox~=U3iaN|EL(@7<+%0D05k?NOR7zFhy!EAMTf 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%0A$ZCV0po_6vgs*$#Da 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

BriF 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 diff --git a/data/form_russian_in.odt b/data/form_russian_in.odt deleted file mode 100644 index 83e500fa4a3711d005c33e28c1e471b52aa9befe..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 13866 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 diff --git a/data/makePDFfromPS.sh b/data/makePDFfromPS.sh deleted file mode 100755 index 3bfca97ef..000000000 --- a/data/makePDFfromPS.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/bash -# License: GPL-2.0 -set -x -for f in *.ps -do ps2pdf12 $f ${f%.ps}.pdf -done - diff --git a/data/secret.pdf b/data/secret.pdf deleted file mode 100644 index 2ffe0ae874696ecb43eaf0a0cec275fe27f74d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 1187 zc-nfgc5bdd|_5?y8fjDf1LW`=@Ydek|R7E6BTq@cUCFzB<2glx~TO4n&*AO^y zK)oSQ#R(M&7195|6{!ML3GpZ3#4q5)*p2N%D)7PDdGluW?R?}KH8U^ID=7Er=ZgcR zVS;&g2Q4f}E7a?WZCnJTDVfX_)W?$PT7uT7%^g}Uqd@r7>Z9TQT$_AT+hn3L`)2=` zzJ2+1{WG1qwEndDez*+*0 z#Hgtl9IL^xoB&IAm=oYmEVhX@F=PlKqkJk<{_`C`v4rLFUXnun>nbDTH+7^(-pr|I z&Z-sANWOHNx(}&fwslgc2t!gU?25>WAUTUyvUsS66WY|DU23f0rEMOF!1mcd;L^NY zxB<0*_6Wwk8WeW6c2gm8fk44!qoAwBIik!EG6S?#?);NcE|0Dr`g98sTtX!Ma8c7# z4R7HDQw^wA?D0}eQ8L+gTK2X$R!)X)xrRFni*l`A(d%YG(Q9?3qUgF-s#fGh&8!mBAV&HBj7=18pXH0V zbj5;(a=H3l6aLqtg|1fC*6K~LTHE{)9{3Dq*Ux)apQZ+jWF>~n?YN;S(6{c>yyZFh zK5V*+cX&6?94uHLP?5Js{ZD}bED#L>=JjGK_gRm5K=FF{Fi38WJn7*yB^t2I(`Bxk JBnm_3px@cZR<8g6 diff --git a/data/secret.ps b/data/secret.ps deleted file mode 100644 index d090a244d..000000000 --- a/data/secret.ps +++ /dev/null @@ -1,22 +0,0 @@ -%! -% Licensed under Apache License v2.0. See the file "LICENSE" for more -% information. -% -% Standard banner page that shows the text "Cover Page". -% You may replace the text "Cover Page" below with your text -% for example "Confidential" and save it as confidential.ps -% then run "ps2pdf12 confidential.ps confidential.pdf" to make a PDF -% and install it as /usr/share/cups/data/confidential.pdf -% then change in /usr/share/cups/banners/confidential the -% line "Template default.pdf" to "Template confidential.pdf" -% to get a banner page that shows "Confidential" in its printout. -/Helvetica findfont -40 scalefont -setfont -newpath -% a4 is 595 842 points -% letter is 612 792 points -150 550 moveto -(Secret) show -showpage - diff --git a/data/standard.pdf b/data/standard.pdf deleted file mode 100644 index 5bb1e060412acd1b2cbac2bc58df8f5f054bb37e..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 1191 zc-n=jD%uhy=|Wl|*S<|}aeO2D1_BcU z>PpoCF%*Olbl@Mbg33dc5PyLICVl}E$8HiAQh^8Sp6`6;-0z-8s%llz%A874AAdf6 zNwOdV-`FPe^U@OaTVe}zL~2rnd4dKYRXkfzi#pt;`8)|lKx==Hb#+fQ!2n%sT* z{LT5j!|xZ~ot=0z_Q5>!p89S-H=ZIrdS92R4vCs4qqvih)=S#BxHAZZJ25W-PWw)&VngF{5%KRQdBANU??O@zx-P_SaQr!mk;` zh`fo@NSsAGq>+5-I`!^S!5sUzP7y|=dUr!aRs<0HCgPDcu5-e=-P=rpv zIs$TYO6CgI0^1`P_bn{!jCEC&m5hu9SImr|>9evrEz8qLTjuUR8Toww>Rv!Mi3~X+ z4?obeSuG2jFu*hu3m<#@A(mD35llM;Q%;GMl~GL5jS(!PtD~5vC71|o=FuSTf1N#` zkOQ1Kj-8JB1Ja_u$w56Hxn{MN_d$7$n(#!?H+K7s@)9?Qf=->j;Fl2r!rt*Nbn2!{{mF|Xvnfj=Se67X17Uif3{A)>UBC+n0dE< z$_`$CS^vZ)uCBkRe*QpKr#5~Sevn(^WQuQ09`BOfqj7R{_r~7o`%8O|4kq{CZoGMT zcx~_a`}L1kCSH!6SeL&s-|gqdQ>4f2>&j{a#7LCZpLLkBX1AEK!dz|_MGqPj-KMI6 zmD4tfQWX+)14UNAdi z-o$Ao&Y~T%NWOB9c~6++j(ygr2qQ|Z+mw+NL244OCGpq~x9hNAW^rv97PmzxLnq)J z3As7dxQ)HQ@klQG5;k_GbVsAAL9yX#*06MQmTJ?KP9tqaxc_A2^ZnAjfNc>9IYNgY zn3;^8fh`zddKMcWd;B4m(aaG{zW`G&h^6W%rkd6WW|-P2rt1kN1Dks+i05D9&l%(Z z-<-fnNAm$?$=?#7ojtmx5;W#bO0I47FJiwRkUi`;wf|jj&C788pmO9H9?nyu2bOr6 M-17#Bx?S#)-+RYb*8l(j diff --git a/data/topsecret.ps b/data/topsecret.ps deleted file mode 100644 index caa2b45d6..000000000 --- a/data/topsecret.ps +++ /dev/null @@ -1,22 +0,0 @@ -%! -% Licensed under Apache License v2.0. See the file "LICENSE" for more -% information. -% -% Standard banner page that shows the text "Cover Page". -% You may replace the text "Cover Page" below with your text -% for example "Confidential" and save it as confidential.ps -% then run "ps2pdf12 confidential.ps confidential.pdf" to make a PDF -% and install it as /usr/share/cups/data/confidential.pdf -% then change in /usr/share/cups/banners/confidential the -% line "Template default.pdf" to "Template confidential.pdf" -% to get a banner page that shows "Confidential" in its printout. -/Helvetica findfont -40 scalefont -setfont -newpath -% a4 is 595 842 points -% letter is 612 792 points -150 550 moveto -(Top Secret) show -showpage - diff --git a/data/unclassified.pdf b/data/unclassified.pdf deleted file mode 100644 index 520dea388a0b041b6315076d71d9acf9febd66ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 1193 zc-npDzxO zhDFTk+h}%HSSDeEZ($vfvf$8wlL!k=;BsOSkNKonL@|$u+eDr2bXEMORK=0Gskhx@ z^6ur=wa;X1eC=8J%LlYNvHolB2f8_mCg}R5qaCz!IEoJMT;Dx@e|i5+_tk^-?%wOY z{e!E!N8hh~yfXHDa*6NcM5lU`@2rI;=?mX+@nh0)P zlCe>cH;_@R3wKHIh;Zt;Cw)pVBvjgUo>&Pa4dUfNJoLkFdnB4#EG^^3Ef(|G zi)f4E!i=Qef?mLQIAx&)jh(V?%c7);&~V9A4Mm?8Al$d+x`w&Q5JViydn_a-*@1DBU$VDxz$y>3|3`}J;tqw!rP7Vq1z;VgC?>vBVb-S&_2*~ zACkNq`uQg8dw{oDJx_hiX%kSM_j>d111(q}62~-bq*NBs1`UA{HuCK_yE*an52q=~ Q0~>hiGzhXp?Kbt%AFFOz>;M1& diff --git a/data/unclassified.ps b/data/unclassified.ps deleted file mode 100644 index 64e0e672f..000000000 --- a/data/unclassified.ps +++ /dev/null @@ -1,22 +0,0 @@ -%! -% Licensed under Apache License v2.0. See the file "LICENSE" for more -% information. -% -% Standard banner page that shows the text "Cover Page". -% You may replace the text "Cover Page" below with your text -% for example "Confidential" and save it as confidential.ps -% then run "ps2pdf12 confidential.ps confidential.pdf" to make a PDF -% and install it as /usr/share/cups/data/confidential.pdf -% then change in /usr/share/cups/banners/confidential the -% line "Template default.pdf" to "Template confidential.pdf" -% to get a banner page that shows "Confidential" in its printout. -/Helvetica findfont -40 scalefont -setfont -newpath -% a4 is 595 842 points -% letter is 612 792 points -150 550 moveto -(Unclassified) show -showpage - diff --git a/drv/generic-brf.drv b/drv/generic-brf.drv deleted file mode 100644 index 7216b59f8..000000000 --- a/drv/generic-brf.drv +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) 2015, 2017-2018 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include - -#po fr "fr-braille.po" - -Manufacturer "Generic" -Version 1.0 - -Throughput 1 - -Filter application/vnd.cups-paged-brf 0 brftoembosser -Filter image/vnd.cups-brf 0 brftoembosser - -MediaSize Legal -MediaSize Letter -MediaSize A3 -MediaSize A4 -*MediaSize A4TF -MediaSize A5 -MediaSize 110x115 -MediaSize 110x120 -MediaSize 110x170 -MediaSize 115x110 -MediaSize 120x120 - -ColorDevice no -HWMargins 1cm 1cm 1cm 1cm -VariablePaperSize true -MinSize 1in 1in -MaxSize 9999in 9999in - -Option "SendFF/Send FormFeed" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - -Option "SendSUB/Send Sub (^Z)" Boolean AnySetup 10 - *Choice "True/Yes" "" - Choice "False/No" "" - -ModelName "Braille embosser" -PCFileName "gen-brf.ppd" diff --git a/drv/generic-ubrl.drv b/drv/generic-ubrl.drv deleted file mode 100644 index 0dc250239..000000000 --- a/drv/generic-ubrl.drv +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) 2015, 2017-2018 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include - -#po fr "fr-braille.po" - -Manufacturer "Generic" -Version 1.0 - -Throughput 1 - -Filter text/vnd.cups-paged-ubrl 0 - -Filter image/vnd.cups-ubrl 0 - - -MediaSize Legal -MediaSize Letter -MediaSize A3 -MediaSize A4 -*MediaSize A4TF -MediaSize A5 -MediaSize 110x115 -MediaSize 110x120 -MediaSize 110x170 -MediaSize 115x110 -MediaSize 120x120 - -ColorDevice no -HWMargins 1cm 1cm 1cm 1cm -VariablePaperSize true -MinSize 1in 1in -MaxSize 9999in 9999in - -ModelName "UBRL generator" -PCFileName "gen-ubrl.ppd" diff --git a/drv/indexv3.drv b/drv/indexv3.drv deleted file mode 100644 index 559a206f2..000000000 --- a/drv/indexv3.drv +++ /dev/null @@ -1,136 +0,0 @@ -// -// Copyright (c) 2015, 2017-2018 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include -#include - -#po fr "fr-braille.po" - -Manufacturer "Index" -Version 1.0 - -Filter application/vnd.cups-paged-brf 0 textbrftoindexv3 -Filter image/vnd.cups-ubrl 0 imageubrltoindexv3 - -// -// Common options -// - -HWMargins 0mm 5mm 0mm 2mm -VariablePaperSize true -MinSize 1in 1in - -Group "Index/Index support" - Option "IndexFirmwareVersion/Installed firmware version" PickOne AnySetup 10 - Choice "102000/10.20 or above" "" - *Choice "103000/10.30 or above" "" - // Provides DBT but we just use transparent mode - //Choice "110210/11.02.1 or above" "" - // Provides user-defined interline size - Choice "120130/12.01.3 or above" "" - - -// -// Basic-D -// -{ - ModelName "Basic-D V3" - PCFileName "ibasicd3.ppd" - - MaxSize 325mm 10m - - Group "General/General" - Option "Duplex/Double-Sided Printing" PickOne AnySetup 10 - Choice "DuplexNoTumble/Long Edge (Standard)" "" - *Choice "None/Off" "" - - Group "Index" - Option "ZFolding/Z-Folding" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - - Attribute "IndexPaperLength" "" In -} - - -// -// Basic-S -// -{ - ModelName "Basic-S V3" - PCFileName "ibasics3.ppd" - - MaxSize 325mm 10m - - Attribute "IndexPaperLength" "" In -} - - -// -// 4-Waves PRO -// -{ - ModelName "4-Waves PRO" - PCFileName "i4waves3.ppd" - - MaxSize 325mm 10m - - Group "General/General" - Option "Duplex/Double-Sided Printing" PickOne AnySetup 10 - Choice "DuplexNoTumble/Long Edge (Standard)" "" - *Choice "None/Off" "" - - Group "Index" - Option "ZFolding/Z-Folding" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" -} - - -// -// Everest-D -// -{ - ModelName "Everest-D V3" - PCFileName "ieveres3.ppd" - - MaxSize 297mm 10m - - Group "General/General" - Option "Duplex/Double-Sided Printing" PickOne AnySetup 10 - Choice "DuplexNoTumble/Long Edge (Standard)" "" - *Choice "None/Off" "" - - Attribute "IndexPaperLength" "" Mm -} - - -// -// 4x4 PRO -// -{ - ModelName "4x4 PRO V3" - PCFileName "i4x4pro3.ppd" - - MaxSize 297mm 10m - - Group "General/General" - Option "Duplex/Double-Sided Printing" PickOne AnySetup 10 - Choice "DuplexNoTumble/Long Edge (Standard)" "" - *Choice "None/Off" "" - - Group "Index" - Option "SaddleStitch/Saddle Stitch 4 pages" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - - Attribute "IndexPaperLength" "" Mm -} diff --git a/drv/indexv4.drv b/drv/indexv4.drv deleted file mode 100644 index ce06f50b6..000000000 --- a/drv/indexv4.drv +++ /dev/null @@ -1,141 +0,0 @@ -// -// Copyright (c) 2015, 2017-2018 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include -#include - -#po fr "fr-braille.po" - -Manufacturer "Index" -Version 1.0 - -Filter application/vnd.cups-paged-brf 0 textbrftoindexv4 -Filter image/vnd.cups-ubrl 0 imageubrltoindexv4 - -// -// Common options -// - -HWMargins 5mm 5mm 5mm 5mm -VariablePaperSize true -MinSize 1in 1in - -Group "Index/Index support" - Option "IndexFirmwareVersion/Installed firmware version" PickOne AnySetup 10 - Choice "102000/10.20 or above" "" - *Choice "103000/10.30 or above" "" - // Provides DBT but we just use transparent mode - //Choice "110210/11.02.1 or above" "" - -Group "Braille" - Option "IndexTable/Firmware Braille Table" PickOne AnySetup 10 - Choice "5/American computer 6 dots" "" - Choice "6/American computer 8 dots" "" - Choice "7/English computer 8 dots" "" - Choice "8/English literal without capital" "" - Choice "9/IBM 437 computer 6 dots" "" - Choice "10/IBM 437 computer 8 dots" "" - Choice "11/IBM 850 computer 8 dots" "" - Choice "12/German computer 8 dots" "" - Choice "13/Italian literal with capital" "" - Choice "14/Italian literal without capital" "" - Choice "15/Portuguese literal with capital" "" - Choice "16/Spanish computer 6 dots" "" - Choice "17/Spanish computer 8 dots" "" - Choice "18/Spanish user defined 3 ANSI" "" - Choice "19/Spanish user defined 4 ASCII" "" - Choice "20/Swedish literal without capital" "" - Choice "21/Polish computer 6 dots" "" - Choice "22/Polish literal with capital" "" - Choice "23/Polish own table 03" "" - Choice "24/Polish own table 04" "" - Choice "25/Dutch computer 6 dots" "" - Choice "26/Dutch own table 02" "" - - -// -// Basic-D -// -{ - ModelName "Basic-D V4/V5" - PCFileName "ibasicd4.ppd" - - MaxSize 325mm 10m - - Group "General/General" - Option "Duplex/Double-Sided Printing" PickOne AnySetup 10 - Choice "DuplexNoTumble/Long Edge (Standard)" "" - *Choice "None/Off" "" - - Group "Index" - Option "ZFolding/Z-Folding" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - - Option "Sideways/Sideways Folding" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - - UIConstraints "*ZFolding True *Sideways True" -} - - -// -// Basic-S -// -{ - ModelName "Basic-S V4/V5" - PCFileName "ibasics4.ppd" - - MaxSize 325mm 10m -} - - -// -// Everest-D -// -{ - ModelName "Everest-D V4/V5" - PCFileName "ieveres4.ppd" - - MaxSize 297mm 10m - - Group "General/General" - Option "Duplex/Double-Sided Printing" PickOne AnySetup 10 - Choice "DuplexNoTumble/Long Edge (Standard)" "" - *Choice "None/Off" "" - - Group "Index" - Option "SaddleStitch/Saddle Stitch 4 pages" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" -} - - -// -// Braille Box -// -{ - ModelName "Braille Box V4/V5" - PCFileName "ibrlbox4.ppd" - - MaxSize 297mm 10m - - Group "General/General" - Option "Duplex/Double-Sided Printing" PickOne AnySetup 10 - Choice "DuplexNoTumble/Long Edge (Standard)" "" - *Choice "None/Off" "" - - Group "Index" - Option "SaddleStitch/Saddle Stitch 4 pages" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" -} diff --git a/filter/braille/drivers/common/fr-braille.po b/filter/braille/drivers/common/fr-braille.po deleted file mode 100644 index c73c6a563..000000000 --- a/filter/braille/drivers/common/fr-braille.po +++ /dev/null @@ -1,243 +0,0 @@ -# -# Copyright (c) 2015, 2017-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -msgid "Generic" -msgstr "Generique" - -msgid "Braille embosser" -msgstr "Embosseuse braille" - -msgid "A4 Tractor Feed" -msgstr "A4 papier listing" - -msgid "Yes" -msgstr "Oui" - -msgid "No" -msgstr "Non" - -msgid "Braille transcription" -msgstr "Transcription Braille" - -msgid "None" -msgstr "Aucun(e)" - -msgid "Additional Braille transcription (2)" -msgstr "Transcription Braille additionnelle (2)" - -msgid "Additional Braille transcription (3)" -msgstr "Transcription Braille additionnelle (3)" - -msgid "Additional Braille transcription (4)" -msgstr "Transcription Braille additionnelle (4)" - -msgid "Default for language" -msgstr "Par défaut pour la langue" - -msgid "Default for language grade 0" -msgstr "Par défaut pour la langue grade 0" - -msgid "Default for language grade 1" -msgstr "Par défaut pour la langue grade 1" - -msgid "Default for language grade 2" -msgstr "Par défaut pour la langue grade 2" - -msgid "Default for language grade 3" -msgstr "Par défaut pour la langue grade 3" - -msgid "Default hyphenation rules for language" -msgstr "Règles de césure par défaut pour la langue" - -msgid "Text dot distance" -msgstr "Distance points texte" - -msgid "Text dots" -msgstr "Points texte" - -msgid "8 dots" -msgstr "8 points" - -msgid "6 dots" -msgstr "6 points" - -msgid "Graphic dot distance" -msgstr "Distance points graphique" - -msgid "Line spacing" -msgstr "Espacement de ligne" - -msgid "Top margin" -msgstr "Marge haut" - -msgid "Bottom margin" -msgstr "Marge bas" - -msgid "Inner margin" -msgstr "Marge intérieure" - -msgid "Outer margin" -msgstr "Marge extérieure" - -msgid "Page number" -msgstr "Numéro de page" - -msgid "Graphical top margin" -msgstr "Marge graphique haut" - -msgid "Graphical bottom margin" -msgstr "Marge graphique bas" - -msgid "Graphical left margin" -msgstr "Marge graphique gauche" - -msgid "Graphical right margin" -msgstr "Marge graphique droite" - -msgid "Braille Page Number" -msgstr "Numéro de page braille" - -msgid "Print Page Number" -msgstr "Numéro de page noir" - -msgid "Top Margin" -msgstr "Marge en haut" - -msgid "Bottom Margin" -msgstr "Marge en bas" - -msgid "Top Inline" -msgstr "Intégré en haut" - -msgid "Bottom Inline" -msgstr "Intégré en bas" - -msgid "Print Page Separator" -msgstr "Montrer les séparations de pages" - -msgid "Print Page Number in Separator" -msgstr "Indiquer le numéro de page dans la séparation" - -msgid "Prefix Print Page Number with Braille Page Number" -msgstr "Préfixer le numéro de page noir avec le numéro de page braille" - -msgid "Image conversion" -msgstr "Conversion d'image" - -msgid "Resize" -msgstr "Retailler" - -msgid "Mirror" -msgstr "Miroir" - -msgid "Rotation" -msgstr "Rotation" - -msgid "No rotation" -msgstr "Pas de rotation" - -msgid "Rotate clockwise to fit paper" -msgstr "Rotation horaire pour correspondre au papier" - -msgid "Rotate counter clockwise to fit paper" -msgstr "Rotation anti-horaire pour correspondre au papier" - -msgid "Clockwise" -msgstr "Horaire" - -msgid "Counter clockwise" -msgstr "Anti-horaire" - -msgid "Upside Down" -msgstr "Retourner" - -msgid "Edge detection" -msgstr "Détection de contour" - -msgid "Simple" -msgstr "Simple" - -msgid "Canny" -msgstr "Canny" - -msgid "Negate" -msgstr "Inversion" - -msgid "EdgeFactor" -msgstr "Facteur de contour" - -msgid "CannyRadius" -msgstr "Radius Canny" - -msgid "CannySigma" -msgstr "Sigma Canny" - -msgid "CannyLower" -msgstr "Lower Canny" - -msgid "CannyUpper" -msgstr "Upper Canny" - -msgid "Index support" -msgstr "Support Index" - -msgid "Installed firmware version" -msgstr "Version de firmware installée" - -msgid "10.20 or above" -msgstr "10.20 ou supérieur" - -msgid "10.30 or above" -msgstr "10.30 ou supérieur" - -msgid "11.02.1 or above" -msgstr "11.02.1 ou supérieur" - -msgid "11.03.2 or above" -msgstr "11.03.2 ou supérieur" - -msgid "12.01.3 or above" -msgstr "12.01.3 ou supérieur" - -msgid "Double-Sided Printing" -msgstr "Impression recto-verso" - -msgid "Off" -msgstr "Désactivé" - -msgid "Long Edge (Standard)" -msgstr "Bord long (standard)" - -msgid "Z-Folding" -msgstr "Pliage accordéon" - -msgid "Sideways Folding" -msgstr "Pliage paysage" - -msgid "Saddle Stich 4 pages" -msgstr "Cahier 4 pages" - -msgid "Firmware Braille Table" -msgstr "Table Braille Firmware" - -msgid "6-dot MIT table" -msgstr "Table MIT 6 points" - -msgid "User-defined Table 1" -msgstr "Table 1 définie par l'utilisateur" - -msgid "User-defined Table 2" -msgstr "Table 2 définie par l'utilisateur" - -msgid "User-defined Table 3" -msgstr "Table 3 définie par l'utilisateur" - -msgid "User-defined Table 4" -msgstr "Table 4 définie par l'utilisateur" - -msgid "Multiple Impact" -msgstr "Impact multiple" diff --git a/filter/braille/drivers/common/media-braille.defs b/filter/braille/drivers/common/media-braille.defs deleted file mode 100644 index a8ee4faf2..000000000 --- a/filter/braille/drivers/common/media-braille.defs +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) 2015 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#media "110x115/11x11.5" 792 828 -#media "110x120/11x12" 792 864 -#media "110x170/11x17" 792 1224 -#media "115x110/11.5x11" 828 792 -#media "120x120/12x12" 864 864 -#media "A4TF/A4 Tractor Feed" 594 864 diff --git a/filter/braille/drivers/generic/brftoembosser.in b/filter/braille/drivers/generic/brftoembosser.in deleted file mode 100755 index 84ae165d7..000000000 --- a/filter/braille/drivers/generic/brftoembosser.in +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015, 2017-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 -if [ -z "$FILE" ] -then - # Get input from stdin - unset FILE - trap -- 'rm -f "$FILE"' EXIT - FILE=$(mktemp "${TMPDIR:-/tmp}/brftoembosser.XXXXXX") - cat > "$FILE" -fi - -. @CUPS_DATADIR@/braille/cups-braille.sh - -SENDFF=$(getOption SendFF) -SENDSUB=$(getOption SendSUB) - -echo "INFO: Writing text to generic embosser" >&2 - -while [ $NB -gt 0 ] -do - < "$FILE" \ - sed -e 's/^$/'$'\015''/' \ - -e 's/'$'\302'$'\240''/ /g' \ - -e 's/'$'\240''/ /g' \ - -e 's/\([^'$'\015'']\)$/\1'$'\015''/' - - if [ "$SENDFF" = True ] - then - printf '\014' - fi - if [ "$SENDSUB" = True ] - then - printf '\032' - fi - NB=$(($NB - 1)) -done - -echo "INFO: Ready" >&2 -exit 0 diff --git a/filter/braille/drivers/index/imageubrltoindexv3.in b/filter/braille/drivers/index/imageubrltoindexv3.in deleted file mode 100755 index f26741bae..000000000 --- a/filter/braille/drivers/index/imageubrltoindexv3.in +++ /dev/null @@ -1,298 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -. @CUPS_DATADIR@/braille/indexv3.sh -printf "$INIT" - -# Enter 4-dot graphic mode -printf "\033\007" - -echo "INFO: Writing text to Index embosser" >&2 -( if [ -z "$FILE" ] -then - cat -else - cat "$FILE" -fi ) | sed \ - -e 's/⠀/@@/g' \ - -e 's/⠁/A@/g' \ - -e 's/⠂/B@/g' \ - -e 's/⠃/C@/g' \ - -e 's/⠄/D@/g' \ - -e 's/⠅/E@/g' \ - -e 's/⠆/F@/g' \ - -e 's/⠇/G@/g' \ - -e 's/⡀/H@/g' \ - -e 's/⡁/I@/g' \ - -e 's/⡂/J@/g' \ - -e 's/⡃/K@/g' \ - -e 's/⡄/L@/g' \ - -e 's/⡅/M@/g' \ - -e 's/⡆/N@/g' \ - -e 's/⡇/O@/g' \ - -e 's/⠈/@A/g' \ - -e 's/⠉/AA/g' \ - -e 's/⠊/BA/g' \ - -e 's/⠋/CA/g' \ - -e 's/⠌/DA/g' \ - -e 's/⠍/EA/g' \ - -e 's/⠎/FA/g' \ - -e 's/⠏/GA/g' \ - -e 's/⡈/HA/g' \ - -e 's/⡉/IA/g' \ - -e 's/⡊/JA/g' \ - -e 's/⡋/KA/g' \ - -e 's/⡌/LA/g' \ - -e 's/⡍/MA/g' \ - -e 's/⡎/NA/g' \ - -e 's/⡏/OA/g' \ - -e 's/⠐/@B/g' \ - -e 's/⠑/AB/g' \ - -e 's/⠒/BB/g' \ - -e 's/⠓/CB/g' \ - -e 's/⠔/DB/g' \ - -e 's/⠕/EB/g' \ - -e 's/⠖/FB/g' \ - -e 's/⠗/GB/g' \ - -e 's/⡐/HB/g' \ - -e 's/⡑/IB/g' \ - -e 's/⡒/JB/g' \ - -e 's/⡓/KB/g' \ - -e 's/⡔/LB/g' \ - -e 's/⡕/MB/g' \ - -e 's/⡖/NB/g' \ - -e 's/⡗/OB/g' \ - -e 's/⠘/@C/g' \ - -e 's/⠙/AC/g' \ - -e 's/⠚/BC/g' \ - -e 's/⠛/CC/g' \ - -e 's/⠜/DC/g' \ - -e 's/⠝/EC/g' \ - -e 's/⠞/FC/g' \ - -e 's/⠟/GC/g' \ - -e 's/⡘/HC/g' \ - -e 's/⡙/IC/g' \ - -e 's/⡚/JC/g' \ - -e 's/⡛/KC/g' \ - -e 's/⡜/LC/g' \ - -e 's/⡝/MC/g' \ - -e 's/⡞/NC/g' \ - -e 's/⡟/OC/g' \ - -e 's/â  /@D/g' \ - -e 's/â ¡/AD/g' \ - -e 's/â ¢/BD/g' \ - -e 's/â £/CD/g' \ - -e 's/â ¤/DD/g' \ - -e 's/â ¥/ED/g' \ - -e 's/â ¦/FD/g' \ - -e 's/â §/GD/g' \ - -e 's/â¡ /HD/g' \ - -e 's/â¡¡/ID/g' \ - -e 's/â¡¢/JD/g' \ - -e 's/â¡£/KD/g' \ - -e 's/⡤/LD/g' \ - -e 's/â¡¥/MD/g' \ - -e 's/⡦/ND/g' \ - -e 's/â¡§/OD/g' \ - -e 's/â ¨/@E/g' \ - -e 's/â ©/AE/g' \ - -e 's/â ª/BE/g' \ - -e 's/â «/CE/g' \ - -e 's/â ¬/DE/g' \ - -e 's/â ­/EE/g' \ - -e 's/â ®/FE/g' \ - -e 's/â ¯/GE/g' \ - -e 's/⡨/HE/g' \ - -e 's/â¡©/IE/g' \ - -e 's/⡪/JE/g' \ - -e 's/â¡«/KE/g' \ - -e 's/⡬/LE/g' \ - -e 's/â¡­/ME/g' \ - -e 's/â¡®/NE/g' \ - -e 's/⡯/OE/g' \ - -e 's/â °/@F/g' \ - -e 's/â ±/AF/g' \ - -e 's/â ²/BF/g' \ - -e 's/â ³/CF/g' \ - -e 's/â ´/DF/g' \ - -e 's/â µ/EF/g' \ - -e 's/â ¶/FF/g' \ - -e 's/â ·/GF/g' \ - -e 's/â¡°/HF/g' \ - -e 's/⡱/IF/g' \ - -e 's/⡲/JF/g' \ - -e 's/⡳/KF/g' \ - -e 's/â¡´/LF/g' \ - -e 's/⡵/MF/g' \ - -e 's/â¡¶/NF/g' \ - -e 's/â¡·/OF/g' \ - -e 's/â ¸/@G/g' \ - -e 's/â ¹/AG/g' \ - -e 's/â º/BG/g' \ - -e 's/â »/CG/g' \ - -e 's/â ¼/DG/g' \ - -e 's/â ½/EG/g' \ - -e 's/â ¾/FG/g' \ - -e 's/â ¿/GG/g' \ - -e 's/⡸/HG/g' \ - -e 's/⡹/IG/g' \ - -e 's/⡺/JG/g' \ - -e 's/â¡»/KG/g' \ - -e 's/⡼/LG/g' \ - -e 's/⡽/MG/g' \ - -e 's/⡾/NG/g' \ - -e 's/â¡¿/OG/g' \ - -e 's/⢀/@H/g' \ - -e 's/⢁/AH/g' \ - -e 's/⢂/BH/g' \ - -e 's/⢃/CH/g' \ - -e 's/⢄/DH/g' \ - -e 's/⢅/EH/g' \ - -e 's/⢆/FH/g' \ - -e 's/⢇/GH/g' \ - -e 's/⣀/HH/g' \ - -e 's/⣁/IH/g' \ - -e 's/⣂/JH/g' \ - -e 's/⣃/KH/g' \ - -e 's/⣄/LH/g' \ - -e 's/⣅/MH/g' \ - -e 's/⣆/NH/g' \ - -e 's/⣇/OH/g' \ - -e 's/⢈/@I/g' \ - -e 's/⢉/AI/g' \ - -e 's/⢊/BI/g' \ - -e 's/⢋/CI/g' \ - -e 's/⢌/DI/g' \ - -e 's/⢍/EI/g' \ - -e 's/⢎/FI/g' \ - -e 's/⢏/GI/g' \ - -e 's/⣈/HI/g' \ - -e 's/⣉/II/g' \ - -e 's/⣊/JI/g' \ - -e 's/⣋/KI/g' \ - -e 's/⣌/LI/g' \ - -e 's/⣍/MI/g' \ - -e 's/⣎/NI/g' \ - -e 's/⣏/OI/g' \ - -e 's/⢐/@J/g' \ - -e 's/⢑/AJ/g' \ - -e 's/⢒/BJ/g' \ - -e 's/⢓/CJ/g' \ - -e 's/⢔/DJ/g' \ - -e 's/⢕/EJ/g' \ - -e 's/⢖/FJ/g' \ - -e 's/⢗/GJ/g' \ - -e 's/⣐/HJ/g' \ - -e 's/⣑/IJ/g' \ - -e 's/⣒/JJ/g' \ - -e 's/⣓/KJ/g' \ - -e 's/⣔/LJ/g' \ - -e 's/⣕/MJ/g' \ - -e 's/⣖/NJ/g' \ - -e 's/⣗/OJ/g' \ - -e 's/⢘/@K/g' \ - -e 's/⢙/AK/g' \ - -e 's/⢚/BK/g' \ - -e 's/⢛/CK/g' \ - -e 's/⢜/DK/g' \ - -e 's/⢝/EK/g' \ - -e 's/⢞/FK/g' \ - -e 's/⢟/GK/g' \ - -e 's/⣘/HK/g' \ - -e 's/⣙/IK/g' \ - -e 's/⣚/JK/g' \ - -e 's/⣛/KK/g' \ - -e 's/⣜/LK/g' \ - -e 's/⣝/MK/g' \ - -e 's/⣞/NK/g' \ - -e 's/⣟/OK/g' \ - -e 's/⢠/@L/g' \ - -e 's/⢡/AL/g' \ - -e 's/⢢/BL/g' \ - -e 's/⢣/CL/g' \ - -e 's/⢤/DL/g' \ - -e 's/⢥/EL/g' \ - -e 's/⢦/FL/g' \ - -e 's/⢧/GL/g' \ - -e 's/⣠/HL/g' \ - -e 's/⣡/IL/g' \ - -e 's/⣢/JL/g' \ - -e 's/⣣/KL/g' \ - -e 's/⣤/LL/g' \ - -e 's/⣥/ML/g' \ - -e 's/⣦/NL/g' \ - -e 's/⣧/OL/g' \ - -e 's/⢨/@M/g' \ - -e 's/⢩/AM/g' \ - -e 's/⢪/BM/g' \ - -e 's/⢫/CM/g' \ - -e 's/⢬/DM/g' \ - -e 's/⢭/EM/g' \ - -e 's/⢮/FM/g' \ - -e 's/⢯/GM/g' \ - -e 's/⣨/HM/g' \ - -e 's/⣩/IM/g' \ - -e 's/⣪/JM/g' \ - -e 's/⣫/KM/g' \ - -e 's/⣬/LM/g' \ - -e 's/⣭/MM/g' \ - -e 's/⣮/NM/g' \ - -e 's/⣯/OM/g' \ - -e 's/⢰/@N/g' \ - -e 's/⢱/AN/g' \ - -e 's/⢲/BN/g' \ - -e 's/⢳/CN/g' \ - -e 's/⢴/DN/g' \ - -e 's/⢵/EN/g' \ - -e 's/⢶/FN/g' \ - -e 's/⢷/GN/g' \ - -e 's/⣰/HN/g' \ - -e 's/⣱/IN/g' \ - -e 's/⣲/JN/g' \ - -e 's/⣳/KN/g' \ - -e 's/⣴/LN/g' \ - -e 's/⣵/MN/g' \ - -e 's/⣶/NN/g' \ - -e 's/⣷/ON/g' \ - -e 's/⢸/@O/g' \ - -e 's/⢹/AO/g' \ - -e 's/⢺/BO/g' \ - -e 's/⢻/CO/g' \ - -e 's/⢼/DO/g' \ - -e 's/⢽/EO/g' \ - -e 's/⢾/FO/g' \ - -e 's/⢿/GO/g' \ - -e 's/⣸/HO/g' \ - -e 's/⣹/IO/g' \ - -e 's/⣺/JO/g' \ - -e 's/⣻/KO/g' \ - -e 's/⣼/LO/g' \ - -e 's/⣽/MO/g' \ - -e 's/⣾/NO/g' \ - -e 's/⣿/OO/g' \ - \ - -e 's/@*$//' \ - -e 's/^$/'$'\015''/' -e 's/\([^'$'\015'']\)$/\1'$'\015''/' - -# Exit 4-dot graphic mode -printf '\033\006' -# Finish document -printf '\032' - -echo "INFO: Ready" >&2 diff --git a/filter/braille/drivers/index/imageubrltoindexv4.in b/filter/braille/drivers/index/imageubrltoindexv4.in deleted file mode 100755 index cc010302d..000000000 --- a/filter/braille/drivers/index/imageubrltoindexv4.in +++ /dev/null @@ -1,299 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -. @CUPS_SERVERBIN@/filter/indexv4.sh -printf "$INIT" - -# Enter 4-dot graphic mode -# TODO: use image mode instead, to benefit from much better resolution -printf "\033\007" - -echo "INFO: Writing text to Index embosser" >&2 -( if [ -z "$FILE" ] -then - cat -else - cat "$FILE" -fi ) | sed \ - -e 's/⠀/@@/g' \ - -e 's/⠁/A@/g' \ - -e 's/⠂/B@/g' \ - -e 's/⠃/C@/g' \ - -e 's/⠄/D@/g' \ - -e 's/⠅/E@/g' \ - -e 's/⠆/F@/g' \ - -e 's/⠇/G@/g' \ - -e 's/⡀/H@/g' \ - -e 's/⡁/I@/g' \ - -e 's/⡂/J@/g' \ - -e 's/⡃/K@/g' \ - -e 's/⡄/L@/g' \ - -e 's/⡅/M@/g' \ - -e 's/⡆/N@/g' \ - -e 's/⡇/O@/g' \ - -e 's/⠈/@A/g' \ - -e 's/⠉/AA/g' \ - -e 's/⠊/BA/g' \ - -e 's/⠋/CA/g' \ - -e 's/⠌/DA/g' \ - -e 's/⠍/EA/g' \ - -e 's/⠎/FA/g' \ - -e 's/⠏/GA/g' \ - -e 's/⡈/HA/g' \ - -e 's/⡉/IA/g' \ - -e 's/⡊/JA/g' \ - -e 's/⡋/KA/g' \ - -e 's/⡌/LA/g' \ - -e 's/⡍/MA/g' \ - -e 's/⡎/NA/g' \ - -e 's/⡏/OA/g' \ - -e 's/⠐/@B/g' \ - -e 's/⠑/AB/g' \ - -e 's/⠒/BB/g' \ - -e 's/⠓/CB/g' \ - -e 's/⠔/DB/g' \ - -e 's/⠕/EB/g' \ - -e 's/⠖/FB/g' \ - -e 's/⠗/GB/g' \ - -e 's/⡐/HB/g' \ - -e 's/⡑/IB/g' \ - -e 's/⡒/JB/g' \ - -e 's/⡓/KB/g' \ - -e 's/⡔/LB/g' \ - -e 's/⡕/MB/g' \ - -e 's/⡖/NB/g' \ - -e 's/⡗/OB/g' \ - -e 's/⠘/@C/g' \ - -e 's/⠙/AC/g' \ - -e 's/⠚/BC/g' \ - -e 's/⠛/CC/g' \ - -e 's/⠜/DC/g' \ - -e 's/⠝/EC/g' \ - -e 's/⠞/FC/g' \ - -e 's/⠟/GC/g' \ - -e 's/⡘/HC/g' \ - -e 's/⡙/IC/g' \ - -e 's/⡚/JC/g' \ - -e 's/⡛/KC/g' \ - -e 's/⡜/LC/g' \ - -e 's/⡝/MC/g' \ - -e 's/⡞/NC/g' \ - -e 's/⡟/OC/g' \ - -e 's/â  /@D/g' \ - -e 's/â ¡/AD/g' \ - -e 's/â ¢/BD/g' \ - -e 's/â £/CD/g' \ - -e 's/â ¤/DD/g' \ - -e 's/â ¥/ED/g' \ - -e 's/â ¦/FD/g' \ - -e 's/â §/GD/g' \ - -e 's/â¡ /HD/g' \ - -e 's/â¡¡/ID/g' \ - -e 's/â¡¢/JD/g' \ - -e 's/â¡£/KD/g' \ - -e 's/⡤/LD/g' \ - -e 's/â¡¥/MD/g' \ - -e 's/⡦/ND/g' \ - -e 's/â¡§/OD/g' \ - -e 's/â ¨/@E/g' \ - -e 's/â ©/AE/g' \ - -e 's/â ª/BE/g' \ - -e 's/â «/CE/g' \ - -e 's/â ¬/DE/g' \ - -e 's/â ­/EE/g' \ - -e 's/â ®/FE/g' \ - -e 's/â ¯/GE/g' \ - -e 's/⡨/HE/g' \ - -e 's/â¡©/IE/g' \ - -e 's/⡪/JE/g' \ - -e 's/â¡«/KE/g' \ - -e 's/⡬/LE/g' \ - -e 's/â¡­/ME/g' \ - -e 's/â¡®/NE/g' \ - -e 's/⡯/OE/g' \ - -e 's/â °/@F/g' \ - -e 's/â ±/AF/g' \ - -e 's/â ²/BF/g' \ - -e 's/â ³/CF/g' \ - -e 's/â ´/DF/g' \ - -e 's/â µ/EF/g' \ - -e 's/â ¶/FF/g' \ - -e 's/â ·/GF/g' \ - -e 's/â¡°/HF/g' \ - -e 's/⡱/IF/g' \ - -e 's/⡲/JF/g' \ - -e 's/⡳/KF/g' \ - -e 's/â¡´/LF/g' \ - -e 's/⡵/MF/g' \ - -e 's/â¡¶/NF/g' \ - -e 's/â¡·/OF/g' \ - -e 's/â ¸/@G/g' \ - -e 's/â ¹/AG/g' \ - -e 's/â º/BG/g' \ - -e 's/â »/CG/g' \ - -e 's/â ¼/DG/g' \ - -e 's/â ½/EG/g' \ - -e 's/â ¾/FG/g' \ - -e 's/â ¿/GG/g' \ - -e 's/⡸/HG/g' \ - -e 's/⡹/IG/g' \ - -e 's/⡺/JG/g' \ - -e 's/â¡»/KG/g' \ - -e 's/⡼/LG/g' \ - -e 's/⡽/MG/g' \ - -e 's/⡾/NG/g' \ - -e 's/â¡¿/OG/g' \ - -e 's/⢀/@H/g' \ - -e 's/⢁/AH/g' \ - -e 's/⢂/BH/g' \ - -e 's/⢃/CH/g' \ - -e 's/⢄/DH/g' \ - -e 's/⢅/EH/g' \ - -e 's/⢆/FH/g' \ - -e 's/⢇/GH/g' \ - -e 's/⣀/HH/g' \ - -e 's/⣁/IH/g' \ - -e 's/⣂/JH/g' \ - -e 's/⣃/KH/g' \ - -e 's/⣄/LH/g' \ - -e 's/⣅/MH/g' \ - -e 's/⣆/NH/g' \ - -e 's/⣇/OH/g' \ - -e 's/⢈/@I/g' \ - -e 's/⢉/AI/g' \ - -e 's/⢊/BI/g' \ - -e 's/⢋/CI/g' \ - -e 's/⢌/DI/g' \ - -e 's/⢍/EI/g' \ - -e 's/⢎/FI/g' \ - -e 's/⢏/GI/g' \ - -e 's/⣈/HI/g' \ - -e 's/⣉/II/g' \ - -e 's/⣊/JI/g' \ - -e 's/⣋/KI/g' \ - -e 's/⣌/LI/g' \ - -e 's/⣍/MI/g' \ - -e 's/⣎/NI/g' \ - -e 's/⣏/OI/g' \ - -e 's/⢐/@J/g' \ - -e 's/⢑/AJ/g' \ - -e 's/⢒/BJ/g' \ - -e 's/⢓/CJ/g' \ - -e 's/⢔/DJ/g' \ - -e 's/⢕/EJ/g' \ - -e 's/⢖/FJ/g' \ - -e 's/⢗/GJ/g' \ - -e 's/⣐/HJ/g' \ - -e 's/⣑/IJ/g' \ - -e 's/⣒/JJ/g' \ - -e 's/⣓/KJ/g' \ - -e 's/⣔/LJ/g' \ - -e 's/⣕/MJ/g' \ - -e 's/⣖/NJ/g' \ - -e 's/⣗/OJ/g' \ - -e 's/⢘/@K/g' \ - -e 's/⢙/AK/g' \ - -e 's/⢚/BK/g' \ - -e 's/⢛/CK/g' \ - -e 's/⢜/DK/g' \ - -e 's/⢝/EK/g' \ - -e 's/⢞/FK/g' \ - -e 's/⢟/GK/g' \ - -e 's/⣘/HK/g' \ - -e 's/⣙/IK/g' \ - -e 's/⣚/JK/g' \ - -e 's/⣛/KK/g' \ - -e 's/⣜/LK/g' \ - -e 's/⣝/MK/g' \ - -e 's/⣞/NK/g' \ - -e 's/⣟/OK/g' \ - -e 's/⢠/@L/g' \ - -e 's/⢡/AL/g' \ - -e 's/⢢/BL/g' \ - -e 's/⢣/CL/g' \ - -e 's/⢤/DL/g' \ - -e 's/⢥/EL/g' \ - -e 's/⢦/FL/g' \ - -e 's/⢧/GL/g' \ - -e 's/⣠/HL/g' \ - -e 's/⣡/IL/g' \ - -e 's/⣢/JL/g' \ - -e 's/⣣/KL/g' \ - -e 's/⣤/LL/g' \ - -e 's/⣥/ML/g' \ - -e 's/⣦/NL/g' \ - -e 's/⣧/OL/g' \ - -e 's/⢨/@M/g' \ - -e 's/⢩/AM/g' \ - -e 's/⢪/BM/g' \ - -e 's/⢫/CM/g' \ - -e 's/⢬/DM/g' \ - -e 's/⢭/EM/g' \ - -e 's/⢮/FM/g' \ - -e 's/⢯/GM/g' \ - -e 's/⣨/HM/g' \ - -e 's/⣩/IM/g' \ - -e 's/⣪/JM/g' \ - -e 's/⣫/KM/g' \ - -e 's/⣬/LM/g' \ - -e 's/⣭/MM/g' \ - -e 's/⣮/NM/g' \ - -e 's/⣯/OM/g' \ - -e 's/⢰/@N/g' \ - -e 's/⢱/AN/g' \ - -e 's/⢲/BN/g' \ - -e 's/⢳/CN/g' \ - -e 's/⢴/DN/g' \ - -e 's/⢵/EN/g' \ - -e 's/⢶/FN/g' \ - -e 's/⢷/GN/g' \ - -e 's/⣰/HN/g' \ - -e 's/⣱/IN/g' \ - -e 's/⣲/JN/g' \ - -e 's/⣳/KN/g' \ - -e 's/⣴/LN/g' \ - -e 's/⣵/MN/g' \ - -e 's/⣶/NN/g' \ - -e 's/⣷/ON/g' \ - -e 's/⢸/@O/g' \ - -e 's/⢹/AO/g' \ - -e 's/⢺/BO/g' \ - -e 's/⢻/CO/g' \ - -e 's/⢼/DO/g' \ - -e 's/⢽/EO/g' \ - -e 's/⢾/FO/g' \ - -e 's/⢿/GO/g' \ - -e 's/⣸/HO/g' \ - -e 's/⣹/IO/g' \ - -e 's/⣺/JO/g' \ - -e 's/⣻/KO/g' \ - -e 's/⣼/LO/g' \ - -e 's/⣽/MO/g' \ - -e 's/⣾/NO/g' \ - -e 's/⣿/OO/g' \ - \ - -e 's/@*$//' \ - -e 's/^$/'$'\015''/' -e 's/\([^'$'\015'']\)$/\1'$'\015''/' - -# Exit 4-dot graphic mode -printf '\033\006' -# Finish document -printf '\032' - -echo "INFO: Ready" >&2 diff --git a/filter/braille/drivers/index/index.defs b/filter/braille/drivers/index/index.defs deleted file mode 100644 index 79349021e..000000000 --- a/filter/braille/drivers/index/index.defs +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (c) 2015-2016, 2018 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -Throughput 1 - -MediaSize Legal -MediaSize Letter -MediaSize A3 -MediaSize A4 -*MediaSize A4TF -MediaSize A5 -MediaSize 110x115 -MediaSize 110x120 -MediaSize 110x170 -MediaSize 115x110 -MediaSize 120x120 - -ColorDevice no - -Group "Index/Index support" - Option "IndexMultipleImpact/Multiple Impact" PickOne AnySetup 10 - *Choice "1" "" - Choice "2" "" - Choice "3" "" - -Group "Braille" - Option "IndexTable/Firmware Braille Table" PickOne AnySetup 10 - *Choice "0/6-dot MIT table" "" - Choice "1/User-defined Table 1" "" - Choice "2/User-defined Table 2" "" - Choice "3/User-defined Table 3" "" - Choice "4/User-defined Table 4" "" diff --git a/filter/braille/drivers/index/index.sh.in b/filter/braille/drivers/index/index.sh.in deleted file mode 100644 index 442242caf..000000000 --- a/filter/braille/drivers/index/index.sh.in +++ /dev/null @@ -1,126 +0,0 @@ -# -# Copyright (c) 2015-2016, 2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -. @CUPS_DATADIR@/braille/cups-braille.sh - -FIRMWARE=$(getOptionNumber IndexFirmwareVersion) -[ $? = 0 ] || exit 1 -DUPLEX=$(getOption Duplex) -[ -n "$DUPLEX" ] || DUPLEX=None -ZFOLDING=$(getOption ZFolding) -[ -n "$ZFOLDING" ] || ZFOLDING=False -SIDEWAYS=$(getOption SideWays) -[ -n "$SIDEWAYS" ] || SIDEWAYS=False -SADDLESTITCH=$(getOption SaddleStitch) -[ -n "$SADDLESTITCH" ] || SADDLESTITCH=False -TABLE=$(getOptionNumber IndexTable) -[ $? = 0 ] || exit 1 -MULTIPLEIMPACT=$(getOptionNumber IndexMultipleImpact) -[ $? = 0 ] || exit 1 -HWPAGENUMBER=$(getOption HardwarePageNumber) - -# Convert from 100th of mm to Inch fraction -mmToIndexIn () { - # 100th of mm - MM=$1 - - # 120th of inches - IN120=$(($MM * 12 / 254)) - - # Integer part - INT=$(($IN120 / 120 )) - - # Fractional part, first in 120th of inch - FRAC=$(($IN120 % 120)) - - # Convert to Index-specific values - if [ $FRAC -lt 30 ]; then - # Round down to zero - FRAC=0 - elif [ $FRAC -ge 30 -a $FRAC -lt 40 ]; then - # Round down to a quarter - FRAC=1 - elif [ $FRAC -ge 40 -a $FRAC -lt 60 ]; then - # Round down to a third - FRAC=2 - elif [ $FRAC -ge 60 -a $FRAC -lt 80 ]; then - # Round down to a half - FRAC=3 - elif [ $FRAC -ge 80 -a $FRAC -lt 90 ]; then - # Round down to two thirds - FRAC=4 - else - # Round down to three quarters - FRAC=5 - fi - - echo $INT$FRAC -} - -# Return options common to v3 and v4 -commonOptions() { - INIT= - # Disable options we don't want: first line offset and page numbering - INIT+=,FO0 - - # Support hardware-assisted multiple copies - if [ $NB != 1 ] - then - INIT+=,MC$NB - fi - - INIT+=,MI$MULTIPLEIMPACT - - case "$DUPLEX" in - None|DuplexNoTumble) ;; - *) printf "ERROR: duplex mode %s is not supported\n" "$DUPLEX" >&2 ; exit 1;; - esac - - case "$DUPLEX,$ZFOLDING,$SADDLESTITCH,$SIDEWAYS" in - None,False,False,False) INIT+=,DP1 ;; # Single Sided - DuplexNoTumble,False,False,False) INIT+=,DP2 ;; # Double Sided - - DuplexNoTumble,True,False,False) INIT+=,DP3 ;; # Double Sided z-folding - None,True,False,False) INIT+=,DP5 ;; # Single Sided z-folding - - DuplexNoTumble,False,True,False) INIT+=,DP4 ;; # Double Sided Saddle Stitch - None,False,True,False) INIT+=,DP8 ;; # Single Sided Saddle Stitch - - DuplexNoTumble,True,False,True) INIT+=,DP6 ;; # Double Sided z-folding sideways - None,True,False,True) INIT+=,DP7 ;; # Single Sided z-folding sideways - - *) printf "ERROR: unsupported page folding: duplex=%s z-folding=%s sideways=%s saddlestich=%s\n" "$DUPLEX" "$ZFOLDING" "$SIDEWAYS" "$SADDLESTITCH" >&2 ; exit 1 ;; - esac - - # Configure dots spacing - case "$TEXTDOTDISTANCE" in - 220) INIT+=,TD1 ;; - 250) INIT+=,TD0 ;; - 320) INIT+=,TD2 ;; - *) printf "ERROR: unsupported '%s' text dot distance\n" "$TEXTDOTDISTANCE" >&2 ; exit 1 ;; - esac - case "$GRAPHICDOTDISTANCE" in - 160) INIT+=,GD2 ;; - 200) INIT+=,GD0 ;; - 250) INIT+=,GD1 ;; - *) printf "ERROR: unsupported '%s' graphic dot distance\n" "$GRAPHICDOTDISTANCE" >&2 ; exit 1 ;; - esac - - # Currently unused, implemented in software - case "$HWPAGENUMBER" in - None|'') INIT+=,PN0 ;; - Top) INIT+=,PN1 ;; - TopLeft) INIT+=,PN2 ;; - TopRight) INIT+=,PN3 ;; - Bottom) INIT+=,PN4 ;; - BottomLeft) INIT+=,PN5 ;; - BottomRight) INIT+=,PN6 ;; - *) printf "ERROR: unsupported %s page number\n" "$HWPAGENUMBER" >&2 ; exit 1 ;; - esac - - echo "$INIT" -} diff --git a/filter/braille/drivers/index/indexv3.sh.in b/filter/braille/drivers/index/indexv3.sh.in deleted file mode 100644 index a7528c869..000000000 --- a/filter/braille/drivers/index/indexv3.sh.in +++ /dev/null @@ -1,88 +0,0 @@ -# -# Copyright (c) 2015-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -. @CUPS_DATADIR@/braille/index.sh - -# Whether lengths should be given to the embosser in In or Mm -PAPERLENGTH=$(getAttribute IndexPaperLength) - -if [ $FIRMWARE -ge 103000 ] -then - # Firmware 10.30 and above support temporary parameters - INIT=$'\033'D - - # Margins are implemented in software - INIT+=TM0,BI0 - - # Trying to disable banner page seems to pose problems - #INIT+=,BP - - # Common options - INIT+="$(commonOptions)" - [ $? = 0 ] || exit 1 - - # Paper size - case "$PAPERLENGTH" in - In) - INIT+=,PW$(mmToIndexIn $PAGEWIDTH),PL$(mmToIndexIn $PAGEHEIGHT) - ;; - Mm) - INIT+=,PW$(($PAGEWIDTH / 100)),PL$(($PAGEHEIGHT / 100)) - ;; - *) ;; - esac - - case $LINESPACING in - 250) INIT+=,LS0 ;; - 375) INIT+=,LS1 ;; - 450) INIT+=,LS2 ;; - 475) INIT+=,LS3 ;; - 500) INIT+=,LS4 ;; - 525) INIT+=,LS5 ;; - 550) INIT+=,LS6 ;; - 750) INIT+=,LS7 ;; - 1000) INIT+=,LS8 ;; - *) - if [ $FIRMWARE -lt 120130 ] - then - echo "ERROR: unsupported $LINESPACING line spacing, please upgrade firmware to at least 12.01.3" >&2 - exit 1 - fi - if [ $LINESPACING -lt 100 ] - then - echo "ERROR: too small $LINESPACING line spacing" >&2 - exit 1 - fi - INIT+=,LS$(($LINESPACING / 10)) - ;; - esac - - if [ $LIBLOUIS1 != None -o \ - $LIBLOUIS2 != None -o \ - $LIBLOUIS3 != None -o \ - $LIBLOUIS4 != None ] - then - # software-translated, enforce a 6-dot table if needed - case $TEXTDOTS in - # Firmware 11.02.1 and above allow to make sure to be using a 6-dot table - 6) INIT+=,BT0 ;; - # Hoping the user properly configured an 8-dot table - 8) ;; - *) echo "ERROR: unsupported $TEXTDOTS dots" >&2 ; exit 1 ;; - esac - else - # Hoping the user configured a table with appropriate number of dots - INIT+=,BT$TABLE - fi - - # roger - INIT+=";" -else - # No support for temporary parameters. Hoping that the user configured CUPS - # the same way as the embosser. - INIT= -fi diff --git a/filter/braille/drivers/index/indexv4.sh.in b/filter/braille/drivers/index/indexv4.sh.in deleted file mode 100644 index 9052ce81d..000000000 --- a/filter/braille/drivers/index/indexv4.sh.in +++ /dev/null @@ -1,58 +0,0 @@ -# -# Copyright (c) 2015-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -. @CUPS_DATADIR@/braille/index.sh - -if [ $FIRMWARE -ge 103000 ] -then - # Firmware 10.30 and above support temporary parameters - INIT=$'\033'D - - # Margins are implemented in software - INIT+=TM0,BI0 - - # Common options - INIT+="$(commonOptions)" - [ $? = 0 ] || exit 1 - - # Paper size - INIT+=,CH$PRINTABLETEXTWIDTH,LP$PRINTABLETEXTHEIGHT - - case $LINESPACING in - 500) INIT+=,LS50 ;; - 1000) INIT+=,LS100 ;; - *) - echo "ERROR: unsupported $LINESPACING line spacing" >&2 - exit 1 - ;; - esac - - if [ $LIBLOUIS1 != None -o \ - $LIBLOUIS2 != None -o \ - $LIBLOUIS3 != None -o \ - $LIBLOUIS4 != None ] - then - # software-translated, enforce a 6-dot table if needed - case $TEXTDOTS in - # Firmware 11.02.1 and above allow to make sure to be using a 6-dot table - 6) INIT+=,BT0 ;; - # Firmware 11.02.1 and above allow to make sure to be using a 8-dot table - 8) INIT+=,BT6 ;; - *) echo "ERROR: unsupported $TEXTDOTS dots" >&2 ; exit 1 ;; - esac - else - # Hoping the user configured a table with appropriate number of dots - INIT+=,BT$TABLE - fi - - # roger - INIT+=";" -else - # No support for temporary parameters. Hoping that the user configured CUPS - # the same way as the embosser. - INIT= -fi diff --git a/filter/braille/drivers/index/textbrftoindexv3.in b/filter/braille/drivers/index/textbrftoindexv3.in deleted file mode 100755 index ec6665f9e..000000000 --- a/filter/braille/drivers/index/textbrftoindexv3.in +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015-2018, 2021 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -case $0 in - *indexv3*) . @CUPS_DATADIR@/braille/indexv3.sh ;; - *indexv4*) . @CUPS_DATADIR@/braille/indexv4.sh ;; - *) echo "ERROR: $0 must be called as somethingindexv3 or somethingindexv4" >&2 - exit 1 - ;; -esac - -printf "$INIT" - -if [ $LIBLOUIS1 != None -o \ - $LIBLOUIS2 != None -o \ - $LIBLOUIS3 != None -o \ - $LIBLOUIS4 != None ] -then - # software-translated, send to printer in transparent mode - echo "INFO: Writing text to Index embosser in transparent mode" >&2 - if [ -z "$FILE" ] - then - cat - else - cat "$FILE" - fi | ( - IFS=$'\n' read -r LINE - EOF=$? - while [ "$EOF" = 0 -o -n "$LINE" ] - do - # Strip CRs - LINE=${LINE//$'\015'} - # Ignore SUBs - LINE=${LINE//$'\032'} - # Turn non-breakable spaces into spaces - LINE=${LINE//$'\302'$'\240'/ } - LINE=${LINE//$'\240'/ } - - # Interpret FFs - while [ -n "$LINE" -a -z "${LINE/#$'\014'*}" ] - do - printf "\\f" - LINE=${LINE#$'\014'} - done - - # Make sure there is nothing else we don't process - if [ -n "$LINE" ] - then - if [ -z "${LINE/*[$'\000'$'\001'-$'\037'$'\177']*}" ] - then - echo "ERROR: unsupported control character in BRF file" >&2 - fi - if [ -z "${LINE/*[$'\200'-$'\377']*}" ] - then - echo "ERROR: unsupported non-ASCII character in BRF file" >&2 - fi - fi - - CHARS=$(printf %s "$LINE" | wc -c) - if [ "$CHARS" -gt 127 ] - then - # Index printers have a bug with numbers between 128 and 255 in - # transparent mode escape sequence. This is normally not a problem since - # 128 chars is more than a line worth of text - echo "ERROR: Line too long ($CHARS)" >&2 - exit 1 - fi - if [ "$CHARS" -gt 0 ] - then - # Enter transparent mode for $CHARS characters - printf "\\033\\\\\\x$(printf %02x $CHARS)\\x00" - # Echo those $CHARS characters. - # First normalize BRF characters (`a-z{|}~ are non-standard). - # Drop unicode patterns with dots 7 or 8 in case the liblouis table happened to erroneously emit one - # Drop remaining erroneous multibyte characters - # Drop remaining erroneous 1-byte characters - # Then turn it into Index 6dots sequences. - printf %s "$LINE" | \ - tr "\`a-z{|}~" "@A-Z\\[\\\\]_" | \ - sed -e 's/[⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿]/ /g' | \ - sed -e "s/[^] \!\"#$%&'()*+,.\\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\\\^_[-]/ /g" | \ - tr "\000-\037\177\200-\377" " " | \ - tr " \!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[\\\\\\]^_" "\\0V tSQW"$'\004'"gvAT@DP"$'\024'"d"$'\002\006'"\"bB&fF\$a\`Cw4q"$'\020\001\003\021'"1!"$'\023'"3#"$'\022'"2"$'\005\007\025'"5%"$'\027'"7'"$'\026'"6EGrUueRcs0p" - fi - if [ "$EOF" = 0 ] - then - printf "\\r\\n" - fi - IFS=$'\n' read -r LINE - EOF=$? - done - ) - if [ $? != 0 ] - then - printf '\032' - exit 1 - fi -else - # not software-translated, send to printer as such - echo "INFO: Writing text to Index embosser" >&2 - if [ -z "$FILE" ] - then - cat - else - cat "$FILE" - fi -fi - -printf '\032' - -echo "INFO: Ready" >&2 diff --git a/filter/braille/drivers/index/ubrlto4dot.c b/filter/braille/drivers/index/ubrlto4dot.c deleted file mode 100644 index bd85b2601..000000000 --- a/filter/braille/drivers/index/ubrlto4dot.c +++ /dev/null @@ -1,28 +0,0 @@ -// -// Character conversion table generator for imageubrltoindexv[34] -// -// Copyright (c) 2015 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include - -int -main(int argc, - char *argv[]) -{ - int i, j, lo, hi; - setlocale(LC_ALL, ""); - for (i = 0; i < 256; i ++) - { - j = (i & 0x7) | ((i & 0x8) << 3) | ((i & 0x70) >> 1) | (i & 0x80); - lo = i & 0xf; - hi = (i & 0xf0) >> 4; - printf(" -e 's/%lc/%c%c/g' \\\n", 0x2800 + j, '@' + lo, '@' + hi); - } - return (0); -} diff --git a/filter/braille/filters/TODO.txt b/filter/braille/filters/TODO.txt deleted file mode 100644 index 43bf90a4d..000000000 --- a/filter/braille/filters/TODO.txt +++ /dev/null @@ -1,3 +0,0 @@ -Add title option for pictures - -In addition to drawing contour, use textures to render colors of areas diff --git a/filter/braille/filters/braille.defs b/filter/braille/filters/braille.defs deleted file mode 100644 index 9edbd9b67..000000000 --- a/filter/braille/filters/braille.defs +++ /dev/null @@ -1,115 +0,0 @@ -// -// Copyright (c) 2015, 2017-2018 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -Group "Braille/Braille transcription" -Option "TextDotDistance/Text dot distance" PickOne AnySetup 10 - Choice "220/2.2mm" "" - *Choice "250/2.5mm" "" - Choice "320/3.2mm" "" - -Option "TextDots/Text dots" PickOne AnySetup 10 - Choice "8/8 dots" "" - *Choice "6/6 dots" "" - -Option "LineSpacing/Line spacing" PickOne AnySetup 10 - Choice "250/2.5mm" "" - Choice "375/3.75mm" "" - Choice "450/4.5mm" "" - Choice "475/4.75mm" "" - *Choice "500/5.0mm" "" - Choice "525/5.25mm" "" - Choice "550/5.5mm" "" - Choice "750/7.5mm" "" - Choice "1000/10.0mm" "" - -Option "TopMargin/Top margin" PickOne AnySetup 10 - Choice "0" "" - Choice "1" "" - *Choice "2" "" - Choice "3" "" - Choice "4" "" - Choice "5" "" - Choice "6" "" - Choice "7" "" - Choice "8" "" - Choice "9" "" - Choice "10" "" - -Option "BottomMargin/Bottom margin" PickOne AnySetup 10 - Choice "0" "" - Choice "1" "" - *Choice "2" "" - Choice "3" "" - Choice "4" "" - Choice "5" "" - Choice "6" "" - Choice "7" "" - Choice "8" "" - Choice "9" "" - Choice "10" "" - -Option "LeftMargin/Left margin" PickOne AnySetup 10 - Choice "0" "" - Choice "1" "" - *Choice "2" "" - Choice "3" "" - Choice "4" "" - Choice "5" "" - Choice "6" "" - Choice "7" "" - Choice "8" "" - Choice "9" "" - Choice "10" "" - -Option "RightMargin/Right margin" PickOne AnySetup 10 - Choice "0" "" - Choice "1" "" - *Choice "2" "" - Choice "3" "" - Choice "4" "" - Choice "5" "" - Choice "6" "" - Choice "7" "" - Choice "8" "" - Choice "9" "" - Choice "10" "" - -Option "BraillePageNumber/Braille Page Number" PickOne AnySetup 10 - Choice "None/None" "" - Choice "TopMargin/Top Margin" "" - Choice "TopInline/Top Inline" "" - *Choice "BottomMargin/Bottom Margin" "" - Choice "BottomInline/Bottom Inline" "" - -Option "PrintPageNumber/Print Page Number" PickOne AnySetup 10 - Choice "None/None" "" - *Choice "TopMargin/Top Margin" "" - Choice "TopInline/Top Inline" "" - Choice "BottomMargin/Bottom Margin" "" - Choice "BottomInline/Bottom Inline" "" - -Option "PageSeparator/Print Page Separator" PickOne AnySetup 10 - *Choice "True/Yes" "" - Choice "False/No" "" - -Option "PageSeparatorNumber/Print Page Number in Separator" PickOne AnySetup 10 - *Choice "True/Yes" "" - Choice "False/No" "" - -Option "ContinuePages/Prefix Print Page Number with Braille Page Number" PickOne AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - -UIConstraints "*TopMargin 0 *BraillePageNumber TopMargin" -UIConstraints "*TopMargin 0 *PrintPageNumber TopMargin" -UIConstraints "*BottomMargin 0 *BraillePageNumber BottomMargin" -UIConstraints "*BottomMargin 0 *PrintPageNumber BottomMargin" - -UIConstraints "*BraillePageNumber TopMargin *PrintPageNumber TopInline" -UIConstraints "*BraillePageNumber BottomMargin *PrintPageNumber BottomInline" -UIConstraints "*BraillePageNumber TopInline *PrintPageNumber TopMargin" -UIConstraints "*BraillePageNumber BottomInline *PrintPageNumber BottomMargin" diff --git a/filter/braille/filters/brftopagedbrf.in b/filter/braille/filters/brftopagedbrf.in deleted file mode 100755 index 4dafad281..000000000 --- a/filter/braille/filters/brftopagedbrf.in +++ /dev/null @@ -1,140 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -. @CUPS_DATADIR@/braille/cups-braille.sh - -shopt -s extglob - -# Construct list of pages from PAGERANGES - -PAGES=" " # Explicit list of pages -AFTER= # All pages including and after this -BEFORE= # All pages before and including this - -[ -n "$PAGERANGES" ] || PAGERANGES="1-" - -while [ -n "${PAGERANGES}" ] -do - PAGERANGE=${PAGERANGES/,*} - PAGERANGE=${PAGERANGE%%*( )} - PAGERANGE=${PAGERANGE##*( )} - if [ -n "${PAGERANGES/*,*}" ] - then - # last range - PAGERANGES="" - else - # Remove leading range - PAGERANGES="${PAGERANGES#*,}" - fi - - if [ -n "${PAGERANGE/*-*}" ] - then - # single-page - PAGES="$PAGES$PAGERANGE " - - elif [ -z "${PAGERANGE%%*-}" ] - then - # To the end - FIRST=${PAGERANGE%%-*} - if [ -z "$AFTER" ] || [ "$FIRST" -lt "$AFTER" ] - then - AFTER="$FIRST" - fi - - elif [ -z "${PAGERANGE##-*}" ] - then - # From the beginning - LAST=${PAGERANGE##*-} - if [ -z "$BEFORE" ] || [ "$LAST" -gt "$BEFORE" ] - then - BEFORE="$LAST" - fi - - else - # page range - FIRST=${PAGERANGE/-*} - LAST=${PAGERANGE/*-} - PAGES="$PAGES$(seq "$FIRST" "$LAST" | tr $'\012' ' ')" - - fi -done - -# Determine whether to print this page -doprint() { - PAGE="$1" - if [ -n "$BEFORE" ] && [ "$PAGE" -le "$BEFORE" ] - then - echo 1 - return - elif [ -n "$AFTER" ] && [ "$PAGE" -ge "$AFTER" ] - then - echo 1 - return - fi - case "$PAGES" in - *\ $PAGE\ *) - echo 1 - return - ;; - esac - echo 0 -} - -if [ -z "$FILE" ] -then - cat -else - cat "$FILE" -fi | ( - P=1 - DOPRINT=$(doprint $P) - IFS=$'\n' read -r LINE - EOF=$? - while [ "$EOF" = 0 -o -n "$LINE" ] - do - while [ -n "$LINE" -a -z "${LINE/*$'\014'*}" ] - do - # Possibly print before FF - HEAD=${LINE%%$'\014'*} - [ $DOPRINT == 0 ] || printf %s "$HEAD"$'\014' - - # Next page - P=$(($P + 1)) - DOPRINT=$(doprint $P) - - # Drop head of line - LINE=${LINE#*$'\014'} - done - - # Remainder of line - if [ "$EOF" = 0 ] - then - NL="\n" - else - NL="" - fi - [ $DOPRINT == 0 ] || printf "%s$NL" "$LINE" - - IFS=$'\n' read -r LINE - EOF=$? - done -) - -echo "INFO: Ready" >&2 -exit 0 diff --git a/filter/braille/filters/cups-braille.sh.in b/filter/braille/filters/cups-braille.sh.in deleted file mode 100644 index fac15e7f3..000000000 --- a/filter/braille/filters/cups-braille.sh.in +++ /dev/null @@ -1,481 +0,0 @@ -# -# Copyright (c) 2015-2018, 2022 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Get an attribute from the ppd file -getAttribute () { - ATTRIBUTE=$1 - VALUE=`grep "^\*$ATTRIBUTE:" "$PPD" | cut -d" " -f2-` - VALUE=${VALUE##\"} - VALUE=${VALUE%%\"} - printf "DEBUG: Attribute $ATTRIBUTE is '%s'\n" "$VALUE" >&2 - printf "%s" "$VALUE" -} - -# Get an option for the document: either default ppd attribute or user-provided value -getOption () { - OPTION=$1 - VALUE=$(getAttribute Default$OPTION) - printf "DEBUG: Default $OPTION is '%s'\n" "$VALUE" >&2 - - if [ -n "$OPTIONS" ] - then - # Case of the very first option - if [ -z "${OPTIONS/$OPTION=*}" ] - then - VALUE=${OPTIONS#$OPTION=} - VALUE=${VALUE%% *} - printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2 - fi - # Case of other options - if [ -z "${OPTIONS/* $OPTION=*}" ] - then - VALUE=${OPTIONS##* $OPTION=} - VALUE=${VALUE%% *} - printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2 - fi - - # Boolean options - if [ -z "${OPTIONS/* $OPTION *}" ] - then - VALUE=True - printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2 - fi - if [ -z "${OPTIONS/* no$OPTION *}" ] - then - VALUE=False - printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2 - fi - fi - - printf "%s" "$VALUE" -} - -# Get an option for the document and check that it is a number -getOptionNumber () { - OPTION=$1 - VALUE=$(getOption $OPTION) - VALUE=${VALUE#Custom.} - case "$VALUE" in - [0-9]*) ;; - *) printf "ERROR: Option $OPTION must be a number, got '%s'\n" "$VALUE" >&2 - exit 1 - ;; - esac - printf "%s" "$VALUE" -} - -# Printing options: number of copies and page ranges -[ -z "$NB" ] && NB=1 -PAGERANGES=$(getOption page-ranges) - -# -# Page size -# Units in 100th of mm -# - -# TODO: better handle imageable area -PAGESIZE=$(getOption PageSize) -case "$PAGESIZE" in - Legal) - PAGEWIDTH=21590 - PAGEHEIGHT=35560 - ;; - Letter) - PAGEWIDTH=21590 - PAGEHEIGHT=27940 - ;; - A3) - PAGEWIDTH=29700 - PAGEHEIGHT=42000 - ;; - A4) - PAGEWIDTH=21000 - PAGEHEIGHT=29700 - ;; - A4TF) - PAGEWIDTH=21000 - PAGEHEIGHT=30480 - ;; - A5) - PAGEWIDTH=14850 - PAGEHEIGHT=21000 - ;; - 110x115) - PAGEWIDTH=27940 - PAGEHEIGHT=29210 - ;; - 110x120) - PAGEWIDTH=27940 - PAGEHEIGHT=30480 - ;; - 110x170) - PAGEWIDTH=27940 - PAGEHEIGHT=43180 - ;; - 115x110) - PAGEWIDTH=29210 - PAGEHEIGHT=27940 - ;; - 120x120) - PAGEWIDTH=30480 - PAGEHEIGHT=30480 - ;; - *) - printf "ERROR: Unknown page size '%s'\n" "$PAGESIZE" >&2 - exit 1 - ;; -esac - -# Margins as announced by embosser -HWMARGINS=$(getAttribute HWMargins) -echo "DEBUG: HW margins are $HWMARGINS" >&2 -HWMARGIN_LEFT="${HWMARGINS%% *}" -HWMARGINS="${HWMARGINS#* }" -HWMARGIN_BOTTOM="${HWMARGINS%% *}" -HWMARGINS="${HWMARGINS#* }" -HWMARGIN_RIGHT="${HWMARGINS%% *}" -HWMARGINS="${HWMARGINS#* }" -HWMARGIN_TOP="${HWMARGINS%% *}" - -# Convert from points (1/72 of inch) to 1/100th of mm -points2mm() { - # First get an integer so bash can compute - # I.e. convert to 10^15th of point - # Note: bash's integer computation will work until about 1000mm, that's plenty :) - POINTS="$1" - INT_POINTS="${POINTS%.*}" - if [ "$INT_POINTS" = "$POINTS" ] - then - FRAC_POINTS=000000000000000 - else - FRAC_POINTS="${POINTS#*.}000000000000000" - fi - FRAC_POINTS="${FRAC_POINTS:0:15}" - FRAC_POINTS="$INT_POINTS$FRAC_POINTS" - # Then we can compute conversion - # We round up to be safe - FRAC_INCH=$((($FRAC_POINTS + 71) / 72)) - FRAC_CM=$((($FRAC_INCH * 254 + 99) / 100)) - HUNDRENDTH_MM=$((($FRAC_CM + 999999999999) / 1000000000000)) - echo $HUNDRENDTH_MM -} - -MARGIN_LEFT=$(points2mm "$HWMARGIN_LEFT") -MARGIN_RIGHT=$(points2mm "$HWMARGIN_RIGHT") -MARGIN_TOP=$(points2mm "$HWMARGIN_TOP") -MARGIN_BOTTOM=$(points2mm "$HWMARGIN_BOTTOM") - -# Margins requested by user -PAGE_LEFT=$(getOptionNumber page-left) -[ "$?" = 0 ] || exit 1 -PAGE_LEFT=$(points2mm $PAGE_LEFT) -PAGE_RIGHT=$(getOptionNumber page-right) -[ "$?" = 0 ] || exit 1 -PAGE_RIGHT=$(points2mm $PAGE_RIGHT) -PAGE_TOP=$(getOptionNumber page-top) -[ "$?" = 0 ] || exit 1 -PAGE_TOP=$(points2mm $PAGE_TOP) -PAGE_BOTTOM=$(getOptionNumber page-bottom) -[ "$?" = 0 ] || exit 1 -PAGE_BOTTOM=$(points2mm $PAGE_BOTTOM) - -[ -n "$PAGE_LEFT" -a "$MARGIN_LEFT" -le "$PAGE_LEFT" ] || PAGE_LEFT=$MARGIN_LEFT -[ -n "$PAGE_RIGHT" -a "$MARGIN_RIGHT" -le "$PAGE_RIGHT" ] || PAGE_RIGHT=$MARGIN_RIGHT -[ -n "$PAGE_TOP" -a "$MARGIN_TOP" -le "$PAGE_TOP" ] || PAGE_TOP=$MARGIN_TOP -[ -n "$PAGE_BOTTOM" -a "$MARGIN_BOTTOM" -le "$PAGE_BOTTOM" ] || PAGE_BOTTOM=$MARGIN_BOTTOM - -echo "DEBUG: hard margins are left $MARGIN_LEFT right $MARGIN_RIGHT top $MARGIN_TOP bottom $MARGIN_BOTTOM" >&2 -echo "DEBUG: graphical margins are left $PAGE_LEFT right $PAGE_RIGHT top $PAGE_TOP bottom $PAGE_BOTTOM" >&2 - -# This is the hardware-printable area -PRINTABLEWIDTH=$(($PAGEWIDTH - $MARGIN_LEFT - $MARGIN_RIGHT)) -PRINTABLEHEIGHT=$(($PAGEHEIGHT - $MARGIN_TOP - $MARGIN_BOTTOM)) - -echo "DEBUG: printable area is ${PRINTABLEWIDTH}x${PRINTABLEHEIGHT}" >&2 - -# -# Text spacing -# - -TEXTDOTDISTANCE=$(getOptionNumber TextDotDistance) -[ "$?" = 0 ] || exit 1 -case "$TEXTDOTDISTANCE" in - 220) TEXTCELLDISTANCE=310 ;; - 250) TEXTCELLDISTANCE=350 ;; - 320) TEXTCELLDISTANCE=525 ;; - *) - printf "ERROR: Unknown text dot distance '%s'\n" "$TEXTDOTDISTANCE" >&2 - exit 1 - ;; -esac - -TEXTDOTS=$(getOptionNumber TextDots) -[ "$?" = 0 ] || exit 1 -LINESPACING=$(getOptionNumber LineSpacing) -[ "$?" = 0 ] || exit 1 - -# Cell dimension, including spacing -TEXTCELLWIDTH=$(( $TEXTDOTDISTANCE + $TEXTCELLDISTANCE )) -TEXTCELLHEIGHT=$(( $TEXTDOTDISTANCE * ($TEXTDOTS / 2 - 1) + $LINESPACING )) - -# Compute number of printable cells according to page width and height -PRINTABLETEXTWIDTH=$(( ($PRINTABLEWIDTH + $TEXTCELLDISTANCE) / $TEXTCELLWIDTH )) -PRINTABLETEXTHEIGHT=$(( ($PRINTABLEHEIGHT + $LINESPACING) / $TEXTCELLHEIGHT )) - -if [ "$(getOption TopMargin)" = "" ] -then - # No margin - TEXTWIDTH=$PRINTABLETEXTWIDTH - TEXTHEIGHT=$PRINTABLETEXTHEIGHT -else - # Margins in cells - TOPMARGIN=$(getOptionNumber TopMargin) - [ "$?" = 0 ] || exit 1 - BOTTOMMARGIN=$(getOptionNumber BottomMargin) - [ "$?" = 0 ] || exit 1 - LEFTMARGIN=$(getOptionNumber LeftMargin) - [ "$?" = 0 ] || exit 1 - RIGHTMARGIN=$(getOptionNumber RightMargin) - [ "$?" = 0 ] || exit 1 - TEXTWIDTH=$(( $PRINTABLETEXTWIDTH - $LEFTMARGIN - $RIGHTMARGIN )) - TEXTHEIGHT=$(( $PRINTABLETEXTHEIGHT - $TOPMARGIN - $BOTTOMMARGIN )) -fi - -# Filter that adds top and left margins on the fly, to be used while producing -# BRF output. -addmargins() { - NEWPAGE="" - if [ -n "$TOPMARGIN" ]; then - for I in $(seq 1 $TOPMARGIN) ; do - NEWPAGE="$NEWPAGE"$'\r'$'\n' - done - fi - NEWPAGESED=${NEWPAGE//$'\n'/\\$'\n'} - LEFTSPACES="" - if [ -n "$LEFTMARGIN" ]; then - for I in $(seq 1 $LEFTMARGIN) ; do - LEFTSPACES="$LEFTSPACES " - done - fi - - echo -n "$NEWPAGE" - sed -e '$s/ $//' \ - -e "s/^\( \?\)\([^ ]\)/\1$LEFTSPACES\2/" \ - -e "s/ / $NEWPAGESED/" - echo -n " " -} - -# -# Graphic spacing -# - -# Compute number of printable cells according to page size -GRAPHICDOTDISTANCE=$(getOptionNumber GraphicDotDistance) -[ "$?" = 0 ] || exit 1 - -# This is the total area we will send to the embosser -TOTALGRAPHICWIDTH=$(( ( ($PRINTABLEWIDTH - 160) / $GRAPHICDOTDISTANCE ) / 2 * 2 )) -TOTALGRAPHICHEIGHT=$(( ( ($PRINTABLEHEIGHT - 160) / $GRAPHICDOTDISTANCE ) / 4 * 4 )) - -echo "DEBUG: total graphical: ${TOTALGRAPHICWIDTH}x${TOTALGRAPHICHEIGHT}" >&2 - -# This is how many dots we have to introduce to respect at least the software left+top margin -GRAPHICHOFFSET=$(( ($PAGE_LEFT - $MARGIN_LEFT + $GRAPHICDOTDISTANCE - 1) / $GRAPHICDOTDISTANCE )) -GRAPHICVOFFSET=$(( ($PAGE_TOP - $MARGIN_TOP + $GRAPHICDOTDISTANCE - 1) / $GRAPHICDOTDISTANCE )) - -echo "DEBUG: graphical offset: ${GRAPHICHOFFSET}x${GRAPHICVOFFSET}" >&2 - -# This is the resulting actual margin -GRAPHICLEFTMARGIN=$(( $MARGIN_LEFT + $GRAPHICHOFFSET * $GRAPHICDOTDISTANCE )) -GRAPHICTOPMARGIN=$(( $MARGIN_TOP + $GRAPHICVOFFSET * $GRAPHICDOTDISTANCE )) - -echo "DEBUG: rounded graphical top-left corner margin: ${GRAPHICLEFTMARGIN}x${GRAPHICTOPMARGIN}" >&2 - -# Then compute how many dots we can afford until reaching the software right+bottom margin -GRAPHICWIDTH=$(( ( ( $PAGEWIDTH - $GRAPHICLEFTMARGIN - $PAGE_RIGHT ) - 160) / $GRAPHICDOTDISTANCE )) -GRAPHICHEIGHT=$(( ( ( $PAGEHEIGHT - $GRAPHICTOPMARGIN - $PAGE_BOTTOM ) - 160) / $GRAPHICDOTDISTANCE )) - -echo "DEBUG: resulting graphical area: ${GRAPHICWIDTH}x${GRAPHICHEIGHT}" >&2 - -# -# Text translation -# - -TABLESDIR=@TABLESDIR@ -echo "DEBUG: Liblouis table directory is $TABLESDIR" >&2 - -getOptionLibLouis () { - OPTION=$1 - VALUE=$(getOption $OPTION) - - # Check validity of input - case "$VALUE" in - [-_.0-9A-Za-z]*) ;; - *) printf "ERROR: Option $OPTION must be a valid liblouis table name, got '%s'\n" "$VALUE" >&2 - exit 1 - ;; - esac - - LOCALE=${LANG%@*} - LOCALE=${LOCALE%.*} - LANGUAGE=${LOCALE%_*} - COUNTRY=${LOCALE#*_} - LOUIS_LOCALE=$LANGUAGE-$COUNTRY - - getLibLouisTableScore () { - GRADE="$1" - printf "DEBUG: looking for locale '%s' and grade '%s' \n" "$LOCALE" "$GRADE" >&2 - # Try to select a good table from its metadata - selected= - selectedscore=0 - for table in "$TABLESDIR/"*.tbl "$TABLESDIR/"*.ctb "$TABLESDIR/"*.utb; do - score=0 - name=${table#$TABLESDIR/} - - if grep -q "^#+locale:$LOUIS_LOCALE$" $table; then - printf "DEBUG: %s has correct locale %s\n" "$name" "$LOUIS_LOCALE" >&2 - score=$((score + 15)) - elif grep -q "^#+region:$LOUIS_LOCALE$" $table; then - printf "DEBUG: %s has correct region %s\n" "$name" "$LOUIS_LOCALE" >&2 - score=$((score + 15)) - elif grep -q "^#+locale:$LANGUAGE$" $table; then - printf "DEBUG: %s has correct language %s\n" "$name" "$LANGUAGE" >&2 - score=$((score + 10)) - elif grep -q "^#+language:$LANGUAGE$" $table; then - printf "DEBUG: %s has correct language %s\n" "$name" "$LANGUAGE" >&2 - score=$((score + 10)) - else - # Requested language is a must - continue - fi - - if [ -n "$GRADE" ]; then - if grep -q "^#+grade:$GRADE$" $table || \ - ( [ "$GRADE" = 0 ] && ( grep -q "^#+contraction:no" $table || grep -q "^#+type:computer" $table ) ) \ - then - printf "DEBUG: %s has correct grade %s\n" "$name" "$GRADE" >&2 - score=$((score + 10)) - else - # Requested grade is a must - continue - fi - fi - - # Dot numbers are not always specified in liblouis :/ - if grep -q "^#+dots:$TEXTDOTS$" $table || \ - ( [ "$TEXTDOTS" = 6 ] && grep -q "^#+grade:[1-3]" $table ) \ - then - printf "DEBUG: %s has correct dots %s\n" "$name" "$TEXTDOTS" >&2 - score=$((score + 2)) - fi - - if [ $score -gt $selectedscore ]; then - printf "DEBUG: %s has better score $score\n" "$name" >&2 - selected=$name - selectedscore=$score - fi - done - - echo $selected - } - - # Check presence of table - case "$VALUE" in - None) - printf None - ;; - Locale) - selected=$(getLibLouisTableScore '') - # Try tagged tables before untagged ones - if [ -n "$selected" ]; then - printf "%s" "$selected" - elif [ -f "$TABLESDIR/$LOCALE.tbl" ]; then - printf "%s" "$LOCALE.tbl" - elif [ -f "$TABLESDIR/$LANGUAGE.tbl" ]; then - printf "%s" "$LANGUAGE.tbl" - else - printf "ERROR: Could not find $OPTION table with locale %s\n" "$LOCALE" >&2 - printf None - exit 1 - fi - ;; - Locale-g[0-3]) - GRADE=${VALUE#Locale-g} - selected=$(getLibLouisTableScore $GRADE) - if [ -n "$selected" ]; then - printf "%s" "$selected" - exit 0 - else - for i in "$TABLESDIR/$LOCALE.tbl" "$TABLESDIR/$LOCALE"*.tbl "$TABLESDIR/$LANGUAGE.tbl" "$TABLESDIR/$LANGUAGE"*.tbl - do - if grep -q "^#+grade:$GRADE$" "$i" - then - printf "%s" "${i//*\/}" - exit 0 - fi - done - fi - printf "ERROR: Could not find $OPTION table with locale %s and grade %s\n" "$LOCALE" "$GRADE" >&2 - printf None - exit 1 - ;; - HyphLocale) - if [ -f "$TABLESDIR/hyph_$LOCALE.dic" ]; then - printf "%s" "hyph_$LOCALE.dic" - elif [ -f "$TABLESDIR/hyph_$LANGUAGE.dic" ]; then - printf "%s" "hyph_$LANGUAGE.dic" - else - printf "WARN: Could not find $OPTION hyphenation table with locale %s\n" "$LOCALE" >&2 - printf None - fi - ;; - *) - [ -f "$TABLESDIR/$VALUE".utb ] && VALUE="$VALUE".utb - [ -f "$TABLESDIR/$VALUE".ctb ] && VALUE="$VALUE".ctb - if [ ! -f "$TABLESDIR/$VALUE" ] - then - printf "ERROR: Could not find $OPTION table '%s'\n" "$VALUE" >&2 - exit 1 - fi - - printf "%s" "$VALUE" - ;; - esac -} - -LIBLOUIS1=$(getOptionLibLouis LibLouis) -[ "$?" = 0 ] || exit 1 -LIBLOUIS2=$(getOptionLibLouis LibLouis2) -[ "$?" = 0 ] || exit 1 -LIBLOUIS3=$(getOptionLibLouis LibLouis3) -[ "$?" = 0 ] || exit 1 -LIBLOUIS4=$(getOptionLibLouis LibLouis4) -[ "$?" = 0 ] || exit 1 - -echo "DEBUG: Table1 $LIBLOUIS1" >&2 -echo "DEBUG: Table2 $LIBLOUIS2" >&2 -echo "DEBUG: Table3 $LIBLOUIS3" >&2 -echo "DEBUG: Table4 $LIBLOUIS4" >&2 - -[ "$LIBLOUIS1" != None ] && LIBLOUIS_TABLES="$LIBLOUIS1" -[ "$LIBLOUIS2" != None ] && LIBLOUIS_TABLES="${LIBLOUIS_TABLES:+$LIBLOUIS_TABLES,}$LIBLOUIS2" -[ "$LIBLOUIS3" != None ] && LIBLOUIS_TABLES="${LIBLOUIS_TABLES:+$LIBLOUIS_TABLES,}$LIBLOUIS3" -[ "$LIBLOUIS4" != None ] && LIBLOUIS_TABLES="${LIBLOUIS_TABLES:+$LIBLOUIS_TABLES,}$LIBLOUIS4" - -# -# Checking for presence of tools -# -checkTool() { - TOOL=$1 - PACKAGE=$2 - USE=$3 - if ! type $TOOL > /dev/null - then - printf "ERROR: The $PACKAGE package is required for $USE\n" >&2 - exit 1 - fi -} diff --git a/filter/braille/filters/imagemagick.defs b/filter/braille/filters/imagemagick.defs deleted file mode 100644 index 79f47843f..000000000 --- a/filter/braille/filters/imagemagick.defs +++ /dev/null @@ -1,109 +0,0 @@ -// -// Copyright (c) 2015, 2017 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -Group "Image/Image conversion" - -Option "GraphicDotDistance/Graphic dot distance" PickOne AnySetup 10 - Choice "160/1.6mm" "" - *Choice "200/2.0mm" "" - Choice "250/2.5mm" "" - -Option "Rotate/Rotation" PickOne AnySetup 10 - Choice "0/No rotation" "" - *Choice "90>/Rotate clockwise to fit paper" "" - Choice "270>/Rotate counter clockwise to fit paper" "" - Choice "90/Clockwise" "" - Choice "270/Counter clockwise" "" - Choice "180/Upside Down" "" - -Option "fitplot/Resize" Boolean AnySetup 10 - *Choice "True/Yes" "" - Choice "False/No" "" - -Option "mirror/Mirror" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - -Option "Edge/Edge detection" PickOne AnySetup 10 - Choice "None/None" "" - Choice "Edge/Simple" "" - *Choice "Canny/Canny" "" - -Option "Negate/Negate" Boolean AnySetup 10 - Choice "True/Yes" "" - *Choice "False/No" "" - -Option "EdgeFactor/EdgeFactor" PickOne AnySetup 10 - *Choice "1/1" "" - Choice "2/2" "" - Choice "5/5" "" - Choice "10/10" "" - -Option "CannyRadius/CannyRadius" PickOne AnySetup 10 - *Choice "0/0" "" - Choice "1/1" "" - Choice "2/2" "" - -Option "CannySigma/CannySigma" PickOne AnySetup 10 - Choice "0/0" "" - *Choice "1/1" "" - Choice "2/2" "" - -Option "CannyLower/CannyLower" PickOne AnySetup 10 - Choice "0/0" "" - Choice "5/5" "" - *Choice "10/10" "" - Choice "15/15" "" - Choice "20/20" "" - -Option "CannyUpper/CannyUpper" PickOne AnySetup 10 - Choice "10/10" "" - Choice "20/20" "" - *Choice "30/30" "" - Choice "40/40" "" - Choice "50/50" "" - -Option "page-top/Graphical top margin" PickOne AnySetup 10.0 - Choice "0/0" "" - Choice "5/5" "" - Choice "10/10" "" - *Choice "15/15" "" - Choice "20/20" "" - Choice "25/25" "" - Attribute Custompage-top True "" - Attribute ParamCustompage-top Points "1 int 1 999" - -Option "page-bottom/Graphical bottom margin" PickOne AnySetup 10.0 - Choice "0/0" "" - Choice "5/5" "" - Choice "10/10" "" - *Choice "15/15" "" - Choice "20/20" "" - Choice "25/25" "" - Attribute Custompage-bottom True "" - Attribute ParamCustompage-bottom Points "1 int 1 999" - -Option "page-left/Graphical left margin" PickOne AnySetup 10.0 - Choice "0/0" "" - Choice "5/5" "" - Choice "10/10" "" - *Choice "15/15" "" - Choice "20/20" "" - Choice "25/25" "" - Attribute Custompage-left True "" - Attribute ParamCustompage-left Points "1 int 1 999" - -Option "page-right/Graphical right margin" PickOne AnySetup 10.0 - Choice "0/0" "" - Choice "5/5" "" - Choice "10/10" "" - *Choice "15/15" "" - Choice "20/20" "" - Choice "25/25" "" - Attribute Custompage-right True "" - Attribute ParamCustompage-right Points "1 int 1 999" - diff --git a/filter/braille/filters/imagetobrf.in b/filter/braille/filters/imagetobrf.in deleted file mode 100755 index 2d23610db..000000000 --- a/filter/braille/filters/imagetobrf.in +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -OUTPUT_FORMAT=brf - -case $0 in - *imagetoubrl*) OUTPUT_FORMAT=ubrl ;; -esac - -. @CUPS_DATADIR@/braille/cups-braille.sh - -checkTool convert imagemagick "embossing images" - -NEGATE=$(getOption Negate) -case "$NEGATE" in - True|true) NEGATE=-negate ;; - False|false) NEGATE= ;; - *) - printf "ERROR: Option Negate must either True or False, got '%s'\n" "$NEGATE" >&2 - exit 1 - ;; -esac - -ROTATE=$(getOption Rotate) -case "$ROTATE" in - 90\>|270\>) - if [ "$GRAPHICWIDTH" -gt "$GRAPHICHEIGHT" ] - then - # Landscape paper, rotate to landscape instead of to portrait - ROTATE=${ROTATE/\>/\<} - fi - ;; - 0|90|270|180) ;; - *) - printf "ERROR: Option Rotate must be a valid rotation value, got '%s'\n" "$ROTATE" >&2 - exit 1 - ;; -esac - -MIRROR=$(getOption mirror) -case "$MIRROR" in - True|true) MIRROR="-flop" ;; - False|false) MIRROR="" ;; - *) - printf "ERROR: Option mirror must either True or False, got '%s'\n" "$MIRROR" >&2 - exit 1 - ;; -esac - -PAGE="-page ${TOTALGRAPHICWIDTH}x${TOTALGRAPHICHEIGHT}+${GRAPHICHOFFSET}+${GRAPHICVOFFSET}" - -RESIZE=$(getOption fitplot) -case "$RESIZE" in - True|true) RESIZE="-size ${GRAPHICWIDTH}x${GRAPHICHEIGHT} -resize ${GRAPHICWIDTH}x${GRAPHICHEIGHT}" ;; - False|false) RESIZE="-crop ${GRAPHICWIDTH}x${GRAPHICHEIGHT}+${GRAPHICHOFFSET}+${GRAPHICVOFFSET}" ;; - *) - printf "ERROR: Option fitplot must either True or False, got '%s'\n" "$RESIZE" >&2 - exit 1 - ;; -esac - -EDGE=$(getOption Edge) -EDGEFACTOR=$(getOptionNumber EdgeFactor) - -CANNY_RADIUS=$(getOptionNumber CannyRadius) -CANNY_SIGMA=$(getOptionNumber CannySigma) -CANNY_LOWER=$(getOptionNumber CannyLower) -CANNY_UPPER=$(getOptionNumber CannyUpper) - -case $EDGE in - None) WORK=$NEGATE;; - Edge) WORK="$NEGATE -edge $EDGEFACTOR -negate" ;; - Canny) WORK="-canny ${CANNY_RADIUS}x${CANNY_SIGMA}+$CANNY_LOWER%+$CANNY_UPPER% -negate" ;; - *) - printf "ERROR: Unknown Edge option value '%s'\n" "$EDGE" >&2 - exit 1 - ;; -esac - -RENDER_CALL="convert $WORK -rotate $ROTATE $PAGE $RESIZE $MIRROR -flatten - $OUTPUT_FORMAT:-" - -# Now proceeed -echo "INFO: Converting image" 1>&2 -if [ -z "$FILE" ] -then - printf "DEBUG: Calling %s from stdin\n" "$RENDER_CALL" 1>&2 - $RENDER_CALL | sed -e '/^\(Width\|X\|Y\): [0-9]*$/,/^$/d' | addmargins -else - printf "DEBUG: Calling %s on '%s'\n" "$RENDER_CALL" "$FILE" 1>&2 - $RENDER_CALL < "$FILE" | sed -e '/^\(Width\|X\|Y\): [0-9]*$/,/^$/d' | addmargins -fi -echo "INFO: Ready" >&2 diff --git a/filter/braille/filters/liblouis.defs b/filter/braille/filters/liblouis.defs deleted file mode 100644 index fd46fd9a3..000000000 --- a/filter/braille/filters/liblouis.defs +++ /dev/null @@ -1,11 +0,0 @@ -// -// Copyright (c) 2015 Samuel Thibault -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include diff --git a/filter/braille/filters/liblouis1.defs.gen.in b/filter/braille/filters/liblouis1.defs.gen.in deleted file mode 100644 index e60a8a88f..000000000 --- a/filter/braille/filters/liblouis1.defs.gen.in +++ /dev/null @@ -1,277 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015, 2017-2018, 2020 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -TABLESDIR=@TABLESDIR@ - -echo 'Group "Braille/Braille transcription"' -echo 'Option "LibLouis/Braille transcription" PickOne AnySetup 10' -echo ' Choice "None/None" ""' -echo ' *Choice "Locale/Default for language" ""' -echo ' Choice "Locale-g0/Default for language grade 0" ""' -echo ' Choice "Locale-g1/Default for language grade 1" ""' -echo ' Choice "Locale-g2/Default for language grade 2" ""' -echo ' Choice "Locale-g3/Default for language grade 3" ""' -echo ' Choice "HyphLocale/Default hyphenation rules for language" ""' - -( - for i in $TABLESDIR/*.utb $TABLESDIR/*.ctb - do - file=${i##*/} - ext=${file##*.} - name=${file%.$ext} - TYPE="" - LANGUAGE="$name" - LOCATION="" - GRADE="" - DISPLAY_NAME="" - display_name=$(grep ^#-display-name: "$i" | cut -d ' ' -f 2-) - if [ -n "$display_name" ]; then - # Table provides a display name, neat - DISPLAY_NAME=$display_name - LANGUAGE=$DISPLAY_NAME - else - case $name in - "afr-za-g1") LANGUAGE=Afrikaans TYPE="grade 1" ;; - "ar-ar-g1") LANGUAGE=Arabic TYPE="grade 1" ;; - "ar-fa") LANGUAGE=Persian TYPE="grade 1" ;; - "as-in-g1") LANGUAGE=Assamese TYPE="grade 1" ;; - "aw-in-g1") LANGUAGE=Awadhi TYPE="grade 1" ;; - "be-in-g1") LANGUAGE=Bengali TYPE="grade 1" ;; - "bg") LANGUAGE=Bulgarian TYPE="grade 1" ;; - "bh") LANGUAGE=Bihari TYPE="grade 1" ;; - "bo") LANGUAGE=Tibetan TYPE="grade 1" ;; - "boxes") TYPE="Box drawing" ;; - "br-in-g1") LANGUAGE=Braj TYPE="Box drawing" ;; - "ca-g1") LANGUAGE=Catalan TYPE="grade 1" ;; - "chr-us-g1") LANGUAGE=Cherokee TYPE="grade 1" ;; - "ckb-g1") LANGUAGE="Sorani (Kurdish)" TYPE="grade 1" ;; - "cs-g1") LANGUAGE=Czech TYPE="grade 1" ;; - "cy-cy-g1") LANGUAGE=Welsh TYPE="grade 1" ;; - "cy-cy-g2") LANGUAGE=Welsh TYPE="grade 2" ;; - "Cz-Cz-g1") LANGUAGE=Czech TYPE="grade 1" ;; - "da-dk-g08") LANGUAGE=Danish TYPE="grade 0" ;; - "da-dk-g16") LANGUAGE=Danish TYPE="grade 1 6 dots" ;; - "da-dk-g18") LANGUAGE=Danish TYPE="grade 1 8 dots" ;; - "da-dk-g26") LANGUAGE=Danish TYPE="grade 2 6 dots" ;; - "da-dk-g26l") LANGUAGE=Danish TYPE="grade 2 6 dots limited" ;; - "da-dk-g28") LANGUAGE=Danish TYPE="grade 2 8 dots" ;; - "da-dk-g28l") LANGUAGE=Danish TYPE="grade 2 8 dots limited" ;; - "da-lt") LANGUAGE=Danish TYPE="LogText" ;; - "de-chess") LANGUAGE=German TYPE="Chess" ;; - "de-ch-g0") LANGUAGE=German LOCATION=Swiss TYPE="grade 0" ;; - "de-ch-g1") LANGUAGE=German LOCATION=Swiss TYPE="grade 1" ;; - "de-ch-g2") LANGUAGE=German LOCATION=Swiss TYPE="grade 2" ;; - "de-de-comp8") LANGUAGE=German LOCATION=Germany TYPE="computer" ;; - "de-de-g0") LANGUAGE=German LOCATION=Germany TYPE="grade 0" ;; - "de-de-g1") LANGUAGE=German LOCATION=Germany TYPE="grade 1" ;; - "de-de-g2") LANGUAGE=German LOCATION=Germany TYPE="grade 2" ;; - "dra") LANGUAGE=Dravidian TYPE="grade 1" ;; - "el") LANGUAGE=Greek TYPE="grade 1" ;; - "en_CA") LANGUAGE=English LOCATION=Canada TYPE="grade 1" ;; - "en-chess") LANGUAGE=English TYPE="Chess" ;; - "en-gb-comp8") LANGUAGE=English LOCATION=U.K. TYPE="computer" ;; - "en-gb-g1") LANGUAGE=English LOCATION=U.K. TYPE="grade 1" ;; - "en-GB-g2") LANGUAGE=English LOCATION=U.K. TYPE="grade 2" ;; - "en-in-g1") LANGUAGE=English LOCATION=India TYPE="grade 1" ;; - "en-ueb-g1") LANGUAGE=English TYPE="Unified grade 1" ;; - "en-ueb-g2") LANGUAGE=English TYPE="Unified grade 2" ;; - "en-ueb-math") LANGUAGE=English TYPE="Unified math definitions" ;; - "en-us-comp6") LANGUAGE=English LOCATION=U.S. TYPE="computer 6 dots" ;; - "en-us-comp8") LANGUAGE=English LOCATION=U.S. TYPE="computer 8 dots" ;; - "en-us-comp8-ext") LANGUAGE=English LOCATION=U.S. TYPE="computer 8 dots extended" ;; - "en-us-compbrl") LANGUAGE=English LOCATION=U.S. TYPE="computer" ;; - "en-us-g1") LANGUAGE=English LOCATION=U.S. TYPE="grade 1" ;; - "en-us-g2") LANGUAGE=English LOCATION=U.S. TYPE="grade 2" ;; - "en-us-interline") LANGUAGE=English LOCATION=U.S. TYPE="interline" ;; - "en-us-mathtext") LANGUAGE=English LOCATION=U.S. TYPE="mathtext" ;; - "eo-g1") LANGUAGE=Esperanto TYPE="grade 1" ;; - "eo-g1-x-system") LANGUAGE=Esperanto TYPE="grade 1 x-system" ;; - "Es-Es-G0") LANGUAGE=Spanish TYPE="grade 0" ;; - "Es-Es-g1") LANGUAGE=Spanish TYPE="grade 1" ;; - "es-g1") LANGUAGE=Spanish TYPE="grade 1" ;; - "et") LANGUAGE=Estonian TYPE="grade 1" ;; - "et-g0") LANGUAGE=Estonian TYPE="grade 0" ;; - "ethio-g1") LANGUAGE=Ethiopic TYPE="grade 1" ;; - "fi") LANGUAGE=Finnish TYPE="6 dot" ;; - "fi1") LANGUAGE=Finnish TYPE="grade 1" ;; - "fi2") LANGUAGE=Finnish TYPE="grade 2" ;; - "fi-fi-8dot") LANGUAGE=Finnish TYPE="8dot" ;; - "fi-fi") LANGUAGE=Finnish TYPE="6dot" ;; - "fr-2007") LANGUAGE=French TYPE="2007" ;; - "fr-bfu-comp6") LANGUAGE=French TYPE="Braille Français Unifié computer 6 dots" ;; - "fr-bfu-comp8") LANGUAGE=French TYPE="Braille Français Unifié computer 8 dots" ;; - "fr-bfu-g2") LANGUAGE=French TYPE="Braille Français Unifié grade 2" ;; - "fr-ca-g1") LANGUAGE=French LOCATION=Canada TYPE="grade 1" ;; - "Fr-Ca-g2") LANGUAGE=French LOCATION=Canada TYPE="grade 2" ;; - "fr-fr-g1") LANGUAGE=French LOCATION=France TYPE="grade 1" ;; - "Fr-Fr-g2") LANGUAGE=French LOCATION=France TYPE="grade 2" ;; - "ga-g1") LANGUAGE=Gaeilge TYPE="grade 1" ;; - "ga-g2") LANGUAGE=Gaeilge TYPE="grade 2" ;; - "gd") LANGUAGE=Gaelic TYPE="grade 1" ;; - "gon") LANGUAGE=Gondi TYPE="grade 1" ;; - "gr-bb") LANGUAGE=Greek TYPE="bb" ;; - "gr-gr-g1") LANGUAGE=Greek TYPE="grade 1" ;; - "gu-in-g1") LANGUAGE=Gujarati TYPE="grade 1" ;; - "haw-us-g1") LANGUAGE=Hawaiian TYPE="grade 1" ;; - "he") LANGUAGE=Hebrew TYPE="grade 1" ;; - "hi-in-g1") LANGUAGE=Hindi TYPE="grade 1" ;; - "hr"|"hr-g1") LANGUAGE=Croatian TYPE="grade 1" ;; - "hr-comp8") LANGUAGE=Croatian TYPE="computer 8 dots" ;; - "hu-hu-comp8") LANGUAGE=Hungarian TYPE="computer 8 dots" ;; - "hu-hu-g1") LANGUAGE=Hungarian TYPE="grade 1" ;; - "hy") LANGUAGE=Armenian TYPE="grade 1" ;; - "is") LANGUAGE=Icelandic TYPE="grade 1" ;; - "it-it-comp6") LANGUAGE=Italian TYPE="computer 6 dots" ;; - "it-it-comp8") LANGUAGE=Italian TYPE="computer 8 dots" ;; - "iu-ca-g1") LANGUAGE=Inuktitut TYPE="grade 1" ;; - "ka-in-g1") LANGUAGE=Kannada TYPE="grade 1" ;; - "kh-in-g1") LANGUAGE=Khasi TYPE="grade 1" ;; - "ko-2006-g1") LANGUAGE=Korean TYPE="grade 1 2006" ;; - "ko-2006-g2") LANGUAGE=Korean TYPE="grade 2 2006" ;; - "ko-g1") LANGUAGE=Korean TYPE="grade 1" ;; - "ko-g2") LANGUAGE=Korean TYPE="grade 2" ;; - "kok") LANGUAGE=Konkani TYPE="grade 1" ;; - "kru") LANGUAGE=Kurukh TYPE="grade 1" ;; - "ks-in-g1") LANGUAGE=Kashmiri TYPE="grade 1" ;; - "lt") LANGUAGE=Lithuanian TYPE="grade 1" ;; - "lt-6dot") LANGUAGE=Lithuanian TYPE="6 dots" ;; - "Lv-Lv-g1") LANGUAGE=Latvian TYPE="grade 1" ;; - "mao-nz-g1") LANGUAGE=Maori TYPE="grade 1" ;; - "marburg") TYPE="Marburg maths" ;; - "marburg_edit") TYPE="Marburg maths post-translation editing" ;; - "ml-in-g1") LANGUAGE=Malayalam TYPE="grade 1" ;; - "mn-in-g1") LANGUAGE=Manipuri TYPE="grade 1" ;; - "mn-MN"|"mn-MN-g1") LANGUAGE=Mongolian TYPE="grade 1" ;; - "mn-MN-g2") LANGUAGE=Mongolian TYPE="grade 2" ;; - "mr-in-g1") LANGUAGE=Marathi TYPE="grade 1" ;; - "mt") LANGUAGE=Maltese TYPE="grade 1" ;; - "mun") LANGUAGE=Munda TYPE="grade 1" ;; - "mwr") LANGUAGE=Marwari TYPE="grade 1" ;; - "ne") LANGUAGE=Nepali TYPE="grade 1" ;; - "nemeth") TYPE="Nemeth Maths" ;; - "nemeth_edit") TYPE="Nemeth Maths post-translation editing" ;; - "nl-BE-g0") LANGUAGE=Dutch LOCATION=Belgium TYPE="grade 0" ;; - "nl-g0") LANGUAGE=Dutch TYPE="grade 0" ;; - "nl-NL-g0") LANGUAGE=Dutch LOCATION="Netherlands" TYPE="grade 0" ;; - "Nl-Nl-g1") LANGUAGE=Dutch LOCATION=Netherlands TYPE="grade 1" ;; - "no-no-8dot-fallback-6dot-g0") LANGUAGE=Norwegian TYPE="grade 0 8 dots fallback 6 dots" ;; - "no-no-8dot") LANGUAGE=Norwegian TYPE="grade 0 8 dots" ;; - "no-no-comp8") LANGUAGE=Norwegian TYPE="grade 0 computer" ;; - "no-no-g0") LANGUAGE=Norwegian TYPE="grade 0" ;; - "no-no-g1") LANGUAGE=Norwegian TYPE="grade 1" ;; - "no-no-g2") LANGUAGE=Norwegian TYPE="grade 2" ;; - "no-no-g3") LANGUAGE=Norwegian TYPE="grade 3" ;; - "no-no-generic") LANGUAGE=Norwegian TYPE="generic" ;; - "np-in-g1") LANGUAGE=Nepali TYPE="grade 1" ;; - "or-in-g1") LANGUAGE=Oriya TYPE="grade 1" ;; - "pi") LANGUAGE=Pali TYPE="grade 1" ;; - "pl-pl-comp8") LANGUAGE=Polish TYPE="computer" ;; - "Pl-Pl-g1") LANGUAGE=Polish TYPE="grade 1" ;; - "pt-pt-comp8") LANGUAGE=Portuguese TYPE="computer" ;; - "pt-pt-g1") LANGUAGE=Portuguese TYPE="grade 1" ;; - "pt-pt-g2") LANGUAGE=Portuguese TYPE="grade 2" ;; - "pu-in-g1") LANGUAGE=Punjabi TYPE="grade 1" ;; - "ro") LANGUAGE=Romanian TYPE="grade 1" ;; - "ru-compbrl") LANGUAGE=Russian TYPE="computer" ;; - "ru") LANGUAGE=Russian TYPE="grade 1" ;; - "ru-litbrl") LANGUAGE=Russian TYPE="literary" ;; - "ru-ru-g1") LANGUAGE=Russian TYPE="grade 1" ;; - "sa-in-g1") LANGUAGE=Sasnskrit TYPE="grade 1" ;; - "se-se") LANGUAGE=Swedish TYPE="grade 1" ;; - "Se-Se-g1") LANGUAGE=Swedish TYPE="grade 1" ;; - "si-in-g1") LANGUAGE=Sindhi TYPE="grade 1" ;; - "sin") LANGUAGE=Sinhala TYPE="grade 1" ;; - "sk-g1") LANGUAGE=Slovak TYPE="grade 1" ;; - "sk-sk-g1") LANGUAGE=Slovak TYPE="grade 1" ;; - "sk-sk") LANGUAGE=Slovak TYPE="grade 1" ;; - "sl-si-comp8") LANGUAGE=Slovenian TYPE="computer" ;; - "sl-si-g1") LANGUAGE=Slovenian TYPE="grade 1" ;; - "sot-za-g1") LANGUAGE=Sotho TYPE="grade 1" ;; - "spaces") TYPE="Spaces" ;; - "sr-g1") LANGUAGE=Serbian TYPE="grade 1" ;; - "sv-1989") LANGUAGE=Swedish TYPE="1989" ;; - "sv-1996") LANGUAGE=Swedish TYPE="1996" ;; - "ta") LANGUAGE=Tamil TYPE="grade 1" ;; - "ta-ta-g1") LANGUAGE=Tamil TYPE="grade 1" ;; - "te-in-g1") LANGUAGE=Telugu TYPE="grade 1" ;; - "tr"|"tr-g1") LANGUAGE=Turkish TYPE="grade 1" ;; - "tr-g2") LANGUAGE=Turkish TYPE="grade 2" ;; - "tsn-za-g1") LANGUAGE=Tswana TYPE="grade 1" ;; - "UEBC-g1") LANGUAGE=English TYPE="Unified grade 1" ;; - "UEBC-g2") LANGUAGE=English TYPE="Unified grade 2" ;; - "ukmaths") TYPE="U.K maths" ;; - "ukmaths_edit") TYPE="U.K maths post-translation editing" ;; - "ur-pk-g1") LANGUAGE=Urdu TYPE="grade 1" ;; - "ur-pk-g2") LANGUAGE=Urdu TYPE="grade 2" ;; - "vi") LANGUAGE=Vietnamese TYPE="grade 1" ;; - "vi-g1") LANGUAGE=Vietnamese TYPE="grade 1" ;; - "wiskunde") LANGUAGE=Flemish TYPE="grade 1" ;; - "zh-hk") LANGUAGE=Chinese LOCATION="Hong Kong" TYPE="grade 1" ;; - "zh-tw") LANGUAGE=Chinese LOCATION="Taiwan" TYPE="grade 1" ;; - "zh-chn") LANGUAGE=Chinese LOCATION="China" TYPE="grade 1" ;; - *) locale=$(grep ^#+locale: "$i" | cut -d ':' -f 2-) - if [ -n "$locale" ]; then - LANGUAGE="$locale" - fi - if [ $ext = ctb ]; then - TYPE="contracted" - else - TYPE="computer" - fi - ;; - esac - fi - echo "$file:$LANGUAGE:$LOCATION:$TYPE:$DISPLAY_NAME" - done - - for i in $TABLESDIR/hyph_*.dic - do - file=${i##*/} - name=${file%.dic} - name=${name#hyph_} - DESC=$name - LANGUAGE="" - LOCATION="" - GRADE="" - case $name in - brl_da_dk) LANGUAGE=Danish ;; - cs_CZ) LANGUAGE=Czech ;; - da_DK) LANGUAGE=Danish ;; - de_DE) LANGUAGE=German ;; - en_US) LANGUAGE=English ;; - eo) LANGUAGE=Esperanto ;; - es_ES) LANGUAGE=Spanish ;; - fr_FR) LANGUAGE=French ;; - hu_HU) LANGUAGE=Hungarian ;; - it_IT) LANGUAGE=Italian ;; - nb_NO) LANGUAGE="Norwegian BokmÃ¥l" ;; - nl_NL) LANGUAGE=Dutch ;; - nn_NO) LANGUAGE="Norwegian Nynorsk" ;; - pl_PL) LANGUAGE=Polish ;; - pt_PT) LANGUAGE=Portuguese ;; - ru) LANGUAGE=Russian ;; - sv_SE) LANGUAGE=Swedish ;; - *) LANGUAGE=$name ;; - esac - echo "$file:$LANGUAGE:$LOCATION:hyphenation rules:" - done - -) | LC_ALL=C sort -f -t : -k 2,3 | ( - -IFS=: -while read file LANGUAGE LOCATION TYPE DISPLAY_NAME -do - DESC="$TYPE ($file)" - [ -n "$LOCATION" ] && DESC="$LOCATION $DESC" - [ -n "$LANGUAGE" ] && DESC="$LANGUAGE $DESC" - [ -n "$DISPLAY_NAME" ] && DESC="$DISPLAY_NAME ($file)" - echo " Choice \"$file/$DESC\" \"\"" -done -) - diff --git a/filter/braille/filters/musicxmltobrf.in b/filter/braille/filters/musicxmltobrf.in deleted file mode 100644 index d41fb9d0b..000000000 --- a/filter/braille/filters/musicxmltobrf.in +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2017-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -. @CUPS_DATADIR@/braille/cups-braille.sh - -checkTool FreeDots FreeDots "translating musicxml files" -checkTool lou_translate liblouis "translating musicxml files" - -CONVERT="FreeDots -nw -w $TEXTWIDTH /dev/stdin" -TRANSLATE="lou_translate en-us-brf.dis,$LIBLOUIS_TABLES,braille-patterns.cti" - -cd $TMPDIR -echo "INFO: Translating MusicXML" >&2 - -printf "DEBUG: Calling $CONVERT | $TRANSLATE on '%s'\n" "$FILE" >&2 -if [ -z "$FILE" ] -then - $CONVERT | $TRANSLATE | addmargins -else - < "$FILE" $CONVERT | $TRANSLATE | addmargins -fi - -echo "INFO: Ready" >&2 diff --git a/filter/braille/filters/texttobrf.in b/filter/braille/filters/texttobrf.in deleted file mode 100755 index 832cc3e80..000000000 --- a/filter/braille/filters/texttobrf.in +++ /dev/null @@ -1,278 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2015-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -. @CUPS_DATADIR@/braille/cups-braille.sh - -#################### -# Liblouis options # -#################### -LIBLOUIS_CONFIG="" - -echo "DEBUG: Input content type: $CONTENT_TYPE" >&2 - -# FIXME CONTENT_TYPE contains original document, not document passed as parameter ?!! - -setupTextRendering() { - # Default rendering without translation: just reformat paragraphs - RENDER_CALL="fmt -$TEXTWIDTH" - - # Tool to be used for the conversion - case $CONTENT_TYPE in - text/plain) - CONVERT="" - ;; - text/html) - CONVERT="" - RENDER_CALL="lynx -width=$TEXTWIDTH -dump -stdin" - checkTool lynx lynx "translating html files" - ;; - application/msword) - CONVERT="antiword -" - checkTool antiword antiword "translating MS-Word doc files" - ;; - application/vnd.openxmlformats-officedocument.wordprocessingml.document) - CONVERT="docx2txt" - checkTool docx2txt docx2txt "translating MS-Word docx files" - ;; - text/rtf|application/rtf) - CONVERT="rtf2txt /dev/stdin" - checkTool rtf2txt rtf2txt "translating RTF files" - ;; - application/pdf|application/vnd.cups-pdf-banner) - CONVERT="pdftotext -raw - -" - checkTool pdftotext poppler "translating PDF files" - ;; - *) - echo "ERROR: unsupported content type $CONTENT_TYPE" >&2 - exit 1 - ;; - esac -} - -# sometimes we can't filter directly from stdin or the original file because the -# tools need to seek within the file (e.g. unzip), or spaces in the path pose -# problem. This can be called in such case to dump the original content to a -# fresh file -dumptofile() { - ORIGFILE="$FILE" - FILE=$(mktemp "${TMPDIR:-/tmp}/texttobrf.tmp.XXXXXX") - trap -- 'rm -f "$FILE"' EXIT - if [ -n "$ORIGFILE" ] - then - cat "$ORIGFILE" > "$FILE" - else - cat > "$FILE" - fi -} - -# Selected braille table -if [ -n "$LIBLOUIS_TABLES" ] -then - if type file2brl > /dev/null - then - # Good, we can use liblouisutdml - case $CONTENT_TYPE in - text/plain) - LIBLOUIS_TOOL="file2brl" - CONVERT="" - ;; - text/html) - LIBLOUIS_TOOL="file2brl -t" - CONVERT="" - ;; - text/xml|application/xml|application/xhtml+xml|application/sgml) - LIBLOUIS_TOOL="file2brl" - CONVERT="" - ;; - application/msword) - LIBLOUIS_TOOL="file2brl" - CONVERT="antiword -x db -" - checkTool antiword antiword "translating MS-Word doc files" - ;; - application/vnd.oasis.opendocument*) - LIBLOUIS_TOOL="file2brl" - dumptofile - CONVERT="unzip -p $FILE content.xml" - CHARSET=utf-8 - checkTool unzip unzip "translating LibreOffice/OpenOffice OpenDocument files" - ;; - application/vnd.openxmlformats-officedocument*) - LIBLOUIS_TOOL="file2brl" - dumptofile - CONVERT="unzip -p $FILE word/document.xml" - CHARSET=utf-8 - checkTool unzip unzip "translating MS-Word docx files" - ;; - text/rtf|application/rtf) - LIBLOUIS_TOOL="file2brl" - CONVERT="rtf2xml /dev/stdin" - checkTool rtf2xml rtf2xml "translating RTF files" - ;; - application/pdf|application/vnd.cups-pdf-banner) - LIBLOUIS_TOOL="file2brl -p" - CONVERT="pdftotext -raw - -" - CHARSET=utf-8 - checkTool pdftotext poppler "translating PDF files" - ;; - *) - echo "ERROR: unsupported content type $CONTENT_TYPE" >&2 - exit 1 - ;; - esac - - # - # text encoding - # - if [ "$CHARSET" = utf-8 ] - then - LIBLOUIS_CONFIG+=" -CinputTextEncoding=UTF8" - else - LIBLOUIS_CONFIG+=" -CinputTextEncoding=ascii8" - fi - - # - # Page numbers options - # - BRAILLEPAGENUMBER=$(getOption BraillePageNumber) - case "$BRAILLEPAGENUMBER" in - None) LIBLOUIS_CONFIG+=" -CbraillePages=no";; - TopMargin) LIBLOUIS_CONFIG+=" -CbraillePages=yes -CbraillePageNumberAt=top -CpageNumberTopSeparateLine=yes";; - BottomMargin) LIBLOUIS_CONFIG+=" -CbraillePages=yes -CbraillePageNumberAt=bottom -CpageNumberBottomSeparateLine=yes";; - TopInline) LIBLOUIS_CONFIG+=" -CbraillePages=yes -CbraillePageNumberAt=top -CpageNumberTopSeparateLine=no";; - BottomInline) LIBLOUIS_CONFIG+=" -CbraillePages=yes -CbraillePageNumberAt=bottom -CpageNumberBottomSeparateLine=no";; - *) - printf "ERROR: Unknown braille page number option '%s'\n" "$BRAILLEPAGENUMBER" >&2 - exit 1 - ;; - esac - - PRINTPAGENUMBER=$(getOption PrintPageNumber) - case "$PRINTPAGENUMBER" in - None) LIBLOUIS_CONFIG+=" -CprintPages=no";; - TopMargin) LIBLOUIS_CONFIG+=" -CprintPages=yes -CprintPageNumberAt=top -CpageNumberTopSeparateLine=yes";; - BottomMargin) LIBLOUIS_CONFIG+=" -CprintPages=yes -CprintPageNumberAt=bottom -CpageNumberBottomSeparateLine=yes";; - TopInline) LIBLOUIS_CONFIG+=" -CprintPages=yes -CprintPageNumberAt=top -CpageNumberTopSeparateLine=no";; - BottomInline) LIBLOUIS_CONFIG+=" -CprintPages=yes -CprintPageNumberAt=bottom -CpageNumberBottomSeparateLine=no";; - *) - printf "ERROR: Unknown print page number option '%s'\n" "$PRINTPAGENUMBER" >&2 - exit 1 - ;; - esac - - # Page numbering in top or bottom margin actually reduce the given margin - if [ "$BRAILLEPAGENUMBER" = TopMargin -o "$PRINTPAGENUMBER" = TopMargin ] - then - TOPMARGIN=$((TOPMARGIN - 1)) - TEXTHEIGHT=$((TEXTHEIGHT + 1)) - fi - if [ "$BRAILLEPAGENUMBER" = BottomMargin -o "$PRINTPAGENUMBER" = BottomMargin ] - then - BOTTOMMARGIN=$((BOTTOMMARGIN - 1)) - TEXTHEIGHT=$((TEXTHEIGHT + 1)) - fi - - PAGESEPARATOR=$(getOption PageSeparator) - case "$PAGESEPARATOR" in - True|true) LIBLOUIS_CONFIG+=" -CpageSeparator=yes";; - False|false) LIBLOUIS_CONFIG+=" -CpageSeparator=no";; - *) - printf "ERROR: Unknown page separator option '%s'\n" "$PAGESEPARATOR" >&2 - exit 1 - ;; - esac - - PAGESEPARATORNUMBER=$(getOption PageSeparatorNumber) - case "$PAGESEPARATORNUMBER" in - True|true) LIBLOUIS_CONFIG+=" -CpageSeparatorNumber=yes";; - False|false) LIBLOUIS_CONFIG+=" -CpageSeparatorNumber=no";; - *) - printf "ERROR: Unknown page separator number option '%s'\n" "$PAGESEPARATORNUMBER" >&2 - exit 1 - ;; - esac - - CONTINUEPAGES=$(getOption ContinuePages) - case "$CONTINUEPAGES" in - True|true) LIBLOUIS_CONFIG+=" -CcontinuePages=yes";; - False|false) LIBLOUIS_CONFIG+=" -CcontinuePages=no";; - *) - printf "ERROR: Unknown page separator number option '%s'\n" "$CONTINUEPAGES" >&2 - exit 1 - ;; - esac - - LIBLOUIS_CONFIG+=" -CcellsPerLine=$TEXTWIDTH -ClinesPerPage=$TEXTHEIGHT " - - RENDER_CALL="$LIBLOUIS_TOOL -Chyphenate=yes -CliteraryTextTable=en-us-brf.dis,$LIBLOUIS_TABLES,braille-patterns.cti $LIBLOUIS_CONFIG" - elif type lou_translate > /dev/null - then - # Only liblouis, but better than nothing - setupTextRendering - printf "WARN: The liblouisutdml package is required for translating braille better\n" >&2 - TRANSLATE="lou_translate en-us-brf.dis,$LIBLOUIS_TABLES,braille-patterns.cti" - else - printf "ERROR: The liblouisutdml package is required for translating braille\n" >&2 - exit 1 - fi -else - # No translation, only text rendering - printf "WARN: No braille table translation was selected\n" >&2 - setupTextRendering -fi - -# Now proceeed -cd $TMPDIR -echo "INFO: Reformating text" >&2 - -( -set -o pipefail -set -e -if [ -z "$CONVERT" ] -then - printf "DEBUG: Calling $RENDER_CALL on '%s'\n" "$FILE" >&2 - if [ -z "$FILE" ] - then - $RENDER_CALL 2> /dev/null | addmargins - else - < "$FILE" $RENDER_CALL 2> /dev/null | addmargins - fi -elif [ -z "$TRANSLATE" ] -then - printf "DEBUG: Calling $CONVERT | $RENDER_CALL on '%s'\n" "$FILE" >&2 - if [ -z "$FILE" ] - then - $CONVERT | $RENDER_CALL 2> /dev/null | addmargins - else - < "$FILE" $CONVERT | $RENDER_CALL 2> /dev/null | addmargins - fi -else - printf "DEBUG: Calling $CONVERT | $RENDER_CALL | $TRANSLATE on '%s'\n" "$FILE" >&2 - if [ -z "$FILE" ] - then - $CONVERT | $RENDER_CALL 2> /dev/null | $TRANSLATE | addmargins - else - < "$FILE" $CONVERT | $RENDER_CALL 2> /dev/null | $TRANSLATE | addmargins - fi -fi -) || { - printf "ERROR: text conversion pipeline $CONVERT | $RENDER_CALL | $TRANSLATE | addmargins failed\n" >&2 - exit 1 -} - -echo "INFO: Ready" >&2 diff --git a/filter/braille/filters/vectortobrf.in b/filter/braille/filters/vectortobrf.in deleted file mode 100644 index 0d35705ff..000000000 --- a/filter/braille/filters/vectortobrf.in +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2017-2018 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -OUTPUT_FORMAT=brf - -case $0 in - *vectortoubrl*) OUTPUT_FORMAT=ubrl ;; -esac - -. @CUPS_DATADIR@/braille/cups-braille.sh - -checkTool convert imagemagick "embossing images" - -NEGATE=$(getOption Negate) -case "$NEGATE" in - True|true) NEGATE=-negate ;; - False|false) NEGATE= ;; - *) - printf "ERROR: Option Negate must either True or False, got '%s'\n" "$NEGATE" >&2 - exit 1 - ;; -esac - -PAGE="-page ${TOTALGRAPHICWIDTH}x${TOTALGRAPHICHEIGHT}+${GRAPHICHOFFSET}+${GRAPHICVOFFSET}" - -GS_CALL="gs -q -dDEVICEWIDTHPOINTS=${GRAPHICWIDTH} -dDEVICEHEIGHTPOINTS=${GRAPHICHEIGHT} -noantialias -dTextAlphaBits=1 -dGraphicsAlphaBits=1 -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngmono -dFitPage -r72 -sOutputFile=-" -RENDER_CALL="convert $NEGATE $PAGE -flatten - $OUTPUT_FORMAT:-" - -# Now proceeed -echo "INFO: Converting image" 1>&2 -if [ -z "$FILE" ] -then - printf "DEBUG: Calling %s and %s from stdin\n" "$GS_CALL" "$RENDER_CALL" 1>&2 - $GS_CALL - | sed -e '/-noantialias/d' | $RENDER_CALL | sed -e '/^\(Width\|X\|Y\): [0-9]*$/,/^$/d' | addmargins -else - printf "DEBUG: Calling %s and %s on '%s'\n" "$GS_CALL" "$RENDER_CALL" "$FILE" 1>&2 - $GS_CALL "$FILE" | sed -e '/-noantialias/d' | $RENDER_CALL | sed -e '/^\(Width\|X\|Y\): [0-9]*$/,/^$/d' | addmargins -fi -echo "INFO: Ready" >&2 diff --git a/filter/braille/filters/vectortopdf.in b/filter/braille/filters/vectortopdf.in deleted file mode 100644 index d1001edab..000000000 --- a/filter/braille/filters/vectortopdf.in +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -# -# Copyright (c) 2017 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Make sure we have enough options -if [ $# != 5 -a $# != 6 ]; then - echo "ERROR: $0 jobid user name nb options [filename]" >&2 - exit 1 -fi - -NB=$4 -OPTIONS=$5 -FILE=$6 - -case $0 in - *svgtopdf*) INPUT_FORMAT=svg ;; - *xfigtopdf*) INPUT_FORMAT=fig ;; - *wmftopdf*) INPUT_FORMAT=wmf ;; - *emftopdf*) INPUT_FORMAT=emf ;; - *cgmtopdf*) INPUT_FORMAT=cgm ;; - *cmxtopdf*) INPUT_FORMAT=cmx ;; -esac - -trap -- 'rm -f "$FILE_FORMAT" "$FILE_PDF"' EXIT -FILE_FORMAT=$(mktemp "${TMPDIR:-/tmp}/vectortopdf.XXXXXX.${INPUT_FORMAT}") -FILE_PDF=$(mktemp "${TMPDIR:-/tmp}/vectortopdf.XXXXXX.pdf") - -if [ -z "$FILE" ] -then - # Get input from stdin - cat > "$FILE_FORMAT" -else - cat "$FILE" > "$FILE_FORMAT" -fi - -. @CUPS_DATADIR@/braille/cups-braille.sh - -checkTool inkscape inkscape "embossing ${INPUT_FORMAT} vector images" - -INKSCAPE="inkscape --export-area-drawing" - -echo "INFO: Converting image" 1>&2 -printf "DEBUG: Calling $INKSCAPE on '%s'\n" "$FILE_FORMAT" 1>&2 - -CURR_VERSION=$(inkscape --version | awk '{print $2}') -NEW_VERSION='1.0' - -printf "DEBUG: Inkscape version: '%s'\n" "$CURR_VERSION" 1>&2 - -# Inkscape versions 1.0.x and greater do not support '-A' flag - -if [ $(printf '%s\n' "$NEW_VERSION" "$CURR_VERSION" | sort -V | head -n1) = "$NEW_VERSION" ]; then - $INKSCAPE --export-type=pdf --export-filename="$FILE_PDF" "$FILE_FORMAT" -else - $INSCKAPE -z -A "$FILE_PDF" "$FILE_FORMAT" -fi - -cat "$FILE_PDF" - -echo "INFO: Ready" >&2 diff --git a/libcupsfilters.pc.in b/libcupsfilters.pc.in deleted file mode 100644 index cdcd0af59..000000000 --- a/libcupsfilters.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libcupsfilters -Description: Library providing everything for filtering/converting print/scan job data -Version: @VERSION@ - -Libs: -L${libdir} -lcupsfilters -Libs.private: @CUPS_LIBS@ @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBTIFF_LIBS@ -Cflags: -I${includedir}/cupsfilters diff --git a/libppd.pc.in b/libppd.pc.in deleted file mode 100644 index 481a293ff..000000000 --- a/libppd.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libppd -Description: Library for handling PPD files and PostScript output -Version: @VERSION@ - -Libs: -L${libdir} -lppd -Libs.private: @CUPS_LIBS@ -Cflags: -I${includedir}/ppd diff --git a/mime/braille.convs b/mime/braille.convs deleted file mode 100644 index d741778e1..000000000 --- a/mime/braille.convs +++ /dev/null @@ -1,94 +0,0 @@ -# -# Copyright (c) 2015, 2017 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -# Textual conversions -text/plain application/vnd.cups-brf 0 texttobrf - -text/html application/vnd.cups-brf 10 texttobrf -application/xhtml application/vnd.cups-brf 10 texttobrf -application/xml application/vnd.cups-brf 10 texttobrf -application/sgml application/vnd.cups-brf 10 texttobrf - -application/vnd.cups-brf application/vnd.cups-paged-brf 0 brftopagedbrf -application/vnd.cups-ubrl application/vnd.cups-paged-ubrl 0 brftopagedbrf - -application/vnd.recordare.musicxml+xml application/vnd.cups-brf 30 musicxmltobrf - -application/vnd.oasis.opendocument.chart application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.formula application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.graphics application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.graphics-template application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.presentation application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.presentation-template application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.spreadsheet application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.spreadsheet-template application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.text application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.text-master application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.text-template application/vnd.cups-brf 30 texttobrf -application/vnd.oasis.opendocument.text-web application/vnd.cups-brf 30 texttobrf - -application/msword application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.presentationml.presentation application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.presentationml.slide application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.presentationml.slideshow application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.presentationml.template application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.spreadsheetml.sheet application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.spreadsheetml.template application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.wordprocessingml.document application/vnd.cups-brf 30 texttobrf -application/vnd.openxmlformats-officedocument.wordprocessingml.template application/vnd.cups-brf 30 texttobrf - -text/rtf application/vnd.cups-brf 30 texttobrf -application/rtf application/vnd.cups-brf 30 texttobrf - -application/pdf application/vnd.cups-brf 100 texttobrf - - -# Picture conversions -image/gif image/vnd.cups-brf 70 imagetobrf -image/jpeg image/vnd.cups-brf 70 imagetobrf -image/pcx image/vnd.cups-brf 70 imagetobrf -image/png image/vnd.cups-brf 70 imagetobrf -image/tiff image/vnd.cups-brf 70 imagetobrf -image/vnd.microsoft.icon image/vnd.cups-brf 70 imagetobrf -image/x-ms-bmp image/vnd.cups-brf 70 imagetobrf -image/x-portable-anymap image/vnd.cups-brf 70 imagetobrf -image/x-portable-bitmap image/vnd.cups-brf 70 imagetobrf -image/x-portable-graymap image/vnd.cups-brf 70 imagetobrf -image/x-portable-pixmap image/vnd.cups-brf 70 imagetobrf -image/x-xbitmap image/vnd.cups-brf 70 imagetobrf -image/x-xpixmap image/vnd.cups-brf 70 imagetobrf -image/x-xwindowdump image/vnd.cups-brf 70 imagetobrf - -image/gif image/vnd.cups-ubrl 70 imagetoubrl -image/jpeg image/vnd.cups-ubrl 70 imagetoubrl -image/pcx image/vnd.cups-ubrl 70 imagetoubrl -image/png image/vnd.cups-ubrl 70 imagetoubrl -image/tiff image/vnd.cups-ubrl 70 imagetoubrl -image/vnd.microsoft.icon image/vnd.cups-ubrl 70 imagetoubrl -image/x-ms-bmp image/vnd.cups-ubrl 70 imagetoubrl -image/x-portable-anymap image/vnd.cups-ubrl 70 imagetoubrl -image/x-portable-bitmap image/vnd.cups-ubrl 70 imagetoubrl -image/x-portable-graymap image/vnd.cups-ubrl 70 imagetoubrl -image/x-portable-pixmap image/vnd.cups-ubrl 70 imagetoubrl -image/x-xbitmap image/vnd.cups-ubrl 70 imagetoubrl -image/x-xpixmap image/vnd.cups-ubrl 70 imagetoubrl -image/x-xwindowdump image/vnd.cups-ubrl 70 imagetoubrl - -image/svg image/vnd.cups-pdf 30 svgtopdf -image/svg+xml image/vnd.cups-pdf 30 svgtopdf -application/x-xfig image/vnd.cups-pdf 30 xfigtopdf -image/wmf image/vnd.cups-pdf 30 wmftopdf -image/x-wmf image/vnd.cups-pdf 30 wmftopdf -windows/metafile image/vnd.cups-pdf 30 wmftopdf -application/x-msmetafile image/vnd.cups-pdf 30 wmftopdf -image/emf image/vnd.cups-pdf 30 emftopdf -image/x-emf image/vnd.cups-pdf 30 emftopdf -image/cgm image/vnd.cups-pdf 30 cgmtopdf -image/x-cmx image/vnd.cups-pdf 30 cmxtopdf - -image/vnd.cups-pdf image/vnd.cups-brf 30 vectortobrf -image/vnd.cups-pdf image/vnd.cups-ubrl 30 vectortoubrl diff --git a/mime/braille.types b/mime/braille.types deleted file mode 100644 index f55ef2a83..000000000 --- a/mime/braille.types +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright (c) 2015, 2017 Samuel Thibault -# -# Licensed under Apache License v2.0. See the file "LICENSE" for more -# information. -# - -application/vnd.cups-brf brf -application/vnd.cups-ubrl -image/vnd.cups-brf -image/vnd.cups-ubrl -image/vnd.cups-pdf - -application/vnd.cups-paged-brf -application/vnd.cups-paged-ubrl - -application/sgml sgml - -application/vnd.recordare.musicxml+xml xml contains(0,1000,") -application/vnd.openxmlformats-officedocument.presentationml.presentation pptx -application/vnd.openxmlformats-officedocument.presentationml.slide sldx -application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx -application/vnd.openxmlformats-officedocument.presentationml.template potx -application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx -application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx -application/vnd.openxmlformats-officedocument.wordprocessingml.document docx -application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx - -image/svg+xml svg svgz -application/x-xfig fig -image/wmf wmf -image/emf emf -image/cgm cgm -image/x-cmx cmx diff --git a/ppd/README.md b/ppd/README.md deleted file mode 100644 index 9d39ca274..000000000 --- a/ppd/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# libppd - -One important point for the CUPS Snap (and also CUPS from version 3.x on) is that it does not support classic printer drivers, consisting of PPD files and filters. So for using it as default CUPS implementation in a Linux distribution, all existing printer drivers need to get turned into Printer Applications, and this with a minimum effort of coding. - -Most of them (probably all except Gutenprint) are difficult to get converted by their original authors, as they do not maintain the drivers any more, supported printers are old and no one wants to code for that any more, driver is simply only a big bunch of PPD files, no code, driver is proprietary, closed source, ... - -So we need a way to retro-fit these drivers by wrapping them into Printer Applications with lowest coding effort possible and if needed also without needing to modify the original driver executables. - -This works best if we use the driver's PPD files inside the Printer Application and so we need to handle PPD files, also after the PPD handling support got removed from CUPS and especially libcups. - -To avoid that we have to invent the wheel again, writing a lot of handling code for a totally obsolete file format, I have grabbed all the PPD handling functions from libcups and from ppdc/ (current GitHub state, CUPS 2.3.3) and put them into the new libppd which I have added to cups-filters. - -It has the following properties: -- All PPD-handling-related functions from libcups (except loading the PPD from a CUPS queue or polling a PPD repository on a CUPS server) are overtaken -- Also the CUPS-private functions related to PPDs are overtaken and added to libppd's public API. -- Other private or internal functions are overtaken from libcups as they are needed for the PPD-related functions to work. They are not added to the API. -- Some functions of tools and utilities like ippeveprinter and ippeveps are overtaken. -- The PPD compiler code (ppdc/ directory) is also overtaken into libppd and the ppdc utilities are overtaken to cups-filters. This allows retro-fitting printer drivers with driver information files (*.drv) instead of ready-made PPDs, both by pre-building the PPDs or by letting them get generated on-the-fly. -- All API functions have names starting with "ppd" (or "ppdc") and written in camel-case, some needed to get renamed for that. -- libppd is separate from libcupsfilters, so it does not need to get included in a Printer Application which uses functionality of cups-filters but does not use PPD files - -NOTE: This is NOT to encourage printer driver developers to continue to create new PPD files and *.drv files for new printers. It is ONLY for retro-fitting existing classic CUPS drivers and PostScript PPD files. There will be no further development on the library's code, especially no new PPD or drv format extensions. diff --git a/ppd/array-private.h b/ppd/array-private.h deleted file mode 100644 index d2a6418b2..000000000 --- a/ppd/array-private.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Private array definitions for libppd. -// -// Copyright 2011-2012 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_ARRAY_PRIVATE_H_ -# define _PPD_ARRAY_PRIVATE_H_ - -// -// Include necessary headers... -// - -# include - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Functions... -// - -extern int _ppdArrayAddStrings(cups_array_t *a, const char *s, - char delim); -extern cups_array_t *_ppdArrayNewStrings(const char *s, char delim); - -# ifdef __cplusplus -} -# endif // __cplusplus -#endif // !_PPD_ARRAY_PRIVATE_H_ diff --git a/ppd/array.c b/ppd/array.c deleted file mode 100644 index 3c7a48eff..000000000 --- a/ppd/array.c +++ /dev/null @@ -1,138 +0,0 @@ -// -// Sorted array routines for libppd. -// -// Copyright 2007-2014 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "debug-internal.h" -#include "array-private.h" - - -// -// '_ppdArrayAddStrings()' - Add zero or more delimited strings to an array. -// -// Note: The array MUST be created using the @link _ppdArrayNewStrings@ -// function. Duplicate strings are NOT added. If the string pointer "s" is NULL -// or the empty string, no strings are added to the array. -// - -int // O - 1 on success, 0 on failure -_ppdArrayAddStrings(cups_array_t *a, // I - Array - const char *s, // I - Delimited strings or NULL - char delim) // I - Delimiter character -{ - char *buffer, // Copy of string - *start, // Start of string - *end; // End of string - int status = 1; // Status of add - - - DEBUG_printf(("_ppdArrayAddStrings(a=%p, s=\"%s\", delim='%c')", - (void *)a, s, delim)); - - if (!a || !s || !*s) - { - DEBUG_puts("1_ppdArrayAddStrings: Returning 0"); - return (0); - } - - if (delim == ' ') - { - // - // Skip leading whitespace... - // - - DEBUG_puts("1_ppdArrayAddStrings: Skipping leading whitespace."); - - while (*s && isspace(*s & 255)) - s ++; - - DEBUG_printf(("1_ppdArrayAddStrings: Remaining string \"%s\".", s)); - } - - if (!strchr(s, delim) && - (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n')))) - { - // - // String doesn't contain a delimiter, so add it as a single value... - // - - DEBUG_puts("1_ppdArrayAddStrings: No delimiter seen, adding a single " - "value."); - - if (!cupsArrayFind(a, (void *)s)) - status = cupsArrayAdd(a, (void *)s); - } - else if ((buffer = strdup(s)) == NULL) - { - DEBUG_puts("1_ppdArrayAddStrings: Unable to duplicate string."); - status = 0; - } - else - { - for (start = end = buffer; *end; start = end) - { - // - // Find the end of the current delimited string and see if we need to add - // it... - // - - if (delim == ' ') - { - while (*end && !isspace(*end & 255)) - end ++; - while (*end && isspace(*end & 255)) - *end++ = '\0'; - } - else if ((end = strchr(start, delim)) != NULL) - *end++ = '\0'; - else - end = start + strlen(start); - - DEBUG_printf(("1_ppdArrayAddStrings: Adding \"%s\", end=\"%s\"", start, - end)); - - if (!cupsArrayFind(a, start)) - status &= cupsArrayAdd(a, start); - } - - free(buffer); - } - - DEBUG_printf(("1_ppdArrayAddStrings: Returning %d.", status)); - - return (status); -} - - -// -// '_ppdArrayNewStrings()' - Create a new array of comma-delimited strings. -// -// Note: The array automatically manages copies of the strings passed. If the -// string pointer "s" is NULL or the empty string, no strings are added to the -// newly created array. -// - -cups_array_t * // O - Array -_ppdArrayNewStrings(const char *s, // I - Delimited strings or NULL - char delim) // I - Delimiter character -{ - cups_array_t *a; // Array - - - if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, - (cups_acopy_func_t)_ppdStrAlloc, - (cups_afree_func_t)_ppdStrFree)) != NULL) - _ppdArrayAddStrings(a, s, delim); - - return (a); -} diff --git a/ppd/debug-internal.h b/ppd/debug-internal.h deleted file mode 100644 index 8a6f38ef5..000000000 --- a/ppd/debug-internal.h +++ /dev/null @@ -1,87 +0,0 @@ -// -// Internal debugging macros for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_DEBUG_INTERNAL_H_ -# define _PPD_DEBUG_INTERNAL_H_ - - -// -// Include necessary headers... -// - -# include "debug-private.h" - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// 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... -// -// Newlines are not required on the end of messages, as both add one when -// writing the output. -// -// If the first character is a digit, then it represents the "log level" of the -// message from 0 to 9. The default level is 1. The following defines the -// current levels we use: -// -// 0 = public APIs, other than value accessor functions -// 1 = return values for public APIs -// 2 = public value accessor APIs, progress for public APIs -// 3 = return values for value accessor APIs -// 4 = private APIs, progress for value accessor APIs -// 5 = return values for private APIs -// 6 = progress for private APIs -// 7 = static functions -// 8 = return values for static functions -// 9 = progress for static functions -// - -# ifdef DEBUG -# include -# define DEBUG_puts(x) _ppd_debug_puts(x) -# define DEBUG_printf(x) _ppd_debug_printf x -# define DEBUG_assert(x) assert(x) -# else -# define DEBUG_puts(x) -# define DEBUG_printf(x) -# define DEBUG_assert(x) -# endif // DEBUG - - -// -// Prototypes... -// - -# ifdef DEBUG -extern int _ppd_debug_fd; -extern int _ppd_debug_level; -extern void _ppd_debug_printf(const char *format, ...); -extern void _ppd_debug_puts(const char *s); -# endif // DEBUG - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_PPD_DEBUG_INTERNAL_H_ diff --git a/ppd/debug-private.h b/ppd/debug-private.h deleted file mode 100644 index 4be572269..000000000 --- a/ppd/debug-private.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// Private debugging APIs for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_DEBUG_PRIVATE_H_ -# define _PPD_DEBUG_PRIVATE_H_ - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// The debug macros are used if you compile with DEBUG defined. -// -// Usage: -// -// DEBUG_set("logfile", "level", "filter", 1) -// -// The DEBUG_set macro allows an application to programmatically enable (or -// disable) debug logging. The arguments correspond to the PPD_DEBUG_LOG, -// PPD_DEBUG_LEVEL, and PPD_DEBUG_FILTER environment variables. The 1 on the -// end forces the values to override the environment. -// - -# ifdef DEBUG -# define DEBUG_set(logfile,level,filter) _ppd_debug_set(logfile,level,filter,1) -# else -# define DEBUG_set(logfile,level,filter) -# endif // DEBUG - - -// -// Prototypes... -// - -extern void _ppd_debug_set(const char *logfile, const char *level, const char *filter, int force); -# ifdef _WIN32 -extern int _ppd_gettimeofday(struct timeval *tv, void *tz); -# define gettimeofday(a,b) _ppd_gettimeofday(a, b) -# endif // _WIN32 - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_PPD_DEBUG_PRIVATE_H_ diff --git a/ppd/debug.c b/ppd/debug.c deleted file mode 100644 index 4120f5905..000000000 --- a/ppd/debug.c +++ /dev/null @@ -1,627 +0,0 @@ -// -// Debugging functions for libppd. -// -// Copyright © 2008-2018 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "debug-internal.h" -#ifdef _WIN32 -# include -# include -# include -# define getpid (int)GetCurrentProcessId -int // O - 0 on success, -1 on failure -_ppd_gettimeofday(struct timeval *tv, // I - Timeval struct - void *tz) // I - Timezone -{ - struct _timeb timebuffer; // Time buffer struct - _ftime(&timebuffer); - tv->tv_sec = (long)timebuffer.time; - tv->tv_usec = timebuffer.millitm * 1000; - return 0; -} -#else -# include -# include -#endif // _WIN32 -#include -#include - - -#ifdef DEBUG -// -// Globals... -// - -int _ppd_debug_fd = -1; - // Debug log file descriptor -int _ppd_debug_level = 1; - // Log level (0 to 9) - - -// -// Local globals... -// - -static regex_t *debug_filter = NULL; - // Filter expression for messages -static int debug_init = 0; // Did we initialize debugging? - - -// -// '_ppd_debug_printf()' - Write a formatted line to the log. -// - -void -_ppd_debug_printf(const char *format, // I - Printf-style format string - ...) // I - Additional arguments as needed -{ - va_list ap; // Pointer to arguments - struct timeval curtime; // Current time - char buffer[2048]; // Output buffer - ssize_t bytes; // Number of bytes in buffer - int level; // Log level in message - - - // - // See if we need to do any logging... - // - - if (!debug_init) - _ppd_debug_set(getenv("PPD_DEBUG_LOG"), getenv("PPD_DEBUG_LEVEL"), - getenv("PPD_DEBUG_FILTER"), 0); - - if (_ppd_debug_fd < 0) - return; - - // - // Filter as needed... - // - - if (isdigit(format[0])) - level = *format++ - '0'; - else - level = 0; - - if (level > _ppd_debug_level) - return; - - if (debug_filter) - { - int result; // Filter result - - result = regexec(debug_filter, format, 0, NULL, 0); - - if (result) - return; - } - - // - // Format the message... - // - - gettimeofday(&curtime, NULL); - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%03d ", - (int)((curtime.tv_sec / 3600) % 24), - (int)((curtime.tv_sec / 60) % 60), - (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000)); - - va_start(ap, format); - bytes = _ppd_safe_vsnprintf(buffer + 19, sizeof(buffer) - 20, format, ap) + - 19; - va_end(ap); - - if ((size_t)bytes >= (sizeof(buffer) - 1)) - { - buffer[sizeof(buffer) - 2] = '\n'; - bytes = sizeof(buffer) - 1; - } - else if (buffer[bytes - 1] != '\n') - { - buffer[bytes++] = '\n'; - buffer[bytes] = '\0'; - } - - // - // Write it out... - // - - write(_ppd_debug_fd, buffer, (size_t)bytes); -} - - -// -// '_ppd_debug_puts()' - Write a single line to the log. -// - -void -_ppd_debug_puts(const char *s) // I - String to output -{ - struct timeval curtime; // Current time - char buffer[2048]; // Output buffer - ssize_t bytes; // Number of bytes in buffer - int level; // Log level in message - - - // - // See if we need to do any logging... - // - - if (!debug_init) - _ppd_debug_set(getenv("PPD_DEBUG_LOG"), getenv("PPD_DEBUG_LEVEL"), - getenv("PPD_DEBUG_FILTER"), 0); - - if (_ppd_debug_fd < 0) - return; - - // - // Filter as needed... - // - - if (isdigit(s[0])) - level = *s++ - '0'; - else - level = 0; - - if (level > _ppd_debug_level) - return; - - if (debug_filter) - { - int result; // Filter result - - result = regexec(debug_filter, s, 0, NULL, 0); - - if (result) - return; - } - - // - // Format the message... - // - - gettimeofday(&curtime, NULL); - bytes = snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%03d %s", - (int)((curtime.tv_sec / 3600) % 24), - (int)((curtime.tv_sec / 60) % 60), - (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000), - s); - - if ((size_t)bytes >= (sizeof(buffer) - 1)) - { - buffer[sizeof(buffer) - 2] = '\n'; - bytes = sizeof(buffer) - 1; - } - else if (buffer[bytes - 1] != '\n') - { - buffer[bytes++] = '\n'; - buffer[bytes] = '\0'; - } - - // - // Write it out... - // - - write(_ppd_debug_fd, buffer, (size_t)bytes); -} - - -// -// '_ppd_debug_set()' - Enable or disable debug logging. -// - -void -_ppd_debug_set(const char *logfile, // I - Log file or NULL - const char *level, // I - Log level or NULL - const char *filter, // I - Filter string or NULL - int force) // I - Force initialization -{ - - if (!debug_init || force) - { - // - // Restore debug settings to defaults... - // - - if (_ppd_debug_fd != -1) - { - close(_ppd_debug_fd); - _ppd_debug_fd = -1; - } - - if (debug_filter) - { - regfree((regex_t *)debug_filter); - debug_filter = NULL; - } - - _ppd_debug_level = 1; - - // - // Open logs, set log levels, etc. - // - - if (!logfile) - _ppd_debug_fd = -1; - else if (!strcmp(logfile, "-")) - _ppd_debug_fd = 2; - else - { - char buffer[1024]; // Filename buffer - - snprintf(buffer, sizeof(buffer), logfile, getpid()); - - if (buffer[0] == '+') - _ppd_debug_fd = open(buffer + 1, O_WRONLY | O_APPEND | O_CREAT, 0644); - else - _ppd_debug_fd = open(buffer, O_WRONLY | O_TRUNC | O_CREAT, 0644); - } - - if (level) - _ppd_debug_level = atoi(level); - - if (filter) - { - if ((debug_filter = (regex_t *)calloc(1, sizeof(regex_t))) == NULL) - fputs("Unable to allocate memory for PPD_DEBUG_FILTER - results not " - "filtered!\n", stderr); - else if (regcomp(debug_filter, filter, REG_EXTENDED)) - { - fputs("Bad regular expression in PPD_DEBUG_FILTER - results not " - "filtered!\n", stderr); - free(debug_filter); - debug_filter = NULL; - } - } - - debug_init = 1; - } -} - - -#else -// -// '_ppd_debug_set()' - Enable or disable debug logging. -// - -void -_ppd_debug_set(const char *logfile, // I - Log file or NULL - const char *level, // I - Log level or NULL - const char *filter, // I - Filter string or NULL - int force) // I - Force initialization -{ - (void)logfile; - (void)level; - (void)filter; - (void)force; -} -#endif // DEBUG - - -// -// '_ppd_safe_vsnprintf()' - Format a string into a fixed size buffer, -// quoting special characters. -// - -ssize_t // O - Number of bytes formatted -_ppd_safe_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 - size, // Size character (h, l, L) - type; // Format type character - int width, // Width of field - prec; // Number of characters of precision - char tformat[100], // Temporary format string for snprintf() - *tptr, // Pointer into temporary format - temp[1024]; // Buffer for formatted numbers - char *s; // Pointer to string - ssize_t bytes; // Total number of bytes needed - - - if (!buffer || bufsize < 2 || !format) - return (-1); - - // - // Loop through the format string, formatting as needed... - // - - bufptr = buffer; - bufend = buffer + bufsize - 1; - bytes = 0; - - while (*format) - { - if (*format == '%') - { - tptr = tformat; - *tptr++ = *format++; - - if (*format == '%') - { - if (bufptr < bufend) - *bufptr++ = *format; - bytes ++; - format ++; - continue; - } - else if (strchr(" -+#\'", *format)) - *tptr++ = *format++; - - if (*format == '*') - { - // - // Get width from argument... - // - - format ++; - width = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width); - tptr += strlen(tptr); - } - else - { - width = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - width = width * 10 + *format++ - '0'; - } - } - - if (*format == '.') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - format ++; - - if (*format == '*') - { - // - // Get precision from argument... - // - - format ++; - prec = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec); - tptr += strlen(tptr); - } - else - { - prec = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - prec = prec * 10 + *format++ - '0'; - } - } - } - - if (*format == 'l' && format[1] == 'l') - { - size = 'L'; - - if (tptr < (tformat + sizeof(tformat) - 2)) - { - *tptr++ = 'l'; - *tptr++ = 'l'; - } - - format += 2; - } - else if (*format == 'h' || *format == 'l' || *format == 'L') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - size = *format++; - } - else - size = 0; - - if (!*format) - break; - - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - type = *format++; - *tptr = '\0'; - - switch (type) - { - case 'E' : // Floating point formats - case 'G' : - case 'e' : - case 'f' : - case 'g' : - if ((size_t)(width + 2) > sizeof(temp)) - break; - - snprintf(temp, sizeof(temp), tformat, va_arg(ap, double)); - - bytes += (int)strlen(temp); - - if (bufptr) - { - strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr += strlen(bufptr); - } - break; - - case 'B' : // Integer formats - case 'X' : - case 'b' : - case 'd' : - case 'i' : - case 'o' : - case 'u' : - case 'x' : - if ((size_t)(width + 2) > sizeof(temp)) - break; - -# ifdef HAVE_LONG_LONG - if (size == 'L') - snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long)); - else -# endif // HAVE_LONG_LONG - if (size == 'l') - snprintf(temp, sizeof(temp), tformat, va_arg(ap, long)); - else - snprintf(temp, sizeof(temp), tformat, va_arg(ap, int)); - - bytes += (int)strlen(temp); - - if (bufptr) - { - strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr += strlen(bufptr); - } - break; - - case 'p' : // Pointer value - if ((size_t)(width + 2) > sizeof(temp)) - break; - - snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *)); - - bytes += (int)strlen(temp); - - if (bufptr) - { - strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr += strlen(bufptr); - } - break; - - case 'c' : // Character or character array - bytes += width; - - if (bufptr) - { - if (width <= 1) - *bufptr++ = (char)va_arg(ap, int); - else - { - if ((bufptr + width) > bufend) - width = (int)(bufend - bufptr); - - memcpy(bufptr, va_arg(ap, char *), (size_t)width); - bufptr += width; - } - } - break; - - case 's' : // String - if ((s = va_arg(ap, char *)) == NULL) - s = "(null)"; - - // - // Copy the C string, replacing control chars and \ with - // C character escapes... - // - - for (bufend --; *s && bufptr < bufend; s ++) - { - if (*s == '\n') - { - *bufptr++ = '\\'; - *bufptr++ = 'n'; - bytes += 2; - } - else if (*s == '\r') - { - *bufptr++ = '\\'; - *bufptr++ = 'r'; - bytes += 2; - } - else if (*s == '\t') - { - *bufptr++ = '\\'; - *bufptr++ = 't'; - bytes += 2; - } - else if (*s == '\\') - { - *bufptr++ = '\\'; - *bufptr++ = '\\'; - bytes += 2; - } - else if (*s == '\'') - { - *bufptr++ = '\\'; - *bufptr++ = '\''; - bytes += 2; - } - else if (*s == '\"') - { - *bufptr++ = '\\'; - *bufptr++ = '\"'; - bytes += 2; - } - else if ((*s & 255) < ' ') - { - if ((bufptr + 2) >= bufend) - break; - - *bufptr++ = '\\'; - *bufptr++ = '0'; - *bufptr++ = '0' + *s / 8; - *bufptr++ = '0' + (*s & 7); - bytes += 4; - } - else - { - *bufptr++ = *s; - bytes ++; - } - } - - bufend ++; - break; - - case 'n' : // Output number of chars so far - *(va_arg(ap, int *)) = (int)bytes; - break; - } - } - else - { - bytes ++; - - if (bufptr < bufend) - *bufptr++ = *format; - - format ++; - } - } - - // - // Nul-terminate the string and return the number of characters needed. - // - - *bufptr = '\0'; - - return (bytes); -} diff --git a/ppd/encode.c b/ppd/encode.c deleted file mode 100644 index 8433771ab..000000000 --- a/ppd/encode.c +++ /dev/null @@ -1,411 +0,0 @@ -// -// Option encoding routines for libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ipp-private.h" -#include "debug-internal.h" - - -// -// Local list of option names, the value tags they should use, and the list of -// supported operations... -// -// **** THIS LIST MUST BE SORTED BY ATTRIBUTE NAME **** -// - -static const ipp_op_t ipp_job_creation[] = -{ - IPP_OP_PRINT_JOB, - IPP_OP_PRINT_URI, - IPP_OP_VALIDATE_JOB, - IPP_OP_CREATE_JOB, - IPP_OP_HOLD_JOB, - IPP_OP_SET_JOB_ATTRIBUTES, - IPP_OP_CUPS_NONE -}; - -static const ipp_op_t ipp_doc_creation[] = -{ - IPP_OP_PRINT_JOB, - IPP_OP_PRINT_URI, - IPP_OP_SEND_DOCUMENT, - IPP_OP_SEND_URI, - IPP_OP_SET_JOB_ATTRIBUTES, - IPP_OP_SET_DOCUMENT_ATTRIBUTES, - IPP_OP_CUPS_NONE -}; - -static const ipp_op_t ipp_all_print[] = -{ - IPP_OP_PRINT_JOB, - IPP_OP_PRINT_URI, - IPP_OP_VALIDATE_JOB, - IPP_OP_CREATE_JOB, - IPP_OP_SEND_DOCUMENT, - IPP_OP_SEND_URI, - IPP_OP_CUPS_NONE -}; - -static const ipp_op_t cups_schemes[] = -{ - IPP_OP_CUPS_GET_DEVICES, - IPP_OP_CUPS_GET_PPDS, - IPP_OP_CUPS_NONE -}; - -static const ipp_op_t cups_get_ppds[] = -{ - IPP_OP_CUPS_GET_PPDS, - IPP_OP_CUPS_NONE -}; - -static const ipp_op_t cups_ppd_name[] = -{ - IPP_OP_CUPS_ADD_MODIFY_PRINTER, - IPP_OP_CUPS_GET_PPD, - IPP_OP_CUPS_NONE -}; - -static const _ppd_ipp_option_t ipp_options[] = -{ - { 1, "auth-info", IPP_TAG_TEXT, IPP_TAG_JOB }, - { 1, "auth-info-default", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 1, "auth-info-required", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "blackplot", IPP_TAG_BOOLEAN, IPP_TAG_JOB }, - { 0, "blackplot-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "brightness", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "brightness-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "columns", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "columns-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "compression", IPP_TAG_KEYWORD, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - ipp_doc_creation }, - { 0, "copies", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "copies-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "date-time-at-completed",IPP_TAG_DATE, IPP_TAG_ZERO }, - // never send as option - { 0, "date-time-at-creation", IPP_TAG_DATE, IPP_TAG_ZERO }, - // never send as option - { 0, "date-time-at-processing",IPP_TAG_DATE, IPP_TAG_ZERO }, - // never send as option - { 0, "device-uri", IPP_TAG_URI, IPP_TAG_PRINTER }, - { 1, "document-copies", IPP_TAG_RANGE, IPP_TAG_JOB, - IPP_TAG_DOCUMENT, - ipp_doc_creation }, - { 0, "document-format", IPP_TAG_MIMETYPE, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - ipp_doc_creation }, - { 0, "document-format-default", IPP_TAG_MIMETYPE, IPP_TAG_PRINTER }, - { 1, "document-numbers", IPP_TAG_RANGE, IPP_TAG_JOB, - IPP_TAG_DOCUMENT, - ipp_all_print }, - { 1, "exclude-schemes", IPP_TAG_NAME, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_schemes }, - { 1, "finishings", IPP_TAG_ENUM, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 1, "finishings-col", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 1, "finishings-col-default", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_PRINTER }, - { 1, "finishings-default", IPP_TAG_ENUM, IPP_TAG_PRINTER }, - { 0, "fit-to-page", IPP_TAG_BOOLEAN, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "fit-to-page-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "fitplot", IPP_TAG_BOOLEAN, IPP_TAG_JOB }, - { 0, "fitplot-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "gamma", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "gamma-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "hue", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "hue-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "include-schemes", IPP_TAG_NAME, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_schemes }, - { 0, "ipp-attribute-fidelity", IPP_TAG_BOOLEAN, IPP_TAG_OPERATION }, - { 0, "job-account-id", IPP_TAG_NAME, IPP_TAG_JOB }, - { 0, "job-account-id-default",IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 0, "job-accounting-user-id", IPP_TAG_NAME, IPP_TAG_JOB }, - { 0, "job-accounting-user-id-default", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 0, "job-authorization-uri", IPP_TAG_URI, IPP_TAG_OPERATION }, - { 0, "job-cancel-after", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "job-cancel-after-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "job-hold-until", IPP_TAG_KEYWORD, IPP_TAG_JOB }, - { 0, "job-hold-until-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "job-id", IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "job-impressions", IPP_TAG_INTEGER, IPP_TAG_OPERATION }, - { 0, "job-impressions-completed", IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "job-k-limit", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "job-k-octets", IPP_TAG_INTEGER, IPP_TAG_OPERATION }, - { 0, "job-k-octets-completed",IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "job-media-sheets", IPP_TAG_INTEGER, IPP_TAG_OPERATION }, - { 0, "job-media-sheets-completed", IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "job-name", IPP_TAG_NAME, IPP_TAG_OPERATION, - IPP_TAG_JOB }, - { 0, "job-page-limit", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "job-pages", IPP_TAG_INTEGER, IPP_TAG_OPERATION }, - { 0, "job-pages-completed", IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "job-password", IPP_TAG_STRING, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - ipp_job_creation }, - { 0, "job-password-encryption", IPP_TAG_KEYWORD, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - ipp_job_creation }, - { 0, "job-priority", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "job-priority-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "job-quota-period", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "job-sheets", IPP_TAG_NAME, IPP_TAG_JOB }, - { 1, "job-sheets-default", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 0, "job-state", IPP_TAG_ENUM, IPP_TAG_ZERO }, - // never send as option - { 0, "job-state-message", IPP_TAG_TEXT, IPP_TAG_ZERO }, - // never send as option - { 0, "job-state-reasons", IPP_TAG_KEYWORD, IPP_TAG_ZERO }, - // never send as option - { 0, "job-uuid", IPP_TAG_URI, IPP_TAG_JOB }, - { 0, "landscape", IPP_TAG_BOOLEAN, IPP_TAG_JOB }, - { 1, "marker-change-time", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "marker-colors", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 1, "marker-high-levels", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "marker-levels", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "marker-low-levels", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "marker-message", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 1, "marker-names", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 1, "marker-types", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 1, "media", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-bottom-margin", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-col", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-col-default", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_PRINTER }, - { 0, "media-color", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 1, "media-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "media-key", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-left-margin", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-right-margin", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-size", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-size-name", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-source", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-top-margin", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "media-type", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "mirror", IPP_TAG_BOOLEAN, IPP_TAG_JOB }, - { 0, "mirror-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "multiple-document-handling", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "multiple-document-handling-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "natural-scaling", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "natural-scaling-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "notify-charset", IPP_TAG_CHARSET, IPP_TAG_SUBSCRIPTION }, - { 1, "notify-events", IPP_TAG_KEYWORD, IPP_TAG_SUBSCRIPTION }, - { 1, "notify-events-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "notify-lease-duration", IPP_TAG_INTEGER, IPP_TAG_SUBSCRIPTION }, - { 0, "notify-lease-duration-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "notify-natural-language", IPP_TAG_LANGUAGE, IPP_TAG_SUBSCRIPTION }, - { 0, "notify-pull-method", IPP_TAG_KEYWORD, IPP_TAG_SUBSCRIPTION }, - { 0, "notify-recipient-uri", IPP_TAG_URI, IPP_TAG_SUBSCRIPTION }, - { 0, "notify-time-interval", IPP_TAG_INTEGER, IPP_TAG_SUBSCRIPTION }, - { 0, "notify-user-data", IPP_TAG_STRING, IPP_TAG_SUBSCRIPTION }, - { 0, "number-up", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "number-up-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "number-up-layout", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "number-up-layout-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "orientation-requested", IPP_TAG_ENUM, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "orientation-requested-default", IPP_TAG_ENUM, IPP_TAG_PRINTER }, - { 0, "output-bin", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "output-bin-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 1, "overrides", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "page-bottom", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "page-bottom-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "page-delivery", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "page-delivery-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "page-left", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "page-left-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "page-ranges", IPP_TAG_RANGE, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "page-right", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "page-right-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "page-top", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "page-top-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "pages", IPP_TAG_RANGE, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "penwidth", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "penwidth-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "port-monitor", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 0, "ppd-device-id", IPP_TAG_TEXT, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppd-make", IPP_TAG_TEXT, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppd-make-and-model", IPP_TAG_TEXT, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppd-model-number", IPP_TAG_INTEGER, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppd-name", IPP_TAG_NAME, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_ppd_name }, - { 0, "ppd-natural-language", IPP_TAG_LANGUAGE, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppd-product", IPP_TAG_TEXT, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppd-psversion", IPP_TAG_TEXT, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppd-type", IPP_TAG_KEYWORD, IPP_TAG_OPERATION, - IPP_TAG_ZERO, - cups_get_ppds }, - { 0, "ppi", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "ppi-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "prettyprint", IPP_TAG_BOOLEAN, IPP_TAG_JOB }, - { 0, "prettyprint-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "print-color-mode", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "print-color-mode-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "print-content-optimize", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "print-content-optimize-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "print-quality", IPP_TAG_ENUM, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "print-quality-default", IPP_TAG_ENUM, IPP_TAG_PRINTER }, - { 0, "print-rendering-intent", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "print-rendering-intent-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "print-scaling", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "print-scaling-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 1, "printer-alert", IPP_TAG_STRING, IPP_TAG_PRINTER }, - { 1, "printer-alert-description", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 1, "printer-commands", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "printer-error-policy", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 1, "printer-finisher", IPP_TAG_STRING, IPP_TAG_PRINTER }, - { 1, "printer-finisher-description", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 1, "printer-finisher-supplies", IPP_TAG_STRING, IPP_TAG_PRINTER }, - { 1, "printer-finisher-supplies-description", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 0, "printer-geo-location", IPP_TAG_URI, IPP_TAG_PRINTER }, - { 0, "printer-info", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 1, "printer-input-tray", IPP_TAG_STRING, IPP_TAG_PRINTER }, - { 0, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "printer-is-shared", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "printer-is-temporary", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "printer-location", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 0, "printer-make-and-model", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 0, "printer-more-info", IPP_TAG_URI, IPP_TAG_PRINTER }, - { 0, "printer-op-policy", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 1, "printer-output-tray", IPP_TAG_STRING, IPP_TAG_PRINTER }, - { 0, "printer-resolution", IPP_TAG_RESOLUTION, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "printer-resolution-default", IPP_TAG_RESOLUTION, IPP_TAG_PRINTER }, - { 0, "printer-state", IPP_TAG_ENUM, IPP_TAG_PRINTER }, - { 0, "printer-state-change-time", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 1, "printer-state-reasons", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 1, "printer-supply", IPP_TAG_STRING, IPP_TAG_PRINTER }, - { 1, "printer-supply-description", IPP_TAG_TEXT, IPP_TAG_PRINTER }, - { 0, "printer-type", IPP_TAG_ENUM, IPP_TAG_PRINTER }, - { 0, "printer-uri", IPP_TAG_URI, IPP_TAG_OPERATION }, - { 1, "printer-uri-supported", IPP_TAG_URI, IPP_TAG_PRINTER }, - { 0, "queued-job-count", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "raw", IPP_TAG_MIMETYPE, IPP_TAG_OPERATION }, - { 1, "requested-attributes", IPP_TAG_NAME, IPP_TAG_OPERATION }, - { 1, "requesting-user-name-allowed", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 1, "requesting-user-name-denied", IPP_TAG_NAME, IPP_TAG_PRINTER }, - { 0, "resolution", IPP_TAG_RESOLUTION, IPP_TAG_JOB }, - { 0, "resolution-default", IPP_TAG_RESOLUTION, IPP_TAG_PRINTER }, - { 0, "saturation", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "saturation-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "scaling", IPP_TAG_INTEGER, IPP_TAG_JOB }, - { 0, "scaling-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER }, - { 0, "sides", IPP_TAG_KEYWORD, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "sides-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER }, - { 0, "time-at-completed", IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "time-at-creation", IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "time-at-processing", IPP_TAG_INTEGER, IPP_TAG_ZERO }, - // never send as option - { 0, "wrap", IPP_TAG_BOOLEAN, IPP_TAG_JOB }, - { 0, "wrap-default", IPP_TAG_BOOLEAN, IPP_TAG_PRINTER }, - { 0, "x-dimension", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT }, - { 0, "y-dimension", IPP_TAG_INTEGER, IPP_TAG_JOB, - IPP_TAG_DOCUMENT } -}; - - -// -// Local functions... -// - -static int ppd_compare_ipp_options(_ppd_ipp_option_t *a, - _ppd_ipp_option_t *b); - - -// -// '_ppdIppFindOption()' - Find the attribute information for an option. -// - -_ppd_ipp_option_t * // O - Attribute information -_ppdIppFindOption(const char *name) // I - Option/attribute name -{ - _ppd_ipp_option_t key; // Search key - - - // - // Lookup the proper value and group tags for this option... - // - - key.name = name; - - return ((_ppd_ipp_option_t *)bsearch(&key, ipp_options, - sizeof(ipp_options) / - sizeof(ipp_options[0]), - sizeof(ipp_options[0]), - (int (*)(const void *, const void *)) - ppd_compare_ipp_options)); -} - - -// -// 'ppd_compare_ipp_options()' - Compare two IPP options. -// - -static int // O - Result of comparison -ppd_compare_ipp_options(_ppd_ipp_option_t *a, // I - First option - _ppd_ipp_option_t *b) // I - Second option -{ - return (strcmp(a->name, b->name)); -} diff --git a/ppd/epson.h b/ppd/epson.h deleted file mode 100644 index ebbe50134..000000000 --- a/ppd/epson.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// This file contains model number definitions for the cups-filters sample -// ESC/P driver. -// -// Copyright 2007 by Apple Inc. -// Copyright 1997-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#define EPSON_9PIN 0 // 9-pin dot matrix -#define EPSON_24PIN 1 // 24-pin dot matrix -#define EPSON_COLOR 2 // Epson Stylus Color with ESC . -#define EPSON_PHOTO 3 // Epson Stylus Photo with ESC . -#define EPSON_ICOLOR 4 // Epson Stylus Color with ESC i -#define EPSON_IPHOTO 5 // Epson Stylus Photo with ESC i diff --git a/ppd/file-private.h b/ppd/file-private.h deleted file mode 100644 index 613d46c17..000000000 --- a/ppd/file-private.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// Private file check definitions for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_FILE_PRIVATE_H_ -# define _PPD_FILE_PRIVATE_H_ - -// -// Include necessary headers... -// - -# include "string-private.h" -# include -# include -# include -# include -# include - -# ifdef _WIN32 -# include -# include -# endif // _WIN32 - - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Types and structures... -// - -typedef enum // **** _ppdFileCheck return values **** -{ - _PPD_FILE_CHECK_OK = 0, // Everything OK - _PPD_FILE_CHECK_MISSING = 1, // File is missing - _PPD_FILE_CHECK_PERMISSIONS = 2, // File (or parent dir) has bad perms - _PPD_FILE_CHECK_WRONG_TYPE = 3, // File has wrong type - _PPD_FILE_CHECK_RELATIVE_PATH = 4 // File contains a relative path -} _ppd_fc_result_t; - -typedef enum // **** _ppdFileCheck file type - // values **** -{ - _PPD_FILE_CHECK_FILE = 0, // Check the file and parent - // directory - _PPD_FILE_CHECK_PROGRAM = 1, // Check the program and parent - // directory - _PPD_FILE_CHECK_FILE_ONLY = 2, // Check the file only - _PPD_FILE_CHECK_DIRECTORY = 3 // Check the directory -} _ppd_fc_filetype_t; - -typedef void (*_ppd_fc_func_t)(void *context, _ppd_fc_result_t result, - const char *message); - -// -// Prototypes... -// - -extern _ppd_fc_result_t _ppdFileCheck(const char *filename, - _ppd_fc_filetype_t filetype, - int dorootchecks, - cf_logfunc_t log, - void *ld); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_PPD_FILE_PRIVATE_H_ diff --git a/ppd/file.c b/ppd/file.c deleted file mode 100644 index ee591b1a2..000000000 --- a/ppd/file.c +++ /dev/null @@ -1,259 +0,0 @@ -// -// File functions for libppd -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. - - -// -// Include necessary headers... -// - -#include "file-private.h" -#include "language-private.h" -#include "debug-internal.h" -#include -#include -#include - -# ifdef HAVE_LIBZ -# include -# endif // HAVE_LIBZ - - -#ifndef _WIN32 -// -// '_ppdFileCheck()' - Check the permissions of the given filename. -// - -_ppd_fc_result_t // O - Check result -_ppdFileCheck( - const char *filename, // I - Filename to check - _ppd_fc_filetype_t filetype, // I - Type of file checks? - int dorootchecks, // I - Check for root permissions? - cf_logfunc_t log, // I - Log function - void *ld) // I - Data pointer for log function -{ - struct stat fileinfo; // File information - char message[1024], // Message string - temp[1024], // Parent directory filename - *ptr; // Pointer into parent directory - _ppd_fc_result_t result; // Check result - - - // - // Does the filename contain a relative path ("../")? - // - - if (strstr(filename, "../")) - { - // - // Yes, fail it! - // - - result = _PPD_FILE_CHECK_RELATIVE_PATH; - goto finishup; - } - - // - // Does the program even exist and is it accessible? - // - - if (stat(filename, &fileinfo)) - { - // - // Nope... - // - - result = _PPD_FILE_CHECK_MISSING; - goto finishup; - } - - // - // Check the execute bit... - // - - result = _PPD_FILE_CHECK_OK; - - switch (filetype) - { - case _PPD_FILE_CHECK_DIRECTORY : - if (!S_ISDIR(fileinfo.st_mode)) - result = _PPD_FILE_CHECK_WRONG_TYPE; - break; - - default : - if (!S_ISREG(fileinfo.st_mode)) - result = _PPD_FILE_CHECK_WRONG_TYPE; - break; - } - - if (result) - goto finishup; - - // - // Are we doing root checks? - // - - if (!dorootchecks) - { - // - // Nope, so anything (else) goes... - // - - goto finishup; - } - - // - // Verify permission of the file itself: - // - // 1. Must be owned by root - // 2. Must not be writable by group - // 3. Must not be setuid - // 4. Must not be writable by others - // - - if (fileinfo.st_uid || // 1. Must be owned by root - (fileinfo.st_mode & S_IWGRP) || // 2. Must not be writable by group - (fileinfo.st_mode & S_ISUID) || // 3. Must not be setuid - (fileinfo.st_mode & S_IWOTH)) // 4. Must not be writable by others - { - result = _PPD_FILE_CHECK_PERMISSIONS; - goto finishup; - } - - if (filetype == _PPD_FILE_CHECK_DIRECTORY || - filetype == _PPD_FILE_CHECK_FILE_ONLY) - goto finishup; - - // - // Now check the containing directory... - // - - strlcpy(temp, filename, sizeof(temp)); - if ((ptr = strrchr(temp, '/')) != NULL) - { - if (ptr == temp) - ptr[1] = '\0'; - else - *ptr = '\0'; - } - - if (stat(temp, &fileinfo)) - { - // - // Doesn't exist?!? - // - - result = _PPD_FILE_CHECK_MISSING; - filetype = _PPD_FILE_CHECK_DIRECTORY; - filename = temp; - - goto finishup; - } - - if (fileinfo.st_uid || // 1. Must be owned by root - (fileinfo.st_mode & S_IWGRP) || // 2. Must not be writable by group - (fileinfo.st_mode & S_ISUID) || // 3. Must not be setuid - (fileinfo.st_mode & S_IWOTH)) // 4. Must not be writable by others - { - result = _PPD_FILE_CHECK_PERMISSIONS; - filetype = _PPD_FILE_CHECK_DIRECTORY; - filename = temp; - } - - // - // Common return point... - // - - finishup: - - if (log) - { - cups_lang_t *lang = cupsLangDefault(); - // Localization information - cf_loglevel_t loglevel; - - switch (result) - { - case _PPD_FILE_CHECK_OK : - loglevel = CF_LOGLEVEL_DEBUG; - if (filetype == _PPD_FILE_CHECK_DIRECTORY) - snprintf(message, sizeof(message), - _ppdLangString(lang, _("Directory \"%s\" permissions OK " - "(0%o/uid=%d/gid=%d).")), - filename, fileinfo.st_mode, (int)fileinfo.st_uid, - (int)fileinfo.st_gid); - else - snprintf(message, sizeof(message), - _ppdLangString(lang, _("File \"%s\" permissions OK " - "(0%o/uid=%d/gid=%d).")), - filename, fileinfo.st_mode, (int)fileinfo.st_uid, - (int)fileinfo.st_gid); - break; - - case _PPD_FILE_CHECK_MISSING : - loglevel = CF_LOGLEVEL_ERROR; - if (filetype == _PPD_FILE_CHECK_DIRECTORY) - snprintf(message, sizeof(message), - _ppdLangString(lang, _("Directory \"%s\" not available: " - "%s")), - filename, strerror(errno)); - else - snprintf(message, sizeof(message), - _ppdLangString(lang, _("File \"%s\" not available: %s")), - filename, strerror(errno)); - break; - - case _PPD_FILE_CHECK_PERMISSIONS : - loglevel = CF_LOGLEVEL_ERROR; - if (filetype == _PPD_FILE_CHECK_DIRECTORY) - snprintf(message, sizeof(message), - _ppdLangString(lang, _("Directory \"%s\" has insecure " - "permissions " - "(0%o/uid=%d/gid=%d).")), - filename, fileinfo.st_mode, (int)fileinfo.st_uid, - (int)fileinfo.st_gid); - else - snprintf(message, sizeof(message), - _ppdLangString(lang, _("File \"%s\" has insecure " - "permissions " - "(0%o/uid=%d/gid=%d).")), - filename, fileinfo.st_mode, (int)fileinfo.st_uid, - (int)fileinfo.st_gid); - break; - - case _PPD_FILE_CHECK_WRONG_TYPE : - loglevel = CF_LOGLEVEL_ERROR; - if (filetype == _PPD_FILE_CHECK_DIRECTORY) - snprintf(message, sizeof(message), - _ppdLangString(lang, _("Directory \"%s\" is a file.")), - filename); - else - snprintf(message, sizeof(message), - _ppdLangString(lang, _("File \"%s\" is a directory.")), - filename); - break; - - case _PPD_FILE_CHECK_RELATIVE_PATH : - loglevel = CF_LOGLEVEL_ERROR; - if (filetype == _PPD_FILE_CHECK_DIRECTORY) - snprintf(message, sizeof(message), - _ppdLangString(lang, _("Directory \"%s\" contains a " - "relative path.")), filename); - else - snprintf(message, sizeof(message), - _ppdLangString(lang, _("File \"%s\" contains a relative " - "path.")), filename); - break; - } - - log(ld, loglevel, message); - } - - return (result); -} -#endif // !_WIN32 diff --git a/ppd/font.defs b/ppd/font.defs deleted file mode 100644 index 519a8e1bd..000000000 --- a/ppd/font.defs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Standard font definitions for the CUPS PPD file compiler. - * - * Copyright © 2007 by Apple Inc. - * Copyright © 1997-2005 by Easy Software Products. - * - * Licensed under Apache License v2.0. See the file "LICENSE" for more - * information. - */ - -#font AvantGarde-Book Standard "(1.05)" Standard ROM -#font AvantGarde-BookOblique Standard "(1.05)" Standard ROM -#font AvantGarde-Demi Standard "(1.05)" Standard ROM -#font AvantGarde-DemiOblique Standard "(1.05)" Standard ROM -#font Bookman-Demi Standard "(1.05)" Standard ROM -#font Bookman-DemiItalic Standard "(1.05)" Standard ROM -#font Bookman-Light Standard "(1.05)" Standard ROM -#font Bookman-LightItalic Standard "(1.05)" Standard ROM -#font Courier Standard "(1.05)" Standard ROM -#font Courier-Bold Standard "(1.05)" Standard ROM -#font Courier-BoldOblique Standard "(1.05)" Standard ROM -#font Courier-Oblique Standard "(1.05)" Standard ROM -#font Helvetica Standard "(1.05)" Standard ROM -#font Helvetica-Bold Standard "(1.05)" Standard ROM -#font Helvetica-BoldOblique Standard "(1.05)" Standard ROM -#font Helvetica-Narrow Standard "(1.05)" Standard ROM -#font Helvetica-Narrow-Bold Standard "(1.05)" Standard ROM -#font Helvetica-Narrow-BoldOblique Standard "(1.05)" Standard ROM -#font Helvetica-Narrow-Oblique Standard "(1.05)" Standard ROM -#font Helvetica-Oblique Standard "(1.05)" Standard ROM -#font NewCenturySchlbk-Bold Standard "(1.05)" Standard ROM -#font NewCenturySchlbk-BoldItalic Standard "(1.05)" Standard ROM -#font NewCenturySchlbk-Italic Standard "(1.05)" Standard ROM -#font NewCenturySchlbk-Roman Standard "(1.05)" Standard ROM -#font Palatino-Bold Standard "(1.05)" Standard ROM -#font Palatino-BoldItalic Standard "(1.05)" Standard ROM -#font Palatino-Italic Standard "(1.05)" Standard ROM -#font Palatino-Roman Standard "(1.05)" Standard ROM -#font Symbol Special "(001.005)" Special ROM -#font Times-Bold Standard "(1.05)" Standard ROM -#font Times-BoldItalic Standard "(1.05)" Standard ROM -#font Times-Italic Standard "(1.05)" Standard ROM -#font Times-Roman Standard "(1.05)" Standard ROM -#font ZapfChancery-MediumItalic Standard "(1.05)" Standard ROM -#font ZapfDingbats Special "(001.005)" Special ROM diff --git a/ppd/genstrings.cxx b/ppd/genstrings.cxx deleted file mode 100644 index a43208e79..000000000 --- a/ppd/genstrings.cxx +++ /dev/null @@ -1,197 +0,0 @@ -// -// GNU gettext message generator for the libppd PPD Compiler. -// -// This program is used to generate a dummy source file containing all of -// the standard media and sample driver strings. The results are picked up -// by GNU gettext and placed in the CUPS message catalog. -// -// Copyright 2008-2014 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more information. -// -// Usage: -// -// ./genstrings >sample.c -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include - - -// -// Local functions... -// - -static void add_ui_strings(ppdcDriver *d, ppdcCatalog *catalog); -static void write_cstring(const char *s); - - -// -// 'main()' - Main entry for the PPD compiler. -// - -int // O - Exit status -main(void) -{ - ppdcSource *src; // PPD source file data - ppdcCatalog *catalog; // Catalog to hold all of the UI strings - - - // Make sure we are in the right place... - if (access("../data", 0) || access("sample.drv", 0)) - { - puts("You must run genstrings from the ppdc directory."); - return (1); - } - - // Load the sample drivers... - ppdcSource::add_include("../data"); - - src = new ppdcSource("sample.drv"); - catalog = new ppdcCatalog(NULL); - - catalog->add_message("ISOLatin1"); - catalog->add_message("English"); - - // Add the media size strings... - ppdcMediaSize *size; // Current media size - - for (size = (ppdcMediaSize *)src->sizes->first(); - size; - size = (ppdcMediaSize *)src->sizes->next()) - catalog->add_message(size->text->value); - - // Then collect all of the UI strings from the sample drivers... - ppdcDriver *d; // Current driver - - for (d = (ppdcDriver *)src->drivers->first(); - d; - d = (ppdcDriver *)src->drivers->next()) - add_ui_strings(d, catalog); - - // Finally, write all of the strings... - ppdcMessage *message; - - for (message = (ppdcMessage *)catalog->messages->first(); - message; - message = (ppdcMessage *)catalog->messages->next()) - write_cstring(message->id->value); - - src->release(); - catalog->release(); - - // Return with no errors. - return (0); -} - - -// -// 'add_ui_strings()' - Add all UI strings from the driver. -// - -static void -add_ui_strings(ppdcDriver *d, // I - Driver data - ppdcCatalog *catalog) // I - Message catalog -{ - // Add the make/model/language strings... - catalog->add_message(d->manufacturer->value); - catalog->add_message(d->model_name->value); - - // Add the group/option/choice strings... - ppdcGroup *g; // Current group - ppdcOption *o; // Current option - ppdcChoice *c; // Current choice - - for (g = (ppdcGroup *)d->groups->first(); - g; - g = (ppdcGroup *)d->groups->next()) - { - if (!g->options->count) - continue; - - if (strcasecmp(g->name->value, "General")) - catalog->add_message(g->text->value); - - for (o = (ppdcOption *)g->options->first(); - o; - o = (ppdcOption *)g->options->next()) - { - if (!o->choices->count) - continue; - - if (o->text->value && strcmp(o->name->value, o->text->value)) - catalog->add_message(o->text->value); - else - catalog->add_message(o->name->value); - - for (c = (ppdcChoice *)o->choices->first(); - c; - c = (ppdcChoice *)o->choices->next()) - if (c->text->value && strcmp(c->name->value, c->text->value)) - catalog->add_message(c->text->value); - else - catalog->add_message(c->name->value); - } - } - - // Add profile and preset strings... - ppdcAttr *a; // Current attribute - for (a = (ppdcAttr *)d->attrs->first(); - a; - a = (ppdcAttr *)d->attrs->next()) - { - if (a->text->value && a->text->value[0] && - (a->localizable || - !strncmp(a->name->value, "Custom", 6) || - !strncmp(a->name->value, "ParamCustom", 11) || - !strcmp(a->name->value, "APCustomColorMatchingName") || - !strcmp(a->name->value, "APPrinterPreset") || - !strcmp(a->name->value, "cupsICCProfile") || - !strcmp(a->name->value, "cupsIPPReason") || - !strcmp(a->name->value, "cupsMarkerName"))) - { - catalog->add_message(a->text->value); - - if ((a->localizable && a->value->value[0]) || - !strcmp(a->name->value, "cupsIPPReason")) - catalog->add_message(a->value->value); - } - else if (!strncmp(a->name->value, "Custom", 6) || - !strncmp(a->name->value, "ParamCustom", 11)) - catalog->add_message(a->name->value); - } -} - - -// -// 'write_cstring()' - Write a translation string as a valid C string to stdout. -// - -static void -write_cstring(const char *s) // I - String to write -{ - fputs("_(\"", stdout); - if (s) - { - while (*s) - { - if (*s == '\\') - fputs("\\\\", stdout); - else if (*s == '\"') - fputs("\\\"", stdout); - else if (*s == '\t') - fputs("\\t", stdout); - else if (*s == '\n') - fputs("\\n", stdout); - else - putchar(*s); - - s ++; - } - } - puts("\");"); -} diff --git a/ppd/hp.h b/ppd/hp.h deleted file mode 100644 index cddf3e305..000000000 --- a/ppd/hp.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// This file contains model number definitions for the CUPS sample -// HP driver. -// -// Copyright 2007 by Apple Inc. -// Copyright 1997-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#define HP_LASERJET 0 // HP LaserJet -#define HP_DESKJET 1 // HP DeskJet with simple color -#define HP_DESKJET2 2 // HP DeskJet with CRet color diff --git a/ppd/imagetops-pstops.c b/ppd/imagetops-pstops.c deleted file mode 100644 index a86bf8a6e..000000000 --- a/ppd/imagetops-pstops.c +++ /dev/null @@ -1,5307 +0,0 @@ -// -// PostScript filter function and image file to PostScript filter function -// for libppd. -// -// Copyright © 2020 by Till Kamppeter -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// -// Constants... -// - -#define PSTOPS_BORDERNONE 0 // No border or hairline border -#define PSTOPS_BORDERTHICK 1 // Think border -#define PSTOPS_BORDERSINGLE 2 // Single-line hairline border -#define PSTOPS_BORDERSINGLE2 3 // Single-line thick border -#define PSTOPS_BORDERDOUBLE 4 // Double-line hairline border -#define PSTOPS_BORDERDOUBLE2 5 // Double-line thick border - -#define PSTOPS_LAYOUT_LRBT 0 // Left to right, bottom to top -#define PSTOPS_LAYOUT_LRTB 1 // Left to right, top to bottom -#define PSTOPS_LAYOUT_RLBT 2 // Right to left, bottom to top -#define PSTOPS_LAYOUT_RLTB 3 // Right to left, top to bottom -#define PSTOPS_LAYOUT_BTLR 4 // Bottom to top, left to right -#define PSTOPS_LAYOUT_TBLR 5 // Top to bottom, left to right -#define PSTOPS_LAYOUT_BTRL 6 // Bottom to top, right to left -#define PSTOPS_LAYOUT_TBRL 7 // Top to bottom, right to left - -#define PSTOPS_LAYOUT_NEGATEY 1 // The bits for the layout -#define PSTOPS_LAYOUT_NEGATEX 2 // definitions above... -#define PSTOPS_LAYOUT_VERTICAL 4 - - -// -// Types... -// - -typedef struct // **** Page information **** -{ - char *label; // Page label - int bounding_box[4]; // PageBoundingBox - off_t offset; // Offset to start of page - ssize_t length; // Number of bytes for page - int num_options; // Number of options for this page - cups_option_t *options; // Options for this page -} pstops_page_t; - -typedef struct // **** Document information **** -{ - int page; // Current page - int bounding_box[4]; // BoundingBox from header - int new_bounding_box[4]; // New composite bounding box - int num_options; // Number of document-wide options - cups_option_t *options; // Document-wide options - int normal_landscape, // Normal rotation for landscape? - saw_eof, // Saw the %%EOF comment? - slow_collate, // Collate copies by hand? - slow_duplex, // Duplex pages slowly? - slow_order, // Reverse pages slowly? - use_ESPshowpage; // Use ESPshowpage? - cups_array_t *pages; // Pages in document - cups_file_t *temp; // Temporary file, if any - char tempfile[1024]; // Temporary filename - int job_id; // Job ID - const char *user, // User name - *title; // Job name - int copies; // Number of copies - const char *ap_input_slot, // AP_FIRSTPAGE_InputSlot value - *ap_manual_feed, // AP_FIRSTPAGE_ManualFeed value - *ap_media_color, // AP_FIRSTPAGE_MediaColor value - *ap_media_type, // AP_FIRSTPAGE_MediaType value - *ap_page_region, // AP_FIRSTPAGE_PageRegion value - *ap_page_size; // AP_FIRSTPAGE_PageSize value - int collate, // Collate copies? - emit_jcl, // Emit JCL commands? - fit_to_page; // Fit pages to media - const char *input_slot, // InputSlot value - *manual_feed, // ManualFeed value - *media_color, // MediaColor value - *media_type, // MediaType value - *page_region, // PageRegion value - *page_size; // PageSize value - int mirror, // doc->mirror/mirror pages - number_up, // Number of pages on each sheet - number_up_layout, // doc->number_up_layout of N-up - // pages - output_order, // Requested reverse output order? - page_border; // doc->page_border around pages - const char *page_label, // page-label option, if any - *page_ranges, // page-ranges option, if any - *inputPageRange, // input-page-ranges option, if any - *page_set; // page-set option, if any - // Basic settings from PPD defaults/options, global vars of original pstops - int Orientation, // 0 = portrait, 1 = landscape, etc. - Duplex, // Duplexed? - LanguageLevel, // PS Language level of printer - Color; // Color printer? - float PageLeft, // Left margin - PageRight, // Right margin - PageBottom, // Bottom margin - PageTop, // Top margin - PageWidth, // Total page width - PageLength; // Total page length - cups_file_t *inputfp; // Temporary file, if any - FILE *outputfp; // Temporary file, if any - cf_logfunc_t logfunc; // Logging function, NULL for no - // logging - void *logdata; // User data for logging function, can - // be NULL - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled function, - // can be NULL -} pstops_doc_t; - - -// -// Convenience macros... -// - -#define is_first_page(p) (doc->number_up == 1 || \ - ((p) % doc->number_up) == 1) -#define is_last_page(p) (doc->number_up == 1 || \ - ((p) % doc->number_up) == 0) -#define is_not_last_page(p) (doc->number_up > 1 && \ - ((p) % doc->number_up) != 0) - - -// -// Local functions... -// - -static pstops_page_t *add_page(pstops_doc_t *doc, const char *label); -static int check_range(int page, const char *Ranges, - const char *pageset); -static void copy_bytes(pstops_doc_t *doc, - off_t offset, size_t length); -static ssize_t copy_comments(pstops_doc_t *doc, - ppd_file_t *ppd, char *line, - ssize_t linelen, size_t linesize); -static void copy_dsc(pstops_doc_t *doc, - ppd_file_t *ppd, char *line, ssize_t linelen, - size_t linesize); -static void copy_non_dsc(pstops_doc_t *doc, - ppd_file_t *ppd, char *line, - ssize_t linelen, size_t linesize); -static ssize_t copy_page(pstops_doc_t *doc, - ppd_file_t *ppd, int number, char *line, - ssize_t linelen, size_t linesize); -static ssize_t copy_prolog(pstops_doc_t *doc, - ppd_file_t *ppd, char *line, - ssize_t linelen, size_t linesize); -static ssize_t copy_setup(pstops_doc_t *doc, - ppd_file_t *ppd, char *line, - ssize_t linelen, size_t linesize); -static ssize_t copy_trailer(pstops_doc_t *doc, - ppd_file_t *ppd, int number, char *line, - ssize_t linelen, size_t linesize); -static void do_prolog(pstops_doc_t *doc, ppd_file_t *ppd); -static void do_setup(pstops_doc_t *doc, ppd_file_t *ppd); -static void doc_printf(pstops_doc_t *doc, const char *format, ...); -static void doc_putc(pstops_doc_t *doc, const char c); -static void doc_puts(pstops_doc_t *doc, const char *s); -static void doc_write(pstops_doc_t *doc, const char *s, size_t len); -static void end_nup(pstops_doc_t *doc, int number); -static int include_feature(pstops_doc_t *doc, ppd_file_t *ppd, - const char *line, int num_options, - cups_option_t **options); -static char *parse_text(const char *start, char **end, char *buffer, - size_t bufsize); -static void ps_hex(pstops_doc_t *doc, cf_ib_t *data, int length, - int last_line); -static void ps_ascii85(pstops_doc_t *doc, cf_ib_t *data, int length, - int last_line); -static int set_pstops_options(pstops_doc_t *doc, ppd_file_t *ppd, - int job_id, char *job_user, - char *job_title, int copies, - int num_options, - cups_option_t *options, - cf_logfunc_t logfunc, - void *logdata, - cf_filter_iscanceledfunc_t - iscanceledfunc, - void *iscanceleddata); -static ssize_t skip_page(pstops_doc_t *doc, - char *line, ssize_t linelen, size_t linesize); -static void start_nup(pstops_doc_t *doc, int number, - int show_border, const int *bounding_box); -static void write_common(pstops_doc_t *doc); -static void write_label_prolog(pstops_doc_t *doc, const char *label, - float bottom, float top, - float width); -static void write_labels(pstops_doc_t *doc, int orient); -static void write_labels_outputfile_only(pstops_doc_t *doc, - int orient); -static void write_options(pstops_doc_t *doc, ppd_file_t *ppd, - int num_options, cups_option_t *options); -static void write_text_comment(pstops_doc_t *doc, - const char *name, const char *value); - - -// -// 'ppdFilterPSToPS()' - Filter function to insert PostScript code from -// PPD file (PostScript printer driver) into a -// PostScript data stream -// - -int // O - Error status -ppdFilterPSToPS(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataGetExt(data, - PPD_FILTER_DATA_EXT); - pstops_doc_t doc; // Document information - cups_file_t *inputfp,*fp; // Print file - FILE *outputfp; // Output data stream - char line[8192], - buffer[8192]; // Line buffers - ssize_t len, bytes; // Length of line buffers - pstops_page_t *pageinfo; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int proc_pipe[2]; - int pstops_pid = 0; - int childStatus; - int status = 0; - - (void)inputseekable; - (void)parameters; - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Process job options... - // - - if (set_pstops_options(&doc, (filter_data_ext ? filter_data_ext->ppd : NULL), - data->job_id, data->job_user, - data->job_title, data->copies, - data->num_options, data->options, - log, ld, iscanceled, icd) == 1) - { - close(inputfd); - close(outputfd); - - return (1); - } - - if (doc.inputPageRange) - { - if (pipe(proc_pipe)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Unable to create pipe for input-page-ranges"); - return (1); - } - if ((pstops_pid = fork()) == 0) - { - close(proc_pipe[0]); - - if ((fp = cupsFileOpenFd(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Unable to open input data stream."); - } - - exit (1); - } - - bytes = cupsFileGetLine(fp,buffer,sizeof(buffer)); - - while (strncmp(buffer, "%%Page:", 7) && strncmp(buffer, "%%Trailer", 9)) - { - bytes = write(proc_pipe[1], buffer, bytes); - if ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) == 0) - break; - } - - int input_page_number = 0; - - while (!strncmp(buffer, "%%Page:", 7)) - { - input_page_number ++; - - if(check_range(input_page_number,doc.inputPageRange,NULL)) - { - bytes = write(proc_pipe[1], buffer, bytes); - bytes = cupsFileGetLine(fp, buffer, sizeof(buffer)); - while(strncmp(buffer, "%%Page:", 7) && - strncmp(buffer, "%%Trailer", 9)) - { - bytes = write(proc_pipe[1], buffer, bytes); - bytes = cupsFileGetLine(fp, buffer, sizeof(buffer)); - } - } - else - { - bytes = cupsFileGetLine(fp, buffer, sizeof(buffer)); - while(strncmp(buffer, "%%Page:", 7) && - strncmp(buffer, "%%Trailer", 9)) - bytes = cupsFileGetLine(fp, buffer, sizeof(buffer)); - } - } - while (bytes) - { - bytes = write(proc_pipe[1], buffer, bytes); - bytes= cupsFileGetLine(fp,buffer,sizeof(buffer)); - } - close(proc_pipe[1]); - cupsFileClose(fp); - exit(0); - } - else - { - close(proc_pipe[1]); - close(inputfd); - inputfd=proc_pipe[0]; - } - } - - // - // Open the input data stream specified by the inputfd... - // - - if ((inputfp = cupsFileOpenFd(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Unable to open input data stream."); - } - - return (1); - } - - // - // Open the output data stream specified by the outputfd... - // - - if ((outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Unable to open output data stream."); - } - - cupsFileClose(inputfp); - - return (1); - } - - // - // Read the first line to see if we have DSC comments... - // - - if ((len = (ssize_t)cupsFileGetLine(inputfp, line, sizeof(line))) == 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: The print file is empty."); - // Do not treat this an error, if a previous filter eliminated all - // pages the job should get dequeued without anything printed. - return (0); - } - - doc.inputfp = inputfp; - doc.outputfp = outputfp; - - // - // Write any "exit server" options that have been selected... - // - - if (filter_data_ext) - ppdEmit(filter_data_ext->ppd, outputfp, PPD_ORDER_EXIT); - - // - // Write any JCL commands that are needed to print PostScript code... - // - - if (filter_data_ext && doc.emit_jcl) - ppdEmitJCL(filter_data_ext->ppd, outputfp, doc.job_id, doc.user, doc.title); - - // - // Start with a DSC header... - // - - doc_puts(&doc, "%!PS-Adobe-3.0\n"); - - // - // Skip leading PJL in the document... - // - - while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5)) - { - // - // Yup, we have leading PJL fun, so skip it until we hit the line - // with "ENTER LANGUAGE"... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Skipping PJL header..."); - - while (strstr(line, "ENTER LANGUAGE") == NULL && strncmp(line, "%!", 2)) - if ((len = (ssize_t)cupsFileGetLine(inputfp, line, sizeof(line))) == 0) - break; - - if (!strncmp(line, "%!", 2)) - break; - - if ((len = (ssize_t)cupsFileGetLine(inputfp, line, sizeof(line))) == 0) - break; - } - - // - // Now see if the document conforms to the Adobe Document Structuring - // Conventions... - // - - if (!strncmp(line, "%!PS-Adobe-", 11)) - { - // - // Yes, filter the document... - // - - if (filter_data_ext) - copy_dsc(&doc, filter_data_ext->ppd, line, len, sizeof(line)); - } - else - { - // - // No, display an error message and treat the file as if it contains - // a single page... - // - - if (filter_data_ext) - copy_non_dsc(&doc, filter_data_ext->ppd, line, len, sizeof(line)); - } - - // - // Send %%EOF as needed... - // - - if (!doc.saw_eof) - doc_puts(&doc, "%%EOF\n"); - - // - // End the job with the appropriate JCL command or CTRL-D... - // - - if (doc.emit_jcl) - { - if (filter_data_ext && filter_data_ext->ppd && - filter_data_ext->ppd->jcl_end) - ppdEmitJCLEnd(filter_data_ext->ppd, doc.outputfp); - else - doc_putc(&doc, 0x04); - } - - // - // Close files and remove the temporary file if needed... - // - - if (doc.temp) - { - cupsFileClose(doc.temp); - unlink(doc.tempfile); - } - - if (doc.pages) - { - for (pageinfo = (pstops_page_t *)cupsArrayFirst(doc.pages); - pageinfo; pageinfo = (pstops_page_t *)cupsArrayNext(doc.pages)) - { - if (pageinfo->label) - free(pageinfo->label); - if (pageinfo->num_options && pageinfo->options) - cupsFreeOptions(pageinfo->num_options, pageinfo->options); - free(pageinfo); - } - cupsArrayDelete(doc.pages); - doc.pages = NULL; - } - - if (doc.inputPageRange) - { - retry_wait: - if (waitpid (pstops_pid, &childStatus, 0) == -1) - { - if (errno == EINTR) - goto retry_wait; - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Error while waiting for input_page_ranges to finish - %s.", - strerror(errno)); - } - // How did the sub-process terminate - if (childStatus) - { - if (WIFEXITED(childStatus)) - { - // Via exit() anywhere or return() in the main() function - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: input-page-ranges filter (PID %d) stopped with status %d", - pstops_pid, WEXITSTATUS(childStatus)); - } - else - { - // Via signal - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: imput-page-ranges filter (PID %d) crashed on signal %d", - pstops_pid, WTERMSIG(childStatus)); - } - status = 1; - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: input-page-ranges-filter (PID %d) exited with no errors.", - pstops_pid); - } - } - - cupsFileClose(inputfp); - close(inputfd); - - fclose(outputfp); - close(outputfd); - - return (status); -} - - -// -// 'ppdFilterImageToPS()' - Filter function to convert many common image file -// formats into PostScript -// - -int // O - Error status -ppdFilterImageToPS(int inputfd, // I - File descriptor input - // stream - int outputfd, // I - File descriptor output - // stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific - // parameters (unused) -{ - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataGetExt(data, - PPD_FILTER_DATA_EXT); - pstops_doc_t doc; // Document information - cf_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; - float aspect; // Aspect ratio - int xpages, // # x pages - ypages, // # y pages - xpage, // Current x page - ypage, // Current y page - page; // Current page number - int xc0, yc0, // Corners of the page in image coords - xc1, yc1; - cf_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 = NULL; // PPD file - ppd_choice_t *choice; // PPD option choice - 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 xppi, yppi; // Pixels-per-inch - int hue, sat; // Hue and saturation adjustment - int realcopies, // Real copies being printed - emit_jcl; // Emit JCL? - float left, top; // Left and top of image - time_t curtime; // Current time - struct tm *curtm; // Current date - char curdate[255]; // Current date string - int fillprint = 0; // print-scaling = fill - int cropfit = 0; // -o crop-to-fit = true - cups_page_header2_t h; // CUPS Raster page header, to - // accommodate results of command - // line parsing for PPD-less queue - int Flip, // Flip/mirror pages - XPosition, // Horizontal position on page - YPosition, // Vertical position on page - Collate, // Collate copies? - Copies; // Number of copies - char tempfile[1024]; // Name of file to print - FILE *inputfp; // Input file - int fd; // File descriptor for temp file - char buf[BUFSIZ]; - int bytes; - cups_cspace_t cspace = (cups_cspace_t)(-1); - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - - // - // Make sure status messages are not buffered... - // - - setbuf(stderr, NULL); - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Initialize data structure - // - - Flip = 0; - XPosition = 0; - YPosition = 0; - Collate = 0; - Copies = 1; - - // - // Initialize document information structure... - // - - memset(&doc, 0, sizeof(pstops_doc_t)); - - // - // Open the input data stream specified by the inputfd ... - // - - if ((inputfp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterImageToPS: Unable to open input data stream."); - } - - return (1); - } - - // - // Copy input into temporary file if needed ... - // - - if (!inputseekable) { - if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterImageToPS: Unable to copy input: %s", - strerror(errno)); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) - bytes = write(fd, buf, bytes); - - fclose(inputfp); - close(fd); - - // - // Open the temporary file to read it instead of the original input ... - // - - if ((inputfp = fopen(tempfile, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterImageToPS: Unable to open temporary file."); - } - - unlink(tempfile); - return (1); - } - } - - // - // Open the output data stream specified by the outputfd... - // - - if ((doc.outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterImageToPS: Unable to open output data stream."); - } - - fclose(inputfp); - - if (!inputseekable) - unlink(tempfile); - return (1); - } - - // - // Process command-line options and write the prolog... - // - - zoom = 1.0; - xppi = 0; - yppi = 0; - hue = 0; - sat = 100; - g = 1.0; - b = 1.0; - - Copies = data->copies; - - // - // Option list... - // - - options = data->options; - num_options = data->num_options; - - // - // Process job options... - // - - if (filter_data_ext) - ppd = filter_data_ext->ppd; - ppdFilterSetCommonOptions(ppd, num_options, options, 0, - &doc.Orientation, &doc.Duplex, - &doc.LanguageLevel, &doc.Color, - &doc.PageLeft, &doc.PageRight, - &doc.PageTop, &doc.PageBottom, - &doc.PageWidth, &doc.PageLength, - log, ld); - - // - // The ppdFilterSetCommonOptions() does not set doc.Color - // according to option settings (user's demand for color/gray), - // so we parse the options and set the mode here - // - - cfRasterPrepareHeader(&h, data, CF_FILTER_OUT_FORMAT_CUPS_RASTER, - CF_FILTER_OUT_FORMAT_CUPS_RASTER, 0, &cspace); - if (doc.Color) - doc.Color = h.cupsNumColors <= 1 ? 0 : 1; - if (!ppd) - { - // Without PPD use also the other findings of cfRasterParseIPPOptions() - doc.Orientation = h.Orientation; - doc.Duplex = h.Duplex; - doc.LanguageLevel = 2; - doc.PageWidth = h.cupsPageSize[0] != 0.0 ? h.cupsPageSize[0] : - (float)h.PageSize[0]; - doc.PageLength = h.cupsPageSize[1] != 0.0 ? h.cupsPageSize[1] : - (float)h.PageSize[1]; - doc.PageLeft = h.cupsImagingBBox[0] != 0.0 ? h.cupsImagingBBox[0] : - (float)h.ImagingBoundingBox[0]; - doc.PageBottom = h.cupsImagingBBox[1] != 0.0 ? h.cupsImagingBBox[1] : - (float)h.ImagingBoundingBox[1]; - doc.PageRight = h.cupsImagingBBox[2] != 0.0 ? h.cupsImagingBBox[2] : - (float)h.ImagingBoundingBox[2]; - doc.PageTop = h.cupsImagingBBox[3] != 0.0 ? h.cupsImagingBBox[3] : - (float)h.ImagingBoundingBox[3]; - Flip = h.MirrorPrint ? 1 : 0; - Collate = h.Collate ? 1 : 0; - Copies = h.NumCopies; - } - - 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-uncollated-copies allows for uncollated copies. - // - - Collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0; - } - - if ((val = cupsGetOption("Collate", num_options, options)) != NULL && - strcasecmp(val, "True") == 0) - Collate = 1; - - if ((val = cupsGetOption("gamma", num_options, options)) != NULL) - { - // - // Get gamma value from 1 to 10000... - // - - g = atoi(val) * 0.001f; - - if (g < 0.001f) - g = 0.001f; - else if (g > 10.0f) - g = 10.0f; - } - - if ((val = cupsGetOption("brightness", num_options, options)) != NULL) - { - // - // Get brightness value from 10 to 1000. - // - - b = atoi(val) * 0.01f; - - if (b < 0.1f) - b = 0.1f; - else if (b > 10.0f) - b = 10.0f; - } - - if ((val = cupsGetOption("ppi", num_options, options)) != NULL) - { - sscanf(val, "%d", &xppi); - yppi = xppi; - zoom = 0.0; - } - - if ((val = cupsGetOption("position", num_options, options)) != NULL) - { - if (strcasecmp(val, "center") == 0) - { - XPosition = 0; - YPosition = 0; - } - else if (strcasecmp(val, "top") == 0) - { - XPosition = 0; - YPosition = 1; - } - else if (strcasecmp(val, "left") == 0) - { - XPosition = -1; - YPosition = 0; - } - else if (strcasecmp(val, "right") == 0) - { - XPosition = 1; - YPosition = 0; - } - else if (strcasecmp(val, "top-left") == 0) - { - XPosition = -1; - YPosition = 1; - } - else if (strcasecmp(val, "top-right") == 0) - { - XPosition = 1; - YPosition = 1; - } - else if (strcasecmp(val, "bottom") == 0) - { - XPosition = 0; - YPosition = -1; - } - else if (strcasecmp(val, "bottom-left") == 0) - { - XPosition = -1; - YPosition = -1; - } - else if (strcasecmp(val, "bottom-right") == 0) - { - XPosition = 1; - YPosition = -1; - } - } - - if ((val = cupsGetOption("saturation", num_options, options)) != NULL) - sat = atoi(val); - - if ((val = cupsGetOption("hue", num_options, options)) != NULL) - hue = atoi(val); - - if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL) - { - val = choice->choice; - choice->marked = 0; - } - else - val = cupsGetOption("mirror", num_options, options); - - if (val && (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes"))) - Flip = 1; - - if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL && - (!strcasecmp(val, "false") || !strcasecmp(val, "off") || - !strcasecmp(val, "no") || !strcmp(val, "0"))) - emit_jcl = 0; - else - emit_jcl = 1; - - // - // Open the input image to print... - // - - colorspace = doc.Color ? CF_IMAGE_RGB_CMYK : CF_IMAGE_WHITE; - - img = cfImageOpenFP(inputfp, colorspace, CF_IMAGE_WHITE, sat, hue, NULL); - if (img != NULL) - { - - int margin_defined = 0; - int fidelity = 0; - int document_large = 0; - - if (ppd && (ppd->custom_margins[0] || ppd->custom_margins[1] || - ppd->custom_margins[2] || ppd->custom_margins[3])) - // In case of custom margins - margin_defined = 1; - if (doc.PageLength != doc.PageTop - doc.PageBottom || - doc.PageWidth != doc.PageRight - doc.PageLeft) - margin_defined = 1; - - if((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) - != NULL) - { - if(!strcasecmp(val, "true") || !strcasecmp(val, "yes") || - !strcasecmp(val, "on")) { - fidelity = 1; - } - } - - float w = (float)cfImageGetWidth(img); - float h = (float)cfImageGetHeight(img); - float pw = doc.PageRight - doc.PageLeft; - float ph = doc.PageTop - doc.PageBottom; - int tempOrientation = doc.Orientation; - if ((val = cupsGetOption("orientation-requested", num_options, options)) != - NULL) - tempOrientation = atoi(val); - else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if(!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - tempOrientation = 4; - } - if (tempOrientation == 0) - { - if (((pw > ph) && (w < h)) || ((pw < ph) && (w > h))) - tempOrientation = 4; - } - if (tempOrientation == 4 || tempOrientation == 5) - { - int tmp = pw; - pw = ph; - ph = tmp; - } - if (w * 72.0 / img->xppi > pw || h * 72.0 / img->yppi > ph) - document_large = 1; - - if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) - { - if (!strcasecmp(val, "auto")) - { - if (fidelity || document_large) - { - if (margin_defined) - zoom = 1.0; // fit method - else - fillprint = 1; // fill method - } - else - cropfit = 1; // none method - } - else if (!strcasecmp(val, "auto-fit")) - { - if (fidelity || document_large) - zoom = 1.0; // fit method - else - cropfit = 1; // none method - } - else if (!strcasecmp(val, "fill")) - fillprint = 1; // fill method - else if (!strcasecmp(val, "fit")) - zoom = 1.0; // fitplot = 1 or fit method - else - cropfit = 1; // none or crop-to-fit - } - else - { // print-scaling is not defined, look for alternate options. - if ((val = cupsGetOption("scaling", num_options, options)) != NULL) - zoom = atoi(val) * 0.01; - else if (((val = - cupsGetOption("fit-to-page", num_options, options)) != NULL) || - ((val = cupsGetOption("fitplot", num_options, options)) != NULL)) - { - if (!strcasecmp(val, "yes") || !strcasecmp(val, "on") || - !strcasecmp(val, "true")) - zoom = 1.0; - else - zoom = 0.0; - } - else if ((val = cupsGetOption("natural-scaling", num_options, options)) != - NULL) - zoom = 0.0; - - if ((val = cupsGetOption("fill", num_options,options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - fillprint = 1; - } - - if ((val = cupsGetOption("crop-to-fit", num_options,options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - cropfit=1; - } - } - if (fillprint || cropfit) - { - // For cropfit do the math without the unprintable margins to get correct - // centering - if (cropfit) - { - pw = doc.PageWidth; - ph = doc.PageLength; - doc.PageBottom = 0.0; - doc.PageTop = doc.PageLength; - doc.PageLeft = 0.0; - doc.PageRight = doc.PageWidth; - } - tempOrientation = doc.Orientation; - int flag = 3; - if ((val = cupsGetOption("orientation-requested", num_options, - options)) != NULL) - tempOrientation = atoi(val); - else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if (!strcasecmp(val,"true") || !strcasecmp(val,"yes")) - tempOrientation = 4; - } - if (tempOrientation > 0) - { - if (tempOrientation == 4 || tempOrientation == 5) - { - float temp = pw; - pw = ph; - ph = temp; - flag = 4; - } - } - if (tempOrientation==0) - { - if (((pw > ph) && (w < h)) || ((pw < ph) && (w > h))) - { - int temp = pw; - pw = ph; - ph = temp; - flag = 4; - } - } - if (fillprint) - { - float final_w, final_h; - if (w * ph / pw <= h) - { - final_w = w; - final_h = w * ph / pw; - } - else - { - final_w = h * pw / ph; - final_h = h; - } - float posw = (w - final_w) / 2, posh = (h - final_h) / 2; - posw = (1 + XPosition) * posw; - posh = (1 - YPosition) * posh; - cf_image_t *img2 = cfImageCrop(img, posw, posh, final_w, final_h); - cfImageClose(img); - img = img2; - } - else - { - float final_w = w, final_h = h; - if (w > pw * img->xppi / 72.0) - final_w = pw * img->xppi / 72.0; - if (h > ph * img->yppi / 72.0) - final_h = ph * img->yppi / 72.0; - float posw = (w - final_w) / 2, posh = (h - final_h) / 2; - posw = (1 + XPosition) * posw; - posh = (1 - YPosition) * posh; - cf_image_t *img2 = cfImageCrop(img, posw, posh, final_w, final_h); - cfImageClose(img); - img = img2; - if (flag == 4) - { - doc.PageBottom += (doc.PageLength - final_w * 72.0 / img->xppi) / 2; - doc.PageTop = doc.PageBottom + final_w * 72.0 / img->xppi; - doc.PageLeft += (doc.PageWidth - final_h * 72.0 / img->yppi) / 2; - doc.PageRight = doc.PageLeft + final_h * 72.0 / img->yppi; - } - else - { - doc.PageBottom += (doc.PageLength - final_h * 72.0 / img->yppi) / 2; - doc.PageTop = doc.PageBottom + final_h * 72.0 / img->yppi; - doc.PageLeft += (doc.PageWidth - final_w * 72.0 / img->xppi) / 2; - doc.PageRight = doc.PageLeft + final_w * 72.0 / img->xppi; - } - if (doc.PageBottom < 0) doc.PageBottom = 0; - if (doc.PageLeft < 0) doc.PageLeft = 0; - } - } - } - - if (!inputseekable) - unlink(tempfile); - - if (img == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterImageToPS: The print file could not be opened - %s", - strerror(errno)); - return (1); - } - - colorspace = cfImageGetColorSpace(img); - - // - // Scale as necessary... - // - - if (zoom == 0.0 && xppi == 0) - { - xppi = cfImageGetXPPI(img); - yppi = cfImageGetYPPI(img); - } - - if (yppi == 0) - yppi = xppi; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Before scaling: xppi=%d, yppi=%d, zoom=%.2f", - xppi, yppi, zoom); - - if (xppi > 0) - { - // - // Scale the image as neccesary to match the desired pixels-per-inch. - // - - if (doc.Orientation & 1) - { - xprint = (doc.PageTop - doc.PageBottom) / 72.0; - yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - xprint = (doc.PageRight - doc.PageLeft) / 72.0; - yprint = (doc.PageTop - doc.PageBottom) / 72.0; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Before scaling: xprint=%.1f, yprint=%.1f", - xprint, yprint); - - xinches = (float)cfImageGetWidth(img) / (float)xppi; - yinches = (float)cfImageGetHeight(img) / (float)yppi; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Image size is %.1f x %.1f inches...", - xinches, yinches); - - if ((val = cupsGetOption("natural-scaling", num_options, options)) != NULL) - { - xinches = xinches * atoi(val) / 100; - yinches = yinches * atoi(val) / 100; - } - - if (cupsGetOption("orientation-requested", num_options, options) == NULL && - cupsGetOption("landscape", num_options, options) == NULL) - { - // - // Rotate the image if it will fit landscape but not portrait... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Auto orientation..."); - - if ((xinches > xprint || yinches > yprint) && - xinches <= yprint && yinches <= xprint) - { - // - // Rotate the image as needed... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Using landscape orientation..."); - - doc.Orientation = (doc.Orientation + 1) & 3; - xsize = yprint; - yprint = xprint; - xprint = xsize; - } - } - } - else - { - // - // Scale percentage of page size... - // - - xprint = (doc.PageRight - doc.PageLeft) / 72.0; - yprint = (doc.PageTop - doc.PageBottom) / 72.0; - aspect = (float)cfImageGetYPPI(img) / (float)cfImageGetXPPI(img); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Before scaling: xprint=%.1f, yprint=%.1f", - xprint, yprint); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: cfImageGetXPPI(img) = %d, " - "cfImageGetYPPI(img) = %d, aspect = %f", - cfImageGetXPPI(img), cfImageGetYPPI(img), aspect); - - xsize = xprint * zoom; - ysize = xsize * cfImageGetHeight(img) / cfImageGetWidth(img) / aspect; - - if (ysize > (yprint * zoom)) - { - ysize = yprint * zoom; - xsize = ysize * cfImageGetWidth(img) * aspect / - cfImageGetHeight(img); - } - - xsize2 = yprint * zoom; - ysize2 = xsize2 * cfImageGetHeight(img) / cfImageGetWidth(img) / - aspect; - - if (ysize2 > (xprint * zoom)) - { - ysize2 = xprint * zoom; - xsize2 = ysize2 * cfImageGetWidth(img) * aspect / - cfImageGetHeight(img); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Portrait size is %.2f x %.2f inches", - xsize, ysize); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Landscape size is %.2f x %.2f inches", - xsize2, ysize2); - - if (cupsGetOption("orientation-requested", num_options, options) == NULL && - cupsGetOption("landscape", num_options, options) == NULL) - { - // - // Choose the rotation with the largest area, but prefer - // portrait if they are equal... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Auto orientation..."); - - if ((xsize * ysize) < (xsize2 * xsize2)) - { - // - // Do landscape orientation... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Using landscape orientation..."); - - doc.Orientation = 1; - xinches = xsize2; - yinches = ysize2; - xprint = (doc.PageTop - doc.PageBottom) / 72.0; - yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - // - // Do portrait orientation... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Using portrait orientation..."); - - doc.Orientation = 0; - xinches = xsize; - yinches = ysize; - } - } - else if (doc.Orientation & 1) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Using landscape orientation..."); - - xinches = xsize2; - yinches = ysize2; - xprint = (doc.PageTop - doc.PageBottom) / 72.0; - yprint = (doc.PageRight - doc.PageLeft) / 72.0; - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Using portrait orientation..."); - - xinches = xsize; - yinches = ysize; - xprint = (doc.PageRight - doc.PageLeft) / 72.0; - yprint = (doc.PageTop - doc.PageBottom) / 72.0; - } - } - - // - // Compute the number of pages to print and the size of the image on each - // page... - // - - xpages = ceil(xinches / xprint); - ypages = ceil(yinches / yprint); - - xprint = xinches / xpages; - yprint = yinches / ypages; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: xpages = %dx%.2fin, ypages = %dx%.2fin", - xpages, xprint, ypages, yprint); - - // - // Update the page size for custom sizes... - // - - if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) != NULL && - strcasecmp(choice->choice, "Custom") == 0) - { - float width, // New width in points - length; // New length in points - char s[255]; // New custom page size... - - - // - // Use the correct width and length for the current orientation... - // - - if (doc.Orientation & 1) - { - width = yprint * 72.0; - length = xprint * 72.0; - } - else - { - width = xprint * 72.0; - length = yprint * 72.0; - } - - // - // Add margins to page size... - // - - width += ppd->custom_margins[0] + ppd->custom_margins[2]; - length += ppd->custom_margins[1] + ppd->custom_margins[3]; - - // - // Enforce minimums... - // - - if (width < ppd->custom_min[0]) - width = ppd->custom_min[0]; - - if (length < ppd->custom_min[1]) - length = ppd->custom_min[1]; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Updated custom page size to %.2f x %.2f inches...", - width / 72.0, length / 72.0); - - // - // Set the new custom size... - // - - sprintf(s, "Custom.%.0fx%.0f", width, length); - ppdMarkOption(ppd, "PageSize", s); - - // - // Update page variables... - // - - doc.PageWidth = width; - doc.PageLength = length; - doc.PageLeft = ppd->custom_margins[0]; - doc.PageRight = width - ppd->custom_margins[2]; - doc.PageBottom = ppd->custom_margins[1]; - doc.PageTop = length - ppd->custom_margins[3]; - } - - // - // 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 (Copies > 1 && !slowcollate) - { - realcopies = Copies; - Copies = 1; - } - else - realcopies = 1; - - // - // Write any "exit server" options that have been selected... - // - - ppdEmit(ppd, doc.outputfp, PPD_ORDER_EXIT); - - // - // Write any JCL commands that are needed to print PostScript code... - // - - if (emit_jcl) - ppdEmitJCL(ppd, doc.outputfp, data->job_id, data->job_user, - data->job_title); - - // - // Start sending the document with any commands needed... - // - - curtime = time(NULL); - curtm = localtime(&curtime); - - doc_puts(&doc, "%!PS-Adobe-3.0\n"); - doc_printf(&doc, "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", - doc.PageLeft, doc.PageBottom, doc.PageRight, doc.PageTop); - doc_printf(&doc, "%%%%LanguageLevel: %d\n", doc.LanguageLevel); - doc_printf(&doc, "%%%%Pages: %d\n", xpages * ypages * Copies); - doc_puts(&doc, "%%DocumentData: Clean7Bit\n"); - doc_puts(&doc, "%%DocumentNeededResources: font Helvetica-Bold\n"); - doc_puts(&doc, "%%Creator: imagetops\n"); - strftime(curdate, sizeof(curdate), "%c", curtm); - doc_printf(&doc, "%%%%CreationDate: %s\n", curdate); - write_text_comment(&doc, "Title", data->job_title); - write_text_comment(&doc, "For", data->job_user); - if (doc.Orientation & 1) - doc_puts(&doc, "%%Orientation: Landscape\n"); - else - doc_puts(&doc, "%%Orientation: Portrait\n"); - doc_puts(&doc, "%%EndComments\n"); - doc_puts(&doc, "%%BeginProlog\n"); - - if (ppd != NULL && ppd->patches != NULL) - { - doc_puts(&doc, ppd->patches); - doc_putc(&doc, '\n'); - } - - ppdEmit(ppd, doc.outputfp, PPD_ORDER_DOCUMENT); - ppdEmit(ppd, doc.outputfp, PPD_ORDER_ANY); - ppdEmit(ppd, doc.outputfp, PPD_ORDER_PROLOG); - - if (g != 1.0 || b != 1.0) - doc_printf(&doc, - "{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } " - "ifelse %.3f mul } bind settransfer\n", g, b); - - write_common(&doc); - switch (doc.Orientation) - { - case 0 : - write_label_prolog(&doc, cupsGetOption("page-label", num_options, - options), - doc.PageBottom, doc.PageTop, doc.PageWidth); - break; - - case 1 : - write_label_prolog(&doc, cupsGetOption("page-label", num_options, - options), - doc.PageLeft, doc.PageRight, doc.PageLength); - break; - - case 2 : - write_label_prolog(&doc, cupsGetOption("page-label", num_options, - options), - doc.PageLength - doc.PageTop, - doc.PageLength - doc.PageBottom, - doc.PageWidth); - break; - - case 3 : - write_label_prolog(&doc, cupsGetOption("page-label", num_options, - options), - doc.PageWidth - doc.PageRight, - doc.PageWidth - doc.PageLeft, - doc.PageLength); - break; - } - - if (realcopies > 1) - { - if (ppd == NULL || ppd->language_level == 1) - doc_printf(&doc, "/#copies %d def\n", realcopies); - else - doc_printf(&doc, "<>setpagedevice\n", realcopies); - } - - doc_puts(&doc, "%%EndProlog\n"); - - // - // Output the pages... - // - - row = malloc(cfImageGetWidth(img) * abs(colorspace) + 3); - if (row == NULL) - { - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterImageToPS: Could not allocate memory."); - cfImageClose(img); - return (2); - } - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: XPosition=%d, YPosition=%d, Orientation=%d", - XPosition, YPosition, doc.Orientation); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: xprint=%.1f, yprint=%.1f", xprint, yprint); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: PageLeft=%.0f, PageRight=%.0f, PageWidth=%.0f", - doc.PageLeft, doc.PageRight, doc.PageWidth); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: PageBottom=%.0f, PageTop=%.0f, PageLength=%.0f", - doc.PageBottom, doc.PageTop, doc.PageLength); - } - - switch (doc.Orientation) - { - default : - switch (XPosition) - { - case -1 : - left = doc.PageLeft; - break; - default : - left = (doc.PageRight + doc.PageLeft - xprint * 72) / 2; - break; - case 1 : - left = doc.PageRight - xprint * 72; - break; - } - - switch (YPosition) - { - case -1 : - top = doc.PageBottom + yprint * 72; - break; - default : - top = (doc.PageTop + doc.PageBottom + yprint * 72) / 2; - break; - case 1 : - top = doc.PageTop; - break; - } - break; - - case 1 : - switch (XPosition) - { - case -1 : - left = doc.PageBottom; - break; - default : - left = (doc.PageTop + doc.PageBottom - xprint * 72) / 2; - break; - case 1 : - left = doc.PageTop - xprint * 72; - break; - } - - switch (YPosition) - { - case -1 : - top = doc.PageLeft + yprint * 72; - break; - default : - top = (doc.PageRight + doc.PageLeft + yprint * 72) / 2; - break; - case 1 : - top = doc.PageRight; - break; - } - break; - - case 2 : - switch (XPosition) - { - case 1 : - left = doc.PageLeft; - break; - default : - left = (doc.PageRight + doc.PageLeft - xprint * 72) / 2; - break; - case -1 : - left = doc.PageRight - xprint * 72; - break; - } - - switch (YPosition) - { - case 1 : - top = doc.PageBottom + yprint * 72; - break; - default : - top = (doc.PageTop + doc.PageBottom + yprint * 72) / 2; - break; - case -1 : - top = doc.PageTop; - break; - } - break; - - case 3 : - switch (XPosition) - { - case 1 : - left = doc.PageBottom; - break; - default : - left = (doc.PageTop + doc.PageBottom - xprint * 72) / 2; - break; - case -1 : - left = doc.PageTop - xprint * 72; - break; - } - - switch (YPosition) - { - case 1 : - top = doc.PageLeft + yprint * 72; - break; - default : - top = (doc.PageRight + doc.PageLeft + yprint * 72) / 2; - break; - case -1 : - top = doc.PageRight; - break; - } - break; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: left=%.2f, top=%.2f", left, top); - - for (page = 1; Copies > 0; Copies --) - for (xpage = 0; xpage < xpages; xpage ++) - for (ypage = 0; ypage < ypages; ypage ++, page ++) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Job canceled"); - goto canceled; - } - - if (log && ppd && ppd->num_filters == 0) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", page, realcopies); - - if (log) log(ld, CF_LOGLEVEL_INFO, - "ppdFilterImageToPS: Printing page %d.", page); - - doc_printf(&doc, "%%%%Page: %d %d\n", page, page); - - ppdEmit(ppd, doc.outputfp, PPD_ORDER_PAGE); - - doc_puts(&doc, "gsave\n"); - - if (Flip) - doc_printf(&doc, "%.0f 0 translate -1 1 scale\n", doc.PageWidth); - - switch (doc.Orientation) - { - case 1 : // Landscape - doc_printf(&doc, "%.0f 0 translate 90 rotate\n", - doc.PageWidth); - break; - case 2 : // Reverse Portrait - doc_printf(&doc, "%.0f %.0f translate 180 rotate\n", - doc.PageWidth, doc.PageLength); - break; - case 3 : // Reverse Landscape - doc_printf(&doc, "0 %.0f translate -90 rotate\n", - doc.PageLength); - break; - } - - doc_puts(&doc, "gsave\n"); - - xc0 = cfImageGetWidth(img) * xpage / xpages; - xc1 = cfImageGetWidth(img) * (xpage + 1) / xpages - 1; - yc0 = cfImageGetHeight(img) * ypage / ypages; - yc1 = cfImageGetHeight(img) * (ypage + 1) / ypages - 1; - - doc_printf(&doc, "%.1f %.1f translate\n", left, top); - - doc_printf(&doc, "%.3f %.3f scale\n\n", - xprint * 72.0 / (xc1 - xc0 + 1), - yprint * 72.0 / (yc1 - yc0 + 1)); - - if (doc.LanguageLevel == 1) - { - doc_printf(&doc, "/picture %d string def\n", - (xc1 - xc0 + 1) * abs(colorspace)); - doc_printf(&doc, "%d %d 8[1 0 0 -1 0 1]", - (xc1 - xc0 + 1), (yc1 - yc0 + 1)); - - if (colorspace == CF_IMAGE_WHITE) - doc_puts(&doc, "{currentfile picture readhexstring pop} image\n"); - else - doc_printf(&doc, - "{currentfile picture readhexstring pop} false %d " - "colorimage\n", - abs(colorspace)); - - for (y = yc0; y <= yc1; y ++) - { - cfImageGetRow(img, xc0, y, xc1 - xc0 + 1, row); - ps_hex(&doc, row, (xc1 - xc0 + 1) * abs(colorspace), - y == yc1); - } - } - else - { - switch (colorspace) - { - case CF_IMAGE_WHITE : - doc_puts(&doc, "/DeviceGray setcolorspace\n"); - break; - case CF_IMAGE_RGB : - doc_puts(&doc, "/DeviceRGB setcolorspace\n"); - break; - case CF_IMAGE_CMYK : - doc_puts(&doc, "/DeviceCMYK setcolorspace\n"); - break; - } - - doc_printf(&doc, - "<<" - "/ImageType 1" - "/Width %d" - "/Height %d" - "/BitsPerComponent 8", - xc1 - xc0 + 1, yc1 - yc0 + 1); - - switch (colorspace) - { - case CF_IMAGE_WHITE : - doc_puts(&doc, "/Decode[0 1]"); - break; - case CF_IMAGE_RGB : - doc_puts(&doc, "/Decode[0 1 0 1 0 1]"); - break; - case CF_IMAGE_CMYK : - doc_puts(&doc, "/Decode[0 1 0 1 0 1 0 1]"); - break; - } - - doc_puts(&doc, "\n/DataSource currentfile/ASCII85Decode filter"); - - if (((xc1 - xc0 + 1) / xprint) < 100.0) - doc_puts(&doc, "/Interpolate true"); - - doc_puts(&doc, "/ImageMatrix[1 0 0 -1 0 1]>>image\n"); - - for (y = yc0, out_offset = 0; y <= yc1; y ++) - { - cfImageGetRow(img, xc0, y, xc1 - xc0 + 1, row + out_offset); - - out_length = (xc1 - xc0 + 1) * abs(colorspace) + out_offset; - out_offset = out_length & 3; - - ps_ascii85(&doc, row, out_length, y == yc1); - - if (out_offset > 0) - memcpy(row, row + out_length - out_offset, out_offset); - } - } - - doc_puts(&doc, "grestore\n"); - write_labels(&doc, 0); - doc_puts(&doc, "grestore\n"); - doc_puts(&doc, "showpage\n"); - } - - canceled: - doc_puts(&doc, "%%EOF\n"); - - free(row); - - // - // End the job with the appropriate JCL command or CTRL-D otherwise. - // - - if (emit_jcl) - { - if (ppd && ppd->jcl_end) - ppdEmitJCLEnd(ppd, doc.outputfp); - else - doc_putc(&doc, 0x04); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterImageToPS: Printing completed.", page); - - // - // Close files... - // - - cfImageClose(img); - fclose(doc.outputfp); - close(outputfd); - - return (0); -} - - -// -// 'add_page()' - Add a page to the pages array. -// - -static pstops_page_t * // O - New page info object -add_page(pstops_doc_t *doc, // I - Document information - const char *label) // I - Page label -{ - pstops_page_t *pageinfo; // New page info object - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - if (!doc->pages) - doc->pages = cupsArrayNew(NULL, NULL); - - if (!doc->pages) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Unable to allocate memory for pages array"); - return (NULL); - } - - if ((pageinfo = calloc(1, sizeof(pstops_page_t))) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Unable to allocate memory for page info"); - return (NULL); - } - - pageinfo->label = strdup(label); - pageinfo->offset = cupsFileTell(doc->temp); - - cupsArrayAdd(doc->pages, pageinfo); - - doc->page ++; - - return (pageinfo); -} - - -// -// '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 *Ranges, // I - page_ranges or inputPageRange - const char *pageset ) // I - Only provided with page_ranges - // else NULL -{ - const char *range; // Pointer into range string - int lower, upper; // Lower and upper page numbers - - - if (pageset) - { - // - // See if we only print even or odd pages... - // - - if (!strcasecmp(pageset, "even") && (page & 1)) - return (0); - - if (!strcasecmp(pageset, "odd") && !(page & 1)) - return (0); - } - - if (!Ranges) - return (1); // No range, print all pages... - - for (range = Ranges; *range != '\0';) - { - if (*range == '-') - { - lower = 1; - range ++; - upper = (int)strtol(range, (char **)&range, 10); - } - else - { - lower = (int)strtol(range, (char **)&range, 10); - - if (*range == '-') - { - range ++; - if (!isdigit(*range & 255)) - upper = 65535; - else - upper = (int)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 temporary file to the output file. -// - -static void -copy_bytes(pstops_doc_t *doc, // I - Document info - off_t offset, // I - Offset to page data - size_t length) // I - Length of page data -{ - char buffer[8192]; // Data buffer - ssize_t nbytes; // Number of bytes read - size_t nleft; // Number of bytes left/remaining - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - nleft = length; - - if (cupsFileSeek(doc->temp, offset) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Unable to seek in file"); - return; - } - - while (nleft > 0 || length == 0) - { - if (nleft > sizeof(buffer) || length == 0) - nbytes = sizeof(buffer); - else - nbytes = (ssize_t)nleft; - - if ((nbytes = cupsFileRead(doc->temp, buffer, (size_t)nbytes)) < 1) - return; - - nleft -= (size_t)nbytes; - - doc_write(doc, buffer, (size_t)nbytes); - } -} - - -// -// 'copy_comments()' - Copy all of the comments section. -// -// This function expects "line" to be filled with a comment line. -// On return, "line" will contain the next line in the file, if any. -// - -static ssize_t // O - Length of next line -copy_comments(pstops_doc_t *doc, // I - Document info - ppd_file_t *ppd, // I - PPD file - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - int saw_bounding_box, // Saw %%BoundingBox: comment? - saw_for, // Saw %%For: comment? - saw_pages, // Saw %%Pages: comment? - saw_title; // Saw %%Title: comment? - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - // - // Loop until we see %%EndComments or a non-comment line... - // - - saw_bounding_box = 0; - saw_for = 0; - saw_pages = 0; - saw_title = 0; - - while (line[0] == '%') - { - // - // Strip trailing whitespace... - // - - while (linelen > 0) - { - linelen --; - - if (!isspace(line[linelen] & 255)) - break; - else - line[linelen] = '\0'; - } - - // - // Log the header... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: %s", line); - - // - // Pull the headers out... - // - - if (!strncmp(line, "%%Pages:", 8)) - { - int pages; // Number of pages - - if (saw_pages && log) - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: A duplicate %%Pages: comment was seen."); - - saw_pages = 1; - - if (doc->Duplex && - (pages = atoi(line + 8)) > 0 && pages <= doc->number_up) - { - // - // Since we will only be printing on a single page, disable duplexing. - // - - doc->Duplex = 0; - doc->slow_duplex = 0; - - if (cupsGetOption("sides", doc->num_options, doc->options)) - doc->num_options = cupsAddOption("sides", "one-sided", - doc->num_options, &(doc->options)); - - if (cupsGetOption("Duplex", doc->num_options, doc->options)) - doc->num_options = cupsAddOption("Duplex", "None", - doc->num_options, &(doc->options)); - - if (cupsGetOption("EFDuplex", doc->num_options, doc->options)) - doc->num_options = cupsAddOption("EFDuplex", "None", - doc->num_options, &(doc->options)); - - if (cupsGetOption("EFDuplexing", doc->num_options, doc->options)) - doc->num_options = cupsAddOption("EFDuplexing", "False", - doc->num_options, &(doc->options)); - - if (cupsGetOption("ARDuplex", doc->num_options, doc->options)) - doc->num_options = cupsAddOption("ARDuplex", "None", - doc->num_options, &(doc->options)); - - if (cupsGetOption("KD03Duplex", doc->num_options, doc->options)) - doc->num_options = cupsAddOption("KD03Duplex", "None", - doc->num_options, &(doc->options)); - - if (cupsGetOption("JCLDuplex", doc->num_options, doc->options)) - doc->num_options = cupsAddOption("JCLDuplex", "None", - doc->num_options, &(doc->options)); - - ppdMarkOption(ppd, "Duplex", "None"); - ppdMarkOption(ppd, "EFDuplex", "None"); - ppdMarkOption(ppd, "EFDuplexing", "False"); - ppdMarkOption(ppd, "ARDuplex", "None"); - ppdMarkOption(ppd, "KD03Duplex", "None"); - ppdMarkOption(ppd, "JCLDuplex", "None"); - } - } - else if (!strncmp(line, "%%BoundingBox:", 14)) - { - if (saw_bounding_box) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: A duplicate %%BoundingBox: comment was seen."); - } - else if (strstr(line + 14, "(atend)")) - { - // - // Do nothing for now but use the default imageable area... - // - } - else if (sscanf(line + 14, "%d%d%d%d", doc->bounding_box + 0, - doc->bounding_box + 1, doc->bounding_box + 2, - doc->bounding_box + 3) != 4) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: A bad %%BoundingBox: comment was seen."); - - doc->bounding_box[0] = (int)(doc->PageLeft); - doc->bounding_box[1] = (int)(doc->PageBottom); - doc->bounding_box[2] = (int)(doc->PageRight); - doc->bounding_box[3] = (int)(doc->PageTop); - } - - saw_bounding_box = 1; - } - else if (!strncmp(line, "%%For:", 6)) - { - saw_for = 1; - doc_printf(doc, "%s\n", line); - } - else if (!strncmp(line, "%%Title:", 8)) - { - saw_title = 1; - doc_printf(doc, "%s\n", line); - } - else if (!strncmp(line, "%cupsRotation:", 14)) - { - // - // Reset orientation of document? - // - - int orient = (atoi(line + 14) / 90) & 3; - - if (orient != doc->Orientation) - { - // - // Yes, update things so that the pages come out right... - // - - doc->Orientation = (4 - doc->Orientation + orient) & 3; - ppdFilterUpdatePageVars(doc->Orientation, - &doc->PageLeft, &doc->PageRight, - &doc->PageTop, &doc->PageBottom, - &doc->PageWidth, &doc->PageLength); - doc->Orientation = orient; - } - } - else if (!strcmp(line, "%%EndComments")) - { - linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize); - break; - } - else if (strncmp(line, "%!", 2) && strncmp(line, "%cups", 5)) - doc_printf(doc, "%s\n", line); - - if ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) == 0) - break; - } - - if (!saw_bounding_box && log) - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: There wasn't a %%BoundingBox: comment in the header."); - - if (!saw_pages && log) - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: There wasn't a %%Pages: comment in the header."); - - if (!saw_for) - write_text_comment(doc, "For", doc->user); - - if (!saw_title) - write_text_comment(doc, "Title", doc->title); - - if (doc->copies != 1 && (!doc->collate || !doc->slow_collate)) - { - // - // Tell the document processor the copy and duplex options - // that are required... - // - - doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies, - doc->collate ? " collate" : "", - doc->Duplex ? " duplex" : ""); - - // - // Apple uses RBI comments for various non-PPD options... - // - - doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies); - } - else - { - // - // Tell the document processor the duplex option that is required... - // - - if (doc->Duplex) - doc_puts(doc, "%%Requirements: duplex\n"); - - // - // Apple uses RBI comments for various non-PPD options... - // - - doc_puts(doc, "%RBINumCopies: 1\n"); - } - - doc_puts(doc, "%%Pages: (atend)\n"); - doc_puts(doc, "%%BoundingBox: (atend)\n"); - doc_puts(doc, "%%EndComments\n"); - - return (linelen); -} - - -// -// 'copy_dsc()' - Copy a DSC-conforming document. -// -// This function expects "line" to be filled with the %!PS-Adobe comment line. -// - -static void -copy_dsc(pstops_doc_t *doc, // I - Document info - ppd_file_t *ppd, // I - PPD file - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - int number; // Page number - pstops_page_t *pageinfo; // Page information - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - cf_filter_iscanceledfunc_t iscanceled = doc->iscanceledfunc; - void *icd = doc->iscanceleddata; - - - // - // Make sure we use ESPshowpage for EPS files... - // - - if (strstr(line, "EPSF")) - { - doc->use_ESPshowpage = 1; - doc->number_up = 1; - } - - // - // Start sending the document with any commands needed... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Before copy_comments - %s", line); - linelen = copy_comments(doc, ppd, line, linelen, linesize); - - // - // Now find the prolog section, if any... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Before copy_prolog - %s", line); - linelen = copy_prolog(doc, ppd, line, linelen, linesize); - - // - // Then the document setup section... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Before copy_setup - %s", line); - linelen = copy_setup(doc, ppd, line, linelen, linesize); - - // - // Copy until we see %%Page:... - // - - while (strncmp(line, "%%Page:", 7) && strncmp(line, "%%Trailer", 9)) - { - doc_write(doc, line, (size_t)linelen); - - if ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) == 0) - break; - } - - // - // Then process pages until we have no more... - // - - number = 0; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Before page loop - %s", line); - while (!strncmp(line, "%%Page:", 7)) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Job canceled"); - break; - } - - number ++; - - if (check_range((number - 1) / doc->number_up + 1, - doc->page_ranges, doc->page_set)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Copying page %d...", number); - linelen = copy_page(doc, ppd, number, line, linelen, linesize); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Skipping page %d...", number); - linelen = skip_page(doc, line, linelen, linesize); - } - } - - // - // Finish up the last page(s)... - // - - if (number && is_not_last_page(number) && cupsArrayLast(doc->pages) && - check_range((number - 1) / doc->number_up + 1, doc->page_ranges, - doc->page_set)) - { - pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages); - - start_nup(doc, doc->number_up, 0, doc->bounding_box); - doc_puts(doc, "showpage\n"); - end_nup(doc, doc->number_up); - - pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset); - } - - if (doc->slow_duplex && (doc->page & 1)) - { - // - // Make sure we have an even number of pages... - // - - pageinfo = add_page(doc, "(filler)"); - if (pageinfo == NULL) - return; - - if (!doc->slow_order) - { - if ((!ppd || !ppd->num_filters) && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", doc->page, - doc->slow_collate ? 1 : doc->copies); - - doc_printf(doc, "%%%%Page: (filler) %d\n", doc->page); - } - - start_nup(doc, doc->number_up, 0, doc->bounding_box); - doc_puts(doc, "showpage\n"); - end_nup(doc, doc->number_up); - - pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset); - } - - // - // Make additional copies as necessary... - // - - number = doc->slow_order ? 0 : doc->page; - - if (doc->temp && (!iscanceled || !iscanceled(icd)) && - cupsArrayCount(doc->pages) > 0) - { - int copy; // Current copy - - - // - // Reopen the temporary file for reading... - // - - cupsFileClose(doc->temp); - - doc->temp = cupsFileOpen(doc->tempfile, "r"); - - // - // Make the copies... - // - - if (doc->slow_collate) - copy = !doc->slow_order; - else - copy = doc->copies - 1; - - for (; copy < doc->copies; copy ++) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Job canceled"); - break; - } - - // - // Send end-of-job stuff followed by any start-of-job stuff required - // for the JCL options... - // - - if (number && doc->emit_jcl && ppd && ppd->jcl_end) - { - // - // Send the trailer... - // - - doc_puts(doc, "%%Trailer\n"); - doc_printf(doc, "%%%%Pages: %d\n", cupsArrayCount(doc->pages)); - if (doc->number_up > 1 || doc->fit_to_page) - doc_printf(doc, "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", - doc->PageLeft, doc->PageBottom, - doc->PageRight, doc->PageTop); - else - doc_printf(doc, "%%%%BoundingBox: %d %d %d %d\n", - doc->new_bounding_box[0], doc->new_bounding_box[1], - doc->new_bounding_box[2], doc->new_bounding_box[3]); - doc_puts(doc, "%%EOF\n"); - - // - // Start a new document... - // - - ppdEmitJCLEnd(ppd, doc->outputfp); - ppdEmitJCL(ppd, doc->outputfp, doc->job_id, doc->user, doc->title); - - doc_puts(doc, "%!PS-Adobe-3.0\n"); - - number = 0; - } - - // - // Copy the prolog as needed... - // - - if (!number) - { - pageinfo = (pstops_page_t *)cupsArrayFirst(doc->pages); - copy_bytes(doc, 0, (size_t)pageinfo->offset); - } - - // - // Then copy all of the pages... - // - - pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayLast(doc->pages) : - (pstops_page_t *)cupsArrayFirst(doc->pages); - - while (pageinfo) - { - - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Job canceled"); - break; - } - - number ++; - - if ((!ppd || !ppd->num_filters) && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", number, - doc->slow_collate ? 1 : doc->copies); - - if (doc->number_up > 1) - { - doc_printf(doc, "%%%%Page: (%d) %d\n", number, number); - doc_printf(doc, "%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n", - doc->PageLeft, doc->PageBottom, - doc->PageRight, doc->PageTop); - } - else - { - doc_printf(doc, "%%%%Page: %s %d\n", pageinfo->label, number); - doc_printf(doc, "%%%%PageBoundingBox: %d %d %d %d\n", - pageinfo->bounding_box[0], pageinfo->bounding_box[1], - pageinfo->bounding_box[2], pageinfo->bounding_box[3]); - } - - copy_bytes(doc, pageinfo->offset, (size_t)pageinfo->length); - - pageinfo = doc->slow_order ? - (pstops_page_t *)cupsArrayPrev(doc->pages) : - (pstops_page_t *)cupsArrayNext(doc->pages); - } - } - } - - // - // Restore the old showpage operator as needed... - // - - if (doc->use_ESPshowpage) - doc_puts(doc, "userdict/showpage/ESPshowpage load put\n"); - - // - // Write/copy the trailer... - // - - if (!iscanceled || !iscanceled(icd)) - copy_trailer(doc, ppd, number, line, linelen, linesize); -} - - -// -// 'copy_non_dsc()' - Copy a document that does not conform to the DSC. -// -// This function expects "line" to be filled with the %! comment line. -// - -static void -copy_non_dsc(pstops_doc_t *doc, // I - Document info - ppd_file_t *ppd, // I - PPD file - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - int copy; // Current copy - char buffer[8192]; // Copy buffer - ssize_t bytes; // Number of bytes copied - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - cf_filter_iscanceledfunc_t iscanceled = doc->iscanceledfunc; - void *icd = doc->iscanceleddata; - - - (void)linesize; - - // - // First let the user know that they are attempting to print a file - // that may not print correctly... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: This document does not conform to the Adobe Document " - "Structuring Conventions and may not print correctly."); - - // - // Then write a standard DSC comment section... - // - - doc_printf(doc, "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", - doc->PageLeft, doc->PageBottom, doc->PageRight, doc->PageTop); - - if (doc->slow_collate && doc->copies > 1) - doc_printf(doc, "%%%%Pages: %d\n", doc->copies); - else - doc_puts(doc, "%%Pages: 1\n"); - - write_text_comment(doc, "For", doc->user); - write_text_comment(doc, "Title", doc->title); - - if (doc->copies != 1 && (!doc->collate || !doc->slow_collate)) - { - // - // Tell the document processor the copy and duplex options - // that are required... - // - - doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies, - doc->collate ? " collate" : "", - doc->Duplex ? " duplex" : ""); - - // - // Apple uses RBI comments for various non-PPD options... - // - - doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies); - } - else - { - // - // Tell the document processor the duplex option that is required... - // - - if (doc->Duplex) - doc_puts(doc, "%%Requirements: duplex\n"); - - // - // Apple uses RBI comments for various non-PPD options... - // - - doc_puts(doc, "%RBINumCopies: 1\n"); - } - - doc_puts(doc, "%%EndComments\n"); - - // - // Then the prolog... - // - - doc_puts(doc, "%%BeginProlog\n"); - - do_prolog(doc, ppd); - - doc_puts(doc, "%%EndProlog\n"); - - // - // Then the setup section... - // - - doc_puts(doc, "%%BeginSetup\n"); - - do_setup(doc, ppd); - - doc_puts(doc, "%%EndSetup\n"); - - // - // Finally, embed a copy of the file inside a %%Page... - // - - if ((!ppd || !ppd->num_filters) && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: 1 %d", doc->temp ? 1 : doc->copies); - - doc_puts(doc, "%%Page: 1 1\n"); - doc_puts(doc, "%%BeginPageSetup\n"); - ppdEmit(ppd, doc->outputfp, PPD_ORDER_PAGE); - doc_puts(doc, "%%EndPageSetup\n"); - doc_puts(doc, "%%BeginDocument: nondsc\n"); - - doc_write(doc, line, (size_t)linelen); - - if (doc->temp) - cupsFileWrite(doc->temp, line, (size_t)linelen); - - while ((bytes = cupsFileRead(doc->inputfp, buffer, sizeof(buffer))) > 0) - { - doc_write(doc, buffer, (size_t)bytes); - - if (doc->temp) - cupsFileWrite(doc->temp, buffer, (size_t)bytes); - } - - doc_puts(doc, "%%EndDocument\n"); - - if (doc->use_ESPshowpage) - { - write_labels_outputfile_only(doc, doc->Orientation); - doc_puts(doc, "ESPshowpage\n"); - } - - if (doc->temp && (!iscanceled || !iscanceled(icd))) - { - // - // Reopen the temporary file for reading... - // - - cupsFileClose(doc->temp); - - doc->temp = cupsFileOpen(doc->tempfile, "r"); - - // - // Make the additional copies as needed... - // - - for (copy = 1; copy < doc->copies; copy ++) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Job canceled"); - break; - } - - if ((!ppd || !ppd->num_filters) && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: 1 1"); - - doc_printf(doc, "%%%%Page: %d %d\n", copy + 1, copy + 1); - doc_puts(doc, "%%BeginPageSetup\n"); - ppdEmit(ppd, doc->outputfp, PPD_ORDER_PAGE); - doc_puts(doc, "%%EndPageSetup\n"); - doc_puts(doc, "%%BeginDocument: nondsc\n"); - - copy_bytes(doc, 0, 0); - - doc_puts(doc, "%%EndDocument\n"); - - if (doc->use_ESPshowpage) - { - write_labels_outputfile_only(doc, doc->Orientation); - doc_puts(doc, "ESPshowpage\n"); - } - } - } - - // - // Restore the old showpage operator as needed... - // - - if (doc->use_ESPshowpage) - doc_puts(doc, "userdict/showpage/ESPshowpage load put\n"); -} - - -// -// 'copy_page()' - Copy a page description. -// -// This function expects "line" to be filled with a %%Page comment line. -// On return, "line" will contain the next line in the file, if any. -// - -static ssize_t // O - Length of next line -copy_page(pstops_doc_t *doc, // I - Document info - ppd_file_t *ppd, // I - PPD file - int number, // I - Current page number - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - char label[256], // Page label string - *ptr; // Pointer into line - int level; // Embedded document level - pstops_page_t *pageinfo; // Page information - int first_page; // First page on N-up output? - int has_page_setup = 0; // Does the page have - // %%Begin/EndPageSetup? - int bounding_box[4]; // PageBoundingBox - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - // - // Get the page label for this page... - // - - first_page = is_first_page(number); - - if (!parse_text(line + 7, &ptr, label, sizeof(label))) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: There was a bad %%Page: comment in the file."); - label[0] = '\0'; - number = doc->page; - } - else if (strtol(ptr, &ptr, 10) == LONG_MAX || !isspace(*ptr & 255)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: There was a bad %%Page: comment in the file."); - number = doc->page; - } - - // - // Create or update the current output page... - // - - if (first_page) - { - pageinfo = add_page(doc, label); - if (pageinfo == NULL) - return (0); - } - else - pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages); - - // - // Handle first page override... - // - - if (doc->ap_input_slot || doc->ap_manual_feed) - { - if ((doc->page == 1 && (!doc->slow_order || !doc->Duplex)) || - (doc->page == 2 && doc->slow_order && doc->Duplex)) - { - // - // First page/sheet gets AP_FIRSTPAGE_* options... - // - - pageinfo->num_options = cupsAddOption("InputSlot", doc->ap_input_slot, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("ManualFeed", - doc->ap_input_slot ? "False" : - doc->ap_manual_feed, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("MediaColor", doc->ap_media_color, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("MediaType", doc->ap_media_type, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("PageRegion", doc->ap_page_region, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("PageSize", doc->ap_page_size, - pageinfo->num_options, - &(pageinfo->options)); - } - else if (doc->page == (doc->Duplex + 2)) - { - // - // Second page/sheet gets default options... - // - - pageinfo->num_options = cupsAddOption("InputSlot", doc->input_slot, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("ManualFeed", - doc->input_slot ? "False" : - doc->manual_feed, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("MediaColor", doc->media_color, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("MediaType", doc->media_type, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("PageRegion", doc->page_region, - pageinfo->num_options, - &(pageinfo->options)); - pageinfo->num_options = cupsAddOption("PageSize", doc->page_size, - pageinfo->num_options, - &(pageinfo->options)); - } - } - - // - // Scan comments until we see something other than %%Page*: or - // %%Include*... - // - - memcpy(bounding_box, doc->bounding_box, sizeof(bounding_box)); - - while ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) > 0) - { - if (!strncmp(line, "%%PageBoundingBox:", 18)) - { - // - // %%PageBoundingBox: llx lly urx ury - // - - if (sscanf(line + 18, "%d%d%d%d", bounding_box + 0, - bounding_box + 1, bounding_box + 2, - bounding_box + 3) != 4) - { - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: There was a bad %%PageBoundingBox: comment in the " - "file."); - memcpy(bounding_box, doc->bounding_box, - sizeof(bounding_box)); - } - else if (doc->number_up == 1 && !doc->fit_to_page && doc->Orientation) - { - int temp_bbox[4]; // Temporary bounding box - - - memcpy(temp_bbox, bounding_box, sizeof(temp_bbox)); - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Orientation = %d", doc->Orientation); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: original bounding_box = [ %d %d %d %d ]", - bounding_box[0], bounding_box[1], - bounding_box[2], bounding_box[3]); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: PageWidth = %.1f, PageLength = %.1f", - doc->PageWidth, doc->PageLength); - } - - switch (doc->Orientation) - { - case 1 : // Landscape - bounding_box[0] = (int)(doc->PageLength - temp_bbox[3]); - bounding_box[1] = temp_bbox[0]; - bounding_box[2] = (int)(doc->PageLength - temp_bbox[1]); - bounding_box[3] = temp_bbox[2]; - break; - - case 2 : // Reverse Portrait - bounding_box[0] = (int)(doc->PageWidth - temp_bbox[2]); - bounding_box[1] = (int)(doc->PageLength - temp_bbox[3]); - bounding_box[2] = (int)(doc->PageWidth - temp_bbox[0]); - bounding_box[3] = (int)(doc->PageLength - temp_bbox[1]); - break; - - case 3 : // Reverse Landscape - bounding_box[0] = temp_bbox[1]; - bounding_box[1] = (int)(doc->PageWidth - temp_bbox[2]); - bounding_box[2] = temp_bbox[3]; - bounding_box[3] = (int)(doc->PageWidth - temp_bbox[0]); - break; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: updated bounding_box = [ %d %d %d %d ]", - bounding_box[0], bounding_box[1], - bounding_box[2], bounding_box[3]); - } - } -#if 0 - else if (!strncmp(line, "%%PageCustomColors:", 19) || - !strncmp(line, "%%PageMedia:", 12) || - !strncmp(line, "%%PageOrientation:", 18) || - !strncmp(line, "%%PageProcessColors:", 20) || - !strncmp(line, "%%PageRequirements:", 18) || - !strncmp(line, "%%PageResources:", 16)) - { - // - // Copy literal... - // - } -#endif // 0 - else if (!strncmp(line, "%%PageCustomColors:", 19)) - { - // - // %%PageCustomColors: ... - // - } - else if (!strncmp(line, "%%PageMedia:", 12)) - { - // - // %%PageMedia: ... - // - } - else if (!strncmp(line, "%%PageOrientation:", 18)) - { - // - // %%PageOrientation: ... - // - } - else if (!strncmp(line, "%%PageProcessColors:", 20)) - { - // - // %%PageProcessColors: ... - // - } - else if (!strncmp(line, "%%PageRequirements:", 18)) - { - // - // %%PageRequirements: ... - // - } - else if (!strncmp(line, "%%PageResources:", 16)) - { - // - // %%PageResources: ... - // - } - else if (!strncmp(line, "%%IncludeFeature:", 17)) - { - // - // %%IncludeFeature: *MainKeyword OptionKeyword - // - - if (doc->number_up == 1 &&!doc->fit_to_page) - pageinfo->num_options = include_feature(doc, ppd, line, - pageinfo->num_options, - &(pageinfo->options)); - } - else if (!strncmp(line, "%%BeginPageSetup", 16)) - { - has_page_setup = 1; - break; - } - else - break; - } - - if (doc->number_up == 1) - { - // - // Update the document's composite and page bounding box... - // - - memcpy(pageinfo->bounding_box, bounding_box, - sizeof(pageinfo->bounding_box)); - - if (bounding_box[0] < doc->new_bounding_box[0]) - doc->new_bounding_box[0] = bounding_box[0]; - if (bounding_box[1] < doc->new_bounding_box[1]) - doc->new_bounding_box[1] = bounding_box[1]; - if (bounding_box[2] > doc->new_bounding_box[2]) - doc->new_bounding_box[2] = bounding_box[2]; - if (bounding_box[3] > doc->new_bounding_box[3]) - doc->new_bounding_box[3] = bounding_box[3]; - } - - // - // Output the page header as needed... - // - - if (!doc->slow_order && first_page) - { - if ((!ppd || !ppd->num_filters) && log) - log(ld, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", doc->page, - doc->slow_collate ? 1 : doc->copies); - - if (doc->number_up > 1) - { - doc_printf(doc, "%%%%Page: (%d) %d\n", doc->page, doc->page); - doc_printf(doc, "%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n", - doc->PageLeft, doc->PageBottom, doc->PageRight, doc->PageTop); - } - else - { - doc_printf(doc, "%%%%Page: %s %d\n", pageinfo->label, doc->page); - doc_printf(doc, "%%%%PageBoundingBox: %d %d %d %d\n", - pageinfo->bounding_box[0], pageinfo->bounding_box[1], - pageinfo->bounding_box[2], pageinfo->bounding_box[3]); - } - } - - // - // Copy any page setup commands... - // - - if (first_page) - doc_puts(doc, "%%BeginPageSetup\n"); - - if (has_page_setup) - { - int feature = 0; // In a Begin/EndFeature block? - - while ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) > - 0) - { - if (!strncmp(line, "%%EndPageSetup", 14)) - break; - else if (!strncmp(line, "%%BeginFeature:", 15)) - { - feature = 1; - - if (doc->number_up > 1 || doc->fit_to_page) - continue; - } - else if (!strncmp(line, "%%EndFeature", 12)) - { - feature = 0; - - if (doc->number_up > 1 || doc->fit_to_page) - continue; - } - else if (!strncmp(line, "%%IncludeFeature:", 17)) - { - pageinfo->num_options = include_feature(doc, ppd, line, - pageinfo->num_options, - &(pageinfo->options)); - continue; - } - else if (!strncmp(line, "%%Include", 9)) - continue; - - if (line[0] != '%' && !feature) - break; - - if (!feature || (doc->number_up == 1 && !doc->fit_to_page)) - doc_write(doc, line, (size_t)linelen); - } - - // - // Skip %%EndPageSetup... - // - - if (linelen > 0 && !strncmp(line, "%%EndPageSetup", 14)) - linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize); - } - - if (first_page) - { - char *page_setup; // PageSetup commands to send - - - if (pageinfo->num_options > 0) - write_options(doc, ppd, pageinfo->num_options, pageinfo->options); - - // - // Output commands for the current page... - // - - page_setup = ppdEmitString(ppd, PPD_ORDER_PAGE, 0); - - if (page_setup) - { - doc_puts(doc, page_setup); - free(page_setup); - } - } - - // - // Prep for the start of the page description... - // - - start_nup(doc, number, 1, bounding_box); - - if (first_page) - doc_puts(doc, "%%EndPageSetup\n"); - - // - // Read the rest of the page description... - // - - level = 0; - - do - { - if (level == 0 && - (!strncmp(line, "%%Page:", 7) || - !strncmp(line, "%%Trailer", 9) || - !strncmp(line, "%%EOF", 5))) - break; - else if (!strncmp(line, "%%BeginDocument", 15) || - !strncmp(line, "%ADO_BeginApplication", 21)) - { - doc_write(doc, line, (size_t)linelen); - - level ++; - } - else if ((!strncmp(line, "%%EndDocument", 13) || - !strncmp(line, "%ADO_EndApplication", 19)) && level > 0) - { - doc_write(doc, line, (size_t)linelen); - - level --; - } - else if (!strncmp(line, "%%BeginBinary:", 14) || - (!strncmp(line, "%%BeginData:", 12) && - !strstr(line, "ASCII") && !strstr(line, "Hex"))) - { - // - // Copy binary data... - // - - int bytes; // Bytes of data - - - doc_write(doc, line, (size_t)linelen); - - bytes = atoi(strchr(line, ':') + 1); - - while (bytes > 0) - { - if ((size_t)bytes > linesize) - linelen = cupsFileRead(doc->inputfp, line, linesize); - else - linelen = cupsFileRead(doc->inputfp, line, (size_t)bytes); - - if (linelen < 1) - { - line[0] = '\0'; - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Early end-of-file while reading binary data: %s", - strerror(errno)); - return (0); - } - - doc_write(doc, line, (size_t)linelen); - - bytes -= linelen; - } - } - else - doc_write(doc, line, (size_t)linelen); - } - while ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) > - 0); - - // - // Finish up this page and return... - // - - end_nup(doc, number); - - pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset); - - return (linelen); -} - - -// -// 'copy_prolog()' - Copy the document prolog section. -// -// This function expects "line" to be filled with a %%BeginProlog comment line. -// On return, "line" will contain the next line in the file, if any. -// - -static ssize_t // O - Length of next line -copy_prolog(pstops_doc_t *doc, // I - Document info - ppd_file_t *ppd, // I - PPD file - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - while (strncmp(line, "%%BeginProlog", 13)) - { - if (!strncmp(line, "%%BeginSetup", 12) || !strncmp(line, "%%Page:", 7)) - break; - - doc_write(doc, line, (size_t)linelen); - - if ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) == 0) - break; - } - - doc_puts(doc, "%%BeginProlog\n"); - - do_prolog(doc, ppd); - - if (!strncmp(line, "%%BeginProlog", 13)) - { - while ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) > - 0) - { - if (!strncmp(line, "%%EndProlog", 11) || - !strncmp(line, "%%BeginSetup", 12) || - !strncmp(line, "%%Page:", 7)) - break; - - doc_write(doc, line, (size_t)linelen); - } - - if (!strncmp(line, "%%EndProlog", 11)) - linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize); - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: The %%EndProlog comment is missing."); - } - - doc_puts(doc, "%%EndProlog\n"); - - return (linelen); -} - - -// -// 'copy_setup()' - Copy the document setup section. -// -// This function expects "line" to be filled with a %%BeginSetup comment line. -// On return, "line" will contain the next line in the file, if any. -// - -static ssize_t // O - Length of next line -copy_setup(pstops_doc_t *doc, // I - Document info - ppd_file_t *ppd, // I - PPD file - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - int num_options; // Number of options - cups_option_t *options; // Options - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - while (strncmp(line, "%%BeginSetup", 12)) - { - if (!strncmp(line, "%%Page:", 7)) - break; - - doc_write(doc, line, (size_t)linelen); - - if ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) == 0) - break; - } - - doc_puts(doc, "%%BeginSetup\n"); - - do_setup(doc, ppd); - - num_options = 0; - options = NULL; - - if (!strncmp(line, "%%BeginSetup", 12)) - { - while (strncmp(line, "%%EndSetup", 10)) - { - if (!strncmp(line, "%%Page:", 7)) - break; - else if (!strncmp(line, "%%IncludeFeature:", 17)) - { - // - // %%IncludeFeature: *MainKeyword OptionKeyword - // - - if (doc->number_up == 1 && !doc->fit_to_page) - num_options = include_feature(doc, ppd, line, num_options, &options); - } - else if (strncmp(line, "%%BeginSetup", 12)) - doc_write(doc, line, (size_t)linelen); - - if ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) == - 0) - break; - } - - if (!strncmp(line, "%%EndSetup", 10)) - linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize); - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: The %%EndSetup comment is missing."); - } - - if (num_options > 0) - { - write_options(doc, ppd, num_options, options); - cupsFreeOptions(num_options, options); - } - - doc_puts(doc, "%%EndSetup\n"); - - return (linelen); -} - - -// -// 'copy_trailer()' - Copy the document trailer. -// -// This function expects "line" to be filled with a %%Trailer comment line. -// On return, "line" will contain the next line in the file, if any. -// - -static ssize_t // O - Length of next line -copy_trailer(pstops_doc_t *doc, // I - Document info - ppd_file_t *ppd, // I - PPD file - int number, // I - Number of pages - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - // - // Write the trailer comments... - // - - (void)ppd; - - doc_puts(doc, "%%Trailer\n"); - - while (linelen > 0) - { - if (!strncmp(line, "%%EOF", 5)) - break; - else if (strncmp(line, "%%Trailer", 9) && - strncmp(line, "%%Pages:", 8) && - strncmp(line, "%%BoundingBox:", 14)) - doc_write(doc, line, (size_t)linelen); - - linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: Wrote %d pages...", number); - - doc_printf(doc, "%%%%Pages: %d\n", number); - if (doc->number_up > 1 || doc->fit_to_page) - doc_printf(doc, "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", - doc->PageLeft, doc->PageBottom, doc->PageRight, doc->PageTop); - else - doc_printf(doc, "%%%%BoundingBox: %d %d %d %d\n", - doc->new_bounding_box[0], doc->new_bounding_box[1], - doc->new_bounding_box[2], doc->new_bounding_box[3]); - - return (linelen); -} - - -// -// 'do_prolog()' - Send the necessary document prolog commands. -// - -static void -do_prolog(pstops_doc_t *doc, // I - Document information - ppd_file_t *ppd) // I - PPD file -{ - char *ps; // PS commands - - - // - // Send the document prolog commands... - // - - if (ppd && ppd->patches) - { - doc_puts(doc, "%%BeginFeature: *JobPatchFile 1\n"); - doc_puts(doc, ppd->patches); - doc_puts(doc, "\n%%EndFeature\n"); - } - - if ((ps = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL) - { - doc_puts(doc, ps); - free(ps); - } - - // - // Define ESPshowpage here so that applications that define their - // own procedure to do a showpage pick it up... - // - - if (doc->use_ESPshowpage) - doc_puts(doc, "userdict/ESPshowpage/showpage load put\n" - "userdict/showpage{}put\n"); -} - - -// -// 'do_setup()' - Send the necessary document setup commands. -// - -static void -do_setup(pstops_doc_t *doc, // I - Document information - ppd_file_t *ppd) // I - PPD file -{ - char *ps; // PS commands - - - // - // Disable CTRL-D so that embedded files don't cause printing - // errors... - - - doc_puts(doc, "% Disable CTRL-D as an end-of-file marker...\n"); - doc_puts(doc, "userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n"); - - // - // Send all the printer-specific setup commands... - // - - if ((ps = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL) - { - doc_puts(doc, ps); - free(ps); - } - - if ((ps = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL) - { - doc_puts(doc, ps); - free(ps); - } - - // - // Set the number of copies for the job... - // - - if (doc->copies != 1 && (!doc->collate || !doc->slow_collate)) - { - doc_printf(doc, "%%RBIBeginNonPPDFeature: *NumCopies %d\n", doc->copies); - doc_printf(doc, - "%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse\n" - "{1 dict begin/NumCopies exch def currentdict end " - "setpagedevice}\n" - "{userdict/#copies 3 -1 roll put}ifelse\n", doc->copies); - doc_puts(doc, "%RBIEndNonPPDFeature\n"); - } - - // - // If we are doing N-up printing, disable setpagedevice... - // - - if (doc->number_up > 1) - { - doc_puts(doc, "userdict/CUPSsetpagedevice/setpagedevice load put\n"); - doc_puts(doc, "userdict/setpagedevice{pop}bind put\n"); - } - - // - // Make sure we have rectclip and rectstroke procedures of some sort... - // - - write_common(doc); - - // - // Write the page and label prologs... - // - - if (doc->number_up == 2 || doc->number_up == 6) - { - // - // For 2- and 6-up output, rotate the labels to match the orientation - // of the pages... - // - - if (doc->Orientation & 1) - write_label_prolog(doc, doc->page_label, doc->PageBottom, - doc->PageWidth - doc->PageLength + - doc->PageTop, doc->PageLength); - else - write_label_prolog(doc, doc->page_label, doc->PageLeft, doc->PageRight, - doc->PageLength); - } - else - write_label_prolog(doc, doc->page_label, doc->PageBottom, doc->PageTop, - doc->PageWidth); -} - - -// -// 'doc_printf()' - Send a formatted string to the output file and/or the -// temp file. -// -// This function should be used for all page-level output that is affected -// by ordering, collation, etc. -// - -static void -doc_printf(pstops_doc_t *doc, // I - Document information - const char *format, // I - Printf-style format string - ...) // I - Additional arguments as needed -{ - va_list ap; // Pointer to arguments - char buffer[1024]; // Output buffer - ssize_t bytes; // Number of bytes to write - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - va_start(ap, format); - bytes = vsnprintf(buffer, sizeof(buffer), format, ap); - va_end(ap); - - if ((size_t)bytes > sizeof(buffer)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Buffer overflow detected, truncating."); - bytes = sizeof(buffer); - } - - doc_write(doc, buffer, (size_t)bytes); -} - - -// -// 'doc_putc()' - Send a single character to the output file and/or the -// temp file. -// -// This function should be used for all page-level output that is affected -// by ordering, collation, etc. -// - -static void -doc_putc(pstops_doc_t *doc, // I - Document information - const char c) // I - Character to send -{ - doc_write(doc, &c, 1); -} - - -// -// 'doc_puts()' - Send a nul-terminated string to the output file and/or the -// temp file. -// -// This function should be used for all page-level output that is affected -// by ordering, collation, etc. -// - -static void -doc_puts(pstops_doc_t *doc, // I - Document information - const char *s) // I - String to send -{ - doc_write(doc, s, strlen(s)); -} - - -// -// 'doc_write()' - Send data to the output file and/or the temp file. -// - -static void -doc_write(pstops_doc_t *doc, // I - Document information - const char *s, // I - Data to send - size_t len) // I - Number of bytes to send -{ - if (!doc->slow_order) - fwrite(s, 1, len, doc->outputfp); - - if (doc->temp) - cupsFileWrite(doc->temp, s, len); -} - - -// -// 'end_nup()' - End processing for N-up printing. -// - -static void -end_nup(pstops_doc_t *doc, // I - Document information - int number) // I - Page number -{ - if (doc->number_up > 1) - doc_puts(doc, "userdict/ESPsave get restore\n"); - - switch (doc->number_up) - { - case 1 : - if (doc->use_ESPshowpage) - { - write_labels(doc, doc->Orientation); - doc_puts(doc, "ESPshowpage\n"); - } - break; - - case 2 : - case 6 : - if (is_last_page(number) && doc->use_ESPshowpage) - { - if (doc->Orientation & 1) - { - // - // Rotate the labels back to portrait... - // - - write_labels(doc, doc->Orientation - 1); - } - else if (doc->Orientation == 0) - { - // - // Rotate the labels to landscape... - // - - write_labels(doc, doc->normal_landscape ? 1 : 3); - } - else - { - // - // Rotate the labels to landscape... - // - - write_labels(doc, doc->normal_landscape ? 3 : 1); - } - - doc_puts(doc, "ESPshowpage\n"); - } - break; - - default : - if (is_last_page(number) && doc->use_ESPshowpage) - { - write_labels(doc, doc->Orientation); - doc_puts(doc, "ESPshowpage\n"); - } - break; - } - - fflush(doc->outputfp); -} - - -// -// 'include_feature()' - Include a printer option/feature command. -// - -static int // O - New number of options -include_feature( - pstops_doc_t *doc, // I - Document information - ppd_file_t *ppd, // I - PPD file - const char *line, // I - DSC line - int num_options, // I - Number of options - cups_option_t **options) // IO - Options -{ - char name[255], // Option name - value[255]; // Option value - ppd_option_t *option; // Option in file - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - // - // Get the "%%IncludeFeature: *Keyword OptionKeyword" values... - // - - if (sscanf(line + 17, "%254s%254s", name, value) != 2) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: The %%IncludeFeature: comment is not valid."); - return (num_options); - } - - // - // Find the option and choice... - // - - if ((option = ppdFindOption(ppd, name + 1)) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPSToPS: Unknown option \"%s\".", name + 1); - return (num_options); - } - - if (option->section == PPD_ORDER_EXIT || - option->section == PPD_ORDER_JCL) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPSToPS: Option \"%s\" cannot be included via " - "%%%%IncludeFeature.", name + 1); - return (num_options); - } - - if (!ppdFindChoice(option, value)) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPSToPS: Unknown choice \"%s\" for option \"%s\".", - value, name + 1); - return (num_options); - } - - // - // Add the option to the option array and return... - // - - return (cupsAddOption(name + 1, value, num_options, options)); -} - - -// -// 'parse_text()' - Parse a text value in a comment. -// -// This function parses a DSC text value as defined on page 36 of the -// DSC specification. Text values are either surrounded by parenthesis -// or whitespace-delimited. -// -// The value returned is the literal characters for the entire text -// string, including any parenthesis and escape characters. -// - -static char * // O - Value or NULL on error -parse_text(const char *start, // I - Start of text value - char **end, // O - End of text value - char *buffer, // I - Buffer - size_t bufsize) // I - Size of buffer -{ - char *bufptr, // Pointer in buffer - *bufend; // End of buffer - int level; // Parenthesis level - - - // - // Skip leading whitespace... - // - - while (isspace(*start & 255)) - start ++; - - // - // Then copy the value... - // - - level = 0; - bufptr = buffer; - bufend = buffer + bufsize - 1; - - while (*start && bufptr < bufend) - { - if (isspace(*start & 255) && !level) - break; - - *bufptr++ = *start; - - if (*start == '(') - level ++; - else if (*start == ')') - { - if (!level) - { - start ++; - break; - } - else - level --; - } - else if (*start == '\\') - { - // - // Copy escaped character... - // - - int i; // Looping var - - - for (i = 1; - i <= 3 && isdigit(start[i] & 255) && bufptr < bufend; - *bufptr++ = start[i], i ++); - } - - start ++; - } - - *bufptr = '\0'; - - // - // Return the value and new pointer into the line... - // - - if (end) - *end = (char *)start; - - if (bufptr == bufend) - return (NULL); - else - return (buffer); -} - - -// -// 'ps_hex()' - Print binary data as a series of hexadecimal numbers. -// - -static void -ps_hex(pstops_doc_t *doc, - cf_ib_t *data, // I - Data to print - int length, // I - Number of bytes to print - int last_line) // I - Last line of raster data? -{ - static int col = 0; // Current column - static char *hex = "0123456789ABCDEF"; - // Hex digits - - - while (length > 0) - { - // - // Put the hex chars out to the file; note that we don't use fprintf() - // for speed reasons... - // - - doc_putc(doc, hex[*data >> 4]); - doc_putc(doc, hex[*data & 15]); - - data ++; - length --; - - col += 2; - if (col > 78) - { - doc_putc(doc, '\n'); - col = 0; - } - } - - if (last_line && col) - { - doc_putc(doc, '\n'); - col = 0; - } -} - - -// -// 'ps_ascii85()' - Print binary data as a series of base-85 numbers. -// - -static void -ps_ascii85(pstops_doc_t *doc, - cf_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 - static int col = 0; // Current column - - - while (length > 3) - { - b = (((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3]; - - if (b == 0) - { - doc_putc(doc, 'z'); - col ++; - } - 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 + '!'; - - doc_write(doc, (const char *)c, 5); - col += 5; - } - - data += 4; - length -= 4; - - if (col >= 75) - { - doc_putc(doc, '\n'); - col = 0; - } - } - - 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 + '!'; - - doc_write(doc, (const char *)c, length + 1); - } - - doc_puts(doc, "~>\n"); - col = 0; - } -} - - -// -// 'set_pstops_options()' - Set pstops options. -// - -static int -set_pstops_options( - pstops_doc_t *doc, // I - Document information - ppd_file_t *ppd, // I - PPD file - int job_id, // I - Job ID - char *job_user, // I - Job User - char *job_title, // I - Job Title - int copies, // I - Number of copies - int num_options, // I - Number of options - cups_option_t *options, // I - Options - cf_logfunc_t logfunc, // I - Logging function, - // NULL for no logging - void *logdata, // I - User data for logging function, - // can be NULL - cf_filter_iscanceledfunc_t iscanceledfunc, // I - Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata) // I - User data for is-canceled - // function, can be NULL -{ - const char *val; // Option value - int intval; // Integer option value - ppd_attr_t *attr; // PPD attribute - ppd_option_t *option; // PPD option - ppd_choice_t *choice; // PPD choice - const char *content_type; // Original content type - int max_copies; // Maximum number of copies supported - cf_logfunc_t log = logfunc; - void *ld = logdata; - cf_filter_iscanceledfunc_t iscanceled = iscanceledfunc; - void *icd = iscanceleddata; - - - // - // Initialize document information structure... - // - - memset(doc, 0, sizeof(pstops_doc_t)); - - // Job info, to appear on the printer's front panel while printing - doc->job_id = (job_id > 0 ? job_id : 0); - doc->user = (job_user ? job_user : "Unknown"); - doc->title = (job_title ? job_title : ""); - - // Number of copies, 1 if this filter should not handle copies - doc->copies = (copies > 0 ? copies : 1); - - // Logging function - doc->logfunc = log; - doc->logdata = ld; - - // Job-is-canceled function - doc->iscanceledfunc = iscanceled; - doc->iscanceleddata = icd; - - // Set some common values - ppdFilterSetCommonOptions(ppd, num_options, options, 1, - &doc->Orientation, &doc->Duplex, - &doc->LanguageLevel, &doc->Color, - &doc->PageLeft, &doc->PageRight, - &doc->PageTop, &doc->PageBottom, - &doc->PageWidth, &doc->PageLength, - log, ld); - - if (ppd && ppd->landscape > 0) - doc->normal_landscape = 1; - - doc->bounding_box[0] = (int)(doc->PageLeft); - doc->bounding_box[1] = (int)(doc->PageBottom); - doc->bounding_box[2] = (int)(doc->PageRight); - doc->bounding_box[3] = (int)(doc->PageTop); - - doc->new_bounding_box[0] = INT_MAX; - doc->new_bounding_box[1] = INT_MAX; - doc->new_bounding_box[2] = INT_MIN; - doc->new_bounding_box[3] = INT_MIN; - - // - // AP_FIRSTPAGE_* and the corresponding non-first-page options. - // - - doc->ap_input_slot = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options, - options); - doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options, - options); - doc->ap_media_color = cupsGetOption("AP_FIRSTPAGE_MediaColor", num_options, - options); - doc->ap_media_type = cupsGetOption("AP_FIRSTPAGE_MediaType", num_options, - options); - doc->ap_page_region = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options, - options); - doc->ap_page_size = cupsGetOption("AP_FIRSTPAGE_PageSize", num_options, - options); - - if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL) - doc->input_slot = choice->choice; - if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL) - doc->manual_feed = choice->choice; - if ((choice = ppdFindMarkedChoice(ppd, "MediaColor")) != NULL) - doc->media_color = choice->choice; - if ((choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL) - doc->media_type = choice->choice; - if ((choice = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL) - doc->page_region = choice->choice; - if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) != NULL) - doc->page_size = choice->choice; - - // - // collate, multiple-document-handling - // - - 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-uncollated-copies allows for uncollated copies. - // - - doc->collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0; - } - - if ((val = cupsGetOption("Collate", num_options, options)) != NULL && - (!strcasecmp(val, "true") ||!strcasecmp(val, "on") || - !strcasecmp(val, "yes"))) - doc->collate = 1; - - // - // emit-jcl - // - - if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL && - (!strcasecmp(val, "false") || !strcasecmp(val, "off") || - !strcasecmp(val, "no") || !strcmp(val, "0"))) - doc->emit_jcl = 0; - else - doc->emit_jcl = 1; - - // - // fit-to-page/ipp-attribute-fidelity - // - // (Only for original PostScript content) - // - - if ((content_type = getenv("CONTENT_TYPE")) == NULL) - content_type = "application/postscript"; - - if (!strcasecmp(content_type, "application/postscript")) - { - if ((val = cupsGetOption("fit-to-page", num_options, options)) != NULL && - !strcasecmp(val, "true")) - doc->fit_to_page = 1; - else if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, - options)) != NULL && - !strcasecmp(val, "true")) - doc->fit_to_page = 1; - } - - // - // mirror/MirrorPrint - // - - if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL) - { - val = choice->choice; - choice->marked = 0; - } - else - val = cupsGetOption("mirror", num_options, options); - - if (val && (!strcasecmp(val, "true") || !strcasecmp(val, "on") || - !strcasecmp(val, "yes"))) - doc->mirror = 1; - - // - // number-up - // - - if ((val = cupsGetOption("number-up", num_options, options)) != NULL) - { - switch (intval = atoi(val)) - { - case 1 : - case 2 : - case 4 : - case 6 : - case 9 : - case 16 : - doc->number_up = intval; - break; - default : - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Unsupported number-up value %d, using " - "number-up=1.", intval); - doc->number_up = 1; - break; - } - } - else - doc->number_up = 1; - - // - // number-up-layout - // - - if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) - { - if (!strcasecmp(val, "lrtb")) - doc->number_up_layout = PSTOPS_LAYOUT_LRTB; - else if (!strcasecmp(val, "lrbt")) - doc->number_up_layout = PSTOPS_LAYOUT_LRBT; - else if (!strcasecmp(val, "rltb")) - doc->number_up_layout = PSTOPS_LAYOUT_RLTB; - else if (!strcasecmp(val, "rlbt")) - doc->number_up_layout = PSTOPS_LAYOUT_RLBT; - else if (!strcasecmp(val, "tblr")) - doc->number_up_layout = PSTOPS_LAYOUT_TBLR; - else if (!strcasecmp(val, "tbrl")) - doc->number_up_layout = PSTOPS_LAYOUT_TBRL; - else if (!strcasecmp(val, "btlr")) - doc->number_up_layout = PSTOPS_LAYOUT_BTLR; - else if (!strcasecmp(val, "btrl")) - doc->number_up_layout = PSTOPS_LAYOUT_BTRL; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Unsupported number-up-layout value %s, using " - "number-up-layout=lrtb.", val); - doc->number_up_layout = PSTOPS_LAYOUT_LRTB; - } - } - else - doc->number_up_layout = PSTOPS_LAYOUT_LRTB; - - // - // OutputOrder - // - - if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL) - { - if (!strcasecmp(val, "Reverse")) - doc->output_order = 1; - } - else if (ppd) - { - // - // Figure out the right default output order from the PPD file... - // - - if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL && - (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL && - attr->value) - doc->output_order = !strcasecmp(attr->value, "Reverse"); - else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL && - attr->value) - doc->output_order = !strcasecmp(attr->value, "Reverse"); - } - - // - // page-border - // - - if ((val = cupsGetOption("page-border", num_options, options)) != NULL) - { - if (!strcasecmp(val, "none")) - doc->page_border = PSTOPS_BORDERNONE; - else if (!strcasecmp(val, "single")) - doc->page_border = PSTOPS_BORDERSINGLE; - else if (!strcasecmp(val, "single-thick")) - doc->page_border = PSTOPS_BORDERSINGLE2; - else if (!strcasecmp(val, "double")) - doc->page_border = PSTOPS_BORDERDOUBLE; - else if (!strcasecmp(val, "double-thick")) - doc->page_border = PSTOPS_BORDERDOUBLE2; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Unsupported page-border value %s, using " - "page-border=none.", val); - doc->page_border = PSTOPS_BORDERNONE; - } - } - else - doc->page_border = PSTOPS_BORDERNONE; - - // - // page-label - // - - doc->page_label = cupsGetOption("page-label", num_options, options); - - // - // input-page-ranges - // - - doc->inputPageRange=cupsGetOption("input-page-ranges", num_options, options); - - // - // page-ranges - // - - doc->page_ranges = cupsGetOption("page-ranges", num_options, options); - - // - // page-set - // - - doc->page_set = cupsGetOption("page-set", num_options, options); - - // - // Now figure out if we have to force collated copies, etc. - // - - if ((attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL) - max_copies = atoi(attr->value); - else if (ppd && ppd->manual_copies) - max_copies = 1; - else - max_copies = 9999; - - if (doc->copies > max_copies) - doc->collate = 1; - else if (ppd && ppd->manual_copies && doc->Duplex && doc->copies > 1) - { - // - // Force collated copies when printing a duplexed document to - // a non-PS printer that doesn't do hardware copy generation. - // Otherwise the copies will end up on the front/back side of - // each page. - // - - doc->collate = 1; - } - - // - // See if we have to filter the fast or slow way... - // - - if (doc->collate && doc->copies > 1) - { - // - // See if we need to manually collate the pages... - // - - doc->slow_collate = 1; - - if (doc->copies <= max_copies && - (choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL && - !strcasecmp(choice->choice, "True")) - { - // - // Hardware collate option is selected, see if the option is - // conflicting - if not, collate in hardware. Otherwise, - // turn the hardware collate option off... - // - - if ((option = ppdFindOption(ppd, "Collate")) != NULL && - !option->conflicted) - doc->slow_collate = 0; - else - ppdMarkOption(ppd, "Collate", "False"); - } - } - else - doc->slow_collate = 0; - - if (!ppdFindOption(ppd, "OutputOrder") && doc->output_order) - doc->slow_order = 1; - else - doc->slow_order = 0; - - if (doc->Duplex && - (doc->slow_collate || doc->slow_order || - ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL && - attr->value && !strcasecmp(attr->value, "true")))) - doc->slow_duplex = 1; - else - doc->slow_duplex = 0; - - // - // Create a temporary file for page data if we need to filter slowly... - // - - if (doc->slow_order || doc->slow_collate) - { - if ((doc->temp = cupsTempFile2(doc->tempfile, - sizeof(doc->tempfile))) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Unable to create temporary file: %s", - strerror(errno)); - return (1); - } - } - - // - // Figure out if we should use ESPshowpage or not... - // - - if (doc->page_label || getenv("CLASSIFICATION") || doc->number_up > 1 || - doc->page_border) - { - // - // Yes, use ESPshowpage... - // - - doc->use_ESPshowpage = 1; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: slow_collate=%d, slow_duplex=%d, slow_order=%d", - doc->slow_collate, doc->slow_duplex, doc->slow_order); - - return(0); -} - - -// -// 'skip_page()' - Skip past a page that won't be printed. -// - -static ssize_t // O - Length of next line -skip_page(pstops_doc_t *doc, // I - Document information - char *line, // I - Line buffer - ssize_t linelen, // I - Length of initial line - size_t linesize) // I - Size of line buffer -{ - int level; // Embedded document level - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - level = 0; - - while ((linelen = (ssize_t)cupsFileGetLine(doc->inputfp, line, linesize)) > 0) - { - if (level == 0 && - (!strncmp(line, "%%Page:", 7) || !strncmp(line, "%%Trailer", 9))) - break; - else if (!strncmp(line, "%%BeginDocument", 15) || - !strncmp(line, "%ADO_BeginApplication", 21)) - level ++; - else if ((!strncmp(line, "%%EndDocument", 13) || - !strncmp(line, "%ADO_EndApplication", 19)) && level > 0) - level --; - else if (!strncmp(line, "%%BeginBinary:", 14) || - (!strncmp(line, "%%BeginData:", 12) && - !strstr(line, "ASCII") && !strstr(line, "Hex"))) - { - // - // Skip binary data... - // - - ssize_t bytes; // Bytes of data - - bytes = atoi(strchr(line, ':') + 1); - - while (bytes > 0) - { - if ((size_t)bytes > linesize) - linelen = (ssize_t)cupsFileRead(doc->inputfp, line, linesize); - else - linelen = (ssize_t)cupsFileRead(doc->inputfp, line, (size_t)bytes); - - if (linelen < 1) - { - line[0] = '\0'; - if (log) - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPSToPS: Early end-of-file while reading binary data: %s", - strerror(errno)); - return (0); - } - - bytes -= linelen; - } - } - } - - return (linelen); -} - - -// -// 'start_nup()' - Start processing for N-up printing. -// - -static void -start_nup(pstops_doc_t *doc, // I - Document information - int number, // I - Page number - int show_border, // I - Show the border? - const int *bounding_box) // I - BoundingBox value -{ - int pos; // Position on page - int x, y; // Relative position of subpage - double w, l, // Width and length of subpage - tx, ty; // Translation values for subpage - double pagew, // Printable width of page - pagel; // Printable height of page - int bboxx, // BoundingBox X origin - bboxy, // BoundingBox Y origin - bboxw, // BoundingBox width - bboxl; // BoundingBox height - double margin = 0; // Current margin for border - cf_logfunc_t log = doc->logfunc; - void *ld = doc->logdata; - - - if (doc->number_up > 1) - doc_puts(doc, "userdict/ESPsave save put\n"); - - pos = (number - 1) % doc->number_up; - pagew = doc->PageRight - doc->PageLeft; - pagel = doc->PageTop - doc->PageBottom; - - if (doc->fit_to_page) - { - bboxx = bounding_box[0]; - bboxy = bounding_box[1]; - bboxw = bounding_box[2] - bounding_box[0]; - bboxl = bounding_box[3] - bounding_box[1]; - } - else - { - bboxx = 0; - bboxy = 0; - bboxw = (int)(doc->PageWidth); - bboxl = (int)(doc->PageLength); - } - - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: pagew = %.1f, pagel = %.1f", pagew, pagel); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: bboxx = %d, bboxy = %d, bboxw = %d, bboxl = %d", - bboxx, bboxy, bboxw, bboxl); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: PageLeft = %.1f, PageRight = %.1f", - doc->PageLeft, doc->PageRight); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: PageTop = %.1f, PageBottom = %.1f", - doc->PageTop, doc->PageBottom); - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPSToPS: PageWidth = %.1f, PageLength = %.1f", - doc->PageWidth, doc->PageLength); - } - - switch (doc->Orientation) - { - case 1 : // Landscape - doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", doc->PageLength); - break; - case 2 : // Reverse Portrait - doc_printf(doc, "%.1f %.1f translate 180 rotate\n", doc->PageWidth, - doc->PageLength); - break; - case 3 : // Reverse Landscape - doc_printf(doc, "0.0 %.1f translate -90 rotate\n", doc->PageWidth); - break; - } - - // - // Mirror the page as needed... - // - - if (doc->mirror) - doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", doc->PageWidth); - - // - // Offset and scale as necessary for fit_to_page/fit-to-page/number-up... - // - - if (doc->Duplex && doc->number_up > 1 && ((number / doc->number_up) & 1)) - doc_printf(doc, "%.1f %.1f translate\n", doc->PageWidth - doc->PageRight, - doc->PageBottom); - else if (doc->number_up > 1 || doc->fit_to_page) - doc_printf(doc, "%.1f %.1f translate\n", doc->PageLeft, doc->PageBottom); - - switch (doc->number_up) - { - default : - if (doc->fit_to_page) - { - w = pagew; - l = w * bboxl / bboxw; - - if (l > pagel) - { - l = pagel; - w = l * bboxw / bboxl; - } - - tx = 0.5 * (pagew - w); - ty = 0.5 * (pagel - l); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", tx, ty, - w / bboxw, l / bboxl); - } - else - w = doc->PageWidth; - break; - - case 2 : - if (doc->Orientation & 1) - { - x = pos & 1; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - x = 1 - x; - - w = pagel; - l = w * bboxl / bboxw; - - if (l > (pagew * 0.5)) - { - l = pagew * 0.5; - w = l * bboxw / bboxl; - } - - tx = 0.5 * (pagew * 0.5 - l); - ty = 0.5 * (pagel - w); - - if (doc->normal_landscape) - doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel); - else - doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", - ty, tx + pagew * 0.5 * x, w / bboxw, l / bboxl); - } - else - { - x = pos & 1; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 1 - x; - - l = pagew; - w = l * bboxw / bboxl; - - if (w > (pagel * 0.5)) - { - w = pagel * 0.5; - l = w * bboxl / bboxw; - } - - tx = 0.5 * (pagel * 0.5 - w); - ty = 0.5 * (pagew - l); - - if (doc->normal_landscape) - doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew); - else - doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", - tx + pagel * 0.5 * x, ty, w / bboxw, l / bboxl); - } - break; - - case 4 : - if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL) - { - x = (pos / 2) & 1; - y = pos & 1; - } - else - { - x = pos & 1; - y = (pos / 2) & 1; - } - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 1 - x; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - y = 1 - y; - - w = pagew * 0.5; - l = w * bboxl / bboxw; - - if (l > (pagel * 0.5)) - { - l = pagel * 0.5; - w = l * bboxw / bboxl; - } - - tx = 0.5 * (pagew * 0.5 - w); - ty = 0.5 * (pagel * 0.5 - l); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", - tx + x * pagew * 0.5, ty + y * pagel * 0.5, - w / bboxw, l / bboxl); - break; - - case 6 : - if (doc->Orientation & 1) - { - if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL) - { - x = pos / 3; - y = pos % 3; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 1 - x; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - y = 2 - y; - } - else - { - x = pos & 1; - y = pos / 2; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 1 - x; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - y = 2 - y; - } - - w = pagel * 0.5; - l = w * bboxl / bboxw; - - if (l > (pagew * 0.333)) - { - l = pagew * 0.333; - w = l * bboxw / bboxl; - } - - tx = 0.5 * (pagel - 2 * w); - ty = 0.5 * (pagew - 3 * l); - - if (doc->normal_landscape) - doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel); - else - doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", - tx + x * w, ty + y * l, l / bboxl, w / bboxw); - } - else - { - if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL) - { - x = pos / 2; - y = pos & 1; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 2 - x; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - y = 1 - y; - } - else - { - x = pos % 3; - y = pos / 3; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 2 - x; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - y = 1 - y; - } - - l = pagew * 0.5; - w = l * bboxw / bboxl; - - if (w > (pagel * 0.333)) - { - w = pagel * 0.333; - l = w * bboxl / bboxw; - } - - tx = 0.5 * (pagel - 3 * w); - ty = 0.5 * (pagew - 2 * l); - - if (doc->normal_landscape) - doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew); - else - doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", - tx + w * x, ty + l * y, w / bboxw, l / bboxl); - - } - break; - - case 9 : - if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL) - { - x = (pos / 3) % 3; - y = pos % 3; - } - else - { - x = pos % 3; - y = (pos / 3) % 3; - } - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 2 - x; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - y = 2 - y; - - w = pagew * 0.333; - l = w * bboxl / bboxw; - - if (l > (pagel * 0.333)) - { - l = pagel * 0.333; - w = l * bboxw / bboxl; - } - - tx = 0.5 * (pagew * 0.333 - w); - ty = 0.5 * (pagel * 0.333 - l); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", - tx + x * pagew * 0.333, ty + y * pagel * 0.333, - w / bboxw, l / bboxl); - break; - - case 16 : - if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL) - { - x = (pos / 4) & 3; - y = pos & 3; - } - else - { - x = pos & 3; - y = (pos / 4) & 3; - } - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX) - x = 3 - x; - - if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY) - y = 3 - y; - - w = pagew * 0.25; - l = w * bboxl / bboxw; - - if (l > (pagel * 0.25)) - { - l = pagel * 0.25; - w = l * bboxw / bboxl; - } - - tx = 0.5 * (pagew * 0.25 - w); - ty = 0.5 * (pagel * 0.25 - l); - - doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", - tx + x * pagew * 0.25, ty + y * pagel * 0.25, - w / bboxw, l / bboxl); - break; - } - - // - // Draw borders as necessary... - // - - if (doc->page_border && show_border) - { - int rects; // Number of border rectangles - double fscale; // Scaling value for points - - - rects = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1; - fscale = doc->PageWidth / w; - margin = 2.25 * fscale; - - // - // Set the line width and color... - // - - doc_puts(doc, "gsave\n"); - doc_printf(doc, "%.3f setlinewidth 0 setgray newpath\n", - (doc->page_border & PSTOPS_BORDERTHICK) ? 0.5 * fscale : - 0.24 * fscale); - - // - // Draw border boxes... - // - - for (; rects > 0; rects --, margin += 2 * fscale) - if (doc->number_up > 1) - doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n", - margin, - margin, - bboxw - 2 * margin, - bboxl - 2 * margin); - else - doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n", - doc->PageLeft + margin, - doc->PageBottom + margin, - doc->PageRight - doc->PageLeft - 2 * margin, - doc->PageTop - doc->PageBottom - 2 * margin); - - // - // Restore pen settings... - // - - doc_puts(doc, "grestore\n"); - } - - if (doc->fit_to_page) - { - // - // Offset the page by its bounding box... - // - - doc_printf(doc, "%d %d translate\n", -bounding_box[0], - -bounding_box[1]); - } - - if (doc->fit_to_page || doc->number_up > 1) - { - // - // Clip the page to the page's bounding box... - // - - doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrc\n", - bboxx + margin, bboxy + margin, - bboxw - 2 * margin, bboxl - 2 * margin); - } -} - - -// -// 'write_common()' - Write common procedures... -// - -static void -write_common(pstops_doc_t *doc) -{ - doc_puts(doc, - "% x y w h ESPrc - Clip to a rectangle.\n" - "userdict/ESPrc/rectclip where{pop/rectclip load}\n" - "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n" - "neg 0 rlineto closepath clip newpath}bind}ifelse put\n"); - doc_puts(doc, - "% x y w h ESPrf - Fill a rectangle.\n" - "userdict/ESPrf/rectfill where{pop/rectfill load}\n" - "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n" - "neg 0 rlineto closepath fill grestore}bind}ifelse put\n"); - doc_puts(doc, - "% x y w h ESPrs - Stroke a rectangle.\n" - "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n" - "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n" - "neg 0 rlineto closepath stroke grestore}bind}ifelse put\n"); -} - - -// -// 'write_label_prolog()' - Write the prolog with the classification -// and page label. - - -static void -write_label_prolog(pstops_doc_t *doc, // I - Document info - const char *label, // I - Page label - float bottom, // I - Bottom position in points - float top, // I - Top position in points - float width) // I - Width in points -{ - const char *classification; // CLASSIFICATION environment - // variable - const char *ptr; // Temporary string pointer - - - // - // First get the current classification... - // - - if ((classification = getenv("CLASSIFICATION")) == NULL) - classification = ""; - if (strcmp(classification, "none") == 0) - classification = ""; - - // - // If there is nothing to show, bind an empty 'write labels' procedure - // and return... - // - - if (!classification[0] && (label == NULL || !label[0])) - { - doc_puts(doc, "userdict/ESPwl{}bind put\n"); - return; - } - - // - // Set the classification + page label string... - // - - doc_puts(doc, "userdict"); - if (!strcmp(classification, "confidential")) - doc_puts(doc, "/ESPpl(CONFIDENTIAL"); - else if (!strcmp(classification, "classified")) - doc_puts(doc, "/ESPpl(CLASSIFIED"); - else if (!strcmp(classification, "secret")) - doc_puts(doc, "/ESPpl(SECRET"); - else if (!strcmp(classification, "topsecret")) - doc_puts(doc, "/ESPpl(TOP SECRET"); - else if (!strcmp(classification, "unclassified")) - doc_puts(doc, "/ESPpl(UNCLASSIFIED"); - else - { - doc_puts(doc, "/ESPpl("); - - for (ptr = classification; *ptr; ptr ++) - { - if (*ptr < 32 || *ptr > 126) - doc_printf(doc, "\\%03o", *ptr); - else if (*ptr == '_') - doc_puts(doc, " "); - else if (*ptr == '(' || *ptr == ')' || *ptr == '\\') - doc_printf(doc, "\\%c", *ptr); - else - doc_printf(doc, "%c", *ptr); - } - } - - if (label) - { - if (classification[0]) - doc_puts(doc, " - "); - - // - // Quote the label string as needed... - // - - for (ptr = label; *ptr; ptr ++) - { - if (*ptr < 32 || *ptr > 126) - doc_printf(doc, "\\%03o", *ptr); - else if (*ptr == '(' || *ptr == ')' || *ptr == '\\') - doc_printf(doc, "\\%c", *ptr); - else - doc_printf(doc, "%c", *ptr); - } - } - - doc_puts(doc, ")put\n"); - - // - // Then get a 14 point Helvetica-Bold font... - // - - doc_puts(doc, "userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put\n"); - - // - // Finally, the procedure to write the labels on the page... - // - - doc_puts(doc, "userdict/ESPwl{\n"); - doc_puts(doc, " ESPpf setfont\n"); - doc_printf(doc, " ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n", - width * 0.5f); - doc_puts(doc, " 1 setgray\n"); - doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0); - doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0); - doc_puts(doc, " 0 setgray\n"); - doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0); - doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0); - doc_printf(doc, " dup %.0f moveto ESPpl show\n", bottom + 2.0); - doc_printf(doc, " %.0f moveto ESPpl show\n", top - 14.0); - doc_puts(doc, "pop\n"); - doc_puts(doc, "}bind put\n"); -} - - -// -// 'write_labels()' - Write the actual page labels. -// -// This function is a copy of the one in common.c since we need to -// use doc_puts/doc_printf instead of puts/printf... -// - -static void -write_labels(pstops_doc_t *doc, // I - Document information - int orient) // I - Orientation of the page -{ - float width, // Width of page - length; // Length of page - - - doc_puts(doc, "gsave\n"); - - if ((orient ^ doc->Orientation) & 1) - { - width = doc->PageLength; - length = doc->PageWidth; - } - else - { - width = doc->PageWidth; - length = doc->PageLength; - } - - switch (orient & 3) - { - case 1 : // Landscape - doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length); - break; - case 2 : // Reverse Portrait - doc_printf(doc, "%.1f %.1f translate 180 rotate\n", width, length); - break; - case 3 : // Reverse Landscape - doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width); - break; - } - - doc_puts(doc, "ESPwl\n"); - doc_puts(doc, "grestore\n"); -} - - -// -// 'write_labels_outputfile_only()' - Write the actual page labels to the -// output file. -// -// This function is a copy of the one in common.c. Since we use it only -// in this filter, we have moved it to here. -// - -static void -write_labels_outputfile_only(pstops_doc_t *doc,// I - Document information - int orient)// I - Orientation of the page -{ - float width, // Width of page - length; // Length of page - - - doc_puts(doc, "gsave\n"); - - if ((orient ^ doc->Orientation) & 1) - { - width = doc->PageLength; - length = doc->PageWidth; - } - else - { - width = doc->PageWidth; - length = doc->PageLength; - } - - switch (orient & 3) - { - case 1 : // Landscape - doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length); - break; - case 2 : // Reverse Portrait - doc_printf(doc, "%.1f %.1f translate 180 rotate\n", - width, length); - break; - case 3 : // Reverse Landscape - doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width); - break; - } - - doc_puts(doc, "ESPwl\n"); - doc_puts(doc, "grestore\n"); -} - - -// -// 'write_options()' - Write options provided via %%IncludeFeature. -// - -static void -write_options( - pstops_doc_t *doc, // I - Document - ppd_file_t *ppd, // I - PPD file - int num_options, // I - Number of options - cups_option_t *options) // I - Options -{ - int i; // Looping var - ppd_option_t *option; // PPD option - float min_order; // Minimum OrderDependency value - char *doc_setup, // DocumentSetup commands to send - *any_setup; // AnySetup commands to send - - - // - // Figure out the minimum OrderDependency value... - // - - if ((option = ppdFindOption(ppd, "PageRegion")) != NULL) - min_order = option->order; - else - min_order = 999.0f; - - for (i = 0; i < num_options; i ++) - if ((option = ppdFindOption(ppd, options[i].name)) != NULL && - option->order < min_order) - min_order = option->order; - - // - // Mark and extract them... - // - - ppdMarkOptions(ppd, num_options, options); - - doc_setup = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, min_order); - any_setup = ppdEmitString(ppd, PPD_ORDER_ANY, min_order); - - // - // Then send them out... - // - - if (doc->number_up > 1) - { - // - // Temporarily restore setpagedevice so we can set the options... - // - - doc_puts(doc, "userdict/setpagedevice/CUPSsetpagedevice load put\n"); - } - - if (doc_setup) - { - doc_puts(doc, doc_setup); - free(doc_setup); - } - - if (any_setup) - { - doc_puts(doc, any_setup); - free(any_setup); - } - - if (doc->number_up > 1) - { - // - // Disable setpagedevice again... - // - - doc_puts(doc, "userdict/setpagedevice{pop}bind put\n"); - } -} - - -// -// 'write_text_comment()' - Write a DSC text comment. -// - -static void -write_text_comment(pstops_doc_t *doc, // I - Document - const char *name, // I - Comment name ("Title", etc.) - const char *value) // I - Comment value -{ - int len; // Current line length - - - // - // DSC comments are of the form: - // - // %%name: value - // - // The name and value must be limited to 7-bit ASCII for most printers, - // so we escape all non-ASCII and ASCII control characters as described - // in the Adobe Document Structuring Conventions specification. - // - - doc_printf(doc, "%%%%%s: (", name); - len = 5 + strlen(name); - - while (*value) - { - if (*value < ' ' || *value >= 127) - { - // - // Escape this character value... - // - - if (len >= 251) // Keep line < 254 chars - break; - - doc_printf(doc, "\\%03o", *value & 255); - len += 4; - } - else if (*value == '\\') - { - // - // Escape the backslash... - // - - if (len >= 253) // Keep line < 254 chars - break; - - doc_putc(doc, '\\'); - doc_putc(doc, '\\'); - len += 2; - } - else - { - // - // Put this character literally... - // - - if (len >= 254) // Keep line < 254 chars - break; - - doc_putc(doc, *value); - len ++; - } - - value ++; - } - - doc_puts(doc, ")\n"); -} diff --git a/ppd/ipp-private.h b/ppd/ipp-private.h deleted file mode 100644 index 8b36b7bc1..000000000 --- a/ppd/ipp-private.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// Private IPP definitions for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_IPP_PRIVATE_H_ -# define _PPD_IPP_PRIVATE_H_ - - -// -// Include necessary headers... -// - -# include -# include - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Structures... -// - -typedef struct _ppd_ipp_option_s // **** Attribute mapping data **** -{ - int multivalue; // Option has multiple values? - const char *name; // Option/attribute name - ipp_tag_t value_tag; // Value tag for this attribute - ipp_tag_t group_tag; // Group tag for this attribute - ipp_tag_t alt_group_tag; // Alternate group tag for this - // attribute - const ipp_op_t *operations; // Allowed operations for this attr -} _ppd_ipp_option_t; - -// -// Prototypes for private functions... -// - -// encode.c -extern _ppd_ipp_option_t *_ppdIppFindOption(const char *name); - - -// -// C++ magic... -// - -# ifdef __cplusplus -} -# endif // __cplusplus -#endif // !_PPD_IPP_PRIVATE_H_ diff --git a/ppd/label.h b/ppd/label.h deleted file mode 100644 index 04bfab853..000000000 --- a/ppd/label.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// This file contains model number definitions for the CUPS sample -// label printer driver. -// -// Copyright 2007 by Apple Inc. -// Copyright 1997-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#define DYMO_3x0 0 // Dymo Labelwriter 300/330/330 Turbo - -#define ZEBRA_EPL_LINE 0x10 // Zebra EPL line mode printers -#define ZEBRA_EPL_PAGE 0x11 // Zebra EPL page mode printers -#define ZEBRA_ZPL 0x12 // Zebra ZPL-based printers -#define ZEBRA_CPCL 0x13 // Zebra CPCL-based printers - -#define INTELLITECH_PCL 0x20 // Intellitech PCL-based printers diff --git a/ppd/language-private.h b/ppd/language-private.h deleted file mode 100644 index 5bf96fc93..000000000 --- a/ppd/language-private.h +++ /dev/null @@ -1,74 +0,0 @@ -// -// Private localization support functions for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_LANGUAGE_PRIVATE_H_ -# define _PPD_LANGUAGE_PRIVATE_H_ - -// -// Include necessary headers... -// - -# include "config.h" -# include -# include -# ifdef __APPLE__ -# include -# endif // __APPLE__ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Macro for localized text... -// - -# define _(x) x - - -// -// Constants... -// - -# define _PPD_MESSAGE_PO 0 // Message file is in GNU .po format -# define _PPD_MESSAGE_UNQUOTE 1 // Unescape \foo in strings? -# define _PPD_MESSAGE_STRINGS 2 // Message file is in Apple .strings - // format -# define _PPD_MESSAGE_EMPTY 4 // Allow empty localized strings - - -// -// Types... -// - -typedef struct _ppd_message_s // **** Message catalog entry **** -{ - char *msg, // Original string - *str; // Localized string -} _ppd_message_t; - - -// -// Prototypes... -// - -extern const char *_ppdLangString(cups_lang_t *lang, const char *message); -extern void _ppdMessageFree(cups_array_t *a); -extern cups_array_t *_ppdMessageLoad(const char *filename, int flags); -extern const char *_ppdMessageLookup(cups_array_t *a, const char *m); -extern cups_array_t *_ppdMessageNew(void *context); - - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_PPD_LANGUAGE_PRIVATE_H_ diff --git a/ppd/language.c b/ppd/language.c deleted file mode 100644 index 513ca12e2..000000000 --- a/ppd/language.c +++ /dev/null @@ -1,721 +0,0 @@ -// -// I18N/language support for libppd. -// -// Copyright 2007-2017 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "language-private.h" -#include "thread-private.h" -#include "debug-internal.h" -#ifdef HAVE_LANGINFO_H -# include -#endif // HAVE_LANGINFO_H -#ifdef _WIN32 -# include -#else -# include -#endif // _WIN32 -#ifdef HAVE_COREFOUNDATION_H -# include -#endif // HAVE_COREFOUNDATION_H -#include - - -// -// Local globals... -// - -static _ppd_mutex_t lang_mutex = _PPD_MUTEX_INITIALIZER; - // Mutex to control access to cache - -#ifdef __APPLE__ -typedef struct -{ - const char * const language; // Language ID - const char * const locale; // Locale ID -} _apple_language_locale_t; - -static const _apple_language_locale_t apple_language_locale[] = -{ // Language to locale ID LUT - { "en", "en_US" }, - { "nb", "no" }, - { "nb_NO", "no" }, - { "zh-Hans", "zh_CN" }, - { "zh_HANS", "zh_CN" }, - { "zh-Hant", "zh_TW" }, - { "zh_HANT", "zh_TW" }, - { "zh-Hant_CN", "zh_TW" } -}; -#endif // __APPLE__ - - -// -// Local functions... -// - -#ifdef __APPLE__ -static const char *appleLangDefault(void); -# ifdef CUPS_BUNDLEDIR -# ifndef CF_RETURNS_RETAINED -# if __has_feature(attribute_cf_returns_retained) -# define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) -# else -# define CF_RETURNS_RETAINED -# endif // __has_feature(attribute_cf_returns_retained) -# endif // !CF_RETURNED_RETAINED -static cups_array_t *appleMessageLoad(const char *locale) CF_RETURNS_RETAINED; -# endif // CUPS_BUNDLEDIR -#endif // __APPLE__ -static int ppd_message_compare(_ppd_message_t *m1, _ppd_message_t *m2); -static void ppd_message_free(_ppd_message_t *m); -static void ppd_message_load(cups_lang_t *lang); -static int ppd_read_strings(cups_file_t *fp, int flags, cups_array_t *a); -static void ppd_unquote(char *d, const char *s); - - -// -// '_ppdLangString()' - Get a message string. -// -// The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to -// convert the string to the language encoding. -// - -const char * // O - Localized message -_ppdLangString(cups_lang_t *lang, // I - Language - const char *message) // I - Message -{ - const char *s; // Localized message - - - DEBUG_printf(("_ppdLangString(lang=%p, message=\"%s\")", - (void *)lang, message)); - - // - // Range check input... - // - - if (!lang || !message || !*message) - return (message); - - _ppdMutexLock(&lang_mutex); - - // - // Load the message catalog if needed... - // - - if (!lang->strings) - ppd_message_load(lang); - - s = _ppdMessageLookup(lang->strings, message); - - _ppdMutexUnlock(&lang_mutex); - - return (s); -} - - -// -// '_ppdMessageFree()' - Free a messages array. -// - -void -_ppdMessageFree(cups_array_t *a) // I - Message array -{ -#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) - // - // Release the cups.strings dictionary as needed... - // - - if (cupsArrayUserData(a)) - CFRelease((CFDictionaryRef)cupsArrayUserData(a)); -#endif // __APPLE__ && CUPS_BUNDLEDIR - - // - // Free the array... - // - - cupsArrayDelete(a); -} - - -// -// '_ppdMessageLoad()' - Load a .po or .strings file into a messages array. -// - -cups_array_t * // O - New message array -_ppdMessageLoad(const char *filename, // I - Message catalog to load - int flags) // I - Load flags -{ - cups_file_t *fp; // Message file - cups_array_t *a; // Message array - _ppd_message_t *m; // Current message - char s[4096], // String buffer - *ptr, // Pointer into buffer - *temp; // New string - size_t length, // Length of combined strings - ptrlen; // Length of string - - - DEBUG_printf(("4_ppdMessageLoad(filename=\"%s\")", filename)); - - // - // Create an array to hold the messages... - // - - if ((a = _ppdMessageNew(NULL)) == NULL) - { - DEBUG_puts("5_ppdMessageLoad: Unable to allocate array!"); - return (NULL); - } - - // - // Open the message catalog file... - // - - if ((fp = cupsFileOpen(filename, "r")) == NULL) - { - DEBUG_printf(("5_ppdMessageLoad: Unable to open file: %s", - strerror(errno))); - return (a); - } - - if (flags & _PPD_MESSAGE_STRINGS) - { - while (ppd_read_strings(fp, flags, a)); - } - else - { - // - // Read messages from the catalog file until EOF... - // - // The format is the GNU gettext .po format, which is fairly simple: - // - // msgid "some text" - // msgstr "localized text" - // - // The ID and localized text can span multiple lines using the form: - // - // msgid "" - // "some long text" - // msgstr "" - // "localized text spanning " - // "multiple lines" - // - - m = NULL; - - while (cupsFileGets(fp, s, sizeof(s)) != NULL) - { - // - // Skip blank and comment lines... - // - - if (s[0] == '#' || !s[0]) - continue; - - // - // Strip the trailing quote... - // - - if ((ptr = strrchr(s, '\"')) == NULL) - continue; - - *ptr = '\0'; - - // - // Find start of value... - // - - if ((ptr = strchr(s, '\"')) == NULL) - continue; - - ptr ++; - - // - // Unquote the text... - // - - if (flags & _PPD_MESSAGE_UNQUOTE) - ppd_unquote(ptr, ptr); - - // - // Create or add to a message... - // - - if (!strncmp(s, "msgid", 5)) - { - // - // Add previous message as needed... - // - - if (m) - { - if (m->str && (m->str[0] || (flags & _PPD_MESSAGE_EMPTY))) - cupsArrayAdd(a, m); - else - { - // - // Translation is empty, don't add it... (STR #4033) - // - - free(m->msg); - if (m->str) - free(m->str); - free(m); - } - } - - // - // Create a new message with the given msgid string... - // - - if ((m = (_ppd_message_t *)calloc(1, sizeof(_ppd_message_t))) == NULL) - break; - - if ((m->msg = strdup(ptr)) == NULL) - { - free(m); - m = NULL; - break; - } - } - else if (s[0] == '\"' && m) - { - // - // Append to current string... - // - - length = strlen(m->str ? m->str : m->msg); - ptrlen = strlen(ptr); - - if ((temp = realloc(m->str ? m->str : m->msg, - length + ptrlen + 1)) == NULL) - { - if (m->str) - free(m->str); - free(m->msg); - free(m); - m = NULL; - break; - } - - if (m->str) - { - // - // Copy the new portion to the end of the msgstr string - safe - // to use memcpy because the buffer is allocated to the correct - // size... - // - - m->str = temp; - - memcpy(m->str + length, ptr, ptrlen + 1); - } - else - { - // - // Copy the new portion to the end of the msgid string - safe - // to use memcpy because the buffer is allocated to the correct - // size... - // - - m->msg = temp; - - memcpy(m->msg + length, ptr, ptrlen + 1); - } - } - else if (!strncmp(s, "msgstr", 6) && m) - { - // - // Set the string... - // - - if ((m->str = strdup(ptr)) == NULL) - { - free(m->msg); - free(m); - m = NULL; - break; - } - } - } - - // - // Add the last message string to the array as needed... - // - - if (m) - { - if (m->str && (m->str[0] || (flags & _PPD_MESSAGE_EMPTY))) - cupsArrayAdd(a, m); - else - { - // - // Translation is empty, don't add it... (STR #4033) - // - - free(m->msg); - if (m->str) - free(m->str); - free(m); - } - } - } - - // - // Close the message catalog file and return the new array... - // - - cupsFileClose(fp); - - DEBUG_printf(("5_ppdMessageLoad: Returning %d messages...", - cupsArrayCount(a))); - - return (a); -} - - -// -// '_ppdMessageLookup()' - Lookup a message string. -// - -const char * // O - Localized message -_ppdMessageLookup(cups_array_t *a, // I - Message array - const char *m) // I - Message -{ - _ppd_message_t key, // Search key - *match; // Matching message - - - DEBUG_printf(("_ppdMessageLookup(a=%p, m=\"%s\")", (void *)a, m)); - - // - // Lookup the message string; if it doesn't exist in the catalog, - // then return the message that was passed to us... - // - - key.msg = (char *)m; - match = (_ppd_message_t *)cupsArrayFind(a, &key); - -#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) - if (!match && cupsArrayUserData(a)) - { - // - // Try looking the string up in the cups.strings dictionary... - // - - CFDictionaryRef dict; // cups.strings dictionary - CFStringRef cfm, // Message as a CF string - cfstr; // Localized text as a CF string - - dict = (CFDictionaryRef)cupsArrayUserData(a); - cfm = CFStringCreateWithCString(kCFAllocatorDefault, m, - kCFStringEncodingUTF8); - match = calloc(1, sizeof(_ppd_message_t)); - match->msg = strdup(m); - cfstr = cfm ? CFDictionaryGetValue(dict, cfm) : NULL; - - if (cfstr) - { - char buffer[1024]; // Message buffer - - CFStringGetCString(cfstr, buffer, sizeof(buffer), kCFStringEncodingUTF8); - match->str = strdup(buffer); - - DEBUG_printf(("1_ppdMessageLookup: Found \"%s\" as \"%s\"...", - m, buffer)); - } - else - { - match->str = strdup(m); - - DEBUG_printf(("1_ppdMessageLookup: Did not find \"%s\"...", m)); - } - - cupsArrayAdd(a, match); - - if (cfm) - CFRelease(cfm); - } -#endif // __APPLE__ && CUPS_BUNDLEDIR - - if (match && match->str) - return (match->str); - else - return (m); -} - - -// -// '_ppdMessageNew()' - Make a new message catalog array. -// - -cups_array_t * // O - Array -_ppdMessageNew(void *context) // I - User data -{ - return (cupsArrayNew3((cups_array_func_t)ppd_message_compare, context, - (cups_ahash_func_t)NULL, 0, - (cups_acopy_func_t)NULL, - (cups_afree_func_t)ppd_message_free)); -} - - -// -// 'ppd_message_compare()' - Compare two messages. -// - -static int // O - Result of comparison -ppd_message_compare( - _ppd_message_t *m1, // I - First message - _ppd_message_t *m2) // I - Second message -{ - return (strcmp(m1->msg, m2->msg)); -} - - -// -// 'ppd_message_free()' - Free a message. -// - -static void -ppd_message_free(_ppd_message_t *m) // I - Message -{ - if (m->msg) - free(m->msg); - - if (m->str) - free(m->str); - - free(m); -} - - -// -// 'ppd_message_load()' - Load the message catalog for a language. -// - -static void -ppd_message_load(cups_lang_t *lang) // I - Language -{ -#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) - lang->strings = appleMessageLoad(lang->language); - -#else - char filename[1024]; // Filename for language locale file - char localedir[1003];// Filename for language locale dir - char *p; - - // Directory supplied by environment variable CUPS_LOCALEDIR - if ((p = getenv("CUPS_LOCALEDIR")) != NULL) - strncpy (localedir, p, sizeof(localedir) - 1); - else - { - // Determine CUPS datadir (usually /usr/share/cups) - if ((p = getenv("CUPS_DATADIR")) == NULL) - p = CUPS_DATADIR; - snprintf(localedir, sizeof(localedir), "%s/locale", p); - } - - snprintf(filename, sizeof(filename), "%s/%.5s/cups_%.5s.po", localedir, - lang->language, lang->language); - - if (strchr(lang->language, '_') && access(filename, 0)) - { - // - // Country localization not available, look for generic localization... - // - - snprintf(filename, sizeof(filename), "%s/%.2s/cups_%.2s.po", localedir, - lang->language, lang->language); - - if (access(filename, 0)) - { - // - // No generic localization, so use POSIX... - // - - DEBUG_printf(("4ppd_message_load: access(\"%s\", 0): %s", filename, - strerror(errno))); - - snprintf(filename, sizeof(filename), "%s/C/cups_C.po", localedir); - } - } - - // - // Read the strings from the file... - // - - lang->strings = _ppdMessageLoad(filename, _PPD_MESSAGE_UNQUOTE); -#endif // __APPLE__ && CUPS_BUNDLEDIR -} - - -// -// 'ppd_read_strings()' - Read a pair of strings from a .strings file. -// - -static int // O - 1 on success, 0 on failure -ppd_read_strings(cups_file_t *fp, // I - .strings file - int flags, // I - CUPS_MESSAGE_xxx flags - cups_array_t *a) // I - Message catalog array -{ - char buffer[8192], // Line buffer - *bufptr, // Pointer into buffer - *msg, // Pointer to start of message - *str; // Pointer to start of translation string - _ppd_message_t *m; // New message - - - while (cupsFileGets(fp, buffer, sizeof(buffer))) - { - // - // Skip any line (comments, blanks, etc.) that isn't: - // - // "message" = "translation"; - // - - for (bufptr = buffer; *bufptr && isspace(*bufptr & 255); bufptr ++); - - if (*bufptr != '\"') - continue; - - // - // Find the end of the message... - // - - bufptr ++; - for (msg = bufptr; *bufptr && *bufptr != '\"'; bufptr ++) - if (*bufptr == '\\' && bufptr[1]) - bufptr ++; - - if (!*bufptr) - continue; - - *bufptr++ = '\0'; - - if (flags & _PPD_MESSAGE_UNQUOTE) - ppd_unquote(msg, msg); - - // - // Find the start of the translation... - // - - while (*bufptr && isspace(*bufptr & 255)) - bufptr ++; - - if (*bufptr != '=') - continue; - - bufptr ++; - while (*bufptr && isspace(*bufptr & 255)) - bufptr ++; - - if (*bufptr != '\"') - continue; - - // - // Find the end of the translation... - // - - bufptr ++; - for (str = bufptr; *bufptr && *bufptr != '\"'; bufptr ++) - if (*bufptr == '\\' && bufptr[1]) - bufptr ++; - - if (!*bufptr) - continue; - - *bufptr++ = '\0'; - - if (flags & _PPD_MESSAGE_UNQUOTE) - ppd_unquote(str, str); - - // - // If we get this far we have a valid pair of strings, add them... - // - - if ((m = malloc(sizeof(_ppd_message_t))) == NULL) - break; - - m->msg = strdup(msg); - m->str = strdup(str); - - if (m->msg && m->str) - cupsArrayAdd(a, m); - else - { - if (m->msg) - free(m->msg); - - if (m->str) - free(m->str); - - free(m); - break; - } - - return (1); - } - - // - // No more strings... - // - - return (0); -} - - -// -// 'ppd_unquote()' - Unquote characters in strings... -// - -static void -ppd_unquote(char *d, // O - Unquoted string - const char *s) // I - Original string -{ - while (*s) - { - if (*s == '\\') - { - s ++; - if (isdigit(*s)) - { - *d = 0; - - while (isdigit(*s)) - { - *d = *d * 8 + *s - '0'; - s ++; - } - - d ++; - } - else - { - if (*s == 'n') - *d ++ = '\n'; - else if (*s == 'r') - *d ++ = '\r'; - else if (*s == 't') - *d ++ = '\t'; - else - *d++ = *s; - - s ++; - } - } - else - *d++ = *s++; - } - - *d = '\0'; -} diff --git a/ppd/media.defs b/ppd/media.defs deleted file mode 100644 index 179552d62..000000000 --- a/ppd/media.defs +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Adobe standard media size definitions for the CUPS PPD file compiler. - * - * Copyright © 2007-2016 by Apple Inc. - * Copyright © 1997-2005 by Easy Software Products. - * - * Licensed under Apache License v2.0. See the file "LICENSE" for more - * information. - */ - -#media "3x5/3 x 5" 216 360 -#media "3.5x5/3.5 x 5" 252 360 -#media "5x7/5 x 7" 360 504 -#media "10x11/10 x 11" 720 792 -#media "10x13/10 x 13" 720 936 -#media "10x14/10 x 14" 720 1008 -#media "12x11/12 x 11" 864 792 -#media "15x11/15 x 11" 1080 792 -#media "7x9/7 x 9" 504 648 -#media "8x10/8 x 10" 576 720 -#media "9x11/9 x 11" 648 792 -#media "9x12/9 x 12" 648 864 -#media "A0/A0" 2384 3370 -#media "A0.Transverse/A0 Long Edge" 3370 2384 -#media "A1/A1" 1684 2384 -#media "A1.Transverse/A1 Long Edge" 2384 1684 -#media "A2/A2" 1191 1684 -#media "A2.Transverse/A2 Long Edge" 1684 1191 -#media "A3/A3" 842 1191 -#media "A3.Transverse/A3 Long Edge" 1191 842 -#media "A3Extra/A3 Oversize" 913 1262 -#media "A3Extra.Transverse/A3 Oversize Long Edge" 913 1262 -#media "A3Rotated/A3 Long Edge" 1191 842 -#media "A4/A4" 595 842 -#media "A4Extra/A4 Oversize" 667 914 -#media "A4Plus/A4 Oversize" 595 936 -#media "A4Rotated/A4 Long Edge" 842 595 -#media "A4Small/A4 Small" 595 842 -#media "A4.Transverse/A4 Long Edge" 842 595 -#media "A5/A5" 420 595 -#media "A5Extra/A5 Oversize" 492 668 -#media "A5Rotated/A5 Long Edge" 595 420 -#media "A5.Transverse/A5 Long Edge" 595 420 -#media "A6/A6" 297 420 -#media "A6Rotated/A6 Long Edge" 420 297 -#media "A7/A7" 210 297 -#media "A8/A8" 148 210 -#media "A9/A9" 105 148 -#media "A10/A10" 73 105 -#media "AnsiA/ANSI A" 612 792 -#media "AnsiB/ANSI B" 792 1224 -#media "AnsiC/ANSI C" 1224 1584 -#media "AnsiD/ANSI D" 1584 2448 -#media "AnsiE/ANSI E" 2448 3168 -#media "ARCHA/Letter Oversize" 648 864 -#media "ARCHA.Transverse/Letter Oversize Long Edge" 864 648 -#media "ARCHB/Tabloid Oversize" 864 1296 -#media "ARCHB.Transverse/Tabloid Oversize Long Edge" 1296 864 -#media "ARCHC/ARCH C" 1296 1728 -#media "ARCHC.Transverse/ARCH C Long Edge" 1728 1296 -#media "ARCHD/ARCH D" 1728 2592 -#media "ARCHD.Transverse/ARCH D Long Edge" 2592 1728 -#media "ARCHE/ARCH E" 2592 3456 -#media "ARCHE.Transverse/ARCH E Long Edge" 3456 2592 -#media "B0/JIS B0" 2920 4127 -#media "B10/JIS B10" 91 127 -#media "B1/JIS B1" 2064 2918 -#media "B1/JIS B1" 2064 2920 -#media "B2/JIS B2" 1460 2064 -#media "B3/JIS B3" 1032 1460 -#media "B4/JIS B4" 729 1032 -#media "B4Rotated/JIS B4 Long Edge" 1032 729 -#media "B5/JIS B5" 516 729 -#media "B5Rotated/JIS B5 Long Edge" 729 516 -#media "B5.Transverse/JIS B5 Long Edge" 516 729 -#media "B6/JIS B6" 363 516 -#media "B6Rotated/JIS B6 Long Edge" 516 363 -#media "B7/JIS B7" 258 363 -#media "B8/JIS B8" 181 258 -#media "B9/JIS B9" 127 181 -#media "C4/Envelope C4" 649 918 -#media "C5/Envelope C5" 459 649 -#media "C6/Envelope C6" 323 459 -#media "DL/Envelope DL" 312 624 -#media "DoublePostcard/Postcard Double" 567 420 -#media "DoublePostcardRotated/Postcard Double Long Edge" 420 567 -#media "Env10/Envelope #10" 297 684 -#media "Env11/Envelope #11" 324 747 -#media "Env12/Envelope #12" 342 792 -#media "Env14/Envelope #14" 360 828 -#media "Env9/Envelope #9" 279 639 -#media "EnvC0/Envelope C0" 2599 3676 -#media "EnvC1/Envelope C1" 1837 2599 -#media "EnvC2/Envelope C2" 1298 1837 -#media "EnvC3/Envelope C3" 918 1296 -#media "EnvC4/Envelope C4" 649 918 -#media "EnvC5/Envelope C5" 459 649 -#media "EnvC65/Envelope C65" 324 648 -#media "EnvC6/Envelope C6" 323 459 -#media "EnvC7/Envelope C7" 230 323 -#media "EnvChou3/Envelope Choukei 3" 340 666 -#media "EnvChou3Rotated/Envelope Choukei 3 Long Edge" 666 340 -#media "EnvChou4/Envelope Choukei 4" 255 581 -#media "EnvChou4Rotated/Envelope Choukei 4 Long Edge" 581 255 -#media "EnvDL/Envelope DL" 312 624 -#media "EnvInvite/Envelope Invite" 624 624 -#media "EnvISOB4/Envelope B4" 708 1001 -#media "EnvISOB5/Envelope B5" 499 709 -#media "EnvISOB6/Envelope B6" 499 354 -#media "EnvItalian/Envelope Italian" 312 652 -#media "EnvKaku2/Envelope Kaku2" 680 941 -#media "EnvKaku2Rotated/Envelope Kaku2 Long Edge" 941 680 -#media "EnvKaku3/Envelope Kaku3" 612 785 -#media "EnvKaku3Rotated/Envelope Kaku3 Long Edge" 785 612 -#media "EnvMonarch/Envelope Monarch" 279 540 -#media "EnvPersonal/Envelope Personal" 261 468 -#media "EnvPRC1/Envelope PRC1" 289 468 -#media "EnvPRC1Rotated/Envelope PRC1 Long Edge" 468 289 -#media "EnvPRC2/Envelope PRC2" 289 499 -#media "EnvPRC2Rotated/Envelope PRC2 Long Edge" 499 289 -#media "EnvPRC3/Envelope PRC3" 354 499 -#media "EnvPRC3Rotated/Envelope PRC3 Long Edge" 499 354 -#media "EnvPRC4/Envelope PRC4" 312 590 -#media "EnvPRC4Rotated/Envelope PRC4 Long Edge" 590 312 -#media "EnvPRC5/Envelope PRC5PRC5" 312 624 -#media "EnvPRC5Rotated/Envelope PRC5 Long Edge" 624 312 -#media "EnvPRC6/Envelope PRC6" 340 652 -#media "EnvPRC6Rotated/Envelope PRC6 Long Edge" 652 340 -#media "EnvPRC7/Envelope PRC7" 454 652 -#media "EnvPRC7Rotated/Envelope PRC7 Long Edge" 652 454 -#media "EnvPRC8/Envelope PRC8" 340 876 -#media "EnvPRC8Rotated/Envelope PRC8 Long Edge" 876 340 -#media "EnvPRC9/Envelope PRC9" 649 918 -#media "EnvPRC9Rotated/Envelope PRC9 Long Edge" 918 649 -#media "EnvPRC10/Envelope PRC10" 918 1298 -#media "EnvPRC10Rotated/Envelope PRC10 Long Edge" 1298 918 -#media "EnvYou4/Envelope You4" 298 666 -#media "EnvYou4Rotated/Envelope You4 Long Edge" 666 298 -#media "Executive/Executive" 522 756 -#media "FanFoldGerman/European Fanfold" 612 864 -#media "FanFoldGermanLegal/European Fanfold Legal" 612 936 -#media "FanFoldUS/US Fanfold" 1071 792 -#media "Folio/Folio" 595 935 -#media "ISOB0/B0" 2835 4008 -#media "ISOB1/B1" 2004 2835 -#media "ISOB2/B2" 1417 2004 -#media "ISOB3/B3" 1001 1417 -#media "ISOB4/B4" 709 1001 -#media "ISOB5/B5" 499 709 -#media "ISOB5Extra/B5 Oversize" 570 782 -#media "ISOB6/B6" 354 499 -#media "ISOB7/B7" 249 354 -#media "ISOB8/B8" 176 249 -#media "ISOB9/B9" 125 176 -#media "ISOB10/B10" 88 125 -#media "Ledger/US Ledger" 1224 792 -#media "Legal/US Legal" 612 1008 -#media "LegalExtra/US Legal Oversize" 684 1080 -#media "Letter/US Letter" 612 792 -#media "Letter.Transverse/US Letter Long Edge" 792 612 -#media "LetterExtra/US Letter Oversize" 684 864 -#media "LetterExtra.Transverse/US Letter Oversize Long Edge" 864 684 -#media "LetterPlus/US Letter Oversize" 612 914 -#media "LetterRotated/US Letter Long Edge" 792 612 -#media "LetterSmall/US Letter Small" 612 792 -#media "Monarch/Envelope Monarch" 279 540 -#media "Note/Note" 612 792 -#media "Postcard/Postcard" 284 419 -#media "PostcardRotated/Postcard Long Edge" 419 284 -#media "PRC16K/PRC16K" 414 610 -#media "PRC16KRotated/PRC16K Long Edge" 610 414 -#media "PRC32K/PRC32K" 275 428 -#media "PRC32KBig/PRC32K Oversize" 275 428 -#media "PRC32KBigRotated/PRC32K Oversize Long Edge" 428 275 -#media "PRC32KRotated/PRC32K Long Edge" 428 275 -#media "Quarto/Quarto" 610 780 -#media "Statement/Statement" 396 612 -#media "SuperA/Super A" 643 1009 -#media "SuperB/Super B" 864 1380 -#media "Tabloid/Tabloid" 792 1224 -#media "TabloidExtra/Tabloid Oversize" 864 1296 - -/* - * Non-standard sizes... - */ - -#media "Photo4x6/Photo" 288 432 -#media "PhotoLabel/Photo Labels" 288 468 -#media "w936h1368/Super B/A3" 936 1368 -#media "w81h252/Address" 81 252 -#media "w101h252/Large Address" 101 252 -#media "w54h144/Return Address" 54 144 -#media "w167h288/Shipping Address" 167 288 -#media "w162h540/Internet Postage 2-Part" 162 540 -#media "w162h504/Internet Postage 3-Part" 162 504 -#media "w41h248/File Folder" 41 248 -#media "w41h144/Hanging Folder" 41 144 -#media "w153h198/3.5\" Disk" 153 198 diff --git a/ppd/pdftops.c b/ppd/pdftops.c deleted file mode 100644 index 137d7a026..000000000 --- a/ppd/pdftops.c +++ /dev/null @@ -1,1867 +0,0 @@ -// -// PDF-to-PostScript filter function for libppd. -// -// Copyright 2011-2020 by Till Kamppeter -// Copyright 2007-2011 by Apple Inc. -// Copyright 1997-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// parsePDFTOPDFComment() - Check whether we are executed after pdftopdf -// remove_options() - Remove unwished entries from an option list -// log_command_line() - Log the command line of a program which we call -// ppdFilterPDFToPS() - pdftops filter function -// - -// -// Include necessary headers... -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ppd.h" -#include "ppd-filter.h" - -#define MAX_CHECK_COMMENT_LINES 20 - -// -// Type definitions -// - -typedef unsigned renderer_t; -enum renderer_e -{ - GS = 0, - PDFTOPS = 1, - ACROREAD = 2, - PDFTOCAIRO = 3, - MUPDF = 4, - HYBRID = 5 -}; - - -// -// When calling the "pstops" filter we exclude the following options from its -// command line as we have applied these options already to the PDF input, -// either on the "pdftops"/Ghostscript call in this filter or by use of the -// "pdftopdf" filter before this filter. -// - -const char *pstops_exclude_general[] = -{ - "crop-to-fit", - "fill", - "fitplot", - "fit-to-page", - "landscape", - "orientation-requested", - NULL -}; - -const char *pstops_exclude_page_management[] = -{ - "brightness", - "Collate", - "even-duplex", - "gamma", - "hue", - "ipp-attribute-fidelity", - "MirrorPrint", - "mirror", - "multiple-document-handling", - "natural-scaling", - "number-up", - "number-up-layout", - "OutputOrder", - "page-border", - "page-bottom", - "page-label", - "page-left", - "input-page-ranges", - "page-ranges", - "page-right", - "page-set", - "page-top", - "position", - "saturation", - "scaling", - NULL -}; - -// -// Check whether we were called after the "pdftopdf" filter and extract -// parameters passed over by "pdftopdf" in the header comments of the PDF -// file -// - -static void -parse_pdftopdf_comment(char *filename, // I - Input file - int *pdftopdfapplied, // O - Does the input - // data come from - // pdftopdf filter? - char *deviceCopies, // O - Number of copies - // (hardware) - int *deviceCollate, // O - Hardware collate - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for - // log function -{ - char buf[4096]; - int i; - FILE *fp; - - if ((fp = fopen(filename, "rb")) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Cannot open input file \"%s\"", - filename); - return; - } - - // skip until PDF start header - while (fgets(buf, sizeof(buf), fp) != 0) - { - if (strncmp(buf, "%PDF", 4) == 0) - break; - } - for (i = 0; i < MAX_CHECK_COMMENT_LINES; i ++) - { - if (fgets(buf, sizeof(buf), fp) == 0) - break; - if (strncmp(buf, "%%PDFTOPDFNumCopies", 19) == 0) - { - char *p; - - p = strchr(buf + 19, ':') + 1; - while (*p == ' ' || *p == '\t') - p ++; - strncpy(deviceCopies, p, 31); - deviceCopies[31] = '\0'; - p = deviceCopies + strlen(deviceCopies) - 1; - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') - p --; - *(p + 1) = '\0'; - *pdftopdfapplied = 1; - } - else if (strncmp(buf, "%%PDFTOPDFCollate", 17) == 0) - { - char *p; - - p = strchr(buf + 17, ':') + 1; - while (*p == ' ' || *p == '\t') - p ++; - if (strncasecmp(p, "true", 4) == 0) - *deviceCollate = 1; - else - *deviceCollate = 0; - *pdftopdfapplied = 1; - } - else if (strcmp(buf, "% This file was generated by pdftopdf") == 0) - *pdftopdfapplied = 1; - } - - fclose(fp); -} - - -// -// Check whether given file is empty -// - -static int // O - Result: 1: Empty; 0: Contains pages -is_empty(char *filename, // I - Input file - cf_logfunc_t log, // I - Log function - void *ld) // I - Auxiliary data for log function -{ - FILE *fp = NULL; - fp = fopen(filename, "rb"); - if (fp == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Cannot open input file \"%s\"", - filename); - return (1); - } - else - { - char buf[1]; - rewind(fp); - if (fread(buf, 1, 1, fp) == 0) - { - fclose(fp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Input is empty, outputting empty file."); - return (1); - } - fclose(fp); - int pages = cfPDFPages(filename); - if (pages == 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: No pages left, outputting empty file."); - return (1); - } - if (pages > 0) - return (0); - return (1); - } -} - - -// -// Before calling any command line utility, log its command line in CUPS' -// debug mode -// - -void -log_command_line(const char* file, // I - Program to be executed - char *const argv[], // I - Argument list - cf_logfunc_t log, // I - Log function - void *ld) // I - Auxiliary data for log function -{ - int i; - char *apos; - char buf[32768]; - - if (log == NULL) - return; - - // Debug output: Full command line of program to be called - snprintf(buf, sizeof(buf) - 1, - "ppdFilterPDFToPS: Running command line for %s:", - (file ? file : argv[0])); - if (file) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1, - " %s", file); - for (i = (file ? 1 : 0); argv[i]; i ++) - { - if ((strchr(argv[i],' ')) || (strchr(argv[i],'\t'))) - apos = "'"; - else - apos = ""; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1, - " %s%s%s", apos, argv[i], apos); - } - buf[sizeof(buf) - 1] = '\0'; - - log(ld, CF_LOGLEVEL_DEBUG, "%s", buf); -} - - -// -// 'ppdFilterPDFToPS()' - Filter function to convert PDF input into -// PostScript to be printed on PostScript printers -// - -int // O - Error status -ppdFilterPDFToPS(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data,// I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataGetExt(data, - PPD_FILTER_DATA_EXT); - renderer_t renderer = CUPS_PDFTOPS_RENDERER; // Renderer: gs or pdftops - // or acroread or pdftocairo - // or hybrid - FILE *inputfp; // Input file pointer - int fd = 0; // Copy file descriptor - int i, j; - int pdftopdfapplied = 0; // Input data from pdftopdf filter? - char deviceCopies[32] = "1"; // Hardware copies - int deviceCollate = 0; // Hardware collate - char make_model[128] = ""; // Printer make and model (for quirks) - char *filename, // PDF file to convert - tempfile[1024]; // Temporary file - char buffer[8192]; // Copy buffer - int bytes; // Bytes copied - int num_options = 0, // Number of options - num_pstops_options; // Number of options for pstops - cups_option_t *options = NULL, // Options - *pstops_options, // Options for pstops filter function - *option; - const char *exclude; - cf_filter_data_t pstops_filter_data; - int ret; - const char *val; // Option value - ppd_file_t *ppd = NULL; // PPD file - char resolution[128] = ""; // Output resolution - int xres = 0, yres = 0, // resolution values - mres, res, - maxres = CUPS_PDFTOPS_MAX_RESOLUTION, - // Maximum image rendering resolution - numvalues = 0; // Number of values actually read - ppd_choice_t *choice; - ppd_attr_t *attr; - cups_page_header2_t header; - cups_file_t *fp; // Post-processing input file - int pdf_pid, // Process ID for pdftops/gs - pdf_argc = 0, // Number of args for pdftops/gs - pstops_pid = 0, // Process ID of pstops filter - pstops_pipe[2], // Pipe to pstops filter - need_post_proc = 0, // Post-processing needed? - post_proc_pid = 0, // Process ID of post-processing - post_proc_pipe[2], // Pipe to post-processing - wait_children, // Number of child processes left - wait_pid, // Process ID from wait() - wait_status, // Status from child - exit_status = 0; // Exit status - int gray_output = 0; // Checking for monochrome/grayscale - // PostScript output - char *pdf_argv[100], // Arguments for pdftops/gs - *ptr; // Pointer into value - int duplex, tumble; // Duplex settings for PPD-less - // printing - cups_cspace_t cspace = (cups_cspace_t)(-1); - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *ipp; - - - (void)inputseekable; - (void)parameters; - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Open the input data stream specified by the inputfd... - // - - if ((inputfp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Unable to open input data stream."); - } - - return (1); - } - - // - // Copy input into temporary file ... - // - - if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to copy PDF file: %s", strerror(errno)); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) - bytes = write(fd, buffer, bytes); - - if (inputfd) - { - fclose(inputfp); - close(inputfd); - } - close(fd); - - filename = tempfile; - - // - // Stop on empty or zero-pages files without error, perhaps we eliminated - // all pages via the "page-ranges" option and a previous filter - // - - if (is_empty(filename, log, ld)) - { - unlink(tempfile); - return 0; - } - - // - // Read out copy counts and collate setting passed over by pdftopdf - // - - parse_pdftopdf_comment(filename, &pdftopdfapplied, deviceCopies, - &deviceCollate, log, ld); - - // - // CUPS option list - // - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - - if (filter_data_ext) - ppd = filter_data_ext->ppd; - - // - // Process job options... - // - - if ((val = cupsGetOption("make-and-model", num_options, options)) != NULL) - { - strncpy(make_model, val, sizeof(make_model) - 1); - if (strlen(val) > 127) - make_model[127] = '\0'; - for (ptr = make_model; *ptr; ptr ++) - if (*ptr == '-') *ptr = ' '; - } - else if(printer_attrs) - { - if ((ipp = ippFindAttribute(printer_attrs, "printer-make-and-model", - IPP_TAG_ZERO)) != NULL) - { - char make[56]; - char* model; - ippAttributeString(ipp, make, sizeof(make)); - if (!strncasecmp(make, "Hewlett Packard ", 16) || - !strncasecmp(make, "Hewlett-Packard ", 16)) - { - model = make + 16; - strncpy(make, "HP", sizeof(make)); - } - else if ((model = strchr(make, ' ')) != NULL) - *model++ = '\0'; - else - model = make; - snprintf(make_model, sizeof(make_model), "%s %s", make, model); - } - } - else if (ppd) - { - snprintf(make_model, sizeof(make_model), "%s %s", ppd->manufacturer, - ppd->product + 1); - make_model[strlen(make_model) - 1] = '\0'; - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Printer make and model: %s", make_model); - - // - // Select the PDF renderer: Ghostscript (gs), Poppler (pdftops), - // Adobe Reader (arcoread), Poppler with Cairo (pdftocairo), or - // Hybrid (hybrid, Poppler for Brother, Minolta, Konica Minolta, Dell, and - // old HP LaserJets and Ghostscript otherwise) - // - - if ((val = cupsGetOption("pdftops-renderer", num_options, options)) != NULL) - { - if (strcasecmp(val, "gs") == 0) - renderer = GS; - else if (strcasecmp(val, "pdftops") == 0) - renderer = PDFTOPS; - else if (strcasecmp(val, "acroread") == 0) - renderer = ACROREAD; - else if (strcasecmp(val, "pdftocairo") == 0) - renderer = PDFTOCAIRO; - else if (strcasecmp(val, "mupdf") == 0) - renderer = MUPDF; - else if (strcasecmp(val, "hybrid") == 0) - renderer = HYBRID; - else - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Invalid value for \"pdftops-renderer\": \"%s\"", - val); - } - - if (renderer == HYBRID) - { - if (make_model[0] && - (!strncasecmp(make_model, "Brother", 7) || - !strncasecmp(make_model, "Dell", 4) || - strcasestr(make_model, "Minolta") || - (!strncasecmp(make_model, "Apple", 5) && - (ptr = strcasestr(make_model, "LaserWriter"))))) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Switching to Poppler's pdftops instead of " - "Ghostscript for Brother, Minolta, Konica Minolta, Dell, " - "and Apple LaserWriter printers to work around bugs in the " - "printer's PS interpreters"); - renderer = PDFTOPS; - } - else - renderer = GS; - - // - // Use Poppler instead of Ghostscript for old HP LaserJet printers due to - // a bug in their PS interpreters. They are very slow with Ghostscript. - // A LaserJet is considered old if its model number does not have a letter - // in the beginning, like LaserJet 3 or LaserJet 4000, not LaserJet P2015. - // See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=742765 - // - - if (make_model[0] && - ((!strncasecmp(make_model, "HP", 2) || - !strncasecmp(make_model, "Hewlett-Packard", 15) || - !strncasecmp(make_model, "Hewlett Packard", 15)) && - (ptr = strcasestr(make_model, "LaserJet")))) - { - for (ptr += 8; *ptr; ptr ++) - { - if (isspace(*ptr)) continue; - if (isdigit(*ptr)) - { - while (*ptr && isalnum(*ptr)) - ptr ++; - if (!*ptr) // End of string, no further word - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Switching to Poppler's pdftops instead of " - "Ghostscript for old HP LaserJet (\"LaserJet " - "\", no letters before , no " - "additional words after ) printers to " - "work around bugs in the printer's PS interpreters"); - renderer = PDFTOPS; - } - } - break; - } - } - } - - // - // Create option list for pstops filter function, stripping options which - // "pstops" does not need to apply any more - // - - num_pstops_options = 0; - pstops_options = NULL; - for (i = num_options, option = options; i > 0; i --, option ++) - { - for (j = 0, exclude = pstops_exclude_general[j]; exclude; - j ++, exclude = pstops_exclude_general[j]) - if (!strcasecmp(option->name, exclude)) - break; - if (exclude) - continue; - if (pdftopdfapplied) - { - for (j = 0, exclude = pstops_exclude_page_management[j]; exclude; - j++, exclude = pstops_exclude_page_management[j]) - if (!strcasecmp(option->name, exclude)) - break; - if (exclude) - continue; - } - num_pstops_options = cupsAddOption(option->name, - option->value, - num_pstops_options, &pstops_options); - } - - if (pdftopdfapplied && deviceCollate) - { - // - // Add collate option to the pstops call if pdftopdf has found out that the - // printer does hardware collate. - // - - num_pstops_options = cupsAddOption("Collate", - "True", - num_pstops_options, &pstops_options); - } - - // - // Create data record to call the pstops filter function - // - - pstops_filter_data.job_id = data->job_id; - pstops_filter_data.job_user = data->job_user; - pstops_filter_data.job_title = data->job_title; - if (pdftopdfapplied) - pstops_filter_data.copies = atoi(deviceCopies); - else - pstops_filter_data.copies = data->copies; - pstops_filter_data.job_attrs = NULL; - pstops_filter_data.printer_attrs = NULL; - pstops_filter_data.num_options = num_pstops_options; - pstops_filter_data.options = pstops_options; - pstops_filter_data.extension = data->extension; - pstops_filter_data.logfunc = log; - pstops_filter_data.logdata = ld; - pstops_filter_data.iscanceledfunc = iscanceled; - pstops_filter_data.iscanceleddata = icd; - - // - // Force monochrome/grayscale PostScript output - // if job is to be printed in monochrome/grayscale - // - - if (ppd && ppd->color_device == 0) // Monochrome printer - gray_output = 1; - else //Color Printer - user option for Grayscale - { - if ((val = cupsGetOption("pwg-raster-document-type", num_options, - options)) != NULL || - (val = cupsGetOption("PwgRasterDocumentType", num_options, - options)) != NULL || - (val = cupsGetOption("print-color-mode", num_options, - options)) != NULL || - (val = cupsGetOption("PrintColorMode", num_options, - options)) != NULL || - (val = cupsGetOption("color-space", num_options, - options)) != NULL || - (val = cupsGetOption("ColorSpace", num_options, - options)) != NULL || - (val = cupsGetOption("color-model", num_options, - options)) != NULL || - (val = cupsGetOption("ColorModel", num_options, - options)) != NULL || - (val = cupsGetOption("output-mode", num_options, - options)) != NULL || - (val = cupsGetOption("OutputMode", num_options, - options)) != NULL) - { - if (strcasestr(val, "Black") || - strcasestr(val, "Gray") || - strcasestr(val, "Mono")) - gray_output = 1; - } - else - { - if(job_attrs != NULL) - { - if ((ipp = ippFindAttribute(job_attrs, "pwg-raster-document-type", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(job_attrs, "color-space", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(job_attrs, "color-model", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(job_attrs, "print-color-mode", - IPP_TAG_ZERO)) != NULL || - (ipp = ippFindAttribute(job_attrs, "output-mode", - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp, buffer, sizeof(buffer)); - val = buffer; - if(strcasestr(val, "Black") || - (strcasestr(val, "Gray")) || - (strcasestr(val, "Mono"))) - gray_output = 1; - } - } - } - } - - // - // Build the command-line for the ppdFilterPDFToPS, gs, mutool, pdftocairo, or - // acroread filter... - // - - if (renderer == PDFTOPS) - { - pdf_argv[0] = (char *)"pdftops"; - pdf_argc = 1; - } - else if (renderer == GS) - { - pdf_argv[0] = (char *)"gs"; - pdf_argv[1] = (char *)"-q"; - pdf_argv[2] = (char *)"-dNOPAUSE"; - pdf_argv[3] = (char *)"-dBATCH"; - pdf_argv[4] = (char *)"-dSAFER"; - pdf_argv[5] = (char *)"-dNOMEDIAATTRS"; - pdf_argv[6] = (char *)"-sstdout=%stderr"; -# ifdef HAVE_GHOSTSCRIPT_PS2WRITE - pdf_argv[7] = (char *)"-sDEVICE=ps2write"; -# else - pdf_argv[7] = (char *)"-sDEVICE=pswrite"; -# endif // HAVE_GHOSTSCRIPT_PS2WRITE - pdf_argv[8] = (char *)"-dShowAcroForm"; - pdf_argv[9] = (char *)"-sOUTPUTFILE=%stdout"; - if (gray_output == 1) // Checking for monochrome/grayscale PostScript - // output - { - pdf_argv[10] = (char *)"-sProcessColorModel=DeviceGray"; - pdf_argv[11] = (char *)"-sColorConversionStrategy=Gray"; - pdf_argc = 12; - } - else - pdf_argc = 10; - } - else if (renderer == MUPDF) - { - pdf_argv[0] = (char *)"mutool"; - pdf_argv[1] = (char *)"draw"; - pdf_argv[2] = (char *)"-L"; - pdf_argv[3] = (char *)"-smtf"; - pdf_argv[4] = (char *)"-Fps"; - pdf_argv[5] = (char *)"-o-"; - if (gray_output == 1) // Checking for monochrome/grayscale PostScript - // output - pdf_argv[6] = (char *)"-cgray"; - else - pdf_argv[6] = (char *)"-crgb"; - pdf_argc = 7; - } - else if (renderer == PDFTOCAIRO) - { - pdf_argv[0] = (char *)"pdftocairo"; - pdf_argv[1] = (char *)"-ps"; - pdf_argc = 2; - } - else if (renderer == ACROREAD) - { - pdf_argv[0] = (char *)"acroread"; - pdf_argv[1] = (char *)"-toPostScript"; - pdf_argc = 2; - } - - // - // Set language level and TrueType font handling... - // - - if (ppd) - { - if (ppd->language_level == 1) - { - if (renderer == PDFTOPS) - { - pdf_argv[pdf_argc++] = (char *)"-level1"; - pdf_argv[pdf_argc++] = (char *)"-noembtt"; - } - else if (renderer == GS) - pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=1"; - else if (renderer == PDFTOCAIRO) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Level 1 PostScript not supported by " - "pdftocairo."); - } - else if (renderer == ACROREAD) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Level 1 PostScript not supported by acroread."); - } - else if (renderer == MUPDF) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Level 1 PostScript not supported by mutool."); - } - } - else if (ppd->language_level == 2) - { - if (renderer == PDFTOPS) - { - pdf_argv[pdf_argc++] = (char *)"-level2"; - if (!ppd->ttrasterizer) - pdf_argv[pdf_argc++] = (char *)"-noembtt"; - } - else if (renderer == GS) - pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2"; - else if (renderer != MUPDF) // MuPDF is PS level 2 only - // PDFTOCAIRO, ACROREAD - pdf_argv[pdf_argc++] = (char *)"-level2"; - } - else - { - if (renderer == PDFTOPS) - { - // Do not emit PS Level 3 with Poppler on Brother and HP PostScript - // laser printers as some do not like it. - // See https://bugs.launchpad.net/bugs/277404 and - // https://bugs.launchpad.net/bugs/1306849 comment #42. - if (!make_model[0] || - !strncasecmp(make_model, "Brother", 7) || - ((!strncasecmp(make_model, "HP", 2) || - !strncasecmp(make_model, "Hewlett-Packard", 15) || - !strncasecmp(make_model, "Hewlett Packard", 15)) && - (strcasestr(make_model, "LaserJet")))) - pdf_argv[pdf_argc++] = (char *)"-level2"; - else - pdf_argv[pdf_argc++] = (char *)"-level3"; - } - else if (renderer == GS) - { - // Do not emit PS Level 3 with Ghostscript on Brother PostScript - // laser printers as some do not like it. - // See https://bugs.launchpad.net/bugs/1306849 comment #42. - if (!make_model[0] || - !strncasecmp(make_model, "Brother", 7)) - pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2"; - else - pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3"; - } - else if (renderer == MUPDF) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Level 3 PostScript not supported by mutool."); - } - else // PDFTOCAIRO || ACROREAD - pdf_argv[pdf_argc++] = (char *)"-level3"; - } - } - else - { - if (renderer == PDFTOPS) - { - // Do not emit PS Level 3 with Poppler on HP PostScript laser printers - // as some do not like it. See https://bugs.launchpad.net/bugs/277404. - if (!make_model[0] || - ((!strncasecmp(make_model, "HP", 2) || - !strncasecmp(make_model, "Hewlett-Packard", 15) || - !strncasecmp(make_model, "Hewlett Packard", 15)) && - (strcasestr(make_model, "LaserJet")))) - pdf_argv[pdf_argc++] = (char *)"-level2"; - else - pdf_argv[pdf_argc++] = (char *)"-level3"; - pdf_argv[pdf_argc++] = (char *)"-noembtt"; - } - else if (renderer == GS) - pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3"; - else if (renderer != MUPDF) // MuPDF is PS level 2 only - // PDFTOCAIRO || ACROREAD - pdf_argv[pdf_argc++] = (char *)"-level3"; - } - -#ifdef HAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES - if ((renderer == PDFTOPS) || (renderer == PDFTOCAIRO)) - { - // - // Use the page sizes of the original PDF document, this way documents - // which contain pages of different sizes can be printed correctly - // - - pdf_argv[pdf_argc++] = (char *)"-origpagesizes"; - pdf_argv[pdf_argc++] = (char *)"-nocenter"; - } - else -#endif // HAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES - if (renderer == ACROREAD) - { - // - // Use the page sizes of the original PDF document, this way documents - // which contain pages of different sizes can be printed correctly - // - - pdf_argv[pdf_argc++] = (char *)"-choosePaperByPDFPageSize"; - } - - // - // Set output resolution ... - // - - if (ppd) - { - // Ignore error exits of ppdRasterInterpretPPD(), if it found a resolution - // setting before erroring it is OK for us - ppdRasterInterpretPPD(&header, ppd, num_options, options, NULL); - // 100 dpi is default, this means that if we have 100 dpi here this - // method failed to find the printing resolution - resolution[0] = '\0'; - if (header.HWResolution[0] != 100 || header.HWResolution[1] != 100) - { - xres = header.HWResolution[0]; - yres = header.HWResolution[1]; - } - else if ((choice = ppdFindMarkedChoice(ppd, "Resolution")) != NULL) - strncpy(resolution, choice->choice, sizeof(resolution) - 1); - else if ((attr = ppdFindAttr(ppd,"DefaultResolution",NULL)) != NULL) - strncpy(resolution, attr->value, sizeof(resolution) - 1); - resolution[sizeof(resolution) - 1] = '\0'; - if (((xres == 100 && yres == 100) || xres <= 0 || yres <= 0)) - { - if (resolution[0] && - (numvalues = sscanf(resolution, "%dx%d", &xres, &yres)) == 1) - yres = xres; - if (numvalues <= 0 && log) - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: No resolution information found in the PPD file."); - } - } - else - { - cfRasterPrepareHeader(&header, data, CF_FILTER_OUT_FORMAT_CUPS_RASTER, - CF_FILTER_OUT_FORMAT_CUPS_RASTER, 0, &cspace); - if (header.HWResolution[0] > 100 && header.HWResolution[1] > 100) - { - xres = header.HWResolution[0]; - yres = header.HWResolution[1]; - } - else if ((ipp = ippFindAttribute(printer_attrs, - "printer-resolution-default", - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp, buffer, sizeof(buffer)); - const char *p = buffer; - xres = atoi(p); - if ((p = strchr(p, 'x')) != NULL) - yres = atoi(p + 1); - else - yres = xres; - } - else if ((ipp = ippFindAttribute(printer_attrs, - "printer-resolution-supported", - IPP_TAG_ZERO))!=NULL){ - ippAttributeString(ipp, buffer, sizeof(buffer)); - for (i = 0; buffer[i] != '\0'; i ++) - { - if(buffer[i] == ' ' || - buffer[i] == ',') - { - buffer[i] = '\0'; - break; - } - } - const char *p = buffer; - xres = atoi(p); - if((p = strchr(p, 'x')) != NULL) - yres = atoi(p + 1); - else - yres = xres; - } - } - if ((xres == 0) && (yres == 0)) - { - if ((val = cupsGetOption("printer-resolution", num_options, - options)) != NULL || - (val = cupsGetOption("Resolution", num_options, options)) != NULL) - { - xres = yres = strtol(val, (char **)&ptr, 10); - if (ptr > val && xres > 0) - { - if (*ptr == 'x') - yres = strtol(ptr + 1, (char **)&ptr, 10); - } - - if (ptr <= val || xres <= 0 || yres <= 0 || !ptr || - (*ptr != '\0' && - strcasecmp(ptr, "dpi") && - strcasecmp(ptr, "dpc") && - strcasecmp(ptr, "dpcm"))) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Bad resolution value \"%s\".", val); - } - else - { - if (!strcasecmp(ptr, "dpc") || - !strcasecmp(ptr, "dpcm")) - { - xres = xres * 254 / 100; - yres = yres * 254 / 100; - } - } - } - } - if ((xres > 0) || (yres > 0)) - { - if (yres == 0) yres = xres; - if (xres == 0) xres = yres; - if (xres > yres) - res = yres; - else - res = xres; - } - else - res = 300; - - // - // Get the ceiling for the image rendering resolution - // - - if ((val = cupsGetOption("pdftops-max-image-resolution", - num_options, options)) != NULL) - { - if ((numvalues = sscanf(val, "%d", &mres)) > 0) - maxres = mres; - else - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Invalid value for " - "\"pdftops-max-image-resolution\": \"%s\"", - val); - } - - // - // Reduce the image rendering resolution to not exceed a given maximum - // to make processing of jobs by the PDF->PS converter and the printer faster - // - // maxres = 0 means no limit - // - - if (maxres) - while (res > maxres) - res = res / 2; - - if ((renderer == PDFTOPS) || (renderer == PDFTOCAIRO)) - { -#ifdef HAVE_POPPLER_PDFTOPS_WITH_RESOLUTION - // - // Set resolution to avoid slow processing by the printer when the - // resolution of embedded images does not match the printer's resolution - // - - pdf_argv[pdf_argc++] = (char *)"-r"; - snprintf(resolution, sizeof(resolution), "%d", res); - pdf_argv[pdf_argc++] = resolution; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Using image rendering resolution %d dpi", - res); -#endif // HAVE_POPPLER_PDFTOPS_WITH_RESOLUTION - if (gray_output == 1) // Checking for monochrome/grayscale PostScript - // output - { - // - // Poppler does not explicitly support turning colored PDF files into - // grayscale PostScript. As a workaround, one could let the "pdftops" - // command line utility generate PostScript level 1 output which is - // always grayscale, but output files get huge and printing too - // slow. - // Recommended solution is to not use Poppler as PDF renderer for - // printing, especially if one uses a color PostScript printer and - // wants to have the possibility to print jobs also in grayscale. - // See the use of the "pdftops-renderer" option in the README file. - // Example code for PostScript level1 workaround: - // pdf_argv[1] = (char *)"-level1"; - // pdf_argv[pdf_argc++] = (char *)"-optimizecolorspace"; - // - - // Issue a warning message when printing a grayscale job with Poppler - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Grayscale/monochrome printing requested for this " - "job but Poppler is not able to convert to " - "grayscale/monochrome PostScript."); - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterPDFToPS: Use \"pdftops-renderer\" option (see " - "cups-filters README file) to use Ghostscript or MuPDF for " - "the PDF -> PostScript conversion."); - } - pdf_argv[pdf_argc++] = filename; - pdf_argv[pdf_argc++] = (char *)"-"; - } - else if (renderer == GS) - { - // - // Set resolution to avoid slow processing by the printer when the - // resolution of embedded images does not match the printer's resolution - // - - snprintf(resolution, 127, "-r%d", res); - pdf_argv[pdf_argc++] = resolution; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Using image rendering resolution %d dpi", - res); - // - // PostScript debug mode: If you send a job with "lpr -o psdebug" - // Ghostscript will not compress the pages, so that the PostScript - // code can get analysed. This is especially important if a - // PostScript printer errors or misbehaves on Ghostscript's - // output. On Kyocera and Utax (uses Kyocera hard- and software) - // printers we always suppress page compression, to avoid slow - // processing of raster images. - // - - val = cupsGetOption("psdebug", num_options, options); - if ((val && strcasecmp(val, "no") && strcasecmp(val, "off") && - strcasecmp(val, "false")) || - (make_model[0] && - (!strncasecmp(make_model, "Kyocera", 7) || - !strncasecmp(make_model, "Utax", 4)))) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Deactivated compression of pages in " - "Ghostscript's PostScript output (\"psdebug\" debug mode " - "or Kyocera/Utax printer)"); - pdf_argv[pdf_argc++] = (char *)"-dCompressPages=false"; - } - - // - // The PostScript interpreters on many printers have bugs which make - // the interpreter crash, error out, or otherwise misbehave on too - // heavily compressed input files, especially if code with compressed - // elements is compressed again. Therefore we reduce compression here. - // - - pdf_argv[pdf_argc++] = (char *)"-dCompressFonts=false"; - pdf_argv[pdf_argc++] = (char *)"-dNoT3CCITT"; - if (make_model[0] && - !strncasecmp(make_model, "Brother", 7)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Deactivation of Ghostscript's image compression " - "for Brother printers to workarounmd PS interpreter bug"); - pdf_argv[pdf_argc++] = (char *)"-dEncodeMonoImages=false"; - pdf_argv[pdf_argc++] = (char *)"-dEncodeColorImages=false"; - } - - // - // Toshiba's PS interpreters have an issue with how we handle - // TrueType/Type42 fonts, therefore we add command line options to turn - // the TTF outlines into bitmaps, usually Type 3 PostScript fonts, only - // large glyphs into normal image data. - // See https://bugs.launchpad.net/bugs/978120 - // - - if (make_model[0] && - !strncasecmp(make_model, "Toshiba", 7)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: To work around a bug in Toshiba's PS " - "interpreters turn TTF font glyphs into bitmaps, usually " - "Type 3 PS fonts, or images for large characters"); - pdf_argv[pdf_argc++] = (char *)"-dHaveTrueTypes=false"; - } - pdf_argv[pdf_argc++] = (char *)"-dNOINTERPOLATE"; - pdf_argv[pdf_argc++] = (char *)"-c"; - if (make_model[0] && - !strncasecmp(make_model, "Toshiba", 7)) - pdf_argv[pdf_argc++] = (char *)"<< /MaxFontItem 500000 >> setuserparams"; - pdf_argv[pdf_argc++] = (char *)"save pop"; - pdf_argv[pdf_argc++] = (char *)"-f"; - pdf_argv[pdf_argc++] = filename; - } - else if (renderer == MUPDF) - { - // - // Add Resolution option to avoid slow processing by the printer when the - // resolution of embedded images does not match the printer's resolution - // - - snprintf(resolution, 127, "-r%dx%d", res, res); - pdf_argv[pdf_argc++] = resolution; - - // - // Add input file name - // - - pdf_argv[pdf_argc++] = filename; - } - - pdf_argv[pdf_argc] = NULL; - - log_command_line(NULL, pdf_argv, log, ld); - - // - // Do we need post-processing of the PostScript output to work around bugs - // of the printer's PostScript interpreter? - // - - if ((renderer == PDFTOPS) || (renderer == PDFTOCAIRO) || - (renderer == MUPDF)) - need_post_proc = 0; - else if (renderer == GS) - need_post_proc = - (make_model[0] && - (!strncasecmp(make_model, "Kyocera", 7) || - !strncasecmp(make_model, "Utax", 4) || - !strncasecmp(make_model, "Brother", 7)) ? 1 : 0); - else - need_post_proc = 1; - - // - // Do we need post-processing of the PostScript output to apply option - // settings when doing PPD-less printing? - // - - if (!ppd) - need_post_proc = 1; - - // - // Execute "pdftops/gs/mutool [ | PS post-processing ] [ | pstops ]"... - // - - - // - // Create a pipe for each pair of subsequent processes. The variables - // are named by the receiving process. - // - - if (ppd) - { - if (pipe(pstops_pipe)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to create pipe for ppdFilterPSToPS: %s", - strerror(errno)); - - exit_status = 1; - goto error; - } - } - - if (need_post_proc) - { - if (pipe(post_proc_pipe)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to create pipe for post-processing: %s", - strerror(errno)); - - exit_status = 1; - goto error; - } - } - - if ((pdf_pid = fork()) == 0) - { - // - // Child comes here... - // - - if (need_post_proc) - { - dup2(post_proc_pipe[1], 1); - close(post_proc_pipe[0]); - close(post_proc_pipe[1]); - } - else if (ppd) - { - dup2(pstops_pipe[1], 1); - close(pstops_pipe[0]); - close(pstops_pipe[1]); - } - else - dup2(outputfd, 1); - - if (renderer == PDFTOPS) - { - execvp(CUPS_POPPLER_PDFTOPS, pdf_argv); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute pdftops program: %s", - strerror(errno)); - } - else if (renderer == GS) - { - execvp(CUPS_GHOSTSCRIPT, pdf_argv); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute gs program: %s", - strerror(errno)); - } - else if (renderer == PDFTOCAIRO) - { - execvp(CUPS_POPPLER_PDFTOCAIRO, pdf_argv); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute pdftocairo program: %s", - strerror(errno)); - } - else if (renderer == ACROREAD) - { - // - // use filename as stdin for acroread to force output to stdout - // - - if ((fd = open(filename, O_RDONLY))) - { - dup2(fd, 0); - close(fd); - } - - execvp(CUPS_ACROREAD, pdf_argv); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute acroread program: %s", - strerror(errno)); - } - else if (renderer == MUPDF) - { - execvp(CUPS_MUTOOL, pdf_argv); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute mutool program: %s", - strerror(errno)); - } - - exit(1); - } - else if (pdf_pid < 0) - { - // - // Unable to fork! - // - - if (log) - { - if (renderer == PDFTOPS) - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute pdftops program: %s", - strerror(errno)); - else if (renderer == GS) - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute gs program: %s", - strerror(errno)); - else if (renderer == PDFTOCAIRO) - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute pdftocairo program: %s", - strerror(errno)); - else if (renderer == ACROREAD) - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute acroread program: %s", - strerror(errno)); - else if (renderer == MUPDF) - log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute mutool program: %s", - strerror(errno)); - } - - exit_status = 1; - goto error; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Started filter %s (PID %d)", - pdf_argv[0], pdf_pid); - - if (need_post_proc) - { - if ((post_proc_pid = fork()) == 0) - { - // - // Child comes here... - // - - close(post_proc_pipe[1]); - if (ppd) - { - dup2(pstops_pipe[1], 1); - close(pstops_pipe[0]); - close(pstops_pipe[1]); - } - else - dup2(outputfd, 1); - - fp = cupsFileOpenFd(post_proc_pipe[0], "r"); - - if (renderer == ACROREAD) - { - // - // Set %Title and %For from filter arguments since acroread inserts - // garbage for these when using -toPostScript - // - - while ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) > 0 && - strncmp(buffer, "%%BeginProlog", 13)) - { - if (strncmp(buffer, "%%Title", 7) == 0) - printf("%%%%Title: %s\n", data->job_title); - else if (strncmp(buffer, "%%For", 5) == 0) - printf("%%%%For: %s\n", data->job_user); - else - printf("%s", buffer); - } - - // - // Copy the rest of the file - // - - while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) - fwrite(buffer, 1, bytes, stdout); - } - else - { - // - // Copy everything until after initial comments (Prolog section) - // - - while ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) > 0 && - strncmp(buffer, "%%BeginProlog", 13) && - strncmp(buffer, "%%EndProlog", 11) && - strncmp(buffer, "%%BeginSetup", 12) && - strncmp(buffer, "%%Page:", 7)) - printf("%s", buffer); - - if (bytes > 0) - { - // - // Insert PostScript interpreter bug fix code in the beginning of - // the Prolog section (before the first active PostScript code) - // - - if (strncmp(buffer, "%%BeginProlog", 13)) - { - // No Prolog section, create one - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Adding Prolog section for workaround " - "PostScript code"); - puts("%%BeginProlog"); - } - else - printf("%s", buffer); - - if (renderer == GS && make_model[0]) - { - // - // Kyocera (and Utax) printers have a bug in their PostScript - // interpreter making them crashing on PostScript input data - // generated by Ghostscript's "ps2write" output device. - // - // The problem can be simply worked around by preceding the - // PostScript code with some extra bits. - // - // See https://bugs.launchpad.net/bugs/951627 - // - // In addition, at least some of Kyocera's PostScript printers are - // very slow on rendering images which request interpolation. So we - // also add some code to eliminate interpolation requests. - // - // See https://bugs.launchpad.net/bugs/1026974 - // - - if (!strncasecmp(make_model, "Kyocera", 7) || - !strncasecmp(make_model, "Utax", 4)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Inserted workaround PostScript code for " - "Kyocera and Utax printers"); - puts("% ===== Workaround insertion by pdftops CUPS filter ====="); - puts("% Kyocera's/Utax's PostScript interpreter crashes on " - "early name binding,"); - puts("% so eliminate all \"bind\"s by redefining \"bind\" to " - "no-op"); - puts("/bind {} bind def"); - puts("% Some Kyocera and Utax printers have an unacceptably " - "slow implementation"); - puts("% of image interpolation."); - puts("/image"); - puts("{"); - puts(" dup /Interpolate known"); - puts(" {"); - puts(" dup /Interpolate undef"); - puts(" } if"); - puts(" systemdict /image get exec"); - puts("} def"); - puts("% ====="); - } - - // - // Brother printers have a bug in their PostScript interpreter - // making them printing one blank page if PostScript input data - // generated by Ghostscript's "ps2write" output device is used. - // - // The problem can be simply worked around by preceding the - // PostScript code with some extra bits. - // - // See https://bugs.launchpad.net/bugs/950713 - // - - else if (!strncasecmp(make_model, "Brother", 7)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Inserted workaround PostScript code for " - "Brother printers"); - puts("% ===== Workaround insertion by pdftops CUPS filter ====="); - puts("% Brother's PostScript interpreter spits out the current " - "page"); - puts("% and aborts the job on the \"currenthalftone\" operator, " - "so redefine"); - puts("% it to null"); - puts("/currenthalftone {//null} bind def"); - puts("/orig.sethalftone systemdict /sethalftone get def"); - puts("/sethalftone {dup //null eq not {//orig.sethalftone}{pop} " - "ifelse} bind def"); - puts("% ====="); - } - } - - if (strncmp(buffer, "%%BeginProlog", 13)) - { - // Close newly created Prolog section - if (strncmp(buffer, "%%EndProlog", 11)) - puts("%%EndProlog"); - printf("%s", buffer); - } - - if (!ppd) - { - // - // Copy everything until the setup section - // - - while (bytes > 0 && - strncmp(buffer, "%%BeginSetup", 12) && - strncmp(buffer, "%%EndSetup", 10) && - strncmp(buffer, "%%Page:", 7)) - { - bytes = cupsFileGetLine(fp, buffer, sizeof(buffer)); - if (strncmp(buffer, "%%Page:", 7) && - strncmp(buffer, "%%EndSetup", 10)) - printf("%s", buffer); - } - - if (bytes > 0) - { - // - // Insert option PostScript code in Setup section - // - - if (strncmp(buffer, "%%BeginSetup", 12)) - { - // No Setup section, create one - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Adding Setup section for option " - "PostScript code"); - puts("%%BeginSetup"); - } - - // - // Duplex - // - - duplex = 0; - tumble = 0; - if ((val = cupsGetOption("sides", - num_options, options)) != NULL || - (val = cupsGetOption("Duplex", num_options, options)) != NULL) - { - if (!strcasecmp(val, "On") || - !strcasecmp(val, "True") || !strcasecmp(val, "Yes") || - !strncasecmp(val, "two-sided", 9) || - !strncasecmp(val, "TwoSided", 8) || - !strncasecmp(val, "Duplex", 6)) - { - duplex = 1; - if (!strncasecmp(val, "DuplexTumble", 12)) - tumble = 1; - } - } - - if ((val = cupsGetOption("sides", - num_options, options)) != NULL || - (val = cupsGetOption("Tumble", num_options, options)) != NULL) - { - if (!strcasecmp(val, "None") || !strcasecmp(val, "Off") || - !strcasecmp(val, "False") || !strcasecmp(val, "No") || - !strcasecmp(val, "one-sided") || - !strcasecmp(val, "OneSided") || - !strcasecmp(val, "two-sided-long-edge") || - !strcasecmp(val, "TwoSidedLongEdge") || - !strcasecmp(val, "DuplexNoTumble")) - tumble = 0; - else if (!strcasecmp(val, "On") || - !strcasecmp(val, "True") || !strcasecmp(val, "Yes") || - !strcasecmp(val, "two-sided-short-edge") || - !strcasecmp(val, "TwoSidedShortEdge") || - !strcasecmp(val, "DuplexTumble")) - tumble = 1; - } - - if (duplex) - { - if (tumble) - puts("<> setpagedevice"); - else - puts("<> setpagedevice"); - } - else - puts("<> setpagedevice"); - - // - // Resolution - // - - if ((xres > 0) && (yres > 0)) - printf("<> setpagedevice\n", xres, yres); - - // - // InputSlot/MediaSource - // - - if ((val = cupsGetOption("media-position", num_options, - options)) != NULL || - (val = cupsGetOption("MediaPosition", num_options, - options)) != NULL || - (val = cupsGetOption("media-source", num_options, - options)) != NULL || - (val = cupsGetOption("MediaSource", num_options, - options)) != NULL || - (val = cupsGetOption("InputSlot", num_options, - options)) != NULL) - { - if (!strncasecmp(val, "Auto", 4) || - !strncasecmp(val, "Default", 7)) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Main")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Alternate")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Manual")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Top")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Bottom")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "ByPassTray")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Tray1")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Tray2")) - puts("<> setpagedevice"); - else if (!strcasecmp(val, "Tray3")) - puts("<> setpagedevice"); - } - - // - // ColorModel - // - - if ((val = cupsGetOption("pwg-raster-document-type", num_options, - options)) != NULL || - (val = cupsGetOption("PwgRasterDocumentType", num_options, - options)) != NULL || - (val = cupsGetOption("print-color-mode", num_options, - options)) != NULL || - (val = cupsGetOption("PrintColorMode", num_options, - options)) != NULL || - (val = cupsGetOption("color-space", num_options, - options)) != NULL || - (val = cupsGetOption("ColorSpace", num_options, - options)) != NULL || - (val = cupsGetOption("color-model", num_options, - options)) != NULL || - (val = cupsGetOption("ColorModel", num_options, - options)) != NULL) - { - if (!strncasecmp(val, "Black", 5)) - puts("<> setpagedevice"); - else if (!strncasecmp(val, "Cmyk", 4)) - puts("<> setpagedevice"); - else if (!strncasecmp(val, "Cmy", 3)) - puts("<> setpagedevice"); - else if (!strncasecmp(val, "Rgb", 3)) - puts("<> setpagedevice"); - else if (!strncasecmp(val, "Gray", 4)) - puts("<> setpagedevice"); - else if (!strncasecmp(val, "Color", 5)) - puts("<> setpagedevice"); - } - - if (strncmp(buffer, "%%BeginSetup", 12)) - { - // Close newly created Setup section - if (strncmp(buffer, "%%EndSetup", 10)) - puts("%%EndSetup"); - printf("%s", buffer); - } - } - } - - // - // Copy the rest of the file - // - - while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) - fwrite(buffer, 1, bytes, stdout); - } - } - close(post_proc_pipe[0]); - - exit(0); - } - else if (post_proc_pid < 0) - { - // - // Unable to fork! - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute post-processing process: %s", - strerror(errno)); - - exit_status = 1; - goto error; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Started post-processing (PID %d)", - post_proc_pid); - } - - if (ppd) - { - if ((pstops_pid = fork()) == 0) - { - // - // Child comes here... - // - - close(pstops_pipe[1]); - if (need_post_proc) - { - close(post_proc_pipe[0]); - close(post_proc_pipe[1]); - } - - ret = ppdFilterPSToPS(pstops_pipe[0], outputfd, 0, &pstops_filter_data, - NULL); - close(pstops_pipe[0]); - - if (ret && log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: pstops filter function failed."); - - close(outputfd); - exit(ret); - } - else if (pstops_pid < 0) - { - // - // Unable to fork! - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: Unable to execute pstops program: %s", - strerror(errno)); - - exit_status = 1; - goto error; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Started filter pstops (PID %d)", - pstops_pid); - - close(pstops_pipe[0]); - close(pstops_pipe[1]); - } - - if (need_post_proc) - { - close(post_proc_pipe[0]); - close(post_proc_pipe[1]); - } - - // - // Wait for the child processes to exit... - // - - wait_children = 1 + need_post_proc + (ppd ? 1 : 0); - - while (wait_children > 0) - { - // - // Wait until we get a valid process ID or the job is canceled... - // - - while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR) - { - if (iscanceled && iscanceled(icd)) - { - kill(pdf_pid, SIGTERM); - if (need_post_proc) - kill(post_proc_pid, SIGTERM); - kill(pstops_pid, SIGTERM); - } - } - - if (wait_pid < 0) - break; - - wait_children --; - - // - // Report child status... - // - - if (wait_status) - { - if (WIFEXITED(wait_status)) - { - exit_status = WEXITSTATUS(wait_status); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: PID %d (%s) stopped with status %d!", - wait_pid, - wait_pid == pdf_pid ? - (renderer == PDFTOPS ? "pdftops" : - (renderer == PDFTOCAIRO ? "pdftocairo" : - (renderer == GS ? "gs" : - (renderer == ACROREAD ? "acroread" : - (renderer == MUPDF ? "mutool" : - "Unknown renderer"))))) : - (wait_pid == pstops_pid ? "pstops" : - (wait_pid == post_proc_pid ? "Post-processing" : - "Unknown process")), - exit_status); - } - else if (WTERMSIG(wait_status) == SIGTERM) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: PID %d (%s) was terminated normally with " - "signal %d!", - wait_pid, - wait_pid == pdf_pid ? - (renderer == PDFTOPS ? "pdftops" : - (renderer == PDFTOCAIRO ? "pdftocairo" : - (renderer == GS ? "gs" : - (renderer == ACROREAD ? "acroread" : - (renderer == MUPDF ? "mutool" : - "Unknown renderer"))))) : - (wait_pid == pstops_pid ? "pstops" : - (wait_pid == post_proc_pid ? "Post-processing" : - "Unknown process")), - exit_status); - } - else - { - exit_status = WTERMSIG(wait_status); - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterPDFToPS: PID %d (%s) crashed on signal %d!", - wait_pid, - wait_pid == pdf_pid ? - (renderer == PDFTOPS ? "pdftops" : - (renderer == PDFTOCAIRO ? "pdftocairo" : - (renderer == GS ? "gs" : - (renderer == ACROREAD ? "acroread" : - (renderer == MUPDF ? "mutool" : - "Unknown renderer"))))) : - (wait_pid == pstops_pid ? "pstops" : - (wait_pid == post_proc_pid ? "Post-processing" : - "Unknown process")), - exit_status); - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: PID %d (%s) exited with no errors.", - wait_pid, - wait_pid == pdf_pid ? - (renderer == PDFTOPS ? "pdftops" : - (renderer == PDFTOCAIRO ? "pdftocairo" : - (renderer == GS ? "gs" : - (renderer == ACROREAD ? "acroread" : - (renderer == MUPDF ? "mutool" : - "Unknown renderer"))))) : - (wait_pid == pstops_pid ? "pstops" : - (wait_pid == post_proc_pid ? "Post-processing" : - "Unknown process"))); - } - } - - // - // Cleanup and exit... - // - - error: - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterPDFToPS: Closing files ..."); - - close(outputfd); - - unlink(tempfile); - - return (exit_status); -} diff --git a/ppd/ppd-attr.c b/ppd/ppd-attr.c deleted file mode 100644 index f0bec8c07..000000000 --- a/ppd/ppd-attr.c +++ /dev/null @@ -1,315 +0,0 @@ -// -// PPD model-specific attribute routines for libppd. -// -// Copyright 2007-2015 by Apple Inc. -// Copyright 1997-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "ppd.h" -#include "debug-internal.h" - - -// -// 'ppdFindAttr()' - Find the first matching attribute. -// -// @since CUPS 1.1.19/macOS 10.3@ -// - -ppd_attr_t * // O - Attribute or @code NULL@ if not - // found -ppdFindAttr(ppd_file_t *ppd, // I - PPD file data - const char *name, // I - Attribute name - const char *spec) // I - Specifier string or @code NULL@ -{ - ppd_attr_t key, // Search key - *attr; // Current attribute - - - DEBUG_printf(("2ppdFindAttr(ppd=%p, name=\"%s\", spec=\"%s\")", ppd, name, - spec)); - - // - // Range check input... - // - - if (!ppd || !name || ppd->num_attrs == 0) - return (NULL); - - // - // Search for a matching attribute... - // - - memset(&key, 0, sizeof(key)); - strlcpy(key.name, name, sizeof(key.name)); - - // - // Return the first matching attribute, if any... - // - - if ((attr = (ppd_attr_t *)cupsArrayFind(ppd->sorted_attrs, &key)) != NULL) - { - if (spec) - { - // - // Loop until we find the first matching attribute for "spec"... - // - - while (attr && _ppd_strcasecmp(spec, attr->spec)) - { - if ((attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs)) != NULL && - _ppd_strcasecmp(attr->name, name)) - attr = NULL; - } - } - } - - return (attr); -} - - -// -// 'ppdFindNextAttr()' - Find the next matching attribute. -// -// @since CUPS 1.1.19/macOS 10.3@ -// - -ppd_attr_t * // O - Attribute or @code NULL@ if not - // found -ppdFindNextAttr(ppd_file_t *ppd, // I - PPD file data - const char *name, // I - Attribute name - const char *spec) // I - Specifier string or @code NULL@ -{ - ppd_attr_t *attr; // Current attribute - - - // - // Range check input... - // - - if (!ppd || !name || ppd->num_attrs == 0) - return (NULL); - - // - // See if there are more attributes to return... - // - - while ((attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs)) != NULL) - { - // - // Check the next attribute to see if it is a match... - // - - if (_ppd_strcasecmp(attr->name, name)) - { - // - // Nope, reset the current pointer to the end of the array... - // - - cupsArrayIndex(ppd->sorted_attrs, cupsArrayCount(ppd->sorted_attrs)); - - return (NULL); - } - - if (!spec || !_ppd_strcasecmp(attr->spec, spec)) - break; - } - - // - // Return the next attribute's value... - // - - return (attr); -} - - -// -// 'ppdNormalizeMakeAndModel()' - Normalize a product/make-and-model string. -// -// This function tries to undo the mistakes made by many printer manufacturers -// to produce a clean make-and-model string we can use. -// - -char * // O - Normalized make-and-model string - // or NULL on error -ppdNormalizeMakeAndModel( - const char *make_and_model, // I - Original make-and-model string - char *buffer, // I - String buffer - size_t bufsize) // I - Size of string buffer -{ - char *bufptr; // Pointer into buffer - - - if (!make_and_model || !buffer || bufsize < 1) - { - if (buffer) - *buffer = '\0'; - - return (NULL); - } - - // - // Skip leading whitespace... - // - - while (_ppd_isspace(*make_and_model)) - make_and_model ++; - - // - // Remove parenthesis and add manufacturers as needed... - // - - if (make_and_model[0] == '(') - { - strlcpy(buffer, make_and_model + 1, bufsize); - - if ((bufptr = strrchr(buffer, ')')) != NULL) - *bufptr = '\0'; - } - else if (!_ppd_strncasecmp(make_and_model, "XPrint", 6)) - { - // - // Xerox XPrint... - // - - snprintf(buffer, bufsize, "Xerox %s", make_and_model); - } - else if (!_ppd_strncasecmp(make_and_model, "Eastman", 7)) - { - // - // Kodak... - // - - snprintf(buffer, bufsize, "Kodak %s", make_and_model + 7); - } - else if (!_ppd_strncasecmp(make_and_model, "laserwriter", 11)) - { - // - // Apple LaserWriter... - // - - snprintf(buffer, bufsize, "Apple LaserWriter%s", make_and_model + 11); - } - else if (!_ppd_strncasecmp(make_and_model, "colorpoint", 10)) - { - // - // Seiko... - // - - snprintf(buffer, bufsize, "Seiko %s", make_and_model); - } - else if (!_ppd_strncasecmp(make_and_model, "fiery", 5)) - { - // - // EFI... - // - - snprintf(buffer, bufsize, "EFI %s", make_and_model); - } - else if (!_ppd_strncasecmp(make_and_model, "ps ", 3) || - !_ppd_strncasecmp(make_and_model, "colorpass", 9)) - { - // - // Canon... - // - - snprintf(buffer, bufsize, "Canon %s", make_and_model); - } - else if (!_ppd_strncasecmp(make_and_model, "designjet", 9) || - !_ppd_strncasecmp(make_and_model, "deskjet", 7)) - { - // - // HP... - // - - snprintf(buffer, bufsize, "HP %s", make_and_model); - } - else - strlcpy(buffer, make_and_model, bufsize); - - // - // Clean up the make... - // - - if (!_ppd_strncasecmp(buffer, "agfa", 4)) - { - // - // Replace with AGFA (all uppercase)... - // - - buffer[0] = 'A'; - buffer[1] = 'G'; - buffer[2] = 'F'; - buffer[3] = 'A'; - } - else if (!_ppd_strncasecmp(buffer, "Hewlett-Packard hp ", 19)) - { - // - // Just put "HP" on the front... - // - - buffer[0] = 'H'; - buffer[1] = 'P'; - _ppd_strcpy(buffer + 2, buffer + 18); - } - else if (!_ppd_strncasecmp(buffer, "Hewlett-Packard ", 16)) - { - // - // Just put "HP" on the front... - // - - buffer[0] = 'H'; - buffer[1] = 'P'; - _ppd_strcpy(buffer + 2, buffer + 15); - } - else if (!_ppd_strncasecmp(buffer, "Lexmark International", 21)) - { - // - // Strip "International"... - // - - _ppd_strcpy(buffer + 8, buffer + 21); - } - else if (!_ppd_strncasecmp(buffer, "herk", 4)) - { - // - // Replace with LHAG... - // - - buffer[0] = 'L'; - buffer[1] = 'H'; - buffer[2] = 'A'; - buffer[3] = 'G'; - } - else if (!_ppd_strncasecmp(buffer, "linotype", 8)) - { - // - // Replace with LHAG... - // - - buffer[0] = 'L'; - buffer[1] = 'H'; - buffer[2] = 'A'; - buffer[3] = 'G'; - _ppd_strcpy(buffer + 4, buffer + 8); - } - - // - // Remove trailing whitespace and return... - // - - for (bufptr = buffer + strlen(buffer) - 1; - bufptr >= buffer && _ppd_isspace(*bufptr); - bufptr --); - - bufptr[1] = '\0'; - - return (buffer[0] ? buffer : NULL); -} diff --git a/ppd/ppd-cache.c b/ppd/ppd-cache.c deleted file mode 100644 index 9ce9f9b8a..000000000 --- a/ppd/ppd-cache.c +++ /dev/null @@ -1,4721 +0,0 @@ -// -// PPD cache implementation for libppd. -// -// Copyright © 2010-2019 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "array-private.h" -#include "ipp-private.h" -#include "language-private.h" -#include "ppd.h" -#include "debug-internal.h" -#include -#include -#include - - -// -// Macro to test for two almost-equal PWG measurements. -// - -#define _PPD_PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2) - -// -// Macros to work around typos in older libcups version -// - -#if (CUPS_VERSION_MAJOR < 2) || ((CUPS_VERSION_MAJOR == 2) && ((CUPS_VERSION_MINOR < 3) || ((CUPS_VERSION_MINOR == 3) && (CUPS_VERSION_PATCH < 1)))) -#define IPP_FINISHINGS_CUPS_FOLD_ACCORDION IPP_FINISHINGS_CUPS_FOLD_ACCORDIAN -#define IPP_FINISHINGS_FOLD_ACCORDION IPP_FINISHINGS_FOLD_ACCORDIAN -#endif - - -// -// Local functions... -// - -static void ppd_pwg_add_finishing(cups_array_t *finishings, ipp_finishings_t template, const char *name, const char *value); -static void ppd_pwg_add_message(cups_array_t *a, const char *msg, const char *str); -static int ppd_pwg_compare_finishings(ppd_pwg_finishings_t *a, ppd_pwg_finishings_t *b); -static void ppd_pwg_free_finishings(ppd_pwg_finishings_t *f); - -char *ppd_cache_status_message = NULL; // Last PPD cache error - - -// -// 'set_error()' - Set the last status-message of PPD cache functions. -// - -static void -set_error(const char *message, // I - status-message value - int localize) // I - Localize the message? -{ - if (!message && errno) - { - message = strerror(errno); - localize = 0; - } - - if (ppd_cache_status_message) - { - _ppdStrFree(ppd_cache_status_message); - - ppd_cache_status_message = NULL; - } - - if (message) - { - if (localize) - { - // - // Get the message catalog... - // - - ppd_cache_status_message = - _ppdStrAlloc(_ppdLangString(cupsLangDefault(), - message)); - } - else - ppd_cache_status_message = _ppdStrAlloc(message); - } - - DEBUG_printf(("4set_error: last_status_message=\"%s\"", - ppd_cache_status_message)); -} - -// -// 'ppdConvertOptions()' - Convert printer options to standard IPP attributes. -// -// This functions converts PPD and CUPS-specific options to their standard IPP -// attributes and values and adds them to the specified IPP request. -// - -int // O - New number of copies -ppdConvertOptions( - ipp_t *request, // I - IPP request - ppd_file_t *ppd, // I - PPD file - ppd_cache_t *pc, // I - PPD cache info - ipp_attribute_t *media_col_sup, // I - media-col-supported values - ipp_attribute_t *doc_handling_sup, - // I - multiple-document-handling-supported values - ipp_attribute_t *print_color_mode_sup, - // I - Printer supports print-color-mode - const char *user, // I - User info - const char *format, // I - document-format value - int copies, // I - Number of copies - int num_options, // I - Number of options - cups_option_t *options) // I - Options -{ - int i; // Looping var - const char *keyword, // PWG keyword - *password; // Password string - pwg_size_t *size; // PWG media size - ipp_t *media_col, // media-col value - *media_size; // media-size value - const char *media_source, // media-source value - *media_type, // media-type value - *collate_str, // multiple-document-handling value - *color_attr_name, // Supported color attribute - *mandatory, // Mandatory attributes - *finishing_template; // Finishing template - int num_finishings = 0, // Number of finishing values - finishings[10]; // Finishing enum values - ppd_choice_t *choice; // Marked choice - int finishings_copies = copies; - // Number of copies for finishings - - - // - // Send standard IPP attributes... - // - - if (pc->password && - (password = cupsGetOption("job-password", - num_options, options)) != NULL && - ippGetOperation(request) != IPP_OP_VALIDATE_JOB) - { - ipp_attribute_t *attr = NULL; // job-password attribute - - if ((keyword = cupsGetOption("job-password-encryption", num_options, - options)) == NULL) - keyword = "none"; - - if (!strcmp(keyword, "none")) - { - // - // Add plain-text job-password... - // - - attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", - password, (int)strlen(password)); - } - else - { - // - // Add hashed job-password... - // - - unsigned char hash[64]; // Hash of password - ssize_t hashlen; // Length of hash - - if ((hashlen = cupsHashData(keyword, password, strlen(password), hash, - sizeof(hash))) > 0) - attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", - hash, (int)hashlen); - } - - if (attr) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "job-password-encryption", NULL, keyword); - } - - if (pc->account_id) - { - if ((keyword = cupsGetOption("job-account-id", - num_options, options)) == NULL) - keyword = cupsGetOption("job-billing", num_options, options); - - if (keyword) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", NULL, - keyword); - } - - if (pc->accounting_user_id) - { - if ((keyword = cupsGetOption("job-accounting-user-id", - num_options, options)) == NULL) - keyword = user; - - if (keyword) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, - "job-accounting-user-id", NULL, keyword); - } - - for (mandatory = (const char *)cupsArrayFirst(pc->mandatory); mandatory; - mandatory = (const char *)cupsArrayNext(pc->mandatory)) - { - if (strcmp(mandatory, "copies") && - strcmp(mandatory, "destination-uris") && - strcmp(mandatory, "finishings") && - strcmp(mandatory, "finishings-col") && - strcmp(mandatory, "finishing-template") && - strcmp(mandatory, "job-account-id") && - strcmp(mandatory, "job-accounting-user-id") && - strcmp(mandatory, "job-password") && - strcmp(mandatory, "job-password-encryption") && - strcmp(mandatory, "media") && - strncmp(mandatory, "media-col", 9) && - strcmp(mandatory, "multiple-document-handling") && - strcmp(mandatory, "output-bin") && - strcmp(mandatory, "print-color-mode") && - strcmp(mandatory, "print-quality") && - strcmp(mandatory, "sides") && - (keyword = cupsGetOption(mandatory, num_options, options)) != NULL) - { - _ppd_ipp_option_t *opt = _ppdIppFindOption(mandatory); - // Option type - ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME; - // Value type - - switch (value_tag) - { - case IPP_TAG_INTEGER : - case IPP_TAG_ENUM : - ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, - atoi(keyword)); - break; - case IPP_TAG_BOOLEAN : - ippAddBoolean(request, IPP_TAG_JOB, mandatory, - !_ppd_strcasecmp(keyword, "true")); - break; - case IPP_TAG_RANGE : - { - int lower, upper; // Range - - if (sscanf(keyword, "%d-%d", &lower, &upper) != 2) - lower = upper = atoi(keyword); - - ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper); - } - break; - case IPP_TAG_STRING : - ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, - (int)strlen(keyword)); - break; - default : - if (!strcmp(mandatory, "print-color-mode") && - !strcmp(keyword, "monochrome")) - { - if (ippContainsString(print_color_mode_sup, "auto-monochrome")) - keyword = "auto-monochrome"; - else if (ippContainsString(print_color_mode_sup, - "process-monochrome") && - !ippContainsString(print_color_mode_sup, "monochrome")) - keyword = "process-monochrome"; - } - - ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, NULL, - keyword); - break; - } - } - } - - if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL) - keyword = cupsGetOption("media", num_options, options); - - media_source = ppdCacheGetSource(pc, cupsGetOption("InputSlot", num_options, - options)); - media_type = ppdCacheGetType(pc, cupsGetOption("MediaType", num_options, - options)); - size = ppdCacheGetSize(pc, keyword); - - if (size || media_source || media_type) - { - // - // Add a media-col value... - // - - media_col = ippNew(); - - if (size) - { - media_size = ippNew(); - ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "x-dimension", size->width); - ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "y-dimension", size->length); - - ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size); - } - - for (i = 0; i < ippGetCount(media_col_sup); i ++) - { - if (size && !strcmp(ippGetString(media_col_sup, i, NULL), - "media-left-margin")) - ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "media-left-margin", size->left); - else if (size && !strcmp(ippGetString(media_col_sup, i, NULL), - "media-bottom-margin")) - ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "media-bottom-margin", size->bottom); - else if (size && !strcmp(ippGetString(media_col_sup, i, NULL), - "media-right-margin")) - ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "media-right-margin", size->right); - else if (size && !strcmp(ippGetString(media_col_sup, i, NULL), - "media-top-margin")) - ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "media-top-margin", size->top); - else if (media_source && !strcmp(ippGetString(media_col_sup, i, NULL), - "media-source")) - ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, - "media-source", NULL, media_source); - else if (media_type && !strcmp(ippGetString(media_col_sup, i, NULL), - "media-type")) - ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, - "media-type", NULL, media_type); - } - - ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col); - } - - if ((keyword = cupsGetOption("output-bin", num_options, options)) == NULL) - { - if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL) - keyword = ppdCacheGetBin(pc, choice->choice); - } - - if (keyword) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin", NULL, - keyword); - - color_attr_name = print_color_mode_sup ? "print-color-mode" : "output-mode"; - - if ((keyword = cupsGetOption("print-color-mode", - num_options, options)) == NULL) - { - if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL) - { - if (!_ppd_strcasecmp(choice->choice, "Gray")) - keyword = "monochrome"; - else - keyword = "color"; - } - } - - if (keyword && !strcmp(keyword, "monochrome")) - { - if (ippContainsString(print_color_mode_sup, "auto-monochrome")) - keyword = "auto-monochrome"; - else if (ippContainsString(print_color_mode_sup, "process-monochrome") && - !ippContainsString(print_color_mode_sup, "monochrome")) - keyword = "process-monochrome"; - } - - if (keyword) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name, NULL, - keyword); - - if ((keyword = cupsGetOption("print-quality", num_options, options)) != NULL) - ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", - atoi(keyword)); - else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL) - { - if (!_ppd_strcasecmp(choice->choice, "draft")) - ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", - IPP_QUALITY_DRAFT); - else if (!_ppd_strcasecmp(choice->choice, "normal")) - ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", - IPP_QUALITY_NORMAL); - else if (!_ppd_strcasecmp(choice->choice, "high")) - ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", - IPP_QUALITY_HIGH); - } - - if ((keyword = cupsGetOption("sides", num_options, options)) != NULL) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, keyword); - else if (pc->sides_option && - (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL) - { - if (pc->sides_1sided && !_ppd_strcasecmp(choice->choice, pc->sides_1sided)) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, - "one-sided"); - else if (pc->sides_2sided_long && - !_ppd_strcasecmp(choice->choice, pc->sides_2sided_long)) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, - "two-sided-long-edge"); - else if (pc->sides_2sided_short && - !_ppd_strcasecmp(choice->choice, pc->sides_2sided_short)) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, - "two-sided-short-edge"); - } - - // - // Copies... - // - - if ((keyword = cupsGetOption("multiple-document-handling", - num_options, options)) != NULL) - { - if (strstr(keyword, "uncollated")) - keyword = "false"; - else - keyword = "true"; - } - else if ((keyword = cupsGetOption("collate", num_options, options)) == NULL) - keyword = "true"; - - if (format) - { - if (!_ppd_strcasecmp(format, "image/gif") || - !_ppd_strcasecmp(format, "image/jp2") || - !_ppd_strcasecmp(format, "image/jpeg") || - !_ppd_strcasecmp(format, "image/png") || - !_ppd_strcasecmp(format, "image/tiff") || - !_ppd_strncasecmp(format, "image/x-", 8)) - { - // - // Collation makes no sense for single page image formats... - // - - keyword = "false"; - } - else if (!_ppd_strncasecmp(format, "image/", 6) || - !_ppd_strcasecmp(format, "application/vnd.cups-raster")) - { - // - // Multi-page image formats will have copies applied by the upstream - // filters... - // - - copies = 1; - } - } - - if (doc_handling_sup) - { - if (!_ppd_strcasecmp(keyword, "true")) - collate_str = "separate-documents-collated-copies"; - else - collate_str = "separate-documents-uncollated-copies"; - - for (i = 0; i < ippGetCount(doc_handling_sup); i ++) - { - if (!strcmp(ippGetString(doc_handling_sup, i, NULL), collate_str)) - { - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, - "multiple-document-handling", NULL, collate_str); - break; - } - } - - if (i >= ippGetCount(doc_handling_sup)) - copies = 1; - } - - // - // Map finishing options... - // - - if ((finishing_template = cupsGetOption("cupsFinishingTemplate", - num_options, options)) == NULL) - finishing_template = cupsGetOption("finishing-template", - num_options, options); - - if (finishing_template && strcmp(finishing_template, "none")) - { - ipp_t *fin_col = ippNew(); // finishings-col value - - ippAddString(fin_col, IPP_TAG_JOB, IPP_TAG_KEYWORD, "finishing-template", - NULL, finishing_template); - ippAddCollection(request, IPP_TAG_JOB, "finishings-col", fin_col); - ippDelete(fin_col); - - if (copies != finishings_copies && - (keyword = cupsGetOption("job-impressions", - num_options, options)) != NULL) - { - // - // Send job-pages-per-set attribute to apply finishings correctly... - // - - ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", - atoi(keyword) / finishings_copies); - } - } - else - { - num_finishings = - ppdCacheGetFinishingValues(ppd, pc, - (int)(sizeof(finishings) / - sizeof(finishings[0])), finishings); - if (num_finishings > 0) - { - ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", - num_finishings, finishings); - - if (copies != finishings_copies && - (keyword = cupsGetOption("job-impressions", - num_options, options)) != NULL) - { - // - // Send job-pages-per-set attribute to apply finishings correctly... - // - - ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, - "job-pages-per-set", atoi(keyword) / finishings_copies); - } - } - } - - return (copies); -} - - -// -// 'ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a -// written file. -// -// Use the @link ppdCacheWriteFile@ function to write PWG mapping data to a -// file. -// - -ppd_cache_t * // O - PPD cache and mapping data -ppdCacheCreateWithFile( - const char *filename, // I - File to read - ipp_t **attrs) // IO - IPP attributes, if any -{ - cups_file_t *fp; // File - ppd_cache_t *pc; // PWG mapping data - pwg_size_t *size; // Current size - pwg_map_t *map; // Current map - ppd_pwg_finishings_t *finishings; // Current finishings option - int linenum, // Current line number - num_bins, // Number of bins in file - num_sizes, // Number of sizes in file - num_sources, // Number of sources in file - num_types; // Number of types in file - char line[2048], // Current line - *value, // Pointer to value in line - *valueptr, // Pointer into value - pwg_keyword[128], // PWG keyword - ppd_keyword[PPD_MAX_NAME]; - // PPD keyword - ppd_pwg_print_color_mode_t print_color_mode; - // Print color mode for preset - ppd_pwg_print_quality_t print_quality;// Print quality for preset - ppd_pwg_print_content_optimize_t print_content_optimize; - // Content optimize for preset - - DEBUG_printf(("ppdCacheCreateWithFile(filename=\"%s\")", filename)); - - // - // Range check input... - // - - if (attrs) - *attrs = NULL; - - if (!filename) - { - set_error(strerror(EINVAL), 0); - return (NULL); - } - - // - // Open the file... - // - - if ((fp = cupsFileOpen(filename, "r")) == NULL) - { - set_error(strerror(errno), 0); - return (NULL); - } - - // - // Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it... - // - - if (!cupsFileGets(fp, line, sizeof(line))) - { - set_error(strerror(errno), 0); - DEBUG_puts("ppdCacheCreateWithFile: Unable to read first line."); - cupsFileClose(fp); - return (NULL); - } - - if (strncmp(line, "#CUPS-PPD-CACHE-", 16)) - { - set_error(_("Bad PPD cache file."), 1); - DEBUG_printf(("ppdCacheCreateWithFile: Wrong first line \"%s\".", line)); - cupsFileClose(fp); - return (NULL); - } - - if (atoi(line + 16) != PPD_CACHE_VERSION) - { - set_error(_("Out of date PPD cache file."), 1); - DEBUG_printf(("ppdCacheCreateWithFile: Cache file has version %s, " - "expected %d.", line + 16, PPD_CACHE_VERSION)); - cupsFileClose(fp); - return (NULL); - } - - // - // Allocate the mapping data structure... - // - - if ((pc = calloc(1, sizeof(ppd_cache_t))) == NULL) - { - set_error(strerror(errno), 0); - DEBUG_puts("ppdCacheCreateWithFile: Unable to allocate ppd_cache_t."); - goto create_error; - } - - pc->max_copies = 9999; - - // - // Read the file... - // - - linenum = 0; - num_bins = 0; - num_sizes = 0; - num_sources = 0; - num_types = 0; - - while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) - { - DEBUG_printf(("ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", " - "linenum=%d", line, value, linenum)); - - if (!value) - { - DEBUG_printf(("ppdCacheCreateWithFile: Missing value on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - else if (!_ppd_strcasecmp(line, "Filter")) - { - if (!pc->filters) - pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - cupsArrayAdd(pc->filters, value); - } - else if (!_ppd_strcasecmp(line, "PreFilter")) - { - if (!pc->prefilters) - pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - cupsArrayAdd(pc->prefilters, value); - } - else if (!_ppd_strcasecmp(line, "Product")) - { - pc->product = strdup(value); - } - else if (!_ppd_strcasecmp(line, "SingleFile")) - { - pc->single_file = !_ppd_strcasecmp(value, "true"); - } - else if (!_ppd_strcasecmp(line, "IPP")) - { - off_t pos = cupsFileTell(fp), // Position in file - length = strtol(value, NULL, 10); - // Length of IPP attributes - - if (attrs && *attrs) - { - DEBUG_puts("ppdCacheCreateWithFile: IPP listed multiple times."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - else if (length <= 0) - { - DEBUG_puts("ppdCacheCreateWithFile: Bad IPP length."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (attrs) - { - // - // Read IPP attributes into the provided variable... - // - - *attrs = ippNew(); - - if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, - *attrs) != IPP_STATE_DATA) - { - DEBUG_puts("ppdCacheCreateWithFile: Bad IPP data."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - } - else - { - // - // Skip the IPP data entirely... - // - - cupsFileSeek(fp, pos + length); - } - - if (cupsFileTell(fp) != (pos + length)) - { - DEBUG_puts("ppdCacheCreateWithFile: Bad IPP data."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - } - else if (!_ppd_strcasecmp(line, "NumBins")) - { - if (num_bins > 0) - { - DEBUG_puts("ppdCacheCreateWithFile: NumBins listed multiple times."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if ((num_bins = atoi(value)) <= 0 || num_bins > 65536) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad NumBins value %d on line " - "%d.", num_sizes, linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if ((pc->bins = calloc((size_t)num_bins, sizeof(pwg_map_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithFile: Unable to allocate %d bins.", - num_sizes)); - set_error(strerror(errno), 0); - goto create_error; - } - } - else if (!_ppd_strcasecmp(line, "Bin")) - { - if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad Bin on line %d.", linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (pc->num_bins >= num_bins) - { - DEBUG_printf(("ppdCacheCreateWithFile: Too many Bin's on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - map = pc->bins + pc->num_bins; - map->pwg = strdup(pwg_keyword); - map->ppd = strdup(ppd_keyword); - - pc->num_bins ++; - } - else if (!_ppd_strcasecmp(line, "NumSizes")) - { - if (num_sizes > 0) - { - DEBUG_puts("ppdCacheCreateWithFile: NumSizes listed multiple times."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad NumSizes value %d on line " - "%d.", num_sizes, linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (num_sizes > 0) - { - if ((pc->sizes = calloc((size_t)num_sizes, sizeof(pwg_size_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithFile: Unable to allocate %d sizes.", - num_sizes)); - set_error(strerror(errno), 0); - goto create_error; - } - } - } - else if (!_ppd_strcasecmp(line, "Size")) - { - if (pc->num_sizes >= num_sizes) - { - DEBUG_printf(("ppdCacheCreateWithFile: Too many Size's on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - size = pc->sizes + pc->num_sizes; - - if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword, - &(size->width), &(size->length), &(size->left), - &(size->bottom), &(size->right), &(size->top)) != 8) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad Size on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - size->map.pwg = strdup(pwg_keyword); - size->map.ppd = strdup(ppd_keyword); - - pc->num_sizes ++; - } - else if (!_ppd_strcasecmp(line, "CustomSize")) - { - if (pc->custom_max_width > 0) - { - DEBUG_printf(("ppdCacheCreateWithFile: Too many CustomSize's on line " - "%d.", linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width), - &(pc->custom_max_length), &(pc->custom_min_width), - &(pc->custom_min_length), &(pc->custom_size.left), - &(pc->custom_size.bottom), &(pc->custom_size.right), - &(pc->custom_size.top)) != 8) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad CustomSize on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max", - pc->custom_max_width, pc->custom_max_length, NULL); - pc->custom_max_keyword = strdup(pwg_keyword); - - pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min", - pc->custom_min_width, pc->custom_min_length, NULL); - pc->custom_min_keyword = strdup(pwg_keyword); - } - else if (!_ppd_strcasecmp(line, "SourceOption")) - { - pc->source_option = strdup(value); - } - else if (!_ppd_strcasecmp(line, "NumSources")) - { - if (num_sources > 0) - { - DEBUG_puts("ppdCacheCreateWithFile: NumSources listed multiple " - "times."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if ((num_sources = atoi(value)) <= 0 || num_sources > 65536) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad NumSources value %d on " - "line %d.", num_sources, linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if ((pc->sources = calloc((size_t)num_sources, - sizeof(pwg_map_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithFile: Unable to allocate %d sources.", - num_sources)); - set_error(strerror(errno), 0); - goto create_error; - } - } - else if (!_ppd_strcasecmp(line, "Source")) - { - if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad Source on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (pc->num_sources >= num_sources) - { - DEBUG_printf(("ppdCacheCreateWithFile: Too many Source's on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - map = pc->sources + pc->num_sources; - map->pwg = strdup(pwg_keyword); - map->ppd = strdup(ppd_keyword); - - pc->num_sources ++; - } - else if (!_ppd_strcasecmp(line, "NumTypes")) - { - if (num_types > 0) - { - DEBUG_puts("ppdCacheCreateWithFile: NumTypes listed multiple times."); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if ((num_types = atoi(value)) <= 0 || num_types > 65536) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad NumTypes value %d on " - "line %d.", num_types, linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if ((pc->types = calloc((size_t)num_types, sizeof(pwg_map_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithFile: Unable to allocate %d types.", - num_types)); - set_error(strerror(errno), 0); - goto create_error; - } - } - else if (!_ppd_strcasecmp(line, "Type")) - { - if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad Type on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (pc->num_types >= num_types) - { - DEBUG_printf(("ppdCacheCreateWithFile: Too many Type's on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - map = pc->types + pc->num_types; - map->pwg = strdup(pwg_keyword); - map->ppd = strdup(ppd_keyword); - - pc->num_types ++; - } - else if (!_ppd_strcasecmp(line, "Preset")) - { - // - // Preset output-mode print-quality name=value ... - // - - print_color_mode = - (ppd_pwg_print_color_mode_t)strtol(value, &valueptr, 10); - print_quality = - (ppd_pwg_print_quality_t)strtol(valueptr, &valueptr, 10); - - if (print_color_mode < PPD_PWG_PRINT_COLOR_MODE_MONOCHROME || - print_color_mode >= PPD_PWG_PRINT_COLOR_MODE_MAX || - print_quality < PPD_PWG_PRINT_QUALITY_DRAFT || - print_quality >= PPD_PWG_PRINT_QUALITY_MAX || - valueptr == value || !*valueptr) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad Preset on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - pc->num_presets[print_color_mode][print_quality] = - cupsParseOptions(valueptr, 0, - pc->presets[print_color_mode] + print_quality); - } - else if (!_ppd_strcasecmp(line, "OptimizePreset")) - { - // - // Preset print_content_optimize name=value ... - // - - print_content_optimize = - (ppd_pwg_print_content_optimize_t)strtol(value, &valueptr, 10); - - if (print_content_optimize < PPD_PWG_PRINT_CONTENT_OPTIMIZE_AUTO || - print_content_optimize >= PPD_PWG_PRINT_CONTENT_OPTIMIZE_MAX || - valueptr == value || !*valueptr) - { - DEBUG_printf(("ppdCacheCreateWithFile: Bad Optimize Preset on line %d.", - linenum)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - pc->num_optimize_presets[print_content_optimize] = - cupsParseOptions(valueptr, 0, - pc->optimize_presets + print_content_optimize); - } - else if (!_ppd_strcasecmp(line, "SidesOption")) - pc->sides_option = strdup(value); - else if (!_ppd_strcasecmp(line, "Sides1Sided")) - pc->sides_1sided = strdup(value); - else if (!_ppd_strcasecmp(line, "Sides2SidedLong")) - pc->sides_2sided_long = strdup(value); - else if (!_ppd_strcasecmp(line, "Sides2SidedShort")) - pc->sides_2sided_short = strdup(value); - else if (!_ppd_strcasecmp(line, "Finishings")) - { - if (!pc->finishings) - pc->finishings = - cupsArrayNew3((cups_array_func_t)ppd_pwg_compare_finishings, - NULL, NULL, 0, NULL, - (cups_afree_func_t)ppd_pwg_free_finishings); - - if ((finishings = calloc(1, sizeof(ppd_pwg_finishings_t))) == NULL) - goto create_error; - - finishings->value = (ipp_finishings_t)strtol(value, &valueptr, 10); - finishings->num_options = cupsParseOptions(valueptr, 0, - &(finishings->options)); - - cupsArrayAdd(pc->finishings, finishings); - } - else if (!_ppd_strcasecmp(line, "FinishingTemplate")) - { - if (!pc->templates) - pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - cupsArrayAdd(pc->templates, value); - } - else if (!_ppd_strcasecmp(line, "MaxCopies")) - pc->max_copies = atoi(value); - else if (!_ppd_strcasecmp(line, "ChargeInfoURI")) - pc->charge_info_uri = strdup(value); - else if (!_ppd_strcasecmp(line, "JobAccountId")) - pc->account_id = !_ppd_strcasecmp(value, "true"); - else if (!_ppd_strcasecmp(line, "JobAccountingUserId")) - pc->accounting_user_id = !_ppd_strcasecmp(value, "true"); - else if (!_ppd_strcasecmp(line, "JobPassword")) - pc->password = strdup(value); - else if (!_ppd_strcasecmp(line, "Mandatory")) - { - if (pc->mandatory) - _ppdArrayAddStrings(pc->mandatory, value, ' '); - else - pc->mandatory = _ppdArrayNewStrings(value, ' '); - } - else if (!_ppd_strcasecmp(line, "SupportFile")) - { - if (!pc->support_files) - pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - cupsArrayAdd(pc->support_files, value); - } - else - { - DEBUG_printf(("ppdCacheCreateWithFile: Unknown %s on line %d.", line, - linenum)); - } - } - - if (pc->num_sizes < num_sizes) - { - DEBUG_printf(("ppdCacheCreateWithFile: Not enough sizes (%d < %d).", - pc->num_sizes, num_sizes)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (pc->num_sources < num_sources) - { - DEBUG_printf(("ppdCacheCreateWithFile: Not enough sources (%d < %d).", - pc->num_sources, num_sources)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - if (pc->num_types < num_types) - { - DEBUG_printf(("ppdCacheCreateWithFile: Not enough types (%d < %d).", - pc->num_types, num_types)); - set_error(_("Bad PPD cache file."), 1); - goto create_error; - } - - cupsFileClose(fp); - - return (pc); - - // - // If we get here the file was bad - free any data and return... - // - - create_error: - - cupsFileClose(fp); - ppdCacheDestroy(pc); - - if (attrs) - { - ippDelete(*attrs); - *attrs = NULL; - } - - return (NULL); -} - - -// -// 'ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file. -// - -ppd_cache_t * // O - PPD cache and mapping data -ppdCacheCreateWithPPD(ppd_file_t *ppd) // I - PPD file -{ - int i, j, k; // Looping vars - ppd_cache_t *pc; // PWG mapping data - ppd_option_t *input_slot, // InputSlot option - *media_type, // MediaType option - *output_bin, // OutputBin option - *color_model, // ColorModel option - *duplex, // Duplex option - *ppd_option; // Other PPD option - ppd_choice_t *choice; // Current InputSlot/MediaType - pwg_map_t *map; // Current source/type map - int preset_added = 0; // Preset definition found in PPD? - ppd_attr_t *ppd_attr; // Current PPD preset attribute - int num_options; // Number of preset options and props - cups_option_t *options; // Preset options and properties - ppd_size_t *ppd_size; // Current PPD size - pwg_size_t *pwg_size; // Current PWG size - char pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3], - // PWG keyword string - ppd_name[PPD_MAX_NAME]; - // Normalized PPD name - const char *pwg_name; // Standard PWG media name - pwg_media_t *pwg_media; // PWG media data - ppd_pwg_print_color_mode_t pwg_print_color_mode; - // print-color-mode index - ppd_pwg_print_quality_t pwg_print_quality; - // print-quality index - int similar; // Are the old and new size similar? - pwg_size_t *old_size; // Current old size - int old_imageable, // Old imageable length in 2540ths - old_borderless, // Old borderless state - old_known_pwg; // Old PWG name is well-known - int new_width, // New width in 2540ths - new_length, // New length in 2540ths - new_left, // New left margin in 2540ths - new_bottom, // New bottom margin in 2540ths - new_right, // New right margin in 2540ths - new_top, // New top margin in 2540ths - new_imageable, // New imageable length in 2540ths - new_borderless, // New borderless state - new_known_pwg; // New PWG name is well-known - pwg_size_t *new_size; // New size to add, if any - const char *filter; // Current filter - ppd_pwg_finishings_t *finishings; // Current finishings value - char msg_id[256]; // Message identifier - - - DEBUG_printf(("ppdCacheCreateWithPPD(ppd=%p)", ppd)); - - // - // Range check input... - // - - if (!ppd) - return (NULL); - - // - // Allocate memory... - // - - if ((pc = calloc(1, sizeof(ppd_cache_t))) == NULL) - { - DEBUG_puts("ppdCacheCreateWithPPD: Unable to allocate ppd_cache_t."); - goto create_error; - } - - pc->strings = _ppdMessageNew(NULL); - - // - // Copy and convert size data... - // - - if (ppd->num_sizes > 0) - { - if ((pc->sizes = calloc((size_t)ppd->num_sizes, - sizeof(pwg_size_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithPPD: Unable to allocate %d " - "pwg_size_t's.", ppd->num_sizes)); - goto create_error; - } - - for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes; - i > 0; - i --, ppd_size ++) - { - // - // Don't copy over custom size... - // - - if (!_ppd_strcasecmp(ppd_size->name, "Custom")) - continue; - - // - // Convert the PPD size name to the corresponding PWG keyword name. - // - - if ((pwg_media = - pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width), - PWG_FROM_POINTS(ppd_size->length))) != NULL) - { - // - // Standard name, do we have conflicts? - // - - for (j = 0; j < pc->num_sizes; j ++) - if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg)) - { - pwg_media = NULL; - break; - } - } - - if (pwg_media && strncmp(pwg_media->pwg, "custom_", 7) != 0) - { - // - // Standard name and no conflicts, use it! - // - - pwg_name = pwg_media->pwg; - new_known_pwg = 1; - } - else - { - // - // Not a standard name; convert it to a PWG vendor name of the form: - // - // pp_lowerppd_WIDTHxHEIGHTuu - // - - pwg_name = pwg_keyword; - new_known_pwg = 0; - - ppdPwgUnppdizeName(ppd_size->name, ppd_name, sizeof(ppd_name), "_."); - pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name, - PWG_FROM_POINTS(ppd_size->width), - PWG_FROM_POINTS(ppd_size->length), NULL); - } - - // - // If we have a similar paper with non-zero margins then we only - // want to keep it if it has a larger imageable area length. - // The NULL check is for dimensions that are <= 0... - // - - if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width), - PWG_FROM_POINTS(ppd_size->length))) == - NULL) - continue; - - new_width = pwg_media->width; - new_length = pwg_media->length; - new_left = PWG_FROM_POINTS(ppd_size->left); - new_bottom = PWG_FROM_POINTS(ppd_size->bottom); - new_right = PWG_FROM_POINTS(ppd_size->width - ppd_size->right); - new_top = PWG_FROM_POINTS(ppd_size->length - ppd_size->top); - new_imageable = new_length - new_top - new_bottom; - new_borderless = new_bottom == 0 && new_top == 0 && - new_left == 0 && new_right == 0; - - for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, - new_size = NULL; - k > 0 && !similar; - k --, old_size ++) - { - old_imageable = old_size->length - old_size->top - old_size->bottom; - old_borderless = old_size->left == 0 && old_size->bottom == 0 && - old_size->right == 0 && old_size->top == 0; - old_known_pwg = strncmp(old_size->map.pwg, "oe_", 3) && - strncmp(old_size->map.pwg, "om_", 3); - - similar = old_borderless == new_borderless && - _PPD_PWG_EQUIVALENT(old_size->width, new_width) && - _PPD_PWG_EQUIVALENT(old_size->length, new_length); - - if (similar && - (new_known_pwg || - (!old_known_pwg && new_imageable > old_imageable))) - { - // - // The new paper has a larger imageable area so it could replace - // the older paper. Regardless of the imageable area, we always - // prefer the size with a well-known PWG name. - // - - new_size = old_size; - free(old_size->map.ppd); - free(old_size->map.pwg); - } - } - - if (!similar) - { - // - // The paper was unique enough to deserve its own entry so add it to the - // end. - // - - new_size = pwg_size ++; - pc->num_sizes ++; - } - - if (new_size) - { - // - // Save this size... - // - - new_size->map.ppd = strdup(ppd_size->name); - new_size->map.pwg = strdup(pwg_name); - new_size->width = new_width; - new_size->length = new_length; - new_size->left = new_left; - new_size->bottom = new_bottom; - new_size->right = new_right; - new_size->top = new_top; - } - } - } - - if (ppd->variable_sizes) - { - // - // Generate custom size data... - // - - pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max", - PWG_FROM_POINTS(ppd->custom_max[0]), - PWG_FROM_POINTS(ppd->custom_max[1]), NULL); - - // Some PPD files have upper limits too large to be treated with - // int numbers, if we have an overflow (negative result for one - // dimension) use a fixed, large value instead - char *p1, *p2; - char *newmax = (pwg_keyword[strlen(pwg_keyword) - 1] == 'n' ? - "10000" : "100000"); - p1 = strrchr(pwg_keyword, '_'); - p1 ++; - if (*p1 == '-') - { - for (p2 = p1; *p2 != 'x'; p2 ++); - memmove(p1 + strlen(newmax), p2, strlen(p2) + 1); - memmove(p1, newmax, strlen(newmax)); - } - p1 = strrchr(pwg_keyword, 'x'); - p1 ++; - if (*p1 == '-') - { - for (p2 = p1; *p2 != 'm' && *p2 != 'i'; p2 ++); - memmove(p1 + strlen(newmax), p2, strlen(p2) + 1); - memmove(p1, newmax, strlen(newmax)); - } - - pc->custom_max_keyword = strdup(pwg_keyword); - pc->custom_max_width = PWG_FROM_POINTS(ppd->custom_max[0]); - pc->custom_max_length = PWG_FROM_POINTS(ppd->custom_max[1]); - - pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min", - PWG_FROM_POINTS(ppd->custom_min[0]), - PWG_FROM_POINTS(ppd->custom_min[1]), NULL); - pc->custom_min_keyword = strdup(pwg_keyword); - pc->custom_min_width = PWG_FROM_POINTS(ppd->custom_min[0]); - pc->custom_min_length = PWG_FROM_POINTS(ppd->custom_min[1]); - - pc->custom_size.left = PWG_FROM_POINTS(ppd->custom_margins[0]); - pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]); - pc->custom_size.right = PWG_FROM_POINTS(ppd->custom_margins[2]); - pc->custom_size.top = PWG_FROM_POINTS(ppd->custom_margins[3]); - } - - // - // Copy and convert InputSlot data... - // - - if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL) - input_slot = ppdFindOption(ppd, "HPPaperSource"); - - if (input_slot) - { - pc->source_option = strdup(input_slot->keyword); - - if ((pc->sources = calloc((size_t)input_slot->num_choices, - sizeof(pwg_map_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithPPD: Unable to allocate %d " - "pwg_map_t's for InputSlot.", input_slot->num_choices)); - goto create_error; - } - - pc->num_sources = input_slot->num_choices; - - for (i = input_slot->num_choices, choice = input_slot->choices, - map = pc->sources; - i > 0; - i --, choice ++, map ++) - { - if (!_ppd_strncasecmp(choice->choice, "Auto", 4) || - !_ppd_strcasecmp(choice->choice, "Default")) - pwg_name = "auto"; - else if (!_ppd_strcasecmp(choice->choice, "Cassette")) - pwg_name = "main"; - else if (!_ppd_strcasecmp(choice->choice, "PhotoTray")) - pwg_name = "photo"; - else if (!_ppd_strcasecmp(choice->choice, "CDTray")) - pwg_name = "disc"; - else if (!_ppd_strncasecmp(choice->choice, "Multipurpose", 12) || - !_ppd_strcasecmp(choice->choice, "MP") || - !_ppd_strcasecmp(choice->choice, "MPTray")) - pwg_name = "by-pass-tray"; - else if (!_ppd_strcasecmp(choice->choice, "LargeCapacity")) - pwg_name = "large-capacity"; - else if (!_ppd_strncasecmp(choice->choice, "Lower", 5)) - pwg_name = "bottom"; - else if (!_ppd_strncasecmp(choice->choice, "Middle", 6)) - pwg_name = "middle"; - else if (!_ppd_strncasecmp(choice->choice, "Upper", 5)) - pwg_name = "top"; - else if (!_ppd_strncasecmp(choice->choice, "Side", 4)) - pwg_name = "side"; - else if (!_ppd_strcasecmp(choice->choice, "Roll")) - pwg_name = "main-roll"; - else - { - // - // Convert PPD name to lowercase... - // - - pwg_name = pwg_keyword; - ppdPwgUnppdizeName(choice->choice, pwg_keyword, sizeof(pwg_keyword), - "_"); - } - - map->pwg = strdup(pwg_name); - map->ppd = strdup(choice->choice); - - // - // Add localized text for PWG keyword to message catalog... - // - - snprintf(msg_id, sizeof(msg_id), "media-source.%s", pwg_name); - ppd_pwg_add_message(pc->strings, msg_id, choice->text); - } - } - - // - // Copy and convert MediaType data... - // - - if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL) - { - if ((pc->types = calloc((size_t)media_type->num_choices, - sizeof(pwg_map_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithPPD: Unable to allocate %d " - "pwg_map_t's for MediaType.", media_type->num_choices)); - goto create_error; - } - - pc->num_types = media_type->num_choices; - - for (i = media_type->num_choices, choice = media_type->choices, - map = pc->types; - i > 0; - i --, choice ++, map ++) - { - if (!_ppd_strncasecmp(choice->choice, "Auto", 4) || - !_ppd_strcasecmp(choice->choice, "Any") || - !_ppd_strcasecmp(choice->choice, "Default")) - pwg_name = "auto"; - else if (!_ppd_strncasecmp(choice->choice, "Card", 4)) - pwg_name = "cardstock"; - else if (!_ppd_strncasecmp(choice->choice, "Env", 3)) - pwg_name = "envelope"; - else if (!_ppd_strncasecmp(choice->choice, "Gloss", 5)) - pwg_name = "photographic-glossy"; - else if (!_ppd_strcasecmp(choice->choice, "HighGloss")) - pwg_name = "photographic-high-gloss"; - else if (!_ppd_strcasecmp(choice->choice, "Matte")) - pwg_name = "photographic-matte"; - else if (!_ppd_strncasecmp(choice->choice, "Plain", 5)) - pwg_name = "stationery"; - else if (!_ppd_strncasecmp(choice->choice, "Coated", 6)) - pwg_name = "stationery-coated"; - else if (!_ppd_strcasecmp(choice->choice, "Inkjet")) - pwg_name = "stationery-inkjet"; - else if (!_ppd_strcasecmp(choice->choice, "Letterhead")) - pwg_name = "stationery-letterhead"; - else if (!_ppd_strncasecmp(choice->choice, "Preprint", 8)) - pwg_name = "stationery-preprinted"; - else if (!_ppd_strcasecmp(choice->choice, "Recycled")) - pwg_name = "stationery-recycled"; - else if (!_ppd_strncasecmp(choice->choice, "Transparen", 10)) - pwg_name = "transparency"; - else - { - // - // Convert PPD name to lowercase... - // - - pwg_name = pwg_keyword; - ppdPwgUnppdizeName(choice->choice, pwg_keyword, sizeof(pwg_keyword), - "_"); - } - - map->pwg = strdup(pwg_name); - map->ppd = strdup(choice->choice); - - // - // Add localized text for PWG keyword to message catalog... - // - - snprintf(msg_id, sizeof(msg_id), "media-type.%s", pwg_name); - ppd_pwg_add_message(pc->strings, msg_id, choice->text); - } - } - - // - // Copy and convert OutputBin data... - // - - if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL) - { - if ((pc->bins = calloc((size_t)output_bin->num_choices, - sizeof(pwg_map_t))) == NULL) - { - DEBUG_printf(("ppdCacheCreateWithPPD: Unable to allocate %d " - "pwg_map_t's for OutputBin.", output_bin->num_choices)); - goto create_error; - } - - pc->num_bins = output_bin->num_choices; - - for (i = output_bin->num_choices, choice = output_bin->choices, - map = pc->bins; - i > 0; - i --, choice ++, map ++) - { - ppdPwgUnppdizeName(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_"); - - map->pwg = strdup(pwg_keyword); - map->ppd = strdup(choice->choice); - - // - // Add localized text for PWG keyword to message catalog... - // - - snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_keyword); - ppd_pwg_add_message(pc->strings, msg_id, choice->text); - } - } - - if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL) - { - // - // "Classic" Mac OS approach - // - - // - // Copy and convert APPrinterPreset (output-mode + print-quality) data... - // - - const char *quality, // com.apple.print.preset.quality value - *output_mode, // com.apple.print.preset.output-mode - // value - *color_model_val, // ColorModel choice - *graphicsType, // com.apple.print.preset.graphicsType - // value - *media_front_coating; - // com.apple.print.preset.media-front-coating value - - do - { - // - // Add localized text for PWG keyword to message catalog... - // - - snprintf(msg_id, sizeof(msg_id), "preset-name.%s", ppd_attr->spec); - ppd_pwg_add_message(pc->strings, msg_id, ppd_attr->text); - - // - // Get the options for this preset... - // - - num_options = ppdParseOptions(ppd_attr->value, 0, &options, - PPD_PARSE_ALL); - - if ((quality = cupsGetOption("com.apple.print.preset.quality", - num_options, options)) != NULL) - { - // - // Get the print-quality for this preset... - // - - if (!strcmp(quality, "low")) - pwg_print_quality = PPD_PWG_PRINT_QUALITY_DRAFT; - else if (!strcmp(quality, "high")) - pwg_print_quality = PPD_PWG_PRINT_QUALITY_HIGH; - else - pwg_print_quality = PPD_PWG_PRINT_QUALITY_NORMAL; - - // - // Ignore graphicsType "Photo" presets that are not high quality. - // - - graphicsType = cupsGetOption("com.apple.print.preset.graphicsType", - num_options, options); - - if (pwg_print_quality != PPD_PWG_PRINT_QUALITY_HIGH && graphicsType && - !strcmp(graphicsType, "Photo")) - continue; - - // - // Ignore presets for normal and draft quality where the coating - // isn't "none" or "autodetect". - // - - media_front_coating = cupsGetOption( - "com.apple.print.preset.media-front-coating", - num_options, options); - - if (pwg_print_quality != PPD_PWG_PRINT_QUALITY_HIGH && - media_front_coating && - strcmp(media_front_coating, "none") && - strcmp(media_front_coating, "autodetect")) - continue; - - // - // Get the output mode for this preset... - // - - output_mode = cupsGetOption("com.apple.print.preset.output-mode", - num_options, options); - color_model_val = cupsGetOption("ColorModel", num_options, options); - - if (output_mode) - { - if (!strcmp(output_mode, "monochrome")) - pwg_print_color_mode = PPD_PWG_PRINT_COLOR_MODE_MONOCHROME; - else - pwg_print_color_mode = PPD_PWG_PRINT_COLOR_MODE_COLOR; - } - else if (color_model_val) - { - if (!_ppd_strcasecmp(color_model_val, "Gray")) - pwg_print_color_mode = PPD_PWG_PRINT_COLOR_MODE_MONOCHROME; - else - pwg_print_color_mode = PPD_PWG_PRINT_COLOR_MODE_COLOR; - } - else - pwg_print_color_mode = PPD_PWG_PRINT_COLOR_MODE_COLOR; - - // - // Save the options for this combination as needed... - // - - if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality]) - pc->num_presets[pwg_print_color_mode][pwg_print_quality] = - ppdParseOptions(ppd_attr->value, 0, - pc->presets[pwg_print_color_mode] + - pwg_print_quality, PPD_PARSE_OPTIONS); - preset_added = 1; - } - - cupsFreeOptions(num_options, options); - } - while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL); - - if (preset_added && - !pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME][PPD_PWG_PRINT_QUALITY_DRAFT] && - !pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME][PPD_PWG_PRINT_QUALITY_NORMAL] && - !pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME][PPD_PWG_PRINT_QUALITY_HIGH]) - { - // - // Try adding some common color options to create grayscale - // presets. These are listed in order of popularity... - // - - const char *color_option = NULL, // Color control option - *gray_choice = NULL; // Choice to select grayscale - - if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL && - ppdFindChoice(color_model, "Gray")) - { - color_option = "ColorModel"; - gray_choice = "Gray"; - } - else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL && - ppdFindChoice(color_model, "grayscale")) - { - color_option = "HPColorMode"; - gray_choice = "grayscale"; - } - else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL && - ppdFindChoice(color_model, "Mono")) - { - color_option = "BRMonoColor"; - gray_choice = "Mono"; - } - else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL && - ppdFindChoice(color_model, "1")) - { - color_option = "CNIJSGrayScale"; - gray_choice = "1"; - } - else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL && - ppdFindChoice(color_model, "True")) - { - color_option = "HPColorAsGray"; - gray_choice = "True"; - } - - if (color_option && gray_choice) - { - // - // Copy and convert ColorModel (output-mode) data... - // - - cups_option_t *coption, // Color option - *moption; // Monochrome option - - for (pwg_print_quality = PPD_PWG_PRINT_QUALITY_DRAFT; - pwg_print_quality < PPD_PWG_PRINT_QUALITY_MAX; - pwg_print_quality ++) - { - if (pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality]) - { - // - // Copy the color options... - // - - num_options = pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [pwg_print_quality]; - options = calloc(sizeof(cups_option_t), (size_t)num_options); - - if (options) - { - for (i = num_options, moption = options, - coption = pc->presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [pwg_print_quality]; - i > 0; - i --, moption ++, coption ++) - { - moption->name = _ppdStrRetain(coption->name); - moption->value = _ppdStrRetain(coption->value); - } - - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] = - num_options; - pc->presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] = - options; - } - } - else if (pwg_print_quality != PPD_PWG_PRINT_QUALITY_NORMAL) - continue; - - // - // Add the grayscale option to the preset... - // - - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] = - cupsAddOption(color_option, gray_choice, - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [pwg_print_quality], - pc->presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] + - pwg_print_quality); - } - } - } - } - - if (!preset_added) - { - // - // Auto-association of PPD file option settings with the IPP job attributes - // print-color-mode, print-quality, and print-content-optimize - // - // This is used to retro-fit PPD files and classic CUPS drivers into - // Printer Applications, which are IPP printers for the clients and so - // should get controlled by standard IPP attributes as far as possible - // - // Note that settings assigned to print-content-optimize are only used - // when printing with "high" print-quality - // - - ppdCacheAssignPresets(ppd, pc); - } - - // - // Copy and convert Duplex (sides) data... - // - - if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL) - if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL) - if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL) - if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL) - if ((duplex = ppdFindOption(ppd, "ARDuplex")) == NULL) - duplex = ppdFindOption(ppd, "KD03Duplex"); - - if (duplex) - { - pc->sides_option = strdup(duplex->keyword); - - for (i = duplex->num_choices, choice = duplex->choices; - i > 0; - i --, choice ++) - { - if ((!_ppd_strcasecmp(choice->choice, "None") || - !_ppd_strcasecmp(choice->choice, "False")) && - !pc->sides_1sided) - pc->sides_1sided = strdup(choice->choice); - else if ((!_ppd_strcasecmp(choice->choice, "DuplexNoTumble") || - !_ppd_strcasecmp(choice->choice, "LongEdge") || - !_ppd_strcasecmp(choice->choice, "Top")) && - !pc->sides_2sided_long) - pc->sides_2sided_long = strdup(choice->choice); - else if ((!_ppd_strcasecmp(choice->choice, "DuplexTumble") || - !_ppd_strcasecmp(choice->choice, "ShortEdge") || - !_ppd_strcasecmp(choice->choice, "Bottom")) && - !pc->sides_2sided_short) - pc->sides_2sided_short = strdup(choice->choice); - } - } - - // - // Copy filters and pre-filters... - // - - pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - cupsArrayAdd(pc->filters, - "application/vnd.cups-raw application/octet-stream 0 -"); - - if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL) - { - do - { - cupsArrayAdd(pc->filters, ppd_attr->value); - } - while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL); - } - else if (ppd->num_filters > 0) - { - for (i = 0; i < ppd->num_filters; i ++) - cupsArrayAdd(pc->filters, ppd->filters[i]); - } - else - cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -"); - - // - // See if we have a command filter... - // - - for (filter = (const char *)cupsArrayFirst(pc->filters); - filter; - filter = (const char *)cupsArrayNext(pc->filters)) - if (!_ppd_strncasecmp(filter, "application/vnd.cups-command", 28) && - _ppd_isspace(filter[28])) - break; - - if (!filter && - ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL || - _ppd_strcasecmp(ppd_attr->value, "none"))) - { - // - // No command filter and no cupsCommands keyword telling us not to use one. - // See if this is a PostScript printer, and if so add a PostScript command - // filter... - // - - for (filter = (const char *)cupsArrayFirst(pc->filters); - filter; - filter = (const char *)cupsArrayNext(pc->filters)) - if (!_ppd_strncasecmp(filter, "application/vnd.cups-postscript", 31) && - _ppd_isspace(filter[31])) - break; - - if (filter) - cupsArrayAdd(pc->filters, - "application/vnd.cups-command application/postscript 100 " - "commandtops"); - } - - if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL) - { - pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - do - cupsArrayAdd(pc->prefilters, ppd_attr->value); - while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL); - } - - if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL) - pc->single_file = !_ppd_strcasecmp(ppd_attr->value, "true"); - - // - // Copy the product string, if any... - // - - if (ppd->product) - pc->product = strdup(ppd->product); - - // - // Copy finishings mapping data... - // - - if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL) - { - // - // Have proper vendor mapping of IPP finishings values to PPD options... - // - - pc->finishings = - cupsArrayNew3((cups_array_func_t)ppd_pwg_compare_finishings, - NULL, NULL, 0, NULL, - (cups_afree_func_t)ppd_pwg_free_finishings); - - do - { - if ((finishings = calloc(1, sizeof(ppd_pwg_finishings_t))) == NULL) - goto create_error; - - finishings->value = (ipp_finishings_t)atoi(ppd_attr->spec); - finishings->num_options = ppdParseOptions(ppd_attr->value, 0, - &(finishings->options), - PPD_PARSE_OPTIONS); - - cupsArrayAdd(pc->finishings, finishings); - } - while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings", - NULL)) != NULL); - } - else - { - // - // No IPP mapping data, try to map common/standard PPD keywords... - // - - pc->finishings = - cupsArrayNew3((cups_array_func_t)ppd_pwg_compare_finishings, - NULL, NULL, 0, NULL, - (cups_afree_func_t)ppd_pwg_free_finishings); - - if ((ppd_option = ppdFindOption(ppd, "StapleLocation")) != NULL) - { - // - // Add staple finishings... - // - - if (ppdFindChoice(ppd_option, "SinglePortrait")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, - "StapleLocation", "SinglePortrait"); - if (ppdFindChoice(ppd_option, "UpperLeft")) // Ricoh extension - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, - "StapleLocation", "UpperLeft"); - if (ppdFindChoice(ppd_option, "UpperRight")) // Ricoh extension - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_RIGHT, - "StapleLocation", "UpperRight"); - if (ppdFindChoice(ppd_option, "SingleLandscape")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_STAPLE_BOTTOM_LEFT, - "StapleLocation", "SingleLandscape"); - if (ppdFindChoice(ppd_option, "DualLandscape")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_DUAL_LEFT, - "StapleLocation", "DualLandscape"); - } - - if ((ppd_option = ppdFindOption(ppd, "RIPunch")) != NULL) - { - // - // Add (Ricoh) punch finishings... - // - - if (ppdFindChoice(ppd_option, "Left2")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_DUAL_LEFT, - "RIPunch", "Left2"); - if (ppdFindChoice(ppd_option, "Left3")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_TRIPLE_LEFT, - "RIPunch", "Left3"); - if (ppdFindChoice(ppd_option, "Left4")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_QUAD_LEFT, - "RIPunch", "Left4"); - if (ppdFindChoice(ppd_option, "Right2")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_DUAL_RIGHT, - "RIPunch", "Right2"); - if (ppdFindChoice(ppd_option, "Right3")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_TRIPLE_RIGHT, - "RIPunch", "Right3"); - if (ppdFindChoice(ppd_option, "Right4")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_QUAD_RIGHT, - "RIPunch", "Right4"); - if (ppdFindChoice(ppd_option, "Upper2")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_DUAL_TOP, - "RIPunch", "Upper2"); - if (ppdFindChoice(ppd_option, "Upper3")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_TRIPLE_TOP, - "RIPunch", "Upper3"); - if (ppdFindChoice(ppd_option, "Upper4")) - ppd_pwg_add_finishing(pc->finishings, - IPP_FINISHINGS_PUNCH_QUAD_TOP, - "RIPunch", "Upper4"); - } - - if ((ppd_option = ppdFindOption(ppd, "BindEdge")) != NULL) - { - // - // Add bind finishings... - // - - if (ppdFindChoice(ppd_option, "Left")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_LEFT, - "BindEdge", "Left"); - if (ppdFindChoice(ppd_option, "Right")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_RIGHT, "BindEdge", "Right"); - if (ppdFindChoice(ppd_option, "Top")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_TOP, "BindEdge", "Top"); - if (ppdFindChoice(ppd_option, "Bottom")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_BOTTOM, "BindEdge", "Bottom"); - } - - if ((ppd_option = ppdFindOption(ppd, "FoldType")) != NULL) - { - // - // Add (Adobe) fold finishings... - // - - if (ppdFindChoice(ppd_option, "ZFold")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_Z, - "FoldType", "ZFold"); - if (ppdFindChoice(ppd_option, "Saddle")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_HALF, - "FoldType", "Saddle"); - if (ppdFindChoice(ppd_option, "DoubleGate")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_DOUBLE_GATE, - "FoldType", "DoubleGate"); - if (ppdFindChoice(ppd_option, "LeftGate")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LEFT_GATE, - "FoldType", "LeftGate"); - if (ppdFindChoice(ppd_option, "RightGate")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_RIGHT_GATE, - "FoldType", "RightGate"); - if (ppdFindChoice(ppd_option, "Letter")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, - "FoldType", "Letter"); - if (ppdFindChoice(ppd_option, "XFold")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_POSTER, - "FoldType", "XFold"); - } - - if ((ppd_option = ppdFindOption(ppd, "RIFoldType")) != NULL) - { - // - // Add (Ricoh) fold finishings... - // - - if (ppdFindChoice(ppd_option, "OutsideTwoFold")) - ppd_pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, - "RIFoldType", "OutsideTwoFold"); - } - - if (cupsArrayCount(pc->finishings) == 0) - { - cupsArrayDelete(pc->finishings); - pc->finishings = NULL; - } - } - - if ((ppd_option = ppdFindOption(ppd, "cupsFinishingTemplate")) != NULL) - { - pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - for (choice = ppd_option->choices, i = ppd_option->num_choices; i > 0; - choice ++, i --) - { - cupsArrayAdd(pc->templates, (void *)choice->choice); - - // - // Add localized text for PWG keyword to message catalog... - // - - snprintf(msg_id, sizeof(msg_id), "finishing-template.%s", choice->choice); - ppd_pwg_add_message(pc->strings, msg_id, choice->text); - } - } - - // - // Max copies... - // - - if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL) - pc->max_copies = atoi(ppd_attr->value); - else if (ppd->manual_copies) - pc->max_copies = 1; - else - pc->max_copies = 9999; - - // - // cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId, - // cupsJobPassword, and cupsMandatory. - // - - if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL) - pc->charge_info_uri = strdup(ppd_attr->value); - - if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL) - pc->account_id = !_ppd_strcasecmp(ppd_attr->value, "true"); - - if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL) - pc->accounting_user_id = !_ppd_strcasecmp(ppd_attr->value, "true"); - - if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL) - pc->password = strdup(ppd_attr->value); - - if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL) - pc->mandatory = _ppdArrayNewStrings(ppd_attr->value, ' '); - - // - // Support files... - // - - pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - - for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); - ppd_attr; - ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) - cupsArrayAdd(pc->support_files, ppd_attr->value); - - if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL) - cupsArrayAdd(pc->support_files, ppd_attr->value); - - // - // Return the cache data... - // - - return (pc); - - // - // If we get here we need to destroy the PWG mapping data and return NULL... - // - - create_error: - - set_error(_("Out of memory."), 1); - ppdCacheDestroy(pc); - - return (NULL); -} - - -// -// 'ppdCacheAssignPresets()' - Go through all the options and choices -// in the PPD to find out which influence -// on color/bw, print quality, and content -// optimization they have to assign them -// to the presets so that jobs can easily -// be controlled with standard IPP -// attributes -// - -void -ppdCacheAssignPresets(ppd_file_t *ppd, - ppd_cache_t *pc) -{ - typedef struct choice_properties_s - { - int sets_mono, - sets_color, - sets_draft, - sets_normal, - sets_high, - for_photo, - for_graphics, - for_text, - for_tg, - is_default; - unsigned int res_x, - res_y; - long total_image_data; - } choice_properties_t; - int i, j, k, l; - unsigned int m; - int pass; - ppd_group_t *group; - ppd_option_t *option; - int is_color; - unsigned int base_res_x = 0, - base_res_y = 0; - cups_page_header2_t header, - optheader; - int preferred_bits; - ppd_attr_t *ppd_attr; - int res_factor = 1; // Weight of the score for the - int name_factor = 10; // print quality - int color_factor = 1000; - - // Do we have a color printer ? - is_color = (ppd->color_device ? 1 : 0); - - // what is the base/default resolution for this PPD? - ppdMarkDefaults(ppd); - ppdRasterInterpretPPD(&header, ppd, 0, NULL, NULL); - if (header.HWResolution[0] != 100 || header.HWResolution[1] != 100) - { - base_res_x = header.HWResolution[0]; - base_res_y = header.HWResolution[1]; - } - else if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL) - { - // Use the PPD-defined default resolution... - if (sscanf(ppd_attr->value, "%dx%d", &base_res_x, &base_res_y) == 1) - base_res_y = base_res_x; - } - - // Go through all options of the PPD file - for (i = ppd->num_groups, group = ppd->groups; - i > 0; - i --, group ++) - { - // Skip the "Installable Options" group - if (strncasecmp(group->name, "Installable", 11) == 0) - continue; - - for (j = group->num_options, option = group->options; - j > 0; - j --, option ++) - { - int sets_color_mode = 0, - sets_quality = 0, - sets_optimization = 0; - int best_mono_draft = 0, - best_mono_normal = 0, - best_mono_high = 0, - best_color_draft = 0, - best_color_normal = 0, - best_color_high = 0, - best_photo = 0, - best_graphics = 0, - best_text = 0, - best_tg = 0; - int default_ch = -1, - best_mono_draft_ch = -1, - best_mono_normal_ch = -1, - best_mono_high_ch = -1, - best_color_draft_ch = -1, - best_color_normal_ch = -1, - best_color_high_ch = -1, - best_photo_ch = -1, - best_graphics_ch = -1, - best_text_ch = -1, - best_tg_ch = -1; - cups_array_t *choice_properties; - choice_properties_t *properties; - char *o, *c, *p; - int score; - - o = option->keyword; - - // Skip options which do not change color mode and quality or - // generally do not make sense in presets - if (strcasecmp(o, "PageSize") == 0 || - strcasecmp(o, "PageRegion") == 0 || - strcasecmp(o, "InputSlot") == 0 || - strcasecmp(o, "MediaSource") == 0 || - strcasecmp(o, "MediaType") == 0 || - strcasecmp(o, "OutputBin") == 0 || - strcasecmp(o, "Duplex") == 0 || - strcasecmp(o, "JCLDuplex") == 0 || - strcasecmp(o, "EFDuplex") == 0 || - strcasecmp(o, "EFDuplexing") == 0 || - strcasecmp(o, "ARDuplex") == 0 || - strcasecmp(o, "KD03Duplex") == 0 || - strcasecmp(o, "Collate") == 0) - continue; - - // - // Set member options of composite options in Foomatic to stay - // controlled by the composite option - // - // Composite options in Foomatic are options which set a number - // of other options, so each choice of them is the same as a - // preset in CUPS. In addition, some PPDs in Foomatic have a - // composite option named "PrintoutMode" with 6 choices, exactly - // the 6 of the grid of CUPS presets, color/mono in draft, - // medium, and high quality. The composite options are created - // by hand, so they surely do for what they are intended for and - // so they are safer as this preset auto-generation - // algorithm. Therefore we only let the composite option be set - // in our presets and set the member options to leave the - // control at the composite option - // - - if (strstr(ppd->nickname, "Foomatic") && - !strncmp(option->choices[0].choice, "From", 4) && - ppdFindOption(ppd, option->choices[0].choice + 4)) - { - for (k = 0; k < 2; k ++) - for (l = 0; l < 3; l ++) - if (cupsGetOption(option->choices[0].choice + 4, - pc->num_presets[k][l], pc->presets[k][l])) - pc->num_presets[k][l] = - cupsAddOption(o, option->choices[0].choice, - pc->num_presets[k][l], &(pc->presets[k][l])); - for (k = 0; k < 5; k ++) - if (cupsGetOption(option->choices[0].choice + 4, - pc->num_optimize_presets[k], - pc->optimize_presets[k])) - pc->num_optimize_presets[k] = - cupsAddOption(o, option->choices[0].choice, - pc->num_optimize_presets[k], - &(pc->optimize_presets[k])); - continue; - } - - // Array for properties of the choices - choice_properties = cupsArrayNew(NULL, NULL); - - // - // Gather the data for each choice - // - - for (k = 0; k < option->num_choices; k ++) - { - properties = - (choice_properties_t *)calloc(1, sizeof(choice_properties_t)); - - c = option->choices[k].choice; - - // Is this the default choice? (preferred for "normal" quality, - // used for color if no choice name suggests being color) - if (strcmp(c, option->defchoice) == 0) - { - properties->is_default = 1; - default_ch = k; - } - - // - // Color/Monochrome - print-color-mode - // - - // If we have a color device, check whether this option sets mono or - // color printing - if (is_color) - { - if (strcasecmp(o, "CNIJSGrayScale") == 0) - { - if (strcasecmp(c, "1") == 0) - properties->sets_mono = 2; - else - properties->sets_color = 1; - } - else if (strcasecmp(o, "HPColorAsGray") == 0 || // HP PostScript - strcasecmp(o, "HPPJLColorAsGray") == 0) // HP PostScript - { - if (strcasecmp(c, "True") == 0 || - strcasecmp(c, "yes") == 0) - properties->sets_mono = 2; - else - properties->sets_color = 1; - } - else if (strcasecmp(o, "ColorModel") == 0 || - strcasestr(o, "ColorMode") || - strcasecmp(o, "OutputMode") == 0 || - strcasecmp(o, "PrintoutMode") == 0 || - strcasecmp(o, "ARCMode") == 0 || // Sharp - strcasestr(o, "ColorMode") || - strcasecmp(o, "ColorResType") == 0 || // Toshiba - strcasestr(o, "MonoColor")) // Brother - { - // Monochrome/grayscale printing - if (strcasestr(c, "Mono") || - strcasecmp(c, "Black") == 0 || - ((p = strcasestr(c, "Black")) && strcasestr(p, "White")) || - (strncasecmp(c, "BW", 2) == 0 && !isalpha(c[2]))) - properties->sets_mono = 2; - else if (strcasestr(c, "Gray") || - strcasestr(c, "Grey") || - strcasecmp(c, "BlackOnly") == 0) // Lexmark - properties->sets_mono = 3; - - // Color printing - if (((p = strcasestr(c, "CMY")) && !strcasestr(p, "Gray")) || - strcasecmp(c, "ColorOnly") == 0 || // Lexmark - ((p = strcasestr(c, "Adobe")) && strcasestr(p, "RGB"))) - properties->sets_color = 2; - else if (strcasestr(c, "sRGB")) - properties->sets_color = 4; - else if (strcasestr(c, "RGB") || - strcasestr(c, "Color")) - properties->sets_color = 3; - } - - // This option actually sets color mode - if (properties->sets_mono || properties->sets_color) - sets_color_mode = 1; - } - - // - // Output Quality - print-quality - // - - // check whether this option affects print quality or content - // optimization - - // Determine influence of the options and choices on the print - // quality by their names - - // Vendor-specific option and choice names - if (strcasecmp(o, "ARCPPriority") == 0) // Sharp - { - if (strcasecmp(c, "Quality") == 0) - properties->sets_high = 10; - else if (strcasecmp(c, "Speed") == 0) - properties->sets_draft = 10; - } - else if (strcasecmp(o, "BRJpeg") == 0) // Brother - { - if (strcasecmp(c, "QualityPrior") == 0) - properties->sets_high = 10; - else if (strcasecmp(c, "SpeedPrior") == 0) - properties->sets_draft = 10; - } - else if (strcasecmp(o, "FXOutputMode") == 0) // Fuji Xerox - { - if (strcasecmp(c, "Quality2") == 0) - properties->sets_high = 10; - else if (strcasecmp(c, "Speed") == 0) - properties->sets_draft = 10; - else if (strcasecmp(c, "Standard") == 0) - properties->sets_normal = 10; - } - else if (strcasecmp(o, "RIPrintMode") == 0) // Ricoh & OEM - { - if (strcasecmp(c, "1rhit") == 0) - properties->sets_high = 7; - else if (strcasecmp(c, "6rhit") == 0) - properties->sets_high = 10; - else if (strcasecmp(c, "3rhit") == 0 || - strcasecmp(c, "4rhit") == 0 || - strcasecmp(c, "5rhit") == 0) - properties->sets_draft = 10; - else if (strcasecmp(c, "0rhit") == 0) - properties->sets_normal = 10; - } - else if (strcasecmp(o, "EconoMode") == 0 || // Foomatic - strcasecmp(o, "EconoFast") == 0) // Foomatic (HP PPA) - { - if (strcasecmp(c, "Off") == 0 || - strcasecmp(c, "False") == 0) - properties->sets_high = 1; - else if (strcasecmp(c, "On") == 0 || - strcasecmp(c, "True") == 0 || - strcasecmp(c, "Low") == 0) - properties->sets_draft = 10; - else if (strcasecmp(c, "High") == 0) - properties->sets_draft = 11; - } - else if (strcasestr(o, "ColorPrecision")) // Gutenprint - { - if (strcasecmp(c, "best") == 0) - properties->sets_high = 10; - } - // Generic boolean options which enhance quality if true - else if (((p = strcasestr(o, "slow")) && strcasestr(p, "dry")) || - ((p = strcasestr(o, "color")) && strcasestr(p, "enhance")) || - ((p = strcasestr(o, "resolution")) && - !strcasestr(p, "enhance")) || - strcasecmp(o, "RET") == 0 || - strcasecmp(o, "Smoothing") == 0 || // HPLIP - ((p = strcasestr(o, "uni")) && strcasestr(p, "direction"))) - { - if (strcasecmp(c, "True") == 0 || - strcasecmp(c, "On") == 0 || - strcasecmp(c, "Yes") == 0 || - strcasecmp(c, "1") == 0 || - strcasecmp(c, "Medium") == 0) // Resolution Enhancement/RET (HP) - properties->sets_high = 3; - else if (strcasecmp(c, "False") == 0 || - strcasecmp(c, "Off") == 0 || - strcasecmp(c, "No") == 0 || - strcasecmp(c, "0") == 0) - properties->sets_draft = 3; - } - // Generic boolean options which reduce quality if true - else if (strcasestr(o, "draft") || - strcasestr(o, "economy") || - ((p = strcasestr(o, "eco")) && strcasestr(p, "mode")) || - ((p = strcasestr(o, "toner")) && strcasestr(p, "sav")) || - ((p = strcasestr(o, "bi")) && strcasestr(p, "direction")) || - strcasecmp(o, "EcoBlack") == 0 || // Foomatic (Alps) - strcasecmp(o, "bidi") == 0 || - strcasecmp(o, "bi-di") == 0) - { - if (strcasecmp(c, "True") == 0 || - strcasecmp(c, "On") == 0 || - strcasecmp(c, "Yes") == 0 || - strcasecmp(c, "1") == 0 || - strcasecmp(c, "Medium") == 0) // EconomyMode (Brother) - properties->sets_draft = 3; - else if (strcasecmp(c, "False") == 0 || - strcasecmp(c, "Off") == 0 || - strcasecmp(c, "No") == 0 || - strcasecmp(c, "0") == 0) - properties->sets_high = 3; - } - // Generic enumerated choice option and choice names - else if (strcasecmp(o, "ColorModel") == 0 || - strcasestr(o, "ColorMode") || - strcasecmp(o, "OutputMode") == 0 || // HPLIP hpcups - strcasecmp(o, "PrintoutMode") == 0 || // Foomatic - strcasecmp(o, "PrintQuality") == 0 || - strcasecmp(o, "PrintMode") == 0 || - strcasestr(o, "ColorMode") || - strcasestr(o, "HalfTone") || // HPLIP - strcasecmp(o, "ColorResType") == 0 || // Toshiba - strcasestr(o, "MonoColor") || // Brother - strcasestr(o, "Quality") || - strcasestr(o, "Resolution") || - strcasestr(o, "Precision") || // ex. stpColorPrecision - // in Gutenprint - strcasestr(o, "PrintingDirection")) // Gutenprint - { - // High quality - if (strcasecmp(c, "Quality") == 0 || - strcasecmp(c, "5") == 0) - properties->sets_high = 1; - else if (strcasestr(c, "Photo") || - strcasestr(c, "Enhance") || - strcasestr(c, "slow") || - strncasecmp(c, "ProRes", 6) == 0 || // HPLIP - strncasecmp(c, "ImageREt", 8) == 0 || // HPLIP - ((p = strcasestr(c, "low")) && strcasestr(p, "speed"))) - properties->sets_high = 2; - else if (strcasestr(c, "fine") || - strcasestr(c, "deep") || - ((p = strcasestr(c, "high")) && !strcasestr(p, "speed")) || - strcasestr(c, "HQ") || - strcasecmp(c, "ProRes600") == 0 || // HPLIP - strcasecmp(c, "ImageREt1200") == 0 || // HPLIP - strcasecmp(c, "Enhanced") == 0) - properties->sets_high = 3; - else if (strcasestr(c, "best") || - strcasecmp(c, "high") == 0 || - strcasecmp(c, "fine") == 0 || - strcasecmp(c, "HQ") == 0 || - strcasecmp(c, "CMYGray") == 0 || // HPLIP - strcasecmp(c, "ProRes1200") == 0 || // HPLIP - strcasecmp(c, "ImageREt2400") == 0 || // HPLIP - strcasestr(c, "unidir")) - properties->sets_high = 4; - else if (strcasecmp(c, "best") == 0 || - strcasecmp(c, "ProRes2400") == 0 || // HPLIP - strcasecmp(c, "monolowdetail") == 0) // Toshiba - properties->sets_high = 5; - - // Low/Draft quality - if (strcasecmp(c, "monolowdetail") == 0 || // Toshiba - strcasecmp(c, "3") == 0) - properties->sets_draft = 1; - else if (((p = strcasestr(c, "fast")) && strcasestr(p, "draft")) || - ((p = strcasestr(c, "high")) && strcasestr(p, "speed")) || - (strcasestr(c, "speed") && !strcasestr(c, "low"))) - properties->sets_draft = 2; - else if (strcasestr(c, "quick") || - (strcasestr(c, "fast") && - !(strncasecmp(c, "FastRes", 7) == 0 && isdigit(*(c + 7))))) - // HPLIP has FastRes600, FastRes1200, ... which are not draft - properties->sets_draft = 3; - else if (strcasecmp(c, "quick") == 0 || - strcasecmp(c, "fast") == 0 || - strcasestr(c, "draft") || - (strcasestr(c, "low") && !strcasestr(c, "slow")) || - strcasestr(c, "coarse")) - properties->sets_draft = 4; - else if (strcasecmp(c, "draft") == 0 || - strcasecmp(c, "low") == 0 || - strcasecmp(c, "coarse") == 0 || - strcasestr(c, "bidir")) - properties->sets_draft = 5; - - // Use high or low quality but not the extremes - if (strcasestr(c, "ultra") || - strcasestr(c, "very") || - strcasestr(c, "super")) - { - if (properties->sets_high > 1) - properties->sets_high --; - if (properties->sets_draft > 1) - properties->sets_draft --; - } - - // Normal quality - if (strcasestr(c, "automatic") || - strcasecmp(c, "none") == 0 || - strcasecmp(c, "4") == 0 || - strcasecmp(c, "FastRes1200") == 0) // HPLIP - properties->sets_normal = 1; - else if (strcasestr(c, "normal") || - strcasestr(c, "standard") || - strcasestr(c, "default") || - strcasecmp(c, "FastRes600") == 0) // HPLIP - properties->sets_normal = 2; - else if (strcasecmp(c, "normal") == 0 || - strcasecmp(c, "standard") == 0 || - strcasecmp(c, "default") == 0) - properties->sets_normal = 4; - } - - // Apply the weight factor for option/choice-name-related scores - properties->sets_high *= name_factor; - properties->sets_draft *= name_factor; - properties->sets_normal *= name_factor; - - // Determine influence of the options and choices on the print - // quality by how they change the output resolution compared to - // the base/default resolution - if (base_res_x && base_res_y) - { - // First, analyse the code snippet (PostScript, PJL) assigned - // to each choice of the option whether it sets resolution - if (option->choices[k].code && option->choices[k].code[0]) - { - // Assume code to be PostScript (also used for CUPS Raster) - preferred_bits = 0; - optheader = header; - if (ppdRasterExecPS(&optheader, &preferred_bits, - option->choices[k].code) == 0) - { - properties->res_x = optheader.HWResolution[0]; - properties->res_y = optheader.HWResolution[1]; - } - else - properties->res_x = properties->res_y = 0; // invalid - if (properties->res_x == 0 || properties->res_y == 0) - { - // Now try PJL - if ((p = strstr(option->choices[k].code, "SET")) && - isspace(*(p + 3)) && (p = strstr(p + 4, "RESOLUTION="))) - { - p += 11; - if (sscanf(p, "%dX%d", - &(properties->res_x), &(properties->res_y)) == 1) - properties->res_y = properties->res_x; - } - } - if (properties->res_x == 100 && properties->res_y == 100) - properties->res_x = properties->res_y = 0; // Code does not - // set resolution - } - else - properties->res_x = properties->res_y = 0; // invalid - - // Then parse the choice name whether it contains a - // resolution value (Must have "dpi", as otherwise can be - // something else, like a page size) - if ((properties->res_x == 0 || properties->res_y == 0) && - (p = strcasestr(c, "dpi")) != NULL) - { - if (p > c) - { - p --; - while (p > c && isspace(*p)) - p --; - if (p > c && isdigit(*p)) - { - char x; - while (p > c && isdigit(*p)) - p --; - if (p > c && (*p == 'x' || *p == 'X')) - p --; - while (p > c && isdigit(*p)) - p --; - while (!isdigit(*p)) - p ++; - if (sscanf(p, "%d%c%d", - &(properties->res_x), &x, &(properties->res_y)) == 2) - properties->res_y = properties->res_x; - } - } - } - - if (properties->res_x != 0 && properties->res_y != 0) - { - // Choice suggests to set the resolution - // Raising resolution compared to default? - m = (properties->res_x * properties->res_y) / - (base_res_x * base_res_y); - // No or small change -> Normal quality - if (m == 1) - properties->sets_normal += res_factor * 4; - // At least double the pixels -> High quality - else if (m == 2) - properties->sets_high += res_factor * 3; - else if (m > 2 && m <= 8) - properties->sets_high += res_factor * 4; - else if (m > 8 && m <= 32) - properties->sets_high += res_factor * 2; - else if (m > 32) - properties->sets_high += res_factor * 1; - else if (m < 1) - { - // Reducing resolution compared to default? - m = (base_res_x * base_res_y) / - (properties->res_x * properties->res_y); - // No or small change -> Normal quality - if (m == 1) - properties->sets_normal += res_factor * 1; - // At most half the pixels -> Draft quality - else if (m == 2) - properties->sets_draft += res_factor * 3; - else if (m > 2 && m < 8) - properties->sets_draft += res_factor * 4; - else if (m >= 8 && m < 32) - properties->sets_draft += res_factor * 2; - else if (m >= 32) - properties->sets_draft += res_factor * 1; - } - } - } - - // This option actually sets print quality - if (properties->sets_draft || properties->sets_high) - sets_quality = 1; - - // Add the properties of this choice - cupsArrayAdd(choice_properties, properties); - } - - // - // Find the best choice for each field of the color/quality preset - // grid - // - - for (pass = 0; pass < 3; pass ++) - { - for (k = 0; k < option->num_choices; k ++) - { - properties = cupsArrayIndex(choice_properties, k); - - // presets[0][0]: Mono/Draft - if (best_mono_draft >= 0 && - !properties->sets_color && - (!properties->sets_high || pass > 0)) - { - score = color_factor * properties->sets_mono + - properties->sets_draft; - if (score > best_mono_draft) - { - best_mono_draft = score; - best_mono_draft_ch = k; - } - } - - // presets[0][1]: Mono/Normal - if (best_mono_normal >= 0 && - !properties->sets_color && - (!properties->sets_draft || pass > 1) && - (!properties->sets_high || pass > 0)) - { - score = color_factor * properties->sets_mono + - properties->sets_normal; - if (score > best_mono_normal) - { - best_mono_normal = score; - best_mono_normal_ch = k; - } - } - - // presets[0][2]: Mono/High - if (best_mono_high >= 0 && - !properties->sets_color && - (!properties->sets_draft || pass > 0)) - { - score = color_factor * properties->sets_mono + - properties->sets_high; - if (score > best_mono_high) - { - best_mono_high = score; - best_mono_high_ch = k; - } - } - - // presets[1][0]: Color/Draft - if (best_color_draft >= 0 && - !properties->sets_mono && - (!properties->sets_high || pass > 0)) - { - score = color_factor * properties->sets_color + - properties->sets_draft; - if (score > best_color_draft) - { - best_color_draft = score; - best_color_draft_ch = k; - } - } - - // presets[1][1]: Color/Normal - if (best_color_normal >= 0 && - !properties->sets_mono && - (!properties->sets_draft || pass > 1) && - (!properties->sets_high || pass > 0)) - { - score = color_factor * properties->sets_color + - properties->sets_normal; - if (score > best_color_normal) - { - best_color_normal = score; - best_color_normal_ch = k; - } - } - - // presets[1][2]: Color/High - if (best_color_high >= 0 && - !properties->sets_mono && - (!properties->sets_draft || pass > 0)) - { - score = color_factor * properties->sets_color + - properties->sets_high; - if (score > best_color_high) - { - best_color_high = score; - best_color_high_ch = k; - } - } - } - // Block next passes for the presets where we are done - if (best_mono_draft_ch >= 0) - best_mono_draft = -1; - if (best_mono_normal_ch >= 0) - best_mono_normal = -1; - if (best_mono_high_ch >= 0) - best_mono_high = -1; - if (best_color_draft_ch >= 0) - best_color_draft = -1; - if (best_color_normal_ch >= 0) - best_color_normal = -1; - if (best_color_high_ch >= 0) - best_color_high = -1; - } - - // - // Content Optimization - print-content-optimize - // - - for (k = 0; k < option->num_choices; k ++) - { - properties = cupsArrayIndex(choice_properties, k); - c = option->choices[k].choice; - - // Vendor-specific options - if (strcasecmp(o, "ARCOType") == 0) // Sharp - { - if (strcasecmp(c, "COTDrawing") == 0) - { - properties->for_text = 3; - properties->for_graphics = 2; - properties->for_tg = 2; - } - else if (strcasecmp(c, "COTGraphics") == 0) - { - properties->for_graphics = 3; - properties->for_tg = 3; - } - else if (strcasecmp(c, "COTPhoto") == 0) - properties->for_photo = 3; - } - else if (strcasecmp(o, "HPRGBEmulation") == 0) // HP - { - if (strcasecmp(c, "DefaultSRGB") == 0) - properties->for_text = 3; - else if (strcasecmp(c, "VividSRGB") == 0) - { - properties->for_graphics = 3; - properties->for_tg = 3; - } - else if (strcasecmp(c, "PhotoSRGB") == 0) - properties->for_photo = 3; - } - else - // Generic choice names - { - if (strcasestr(c, "photo")) - properties->for_photo = 6; - else if (strcasecmp(c, "photo") == 0) - properties->for_photo = 7; - - if (strcasestr(c, "graphic")) - properties->for_graphics = 6; - else if (strcasecmp(c, "graphic") == 0 || - strcasecmp(c, "graphics") == 0) - properties->for_graphics = 7; - - if (strcasestr(c, "text")) - { - if (strcasestr(c, "graphic")) - properties->for_tg = 7; - else - properties->for_text = 6; - } - else if (strcasecmp(c, "text") == 0) - properties->for_text = 7; - - if (strcasestr(c, "presentation")) - { - properties->for_text = 4; - properties->for_graphics = 4; - properties->for_tg = 4; - } - else if (strcasecmp(c, "presentation") == 0) - { - properties->for_text = 5; - properties->for_graphics = 5; - properties->for_tg = 5; - } - - if (strcasestr(c, "lineart")) - { - properties->for_graphics = 2; - properties->for_tg = 2; - } - else if (strcasecmp(c, "lineart") == 0) - { - properties->for_graphics = 3; - properties->for_tg = 3; - } - - if (strcasestr(c, "drawing")) - { - properties->for_graphics = 4; - properties->for_tg = 4; - } - else if (strcasecmp(c, "drawing") == 0) - { - properties->for_graphics = 5; - properties->for_tg = 5; - } - - if (strcasestr(c, "natural")) - properties->for_photo = 2; - else if (strcasecmp(c, "natural") == 0) - properties->for_photo = 3; - - if (strcasestr(c, "vivid")) - { - properties->for_text = 2; - properties->for_graphics = 2; - properties->for_tg = 2; - } - else if (strcasecmp(c, "vivid") == 0) - { - properties->for_text = 3; - properties->for_graphics = 3; - properties->for_tg = 3; - } - } - - // We apply these optimizations only in high quality mode - // therefore we prefer settings for high quality - if (properties->sets_high && !properties->sets_draft) - { - if (properties->for_photo) - properties->for_photo += 10; - if (properties->for_graphics) - properties->for_graphics += 10; - if (properties->for_text) - properties->for_text += 10; - if (properties->for_tg) - properties->for_tg += 10; - } - - // - // Find the best choice for each field of the content optimize presets - // - - // Find best choice for each task - // optimize_presets[1]: Photo - if (properties->for_photo > best_photo) - { - best_photo = properties->for_photo; - best_photo_ch = k; - } - // optimize_presets[2]: Graphics - if (properties->for_graphics > best_graphics) - { - best_graphics = properties->for_graphics; - best_graphics_ch = k; - } - // optimize_presets[3]: Text - if (properties->for_text > best_text) - { - best_text = properties->for_text; - best_text_ch = k; - } - // optimize_presets[4]: Text and Graphics - if (properties->for_tg > best_tg) - { - best_tg = properties->for_tg; - best_tg_ch = k; - } - - // This option actually does content optimization - if (properties->for_text || properties->for_graphics || - properties->for_tg || properties->for_photo) - sets_optimization = 1; - } - - // - // Fill in the presets - // - - if (sets_color_mode || sets_quality) - { - // presets[0][0]: Mono/Draft - if (best_mono_draft_ch < 0) - best_mono_draft_ch = default_ch; - if (best_mono_draft_ch >= 0) - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_DRAFT] = - cupsAddOption(o, option->choices[best_mono_draft_ch].choice, - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_DRAFT], - &(pc->presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_DRAFT])); - - // presets[0][1]: Mono/Normal - if (best_mono_normal_ch < 0) - best_mono_normal_ch = default_ch; - if (best_mono_normal_ch >= 0) - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_NORMAL] = - cupsAddOption(o, option->choices[best_mono_normal_ch].choice, - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_NORMAL], - &(pc->presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_NORMAL])); - - // presets[0][2]: Mono/High - if (best_mono_high_ch < 0) - best_mono_high_ch = default_ch; - if (best_mono_high_ch >= 0) - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_HIGH] = - cupsAddOption(o, option->choices[best_mono_high_ch].choice, - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_HIGH], - &(pc->presets[PPD_PWG_PRINT_COLOR_MODE_MONOCHROME] - [PPD_PWG_PRINT_QUALITY_HIGH])); - - // presets[1][0]: Color/Draft - if (best_color_draft_ch < 0) - best_color_draft_ch = default_ch; - if (best_color_draft_ch >= 0) - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_DRAFT] = - cupsAddOption(o, option->choices[best_color_draft_ch].choice, - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_DRAFT], - &(pc->presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_DRAFT])); - - // presets[1][1]: Color/Normal - if (best_color_normal_ch < 0) - best_color_normal_ch = default_ch; - if (best_color_normal_ch >= 0) - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_NORMAL] = - cupsAddOption(o, option->choices[best_color_normal_ch].choice, - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_NORMAL], - &(pc->presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_NORMAL])); - - // presets[1][2]: Color/High - if (best_color_high_ch < 0) - best_color_high_ch = default_ch; - if (best_color_high_ch >= 0) - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_HIGH] = - cupsAddOption(o, option->choices[best_color_high_ch].choice, - pc->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_HIGH], - &(pc->presets[PPD_PWG_PRINT_COLOR_MODE_COLOR] - [PPD_PWG_PRINT_QUALITY_HIGH])); - - } - - if (sets_optimization) - { - - // optimize_presets[1]: Photo - if (best_photo_ch >= 0) - pc->num_optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_PHOTO] = - cupsAddOption - (o, option->choices[best_photo_ch].choice, - pc->num_optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_PHOTO], - &(pc->optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_PHOTO])); - - // optimize_presets[2]: Graphics - if (best_graphics_ch >= 0) - pc->num_optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS] = - cupsAddOption - (o, option->choices[best_graphics_ch].choice, - pc->num_optimize_presets - [PPD_PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS], - &(pc->optimize_presets - [PPD_PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS])); - - // optimize_presets[1]: Text - if (best_text_ch >= 0) - pc->num_optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT] = - cupsAddOption - (o, option->choices[best_text_ch].choice, - pc->num_optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT], - &(pc->optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT])); - - // optimize_presets[1]: Text and Graphics - if (best_tg_ch >= 0) - pc->num_optimize_presets - [PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS] = - cupsAddOption - (o, option->choices[best_tg_ch].choice, - pc->num_optimize_presets - [PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS], - &(pc->optimize_presets - [PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS])); - - } - - for (k = 0; k < option->num_choices; k ++) - free(cupsArrayIndex(choice_properties, k)); - cupsArrayDelete(choice_properties); - } - } -} - - -// -// 'ppdCacheDestroy()' - Free all memory used for PWG mapping data. -// - -void -ppdCacheDestroy(ppd_cache_t *pc) // I - PPD cache and mapping data -{ - int i, j; // Looping vars - pwg_map_t *map; // Current map - pwg_size_t *size; // Current size - - - // - // Range check input... - // - - if (!pc) - return; - - // - // Free memory as needed... - // - - if (pc->bins) - { - for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++) - { - free(map->pwg); - free(map->ppd); - } - - free(pc->bins); - } - - if (pc->sizes) - { - for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++) - { - free(size->map.pwg); - free(size->map.ppd); - } - - free(pc->sizes); - } - - free(pc->source_option); - - if (pc->sources) - { - for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++) - { - free(map->pwg); - free(map->ppd); - } - - free(pc->sources); - } - - if (pc->types) - { - for (i = pc->num_types, map = pc->types; i > 0; i --, map ++) - { - free(map->pwg); - free(map->ppd); - } - - free(pc->types); - } - - free(pc->custom_max_keyword); - free(pc->custom_min_keyword); - - free(pc->product); - cupsArrayDelete(pc->filters); - cupsArrayDelete(pc->prefilters); - cupsArrayDelete(pc->finishings); - - free(pc->charge_info_uri); - free(pc->password); - - cupsArrayDelete(pc->mandatory); - - cupsArrayDelete(pc->support_files); - - cupsArrayDelete(pc->strings); - - for (i = PPD_PWG_PRINT_COLOR_MODE_MONOCHROME; - i < PPD_PWG_PRINT_COLOR_MODE_MAX; i ++) - for (j = PPD_PWG_PRINT_QUALITY_DRAFT; j < PPD_PWG_PRINT_QUALITY_MAX; j ++) - if (pc->num_presets[i][j]) - cupsFreeOptions(pc->num_presets[i][j], pc->presets[i][j]); - - for (i = PPD_PWG_PRINT_CONTENT_OPTIMIZE_AUTO; - i < PPD_PWG_PRINT_CONTENT_OPTIMIZE_MAX; i ++) - if (pc->num_optimize_presets[i]) - cupsFreeOptions(pc->num_optimize_presets[i], pc->optimize_presets[i]); - - free(pc); -} - - -// -// 'ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD -// OutputBin. -// - -const char * // O - output-bin or NULL -ppdCacheGetBin( - ppd_cache_t *pc, // I - PPD cache and mapping data - const char *output_bin) // I - PPD OutputBin string -{ - int i; // Looping var - - - // - // Range check input... - - - if (!pc || !output_bin) - return (NULL); - - // - // Look up the OutputBin string... - // - - for (i = 0; i < pc->num_bins; i ++) - if (!_ppd_strcasecmp(output_bin, pc->bins[i].ppd) || - !_ppd_strcasecmp(output_bin, pc->bins[i].pwg)) - return (pc->bins[i].pwg); - - return (NULL); -} - - -// -// 'ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given -// IPP finishings value(s). -// - -int // O - New number of options -ppdCacheGetFinishingOptions( - ppd_cache_t *pc, // I - PPD cache and mapping data - ipp_t *job, // I - Job attributes or NULL - ipp_finishings_t value, // I - IPP finishings value of - // IPP_FINISHINGS_NONE - int num_options, // I - Number of options - cups_option_t **options) // IO - Options -{ - int i; // Looping var - ppd_pwg_finishings_t *f, // PWG finishings options - key; // Search key - ipp_attribute_t *attr; // Finishings attribute - cups_option_t *option; // Current finishings option - - - // - // Range check input... - // - - if (!pc || cupsArrayCount(pc->finishings) == 0 || !options || - (!job && value == IPP_FINISHINGS_NONE)) - return (num_options); - - // - // Apply finishing options... - // - - if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL) - { - int num_values = ippGetCount(attr); // Number of values - - for (i = 0; i < num_values; i ++) - { - key.value = (ipp_finishings_t)ippGetInteger(attr, i); - - if ((f = cupsArrayFind(pc->finishings, &key)) != NULL) - { - int j; // Another looping var - - for (j = f->num_options, option = f->options; j > 0; j --, option ++) - num_options = cupsAddOption(option->name, option->value, - num_options, options); - } - } - } - else if (value != IPP_FINISHINGS_NONE) - { - key.value = value; - - if ((f = cupsArrayFind(pc->finishings, &key)) != NULL) - { - int j; // Another looping var - - for (j = f->num_options, option = f->options; j > 0; j --, option ++) - num_options = cupsAddOption(option->name, option->value, - num_options, options); - } - } - - return (num_options); -} - - -// -// 'ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given -// PPD options. -// - -int // O - Number of finishings values -ppdCacheGetFinishingValues( - ppd_file_t *ppd, // I - Marked PPD file - ppd_cache_t *pc, // I - PPD cache and mapping data - int max_values, // I - Maximum number of finishings values - int *values) // O - Finishings values -{ - int i, // Looping var - num_values = 0; // Number of values - ppd_pwg_finishings_t *f; // Current finishings option - cups_option_t *option; // Current option - ppd_choice_t *choice; // Marked PPD choice - - - // - // Range check input... - // - - DEBUG_printf(("ppdCacheGetFinishingValues(ppd=%p, pc=%p, max_values=%d, values=%p)", - ppd, pc, max_values, values)); - - if (!ppd || !pc || max_values < 1 || !values) - { - DEBUG_puts("ppdCacheGetFinishingValues: Bad arguments, returning 0."); - return (0); - } - else if (!pc->finishings) - { - DEBUG_puts("ppdCacheGetFinishingValues: No finishings support, returning 0."); - return (0); - } - - // - // Go through the finishings options and see what is set... - // - - for (f = (ppd_pwg_finishings_t *)cupsArrayFirst(pc->finishings); - f; - f = (ppd_pwg_finishings_t *)cupsArrayNext(pc->finishings)) - { - DEBUG_printf(("ppdCacheGetFinishingValues: Checking %d (%s)", - (int)f->value, ippEnumString("finishings", (int)f->value))); - - for (i = f->num_options, option = f->options; i > 0; i --, option ++) - { - DEBUG_printf(("ppdCacheGetFinishingValues: %s=%s?", - option->name, option->value)); - - if ((choice = ppdFindMarkedChoice(ppd, option->name)) == NULL || - _ppd_strcasecmp(option->value, choice->choice)) - { - DEBUG_puts("ppdCacheGetFinishingValues: NO"); - break; - } - } - - if (i == 0) - { - DEBUG_printf(("ppdCacheGetFinishingValues: Adding %d (%s)", - (int)f->value, ippEnumString("finishings", (int)f->value))); - - values[num_values ++] = (int)f->value; - - if (num_values >= max_values) - break; - } - } - - if (num_values == 0) - { - // - // Always have at least "finishings" = 'none'... - // - - DEBUG_puts("ppdCacheGetFinishingValues: Adding 3 (none)."); - values[0] = IPP_FINISHINGS_NONE; - num_values ++; - } - - DEBUG_printf(("ppdCacheGetFinishingValues: Returning %d.", num_values)); - - return (num_values); -} - - -// -// 'ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job -// attributes or a keyword string. -// - -const char * // O - PPD InputSlot or NULL -ppdCacheGetInputSlot( - ppd_cache_t *pc, // I - PPD cache and mapping data - ipp_t *job, // I - Job attributes or NULL - const char *keyword) // I - Keyword string or NULL -{ - // - // Range check input... - // - - if (!pc || pc->num_sources == 0 || (!job && !keyword)) - return (NULL); - - if (job && !keyword) - { - // - // Lookup the media-col attribute and any media-source found there... - // - - ipp_attribute_t *media_col, // media-col attribute - *media_source; // media-source attribute - pwg_size_t size; // Dimensional size - int margins_set; // Were the margins set? - - media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION); - if (media_col && - (media_source = ippFindAttribute(ippGetCollection(media_col, 0), - "media-source", - IPP_TAG_KEYWORD)) != NULL) - { - // - // Use the media-source value from media-col... - // - - keyword = ippGetString(media_source, 0, NULL); - } - else if (pwgInitSize(&size, job, &margins_set)) - { - // - // For media <= 5x7, look for a photo tray... - // - - if (size.width <= (5 * 2540) && size.length <= (7 * 2540)) - keyword = "photo"; - } - } - - if (keyword) - { - int i; // Looping var - - for (i = 0; i < pc->num_sources; i ++) - if (!_ppd_strcasecmp(keyword, pc->sources[i].pwg)) - return (pc->sources[i].ppd); - } - - return (NULL); -} - - -// -// 'ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job -// attributes or a keyword string. -// - -const char * // O - PPD MediaType or NULL -ppdCacheGetMediaType( - ppd_cache_t *pc, // I - PPD cache and mapping data - ipp_t *job, // I - Job attributes or NULL - const char *keyword) // I - Keyword string or NULL -{ - // - // Range check input... - // - - if (!pc || pc->num_types == 0 || (!job && !keyword)) - return (NULL); - - if (job && !keyword) - { - // - // Lookup the media-col attribute and any media-source found there... - // - - ipp_attribute_t *media_col, // media-col attribute - *media_type; // media-type attribute - - media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION); - if (media_col) - { - if ((media_type = ippFindAttribute(ippGetCollection(media_col, 0), - "media-type", - IPP_TAG_KEYWORD)) == NULL) - media_type = ippFindAttribute(ippGetCollection(media_col, 0), - "media-type", IPP_TAG_NAME); - - if (media_type) - keyword = ippGetString(media_type, 0, NULL); - } - } - - if (keyword) - { - int i; // Looping var - - for (i = 0; i < pc->num_types; i ++) - if (!_ppd_strcasecmp(keyword, pc->types[i].pwg)) - return (pc->types[i].ppd); - } - - return (NULL); -} - - -// -// 'ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword -// string. -// - -const char * // O - PPD OutputBin or NULL -ppdCacheGetOutputBin( - ppd_cache_t *pc, // I - PPD cache and mapping data - const char *output_bin) // I - Keyword string -{ - int i; // Looping var - - - // - // Range check input... - // - - if (!pc || !output_bin) - return (NULL); - - // - // Look up the OutputBin string... - // - - for (i = 0; i < pc->num_bins; i ++) - if (!_ppd_strcasecmp(output_bin, pc->bins[i].pwg)) - return (pc->bins[i].ppd); - - return (NULL); -} - - -// -// 'ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job -// attributes or a keyword string. -// - -const char * // O - PPD PageSize or NULL -ppdCacheGetPageSize( - ppd_cache_t *pc, // I - PPD cache and mapping data - ipp_t *job, // I - Job attributes or NULL - const char *keyword, // I - Keyword string or NULL - int *exact) // O - 1 if exact match, 0 otherwise -{ - int i, j; // Looping vars - pwg_size_t *size, // Current size - *variant, // Page size variant - *closest, // Closest size - jobsize; // Size data from job - int margins_set, // Were the margins set? - dwidth, // Difference in width - dlength, // Difference in length - dleft, // Difference in left margins - dright, // Difference in right margins - dbottom, // Difference in bottom margins - dtop, // Difference in top margins - dmin, // Minimum difference - dclosest; // Closest difference - const char *ppd_name; // PPD media name - - - DEBUG_printf(("ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)", - pc, job, keyword, exact)); - - // - // Range check input... - // - - if (!pc || (!job && !keyword)) - return (NULL); - - if (exact) - *exact = 0; - - ppd_name = keyword; - - if (job) - { - // - // Try getting the PPD media name from the job attributes... - // - - ipp_attribute_t *attr; // Job attribute - - if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL) - if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL) - attr = ippFindAttribute(job, "media", IPP_TAG_ZERO); - -#ifdef DEBUG - if (attr) - DEBUG_printf(("1ppdCacheGetPageSize: Found attribute %s (%s)", - ippGetName(attr), ippTagString(ippGetValueTag(attr)))); - else - DEBUG_puts("1ppdCacheGetPageSize: Did not find media attribute."); -#endif // DEBUG - - if (attr && (ippGetValueTag(attr) == IPP_TAG_NAME || - ippGetValueTag(attr) == IPP_TAG_KEYWORD)) - ppd_name = ippGetString(attr, 0, NULL); - } - - DEBUG_printf(("1ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name)); - - if (ppd_name) - { - // - // Try looking up the named PPD size first... - // - - for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++) - { - DEBUG_printf(("2ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]", - (int)(size - pc->sizes), size->map.pwg, size->map.ppd)); - - if (!_ppd_strcasecmp(ppd_name, size->map.ppd) || - !_ppd_strcasecmp(ppd_name, size->map.pwg)) - { - if (exact) - *exact = 1; - - DEBUG_printf(("1ppdCacheGetPageSize: Returning \"%s\"", ppd_name)); - - return (size->map.ppd); - } - } - } - - if (job && !keyword) - { - // - // Get the size using media-col or media, with the preference being - // media-col. - // - - if (!pwgInitSize(&jobsize, job, &margins_set)) - return (NULL); - } - else - { - // - // Get the size using a media keyword... - // - - pwg_media_t *media; // Media definition - - - if ((media = pwgMediaForPWG(keyword)) == NULL) - if ((media = pwgMediaForLegacy(keyword)) == NULL) - if ((media = pwgMediaForPPD(keyword)) == NULL) - return (NULL); - - jobsize.width = media->width; - jobsize.length = media->length; - margins_set = 0; - } - - // - // Now that we have the dimensions and possibly the margins, look at the - // available sizes and find the match... - // - - closest = NULL; - dclosest = dmin = 999999999; - - if (!ppd_name || _ppd_strncasecmp(ppd_name, "Custom.", 7) || - _ppd_strncasecmp(ppd_name, "custom_", 7)) - { - for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++) - { - // - // Adobe uses a size matching algorithm with an epsilon of 5 points, which - // is just about 176/2540ths... - // - - dwidth = size->width - jobsize.width; - dlength = size->length - jobsize.length; - - if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176) - continue; - - if (margins_set) - { - // - // Check not only the base size (like "A4") but also variants (like - // "A4.Borderless"). We check only the margins and orientation but do - // not re-check the size. - // - - for (j = pc->num_sizes, variant = pc->sizes; j > 0; j --, variant ++) - { - if (!strcmp(size->map.ppd, variant->map.ppd) || - (!strncmp(size->map.ppd, variant->map.ppd, - strlen(size->map.ppd)) && - (strlen(variant->map.ppd) > strlen(size->map.ppd) + 1) && - variant->map.ppd[strlen(size->map.ppd)] == '.')) - { - // - // Found a variant (or the base size) - // - - // - // First check orientation (we do not want ".Transverse" variants) - // - - if ((size->length - size->width) * - (variant->length - variant->width) < 0) - continue; - - // - // Borderless page size variant, use it only if the job requests - // borderless - // - - if (strchr(variant->map.ppd, '.') && - variant->left == 0 && variant->right == 0 && - variant->top == 0 && variant->bottom == 0 && - (jobsize.left != 0 || jobsize.right != 0 || - jobsize.top != 0 || jobsize.bottom != 0)) - continue; - - // - // Use a tighter epsilon of 1 point (35/2540ths) for margins... - // - - dleft = variant->left - jobsize.left; - dright = variant->right - jobsize.right; - dtop = variant->top - jobsize.top; - dbottom = variant->bottom - jobsize.bottom; - - if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 || - dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35) - { - dleft = dleft < 0 ? -dleft : dleft; - dright = dright < 0 ? -dright : dright; - dbottom = dbottom < 0 ? -dbottom : dbottom; - dtop = dtop < 0 ? -dtop : dtop; - // In the sum we do a slight penalization of the variants to - // prefer the base if it has the same margins) - dmin = dleft + dright + dbottom + dtop + - (strchr(variant->map.ppd, '.') ? 1 : 0); - - if (dmin < dclosest) - { - dclosest = dmin; - closest = variant; - } - } - else - { - dmin = 0; - size = variant; - break; - } - } - } - if (dmin) - continue; - } - - if (exact) - *exact = 1; - - DEBUG_printf(("1ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd)); - - return (size->map.ppd); - } - } - - if (closest) - { - DEBUG_printf(("1ppdCacheGetPageSize: Returning \"%s\" (closest)", - closest->map.ppd)); - - return (closest->map.ppd); - } - - // - // If we get here we need to check for custom page size support... - // - - if (jobsize.width >= pc->custom_min_width && - jobsize.width <= pc->custom_max_width && - jobsize.length >= pc->custom_min_length && - jobsize.length <= pc->custom_max_length) - { - // - // In range, format as Custom.WWWWxLLLL (points). - // - - snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d", - (int)PWG_TO_POINTS(jobsize.width), - (int)PWG_TO_POINTS(jobsize.length)); - - if (margins_set && exact) - { - dleft = pc->custom_size.left - jobsize.left; - dright = pc->custom_size.right - jobsize.right; - dtop = pc->custom_size.top - jobsize.top; - dbottom = pc->custom_size.bottom - jobsize.bottom; - - if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 && - dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35) - *exact = 1; - } - else if (exact) - *exact = 1; - - DEBUG_printf(("1ppdCacheGetPageSize: Returning \"%s\" (custom)", - pc->custom_ppd_size)); - - return (pc->custom_ppd_size); - } - - // - // No custom page size support or the size is out of range - return NULL. - // - - DEBUG_puts("1ppdCacheGetPageSize: Returning NULL"); - - return (NULL); -} - - -// -// 'ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize. -// - -pwg_size_t * // O - PWG size or NULL -ppdCacheGetSize( - ppd_cache_t *pc, // I - PPD cache and mapping data - const char *page_size) // I - PPD PageSize -{ - int i; // Looping var - pwg_media_t *media; // Media - pwg_size_t *size; // Current size - - - // - // Range check input... - // - - if (!pc || !page_size) - return (NULL); - - if (!_ppd_strncasecmp(page_size, "Custom.", 7)) - { - // - // Custom size; size name can be one of the following: - // - // Custom.WIDTHxLENGTHin - Size in inches - // Custom.WIDTHxLENGTHft - Size in feet - // Custom.WIDTHxLENGTHcm - Size in centimeters - // Custom.WIDTHxLENGTHmm - Size in millimeters - // Custom.WIDTHxLENGTHm - Size in meters - // Custom.WIDTHxLENGTH[pt] - Size in points - // - - double w, l; // Width and length of page - char *ptr; // Pointer into PageSize - struct lconv *loc; // Locale data - - loc = localeconv(); - w = (float)_ppdStrScand(page_size + 7, &ptr, loc); - if (!ptr || *ptr != 'x') - return (NULL); - - l = (float)_ppdStrScand(ptr + 1, &ptr, loc); - if (!ptr) - return (NULL); - - if (!_ppd_strcasecmp(ptr, "in")) - { - w *= 2540.0; - l *= 2540.0; - } - else if (!_ppd_strcasecmp(ptr, "ft")) - { - w *= 12.0 * 2540.0; - l *= 12.0 * 2540.0; - } - else if (!_ppd_strcasecmp(ptr, "mm")) - { - w *= 100.0; - l *= 100.0; - } - else if (!_ppd_strcasecmp(ptr, "cm")) - { - w *= 1000.0; - l *= 1000.0; - } - else if (!_ppd_strcasecmp(ptr, "m")) - { - w *= 100000.0; - l *= 100000.0; - } - else - { - w *= 2540.0 / 72.0; - l *= 2540.0 / 72.0; - } - - pc->custom_size.width = (int)w; - pc->custom_size.length = (int)l; - - return (&(pc->custom_size)); - } - - // - // Not a custom size - look it up... - // - - for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++) - if (!_ppd_strcasecmp(page_size, size->map.ppd) || - !_ppd_strcasecmp(page_size, size->map.pwg)) - return (size); - - // - // Look up standard sizes... - // - - if ((media = pwgMediaForPPD(page_size)) == NULL) - if ((media = pwgMediaForLegacy(page_size)) == NULL) - media = pwgMediaForPWG(page_size); - - if (media) - { - pc->custom_size.width = media->width; - pc->custom_size.length = media->length; - - return (&(pc->custom_size)); - } - - return (NULL); -} - - -// -// 'ppdCacheGetSource()' - Get the PWG media-source associated with a PPD -// InputSlot. -// - -const char * // O - PWG media-source keyword -ppdCacheGetSource( - ppd_cache_t *pc, // I - PPD cache and mapping data - const char *input_slot) // I - PPD InputSlot -{ - int i; // Looping var - pwg_map_t *source; // Current source - - - // - // Range check input... - // - - if (!pc || !input_slot) - return (NULL); - - for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++) - if (!_ppd_strcasecmp(input_slot, source->ppd) || - !_ppd_strcasecmp(input_slot, source->pwg)) - return (source->pwg); - - return (NULL); -} - - -// -// 'ppdCacheGetType()' - Get the PWG media-type associated with a PPD -// MediaType. -// - -const char * // O - PWG media-type keyword -ppdCacheGetType( - ppd_cache_t *pc, // I - PPD cache and mapping data - const char *media_type) // I - PPD MediaType -{ - int i; // Looping var - pwg_map_t *type; // Current type - - - // - // Range check input... - // - - if (!pc || !media_type) - return (NULL); - - for (i = pc->num_types, type = pc->types; i > 0; i --, type ++) - if (!_ppd_strcasecmp(media_type, type->ppd) || - !_ppd_strcasecmp(media_type, type->pwg)) - return (type->pwg); - - return (NULL); -} - - -// -// 'ppdCacheWriteFile()' - Write PWG mapping data to a file. -// - -int // O - 1 on success, 0 on failure -ppdCacheWriteFile( - ppd_cache_t *pc, // I - PPD cache and mapping data - const char *filename, // I - File to write - ipp_t *attrs) // I - Attributes to write, if any -{ - int i, j, k; // Looping vars - cups_file_t *fp; // Output file - pwg_size_t *size; // Current size - pwg_map_t *map; // Current map - ppd_pwg_finishings_t *f; // Current finishing option - cups_option_t *option; // Current option - const char *value; // String value - char newfile[1024]; // New filename - - - // - // Range check input... - // - - if (!pc || !filename) - { - set_error(strerror(EINVAL), 0); - return (0); - } - - // - // Open the file and write with compression... - // - - snprintf(newfile, sizeof(newfile), "%s.N", filename); - if ((fp = cupsFileOpen(newfile, "w9")) == NULL) - { - set_error(strerror(errno), 0); - return (0); - } - - // - // Standard header... - // - - cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", PPD_CACHE_VERSION); - - // - // Output bins... - // - - if (pc->num_bins > 0) - { - cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins); - for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++) - cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd); - } - - // - // Media sizes... - // - - cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes); - for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++) - cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg, - size->map.ppd, size->width, size->length, size->left, - size->bottom, size->right, size->top); - if (pc->custom_max_width > 0) - cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n", - pc->custom_max_width, pc->custom_max_length, - pc->custom_min_width, pc->custom_min_length, - pc->custom_size.left, pc->custom_size.bottom, - pc->custom_size.right, pc->custom_size.top); - - // - // Media sources... - // - - if (pc->source_option) - cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option); - - if (pc->num_sources > 0) - { - cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources); - for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++) - cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd); - } - - // - // Media types... - // - - if (pc->num_types > 0) - { - cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types); - for (i = pc->num_types, map = pc->types; i > 0; i --, map ++) - cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd); - } - - // - // Presets... - // - - for (i = PPD_PWG_PRINT_COLOR_MODE_MONOCHROME; - i < PPD_PWG_PRINT_COLOR_MODE_MAX; i ++) - for (j = PPD_PWG_PRINT_QUALITY_DRAFT; j < PPD_PWG_PRINT_QUALITY_MAX; j ++) - if (pc->num_presets[i][j]) - { - cupsFilePrintf(fp, "Preset %d %d", i, j); - for (k = pc->num_presets[i][j], option = pc->presets[i][j]; - k > 0; - k --, option ++) - cupsFilePrintf(fp, " %s=%s", option->name, option->value); - cupsFilePutChar(fp, '\n'); - } - - // - // Optimization Presets... - // - - for (i = PPD_PWG_PRINT_CONTENT_OPTIMIZE_AUTO; - i < PPD_PWG_PRINT_CONTENT_OPTIMIZE_MAX; i ++) - if (pc->num_optimize_presets[i]) - { - cupsFilePrintf(fp, "OptimizePreset %d", i); - for (k = pc->num_optimize_presets[i], option = pc->optimize_presets[i]; - k > 0; - k --, option ++) - cupsFilePrintf(fp, " %s=%s", option->name, option->value); - cupsFilePutChar(fp, '\n'); - } - - // - // Duplex/sides... - // - - if (pc->sides_option) - cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option); - - if (pc->sides_1sided) - cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided); - - if (pc->sides_2sided_long) - cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long); - - if (pc->sides_2sided_short) - cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short); - - // - // Product, cupsFilter, cupsFilter2, and cupsPreFilter... - // - - if (pc->product) - cupsFilePutConf(fp, "Product", pc->product); - - for (value = (const char *)cupsArrayFirst(pc->filters); - value; - value = (const char *)cupsArrayNext(pc->filters)) - cupsFilePutConf(fp, "Filter", value); - - for (value = (const char *)cupsArrayFirst(pc->prefilters); - value; - value = (const char *)cupsArrayNext(pc->prefilters)) - cupsFilePutConf(fp, "PreFilter", value); - - cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false"); - - // - // Finishing options... - // - - for (f = (ppd_pwg_finishings_t *)cupsArrayFirst(pc->finishings); - f; - f = (ppd_pwg_finishings_t *)cupsArrayNext(pc->finishings)) - { - cupsFilePrintf(fp, "Finishings %d", f->value); - for (i = f->num_options, option = f->options; i > 0; i --, option ++) - cupsFilePrintf(fp, " %s=%s", option->name, option->value); - cupsFilePutChar(fp, '\n'); - } - - for (value = (const char *)cupsArrayFirst(pc->templates); value; value = (const char *)cupsArrayNext(pc->templates)) - cupsFilePutConf(fp, "FinishingTemplate", value); - - // - // Max copies... - // - - cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies); - - // - // Accounting/quota/PIN/managed printing values... - // - - if (pc->charge_info_uri) - cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri); - - cupsFilePrintf(fp, "JobAccountId %s\n", pc->account_id ? "true" : "false"); - cupsFilePrintf(fp, "JobAccountingUserId %s\n", - pc->accounting_user_id ? "true" : "false"); - - if (pc->password) - cupsFilePutConf(fp, "JobPassword", pc->password); - - for (value = (char *)cupsArrayFirst(pc->mandatory); - value; - value = (char *)cupsArrayNext(pc->mandatory)) - cupsFilePutConf(fp, "Mandatory", value); - - // - // Support files... - // - - for (value = (char *)cupsArrayFirst(pc->support_files); - value; - value = (char *)cupsArrayNext(pc->support_files)) - cupsFilePutConf(fp, "SupportFile", value); - - // - // IPP attributes, if any... - // - - if (attrs) - { - cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs)); - - ippSetState(attrs, IPP_STATE_IDLE); - ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs); - } - - // - // Close and return... - // - - if (cupsFileClose(fp)) - { - unlink(newfile); - return (0); - } - - unlink(filename); - return (!rename(newfile, filename)); -} - - -// -// 'ppdPwgInputSlotForSource()' - Get the InputSlot name for the given PWG -// media-source. -// - -const char * // O - InputSlot name -ppdPwgInputSlotForSource( - const char *media_source, // I - PWG media-source - char *name, // I - Name buffer - size_t namesize) // I - Size of name buffer -{ - // - // Range check input... - // - - if (!media_source || !name || namesize < PPD_MAX_NAME) - return (NULL); - - if (_ppd_strcasecmp(media_source, "main")) - strlcpy(name, "Cassette", namesize); - else if (_ppd_strcasecmp(media_source, "alternate")) - strlcpy(name, "Multipurpose", namesize); - else if (_ppd_strcasecmp(media_source, "large-capacity")) - strlcpy(name, "LargeCapacity", namesize); - else if (_ppd_strcasecmp(media_source, "bottom")) - strlcpy(name, "Lower", namesize); - else if (_ppd_strcasecmp(media_source, "middle")) - strlcpy(name, "Middle", namesize); - else if (_ppd_strcasecmp(media_source, "top")) - strlcpy(name, "Upper", namesize); - else if (_ppd_strcasecmp(media_source, "rear")) - strlcpy(name, "Rear", namesize); - else if (_ppd_strcasecmp(media_source, "side")) - strlcpy(name, "Side", namesize); - else if (_ppd_strcasecmp(media_source, "envelope")) - strlcpy(name, "Envelope", namesize); - else if (_ppd_strcasecmp(media_source, "main-roll")) - strlcpy(name, "Roll", namesize); - else if (_ppd_strcasecmp(media_source, "alternate-roll")) - strlcpy(name, "Roll2", namesize); - else - ppdPwgPpdizeName(media_source, name, namesize); - - return (name); -} - - -// -// 'ppdPwgMediaTypeForType()' - Get the MediaType name for the given PWG -// media-type. -// - -const char * // O - MediaType name -ppdPwgMediaTypeForType( - const char *media_type, // I - PWG media-type - char *name, // I - Name buffer - size_t namesize) // I - Size of name buffer -{ - // - // Range check input... - // - - if (!media_type || !name || namesize < PPD_MAX_NAME) - return (NULL); - - if (_ppd_strcasecmp(media_type, "auto")) - strlcpy(name, "Auto", namesize); - else if (_ppd_strcasecmp(media_type, "cardstock")) - strlcpy(name, "Cardstock", namesize); - else if (_ppd_strcasecmp(media_type, "envelope")) - strlcpy(name, "Envelope", namesize); - else if (_ppd_strcasecmp(media_type, "photographic-glossy")) - strlcpy(name, "Glossy", namesize); - else if (_ppd_strcasecmp(media_type, "photographic-high-gloss")) - strlcpy(name, "HighGloss", namesize); - else if (_ppd_strcasecmp(media_type, "photographic-matte")) - strlcpy(name, "Matte", namesize); - else if (_ppd_strcasecmp(media_type, "stationery")) - strlcpy(name, "Plain", namesize); - else if (_ppd_strcasecmp(media_type, "stationery-coated")) - strlcpy(name, "Coated", namesize); - else if (_ppd_strcasecmp(media_type, "stationery-inkjet")) - strlcpy(name, "Inkjet", namesize); - else if (_ppd_strcasecmp(media_type, "stationery-letterhead")) - strlcpy(name, "Letterhead", namesize); - else if (_ppd_strcasecmp(media_type, "stationery-preprinted")) - strlcpy(name, "Preprinted", namesize); - else if (_ppd_strcasecmp(media_type, "transparency")) - strlcpy(name, "Transparency", namesize); - else - ppdPwgPpdizeName(media_type, name, namesize); - - return (name); -} - - -// -// 'ppdPwgPageSizeForMedia()' - Get the PageSize name for the given media. -// - -const char * // O - PageSize name -ppdPwgPageSizeForMedia( - pwg_media_t *media, // I - Media - char *name, // I - PageSize name buffer - size_t namesize) // I - Size of name buffer -{ - const char *sizeptr, // Pointer to size in PWG name - *dimptr; // Pointer to dimensions in PWG name - - - // - // Range check input... - // - - if (!media || !name || namesize < PPD_MAX_NAME) - return (NULL); - - // - // Copy or generate a PageSize name... - // - - if (media->ppd) - { - // - // Use a standard Adobe name... - // - - strlcpy(name, media->ppd, namesize); - } - else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) || - (sizeptr = strchr(media->pwg, '_')) == NULL || - (dimptr = strchr(sizeptr + 1, '_')) == NULL || - (size_t)(dimptr - sizeptr) > namesize) - { - // - // Use a name of the form "wNNNhNNN"... - // - - snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width), - (int)PWG_TO_POINTS(media->length)); - } - else - { - // - // Copy the size name from class_sizename_dimensions... - // - - memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1)); - name[dimptr - sizeptr - 1] = '\0'; - } - - return (name); -} - - -// -// 'ppd_pwg_add_finishing()' - Add a finishings value. -// - -static void -ppd_pwg_add_finishing( - cups_array_t *finishings, // I - Finishings array - ipp_finishings_t template, // I - Finishing template - const char *name, // I - PPD option - const char *value) // I - PPD choice -{ - ppd_pwg_finishings_t *f; // New finishings value - - - if ((f = (ppd_pwg_finishings_t *)calloc(1, sizeof(ppd_pwg_finishings_t))) != - NULL) - { - f->value = template; - f->num_options = cupsAddOption(name, value, 0, &f->options); - - cupsArrayAdd(finishings, f); - } -} - - -// -// 'ppd_pwg_add_message()' - Add a message to the PPD cached strings. -// - -static void -ppd_pwg_add_message(cups_array_t *a, // I - Message catalog - const char *msg, // I - Message identifier - const char *str) // I - Localized string -{ - _ppd_message_t *m; // New message - - - if ((m = calloc(1, sizeof(_ppd_message_t))) != NULL) - { - m->msg = strdup(msg); - m->str = strdup(str); - cupsArrayAdd(a, m); - } -} - - -// -// 'ppd_pwg_compare_finishings()' - Compare two finishings values. -// - -static int // O - Result of comparison -ppd_pwg_compare_finishings( - ppd_pwg_finishings_t *a, // I - First finishings value - ppd_pwg_finishings_t *b) // I - Second finishings value -{ - return ((int)b->value - (int)a->value); -} - - -// -// 'ppd_pwg_free_finishings()' - Free a finishings value. -// - -static void -ppd_pwg_free_finishings( - ppd_pwg_finishings_t *f) // I - Finishings value -{ - cupsFreeOptions(f->num_options, f->options); - free(f); -} - - -// -// 'ppdPwgPpdizeName()' - Convert an IPP keyword to a PPD keyword. -// - -void -ppdPwgPpdizeName(const char *ipp, // I - IPP keyword - char *name, // I - Name buffer - size_t namesize) // I - Size of name buffer -{ - char *ptr, // Pointer into name buffer - *end; // End of name buffer - - - if (!ipp) - { - *name = '\0'; - return; - } - - *name = (char)toupper(*ipp++); - - for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;) - { - if (*ipp == '-' && _ppd_isalnum(ipp[1])) - { - ipp ++; - *ptr++ = (char)toupper(*ipp++ & 255); - } - else - *ptr++ = *ipp++; - } - - *ptr = '\0'; -} - - -// -// 'ppdPwgPpdizeResolution()' - Convert PWG resolution values to PPD values. -// - -void -ppdPwgPpdizeResolution( - ipp_attribute_t *attr, // I - Attribute to convert - int element, // I - Element to convert - int *xres, // O - X resolution in DPI - int *yres, // O - Y resolution in DPI - char *name, // I - Name buffer - size_t namesize) // I - Size of name buffer -{ - ipp_res_t units; // Units for resolution - - - *xres = ippGetResolution(attr, element, yres, &units); - - if (units == IPP_RES_PER_CM) - { - *xres = (int)(*xres * 2.54); - *yres = (int)(*yres * 2.54); - } - - if (name && namesize > 4) - { - if (*xres == *yres) - snprintf(name, namesize, "%ddpi", *xres); - else - snprintf(name, namesize, "%dx%ddpi", *xres, *yres); - } -} - - -// -// 'ppdPwgUnppdizeName()' - Convert a PPD keyword to a lowercase IPP keyword. -// - -void -ppdPwgUnppdizeName(const char *ppd, // I - PPD keyword - char *name, // I - Name buffer - size_t namesize, // I - Size of name buffer - const char *dashchars)// I - Characters to be replaced by - // dashes or NULL to replace all - // non-alphanumeric characters -{ - char *ptr, // Pointer into name buffer - *end; // End of name buffer - int nodash = 1; // Next char in IPP name cannot be a - // dash (first char or after a dash) - int firstchar = 1; - - - if (_ppd_islower(*ppd)) - { - // - // Already lowercase name, use as-is? - // - - const char *ppdptr; // Pointer into PPD keyword - - for (ppdptr = ppd + 1; *ppdptr; ppdptr ++) - if (_ppd_isupper(*ppdptr) || - (dashchars && strchr(dashchars, *ppdptr)) || - (!dashchars && !_ppd_isalnum(*ppdptr)) || - (*ppdptr == '-' && *(ppdptr - 1) == '-') || - (*ppdptr == '-' && *(ppdptr + 1) == '\0')) - break; - - if (!*ppdptr) - { - strlcpy(name, ppd, namesize); - return; - } - } - - for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++) - { - if (_ppd_isalnum(*ppd)) - { - *ptr++ = (char)tolower(*ppd & 255); - nodash = 0; - } - else if (*ppd == '-' || (dashchars && strchr(dashchars, *ppd)) || - (!dashchars && !_ppd_isalnum(*ppd))) - { - // Set a dash only if previous char is no dash, and as the first - // character only if it is the sign of a negative number - if (nodash == 0 || (firstchar && *ppd == '-' && isdigit(*(ppd + 1)))) - { - *ptr++ = '-'; - nodash = 1; - } - } - else - { - *ptr++ = *ppd; - nodash = 0; - } - - if (nodash == 0) - { - if (!_ppd_isupper(*ppd) && _ppd_isalnum(*ppd) && - _ppd_isupper(ppd[1]) && ptr < end) - { - *ptr++ = '-'; - nodash = 1; - } - else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255)) - { - *ptr++ = '-'; - nodash = 1; - } - } - - firstchar = 0; - } - - // Remove trailing dashes - while (ptr > name && *(ptr - 1) == '-') - ptr --; - - *ptr = '\0'; -} diff --git a/ppd/ppd-collection.cxx b/ppd/ppd-collection.cxx deleted file mode 100644 index 68ea4db18..000000000 --- a/ppd/ppd-collection.cxx +++ /dev/null @@ -1,3166 +0,0 @@ -// -// PPD collection support for libppd. -// -// This program handles listing and installing static PPD files, PPD files -// created from driver information files, and dynamically generated PPD files -// using driver helper programs. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include -#include "ppd.h" -#include "ppdc.h" -#include "file-private.h" -#include "array-private.h" -#include -#include - - -// -// Constants... -// - -#define TAR_BLOCK 512 // Number of bytes in a block -#define TAR_BLOCKS 10 // Blocking factor - -#define TAR_MAGIC "ustar" // 5 chars and a null -#define TAR_VERSION "00" // POSIX tar version -#define TAR_OLDGNU_MAGIC "ustar " - -#define TAR_OLDNORMAL '\0' // Normal disk file, Unix compat -#define TAR_NORMAL '0' // Normal disk file -#define TAR_LINK '1' // Link to previously dumped file -#define TAR_SYMLINK '2' // Symbolic link -#define TAR_CHR '3' // Character special file -#define TAR_BLK '4' // Block special file -#define TAR_DIR '5' // Directory -#define TAR_FIFO '6' // FIFO special file -#define TAR_CONTIG '7' // Contiguous file - - -// -// PPD information structures... -// - -typedef union // **** TAR record format **** -{ - unsigned char all[TAR_BLOCK]; // Raw data block - struct - { - char pathname[100], // Destination path - mode[8], // Octal file permissions - uid[8], // Octal user ID - gid[8], // Octal group ID - size[12], // Octal size in bytes - mtime[12], // Octal modification time - chksum[8], // Octal checksum value - linkflag, // File type - linkname[100], // Source path for link - magic[6], // Magic string - version[2], // Format version - uname[32], // User name - gname[32], // Group name - devmajor[8], // Octal device major number - devminor[8], // Octal device minor number - prefix[155]; // Prefix for long filenames - } header; -} tar_rec_t; - -typedef struct -{ - cups_array_t *Inodes; // Inodes of directories we've visited - cups_array_t *PPDsByName, - // PPD files sorted by filename and name - *PPDsByMakeModel; - // PPD files sorted by make and model - int ChangedPPD; // Did we change the PPD database? -} ppd_list_t; - -//typedef int (*cupsd_compare_func_t)(const void *, const void *); - - -// -// Globals... -// - -static const char * const PPDTypes[] = // ppd-type values - { - "postscript", - "pdf", - "raster", - "fax", - "object", - "object-direct", - "object-storage", - "unknown", - "drv", - "archive" - }; - - -// -// Local functions... -// - -static ppd_info_t *add_ppd(const char *filename, const char *name, - const char *language, const char *make, - const char *make_and_model, - const char *device_id, const char *product, - const char *psversion, time_t mtime, - size_t size, int model_number, int type, - const char *scheme, ppd_list_t *ppdlist, - cf_logfunc_t log, void *ld); -static cups_file_t *cat_drv(const char *name, char *ppdname, - cf_logfunc_t log, void *ld); -static cups_file_t *cat_static(const char *name, - cf_logfunc_t log, void *ld); -static cups_file_t *cat_tar(const char *name, char *ppdname, - cf_logfunc_t log, void *ld); -static int compare_inodes(struct stat *a, struct stat *b); -static int compare_matches(const ppd_info_t *p0, - const ppd_info_t *p1); -static int compare_names(const ppd_info_t *p0, - const ppd_info_t *p1); -static int compare_ppds(const ppd_info_t *p0, - const ppd_info_t *p1); -static void free_array(cups_array_t *a); -static void free_ppdlist(ppd_list_t *ppdlist); -static int load_driver(const char *filename, - const char *name, - ppd_list_t *ppdlist, - cf_logfunc_t log, void *ld); -static int load_drv(const char *filename, const char *name, - cups_file_t *fp, time_t mtime, off_t size, - ppd_list_t *ppdlist, - cf_logfunc_t log, void *ld); -static void load_ppd(const char *filename, const char *name, - const char *scheme, struct stat *fileinfo, - ppd_info_t *ppd, cups_file_t *fp, off_t end, - ppd_list_t *ppdlist, - cf_logfunc_t log, void *ld); -static int load_ppds(const char *d, const char *p, int descend, - ppd_list_t *ppdlist, - cf_logfunc_t log, void *ld); -static int load_ppds_dat(const char *filename, int verbose, - ppd_list_t *ppdlist, - cf_logfunc_t log, void *ld); -static int load_tar(const char *filename, const char *name, - cups_file_t *fp, time_t mtime, off_t size, - ppd_list_t *ppdlist, - cf_logfunc_t log, void *ld); -static int read_tar(cups_file_t *fp, char *name, size_t namesize, - struct stat *info, - cf_logfunc_t log, void *ld); -static regex_t *regex_device_id(const char *device_id, - cf_logfunc_t log, void *ld); -static regex_t *regex_string(const char *s, - cf_logfunc_t log, void *ld); -static int CompareNames(const char *s, const char *t); -static cups_array_t *CreateStringsArray(const char *s); -static int ExecCommand(const char *command, char **argv); -static cups_file_t *PipeCommand(int *cpid, int *epid, const char *command, - char **argv, uid_t user, - cf_logfunc_t log, void *ld); -static int ClosePipeCommand(cups_file_t *fp, int cpid, int epid, - cf_logfunc_t log, void *ld); - - -// -// 'ppdCollectionListPPDs()' - List PPD files. -// - -cups_array_t * // O - List of PPD files -ppdCollectionListPPDs( - cups_array_t *ppd_collections, // I - Directories to search for PPDs - // in - int limit, // I - Limit - int num_options, // I - Number of options - cups_option_t *options, // I - Options - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - int i; // Looping var - ppd_collection_t *col; // Pointer to PPD collection - int count; // Number of PPDs to list - ppd_info_t *ppd, // Current PPD file - *newppd; // Copy of current PPD - cups_file_t *fp; // ppds.dat file - cups_array_t *include, // PPD schemes to include - *exclude; // PPD schemes to exclude - const char *cachename, // Cache file name - *only_makes, // Do we only want a list of makes? - *device_id, // ppd-device-id option - *language, // ppd-natural-language option - *make, // ppd-make option - *make_and_model, // ppd-make-and-model option - *model_number_str, // ppd-model-number option - *product, // ppd-product option - *psversion, // ppd-psversion option - *type_str; // ppd-type option - int model_number, // ppd-model-number value - type; // ppd-type value - size_t make_and_model_len, // Length of ppd-make-and-model - product_len; // Length of ppd-product - regex_t *device_id_re, // Regular expression for matching - // device ID - *make_and_model_re; // Regular expression for matching make - // and model - regmatch_t re_matches[6]; // Regular expression matches - cups_array_t *matches, // Matching PPDs - *result; // Resulting PPD list - ppd_list_t ppdlist; // Lists of all available PPDs - int matches_array_created = 0; - - - // - // Initialize PPD list... - // - - ppdlist.PPDsByName = cupsArrayNew((cups_array_func_t)compare_names, - NULL); - ppdlist.PPDsByMakeModel = cupsArrayNew((cups_array_func_t)compare_ppds, - NULL); - ppdlist.ChangedPPD = 0; - - - // - // See if we have a PPD database file... - // - - cachename = cupsGetOption("ppd-cache", num_options, options); - if (cachename && cachename[0] && - load_ppds_dat(cachename, 1, &ppdlist, log, ld)) - { - free_ppdlist(&ppdlist); - return(NULL); - } - - // - // Load all PPDs in the specified directories and below... - // - - ppdlist.Inodes = cupsArrayNew((cups_array_func_t)compare_inodes, NULL); - - for (col = (ppd_collection_t *)cupsArrayFirst(ppd_collections); - col; - col = (ppd_collection_t *)cupsArrayNext(ppd_collections)) - load_ppds(col->path, col->name ? col->name : col->path, 1, &ppdlist, - log, ld); - - if (cachename && cachename[0]) - { - // - // Cull PPD files that are no longer present... - // - - for (ppd = (ppd_info_t *)cupsArrayFirst(ppdlist.PPDsByName), i = 0; - ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppdlist.PPDsByName), i ++) - if (!ppd->found) - { - // - // Remove this PPD file from the list... - // - - cupsArrayRemove(ppdlist.PPDsByName, ppd); - cupsArrayRemove(ppdlist.PPDsByMakeModel, ppd); - free(ppd); - - ppdlist.ChangedPPD = 1; - } - - // - // Write the new ppds.dat file... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] ChangedPPD=%d", - ppdlist.ChangedPPD); - - if (ppdlist.ChangedPPD) - { - char newname[1024]; // New filename - - snprintf(newname, sizeof(newname), "%s.%d", cachename, (int)getpid()); - - if ((fp = cupsFileOpen(newname, "w")) != NULL) - { - unsigned ppdsync = PPD_SYNC; // Sync word - - cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync)); - - for (ppd = (ppd_info_t *)cupsArrayFirst(ppdlist.PPDsByName); - ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppdlist.PPDsByName)) - cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t)); - - cupsFileClose(fp); - - if (rename(newname, cachename)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to rename \"%s\" - %s", - newname, strerror(errno)); - } - else - if (log) log(ld, CF_LOGLEVEL_INFO, - "libppd: [PPD Collections] Wrote \"%s\", %d PPDs...", - cachename, cupsArrayCount(ppdlist.PPDsByName)); - } - else - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to write \"%s\" - %s", - cachename, strerror(errno)); - } - else - if (log) log(ld, CF_LOGLEVEL_INFO, - "libppd: [PPD Collections] No new or changed PPDs..."); - } - - // - // Lists for inclusion and exclusion of certain PPD sources... - // - - exclude = CreateStringsArray(cupsGetOption("exclude-schemes", - num_options, options)); - include = CreateStringsArray(cupsGetOption("include-schemes", - num_options, options)); - - // - // Read further options... - // - - only_makes = cupsGetOption("only-makes", num_options, options); - device_id = cupsGetOption("device-id", num_options, options); - language = cupsGetOption("natural-language", num_options, options); - make = cupsGetOption("make", num_options, options); - make_and_model = cupsGetOption("make-and-model", num_options, options); - model_number_str = cupsGetOption("model-number", num_options, options); - product = cupsGetOption("product", num_options, options); - psversion = cupsGetOption("psversion", num_options, options); - type_str = cupsGetOption("type", num_options, options); - - if (make_and_model) - make_and_model_len = strlen(make_and_model); - else - make_and_model_len = 0; - - if (product) - product_len = strlen(product); - else - product_len = 0; - - if (model_number_str) - model_number = atoi(model_number_str); - else - model_number = 0; - - if (type_str) - { - for (type = 0; - type < (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0])); - type ++) - if (!strcmp(type_str, PPDTypes[type])) - break; - - if (type >= (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0]))) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Bad ppd-type=\"%s\" ignored!", - type_str); - type_str = NULL; - } - } - else - type = 0; - - for (i = 0; i < num_options; i ++) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] %s=\"%s\"", options[i].name, - options[i].value); - - if (limit <= 0 || limit > cupsArrayCount(ppdlist.PPDsByMakeModel)) - count = cupsArrayCount(ppdlist.PPDsByMakeModel); - else - count = limit; - - if (device_id || language || make || make_and_model || model_number_str || - product) - { - matches = cupsArrayNew((cups_array_func_t)compare_matches, NULL); - matches_array_created = 1; - - if (device_id) - device_id_re = regex_device_id(device_id, log, ld); - else - device_id_re = NULL; - - if (make_and_model) - make_and_model_re = regex_string(make_and_model, log, ld); - else - make_and_model_re = NULL; - - for (ppd = (ppd_info_t *)cupsArrayFirst(ppdlist.PPDsByMakeModel); - ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppdlist.PPDsByMakeModel)) - { - // - // Filter PPDs based on make, model, product, language, model number, - // and/or device ID using the "matches" score value. An exact match - // for product, make-and-model, or device-id adds 3 to the score. - // Partial matches for make-and-model yield 1 or 2 points, and matches - // for the make and language add a single point. Results are then sorted - // by score, highest score first. - // - - if (ppd->record.type < PPD_TYPE_POSTSCRIPT || - ppd->record.type >= PPD_TYPE_DRV) - continue; - - if (cupsArrayFind(exclude, ppd->record.scheme) || - (include && !cupsArrayFind(include, ppd->record.scheme))) - continue; - - ppd->matches = 0; - - if (device_id_re && - !regexec(device_id_re, ppd->record.device_id, - (size_t)(sizeof(re_matches) / sizeof(re_matches[0])), - re_matches, 0)) - { - // - // Add the number of matching values from the device ID - it will be - // at least 2 (manufacturer and model), and as much as 3 (command set). - // - - for (i = 1; i < (int)(sizeof(re_matches) / sizeof(re_matches[0])); i ++) - if (re_matches[i].rm_so >= 0) - ppd->matches ++; - } - - if (language) - { - for (i = 0; i < PPD_MAX_LANG; i ++) - if (!ppd->record.languages[i][0]) - break; - else if (!strcmp(ppd->record.languages[i], language)) - { - ppd->matches ++; - break; - } - } - - if (make && !_ppd_strcasecmp(ppd->record.make, make)) - ppd->matches ++; - - if (make_and_model_re && - !regexec(make_and_model_re, ppd->record.make_and_model, - (size_t)(sizeof(re_matches) / sizeof(re_matches[0])), - re_matches, 0)) - { - // See how much of the make-and-model string we matched... - if (re_matches[0].rm_so == 0) - { - if ((size_t)re_matches[0].rm_eo == make_and_model_len) - ppd->matches += 3; // Exact match - else - ppd->matches += 2; // Prefix match - } - else - ppd->matches ++; // Infix match - } - - if (model_number_str && ppd->record.model_number == model_number) - ppd->matches ++; - - if (product) - { - for (i = 0; i < PPD_MAX_PROD; i ++) - if (!ppd->record.products[i][0]) - break; - else if (!_ppd_strcasecmp(ppd->record.products[i], product)) - { - ppd->matches += 3; - break; - } - else if (!_ppd_strncasecmp(ppd->record.products[i], product, - product_len)) - { - ppd->matches += 2; - break; - } - } - - if (psversion) - { - for (i = 0; i < PPD_MAX_VERS; i ++) - if (!ppd->record.psversions[i][0]) - break; - else if (!_ppd_strcasecmp(ppd->record.psversions[i], psversion)) - { - ppd->matches ++; - break; - } - } - - if (type_str && ppd->record.type == type) - ppd->matches ++; - - if (ppd->matches) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] %s matches with score %d!", - ppd->record.name, ppd->matches); - cupsArrayAdd(matches, ppd); - } - } - if (device_id_re) - free(device_id_re); - if (make_and_model_re) - free(make_and_model_re); - } - else if (include || exclude) - { - matches = cupsArrayNew((cups_array_func_t)compare_ppds, NULL); - matches_array_created = 1; - - for (ppd = (ppd_info_t *)cupsArrayFirst(ppdlist.PPDsByMakeModel); - ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppdlist.PPDsByMakeModel)) - { - // - // Filter PPDs based on the include/exclude lists. - // - - if (ppd->record.type < PPD_TYPE_POSTSCRIPT || - ppd->record.type >= PPD_TYPE_DRV) - continue; - - if (cupsArrayFind(exclude, ppd->record.scheme) || - (include && !cupsArrayFind(include, ppd->record.scheme))) - continue; - - cupsArrayAdd(matches, ppd); - } - } - else - matches = ppdlist.PPDsByMakeModel; - - result = cupsArrayNew(NULL, NULL); - - for (ppd = (ppd_info_t *)cupsArrayFirst(ppdlist.PPDsByMakeModel), i = 0; - count > 0 && ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppdlist.PPDsByMakeModel), i ++) - { - // - // Skip invalid PPDs... - // - - if (ppd->record.type < PPD_TYPE_POSTSCRIPT || - ppd->record.type >= PPD_TYPE_DRV) - continue; - - // - // Output this PPD... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Sending %s (%s)...", - ppd->record.name, ppd->record.make_and_model); - - count --; - - if (only_makes) - cupsArrayAdd(result, strdup(ppd->record.make)); - else - { - newppd = (ppd_info_t *)malloc(sizeof(ppd_info_t)); - memcpy(newppd, ppd, sizeof(ppd_info_t)); - cupsArrayAdd(result, newppd); - } - - // - // If we have only requested the make, then skip - // the remaining PPDs with this make... - // - - if (only_makes) - { - const char *this_make; // This ppd-make - - - for (this_make = ppd->record.make, - ppd = (ppd_info_t *)cupsArrayNext(matches); - ppd; - ppd = (ppd_info_t *)cupsArrayNext(matches)) - if (_ppd_strcasecmp(this_make, ppd->record.make)) - break; - - cupsArrayPrev(matches); - } - } - - free_ppdlist(&ppdlist); - if (matches_array_created) - cupsArrayDelete(matches); - - return(result); -} - - -// -// 'ppdCollectionGetPPD()' - Copy a PPD file to stdout. -// - -cups_file_t * -ppdCollectionGetPPD( - const char *name, // I - PPD URI of the desired PPD - cups_array_t *ppd_collections, // I - Directories to search for PPDs - // in - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - ppd_collection_t *col; // Pointer to PPD collection - cups_file_t *fp; // Archive file pointer - int fd; - int cpid; // Process ID for driver program - int epid; // Process ID for logging process - int bytes; - int is_archive = 0; - int is_drv = 0; - char realname[1024], // Scheme from PPD name - buffer[8192], // Copy buffer - tempname[1024], // Temp file name - *ptr, // Pointer into string - *ppdname, - ppduri[1024]; // PPD URI - - - // - // Figure out if this is a static or dynamic PPD file... - // - - if (strstr(name, "../")) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Invalid PPD name."); - return(NULL); - } - - if (strstr(name, ".tar:") || strstr(name, ".tar.gz:")) - is_archive = 1; - else if (strstr(name, ".drv:")) - is_drv = 1; - - if (ppd_collections) - { - for (col = (ppd_collection_t *)cupsArrayFirst(ppd_collections); - col; - col = (ppd_collection_t *)cupsArrayNext(ppd_collections)) - { - if (col->name) - { - if (col->name[0]) - { - if (!strncmp(name, col->name, strlen(col->name))) - snprintf(realname, sizeof(realname), "%s/%s", col->path, - name + strlen(col->name)); - else - continue; - } - else - snprintf(realname, sizeof(realname), "%s/%s", col->path, - name); - } else - strlcpy(realname, name, sizeof(realname)); - if ((ptr = strchr(realname, ':')) == NULL) - ppdname = NULL; - else - { - if (!is_archive && !is_drv) - strlcpy(ppduri, realname, sizeof(ppduri)); - *ptr = '\0'; - ppdname = ptr + 1; - } - if (access(realname, R_OK) == 0) - break; - } - if (col == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Requested PPD %s is in none of " - "the collections", - name); - return(NULL); - } - } - else - { - strlcpy(realname, name, sizeof(realname)); - if ((ptr = strchr(realname, ':')) == NULL) - ppdname = NULL; - else - { - if (!is_archive && !is_drv) - strlcpy(ppduri, realname, sizeof(ppduri)); - *ptr = '\0'; - ppdname = ptr + 1; - } - if (access(realname, R_OK)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Cannot access file %s - %s", - realname, strerror(errno)); - return(NULL); - } - } - - if (is_archive) - return(cat_tar(realname, ppdname, log, ld)); - else if (is_drv) - return(cat_drv(realname, ppdname, log, ld)); - else if (ppdname == NULL) - return(cat_static(realname, log, ld)); - else - { - // - // Dynamic PPD, see if we have a driver program to support it... - // - - char *argv[4]; // Arguments for program - - ptr = strrchr(realname, '/'); - if (ptr == NULL) - ptr = realname; - else - ptr ++; - ptr = ppduri + (ptr - realname); - if (access(realname, X_OK)) - { - // - // File does not exist or is not executable... - // - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to access \"%s\" - %s", - realname, strerror(errno)); - - return(NULL); - } - - // - // Yes, let it cat the PPD file... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Grabbing PPD via command: \"%s cat %s\"", - realname, ptr); - - argv[0] = realname; - argv[1] = (char *)"cat"; - argv[2] = (char *)ptr; - argv[3] = NULL; - - if ((fp = PipeCommand(&cpid, &epid, realname, argv, 0, log, ld)) != NULL) - { - if ((fd = cupsTempFd(tempname, sizeof(tempname))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to copy PPD to temp " - "file: %s", - strerror(errno)); - return (NULL); - } - while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) - bytes = write(fd, buffer, bytes); - ClosePipeCommand(fp, cpid, epid, log, ld); - close(fd); - fp = cupsFileOpen(tempname, "r"); - unlink(tempname); - } - else - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "libppd: [PPD Collections] Unable to execute \"%s\": %s", - tempname, strerror(errno)); - return(NULL); - } - } - - // - // Exit with no errors... - // - - return(fp); -} - - -// -// 'ppdCollectionDumpCache()' - Dump the contents of the ppds.dat file. -// - -int -ppdCollectionDumpCache(const char *filename, // I - Filename - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log - // function -{ - ppd_info_t *ppd; // Current PPD - ppd_list_t ppdlist; // Lists of all available PPDs - - - // - // Initialize PPD list... - // - - ppdlist.Inodes = NULL; - ppdlist.PPDsByName = cupsArrayNew((cups_array_func_t)compare_names, - NULL); - ppdlist.PPDsByMakeModel = cupsArrayNew((cups_array_func_t)compare_ppds, - NULL); - ppdlist.ChangedPPD = 0; - - - // - // See if we a PPD database file... - // - - if (load_ppds_dat(filename, 0, &ppdlist, log, ld)) - { - free_ppdlist(&ppdlist); - return(1); - } - - puts("mtime,size,model_number,type,filename,name,languages0,products0," - "psversions0,make,make_and_model,device_id,scheme"); - for (ppd = (ppd_info_t *)cupsArrayFirst(ppdlist.PPDsByName); - ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppdlist.PPDsByName)) - printf("%d,%ld,%d,%d,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"," - "\"%s\",\"%s\"\n", - (int)ppd->record.mtime, (long)ppd->record.size, - ppd->record.model_number, ppd->record.type, ppd->record.filename, - ppd->record.name, ppd->record.languages[0], ppd->record.products[0], - ppd->record.psversions[0], ppd->record.make, - ppd->record.make_and_model, ppd->record.device_id, - ppd->record.scheme); - - free_ppdlist(&ppdlist); - return(0); -} - - -// -// 'add_ppd()' - Add a PPD file. -// - -static ppd_info_t * // O - PPD -add_ppd(const char *filename, // I - PPD filename - const char *name, // I - PPD name - const char *language, // I - LanguageVersion - const char *make, // I - Manufacturer - const char *make_and_model, // I - NickName/ModelName - const char *device_id, // I - 1284DeviceID - const char *product, // I - Product - const char *psversion, // I - PSVersion - time_t mtime, // I - Modification time - size_t size, // I - File size - int model_number, // I - Model number - int type, // I - Driver type - const char *scheme, // I - PPD scheme - ppd_list_t *ppdlist, - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - ppd_info_t *ppd; // PPD - - - // - // Add a new PPD file... - // - - if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Ran out of memory for %d PPD " - "files!", - cupsArrayCount(ppdlist->PPDsByName)); - return (NULL); - } - - // - // Zero-out the PPD data and copy the values over... - // - - ppd->found = 1; - ppd->record.mtime = mtime; - ppd->record.size = (off_t)size; - ppd->record.model_number = model_number; - ppd->record.type = type; - - strlcpy(ppd->record.filename, filename, sizeof(ppd->record.filename)); - strlcpy(ppd->record.name, name, sizeof(ppd->record.name)); - strlcpy(ppd->record.languages[0], language, - sizeof(ppd->record.languages[0])); - strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0])); - strlcpy(ppd->record.psversions[0], psversion, - sizeof(ppd->record.psversions[0])); - strlcpy(ppd->record.make, make, sizeof(ppd->record.make)); - strlcpy(ppd->record.make_and_model, make_and_model, - sizeof(ppd->record.make_and_model)); - strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id)); - strlcpy(ppd->record.scheme, scheme, sizeof(ppd->record.scheme)); - - // - // Add the PPD to the PPD arrays... - // - - cupsArrayAdd(ppdlist->PPDsByName, ppd); - cupsArrayAdd(ppdlist->PPDsByMakeModel, ppd); - - // - // Return the new PPD pointer... - // - - return (ppd); -} - - -// -// 'cat_drv()' - Generate a PPD from a driver info file. -// - -static cups_file_t * // O - Pointer to PPD file -cat_drv(const char *filename, // I - *.drv file name - char *ppdname, // I - PPD name in the *.drv file - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - cups_file_t *fp; // File pointer - int fd; - char tempname[1024]; // Name for the temporary file - ppdcSource *src; // PPD source file data - ppdcDriver *d; // Current driver - cups_file_t *out = NULL; // PPD output to temp file - int fd1, fd2; - - if ((fp = cupsFileOpen(filename, "r")) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to open \"%s\" - %s\n", - filename, strerror(errno)); - - return (NULL); - } - - // Eliminate any output to stderr, to get rid of the error messages of - // the *.drv file parser - fd1 = dup(2); - fd2 = open("/dev/null", O_WRONLY); - dup2(fd2, 2); - close(fd2); - - src = new ppdcSource(filename, fp); - - for (d = (ppdcDriver *)src->drivers->first(); - d; - d = (ppdcDriver *)src->drivers->next()) - if (!strcmp(ppdname, d->pc_file_name->value) || - (d->file_name && !strcmp(ppdname, d->file_name->value))) - break; - - if (d) - { - ppdcArray *locales; // Locale names - ppdcCatalog *catalog; // Message catalog in .drv file - - - if ((fd = cupsTempFd(tempname, sizeof(tempname))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to copy PPD to temp " - "file: %s", - strerror(errno)); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] %u locales defined in \"%s\"...\n", - (unsigned)src->po_files->count, filename); - - locales = new ppdcArray(); - for (catalog = (ppdcCatalog *)src->po_files->first(); - catalog; - catalog = (ppdcCatalog *)src->po_files->next()) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Adding locale \"%s\"...\n", - catalog->locale->value); - catalog->locale->retain(); - locales->add(catalog->locale); - } - - out = cupsFileOpenFd(fd, "w"); - d->write_ppd_file(out, NULL, locales, src, PPDC_LFONLY); - cupsFileClose(out); - close(fd); - locales->release(); - - out = cupsFileOpen(tempname, "r"); - unlink(tempname); - } - } - else - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] PPD \"%s\" not found.\n", ppdname); - - src->release(); - cupsFileClose(fp); - - // Re-activate stderr output - dup2(fd1, 2); - close(fd1); - - return (out); -} - - -// -// 'cat_static()' - Return pointer to static PPD file -// - -static cups_file_t * // O - Pointer to PPD file -cat_static(const char *name, // I - PPD name - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - cups_file_t *fp; - - if ((fp = cupsFileOpen(name, "r")) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to open \"%s\" - %s", - name, strerror(errno)); - - return (NULL); - } - - return(fp); -} - - -// -// 'cat_tar()' - Copy an archived PPD file to temp file. -// - -static cups_file_t * // O - Pointer to PPD file -cat_tar(const char *filename, // I - Archive name - char *ppdname, // I - PPD name in the archive - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - cups_file_t *fp; // Archive file pointer - int fd; - char tempname[1024], // Name for the temporary file - curname[256], // Current name in archive - buffer[8192]; // Copy buffer - struct stat curinfo; // Current file info in archive - off_t total, // Total bytes copied - next; // Offset for next record in archive - ssize_t bytes; // Bytes read - - - // - // Open the archive file... - // - - if ((fp = cupsFileOpen(filename, "r")) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to open \"%s\" - %s", - filename, strerror(errno)); - - return (NULL); - } - - // - // Scan the archive for the PPD... - // - - while (read_tar(fp, curname, sizeof(curname), &curinfo, log, ld)) - { - next = cupsFileTell(fp) + ((curinfo.st_size + TAR_BLOCK - 1) & - ~(TAR_BLOCK - 1)); - - if (!strcmp(ppdname, curname)) - { - if ((fd = cupsTempFd(tempname, sizeof(tempname))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to copy PPD to temp " - "file: %s", - strerror(errno)); - return (NULL); - } - for (total = 0; total < curinfo.st_size; total += bytes) - { - if ((size_t)(bytes = (curinfo.st_size - total)) > sizeof(buffer)) - bytes = sizeof(buffer); - - if ((bytes = cupsFileRead(fp, buffer, (size_t)bytes)) < 0) - { - if (errno == EINTR || errno == EAGAIN) - bytes = 0; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Read error - %s", - strerror(errno)); - cupsFileClose(fp); - close(fd); - unlink(tempname); - return(NULL); - } - } - else if (bytes > 0 && write(fd, buffer, bytes) != bytes) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Write error - %s", - strerror(errno)); - cupsFileClose(fp); - close(fd); - unlink(tempname); - return(NULL); - } - } - - cupsFileClose(fp); - close(fd); - fp = cupsFileOpen(tempname, "r"); - unlink(tempname); - - return(fp); - } - - if (cupsFileTell(fp) != next) - cupsFileSeek(fp, next); - } - - cupsFileClose(fp); - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] PPD \"%s\" not found.", ppdname); - - return (NULL); -} - - -// -// 'compare_inodes()' - Compare two inodes. -// - -static int // O - Result of comparison -compare_inodes(struct stat *a, // I - First inode - struct stat *b) // I - Second inode -{ - if (a->st_dev != b->st_dev) - return (a->st_dev - b->st_dev); - else - return (a->st_ino - b->st_ino); -} - - -// -// 'compare_matches()' - Compare PPD match scores for sorting. -// - -static int -compare_matches(const ppd_info_t *p0, // I - First PPD - const ppd_info_t *p1) // I - Second PPD -{ - if (p1->matches != p0->matches) - return (p1->matches - p0->matches); - else - return (CompareNames(p0->record.make_and_model, - p1->record.make_and_model)); -} - - -// -// 'compare_names()' - Compare PPD filenames for sorting. -// - -static int // O - Result of comparison -compare_names(const ppd_info_t *p0, // I - First PPD file - const ppd_info_t *p1) // I - Second PPD file -{ - int diff; // Difference between strings - - - if ((diff = strcmp(p0->record.filename, p1->record.filename)) != 0) - return (diff); - else - return (strcmp(p0->record.name, p1->record.name)); -} - - -// -// '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 -{ - int diff; // Difference between strings - - - // - // First compare manufacturers... - // - - if ((diff = _ppd_strcasecmp(p0->record.make, p1->record.make)) != 0) - return (diff); - else if ((diff = CompareNames(p0->record.make_and_model, - p1->record.make_and_model)) != 0) - return (diff); - else if ((diff = strcmp(p0->record.languages[0], - p1->record.languages[0])) != 0) - return (diff); - else - return (compare_names(p0, p1)); -} - - -// -// 'free_array()' - Free an array of strings. -// - -static void -free_array(cups_array_t *a) // I - Array to free -{ - char *ptr; // Pointer to string - - - for (ptr = (char *)cupsArrayFirst(a); - ptr; - ptr = (char *)cupsArrayNext(a)) - free(ptr); - - cupsArrayDelete(a); -} - - -// -// 'free_ppdlist()' - Free the PPD list arrays. -// - -static void -free_ppdlist(ppd_list_t *ppdlist) // I - PPD list to free -{ - struct stat *dinfoptr; // Pointer to Inode info - ppd_info_t *ppd; // Pointer to PPD info - - - for (dinfoptr = (struct stat *)cupsArrayFirst(ppdlist->Inodes); - dinfoptr; - dinfoptr = (struct stat *)cupsArrayNext(ppdlist->Inodes)) - free(dinfoptr); - cupsArrayDelete(ppdlist->Inodes); - - for (ppd = (ppd_info_t *)cupsArrayFirst(ppdlist->PPDsByName); - ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppdlist->PPDsByName)) - free(ppd); - cupsArrayDelete(ppdlist->PPDsByName); - cupsArrayDelete(ppdlist->PPDsByMakeModel); -} - - -// -// 'load_driver()' - Load driver-generated PPD files. -// - -static int // O - 1 on success, 0 on failure -load_driver(const char *filename, // I - Driver excutable file name - const char *name, // I - Name to the rest of the world - ppd_list_t *ppdlist, - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - int i; // Looping var - char *start, // Start of value - *ptr; // Pointer into string - const char *scheme = NULL; // Scheme for this driver - int cpid; // Process ID for driver program - int epid; // Process ID for logging process - cups_file_t *fp; // Pipe to driver program - char *argv[3], // Arguments for command - line[2048], // Line from driver - ppd_name[256], // ppd-name - make[128], // ppd-make - make_and_model[128], // ppd-make-and-model - device_id[256], // ppd-device-id - languages[128], // ppd-natural-language - product[128], // ppd-product - psversion[128], // ppd-psversion - type_str[128]; // ppd-type - int type; // PPD type - ppd_info_t *ppd; // Newly added PPD - - - // - // Run the driver with the "list" argument and collect the output... - // - - argv[0] = (char *)filename; - argv[1] = (char *)"list"; - argv[2] = NULL; - - if ((fp = PipeCommand(&cpid, &epid, filename, argv, 0, log, ld)) != NULL) - { - while (cupsFileGets(fp, line, sizeof(line))) - { - // - // Each line is of the form: - // - // "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" - // "ppd-device-id" "ppd-product" "ppd-psversion" - // - - device_id[0] = '\0'; - product[0] = '\0'; - psversion[0] = '\0'; - strlcpy(type_str, "postscript", sizeof(type_str)); - - if (sscanf(line, "\"%255[^\"]\"%127s%*[ \t]\"%127[^\"]\"" - "%*[ \t]\"%127[^\"]\"%*[ \t]\"%255[^\"]\"" - "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\"" - "%*[ \t]\"%127[^\"]\"", - ppd_name, languages, make, make_and_model, - device_id, product, psversion, type_str) < 4) - { - // - // Bad format; strip trailing newline and write an error message. - // - - if (line[strlen(line) - 1] == '\n') - line[strlen(line) - 1] = '\0'; - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Bad line from \"%s\": %s", - filename, line); - break; - } - else - { - // - // Add the device to the array of available devices... - // - - if ((start = strchr(languages, ',')) != NULL) - *start++ = '\0'; - - for (type = 0; - type < (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0])); - type ++) - if (!strcmp(type_str, PPDTypes[type])) - break; - - if (type >= (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0]))) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Bad ppd-type \"%s\" ignored!", - type_str); - type = PPD_TYPE_UNKNOWN; - } - - if ((scheme = strrchr(name, '/')) != NULL && - !strncmp(scheme + 1, ppd_name, strlen(scheme + 1)) && - (scheme - name) + strlen(ppd_name) + 1 < sizeof(ppd_name) && - *(ptr = ppd_name + strlen(scheme + 1)) == ':') - { - scheme ++; - memmove(ppd_name + strlen(name), ptr, strlen(ptr) + 1); - memmove(ppd_name, name, strlen(name)); - } - else if (strncmp(name, ppd_name, strlen(name)) || - *(ppd_name + strlen(name)) != ':') - { - cupsFileClose(fp); - return (0); - } - - if (scheme == 0) - scheme = name; - - ppd = add_ppd(filename, ppd_name, languages, make, make_and_model, - device_id, product, psversion, 0, 0, 0, type, scheme, - ppdlist, log, ld); - - if (!ppd) - { - cupsFileClose(fp); - return (0); - } - - if (start && *start) - { - for (i = 1; i < PPD_MAX_LANG && *start; i ++) - { - if ((ptr = strchr(start, ',')) != NULL) - *ptr++ = '\0'; - else - ptr = start + strlen(start); - - strlcpy(ppd->record.languages[i], start, - sizeof(ppd->record.languages[0])); - - start = ptr; - } - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Adding PPD \"%s\"...", - ppd_name); - } - } - - ClosePipeCommand(fp, cpid, epid, log, ld); - } - else - if (log) log(ld, CF_LOGLEVEL_WARN, - "libppd: [PPD Collections] Unable to execute \"%s\": %s", - filename, strerror(errno)); - - return (1); -} - - -// -// 'load_drv()' - Load the PPDs from a driver information file. -// - -static int // O - 1 on success, 0 on failure -load_drv(const char *filename, // I - Actual filename - const char *name, // I - Name to the rest of the world - cups_file_t *fp, // I - File to read from - time_t mtime, // I - Mod time of driver info file - off_t size, // I - Size of driver info file - ppd_list_t *ppdlist, - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - ppdcSource *src; // Driver information file - ppdcDriver *d; // Current driver - ppdcAttr *device_id, // 1284DeviceID attribute - *product, // Current product value - *ps_version, // PSVersion attribute - *cups_fax, // cupsFax attribute - *nick_name; // NickName attribute - ppdcFilter *filter; // Current filter - ppd_info_t *ppd; // Current PPD - int products_found; // Number of products found - char uri[2048], // Driver URI - make_model[1024]; // Make and model - int type; // Driver type - int fd1, fd2; - - - // - // Eliminate any output to stderr, to get rid of the error messages of - // the *.drv file parser - // - - fd1 = dup(2); - fd2 = open("/dev/null", O_WRONLY); - dup2(fd2, 2); - close(fd2); - - // - // Load the driver info file... - // - - src = new ppdcSource(filename, fp); - - if (src->drivers->count == 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Bad driver information file \"%s\"!\n", - filename); - src->release(); - // Re-activate stderr output - dup2(fd1, 2); - close(fd1); - return (0); - } - - // - // Add a dummy entry for the file... - // - - add_ppd(filename, name, "", "", "", "", "", "", mtime, (size_t)size, 0, - PPD_TYPE_DRV, "drv", ppdlist, log, ld); - - // - // Then the drivers in the file... - // - - for (d = (ppdcDriver *)src->drivers->first(); - d; - d = (ppdcDriver *)src->drivers->next()) - { - snprintf(uri, sizeof(uri), "%s:%s", name, - d->file_name ? d->file_name->value : - d->pc_file_name->value); - - device_id = d->find_attr("1284DeviceID", NULL); - ps_version = d->find_attr("PSVersion", NULL); - nick_name = d->find_attr("NickName", NULL); - - if (nick_name) - strncpy(make_model, nick_name->value->value, sizeof(make_model) - 1); - else if (strncasecmp(d->model_name->value, d->manufacturer->value, - strlen(d->manufacturer->value))) - snprintf(make_model, sizeof(make_model), "%s %s, %s", - d->manufacturer->value, d->model_name->value, - d->version->value); - else - snprintf(make_model, sizeof(make_model), "%s, %s", d->model_name->value, - d->version->value); - - if ((cups_fax = d->find_attr("cupsFax", NULL)) != NULL && - !strcasecmp(cups_fax->value->value, "true")) - type = PPD_TYPE_FAX; - else if (d->type == PPDC_DRIVER_PS) - type = PPD_TYPE_POSTSCRIPT; - else if (d->type != PPDC_DRIVER_CUSTOM) - type = PPD_TYPE_RASTER; - else - { - for (filter = (ppdcFilter *)d->filters->first(), - type = PPD_TYPE_POSTSCRIPT; - filter; - filter = (ppdcFilter *)d->filters->next()) - if (strcasecmp(filter->mime_type->value, "application/vnd.cups-raster")) - type = PPD_TYPE_RASTER; - else if (strcasecmp(filter->mime_type->value, - "application/vnd.cups-pdf")) - type = PPD_TYPE_PDF; - } - - for (product = (ppdcAttr *)d->attrs->first(), products_found = 0, - ppd = NULL; - product; - product = (ppdcAttr *)d->attrs->next()) - if (!strcmp(product->name->value, "Product")) - { - if (!products_found) - ppd = add_ppd(filename, uri, "en", d->manufacturer->value, - make_model, device_id ? device_id->value->value : "", - product->value->value, - ps_version ? ps_version->value->value : "(3010) 0", - mtime, (size_t)size, d->model_number, type, "drv", - ppdlist, log, ld); - else if (products_found < PPD_MAX_PROD) - strncpy(ppd->record.products[products_found], product->value->value, - sizeof(ppd->record.products[0])); - else - break; - - products_found ++; - } - - if (!products_found) - add_ppd(filename, uri, "en", d->manufacturer->value, make_model, - device_id ? device_id->value->value : "", d->model_name->value, - ps_version ? ps_version->value->value : "(3010) 0", mtime, - (size_t)size, d->model_number, type, "drv", ppdlist, log, ld); - } - - src->release(); - - // - // Re-activate stderr output - // - - dup2(fd1, 2); - close(fd1); - - return (1); -} - - -// -// 'load_ppd()' - Load a PPD file. -// - -static void -load_ppd(const char *filename, // I - Real filename - const char *name, // I - Virtual filename - const char *scheme, // I - PPD scheme - struct stat *fileinfo, // I - File information - ppd_info_t *ppd, // I - Existing PPD file or NULL - cups_file_t *fp, // I - File to read from - off_t end, // I - End of file position or 0 - ppd_list_t *ppdlist, - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - int i; // Looping var - char line[256], // Line from file - *ptr, // Pointer into line - lang_version[64], // PPD LanguageVersion - lang_encoding[64], // PPD LanguageEncoding - country[64], // Country code - manufacturer[256], // Manufacturer - make_model[256], // Make and Model - model_name[256], // ModelName - nick_name[256], // NickName - device_id[256], // 1284DeviceID - product[256], // Product - psversion[256], // PSVersion - temp[512]; // Temporary make and model - int install_group, // In the installable options group? - model_number, // cupsModelNumber - type; // ppd-type - cups_array_t *products, // Product array - *psversions, // PSVersion array - *cups_languages; // cupsLanguages array - int new_ppd; // Is this a new PPD? - struct // LanguageVersion translation table - { - const char *version, // LanguageVersion string - *language; // Language code - } languages[] = - { - { "chinese", "zh" }, - { "czech", "cs" }, - { "danish", "da" }, - { "dutch", "nl" }, - { "english", "en" }, - { "finnish", "fi" }, - { "french", "fr" }, - { "german", "de" }, - { "greek", "el" }, - { "hungarian", "hu" }, - { "italian", "it" }, - { "japanese", "ja" }, - { "korean", "ko" }, - { "norwegian", "no" }, - { "polish", "pl" }, - { "portuguese", "pt" }, - { "russian", "ru" }, - { "simplified chinese", "zh_CN" }, - { "slovak", "sk" }, - { "spanish", "es" }, - { "swedish", "sv" }, - { "traditional chinese", "zh_TW" }, - { "turkish", "tr" } - }; - - - // - // Now read until we get the required fields... - // - - cups_languages = cupsArrayNew(NULL, NULL); - products = cupsArrayNew(NULL, NULL); - psversions = cupsArrayNew(NULL, NULL); - - model_name[0] = '\0'; - nick_name[0] = '\0'; - manufacturer[0] = '\0'; - device_id[0] = '\0'; - lang_encoding[0] = '\0'; - strlcpy(lang_version, "en", sizeof(lang_version)); - model_number = 0; - install_group = 0; - type = PPD_TYPE_POSTSCRIPT; - - while ((end == 0 || cupsFileTell(fp) < end) && - cupsFileGets(fp, line, sizeof(line))) - { - if (!strncmp(line, "*Manufacturer:", 14)) - sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer); - else if (!strncmp(line, "*ModelName:", 11)) - sscanf(line, "%*[^\"]\"%127[^\"]", model_name); - else if (!strncmp(line, "*LanguageEncoding:", 18)) - sscanf(line, "%*[^:]:%63s", lang_encoding); - else if (!strncmp(line, "*LanguageVersion:", 17)) - sscanf(line, "%*[^:]:%63s", lang_version); - else if (!strncmp(line, "*NickName:", 10)) - sscanf(line, "%*[^\"]\"%255[^\"]", nick_name); - else if (!_ppd_strncasecmp(line, "*1284DeviceID:", 14)) - { - sscanf(line, "%*[^\"]\"%255[^\"]", device_id); - - // Make sure device ID ends with a semicolon... - if (device_id[0] && device_id[strlen(device_id) - 1] != ';') - strlcat(device_id, ";", sizeof(device_id)); - } - else if (!strncmp(line, "*Product:", 9)) - { - if (sscanf(line, "%*[^\"]\"(%255[^\"]", product) == 1) - { - // - // Make sure the value ends with a right parenthesis - can't stop at - // the first right paren since the product name may contain escaped - // parenthesis... - // - - ptr = product + strlen(product) - 1; - if (ptr > product && *ptr == ')') - { - // - // Yes, ends with a parenthesis, so remove it from the end and - // add the product to the list... - // - - *ptr = '\0'; - cupsArrayAdd(products, strdup(product)); - } - } - } - else if (!strncmp(line, "*PSVersion:", 11)) - { - sscanf(line, "%*[^\"]\"%255[^\"]", psversion); - cupsArrayAdd(psversions, strdup(psversion)); - } - else if (!strncmp(line, "*cupsLanguages:", 15)) - { - char *start; // Start of language - - - for (start = line + 15; *start && isspace(*start & 255); start ++); - - if (*start++ == '\"') - { - while (*start) - { - for (ptr = start + 1; - *ptr && *ptr != '\"' && !isspace(*ptr & 255); - ptr ++); - - if (*ptr) - { - *ptr++ = '\0'; - - while (isspace(*ptr & 255)) - *ptr++ = '\0'; - } - - cupsArrayAdd(cups_languages, strdup(start)); - start = ptr; - } - } - } - else if (!strncmp(line, "*cupsFax:", 9)) - { - for (ptr = line + 9; isspace(*ptr & 255); ptr ++); - - if (!_ppd_strncasecmp(ptr, "true", 4)) - type = PPD_TYPE_FAX; - } - else if ((!strncmp(line, "*cupsFilter:", 12) || - !strncmp(line, "*cupsFilter2:", 13)) && - type == PPD_TYPE_POSTSCRIPT) - { - if (strstr(line + 12, "application/vnd.cups-raster")) - type = PPD_TYPE_RASTER; - else if (strstr(line + 12, "application/vnd.cups-pdf")) - type = PPD_TYPE_PDF; - } - else if (!strncmp(line, "*cupsModelNumber:", 17)) - sscanf(line, "*cupsModelNumber:%d", &model_number); - else if (!strncmp(line, "*OpenGroup: Installable", 23)) - install_group = 1; - else if (!strncmp(line, "*CloseGroup:", 12)) - install_group = 0; - else if (!strncmp(line, "*OpenUI", 7)) - { - // - // Stop early if we have a NickName or ModelName attributes - // before the first non-installable OpenUI... - // - - if (!install_group && (model_name[0] || nick_name[0]) && - cupsArrayCount(products) > 0 && cupsArrayCount(psversions) > 0) - break; - } - } - - // - // See if we got all of the required info... - // - - if (nick_name[0]) - cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name, - sizeof(make_model), ppdGetEncoding(lang_encoding)); - else - strlcpy(make_model, model_name, sizeof(make_model)); - - while (isspace(make_model[0] & 255)) - _ppd_strcpy(make_model, make_model + 1); - - if (!make_model[0] || cupsArrayCount(products) == 0 || - cupsArrayCount(psversions) == 0) - { - // - // We don't have all the info needed, so skip this file... - // - - if (!make_model[0]) - if (log) log(ld, CF_LOGLEVEL_WARN, - "libppd: [PPD Collections] Missing NickName and ModelName " - "in %s!", - filename); - - if (cupsArrayCount(products) == 0) - if (log) log(ld, CF_LOGLEVEL_WARN, - "libppd: [PPD Collections] Missing Product in %s!", - filename); - - if (cupsArrayCount(psversions) == 0) - if (log) log(ld, CF_LOGLEVEL_WARN, - "libppd: [PPD Collections] Missing PSVersion in %s!", - filename); - - free_array(products); - free_array(psversions); - free_array(cups_languages); - - return; - } - - if (model_name[0]) - cupsArrayAdd(products, strdup(model_name)); - - // - // Normalize the make and model string... - // - - while (isspace(manufacturer[0] & 255)) - _ppd_strcpy(manufacturer, manufacturer + 1); - - if (!_ppd_strncasecmp(make_model, manufacturer, strlen(manufacturer))) - strlcpy(temp, make_model, sizeof(temp)); - else - snprintf(temp, sizeof(temp), "%s %s", manufacturer, make_model); - - ppdNormalizeMakeAndModel(temp, make_model, sizeof(make_model)); - - // - // See if we got a manufacturer... - // - - if (!manufacturer[0] || !strcmp(manufacturer, "ESP")) - { - // - // Nope, copy the first part of the make and model then... - // - - strlcpy(manufacturer, make_model, sizeof(manufacturer)); - - // - // 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 - strlcpy(manufacturer, "Other", sizeof(manufacturer)); - } - else if (!_ppd_strncasecmp(manufacturer, "LHAG", 4) || - !_ppd_strncasecmp(manufacturer, "linotype", 8)) - strlcpy(manufacturer, "LHAG", sizeof(manufacturer)); - else if (!_ppd_strncasecmp(manufacturer, "Hewlett", 7)) - strlcpy(manufacturer, "HP", sizeof(manufacturer)); - - // - // Fix the lang_version as needed... - // - - if ((ptr = strchr(lang_version, '-')) != NULL) - *ptr++ = '\0'; - else if ((ptr = strchr(lang_version, '_')) != NULL) - *ptr++ = '\0'; - - if (ptr) - { - // - // Setup the country suffix... - // - - country[0] = '_'; - _ppd_strcpy(country + 1, ptr); - } - else - { - // - // No country suffix... - // - - country[0] = '\0'; - } - - for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++) - if (!_ppd_strcasecmp(languages[i].version, lang_version)) - break; - - if (i < (int)(sizeof(languages) / sizeof(languages[0]))) - { - // - // Found a known language... - // - - snprintf(lang_version, sizeof(lang_version), "%s%s", - languages[i].language, country); - } - else - { - // - // Unknown language; use "xx"... - // - - strlcpy(lang_version, "xx", sizeof(lang_version)); - } - - // - // Record the PPD file... - // - - new_ppd = !ppd; - - if (new_ppd) - { - // - // Add new PPD file... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Adding PPD \"%s\"...", name); - - ppd = add_ppd(filename, name, lang_version, manufacturer, make_model, - device_id, (char *)cupsArrayFirst(products), - (char *)cupsArrayFirst(psversions), fileinfo->st_mtime, - (size_t)fileinfo->st_size, model_number, type, scheme, - ppdlist, log, ld); - - if (!ppd) - return; - } - else - { - // - // Update existing record... - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Updating ppd \"%s\"...", name); - - memset(ppd, 0, sizeof(ppd_info_t)); - - ppd->found = 1; - ppd->record.mtime = fileinfo->st_mtime; - ppd->record.size = fileinfo->st_size; - ppd->record.model_number = model_number; - ppd->record.type = type; - - strlcpy(ppd->record.filename, filename, sizeof(ppd->record.filename)); - strlcpy(ppd->record.name, name, sizeof(ppd->record.name)); - strlcpy(ppd->record.languages[0], lang_version, - sizeof(ppd->record.languages[0])); - strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products), - sizeof(ppd->record.products[0])); - strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions), - sizeof(ppd->record.psversions[0])); - strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make)); - strlcpy(ppd->record.make_and_model, make_model, - sizeof(ppd->record.make_and_model)); - strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id)); - strlcpy(ppd->record.scheme, scheme, sizeof(ppd->record.scheme)); - } - - // - // Add remaining products, versions, and languages... - // - - for (i = 1; - i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL; - i ++) - strlcpy(ppd->record.products[i], ptr, - sizeof(ppd->record.products[0])); - - for (i = 1; - i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL; - i ++) - strlcpy(ppd->record.psversions[i], ptr, - sizeof(ppd->record.psversions[0])); - - for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages); - i < PPD_MAX_LANG && ptr; - i ++, ptr = (char *)cupsArrayNext(cups_languages)) - strlcpy(ppd->record.languages[i], ptr, - sizeof(ppd->record.languages[0])); - - // - // Free products, versions, and languages... - // - - free_array(cups_languages); - free_array(products); - free_array(psversions); - - ppdlist->ChangedPPD = 1; -} - - -// -// 'load_ppds()' - Load PPD files recursively. -// - -static int // O - 1 on success, 0 on failure -load_ppds(const char *d, // I - Actual directory - const char *p, // I - Virtual path in name - int descend, // I - Descend into directories? - ppd_list_t *ppdlist, - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - struct stat dinfo, // Directory information - *dinfoptr; // Pointer to match - cups_file_t *fp; // Pointer to file - cups_dir_t *dir; // Directory pointer - cups_dentry_t *dent; // Directory entry - char filename[1024], // Name of PPD or directory - line[256], // Line from file - *ptr, // Pointer into name - name[1024]; // Name of PPD file - ppd_info_t *ppd, // New PPD file - key; // Search key - - - // - // See if we've loaded this directory before... - // - - if (stat(d, &dinfo)) - { - if (errno != ENOENT) - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to stat \"%s\": %s", d, - strerror(errno)); - - return (0); - } - else if (cupsArrayFind(ppdlist->Inodes, &dinfo)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Skipping \"%s\": loop detected!", - d); - return (1); - } - - // - // Nope, add it to the Inodes array and continue... - // - - dinfoptr = (struct stat *)malloc(sizeof(struct stat)); - memcpy(dinfoptr, &dinfo, sizeof(struct stat)); - cupsArrayAdd(ppdlist->Inodes, dinfoptr); - - // - // Check permissions... - // - - //if (_ppdFileCheck(d, _PPD_FILE_CHECK_DIRECTORY, !geteuid(), log, ld)) - // return (0); - - if ((dir = cupsDirOpen(d)) == NULL) - { - if (errno != ENOENT) - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to open PPD directory " - "\"%s\": %s\n", - d, strerror(errno)); - - return (0); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Loading \"%s\"...", d); - - while ((dent = cupsDirRead(dir)) != NULL) - { - // - // Skip files/directories starting with "."... - // - - if (dent->filename[0] == '.') - continue; - - // - // See if this is a file... - // - - if (!strcmp(d, "/")) - snprintf(filename, sizeof(filename), "/%s", dent->filename); - else - snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename); - - if (!strcmp(p, "/")) - snprintf(name, sizeof(name), "/%s", dent->filename); - else if (p[0]) - snprintf(name, sizeof(name), "%s/%s", p, dent->filename); - else - strlcpy(name, dent->filename, sizeof(name)); - - if (S_ISDIR(dent->fileinfo.st_mode)) - { - // - // Do subdirectory... - // - - if (descend) - { - if (!load_ppds(filename, name, 1, ppdlist, log, ld)) - { - cupsDirClose(dir); - return (1); - } - } - - continue; - } - else if (strstr(filename, ".plist")) - { - // - // Skip plist files in the PPDs directory... - // - - continue; - } - //else if (_ppdFileCheck(filename, _PPD_FILE_CHECK_FILE_ONLY, !geteuid(), - // log, ld)) - // continue; - - // - // See if this file has been scanned before... - // - - strlcpy(key.record.filename, filename, sizeof(key.record.filename)); - strlcpy(key.record.name, name, sizeof(key.record.name)); - - ppd = (ppd_info_t *)cupsArrayFind(ppdlist->PPDsByName, &key); - - if (ppd && - ppd->record.size == dent->fileinfo.st_size && - ppd->record.mtime == dent->fileinfo.st_mtime) - { - // - // Rewind to the first entry for this file... - // - - while ((ppd = (ppd_info_t *)cupsArrayPrev(ppdlist->PPDsByName)) != NULL && - !strcmp(ppd->record.filename, filename)); - - // - // Then mark all of the matches for this file as found... - // - - while ((ppd = (ppd_info_t *)cupsArrayNext(ppdlist->PPDsByName)) != NULL && - !strcmp(ppd->record.filename, filename)) - ppd->found = 1; - - continue; - } - - // - // No, file is new/changed, so re-scan it... - // - - if ((fp = cupsFileOpen(filename, "r")) == NULL) - continue; - - // - // Now see if this is a PPD file... - // - - line[0] = '\0'; - cupsFileGets(fp, line, sizeof(line)); - - if (!strncmp(line, "*PPD-Adobe:", 11)) - { - // - // Yes, load it... - // - - load_ppd(filename, name, "file", &dent->fileinfo, ppd, fp, 0, ppdlist, - log, ld); - } - else - { - // - // Nope, treat it as a an archive, a PPD-generating executable, or a - // driver information file... - // - - cupsFileRewind(fp); - - if ((ptr = strstr(filename, ".tar")) != NULL && - (!strcmp(ptr, ".tar") || !strcmp(ptr, ".tar.gz"))) - load_tar(filename, name, fp, dent->fileinfo.st_mtime, - dent->fileinfo.st_size, ppdlist, log, ld); - else if ((ptr = strstr(filename, ".drv")) != NULL && !strcmp(ptr, ".drv")) - load_drv(filename, name, fp, dent->fileinfo.st_mtime, - dent->fileinfo.st_size, ppdlist, log, ld); - else if ((dent->fileinfo.st_mode & 0111) && - S_ISREG(dent->fileinfo.st_mode)) - { - // File is not a PPD, not an archive, but executable, try whether - // it generates PPDs... - load_driver(filename, name, ppdlist, log, ld); - } - } - - // - // Close the file... - // - - cupsFileClose(fp); - } - - cupsDirClose(dir); - - return (1); -} - - -// -// 'load_ppds_dat()' - Load the ppds.dat file. -// - -static int -load_ppds_dat(const char *filename, // I - Filename - int verbose, // I - Be verbose? - ppd_list_t *ppdlist, - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - ppd_info_t *ppd; // Current PPD file - cups_file_t *fp; // ppds.dat file - struct stat fileinfo; // ppds.dat information - - - if (filename == NULL || !filename[0]) - return(0); - - if ((fp = cupsFileOpen(filename, "r")) != NULL) - { - // - // See if we have the right sync word... - // - - unsigned ppdsync; // Sync word - int num_ppds; // Number of PPDs - - if ((size_t)cupsFileRead(fp, (char *)&ppdsync, - sizeof(ppdsync)) == sizeof(ppdsync) && - ppdsync == PPD_SYNC && - !stat(filename, &fileinfo) && - (((size_t)fileinfo.st_size - sizeof(ppdsync)) % sizeof(ppd_rec_t)) == - 0 && - (num_ppds = ((size_t)fileinfo.st_size - sizeof(ppdsync)) / - sizeof(ppd_rec_t)) > 0) - { - // - // We have a ppds.dat file, so read it! - // - - for (; num_ppds > 0; num_ppds --) - { - if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL) - { - if (verbose) - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to allocate memory " - "for PPD!"); - return(1); - } - - if (cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t)) > 0) - { - cupsArrayAdd(ppdlist->PPDsByName, ppd); - cupsArrayAdd(ppdlist->PPDsByMakeModel, ppd); - } - else - { - free(ppd); - break; - } - } - - if (verbose) - if (log) log(ld, CF_LOGLEVEL_INFO, - "libppd: [PPD Collections] Read \"%s\", %d PPDs...", - filename, cupsArrayCount(ppdlist->PPDsByName)); - } - - cupsFileClose(fp); - } - - return(0); -} - - -// -// 'load_tar()' - Load archived PPD files. -// - -static int // O - 1 on success, 0 on failure -load_tar(const char *filename, // I - Actual filename - const char *name, // I - Name to the rest of the world - cups_file_t *fp, // I - File to read from - time_t mtime, // I - Mod time of driver info file - off_t size, // I - Size of driver info file - ppd_list_t *ppdlist, - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - char curname[256], // Current archive file name - uri[2048]; // Virtual file URI - const char *curext; // Extension on file - struct stat curinfo; // Current archive file information - off_t next; // Position for next header - - - // - // Add a dummy entry for the file... - // - - add_ppd(filename, name, "", "", "", "", "", "", mtime, (size_t)size, 0, - PPD_TYPE_ARCHIVE, "file", ppdlist, log, ld); - ppdlist->ChangedPPD = 1; - - // - // Scan for PPDs in the archive... - // - - while (read_tar(fp, curname, sizeof(curname), &curinfo, log, ld)) - { - next = cupsFileTell(fp) + ((curinfo.st_size + TAR_BLOCK - 1) & - ~(TAR_BLOCK - 1)); - - if ((curext = strrchr(curname, '.')) != NULL && - !_ppd_strcasecmp(curext, ".ppd")) - { - snprintf(uri, sizeof(uri), "%s:%s", name, curname); - load_ppd(filename, uri, "file", &curinfo, NULL, fp, next, ppdlist, - log, ld); - } - - if (cupsFileTell(fp) != next) - cupsFileSeek(fp, next); - } - - return (1); -} - - -// -// 'read_tar()' - Read a file header from an archive. -// -// This function skips all directories and special files. -// - -static int // O - 1 if found, 0 on EOF -read_tar(cups_file_t *fp, // I - Archive to read - char *name, // I - Filename buffer - size_t namesize, // I - Size of filename buffer - struct stat *info, // O - File information - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - tar_rec_t record; // Record from file - - - while ((size_t)cupsFileRead(fp, (char *)&record, - sizeof(record)) == sizeof(record)) - { - // - // Check for a valid tar header... - // - - if ((memcmp(record.header.magic, TAR_MAGIC, 6) || - memcmp(record.header.version, TAR_VERSION, 2)) && - memcmp(record.header.magic, TAR_OLDGNU_MAGIC, 8)) - { - if (record.header.magic[0] || - memcmp(record.header.magic, record.header.magic + 1, 5)) - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Bad tar magic/version."); - break; - } - - // - // Ignore non-files... - // - - if (record.header.linkflag != TAR_OLDNORMAL && - record.header.linkflag != TAR_NORMAL) - continue; - - // - // Grab size and name from tar header and return... - // - - if (record.header.prefix[0]) - snprintf(name, namesize, "%s/%s", record.header.prefix, - record.header.pathname); - else - strlcpy(name, record.header.pathname, namesize); - - info->st_mtime = strtol(record.header.mtime, NULL, 8); - info->st_size = strtoll(record.header.size, NULL, 8); - - return (1); - } - - return (0); -} - - -// -// 'regex_device_id()' - Compile a regular expression based on the 1284 device -// ID. - - -static regex_t * // O - Regular expression -regex_device_id(const char *device_id, // I - IEEE-1284 device ID - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - char res[2048], // Regular expression string - *ptr; // Pointer into string - regex_t *re; // Regular expression - int cmd; // Command set string? - - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] regex_device_id(\"%s\")", - device_id); - - // - // Scan the device ID string and insert class, command set, manufacturer, and - // model attributes to match. We assume that the device ID in the PPD and the - // device ID reported by the device itself use the same attribute names and - // order of attributes. - // - - ptr = res; - - while (*device_id && ptr < (res + sizeof(res) - 6)) - { - cmd = !_ppd_strncasecmp(device_id, "COMMAND SET:", 12) || - !_ppd_strncasecmp(device_id, "CMD:", 4); - - if (cmd || !_ppd_strncasecmp(device_id, "MANUFACTURER:", 13) || - !_ppd_strncasecmp(device_id, "MFG:", 4) || - !_ppd_strncasecmp(device_id, "MFR:", 4) || - !_ppd_strncasecmp(device_id, "MODEL:", 6) || - !_ppd_strncasecmp(device_id, "MDL:", 4)) - { - if (ptr > res) - { - *ptr++ = '.'; - *ptr++ = '*'; - } - - *ptr++ = '('; - - while (*device_id && *device_id != ';' && ptr < (res + sizeof(res) - 8)) - { - if (strchr("[]{}().*\\|", *device_id)) - *ptr++ = '\\'; - if (*device_id == ':') - { - // - // KEY:.*value - // - - *ptr++ = *device_id++; - *ptr++ = '.'; - *ptr++ = '*'; - } - else - *ptr++ = *device_id++; - } - - if (*device_id == ';' || !*device_id) - { - // - // KEY:.*value.*; - // - - *ptr++ = '.'; - *ptr++ = '*'; - *ptr++ = ';'; - } - *ptr++ = ')'; - if (cmd) - *ptr++ = '?'; - } - else if ((device_id = strchr(device_id, ';')) == NULL) - break; - else - device_id ++; - } - - *ptr = '\0'; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] regex_device_id: \"%s\"", res); - - // - // Compile the regular expression and return... - // - - if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL) - { - if (!regcomp(re, res, REG_EXTENDED | REG_ICASE)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] regex_device_id: OK"); - return (re); - } - - free(re); - } - - return (NULL); -} - - -// -// 'regex_string()' - Construct a regular expression to compare a simple string. -// - -static regex_t * // O - Regular expression -regex_string(const char *s, // I - String to compare - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - char res[2048], // Regular expression string - *ptr; // Pointer into string - regex_t *re; // Regular expression - - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] regex_string(\"%s\")", s); - - // - // Convert the string to a regular expression, escaping special characters - // as needed. - // - - ptr = res; - - while (*s && ptr < (res + sizeof(res) - 2)) - { - if (strchr("[]{}().*\\", *s)) - *ptr++ = '\\'; - - *ptr++ = *s++; - } - - *ptr = '\0'; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] regex_string: \"%s\"", res); - - // - // Create a case-insensitive regular expression... - // - - if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL) - { - if (!regcomp(re, res, REG_ICASE)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] regex_string: OK"); - return (re); - } - - free(re); - } - - return (NULL); -} - - -// -// 'CompareNames()' - Compare two names. -// -// This function basically does a _ppd_strcasecmp() of the two strings, -// but is also aware of numbers so that "a2" < "a100". -// - -int // O - Result of comparison -CompareNames(const char *s, // I - First string - const char *t) // I - Second string -{ - int diff, // Difference between digits - digits; // Number of digits - - - // - // Loop through both names, returning only when a difference is - // seen. Also, compare whole numbers rather than just characters, too! - // - - while (*s && *t) - { - if (isdigit(*s & 255) && isdigit(*t & 255)) - { - // - // Got a number; start by skipping leading 0's... - // - - while (*s == '0') - s ++; - while (*t == '0') - t ++; - - // - // Skip equal digits... - // - - while (isdigit(*s & 255) && *s == *t) - { - s ++; - t ++; - } - - // - // Bounce out if *s and *t aren't both digits... - // - - if (isdigit(*s & 255) && !isdigit(*t & 255)) - return (1); - else if (!isdigit(*s & 255) && isdigit(*t & 255)) - return (-1); - else if (!isdigit(*s & 255) || !isdigit(*t & 255)) - continue; - - if (*s < *t) - diff = -1; - else - diff = 1; - - // - // Figure out how many more digits there are... - // - - digits = 0; - s ++; - t ++; - - while (isdigit(*s & 255)) - { - digits ++; - s ++; - } - - while (isdigit(*t & 255)) - { - 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 (0); -} - - -// -// 'CreateStringsArray()' - Create a CUPS array of strings. -// - -cups_array_t * // O - CUPS array -CreateStringsArray(const char *s) // I - Comma-delimited strings -{ - if (!s || !*s) - return (NULL); - else - return (_ppdArrayNewStrings(s, ',')); -} - - -// -// 'ExecCommand()' - Run a program with the correct environment. -// -// On macOS, we need to update the CFProcessPath environment variable that -// is passed in the environment so the child can access its bundled resources. -// - -int // O - exec() status -ExecCommand(const char *command, // I - Full path to program - char **argv) // I - Command-line arguments -{ -#ifdef __APPLE__ - int i, j; // Looping vars - char *envp[500], // Array of environment variables - cfprocesspath[1024], // CFProcessPath environment variable - linkpath[1024]; // Link path for symlinks... - int linkbytes; // Bytes for link path - - - // - // Some macOS programs are bundled and need the CFProcessPath environment - // variable defined. If the command is a symlink, resolve the link and point - // to the resolved location, otherwise, use the command path itself. - // - - if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0) - { - // - // Yes, this is a symlink to the actual program, nul-terminate and - // use it... - // - - linkpath[linkbytes] = '\0'; - - if (linkpath[0] == '/') - snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s", - linkpath); - else - snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s/%s", - dirname((char *)command), linkpath); - } - else - snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s", command); - - envp[0] = cfprocesspath; - - // - // Copy the rest of the environment except for any CFProcessPath that may - // already be there... - // - - for (i = 1, j = 0; - environ[j] && i < (int)(sizeof(envp) / sizeof(envp[0]) - 1); - j ++) - if (strncmp(environ[j], "CFProcessPath=", 14)) - envp[i ++] = environ[j]; - - envp[i] = NULL; - - // - // Use execve() to run the program... - // - - return (execve(command, argv, envp)); - -#else - // - // On other operating systems, just call execv() to use the same environment - // variables as the parent... - // - - return (execv(command, argv)); -#endif // __APPLE__ -} - - -// -// 'PipeCommand()' - Read output from a command. -// - -cups_file_t * // O - CUPS file or NULL on error -PipeCommand(int *cpid, // O - Process ID or 0 on error - int *epid, // O - Log Process ID or 0 on error - const char *command, // I - Command to run - char **argv, // I - Arguments to pass to command - uid_t user, // I - User to run as or 0 for current - cf_logfunc_t log, - void *ld) -{ - int fd, // Temporary file descriptor - cfds[2], // Output Pipe file descriptors - efds[2]; // Error/Log Pipe file descriptors - cups_file_t *outfp, *logfp; - char buf[BUFSIZ]; - cf_loglevel_t log_level; - char *msg; - - - *cpid = *epid = 0; - cfds[0] = cfds[1] = efds[0] = efds[1] = -1; - - // - // First create the pipes... - // - - if (pipe(cfds)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to establish output pipe for %s call: %s", - argv[0], strerror(errno)); - return (NULL); - } - if (log) - if (pipe(efds)) - { - log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to establish error/logging pipe for %s call: %s", - argv[0], strerror(errno)); - return (NULL); - } - - // - // Set the "close on exec" flag on each end of the pipes... - // - - if (fcntl(cfds[0], F_SETFD, fcntl(cfds[0], F_GETFD) | FD_CLOEXEC)) - { - close(cfds[0]); - close(cfds[1]); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to set \"close on exec\" flag on read end of the output pipe for %s call: %s", - argv[0], strerror(errno)); - return (NULL); - } - - if (fcntl(cfds[1], F_SETFD, fcntl(cfds[1], F_GETFD) | FD_CLOEXEC)) - { - close(cfds[0]); - close(cfds[1]); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to set \"close on exec\" flag on write end of the output pipe for %s call: %s", - argv[0], strerror(errno)); - return (NULL); - } - - if (log) - { - if (fcntl(efds[0], F_SETFD, fcntl(efds[0], F_GETFD) | FD_CLOEXEC)) - { - close(cfds[0]); - close(cfds[1]); - close(efds[0]); - close(efds[1]); - log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to set \"close on exec\" flag on read end of the error/logging pipe for %s call: %s", - argv[0], strerror(errno)); - return (NULL); - } - - if (fcntl(efds[1], F_SETFD, fcntl(efds[1], F_GETFD) | FD_CLOEXEC)) - { - close(cfds[0]); - close(cfds[1]); - close(efds[0]); - close(efds[1]); - log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to set \"close on exec\" flag on write end of the error/logging pipe for %s call: %s", - argv[0], strerror(errno)); - return (NULL); - } - } - - // - // Then run the command... - // - - if ((*cpid = fork()) < 0) - { - // - // Unable to fork! - // - - *cpid = 0; - close(cfds[0]); - close(cfds[1]); - if (log) - { - close(efds[0]); - close(efds[1]); - } - - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to fork for %s: %s", argv[0], - strerror(errno)); - - return (NULL); - } - else if (!*cpid) - { - // - // Child (command) comes here... - // - - if (!getuid() && user && setuid(user) < 0) // Run as restricted user - exit(errno); - - if ((fd = open("/dev/null", O_RDONLY)) > 0) - { - dup2(fd, 0); // < /dev/null - close(fd); - } - - dup2(cfds[1], 1); // > command pipe - close(cfds[1]); - - if (log) - { - dup2(efds[1], 2); // 2> error pipe - close(efds[1]); - } - else if ((fd = open("/dev/null", O_WRONLY)) > 0) - { - dup2(fd, 2); // 2> /dev/null - close(fd); - } - - ExecCommand(command, argv); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to launch %s: %s", argv[0], - strerror(errno)); - exit(errno); - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Started %s (PID %d)", argv[0], cpid); - - // - // Parent comes here ... - // - - close(cfds[1]); - - // - // Open the input side of the pipe... - // - - outfp = cupsFileOpenFd(cfds[0], "r"); - - // - // Fork the error logging... - // - - if (log) - { - if ((*epid = fork()) < 0) - { - // - // Unable to fork! - // - - *epid = 0; - close(efds[0]); - close(efds[1]); - - kill(*cpid, SIGTERM); - ClosePipeCommand(outfp, *cpid, 0, log, ld); - - log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Unable to fork for logging for %s: %s", - argv[0], strerror(errno)); - - return (NULL); - } - else if (!*epid) - { - // - // Child (logging) comes here... - // - - close(cfds[0]); - close(efds[1]); - logfp = cupsFileOpenFd(efds[0], "r"); - while (cupsFileGets(logfp, buf, sizeof(buf))) - if (log) - { - if (strncmp(buf, "DEBUG: ", 7) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 7; - } - else if (strncmp(buf, "DEBUG2: ", 8) == 0) - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf + 8; - } - else if (strncmp(buf, "INFO: ", 6) == 0) - { - log_level = CF_LOGLEVEL_INFO; - msg = buf + 6; - } - else if (strncmp(buf, "WARNING: ", 9) == 0) - { - log_level = CF_LOGLEVEL_WARN; - msg = buf + 9; - } - else if (strncmp(buf, "ERROR: ", 7) == 0) - { - log_level = CF_LOGLEVEL_ERROR; - msg = buf + 7; - } - else - { - log_level = CF_LOGLEVEL_DEBUG; - msg = buf; - } - log(ld, log_level, "libppd: [PPD Collections] %s: %s", argv[0], msg); - } - cupsFileClose(logfp); - // No need to close the fd errfds[0], as cupsFileClose(fp) does this - // already - // Ignore errors of the logging process - exit(0); - } - - log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] Started logging for %s (PID %d)", - argv[0], cpid); - - // - // Parent comes here ... - // - - close(efds[0]); - close(efds[1]); - } - - return outfp; -} - - -// -// 'ClosePipeCommand()' - Wait for the command called with PipeCommand() to -// finish and return the status. -// - -static int -ClosePipeCommand(cups_file_t *fp, - int cpid, - int epid, - cf_logfunc_t log, - void *ld) -{ - int pid; - int status = 65536; - int wstatus; - - - // - // close the stream... - // - - cupsFileClose(fp); - - // - // Wait for the child process to exit... - // - - while (cpid > 0 || epid > 0) - { - if ((pid = wait(&wstatus)) < 0) - { - if (errno == EINTR) - continue; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] Error closing sub-processes: %s - killing processes", - strerror(errno)); - kill(cpid, SIGTERM); - cpid = -1; - kill(epid, SIGTERM); - epid = -1; - break; - } - } - - // How did the filter terminate - if (wstatus) - { - if (WIFEXITED(wstatus)) - { - // Via exit() anywhere or return() in the main() function - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] %s (PID %d) stopped with status %d", - (pid == cpid ? "Command" : "Logging"), pid, - WEXITSTATUS(wstatus)); - status = WEXITSTATUS(wstatus); - } - else - { - // Via signal - if (log) log(ld, CF_LOGLEVEL_ERROR, - "libppd: [PPD Collections] %s (PID %d) crashed on signal %d", - (pid == cpid ? "Command" : "Logging"), pid, - WTERMSIG(wstatus)); - status = 256 * WTERMSIG(wstatus); - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "libppd: [PPD Collections] %s (PID %d) exited with no errors.", - (pid == cpid ? "Command" : "Logging"), pid); - status = 0; - } - if (pid == cpid) - cpid = -1; - else if (pid == epid) - epid = -1; - } - - return (status); -} diff --git a/ppd/ppd-conflicts.c b/ppd/ppd-conflicts.c deleted file mode 100644 index 237fd487b..000000000 --- a/ppd/ppd-conflicts.c +++ /dev/null @@ -1,1196 +0,0 @@ -// -// Option conflict management routines for libppd. -// -// Copyright 2007-2018 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "ppd.h" -#include "debug-internal.h" - - -// -// Local constants... -// - -enum -{ - _PPD_OPTION_CONSTRAINTS, - _PPD_INSTALLABLE_CONSTRAINTS, - _PPD_ALL_CONSTRAINTS -}; - - -// -// Local functions... -// - -static int ppd_is_installable(ppd_group_t *installable, - const char *option); -static void ppd_load_constraints(ppd_file_t *ppd); -static cups_array_t *ppd_test_constraints(ppd_file_t *ppd, - const char *option, - const char *choice, - int num_options, - cups_option_t *options, - int which); - - -// -// 'ppdGetConflicts()' - Get a list of conflicting options in a marked PPD. -// -// This function gets a list of options that would conflict if "option" and -// "choice" were marked in the PPD. You would typically call this function -// after marking the currently selected options in the PPD in order to -// determine whether a new option selection would cause a conflict. -// -// The number of conflicting options are returned with "options" pointing to -// the conflicting options. The returned option array must be freed using -// @link cupsFreeOptions@. -// -// @since CUPS 1.4/macOS 10.6@ -// - -int // O - Number of conflicting options -ppdGetConflicts( - ppd_file_t *ppd, // I - PPD file - const char *option, // I - Option to test - const char *choice, // I - Choice to test - cups_option_t **options) // O - Conflicting options -{ - int i, // Looping var - num_options; // Number of conflicting options - cups_array_t *active; // Active conflicts - ppd_cups_uiconsts_t *c; // Current constraints - ppd_cups_uiconst_t *cptr; // Current constraint - ppd_choice_t *marked; // Marked choice - - - // - // Range check input... - // - - if (options) - *options = NULL; - - if (!ppd || !option || !choice || !options) - return (0); - - // - // Test for conflicts... - // - - active = ppd_test_constraints(ppd, option, choice, 0, NULL, - _PPD_ALL_CONSTRAINTS); - - // - // Loop through all of the UI constraints and add any options that conflict... - // - - for (num_options = 0, c = (ppd_cups_uiconsts_t *)cupsArrayFirst(active); - c; - c = (ppd_cups_uiconsts_t *)cupsArrayNext(active)) - { - for (i = c->num_constraints, cptr = c->constraints; - i > 0; - i --, cptr ++) - if (_ppd_strcasecmp(cptr->option->keyword, option)) - { - if (cptr->choice) - num_options = cupsAddOption(cptr->option->keyword, - cptr->choice->choice, num_options, - options); - else if ((marked = ppdFindMarkedChoice(ppd, - cptr->option->keyword)) != NULL) - num_options = cupsAddOption(cptr->option->keyword, marked->choice, - num_options, options); - } - } - - cupsArrayDelete(active); - - return (num_options); -} - - -// -// 'ppdResolveConflicts()' - Resolve conflicts in a marked PPD. -// -// This function attempts to resolve any conflicts in a marked PPD, returning -// a list of option changes that are required to resolve them. On input, -// "num_options" and "options" contain any pending option changes that have -// not yet been marked, while "option" and "choice" contain the most recent -// selection which may or may not be in "num_options" or "options". -// -// On successful return, "num_options" and "options" are updated to contain -// "option" and "choice" along with any changes required to resolve conflicts -// specified in the PPD file and 1 is returned. -// -// If option conflicts cannot be resolved, "num_options" and "options" are not -// changed and 0 is returned. -// -// When resolving conflicts, @code ppdResolveConflicts@ does not consider -// changes to the current page size (@code media@, @code PageSize@, and -// @code PageRegion@) or to the most recent option specified in "option". -// Thus, if the only way to resolve a conflict is to change the page size -// or the option the user most recently changed, @code ppdResolveConflicts@ -// will return 0 to indicate it was unable to resolve the conflicts. -// -// The @code ppdResolveConflicts@ function uses one of two sources of option -// constraint information. The preferred constraint information is defined by -// @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this -// case, the PPD file provides constraint resolution actions. -// -// The backup constraint information is defined by the -// @code UIConstraints@ and @code NonUIConstraints@ attributes. These -// constraints are resolved algorithmically by first selecting the default -// choice for the conflicting option, then iterating over all possible choices -// until a non-conflicting option choice is found. -// -// @since CUPS 1.4/macOS 10.6@ -// - -int // O - 1 on success, 0 on failure -ppdResolveConflicts( - ppd_file_t *ppd, // I - PPD file - const char *option, // I - Newly selected option or - // @code NULL@ for none - const char *choice, // I - Newly selected choice or - // @code NULL@ for none - int *num_options, // IO - Number of additional selected - // options - cups_option_t **options) // IO - Additional selected options -{ - int i, // Looping var - tries, // Number of tries - num_newopts; // Number of new options - cups_option_t *newopts; // New options - cups_array_t *active = NULL, // Active constraints - *pass, // Resolvers for this pass - *resolvers, // Resolvers we have used - *test; // Test array for conflicts - ppd_cups_uiconsts_t *consts; // Current constraints - ppd_cups_uiconst_t *constptr; // Current constraint - ppd_attr_t *resolver; // Current resolver - const char *resval; // Pointer into resolver value - char resoption[PPD_MAX_NAME], - // Current resolver option - reschoice[PPD_MAX_NAME], - // Current resolver choice - *resptr, // Pointer into option/choice - firstpage[255]; // AP_FIRSTPAGE_Keyword string - const char *value; // Selected option value - int changed; // Did we change anything? - ppd_choice_t *marked; // Marked choice - - - // - // Range check input... - // - - if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL)) - return (0); - - // - // Build a shadow option array... - // - - num_newopts = 0; - newopts = NULL; - - for (i = 0; i < *num_options; i ++) - num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value, - num_newopts, &newopts); - if (option && _ppd_strcasecmp(option, "Collate")) - num_newopts = cupsAddOption(option, choice, num_newopts, &newopts); - - // - // Loop until we have no conflicts... - // - - cupsArraySave(ppd->sorted_attrs); - - resolvers = NULL; - pass = cupsArrayNew((cups_array_func_t)_ppd_strcasecmp, NULL); - tries = 0; - - while (tries < 100 && - (active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts, - _PPD_ALL_CONSTRAINTS)) != NULL) - { - tries ++; - - if (!resolvers) - resolvers = cupsArrayNew((cups_array_func_t)_ppd_strcasecmp, NULL); - - for (consts = (ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0; - consts; - consts = (ppd_cups_uiconsts_t *)cupsArrayNext(active)) - { - if (consts->resolver[0]) - { - // - // Look up the resolver... - // - - if (cupsArrayFind(pass, consts->resolver)) - continue; // Already applied this resolver... - - if (cupsArrayFind(resolvers, consts->resolver)) - { - // - // Resolver loop! - // - - DEBUG_printf(("1ppdResolveConflicts: Resolver loop with %s!", - consts->resolver)); - goto error; - } - - if ((resolver = ppdFindAttr(ppd, "cupsUIResolver", - consts->resolver)) == NULL) - { - DEBUG_printf(("1ppdResolveConflicts: Resolver %s not found!", - consts->resolver)); - goto error; - } - - if (!resolver->value) - { - DEBUG_printf(("1ppdResolveConflicts: Resolver %s has no value!", - consts->resolver)); - goto error; - } - - // - // Add the options from the resolver... - // - - cupsArrayAdd(pass, consts->resolver); - cupsArrayAdd(resolvers, consts->resolver); - - for (resval = resolver->value; *resval && !changed;) - { - while (_ppd_isspace(*resval)) - resval ++; - - if (*resval != '*') - break; - - for (resval ++, resptr = resoption; - *resval && !_ppd_isspace(*resval); - resval ++) - if (resptr < (resoption + sizeof(resoption) - 1)) - *resptr++ = *resval; - - *resptr = '\0'; - - while (_ppd_isspace(*resval)) - resval ++; - - for (resptr = reschoice; - *resval && !_ppd_isspace(*resval); - resval ++) - if (resptr < (reschoice + sizeof(reschoice) - 1)) - *resptr++ = *resval; - - *resptr = '\0'; - - if (!resoption[0] || !reschoice[0]) - break; - - // - // Is this the option we are changing? - // - - snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s", resoption); - - if (option && - (!_ppd_strcasecmp(resoption, option) || - !_ppd_strcasecmp(firstpage, option) || - (!_ppd_strcasecmp(option, "PageSize") && - !_ppd_strcasecmp(resoption, "PageRegion")) || - (!_ppd_strcasecmp(option, "AP_FIRSTPAGE_PageSize") && - !_ppd_strcasecmp(resoption, "PageSize")) || - (!_ppd_strcasecmp(option, "AP_FIRSTPAGE_PageSize") && - !_ppd_strcasecmp(resoption, "PageRegion")) || - (!_ppd_strcasecmp(option, "PageRegion") && - !_ppd_strcasecmp(resoption, "PageSize")) || - (!_ppd_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") && - !_ppd_strcasecmp(resoption, "PageSize")) || - (!_ppd_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") && - !_ppd_strcasecmp(resoption, "PageRegion")))) - continue; - - // - // Try this choice... - // - - if ((test = ppd_test_constraints(ppd, resoption, reschoice, - num_newopts, newopts, - _PPD_ALL_CONSTRAINTS)) == NULL) - { - // - // That worked... - // - - changed = 1; - } - else - cupsArrayDelete(test); - - // - // Add the option/choice from the resolver regardless of whether it - // worked; this makes sure that we can cascade several changes to - // make things resolve... - // - - num_newopts = cupsAddOption(resoption, reschoice, num_newopts, - &newopts); - } - } - else - { - // - // Try resolving by choosing the default values for non-installable - // options, then by iterating through the possible choices... - // - - int j; // Looping var - ppd_choice_t *cptr; // Current choice - ppd_size_t *size; // Current page size - - - for (i = consts->num_constraints, constptr = consts->constraints; - i > 0 && !changed; - i --, constptr ++) - { - // - // Can't resolve by changing an installable option... - // - - if (constptr->installable) - continue; - - // - // Is this the option we are changing? - // - - if (option && - (!_ppd_strcasecmp(constptr->option->keyword, option) || - (!_ppd_strcasecmp(option, "PageSize") && - !_ppd_strcasecmp(constptr->option->keyword, "PageRegion")) || - (!_ppd_strcasecmp(option, "PageRegion") && - !_ppd_strcasecmp(constptr->option->keyword, "PageSize")))) - continue; - - // - // Get the current option choice... - // - - if ((value = cupsGetOption(constptr->option->keyword, num_newopts, - newopts)) == NULL) - { - if (!_ppd_strcasecmp(constptr->option->keyword, "PageSize") || - !_ppd_strcasecmp(constptr->option->keyword, "PageRegion")) - { - if ((value = cupsGetOption("PageSize", num_newopts, - newopts)) == NULL) - value = cupsGetOption("PageRegion", num_newopts, newopts); - - if (!value) - { - if ((size = ppdPageSize(ppd, NULL)) != NULL) - value = size->name; - else - value = ""; - } - } - else - { - marked = ppdFindMarkedChoice(ppd, constptr->option->keyword); - value = marked ? marked->choice : ""; - } - } - - if (!_ppd_strncasecmp(value, "Custom.", 7)) - value = "Custom"; - - // - // Try the default choice... - // - - test = NULL; - - if (_ppd_strcasecmp(value, constptr->option->defchoice) && - (test = ppd_test_constraints(ppd, constptr->option->keyword, - constptr->option->defchoice, - num_newopts, newopts, - _PPD_OPTION_CONSTRAINTS)) == NULL) - { - // - // That worked... - // - - num_newopts = cupsAddOption(constptr->option->keyword, - constptr->option->defchoice, - num_newopts, &newopts); - changed = 1; - } - else - { - // - // Try each choice instead... - // - - for (j = constptr->option->num_choices, - cptr = constptr->option->choices; - j > 0; - j --, cptr ++) - { - cupsArrayDelete(test); - test = NULL; - - if (_ppd_strcasecmp(value, cptr->choice) && - _ppd_strcasecmp(constptr->option->defchoice, cptr->choice) && - _ppd_strcasecmp("Custom", cptr->choice) && - (test = ppd_test_constraints(ppd, constptr->option->keyword, - cptr->choice, num_newopts, - newopts, - _PPD_OPTION_CONSTRAINTS)) == - NULL) - { - // - // This choice works... - // - - num_newopts = cupsAddOption(constptr->option->keyword, - cptr->choice, num_newopts, - &newopts); - changed = 1; - break; - } - } - - cupsArrayDelete(test); - } - } - } - } - - if (!changed) - { - DEBUG_puts("1ppdResolveConflicts: Unable to automatically resolve " - "constraint!"); - goto error; - } - - cupsArrayClear(pass); - cupsArrayDelete(active); - active = NULL; - } - - if (tries >= 100) - goto error; - - // - // Free the caller's option array... - // - - cupsFreeOptions(*num_options, *options); - - // - // If Collate is the option we are testing, add it here. Otherwise, remove - // any Collate option from the resolve list since the filters automatically - // handle manual collation... - // - - if (option && !_ppd_strcasecmp(option, "Collate")) - num_newopts = cupsAddOption(option, choice, num_newopts, &newopts); - else - num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts); - - // - // Return the new list of options to the caller... - // - - *num_options = num_newopts; - *options = newopts; - - cupsArrayDelete(pass); - cupsArrayDelete(resolvers); - - cupsArrayRestore(ppd->sorted_attrs); - - DEBUG_printf(("1ppdResolveConflicts: Returning %d options:", num_newopts)); -#ifdef DEBUG - for (i = 0; i < num_newopts; i ++) - DEBUG_printf(("1ppdResolveConflicts: options[%d]: %s=%s", i, - newopts[i].name, newopts[i].value)); -#endif // DEBUG - - return (1); - - // - // If we get here, we failed to resolve... - // - - error: - - cupsFreeOptions(num_newopts, newopts); - - cupsArrayDelete(active); - cupsArrayDelete(pass); - cupsArrayDelete(resolvers); - - cupsArrayRestore(ppd->sorted_attrs); - - DEBUG_puts("1ppdResolveConflicts: Unable to resolve conflicts!"); - - return (0); -} - - -// -// 'ppdConflicts()' - Check to see if there are any conflicts among the -// marked option choices. -// -// The returned value is the same as returned by @link ppdMarkOption@. -// - -int // O - Number of conflicts found -ppdConflicts(ppd_file_t *ppd) // I - PPD to check -{ - int i, // Looping variable - conflicts; // Number of conflicts - cups_array_t *active; // Active conflicts - ppd_cups_uiconsts_t *c; // Current constraints - ppd_cups_uiconst_t *cptr; // Current constraint - ppd_option_t *o; // Current option - - - if (!ppd) - return (0); - - // - // Clear all conflicts... - // - - cupsArraySave(ppd->options); - - for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd)) - o->conflicted = 0; - - cupsArrayRestore(ppd->options); - - // - // Test for conflicts... - // - - active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL, - _PPD_ALL_CONSTRAINTS); - conflicts = cupsArrayCount(active); - - // - // Loop through all of the UI constraints and flag any options - // that conflict... - // - - for (c = (ppd_cups_uiconsts_t *)cupsArrayFirst(active); - c; - c = (ppd_cups_uiconsts_t *)cupsArrayNext(active)) - { - for (i = c->num_constraints, cptr = c->constraints; - i > 0; - i --, cptr ++) - cptr->option->conflicted = 1; - } - - cupsArrayDelete(active); - - // - // Return the number of conflicts found... - // - - return (conflicts); -} - - -// -// 'ppdInstallableConflict()' - Test whether an option choice conflicts with -// an installable option. -// -// This function tests whether a particular option choice is available based -// on constraints against options in the "InstallableOptions" group. -// -// @since CUPS 1.4/macOS 10.6@ -// - -int // O - 1 if conflicting, 0 if not conflicting -ppdInstallableConflict( - ppd_file_t *ppd, // I - PPD file - const char *option, // I - Option - const char *choice) // I - Choice -{ - cups_array_t *active; // Active conflicts - - - DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")", - ppd, option, choice)); - - // - // Range check input... - // - - if (!ppd || !option || !choice) - return (0); - - // - // Test constraints using the new option... - // - - active = ppd_test_constraints(ppd, option, choice, 0, NULL, - _PPD_INSTALLABLE_CONSTRAINTS); - - cupsArrayDelete(active); - - return (active != NULL); -} - - -// -// 'ppd_is_installable()' - Determine whether an option is in the -// InstallableOptions group. -// - -static int // O - 1 if installable, 0 if normal -ppd_is_installable( - ppd_group_t *installable, // I - InstallableOptions group - const char *name) // I - Option name -{ - if (installable) - { - int i; // Looping var - ppd_option_t *option; // Current option - - - for (i = installable->num_options, option = installable->options; - i > 0; - i --, option ++) - if (!_ppd_strcasecmp(option->keyword, name)) - return (1); - } - - return (0); -} - - -// -// 'ppd_load_constraints()' - Load constraints from a PPD file. -// - -static void -ppd_load_constraints(ppd_file_t *ppd) // I - PPD file -{ - int i; // Looping var - ppd_const_t *oldconst; // Current UIConstraints data - ppd_attr_t *constattr; // Current cupsUIConstraints attribute - ppd_cups_uiconsts_t *consts; // Current cupsUIConstraints data - ppd_cups_uiconst_t *constptr; // Current constraint - ppd_group_t *installable; // Installable options group - const char *vptr; // Pointer into constraint value - char option[PPD_MAX_NAME], // Option name/MainKeyword - choice[PPD_MAX_NAME], // Choice/OptionKeyword - *ptr; // Pointer into option or choice - - - DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd)); - - // - // Create an array to hold the constraint data... - // - - ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL); - - // - // Find the installable options group if it exists... - // - - for (i = ppd->num_groups, installable = ppd->groups; - i > 0; - i --, installable ++) - if (!_ppd_strcasecmp(installable->name, "InstallableOptions")) - break; - - if (i <= 0) - installable = NULL; - - // - // Load old-style [Non]UIConstraints data... - // - - for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++) - { - // - // Weed out nearby duplicates, since the PPD spec requires that you - // define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"... - // - - if (i > 1 && - !_ppd_strcasecmp(oldconst[0].option1, oldconst[1].option2) && - !_ppd_strcasecmp(oldconst[0].choice1, oldconst[1].choice2) && - !_ppd_strcasecmp(oldconst[0].option2, oldconst[1].option1) && - !_ppd_strcasecmp(oldconst[0].choice2, oldconst[1].choice1)) - continue; - - // - // Allocate memory... - // - - if ((consts = calloc(1, sizeof(ppd_cups_uiconsts_t))) == NULL) - { - DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " - "UIConstraints!"); - return; - } - - if ((constptr = calloc(2, sizeof(ppd_cups_uiconst_t))) == NULL) - { - free(consts); - DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " - "UIConstraints!"); - return; - } - - // - // Fill in the information... - // - - consts->num_constraints = 2; - consts->constraints = constptr; - - if (!_ppd_strncasecmp(oldconst->option1, "Custom", 6) && - !_ppd_strcasecmp(oldconst->choice1, "True")) - { - constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6); - constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom"); - constptr[0].installable = 0; - } - else - { - constptr[0].option = ppdFindOption(ppd, oldconst->option1); - constptr[0].choice = ppdFindChoice(constptr[0].option, - oldconst->choice1); - constptr[0].installable = ppd_is_installable(installable, - oldconst->option1); - } - - if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0])) - { - DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!", - oldconst->option1, oldconst->choice1)); - free(consts->constraints); - free(consts); - continue; - } - - if (!_ppd_strncasecmp(oldconst->option2, "Custom", 6) && - !_ppd_strcasecmp(oldconst->choice2, "True")) - { - constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6); - constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom"); - constptr[1].installable = 0; - } - else - { - constptr[1].option = ppdFindOption(ppd, oldconst->option2); - constptr[1].choice = ppdFindChoice(constptr[1].option, - oldconst->choice2); - constptr[1].installable = ppd_is_installable(installable, - oldconst->option2); - } - - if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0])) - { - DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!", - oldconst->option2, oldconst->choice2)); - free(consts->constraints); - free(consts); - continue; - } - - consts->installable = constptr[0].installable || constptr[1].installable; - - // - // Add it to the constraints array... - // - - cupsArrayAdd(ppd->cups_uiconstraints, consts); - } - - // - // Then load new-style constraints... - // - - for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL); - constattr; - constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL)) - { - if (!constattr->value) - { - DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!"); - continue; - } - - for (i = 0, vptr = strchr(constattr->value, '*'); - vptr; - i ++, vptr = strchr(vptr + 1, '*')); - - if (i == 0) - { - DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!"); - continue; - } - - if ((consts = calloc(1, sizeof(ppd_cups_uiconsts_t))) == NULL) - { - DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " - "cupsUIConstraints!"); - return; - } - - if ((constptr = calloc((size_t)i, sizeof(ppd_cups_uiconst_t))) == NULL) - { - free(consts); - DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " - "cupsUIConstraints!"); - return; - } - - consts->num_constraints = i; - consts->constraints = constptr; - - strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver)); - - for (i = 0, vptr = strchr(constattr->value, '*'); - vptr; - i ++, vptr = strchr(vptr, '*'), constptr ++) - { - // - // Extract "*Option Choice" or just "*Option"... - // - - for (vptr ++, ptr = option; *vptr && !_ppd_isspace(*vptr); vptr ++) - if (ptr < (option + sizeof(option) - 1)) - *ptr++ = *vptr; - - *ptr = '\0'; - - while (_ppd_isspace(*vptr)) - vptr ++; - - if (*vptr == '*') - choice[0] = '\0'; - else - { - for (ptr = choice; *vptr && !_ppd_isspace(*vptr); vptr ++) - if (ptr < (choice + sizeof(choice) - 1)) - *ptr++ = *vptr; - - *ptr = '\0'; - } - - if (!_ppd_strncasecmp(option, "Custom", 6) && - !_ppd_strcasecmp(choice, "True")) - { - _ppd_strcpy(option, option + 6); - strlcpy(choice, "Custom", sizeof(choice)); - } - - constptr->option = ppdFindOption(ppd, option); - constptr->choice = ppdFindChoice(constptr->option, choice); - constptr->installable = ppd_is_installable(installable, option); - consts->installable |= constptr->installable; - - if (!constptr->option || (!constptr->choice && choice[0])) - { - DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!", - option, choice)); - break; - } - } - - if (!vptr) - cupsArrayAdd(ppd->cups_uiconstraints, consts); - else - { - free(consts->constraints); - free(consts); - } - } -} - - -// -// 'ppd_test_constraints()' - See if any constraints are active. -// - -static cups_array_t * // O - Array of active constraints -ppd_test_constraints( - ppd_file_t *ppd, // I - PPD file - const char *option, // I - Current option - const char *choice, // I - Current choice - int num_options, // I - Number of additional options - cups_option_t *options, // I - Additional options - int which) // I - Which constraints to test -{ - int i; // Looping var - ppd_cups_uiconsts_t *consts; // Current constraints - ppd_cups_uiconst_t *constptr; // Current constraint - ppd_choice_t key, // Search key - *marked; // Marked choice - cups_array_t *active = NULL; // Active constraints - const char *value, // Current value - *firstvalue; // AP_FIRSTPAGE_Keyword value - char firstpage[255]; // AP_FIRSTPAGE_Keyword string - - - DEBUG_printf(("7ppd_test_constraints(ppd=%p, option=\"%s\", choice=\"%s\", " - "num_options=%d, options=%p, which=%d)", ppd, option, choice, - num_options, options, which)); - - if (!ppd->cups_uiconstraints) - ppd_load_constraints(ppd); - - DEBUG_printf(("9ppd_test_constraints: %d constraints!", - cupsArrayCount(ppd->cups_uiconstraints))); - - cupsArraySave(ppd->marked); - - for (consts = (ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints); - consts; - consts = (ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints)) - { - DEBUG_printf(("9ppd_test_constraints: installable=%d, resolver=\"%s\", " - "num_constraints=%d option1=\"%s\", choice1=\"%s\", " - "option2=\"%s\", choice2=\"%s\", ...", - consts->installable, consts->resolver, - consts->num_constraints, - consts->constraints[0].option->keyword, - consts->constraints[0].choice ? - consts->constraints[0].choice->choice : "", - consts->constraints[1].option->keyword, - consts->constraints[1].choice ? - consts->constraints[1].choice->choice : "")); - - if (consts->installable && which < _PPD_INSTALLABLE_CONSTRAINTS) - continue; // Skip installable option constraint - - if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS) - continue; // Skip non-installable option constraint - - if ((which == _PPD_OPTION_CONSTRAINTS || - which == _PPD_INSTALLABLE_CONSTRAINTS) && option) - { - // - // Skip constraints that do not involve the current option... - // - - for (i = consts->num_constraints, constptr = consts->constraints; - i > 0; - i --, constptr ++) - { - if (!_ppd_strcasecmp(constptr->option->keyword, option)) - break; - - if (!_ppd_strncasecmp(option, "AP_FIRSTPAGE_", 13) && - !_ppd_strcasecmp(constptr->option->keyword, option + 13)) - break; - } - - if (!i) - continue; - } - - DEBUG_puts("9ppd_test_constraints: Testing..."); - - for (i = consts->num_constraints, constptr = consts->constraints; - i > 0; - i --, constptr ++) - { - DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr->option->keyword, - constptr->choice ? constptr->choice->choice : "")); - - if (constptr->choice && - (!_ppd_strcasecmp(constptr->option->keyword, "PageSize") || - !_ppd_strcasecmp(constptr->option->keyword, "PageRegion"))) - { - // - // PageSize and PageRegion are used depending on the selected - // input slot and manual feed mode. Validate against the - // selected page size instead of an individual option... - // - - if (option && choice && - (!_ppd_strcasecmp(option, "PageSize") || - !_ppd_strcasecmp(option, "PageRegion"))) - { - value = choice; - } - else if ((value = cupsGetOption("PageSize", num_options, - options)) == NULL) - if ((value = cupsGetOption("PageRegion", num_options, - options)) == NULL) - if ((value = cupsGetOption("media", num_options, options)) == NULL) - { - ppd_size_t *size = ppdPageSize(ppd, NULL); - - if (size) - value = size->name; - } - - if (value && !_ppd_strncasecmp(value, "Custom.", 7)) - value = "Custom"; - - if (option && choice && - (!_ppd_strcasecmp(option, "AP_FIRSTPAGE_PageSize") || - !_ppd_strcasecmp(option, "AP_FIRSTPAGE_PageRegion"))) - { - firstvalue = choice; - } - else if ((firstvalue = cupsGetOption("AP_FIRSTPAGE_PageSize", - num_options, options)) == NULL) - firstvalue = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options, - options); - - if (firstvalue && !_ppd_strncasecmp(firstvalue, "Custom.", 7)) - firstvalue = "Custom"; - - if ((!value || _ppd_strcasecmp(value, constptr->choice->choice)) && - (!firstvalue || _ppd_strcasecmp(firstvalue, constptr->choice->choice))) - { - DEBUG_puts("9ppd_test_constraints: NO"); - break; - } - } - else if (constptr->choice) - { - // - // Compare against the constrained choice... - // - - if (option && choice && - !_ppd_strcasecmp(option, constptr->option->keyword)) - { - if (!_ppd_strncasecmp(choice, "Custom.", 7)) - value = "Custom"; - else - value = choice; - } - else if ((value = cupsGetOption(constptr->option->keyword, num_options, - options)) != NULL) - { - if (!_ppd_strncasecmp(value, "Custom.", 7)) - value = "Custom"; - } - else if (constptr->choice->marked) - value = constptr->choice->choice; - else - value = NULL; - - // - // Now check AP_FIRSTPAGE_option... - // - - snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s", - constptr->option->keyword); - - if (option && choice && !_ppd_strcasecmp(option, firstpage)) - { - if (!_ppd_strncasecmp(choice, "Custom.", 7)) - firstvalue = "Custom"; - else - firstvalue = choice; - } - else if ((firstvalue = cupsGetOption(firstpage, num_options, - options)) != NULL) - { - if (!_ppd_strncasecmp(firstvalue, "Custom.", 7)) - firstvalue = "Custom"; - } - else - firstvalue = NULL; - - DEBUG_printf(("9ppd_test_constraints: value=%s, firstvalue=%s", value, - firstvalue)); - - if ((!value || _ppd_strcasecmp(value, constptr->choice->choice)) && - (!firstvalue || _ppd_strcasecmp(firstvalue, constptr->choice->choice))) - { - DEBUG_puts("9ppd_test_constraints: NO"); - break; - } - } - else if (option && choice && - !_ppd_strcasecmp(option, constptr->option->keyword)) - { - if (!_ppd_strcasecmp(choice, "None") || !_ppd_strcasecmp(choice, "Off") || - !_ppd_strcasecmp(choice, "False")) - { - DEBUG_puts("9ppd_test_constraints: NO"); - break; - } - } - else if ((value = cupsGetOption(constptr->option->keyword, num_options, - options)) != NULL) - { - if (!_ppd_strcasecmp(value, "None") || !_ppd_strcasecmp(value, "Off") || - !_ppd_strcasecmp(value, "False")) - { - DEBUG_puts("9ppd_test_constraints: NO"); - break; - } - } - else - { - key.option = constptr->option; - - if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) - == NULL || - (!_ppd_strcasecmp(marked->choice, "None") || - !_ppd_strcasecmp(marked->choice, "Off") || - !_ppd_strcasecmp(marked->choice, "False"))) - { - DEBUG_puts("9ppd_test_constraints: NO"); - break; - } - } - } - - if (i <= 0) - { - if (!active) - active = cupsArrayNew(NULL, NULL); - - cupsArrayAdd(active, consts); - DEBUG_puts("9ppd_test_constraints: Added..."); - } - } - - cupsArrayRestore(ppd->marked); - - DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!", - cupsArrayCount(active))); - - return (active); -} diff --git a/ppd/ppd-custom.c b/ppd/ppd-custom.c deleted file mode 100644 index da6cd355c..000000000 --- a/ppd/ppd-custom.c +++ /dev/null @@ -1,98 +0,0 @@ -// -// PPD custom option routines for libppd. -// -// Copyright 2007-2015 by Apple Inc. -// Copyright 1997-2006 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers. -// - -#include "string-private.h" -#include "ppd.h" -#include "debug-internal.h" - - -// -// 'ppdFindCustomOption()' - Find a custom option. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_coption_t * // O - Custom option or NULL -ppdFindCustomOption(ppd_file_t *ppd, // I - PPD file - const char *keyword)// I - Custom option name -{ - ppd_coption_t key; // Custom option search key - - - if (!ppd) - return (NULL); - - strlcpy(key.keyword, keyword, sizeof(key.keyword)); - return ((ppd_coption_t *)cupsArrayFind(ppd->coptions, &key)); -} - - -// -// 'ppdFindCustomParam()' - Find a parameter for a custom option. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_cparam_t * // O - Custom parameter or NULL -ppdFindCustomParam(ppd_coption_t *opt, // I - Custom option - const char *name) // I - Parameter name -{ - ppd_cparam_t *param; // Current custom parameter - - - if (!opt) - return (NULL); - - for (param = (ppd_cparam_t *)cupsArrayFirst(opt->params); - param; - param = (ppd_cparam_t *)cupsArrayNext(opt->params)) - if (!_ppd_strcasecmp(param->name, name)) - break; - - return (param); -} - - -// -// 'ppdFirstCustomParam()' - Return the first parameter for a custom option. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_cparam_t * // O - Custom parameter or NULL -ppdFirstCustomParam(ppd_coption_t *opt) // I - Custom option -{ - if (!opt) - return (NULL); - - return ((ppd_cparam_t *)cupsArrayFirst(opt->params)); -} - - -// -// 'ppdNextCustomParam()' - Return the next parameter for a custom option. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_cparam_t * // O - Custom parameter or NULL -ppdNextCustomParam(ppd_coption_t *opt) // I - Custom option -{ - if (!opt) - return (NULL); - - return ((ppd_cparam_t *)cupsArrayNext(opt->params)); -} diff --git a/ppd/ppd-emit.c b/ppd/ppd-emit.c deleted file mode 100644 index a3ea6f128..000000000 --- a/ppd/ppd-emit.c +++ /dev/null @@ -1,1283 +0,0 @@ -// -// PPD code emission routines for libppd. -// -// Copyright 2007-2019 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "debug-internal.h" -#include "ppd.h" -#if defined(_WIN32) || defined(__EMX__) -# include -#else -# include -#endif // _WIN32 || __EMX__ -#include -#include -#include - - -// -// Local functions... -// - -static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b); - - -// -// Local globals... -// - -static const char ppd_custom_code[] = - "pop pop pop\n" - "<>setpagedevice\n"; - - -// -// 'ppdCollect()' - Collect all marked options that reside in the specified -// section. -// -// The choices array should be freed using @code free@ when you are -// finished with it. -// - -int // O - Number of options marked -ppdCollect(ppd_file_t *ppd, // I - PPD file data - ppd_section_t section, // I - Section to collect - ppd_choice_t ***choices) // O - Pointers to choices -{ - return (ppdCollect2(ppd, section, 0.0, choices)); -} - - -// -// 'ppdCollect2()' - Collect all marked options that reside in the -// specified section and minimum order. -// -// The choices array should be freed using @code free@ when you are -// finished with it. -// -// @since CUPS 1.2/macOS 10.5@ -// - -int // O - Number of options marked -ppdCollect2(ppd_file_t *ppd, // I - PPD file data - ppd_section_t section, // I - Section to collect - float min_order, // I - Minimum OrderDependency value - ppd_choice_t ***choices) // O - Pointers to choices -{ - ppd_choice_t *c; // Current choice - ppd_section_t csection; // Current section - float corder; // Current OrderDependency value - int count; // Number of choices collected - ppd_choice_t **collect; // Collected choices - float *orders; // Collected order values - - - DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)", - ppd, section, min_order, choices)); - - if (!ppd || !choices) - { - if (choices) - *choices = NULL; - - return (0); - } - - // - // Allocate memory for up to N selected choices... - // - - count = 0; - if ((collect = calloc(sizeof(ppd_choice_t *), - (size_t)cupsArrayCount(ppd->marked))) == NULL) - { - *choices = NULL; - return (0); - } - - if ((orders = calloc(sizeof(float), - (size_t)cupsArrayCount(ppd->marked))) == NULL) - { - *choices = NULL; - free(collect); - return (0); - } - - // - // Loop through all options and add choices as needed... - // - - for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); - c; - c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) - { - csection = c->option->section; - corder = c->option->order; - - if (!strcmp(c->choice, "Custom")) - { - ppd_attr_t *attr; // NonUIOrderDependency value - float aorder; // Order value - char asection[17], // Section name - amain[PPD_MAX_NAME + 1], - aoption[PPD_MAX_NAME]; - // *CustomFoo and True - - - for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL); - attr; - attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL)) - if (attr->value && - sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain, - aoption) == 4 && - !strncmp(amain, "*Custom", 7) && - !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True")) - { - // - // Use this NonUIOrderDependency... - // - - corder = aorder; - - if (!strcmp(asection, "DocumentSetup")) - csection = PPD_ORDER_DOCUMENT; - else if (!strcmp(asection, "ExitServer")) - csection = PPD_ORDER_EXIT; - else if (!strcmp(asection, "JCLSetup")) - csection = PPD_ORDER_JCL; - else if (!strcmp(asection, "PageSetup")) - csection = PPD_ORDER_PAGE; - else if (!strcmp(asection, "Prolog")) - csection = PPD_ORDER_PROLOG; - else - csection = PPD_ORDER_ANY; - break; - } - } - - if (csection == section && corder >= min_order) - { - collect[count] = c; - orders[count] = corder; - count ++; - } - } - - // - // If we have more than 1 marked choice, sort them... - // - - if (count > 1) - { - int i, j; // Looping vars - - for (i = 0; i < (count - 1); i ++) - for (j = i + 1; j < count; j ++) - if (orders[i] > orders[j]) - { - c = collect[i]; - corder = orders[i]; - collect[i] = collect[j]; - orders[i] = orders[j]; - collect[j] = c; - orders[j] = corder; - } - } - - free(orders); - - DEBUG_printf(("2ppdCollect2: %d marked choices...", count)); - - // - // 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); - } -} - - -// -// '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 -{ - return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0)); -} - - -// -// 'ppdEmitAfterOrder()' - Emit a subset of the code for marked options -// to a file. -// -// When "limit" is non-zero, this function only emits options whose -// OrderDependency value is greater than or equal to "min_order". -// -// When "limit" is zero, this function is identical to ppdEmit(). -// -// @since CUPS 1.2/macOS 10.5@ -// - -int // O - 0 on success, -1 on failure -ppdEmitAfterOrder( - ppd_file_t *ppd, // I - PPD file record - FILE *fp, // I - File to write to - ppd_section_t section, // I - Section to write - int limit, // I - Non-zero to use min_order - float min_order) // I - Lowest OrderDependency -{ - char *buffer; // Option code - int status; // Return status - - - // - // Range check input... - // - - if (!ppd || !fp) - return (-1); - - // - // Get the string... - // - - buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0f); - - // - // Write it as needed and return... - // - - if (buffer) - { - status = fputs(buffer, fp) < 0 ? -1 : 0; - - free(buffer); - } - else - status = 0; - - return (status); -} - - -// -// '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 -{ - char *buffer, // Option code - *bufptr; // Pointer into code - size_t buflength; // Length of option code - ssize_t bytes; // Bytes written - int status; // Return status - - - // - // Range check input... - // - - if (!ppd || fd < 0) - return (-1); - - // - // Get the string... - // - - buffer = ppdEmitString(ppd, section, 0.0); - - // - // Write it as needed and return... - // - - if (buffer) - { - buflength = strlen(buffer); - bufptr = buffer; - bytes = 0; - - while (buflength > 0) - { -#ifdef _WIN32 - if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0) -#else - if ((bytes = write(fd, bufptr, buflength)) < 0) -#endif // _WIN32 - { - if (errno == EAGAIN || errno == EINTR) - continue; - - break; - } - - buflength -= (size_t)bytes; - bufptr += bytes; - } - - status = bytes < 0 ? -1 : 0; - - free(buffer); - } - else - status = 0; - - return (status); -} - - -// -// 'ppdEmitJCL()' - Emit code for JCL options to a file (for PostScript -// output). -// - -int // O - 0 on success, -1 on failure -ppdEmitJCL(ppd_file_t *ppd, // I - PPD file record - FILE *fp, // I - File to write to - int job_id, // I - Job ID - const char *user, // I - Username - const char *title) // I - Title -{ - return ppdEmitJCLPDF(ppd, fp, job_id, user, title, -1, false); -} - - -// -// 'ppdEmitJCLPDF()' - Emit code for JCL options to a file (for PDF output). -// - -int // O - 0 on success, -1 on failure -ppdEmitJCLPDF(ppd_file_t *ppd, // I - PPD file record - FILE *fp, // I - File to write to - int job_id, // I - Job ID - const char *user, // I - Username - const char *title, // I - Title - int hw_copies, // I - Number of hardware (device) - // copies, -1: PS mode - bool hw_collate) // I - Do we have hardware (device) - // collate? -{ - ppd_attr_t *attr; // PPD attribute - const char *jcl_pdf = NULL; - char *ptr; // Pointer into JCL string - char temp[65], // Local title string - displaymsg[33]; // Local display string - - - // - // Range check the input... - // - - if (!ppd || !ppd->jcl_begin) - return (0); - -#if HAVE_CUPS_3_X - jcl_pdf = ppd->jcl_pdf; -#else - if ((attr = ppdFindAttr(ppd, "JCLToPDFInterpreter", NULL)) != NULL) - jcl_pdf = attr->value; -#endif - - if ((!ppd->jcl_ps && hw_copies < 0) || - (!jcl_pdf && hw_copies >= 0)) - return (0); - - // - // See if the printer supports HP PJL... - // - - if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10)) - { - // - // This printer uses HP PJL commands for output; filter the output - // so that we only have a single "@PJL JOB" command in the header... - // - // To avoid bugs in the PJL implementation of certain vendors' products - // (Xerox in particular), we add a dummy "@PJL" command at the beginning - // of the PJL commands to initialize PJL processing. - // - - ppd_attr_t *charset; // PJL charset - ppd_attr_t *display; // PJL display command - - - if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL) - { - if (!charset->value || _ppd_strcasecmp(charset->value, "UTF-8")) - charset = NULL; - } - - if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL) - { - if (!display->value) - display = NULL; - } - - fputs("\033%-12345X@PJL\n", fp); - for (ptr = ppd->jcl_begin + 9; *ptr;) - if (!strncmp(ptr, "@PJL JOB", 8)) - { - // - // Skip job command... - // - - for (;*ptr; ptr ++) - if (*ptr == '\n') - break; - - if (*ptr) - ptr ++; - } - else - { - // - // Copy line... - // - - for (;*ptr; ptr ++) - { - putc(*ptr, fp); - if (*ptr == '\n') - break; - } - - if (*ptr) - ptr ++; - } - - // - // Clean up the job title... - // - - if (!title) - title = "Unknown"; - - if ((ptr = strrchr(title, '/')) != NULL) - { - // - // Only show basename of file path... - // - - title = ptr + 1; - } - - if (!strncmp(title, "smbprn.", 7)) - { - // - // Skip leading smbprn.######## from Samba jobs... - // - - for (title += 7; *title && isdigit(*title & 255); title ++); - while (_ppd_isspace(*title)) - title ++; - - if ((ptr = strstr(title, " - ")) != NULL) - { - // - // Skip application name in "Some Application - Title of job"... - // - - title = ptr + 3; - } - } - - // - // Replace double quotes with single quotes and UTF-8 characters with - // question marks so that the title does not cause a PJL syntax error. - // - - strlcpy(temp, title, sizeof(temp)); - - for (ptr = temp; *ptr; ptr ++) - if (*ptr == '\"') - *ptr = '\''; - else if (!charset && (*ptr & 128)) - *ptr = '?'; - - // - // CUPS STR #3125: Long PJL JOB NAME causes problems with some printers - // - // Generate the display message, truncating at 32 characters + nul to avoid - // issues with some printer's PJL implementations... - // - - if (!user) - user = "anonymous"; - - snprintf(displaymsg, sizeof(displaymsg), "%d %.8s %.17s", job_id, user, - temp); - - // - // Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode... - // - - if (display && strcmp(display->value, "job")) - fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp); - else if (display && !strcmp(display->value, "rdymsg")) - fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg); - else - fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp, - displaymsg); - - // - // Replace double quotes with single quotes and UTF-8 characters with - // question marks so that the user does not cause a PJL syntax error. - // - - strlcpy(temp, user, sizeof(temp)); - - for (ptr = temp; *ptr; ptr ++) - if (*ptr == '\"') - *ptr = '\''; - else if (!charset && (*ptr & 128)) - *ptr = '?'; - - fprintf(fp, "@PJL SET USERNAME = \"%s\"\n", temp); - } - else - fputs(ppd->jcl_begin, fp); - - ppdEmit(ppd, fp, PPD_ORDER_JCL); - - if (hw_copies < 0) - // PostScript output - fputs(ppd->jcl_ps, fp); - else - { - // PDF output - - int hw_copies_done = 0; - - // There is a "Copies" option in the PPD file, so this option setting - // always takes care of correct implementation of the copies - if (ppdFindOption(ppd,"Copies") != NULL && - hw_copies > 1) - hw_copies_done = 1; - - if (hw_copies > 1 && hw_copies_done == 0 && // HW copies - strncmp(ppd->jcl_begin, "\033%-12345X@", 10) == 0) // PJL - // Add a PJL command to implement the hardware copies - fprintf(fp, "@PJL SET %s=%d\n", hw_collate ? "QTY" : "COPIES", - hw_copies); - - fputs(jcl_pdf, fp); - } - - return (0); -} - - -// -// 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file. -// -// @since CUPS 1.2/macOS 10.5@ -// - -int // O - 0 on success, -1 on failure -ppdEmitJCLEnd(ppd_file_t *ppd, // I - PPD file record - FILE *fp) // I - File to write to -{ - // - // Range check the input... - // - - if (!ppd) - return (0); - - if (!ppd->jcl_end) - { - if (ppd->num_filters == 0) - putc(0x04, fp); - - return (0); - } - - // - // See if the printer supports HP PJL... - // - - if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10)) - { - // - // This printer uses HP PJL commands for output; filter the output - // so that we only have a single "@PJL JOB" command in the header... - // - // To avoid bugs in the PJL implementation of certain vendors' products - // (Xerox in particular), we add a dummy "@PJL" command at the beginning - // of the PJL commands to initialize PJL processing. - // - - fputs("\033%-12345X@PJL\n", fp); - fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp); - fputs(ppd->jcl_end + 9, fp); - } - else - fputs(ppd->jcl_end, fp); - - return (0); -} - - -// -// 'ppdEmitString()' - Get a string containing the code for marked options. -// -// When "min_order" is greater than zero, this function only includes options -// whose OrderDependency value is greater than or equal to "min_order". -// Otherwise, all options in the specified section are included in the -// returned string. -// -// The return string is allocated on the heap and should be freed using -// @code free@ when you are done with it. -// -// @since CUPS 1.2/macOS 10.5@ -// - -char * // O - String containing option code or @code NULL@ if there is no option code -ppdEmitString(ppd_file_t *ppd, // I - PPD file record - ppd_section_t section, // I - Section to write - float min_order) // I - Lowest OrderDependency -{ - int i, j, // Looping vars - count; // Number of choices - ppd_choice_t **choices; // Choices - ppd_size_t *size; // Custom page size - ppd_coption_t *coption; // Custom option - ppd_cparam_t *cparam; // Custom parameter - size_t bufsize; // Size of string buffer needed - char *buffer, // String buffer - *bufptr, // Pointer into buffer - *bufend; // End of buffer - struct lconv *loc; // Locale data - - - DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)", - ppd, section, min_order)); - - // - // Range check input... - // - - if (!ppd) - return (NULL); - - // - // Use PageSize or PageRegion as required... - // - - ppdHandleMedia(ppd); - - // - // Collect the options we need to emit... - // - - if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0) - return (NULL); - - // - // Count the number of bytes that are required to hold all of the - // option code... - // - - for (i = 0, bufsize = 1; i < count; i ++) - { - if (section == PPD_ORDER_JCL) - { - if (!_ppd_strcasecmp(choices[i]->choice, "Custom") && - (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword)) - != NULL) - { - // - // Add space to account for custom parameter substitution... - // - - for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) - { - switch (cparam->type) - { - case PPD_CUSTOM_UNKNOWN : - break; - - case PPD_CUSTOM_CURVE : - case PPD_CUSTOM_INVCURVE : - case PPD_CUSTOM_POINTS : - case PPD_CUSTOM_REAL : - case PPD_CUSTOM_INT : - bufsize += 10; - break; - - case PPD_CUSTOM_PASSCODE : - case PPD_CUSTOM_PASSWORD : - case PPD_CUSTOM_STRING : - if (cparam->current.custom_string) - bufsize += strlen(cparam->current.custom_string); - break; - } - } - } - } - else if (section != PPD_ORDER_EXIT) - { - bufsize += 3; // [{\n - - if ((!_ppd_strcasecmp(choices[i]->option->keyword, "PageSize") || - !_ppd_strcasecmp(choices[i]->option->keyword, "PageRegion")) && - !_ppd_strcasecmp(choices[i]->choice, "Custom")) - { - DEBUG_puts("2ppdEmitString: Custom size set!"); - - bufsize += 37; // %%BeginFeature: *CustomPageSize True\n - bufsize += 50; // Five 9-digit numbers + newline - } - else if (!_ppd_strcasecmp(choices[i]->choice, "Custom") && - (coption = ppdFindCustomOption(ppd, - choices[i]->option->keyword)) - != NULL) - { - bufsize += 23 + strlen(choices[i]->option->keyword) + 6; - // %%BeginFeature: *Customkeyword True\n - - - for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) - { - switch (cparam->type) - { - case PPD_CUSTOM_UNKNOWN : - break; - - case PPD_CUSTOM_CURVE : - case PPD_CUSTOM_INVCURVE : - case PPD_CUSTOM_POINTS : - case PPD_CUSTOM_REAL : - case PPD_CUSTOM_INT : - bufsize += 10; - break; - - case PPD_CUSTOM_PASSCODE : - case PPD_CUSTOM_PASSWORD : - case PPD_CUSTOM_STRING : - bufsize += 3; - if (cparam->current.custom_string) - bufsize += 4 * strlen(cparam->current.custom_string); - break; - } - } - } - else - bufsize += 17 + strlen(choices[i]->option->keyword) + 1 + - strlen(choices[i]->choice) + 1; - // %%BeginFeature: *keyword choice\n - - bufsize += 13; // %%EndFeature\n - bufsize += 22; // } stopped cleartomark\n - } - - if (choices[i]->code) - bufsize += strlen(choices[i]->code) + 1; - else - bufsize += strlen(ppd_custom_code); - } - - // - // Allocate memory... - // - - DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...", - (int)bufsize)); - - if ((buffer = calloc(1, bufsize)) == NULL) - { - free(choices); - return (NULL); - } - - bufend = buffer + bufsize - 1; - loc = localeconv(); - - // - // Copy the option code to the buffer... - // - - for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr)) - if (section == PPD_ORDER_JCL) - { - if (!_ppd_strcasecmp(choices[i]->choice, "Custom") && - choices[i]->code && - (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword)) - != NULL) - { - // - // Handle substitutions in custom JCL options... - // - - char *cptr; // Pointer into code - int pnum; // Parameter number - - - for (cptr = choices[i]->code; *cptr && bufptr < bufend;) - { - if (*cptr == '\\') - { - cptr ++; - - if (isdigit(*cptr & 255)) - { - // - // Substitute parameter... - // - - pnum = *cptr++ - '0'; - while (isdigit(*cptr & 255)) - pnum = pnum * 10 + *cptr++ - '0'; - - for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) - if (cparam->order == pnum) - break; - - if (cparam) - { - switch (cparam->type) - { - case PPD_CUSTOM_UNKNOWN : - break; - - case PPD_CUSTOM_CURVE : - case PPD_CUSTOM_INVCURVE : - case PPD_CUSTOM_POINTS : - case PPD_CUSTOM_REAL : - bufptr = _ppdStrFormatd(bufptr, bufend, - cparam->current.custom_real, - loc); - break; - - case PPD_CUSTOM_INT : - snprintf(bufptr, (size_t)(bufend - bufptr), "%d", - cparam->current.custom_int); - bufptr += strlen(bufptr); - break; - - case PPD_CUSTOM_PASSCODE : - case PPD_CUSTOM_PASSWORD : - case PPD_CUSTOM_STRING : - if (cparam->current.custom_string) - { - strlcpy(bufptr, cparam->current.custom_string, - (size_t)(bufend - bufptr)); - bufptr += strlen(bufptr); - } - break; - } - } - } - else if (*cptr) - *bufptr++ = *cptr++; - } - else - *bufptr++ = *cptr++; - } - } - else if (choices[i]->code) - { - // - // Otherwise just copy the option code directly... - // - - strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1)); - bufptr += strlen(bufptr); - } - } - else if (section != PPD_ORDER_EXIT) - { - // - // Add wrapper commands to prevent printer errors for unsupported - // options... - // - - strlcpy(bufptr, "[{\n", (size_t)(bufend - bufptr + 1)); - bufptr += 3; - - // - // Send DSC comments with option... - // - - DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...", - choices[i]->option->keyword, choices[i]->choice)); - - if ((!_ppd_strcasecmp(choices[i]->option->keyword, "PageSize") || - !_ppd_strcasecmp(choices[i]->option->keyword, "PageRegion")) && - !_ppd_strcasecmp(choices[i]->choice, "Custom")) - { - // - // Variable size; write out standard size options, using the - // parameter positions defined in the PPD file... - // - - ppd_attr_t *attr; // PPD attribute - int pos, // Position of custom value - orientation; // Orientation to use - float values[5]; // Values for custom command - - - strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n", - (size_t)(bufend - bufptr + 1)); - bufptr += 37; - - size = ppdPageSize(ppd, "Custom"); - - memset(values, 0, sizeof(values)); - - if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL) - { - pos = atoi(attr->value) - 1; - - if (pos < 0 || pos > 4) - pos = 0; - } - else - pos = 0; - - values[pos] = size->width; - - if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL) - { - pos = atoi(attr->value) - 1; - - if (pos < 0 || pos > 4) - pos = 1; - } - else - pos = 1; - - values[pos] = size->length; - - // - // According to the Adobe PPD specification, an orientation of 1 - // will produce a print that comes out upside-down with the X - // axis perpendicular to the direction of feed, which is exactly - // what we want to be consistent with non-PS printers. - // - // We could also use an orientation of 3 to produce output that - // comes out rightside-up (this is the default for many large format - // printer PPDs), however for consistency we will stick with the - // value 1. - // - // If we wanted to get fancy, we could use orientations of 0 or - // 2 and swap the width and length, however we don't want to get - // fancy, we just want it to work consistently. - // - // The orientation value is range limited by the Orientation - // parameter definition, so certain non-PS printer drivers that - // only support an Orientation of 0 will get the value 0 as - // expected. - // - - orientation = 1; - - if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", - "Orientation")) != NULL) - { - int min_orient, max_orient; // Minimum and maximum orientations - - - if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient, - &max_orient) != 3) - pos = 4; - else - { - pos --; - - if (pos < 0 || pos > 4) - pos = 4; - - if (orientation > max_orient) - orientation = max_orient; - else if (orientation < min_orient) - orientation = min_orient; - } - } - else - pos = 4; - - values[pos] = (float)orientation; - - for (pos = 0; pos < 5; pos ++) - { - bufptr = _ppdStrFormatd(bufptr, bufend, values[pos], loc); - *bufptr++ = '\n'; - } - - if (!choices[i]->code) - { - // - // 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... - // - - strlcpy(bufptr, ppd_custom_code, (size_t)(bufend - bufptr + 1)); - bufptr += strlen(bufptr); - } - } - else if (!_ppd_strcasecmp(choices[i]->choice, "Custom") && - (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword)) - != NULL) - { - // - // Custom option... - // - - const char *s; // Pointer into string value - cups_array_t *params; // Parameters in the correct output - // order - - - params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL); - - for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) - cupsArrayAdd(params, cparam); - - snprintf(bufptr, (size_t)(bufend - bufptr + 1), - "%%%%BeginFeature: *Custom%s True\n", coption->keyword); - bufptr += strlen(bufptr); - - for (cparam = (ppd_cparam_t *)cupsArrayFirst(params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(params)) - { - switch (cparam->type) - { - case PPD_CUSTOM_UNKNOWN : - break; - - case PPD_CUSTOM_CURVE : - case PPD_CUSTOM_INVCURVE : - case PPD_CUSTOM_POINTS : - case PPD_CUSTOM_REAL : - bufptr = _ppdStrFormatd(bufptr, bufend, - cparam->current.custom_real, loc); - *bufptr++ = '\n'; - break; - - case PPD_CUSTOM_INT : - snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%d\n", - cparam->current.custom_int); - bufptr += strlen(bufptr); - break; - - case PPD_CUSTOM_PASSCODE : - case PPD_CUSTOM_PASSWORD : - case PPD_CUSTOM_STRING : - *bufptr++ = '('; - - if (cparam->current.custom_string) - { - for (s = cparam->current.custom_string; *s; s ++) - { - if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127) - { - snprintf(bufptr, (size_t)(bufend - bufptr + 1), - "\\%03o", *s & 255); - bufptr += strlen(bufptr); - } - else - *bufptr++ = *s; - } - } - - *bufptr++ = ')'; - *bufptr++ = '\n'; - break; - } - } - - cupsArrayDelete(params); - } - else - { - snprintf(bufptr, (size_t)(bufend - bufptr + 1), - "%%%%BeginFeature: *%s %s\n", - choices[i]->option->keyword, choices[i]->choice); - bufptr += strlen(bufptr); - } - - if (choices[i]->code && choices[i]->code[0]) - { - j = (int)strlen(choices[i]->code); - memcpy(bufptr, choices[i]->code, (size_t)j); - bufptr += j; - - if (choices[i]->code[j - 1] != '\n') - *bufptr++ = '\n'; - } - - strlcpy(bufptr, "%%EndFeature\n" - "} stopped cleartomark\n", (size_t)(bufend - bufptr + 1)); - bufptr += strlen(bufptr); - - DEBUG_printf(("2ppdEmitString: Offset in string is %d...", - (int)(bufptr - buffer))); - } - else if (choices[i]->code) - { - strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1)); - bufptr += strlen(bufptr); - } - - // - // Nul-terminate, free, and return... - // - - *bufptr = '\0'; - - free(choices); - - return (buffer); -} - - -// -// 'ppdHandleMedia()' - Handle media selection... -// - -void -ppdHandleMedia(ppd_file_t *ppd) // I - PPD file -{ - ppd_choice_t *manual_feed, // ManualFeed choice, if any - *input_slot; // InputSlot choice, if any - ppd_size_t *size; // Current media size - ppd_attr_t *rpr; // RequiresPageRegion value - - - // - // This function determines what page size code to use, if any, for the - // current media size, InputSlot, and ManualFeed selections. - // - // We use the PageSize code if: - // - // 1. A custom media size is selected. - // 2. ManualFeed and InputSlot are not selected (or do not exist). - // 3. ManualFeed is selected but is False and InputSlot is not selected or - // the selection has no code - the latter check done to support "auto" or - // "printer default" InputSlot options. - // - // We use the PageRegion code if: - // - // 4. RequiresPageRegion does not exist and the PPD contains cupsFilter - // keywords, indicating this is a CUPS-based driver. - // 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any - // InputSlot or ManualFeed selection) and is True. - // - // If none of the 5 conditions are true, no page size code is used and we - // unmark any existing PageSize or PageRegion choices. - // - - if ((size = ppdPageSize(ppd, NULL)) == NULL) - return; - - manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed"); - input_slot = ppdFindMarkedChoice(ppd, "InputSlot"); - - if (input_slot != NULL) - rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice); - else - rpr = NULL; - - if (!rpr) - rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All"); - - if (!_ppd_strcasecmp(size->name, "Custom") || - (!manual_feed && !input_slot) || - (manual_feed && !_ppd_strcasecmp(manual_feed->choice, "False") && - (!input_slot || (input_slot->code && !input_slot->code[0]))) || - (!rpr && ppd->num_filters > 0)) - { - // - // Use PageSize code... - // - - ppdMarkOption(ppd, "PageSize", size->name); - } - else if (rpr && rpr->value && !_ppd_strcasecmp(rpr->value, "True")) - { - // - // Use PageRegion code... - // - - ppdMarkOption(ppd, "PageRegion", size->name); - } - else - { - // - // Do not use PageSize or PageRegion code... - // - - ppd_choice_t *page; // PageSize/Region choice, if any - - if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL) - { - // - // Unmark PageSize... - // - - page->marked = 0; - cupsArrayRemove(ppd->marked, page); - } - - if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL) - { - // - // Unmark PageRegion... - // - - page->marked = 0; - cupsArrayRemove(ppd->marked, page); - } - } -} - - -// -// 'ppd_compare_cparams()' - Compare the order of two custom parameters. -// - -static int // O - Result of comparison -ppd_compare_cparams(ppd_cparam_t *a, // I - First parameter - ppd_cparam_t *b) // I - Second parameter -{ - return (a->order - b->order); -} diff --git a/ppd/ppd-filter.c b/ppd/ppd-filter.c deleted file mode 100644 index d5fa680ac..000000000 --- a/ppd/ppd-filter.c +++ /dev/null @@ -1,2047 +0,0 @@ -// -// Filter functions support for libppd. -// -// Copyright © 2020-2022 by Till Kamppeter. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "config.h" -#include "ppd-filter.h" -#include "ppd.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern char **environ; - -// -// 'ppdFilterCUPSWrapper()' - Wrapper function to use a filter function as -// classic CUPS filter -// - -int // O - Exit status -ppdFilterCUPSWrapper( - int argc, // I - Number of command-line args - char *argv[], // I - Command-line arguments - cf_filter_function_t filter, // I - Filter function - void *parameters, // I - Filter function parameters - int *JobCanceled) // I - Var set to 1 when job canceled -{ - int inputfd; // Print file descriptor - int inputseekable; // Is the input seekable (actual file - // not stdin)? - int num_options = 0; // Number of print options - cups_option_t *options = NULL; // Print options - cf_filter_data_t filter_data; - const char *val; - char buf[256]; - int retval = 0; - - - // - // Make sure status messages are not buffered... - // - - setbuf(stderr, NULL); - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Check command-line... - // - - if ((argc < 6 || argc > 7) && argc != 1) - { - 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) - { - inputfd = 0; // stdin - inputseekable = 0; - } - else - { - // - // Try to open the print file... - // - - if ((inputfd = open(argv[6], O_RDONLY)) < 0) - { - if (!JobCanceled) - { - fprintf(stderr, "DEBUG: Unable to open \"%s\": %s\n", argv[6], - strerror(errno)); - fprintf(stderr, "ERROR: Unable to open print file"); - } - - return (1); - } - - inputseekable = 1; - } - - // - // Process command-line options... - // - - options = NULL; - if (argc > 5) - num_options = cupsParseOptions(argv[5], 0, &options); - - if ((filter_data.printer = getenv("PRINTER")) == NULL) - filter_data.printer = argv[0]; - filter_data.job_id = argc > 1 ? atoi(argv[1]) : 0; - filter_data.job_user = argc > 2 ? argv[2] : NULL; - filter_data.job_title = argc > 3 ? argv[3] : NULL; - filter_data.copies = argc > 4 ? atoi(argv[4]) : 1; - filter_data.content_type = getenv("CONTENT_TYPE"); - filter_data.final_content_type = getenv("FINAL_CONTENT_TYPE"); - filter_data.job_attrs = NULL; // We use command line options - // The following two will get populated by ppdFilterLoadPPD() - filter_data.printer_attrs = NULL; // We use the queue's PPD file - filter_data.header = NULL; // CUPS Raster header of queue's PPD - filter_data.num_options = num_options; - filter_data.options = options; // Command line options from 5th arg - filter_data.back_pipe[0] = 3; // CUPS uses file descriptor 3 for - filter_data.back_pipe[1] = 3; // the back channel - filter_data.side_pipe[0] = 4; // CUPS uses file descriptor 4 for - filter_data.side_pipe[1] = 4; // the side channel - filter_data.extension = NULL; - filter_data.logfunc = cfCUPSLogFunc; // Logging scheme of CUPS - filter_data.logdata = NULL; - filter_data.iscanceledfunc = cfCUPSIsCanceledFunc; // Job-is-canceled - // function - filter_data.iscanceleddata = JobCanceled; - - // - // CUPS_FONTPATH (Usually /usr/share/cups/fonts) - // - - if (cupsGetOption("cups-fontpath", - filter_data.num_options, filter_data.options) == NULL) - { - if ((val = getenv("CUPS_FONTPATH")) == NULL) - { - val = CUPS_DATADIR; - snprintf(buf, sizeof(buf), "%s/fonts", val); - val = buf; - } - if (val[0] != '\0') - filter_data.num_options = - cupsAddOption("cups-fontpath", val, - filter_data.num_options, &(filter_data.options)); - } - - // - // Load and prepare the PPD file, also attach it as extension "libppd" - // to the filter_data structure - // - - if (getenv("PPD")) - retval = ppdFilterLoadPPDFile(&filter_data, getenv("PPD")); - - // - // Fire up the filter function (output to stdout, file descriptor 1) - // - - if (!retval) - retval = filter(inputfd, 1, inputseekable, &filter_data, parameters); - - // - // Clean up - // - - cupsFreeOptions(filter_data.num_options, filter_data.options); - ppdFilterFreePPDFile(&filter_data); - - return retval; -} - - -// -// 'ppdFilterLoadPPDFile()' - When preparing the filter data structure -// for calling one or more filter -// functions, load the PPD file specified -// by its file name. If the file name is -// NULL or empty, do nothing. If the PPD -// got successfully loaded add its data to -// the filter data structure as extension -// named "libppd", so that filters -// functions designed for using PPDs can -// access it. Then read out all the -// relevant data with the -// ppdFilterLoadPPD() function. -// - -int // O - Error status -ppdFilterLoadPPDFile(cf_filter_data_t *data, // I/O - Job and printer data - const char *ppdfile) // I - PPD file name -{ - ppd_filter_data_ext_t *filter_data_ext; // Record for "libppd" extension - ppd_file_t *ppd; // PPD data - cf_logfunc_t log = data->logfunc; // Log function - void *ld = data->logdata; // log function data - - if (!ppdfile || !ppdfile[0]) - return (-1); - - if ((ppd = ppdOpenFile(ppdfile)) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterLoadPPDFile: Could not load PPD file %s: %s", - ppdfile, strerror(errno)); - return (-1); - } - - filter_data_ext = - (ppd_filter_data_ext_t *)calloc(1, sizeof(ppd_filter_data_ext_t)); - - filter_data_ext->ppdfile = strdup(ppdfile); // PPD file name - filter_data_ext->ppd = ppd; // PPD data - cfFilterDataAddExt(data, PPD_FILTER_DATA_EXT, filter_data_ext); - - return ppdFilterLoadPPD(data); -} - - -// -// 'ppdFilterLoadPPD()' - When preparing the filter data structure for -// calling one or more filter functions, and a -// PPD file is attached as "libppd" extension, -// set up the PPD's cache, mark default -// settings and if supplied in the data -// structure, also option settings. Then -// convert the capability and option info in -// the PPD file into printer IPP attributes and -// what cannot be represented in IPP as option -// settings and add these results to the filter -// data structure. This allows to use the (by -// itself not PPD-supporting) filter function -// to do its work for the printer represented -// by the PPD file. Add the CUPS PPD file data -// structure with embedded CUPS PPD cache data -// structure (including PPD option setting -// presets for all possible print-color-mode, -// print-quality, and print-content-optimize -// settings) to the filter data structure for -// use by libppd's PPD-requiring filter -// functions. -// - -int // O - Error status -ppdFilterLoadPPD(cf_filter_data_t *data) // I/O - Job and printer data -{ - int i; - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataGetExt(data, - PPD_FILTER_DATA_EXT); - ppd_file_t *ppd; - int num_job_attr_options = 0; - cups_option_t *job_attr_options = NULL; - cups_option_t *opt; - ppd_attr_t *ppd_attr; - ppd_choice_t *choice; - ipp_attribute_t *attr; - ipp_t *col; - const char *val; - char *lastfilter = NULL; - bool hw_copies = false, - hw_collate = false; - const char *page_size, *media; - const char *q1_choice, *q2_choice, *q3_choice; - char buf[1024]; - char cm_qualifier_tmp[1024]; - const char *cm_profile_key; - char *resolution, // Output resolution - *media_type; // Media type - ppd_profile_t *profile; // Color profile - cf_logfunc_t log = data->logfunc; // Log function - void *ld = data->logdata; // log function data - - if (!filter_data_ext || !filter_data_ext->ppd) - return (-1); - - // - // Prepare PPD file and mark options - // - - ppd = filter_data_ext->ppd; - ppd->cache = ppdCacheCreateWithPPD(ppd); - ppdMarkDefaults(ppd); - ppdMarkOptions(ppd, data->num_options, data->options); - num_job_attr_options = ppdGetOptions(&job_attr_options, data->printer_attrs, - data->job_attrs, ppd); - for(i = 0, opt = job_attr_options; i < num_job_attr_options; i++, opt++) - data->num_options = cupsAddOption(opt->name, opt->value, - data->num_options, &(data->options)); - cupsFreeOptions(num_job_attr_options, job_attr_options); - ppdMarkOptions(ppd, data->num_options, data->options); - ppdHandleMedia(ppd); - - // - // Pass on PPD attributes - // - - // Pass on "PWGRaster" PPD attribute (for PWG Raster output) - if ((ppd_attr = ppdFindAttr(ppd, "PWGRaster", 0)) != 0 && - (!strcasecmp(ppd_attr->value, "true") || - !strcasecmp(ppd_attr->value, "on") || - !strcasecmp(ppd_attr->value, "yes"))) - data->num_options = cupsAddOption("media-class", "PwgRaster", - data->num_options, &(data->options)); - - // Pass on "cupsEvenDuplex" PPD attribute - if ((ppd_attr = ppdFindAttr(ppd, "cupsEvenDuplex", 0)) != NULL) - data->num_options = cupsAddOption("even-duplex", ppd_attr->value, - data->num_options, &(data->options)); - - // Pass on "cupsBackSide" (or "cupsFlipDuplex") PPD attribute - if ((ppd_attr = ppdFindAttr(ppd, "cupsBackSide", 0)) != NULL) - { - ppd->flip_duplex = 0; // "cupsBackSide" has priority - data->num_options = cupsAddOption("back-side-orientation", ppd_attr->value, - data->num_options, &(data->options)); - } - else if (ppd->flip_duplex) // "cupsFlipDuplex" same as "Rotated" - data->num_options = cupsAddOption("back-side-orientation", "Rotated", - data->num_options, &(data->options)); - - // Pass on "APDuplexRequiresFlippedMargin" PPD attribute - if ((ppd_attr = ppdFindAttr(ppd, "APDuplexRequiresFlippedMargin", 0)) != NULL) - data->num_options = cupsAddOption("duplex-requires-flipped-margin", - ppd_attr->value, - data->num_options, &(data->options)); - - // Pass on "cupsRasterVersion" PPD attribute - if ((ppd_attr = ppdFindAttr(ppd,"cupsRasterVersion", 0)) != NULL) - data->num_options = cupsAddOption("cups-raster-version", - ppd_attr->value, - data->num_options, &(data->options)); - - // Pass on "DefaultCenterOfPixel" PPD attribute (for Ghostscript) - if ((ppd_attr = ppdFindAttr(ppd,"DefaultCenterOfPixel", 0)) != NULL) - data->num_options = cupsAddOption("center-of-pixel", - ppd_attr->value, - data->num_options, &(data->options)); - - // Set short-edge Duplex when booklet printing is selected - if ((val = cupsGetOption("booklet", - data->num_options, data->options)) != NULL && - (!strcasecmp(val, "on") || !strcasecmp(val, "yes") || - !strcasecmp(val, "true")) && - ppd->cache->sides_option && - ppd->cache->sides_2sided_short && - ppdFindOption(ppd, ppd->cache->sides_option)) - ppdMarkOption(ppd, ppd->cache->sides_option, - ppd->cache->sides_2sided_short); - - // Let the PDF filter do mirrored printing - if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL) - { - choice->marked = 0; - data->num_options = cupsAddOption("mirror", "true", - data->num_options, &(data->options)); - } - - // - // Find out whether we can do hardware copies/collate - // - - if (data->copies == 1) - { - // 1 copy, hardware copies/collate do not make difference - hw_copies = false; - hw_collate = false; - } - else if (!ppd->manual_copies) - { - // Hardware copy generation available - hw_copies = true; - // Check output format (FINAL_CONTENT_TYPE env variable) whether it is - // of a driverless IPP printer (PDF, Apple Raster, PWG Raster, PCLm). - // These printers do always hardware collate if they do hardware copies. - // https://github.com/apple/cups/issues/5433 - // This also assumes that if a classic PDF printer (non-IPP printer printing - // with JCL/PJL-controlled PDF) supports hardware copies that it also does - // hardware collate - if (data->final_content_type && - (strcasestr(data->final_content_type, "/pdf") || - strcasestr(data->final_content_type, "/vnd.cups-pdf") || - strcasestr(data->final_content_type, "/pwg-raster") || - strcasestr(data->final_content_type, "/urf") || - strcasestr(data->final_content_type, "/PCLm"))) - { - // If PPD has "Collate" option set to not collate, do not set - // hw_collate, especially important to set correct JCL/PJL on - // "classic" PDF printers (they will always collate otherwise) - if ((choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL && - (!strcasecmp(choice->choice, "off") || - !strcasecmp(choice->choice, "no") || - !strcasecmp(choice->choice, "false"))) - hw_collate = false; - else - hw_collate = true; - } - else - { - // Check whether printer hardware-collates with current PPD settings - if ((choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL && - (!strcasecmp(choice->choice, "on") || - !strcasecmp(choice->choice, "yes") || - !strcasecmp(choice->choice, "true"))) - { - // Printer can collate, but also for the currently marked PPD - // features? - ppd_option_t *opt = ppdFindOption(ppd, "Collate"); - hw_collate = (opt && !opt->conflicted); - } - else - hw_collate = false; - } - } - else - { - // We have "*cupsManualCopies: True" => - // Software copies/collate - hw_copies = false; - hw_collate = false; - } - - if (!hw_copies) - { - // Software copies - // Make sure any hardware copying is disabled - ppdMarkOption(ppd, "Copies", "1"); - ppdMarkOption(ppd, "JCLCopies", "1"); - } - else - { - // Hardware copies - // If there is a "Copies" option in the PPD file, assure that hardware - // copies are implemented as described by this option - snprintf(buf, sizeof(buf), "%d", hw_copies); - ppdMarkOption(ppd, "Copies", buf); - } - - // Software collate - if (!hw_collate) - // Disable any hardware collate (in JCL) - ppdMarkOption(ppd, "Collate", "False"); - - // Add options telling whether we want hardware copies/collate or not - data->num_options = cupsAddOption("hardware-copies", - (hw_copies ? "true" : "false"), - data->num_options, &(data->options)); - data->num_options = cupsAddOption("hardware-collate", - (hw_collate ? "true" : "false"), - data->num_options, &(data->options)); - - // - // Pass on color management attributes - // - - // Get color space - if ((ppd_attr = ppdFindAttr (ppd, "cupsICCQualifier1", NULL)) != NULL && - ppd_attr->value && ppd_attr->value[0]) - choice = ppdFindMarkedChoice(ppd, ppd_attr->value); - else if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) == NULL) - choice = ppdFindMarkedChoice(ppd, "ColorSpace"); - if (choice && choice->choice && choice->choice[0]) - q1_choice = choice->choice; - else - q1_choice = ""; - - // Get media type - if ((ppd_attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL && - ppd_attr->value && ppd_attr->value[0]) - choice = ppdFindMarkedChoice(ppd, ppd_attr->value); - else - choice = ppdFindMarkedChoice(ppd, "MediaType"); - if (choice && choice->choice && choice->choice[0]) - q2_choice = choice->choice; - else - q2_choice = ""; - - // Get resolution - if ((ppd_attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL && - ppd_attr->value && ppd_attr->value[0]) - choice = ppdFindMarkedChoice(ppd, ppd_attr->value); - else - choice = ppdFindMarkedChoice(ppd, "Resolution"); - if (choice && choice->choice && choice->choice[0]) - q3_choice = choice->choice; - else - { - ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL); - if (ppd_attr && ppd_attr->value && ppd_attr->value[0]) - q3_choice = ppd_attr->value; - else - q3_choice = ""; - } - - // create a string for the option - snprintf(cm_qualifier_tmp, sizeof(cm_qualifier_tmp), - "%s.%s.%s", q1_choice, q2_choice, q3_choice); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: Color profile qualifier determined from job and PPD data '%s'", - cm_qualifier_tmp); - - // Supply qualifier as option - data->num_options = cupsAddOption("cm-profile-qualifier", cm_qualifier_tmp, - data->num_options, &(data->options)); - - // get profile attr, falling back to CUPS - cm_profile_key = "APTiogaProfile"; - ppd_attr = ppdFindAttr(ppd, cm_profile_key, NULL); - if (ppd_attr == NULL) - { - cm_profile_key = "cupsICCProfile"; - ppd_attr = ppdFindAttr(ppd, cm_profile_key, NULL); - } - - if (ppd_attr == NULL) - { - // neither - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: No ICC profiles specified in PPD"); - } - else - { - // Try to find a profile that matches the qualifier exactly - for (;ppd_attr != NULL; - ppd_attr = ppdFindNextAttr(ppd, cm_profile_key, NULL)) - { - // Invalid entry - if (ppd_attr->spec == NULL || ppd_attr->value == NULL) - continue; - - // Matches the qualifier - if (strcmp(cm_qualifier_tmp, ppd_attr->spec) == 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: Found ICC profile %s in PPD for qualifier '%s'", - ppd_attr->value, ppd_attr->spec); - break; - } - } - - // No match - if (ppd_attr == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: No ICC profile in PPD for qualifier '%s'", - cm_qualifier_tmp); - } - else - { - // expand to a full path if not already specified - if (ppd_attr->value[0] != '/') - { - if ((val = getenv("CUPS_DATADIR")) == NULL) - val = CUPS_DATADIR; - snprintf(buf, sizeof(buf), - "%s/profiles/%s", val, ppd_attr->value); - } - else - { - strncpy(buf, ppd_attr->value, sizeof(buf) - 1); - if (strlen(ppd_attr->value) > 1023) - buf[1023] = '\0'; - } - - // check the file exists - if (access(buf, 0)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterLoadPPD: ICC profile %s in PPD does not exist", - buf); - } - else - { - // Supply path as fallback profile option - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: Using ICC profile %s as fallback if colord does not supply another one", - buf); - data->num_options = - cupsAddOption("cm-fallback-profile", buf, - data->num_options, &(data->options)); - } - } - } - - // - // Find a color profile matching the current options... - // - - if (cupsGetOption("profile", data->num_options, data->options) == NULL) - { - if ((choice = ppdFindMarkedChoice(ppd, "Resolution")) != NULL) - resolution = choice->choice; - else - resolution = "-"; - if ((choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL) - media_type = choice->choice; - else - media_type = "-"; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: Searching for profile \"%s/%s\"...", - resolution, media_type); - - for (i = 0, profile = ppd->profiles; i < ppd->num_profiles; - i ++, profile ++) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: \"%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] == '-')) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: MATCH"); - break; - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: no."); - } - - // - // If we found a color profile, use it! - // - - if (i >= ppd->num_profiles) - profile = NULL; - } - else - profile = NULL; - - if (profile) - { - snprintf(buf, sizeof(buf), - "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - (int)(profile->density * 1000.0), - (int)(profile->gamma * 1000.0), - (int)(profile->matrix[0][0] * 1000.0), - (int)(profile->matrix[0][1] * 1000.0), - (int)(profile->matrix[0][2] * 1000.0), - (int)(profile->matrix[1][0] * 1000.0), - (int)(profile->matrix[1][1] * 1000.0), - (int)(profile->matrix[1][2] * 1000.0), - (int)(profile->matrix[2][0] * 1000.0), - (int)(profile->matrix[2][1] * 1000.0), - (int)(profile->matrix[2][2] * 1000.0)); - data->num_options = cupsAddOption("profile", buf, - data->num_options, &(data->options)); - } - - // - // Convert the settings and properties in the PPD into printer IPP - // attributes - // - - data->printer_attrs = ppdLoadAttributes(ppd); - - // - // Generate a CUPS Raster sample header, some filters can easily take - // data from it or make it the base for actual Raster headers. The - // header is based on the pseudo-PostScript code at the option - // settings in the PPD file, this is usually the more reliable way to - // obtain the correct resolution. - // - // This only works if the PPD file is actually for a CUPS Raster - // driver or generated by CUPS for driverless printers via the - // "everywhere" pseudo-driver, as other PPD files do not contain all - // resolution, page geometry and color space/depth info in - // pseudo-PostScript code and the ppdRasterInterpretPPD() function - // only parses the pseudo-PostScript code not the names of the - // options and choices. - // - // Therefore we create the sample header only if the PPD is actually - // for a CUPS Raster driver or an "everywhere" PPD from CUPS. - // - - data->header = NULL; - for (i = 0; i < ppd->num_filters; i ++) - { - if (!strncasecmp(ppd->filters[i], "application/vnd.cups-raster", 27) || - (!strncasecmp(ppd->filters[i], "image/urf", 9) && - !ppdFindAttr(ppd, "cupsUrfSupported", NULL)) || - (!strncasecmp(ppd->filters[i], "image/pwg-raster", 16) && - !ppdFindAttr(ppd, "pwg-raster-document-type-supported", NULL))) - { - // We have a CUPS Raster driver PPD file - data->header = - (cups_page_header2_t *)calloc(1, sizeof(cups_page_header2_t)); - if (ppdRasterInterpretPPD(data->header, ppd, - data->num_options, data->options, NULL) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterLoadPPD: Unable to generate CUPS Raster sample header."); - free(data->header); - data->header = NULL; - } - break; - } - } - - // - // Replace the "PageSize" option by a media-col attribute, as this - // one selects the media much more reliably by numeric size - // dimensions and margins and not by name. - // - // PPDs have special page size names for page size variants (A4, - // A4.Borderless, A4.Duplex, A4.Transverse) which cannot get mapped - // into the printer IPP attributes (media-col-database) and so cannot - // get easily selected by the PPD-support-free core filter functions - // in libcupsfilters. - // - // Usually, the media-col attribute does not need to get copied from - // printer attributes to job attributes, but having it in the job - // attributes tells the filters that the user has requested a page - // size, as at least some filters overtake the sizes of the input - // pages when the user does not request a page size. - // - - page_size = cupsGetOption("PageSize", data->num_options, data->options); - media = cupsGetOption("media", data->num_options, data->options); - if ((page_size || media) && - (attr = ippFindAttribute(data->printer_attrs, "media-col-default", - IPP_TAG_ZERO)) != NULL) - { - // We have already applied the settings of these options to the - // PPD file and converted the PPD option settings into the printer - // IPP attributes. The media size and margins corresponding to the - // name supplied with these options is now included in the printer - // IPP attributes as "media-col-default". Here we remove the - // "PageSize" and "media" options and add "media-col" to the job - // attributes as a copy of "media-col-default" in the printer - // attributes. We do this only if the size specified by "PageSize" - // or "media" is not a custom size ("Custom.XXxYYunit") as a - // custom size cannot get marked in the PPD file and so not set as - // default page size, so it does not make it into - // media-col-default. In this case we only remove "media-col". - - int is_custom = 0; - - if (page_size) - { - if (strncasecmp(page_size, "Custom.", 7) != 0) - data->num_options = cupsRemoveOption("PageSize", data->num_options, - &(data->options)); - else - is_custom = 1; - } - - if (media) - { - if ((val = strcasestr(media, "Custom.")) == NULL || - (val != media && *(val - 1) != ',')) - data->num_options = cupsRemoveOption("media", data->num_options, - &(data->options)); - else - is_custom = 1; - } - - data->num_options = cupsRemoveOption("media-col", data->num_options, - &(data->options)); - ippDeleteAttribute(data->job_attrs, - ippFindAttribute(data->job_attrs, "media-col", - IPP_TAG_ZERO)); - - if (!is_custom) - { - // String from IPP attribute for option list - ippAttributeString(attr, buf, sizeof(buf)); - data->num_options = cupsAddOption("media-col", buf, - data->num_options, &(data->options)); - - // Copy the default media-col - col = ippGetCollection(attr, 0); - ippAddCollection(data->job_attrs, IPP_TAG_PRINTER, "media-col", col); - } - } - - // - // Find out whether the PDF filter (cfFilterPDFToPDF() or - // cfFilterImageToPDF()) should log the pages or whether the last - // filter (usually the printer driver) should do it. - // - - if ((val = cupsGetOption("pdf-filter-page-logging", - data->num_options, data->options)) == NULL || - strcasecmp(val, "auto") == 0) - { - int page_logging = -1; - if (data->final_content_type == NULL) - { - // Final data MIME type not known, we cannot determine - // whether we have to log pages, so do not log. - page_logging = 0; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: No final data MIME type known, so we " - "cannot determine whether the PDF filter has to log pages " - "or not, so turning off page logging for the PDF filter."); - } - // Proceed depending on number of cupsFilter(2) lines in PPD - else if (ppd->num_filters == 0) - { - // No filter line, manufacturer-supplied PostScript PPD - // In this case cfFilterPSToPS, called by cfFilterPDFToPS, does the - // logging - page_logging = 0; - } - else - { - // Filter(s) specified by filter line(s), determine the one which got - // actually used via final data MIME type - bool cupsfilter2 = (ppdFindAttr(ppd, "cupsFilter2", NULL) != NULL); - for (lastfilter = (char *)cupsArrayFirst(ppd->cache->filters); - lastfilter; - lastfilter = (char *)cupsArrayNext(ppd->cache->filters)) - { - char *p = lastfilter; - if (cupsfilter2) - { - // Skip first word as the final content type is the second - while (!isspace(*p)) p ++; - while (isspace(*p)) p ++; - } - if (strlen(p) >= strlen(data->final_content_type) && - !strncasecmp(data->final_content_type, p, - strlen(data->final_content_type)) && - !isalnum(p[strlen(data->final_content_type)])) { - break; - } - } - } - if (page_logging == -1) - { - if (lastfilter) - { - // Get the name of the last filter, without mime type and cost - char *p = lastfilter; - char *q = p + strlen(p) - 1; - while (!isspace(*q) && *q != '/') q --; - lastfilter = q + 1; - // Check whether the PDF filter has to log - if (!strcasecmp(lastfilter, "-")) - { - // No filter defined in the PPD - // If output data is PDF, cfFilterPDFToPDF() is last - // filter (PDF printer) and has to log - // If output data is Postscript, ppdFilterPSToPS() should log, - // but it only logs with a pure PostScript PPD (no filter definition), - // so the PDF filter has to log - // If output data is Apple/PWG Raster or PCLm, cfFilter*ToRaster() is - // last filter (Driverless IPP printer) and cfFilterPDFToPDF() - // also has to log - if (strcasestr(data->final_content_type, "/pdf") || - strcasestr(data->final_content_type, "/vnd.cups-pdf") || - strcasestr(data->final_content_type, "/postscript") || - strcasestr(data->final_content_type, "/vnd.cups-postscript") || - strcasestr(data->final_content_type, "/pwg-raster") || - strcasestr(data->final_content_type, "/urf") || - strcasestr(data->final_content_type, "/pclm")) - page_logging = 1; - else - page_logging = 0; - } - else if (!strcasecmp(lastfilter, "pdftopdf") || - !strcasecmp(lastfilter, "imagetopdf")) - { - // cfFilterPDFToPDF() is last filter (PDF printer) - page_logging = 1; - } - else if (!strcasecmp(lastfilter, "gstopxl")) - { - // cfFilterGhostscript() with PCL-XL output is last filter, - // this is a Ghostscript-based filter without access to the - // pages of the file to be printed, so the PDF filter has to - // log the pages - page_logging = 1; - } - else if (!strcasecmp(lastfilter + strlen(lastfilter) - 8, - "toraster") || - !strcasecmp(lastfilter + strlen(lastfilter) - 5, - "topwg")) - { - // On IPP Everywhere printers which accept PWG Raster data one - // of cfFilterGhostscript(), cfFilterPDFToRaster(), or - // cfFilterMuPDFToPWG() is the last filter. These filters do not - // log pages so the PDF filter has to do it - page_logging = 1; - } - else if (!strcasecmp(lastfilter, "foomatic-rip")) - { - // foomatic-rip is last filter, foomatic-rip is mainly used as - // Ghostscript wrapper to use Ghostscript's built-in printer - // drivers. Here there is also no access to the pages so that we - // delegate the logging to the PDF filter - page_logging = 1; - } - else if (!strncasecmp(lastfilter + strlen(lastfilter) - 4, "tops", 4)) - { - // Something-to PostScript filter is the last filter, so output is - // PostScript but these filters only log with pure PostScript PPD - // (no filter definition), so the PDF filter has to log - page_logging = 1; - } - else if (!strcasecmp(lastfilter, "hpps")) - { - // hpps is last filter, hpps is part of HPLIP and it is a bug that - // it does not do the page logging. - page_logging = 1; - } - else - { - // All the other filters (printer drivers) log pages as expected. - page_logging = 0; - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterLoadPPD: Last filter could not get determined, " - "page logging by the PDF filter turned off."); - page_logging = 0; - } - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterLoadPPD: Last filter determined by the PPD: %s; " - "Final data MIME type: %s => PDF filter will %slog pages in " - "page_log.", - (lastfilter ? lastfilter : "None"), - (data->final_content_type ? data->final_content_type : - "(not supplied)"), - (page_logging == 0 ? "not " : "")); - } - - // Pass on the result as "pdf-filter-page-logging" option - data->num_options = cupsAddOption("pdf-filter-page-logging", - (page_logging == 1 ? "On" : "Off"), - data->num_options, &(data->options)); - } - - return (0); -} - - -// -// 'ppdFilterFreePPDFile()' - After being done with the filter -// functions free the memory used by the -// PPD file data in the data structure. If -// the pointer to the "libppd" is NULL, do -// nothing. -// - -void -ppdFilterFreePPDFile(cf_filter_data_t *data) // I - Job and printer data -{ - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataRemoveExt(data, - PPD_FILTER_DATA_EXT); - - if (filter_data_ext) - { - if (filter_data_ext->ppd) - // ppdClose() frees not only the main data structure but also the cache - ppdClose(filter_data_ext->ppd); - - if (filter_data_ext->ppdfile) - free(filter_data_ext->ppdfile); - - free(filter_data_ext); - - ppdFilterFreePPD(data); - } -} - - -// -// 'ppdFilterFreePPD()' - After being done with the filter functions -// free the memory used by the PPD file data in -// the data structure. If the pointers to the -// data extracted from the PPD are NULL, do -// nothing. -// - -void -ppdFilterFreePPD(cf_filter_data_t *data) // I - Job and printer data -{ - if (data->printer_attrs) - { - ippDelete(data->printer_attrs); - data->printer_attrs = NULL; - } - - if (data->header) - { - free(data->header); - data->header = NULL; - } -} - - -// -// 'ppdFilterExternalCUPS()' - Filter function which calls an external -// classic CUPS filter or System V -// interface script, for example a -// (proprietary) printer driver which -// cannot be converted to a filter -// function or if it is too awkward or -// risky to convert for example when the -// printer hardware is not available for -// testing -// - -int // O - Error status -ppdFilterExternalCUPS(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataGetExt(data, - PPD_FILTER_DATA_EXT); - cf_filter_external_t params = *((cf_filter_external_t *)parameters); - int i; - char *filter_name; // Filter name for logging - char **envp = NULL; // Environment variables for filter - int status; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - - if (!params.filter || !params.filter[0]) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterExternalCUPS: Filter executable path/command not specified"); - return (1); - } - - // Filter name for logging - if ((filter_name = strrchr(params.filter, '/')) != NULL) - filter_name ++; - else - filter_name = (char *)params.filter; - - // - // Ignore broken pipe signals... - // - - signal(SIGPIPE, SIG_IGN); - - // - // Copy the environment variables given by the parameters - // - - if (params.envp) - for (i = 0; params.envp[i]; i ++) - cfFilterAddEnvVar(params.envp[i], NULL, &envp); - - // - // Some default environment variables from CUPS, will be not set if - // also defined in the environment in which the caller is started or - // if already set in the parameters. These are needed for correct - // execution of the CUPS filter or backend (which is not running out - // of CUPS here) - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterExternalCUPS: Setting CUPS-specific environment environment variables: CUPS_DATADIR, CUPS_SERVERBIN, CUPS_SERVERROOT, CUPS_STATEDIR, SOFTWARE, CONTENT_TYPE, FINAL_CONTENT_TYPE"); - - if (!getenv("CUPS_DATADIR") && - !cfFilterGetEnvVar("CUPS_DATADIR", envp)) - cfFilterAddEnvVar("CUPS_DATADIR", CUPS_DATADIR, &envp); - if (!getenv("CUPS_SERVERBIN") && - !cfFilterGetEnvVar("CUPS_SERVERBIN", envp)) - cfFilterAddEnvVar("CUPS_SERVERBIN", CUPS_SERVERBIN, &envp); - if (!getenv("CUPS_SERVERROOT") && - !cfFilterGetEnvVar("CUPS_SERVERROOT", envp)) - cfFilterAddEnvVar("CUPS_SERVERROOT", CUPS_SERVERROOT, &envp); - if (!getenv("CUPS_STATEDIR") && - !cfFilterGetEnvVar("CUPS_STATEDIR", envp)) - cfFilterAddEnvVar("CUPS_STATEDIR", CUPS_STATEDIR, &envp); - if (!getenv("SOFTWARE") && - !cfFilterGetEnvVar("SOFTWARE", envp)) - cfFilterAddEnvVar("SOFTWARE", "CUPS/2.5.99", &envp); - if (data->content_type && - !getenv("CONTENT_TYPE") && - !cfFilterGetEnvVar("CONTENT_TYPE", envp)) - cfFilterAddEnvVar("CONTENT_TYPE", data->content_type, &envp); - if (data->final_content_type && - !getenv("FINAL_CONTENT_TYPE") && - !cfFilterGetEnvVar("FINAL_CONTENT_TYPE", envp)) - cfFilterAddEnvVar("FINAL_CONTENT_TYPE", data->final_content_type, - &envp); - - if (params.exec_mode < 2) // Not needed in discovery mode of backend - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterExternalCUPS: Setting CUPS-specific environment environment variables: PRINTER, PPD, DEVICE_URI"); - - // Print queue name from filter data - if (data->printer) - cfFilterAddEnvVar("PRINTER", data->printer, &envp); - else - cfFilterAddEnvVar("PRINTER", "Unknown", &envp); - - // PPD file path/name from filter data, required for most CUPS filters - if (filter_data_ext && filter_data_ext->ppdfile) - cfFilterAddEnvVar("PPD", filter_data_ext->ppdfile, &envp); - - // Do we have the DEVICE_URI env variable set? - if (params.exec_mode > 0 && - !getenv("DEVICE_URI") && - !cfFilterGetEnvVar("DEVICE_URI", envp)) - if (log) log(ld, CF_LOGLEVEL_WARN, - "ppdFilterExternalCUPS: Running backend and DEVICE_URI environment variable is not set."); - } - - // - // Insert new environment variable list into copy of parameters - // - - params.envp = envp; - - // - // Call cfFilterExternal() to do the actual work - // - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterExternalCUPS: Calling cfFilterExternal()."); - - status = cfFilterExternal(inputfd, outputfd, inputseekable, data, ¶ms); - - // - // Clean up - // - - if (envp) - { - for (i = 0; envp[i]; i ++) - free(envp[i]); - free(envp); - } - - return (status); -} - - -// -// 'fcntl_add_cloexec()' - Add FD_CLOEXEC flag to the flags -// of a given file descriptor. -// - -static int // Return value of fcntl() -fcntl_add_cloexec(int fd) // File descriptor to add FD_CLOEXEC to -{ - return fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); -} - - -// -// 'ppdFilterEmitJCL()' - Wrapper for the PDF-generating filter -// functions to emit JCL (PJL) before and after -// the PDF output. -// - -int // O - Error status -ppdFilterEmitJCL(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters, // I - Filter-specific parameters - cf_filter_function_t orig_filter) -{ - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataGetExt(data, - PPD_FILTER_DATA_EXT); - const char *val; - int streaming = 0; - int ret = 1; - size_t bytes; - char buf[8192]; - int outfds[2], pid = -1; - FILE *fp; - int hw_copies = 1; - bool hw_collate = false; - int status, // Exit status - retval = 1; // Return value - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - - // - // Check whether we are in streaming mode (cfFilterPDFToPDF() only) - // - // If we are in streaming mode of cfFilterPDFToPDF() we only apply - // JCL and do not run the job through cfFilterPDFToPDF() itself (so - // no page management, form flattening, page size/orientation - // adjustment, ...) - // - - if (orig_filter == cfFilterPDFToPDF && - (val = cupsGetOption("filter-streaming-mode", - data->num_options, data->options)) != NULL && - (strcasecmp(val, "false") && strcasecmp(val, "off") & - strcasecmp(val, "no"))) - { - streaming = 1; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterEmitJCL: Streaming mode: No PDF processing, only adding of JCL"); - } - - // - // Call the original filter function without forking if we suppress - // JCL output via option... - // - - if ((val = cupsGetOption("emit-jcl", - data->num_options, data->options)) != NULL && - (strcasecmp(val, "false") == 0 || strcasecmp(val, "off") == 0 || - strcasecmp(val, "no") == 0)) - { - if (!streaming) - // Call actual filter function from libcupsfilters - ret = orig_filter(inputfd, outputfd, inputseekable, data, parameters); - else - { - // Just pass through the data unchanged... - fp = fdopen(outputfd, "w"); - while ((bytes = read(inputfd, buf, sizeof(buf))) > 0) - fwrite(buf, 1, bytes, fp); - close(inputfd); - fclose(fp); - ret = 0; - } - return (ret); - } - - if (!streaming) - { - // - // Create pipe for output of original filter function... - // - - if (pipe(outfds) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterEmitJCL: Could not create pipe for ouput: %s", - strerror(errno)); - return (1); - } - - // - // Fork child process for original filter function... - // - - if ((pid = fork()) == 0) - { - // Send output into pipe for adding JCL - fcntl_add_cloexec(outfds[1]); - close(outfds[0]); - - // Call actual filter function from libcupsfilters - ret = orig_filter(inputfd, outfds[1], inputseekable, data, parameters); - - exit(ret); - } - else if (pid > 0) - { - if (log) log(ld, CF_LOGLEVEL_INFO, - "ppdFilterEmitJCL: Filter function (PID %d) started.", - pid); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterEmitJCL: Unable to fork process for filter function."); - close(outfds[0]); - close(outfds[1]); - retval = 1; - goto out; - } - - if (inputfd >= 0) - close(inputfd); - close(outfds[1]); - } - else - // - // In Streaming mode we simply copy the input - // - - outfds[0] = inputfd; - - // - // Check options for caller's instructions about hardware copies/collate - // - - hw_copies = - ((val = cupsGetOption("hardware-copies", - data->num_options, data->options)) != NULL && - (strcasecmp(val, "true") == 0 || strcasecmp(val, "on") == 0 || - strcasecmp(val, "yes") == 0)) ? - data->copies : 1; - - hw_collate = - (hw_copies > 1 && - (val = cupsGetOption("hardware-collate", - data->num_options, data->options)) != NULL && - (strcasecmp(val, "true") == 0 || strcasecmp(val, "on") == 0 || - strcasecmp(val, "yes") == 0)); - - // - // Assemble the output: Exit server, JCL preamble, PDF output, JCL postamble - // - - fp = fdopen(outputfd, "w"); - if (filter_data_ext) - { - ppdEmit(filter_data_ext->ppd, fp, PPD_ORDER_EXIT); - ppdEmitJCLPDF(filter_data_ext->ppd, fp, - data->job_id, data->job_user, data->job_title, - hw_copies, hw_collate); - } - while ((bytes = read(outfds[0], buf, sizeof(buf))) > 0) - fwrite(buf, 1, bytes, fp); - close(outfds[0]); - if (filter_data_ext) - ppdEmitJCLEnd(filter_data_ext->ppd, fp); - fclose(fp); - - if (!streaming) - { - // - // Wait for filter process to finish - // - - retry_wait: - if (waitpid (pid, &status, 0) == -1) - { - if (errno == EINTR) - goto retry_wait; - if (log) - log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterEmitJCL: Filter function (PID %d) stopped with an error: %s!", - pid, strerror(errno)); - goto out; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterEmitJCL: Filter function (PID %d) exited with no errors.", - pid); - - // How did the filter function terminate - if (WIFEXITED(status)) - // Via exit() anywhere or return() in the main() function - retval = WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - // Via signal - retval = 256 * WTERMSIG(status); - } - else - retval = 0; - - out: - return (retval); -} - - -// -// 'ppdFilterImageToPDF()' - Wrapper for the filter function -// cfFilterImageToPDF() to add PPD file -// support to it. -// - -int // O - Error status -ppdFilterImageToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - return ppdFilterEmitJCL(inputfd, outputfd, inputseekable, data, - parameters, cfFilterImageToPDF); -} - - -// -// 'ppdFilterPDFtoPDF()' - Wrapper for the filter function -// cfFilterPDFtoPDF() to add PPD file -// support to it. -// - -int // O - Error status -ppdFilterPDFToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - return ppdFilterEmitJCL(inputfd, outputfd, inputseekable, data, - parameters, cfFilterPDFToPDF); -} - - -// -// 'ppdFilterUniversal()' - Wrapper for the filter function -// cfFilterUniversal() to add PPD file -// support to it. -// - -int // O - Error status -ppdFilterUniversal(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters -{ - ppd_filter_data_ext_t *filter_data_ext = - (ppd_filter_data_ext_t *)cfFilterDataGetExt(data, - PPD_FILTER_DATA_EXT); - char *input; - char *final_output; - char output[256]; - cf_filter_universal_parameter_t universal_parameters; - ppd_file_t *ppd = NULL; - ppd_cache_t *cache; - cups_array_t *filter_chain; - cf_filter_filter_in_chain_t extra_filter, universal_filter; - int ret; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - - - universal_parameters = *(cf_filter_universal_parameter_t *)parameters; - input = data->content_type; - if (input == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterUniversal: No input data format supplied."); - return (1); - } - - final_output = data->final_content_type; - if (final_output == NULL) - { - final_output = universal_parameters.actual_output_type; - if (final_output == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterUniversal: No output data format supplied."); - return (1); - } - } - - if (filter_data_ext) - ppd = filter_data_ext->ppd; - - if (universal_parameters.actual_output_type) - strncpy(output, universal_parameters.actual_output_type, - sizeof(output) - 1); - else - { - strncpy(output, data->final_content_type, sizeof(output) - 1); - - if (ppd) - { - cache = ppd ? ppd->cache : NULL; - - // Check whether our output format (under CUPS it is taken from - // the FINAL_CONTENT_TYPE env variable) is the destination format - // (2nd word) of a "*cupsFilter2: ..." line (string has 4 words), - // in this case the specified filter (4th word) does the last - // step, converting from the input format (1st word) of the line - // to the destination format and so we only need to convert to the - // input format. In this case we need to correct our output - // format. - // - // If there is more than one line with the given output format and - // an inpout format we can produce, we select the one with the - // lowest cost value (3rd word) as this is the one which CUPS - // should have chosen for this job. - // - // If we have "*cupsFilter: ..." lines (without "2", string with 3 - // words) we do not need to do anything special, as the input - // format specified is the FIMAL_CONTENT_TYPE which CUPS supplies - // to us and into which we have to convert. So we quit parsing if - // the first line has only 3 words, as if CUPS uses the - // "*cupsFilter: ..." lines only if there is no "*cupsFilter2: - // ..." line min the PPD, so if we encounter a line with only 3 - // words the other lines will have only 3 words, too and nothing - // has to be done. - - if (ppd && ppd->num_filters && cache) - { - int lowest_cost = INT_MAX; - char *filter; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterUniversal: \"*cupsFilter(2): ...\" lines in the PPD file:"); - - for (filter = (char *)cupsArrayFirst(cache->filters); - filter; - filter = (char *)cupsArrayNext(cache->filters)) - { - char buf[256]; - char *ptr, - *in = NULL, - *out = NULL, - *coststr = NULL; - int cost; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterUniversal: %s", filter); - - // String of the "*cupsfilter:" or "*cupsfilter2:" line - strncpy(buf, filter, sizeof(buf) - 1); - - // Separate the words - in = ptr = buf; - while (*ptr && !isspace(*ptr)) ptr ++; - if (!*ptr) goto error; - *ptr = '\0'; - ptr ++; - while (*ptr && isspace(*ptr)) ptr ++; - if (!*ptr) goto error; - out = ptr; - while (*ptr && !isspace(*ptr)) ptr ++; - if (!*ptr) goto error; - *ptr = '\0'; - ptr ++; - while (*ptr && isspace(*ptr)) ptr ++; - if (!*ptr) goto error; - coststr = ptr; - if (!isdigit(*ptr)) goto error; - while (*ptr && !isspace(*ptr)) ptr ++; - if (!*ptr) goto error; - *ptr = '\0'; - ptr ++; - while (*ptr && isspace(*ptr)) ptr ++; - if (!*ptr) goto error; - cost = atoi(coststr); - - // Valid "*cupsFilter2: ..." line ... - if (// Must be of lower cost than what we selected before - cost < lowest_cost && - // Must have our FINAL_CONTENT_TYPE as output - strcasecmp(out, final_output) == 0 && - // Must have as input a format we are able to produce - (strcasecmp(in, "application/vnd.cups-raster") == 0 || - strcasecmp(in, "application/vnd.cups-pdf") == 0 || - strcasecmp(in, "application/vnd.cups-postscript") == 0 || - strcasecmp(in, "application/pdf") == 0 || - strcasecmp(in, "image/pwg-raster") == 0 || - strcasecmp(in, "image/urf") == 0 || - strcasecmp(in, "application/PCLm") == 0 || - strcasecmp(in, "application/postscript") == 0)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterUniversal: --> Selecting this line"); - // Take the input format of the line as output format for us - strncpy(output, in, sizeof(output)); - // Update the minimum cost found - lowest_cost = cost; - // We cannot find a "better" solution ... - if (lowest_cost == 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterUniversal: Cost value is down to zero, stopping reading further lines"); - break; - } - } - - continue; - - error: - if (lowest_cost == INT_MAX && coststr) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterUniversal: PPD uses \"*cupsFilter: ...\" lines, so we always convert to format given by FINAL_CONTENT_TYPE"); - break; - } - if (log) log(ld, CF_LOGLEVEL_ERROR, - "ppdFilterUniversal: Invalid \"*cupsFilter2: ...\" line in PPD: %s", - filter); - } - } - } - } - - if (strcasecmp(output, final_output) != 0) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterUniversal: Converting from %s to %s, final output will be %s", - input, output, final_output); - universal_parameters.actual_output_type = output; - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterUniversal: Converting from %s to %s", input, output); - } - - if (strstr(output, "postscript")) - { - // PostScript output, we need to add libppd's ppdFilterPDFToPS() to - // the end of the chain - universal_parameters.actual_output_type = "application/vnd.cups-pdf"; - extra_filter.function = ppdFilterPDFToPS; - extra_filter.parameters = NULL; - extra_filter.name = "pdftops"; - } -#if HAVE_CUPS_3_X - else if (ppd && ppd->jcl_pdf && strstr(output, "pdf")) -#else - else if (ppd && ppdFindAttr(ppd, "JCLToPDFInterpreter", NULL) && - strstr(output, "pdf")) -#endif - { - // Classic PDF printer, with job control via JCL/PJL, needs - // libppd's ppdFilterPDFToPDF() filter function which adds PPD's JCL/PJL - if (!strncmp(input, "image/", 6) && - strcmp(input + 6, "urf") && strcmp(input + 6, "pwg-raster")) - // Input is an image file: call only ppdFilterImageToPDF() as - // cfFilterImageToPDF() + ppdFilterPDFToPDF() would apply the margins - // twice - return ppdFilterImageToPDF(inputfd, outputfd, inputseekable, data, NULL); - else - { - // Any other input format: Replace the cfFilterPDFToPDF() call by - // cfFilterUniversal() with a call of ppdFilterPDFToPDF(), so that - // JCL/PJL from the PPD gets added. - universal_parameters.actual_output_type = "application/pdf"; - extra_filter.function = ppdFilterPDFToPDF; - extra_filter.parameters = NULL; - extra_filter.name = "pdftopdf+JCL"; - } - } - else - // No extra filter needed, cfFilterUniversal() does the job - // correctly with only the filter functions of libcupsfilters - extra_filter.function = NULL; - - if (extra_filter.function) - { - // Chain cfFilterUniversal() with the extra filter function - universal_filter.function = cfFilterUniversal; - universal_filter.parameters = &universal_parameters; - universal_filter.name = "universal"; - filter_chain = cupsArrayNew(NULL, NULL); - cupsArrayAdd(filter_chain, &universal_filter); - cupsArrayAdd(filter_chain, &extra_filter); - ret = cfFilterChain(inputfd, outputfd, inputseekable, data, - filter_chain); - cupsArrayDelete(filter_chain); - } - else - ret = cfFilterUniversal(inputfd, outputfd, inputseekable, data, - &universal_parameters); - - return (ret); -} - - -// -// 'ppdFilterSetCommonOptions()' - Set common filter options for media size, -// etc. based on PPD file -// - -void -ppdFilterSetCommonOptions( - ppd_file_t *ppd, // I - PPD file - int num_options, // I - Number of options - cups_option_t *options, // I - Options - int change_size, // I - Change page size? - int *Orientation, // I/O - Basic page parameters - int *Duplex, - int *LanguageLevel, - int *ColorDevice, - float *PageLeft, - float *PageRight, - float *PageTop, - float *PageBottom, - float *PageWidth, - float *PageLength, - cf_logfunc_t log, // I - Logging function, - // NULL for no logging - void *ld) // I - User data for logging function, - // can be NULL -{ - ppd_size_t *pagesize; // Current page size - const char *val; // Option value - - - *Orientation = 0; // 0 = portrait, 1 = landscape, etc. - *Duplex = 0; // Duplexed? - *LanguageLevel = 1; // Language level of printer - *ColorDevice = 1; // Color printer? - *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 - - if ((pagesize = ppdPageSize(ppd, NULL)) != NULL) - { - int corrected = 0; - if (pagesize->width > 0) - *PageWidth = pagesize->width; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid value for page width: %.0f", - pagesize->width); - corrected = 1; - } - if (pagesize->length > 0) - *PageLength = pagesize->length; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid value for page length: %.0f", - pagesize->length); - corrected = 1; - } - if (pagesize->top >= 0 && pagesize->top <= *PageLength) - *PageTop = pagesize->top; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid value for page top margin: %.0f", - pagesize->top); - if (*PageLength >= *PageBottom) - *PageTop = *PageLength - *PageBottom; - else - *PageTop = *PageLength; - corrected = 1; - } - if (pagesize->bottom >= 0 && pagesize->bottom <= *PageLength) - *PageBottom = pagesize->bottom; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid value for page bottom margin: %.0f", - pagesize->bottom); - if (*PageLength <= *PageBottom) - *PageBottom = 0.0f; - corrected = 1; - } - if (*PageBottom == *PageTop) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid values for page margins: Bottom: %.0f; Top: %.0f", - *PageBottom, *PageTop); - *PageTop = *PageLength - *PageBottom; - if (*PageBottom == *PageTop) - { - *PageBottom = 0.0f; - *PageTop = *PageLength; - } - corrected = 1; - } - if (*PageBottom > *PageTop) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid values for page margins: Bottom: %.0f; Top: %.0f", - *PageBottom, *PageTop); - float swap = *PageBottom; - *PageBottom = *PageTop; - *PageTop = swap; - corrected = 1; - } - - if (pagesize->left >= 0 && pagesize->left <= *PageWidth) - *PageLeft = pagesize->left; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid value for page left margin: %.0f", - pagesize->left); - if (*PageWidth <= *PageLeft) - *PageLeft = 0.0f; - corrected = 1; - } - if (pagesize->right >= 0 && pagesize->right <= *PageWidth) - *PageRight = pagesize->right; - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid value for page right margin: %.0f", - pagesize->right); - if (*PageWidth >= *PageLeft) - *PageRight = *PageWidth - *PageLeft; - else - *PageRight = *PageWidth; - corrected = 1; - } - if (*PageLeft == *PageRight) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid values for page margins: Left: %.0f; Right: %.0f", - *PageLeft, *PageRight); - *PageRight = *PageWidth - *PageLeft; - if (*PageLeft == *PageRight) - { - *PageLeft = 0.0f; - *PageRight = *PageWidth; - } - corrected = 1; - } - if (*PageLeft > *PageRight) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Invalid values for page margins: Left: %.0f; Right: %.0f", - *PageLeft, *PageRight); - float swap = *PageLeft; - *PageLeft = *PageRight; - *PageRight = swap; - corrected = 1; - } - - if (corrected) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "PPD Page = %.0fx%.0f; %.0f,%.0f to %.0f,%.0f", - pagesize->width, pagesize->length, pagesize->left, - pagesize->bottom, pagesize->right, pagesize->top); - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Corrected Page = %.0fx%.0f; %.0f,%.0f to %.0f,%.0f", - *PageWidth, *PageLength, *PageLeft, - *PageBottom, *PageRight, *PageTop); - } - else - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Page = %.0fx%.0f; %.0f,%.0f to %.0f,%.0f", - pagesize->width, pagesize->length, pagesize->left, - pagesize->bottom, pagesize->right, pagesize->top); - } - - if (ppd != NULL) - { - *ColorDevice = ppd->color_device; - *LanguageLevel = ppd->language_level; - } - - if ((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if (strcasecmp(val, "no") != 0 && strcasecmp(val, "off") != 0 && - strcasecmp(val, "false") != 0) - { - if (ppd && ppd->landscape > 0) - *Orientation = 1; - else - *Orientation = 3; - } - } - else 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 & 3) - { - 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 & 3) - { - 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 & 3) - { - case 0 : - *PageBottom = (float)atof(val); - break; - case 1 : - *PageLeft = (float)atof(val); - break; - case 2 : - *PageTop = *PageLength - (float)atof(val); - break; - case 3 : - *PageRight = *PageWidth - (float)atof(val); - break; - } - } - - if ((val = cupsGetOption("page-top", num_options, options)) != NULL) - { - switch (*Orientation & 3) - { - case 0 : - *PageTop = *PageLength - (float)atof(val); - break; - case 1 : - *PageRight = *PageWidth - (float)atof(val); - break; - case 2 : - *PageBottom = (float)atof(val); - break; - case 3 : - *PageLeft = (float)atof(val); - break; - } - } - - if (change_size) - ppdFilterUpdatePageVars(*Orientation, PageLeft, PageRight, - PageTop, PageBottom, PageWidth, PageLength); - - if (ppdIsMarked(ppd, "Duplex", "DuplexNoTumble") || - ppdIsMarked(ppd, "Duplex", "DuplexTumble") || - ppdIsMarked(ppd, "JCLDuplex", "DuplexNoTumble") || - ppdIsMarked(ppd, "JCLDuplex", "DuplexTumble") || - ppdIsMarked(ppd, "EFDuplex", "DuplexNoTumble") || - ppdIsMarked(ppd, "EFDuplex", "DuplexTumble") || - ppdIsMarked(ppd, "EFDuplexing", "DuplexNoTumble") || - ppdIsMarked(ppd, "EFDuplexing", "DuplexTumble") || - ppdIsMarked(ppd, "ARDuplex", "DuplexNoTumble") || - ppdIsMarked(ppd, "ARDuplex", "DuplexTumble") || - ppdIsMarked(ppd, "KD03Duplex", "DuplexNoTumble") || - ppdIsMarked(ppd, "KD03Duplex", "DuplexTumble")) - *Duplex = 1; - - return; -} - - -// -// 'ppdFilterUpdatePageVars()' - Update the page variables for the orientation. -// - -void -ppdFilterUpdatePageVars(int Orientation, - float *PageLeft, float *PageRight, - float *PageTop, float *PageBottom, - float *PageWidth, float *PageLength) -{ - float temp; // Swapping variable - - - switch (Orientation & 3) - { - 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; - } -} diff --git a/ppd/ppd-filter.h b/ppd/ppd-filter.h deleted file mode 100644 index 17fd33326..000000000 --- a/ppd/ppd-filter.h +++ /dev/null @@ -1,205 +0,0 @@ -// -// Filter functions API definitions for libppd. -// -// Copyright © 2020-2022 by Till Kamppeter. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_PPD_FILTER_H_ -# define _PPD_PPD_FILTER_H_ - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Include necessary headers... -// - -# include -# include - -# include -# include -# include -# include - -# if defined(WIN32) || defined(__EMX__) -# include -# else -# include -# include -# endif // WIN32 || __EMX__ - -# include -# include -# include - -# define PPD_FILTER_DATA_EXT "libppd" - - -// -// Types and structures... -// - -typedef struct ppd_filter_data_ext_s { - char *ppdfile; // PPD file name - ppd_file_t *ppd; // PPD file data -} ppd_filter_data_ext_t; - -// -// Prototypes... -// - -extern int ppdFilterCUPSWrapper(int argc, - char *argv[], - cf_filter_function_t filter, - void *parameters, - int *JobCanceled); - - -extern int ppdFilterLoadPPDFile(cf_filter_data_t *data, const char *ppdfile); - - -extern int ppdFilterLoadPPD(cf_filter_data_t *data); - - -extern void ppdFilterFreePPDFile(cf_filter_data_t *data); - - -extern void ppdFilterFreePPD(cf_filter_data_t *data); - - -extern int ppdFilterExternalCUPS(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Parameters: cf_filter_external_t* -// -// Path/Name of the external CUPS/System V filter or backend to be -// called by this filter function, specification whether we call a -// filter or a backend, and in case of backend, whether in job -// processing or discovery mode, extra options for the 5th command -// line argument, and extra environment variables -// -// CUPS filter: -// See "man filter" -// -// CUPS Backend: -// See "man backend" - - -extern int ppdFilterEmitJCL(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters, - cf_filter_function_t orig_filter); - - -extern int ppdFilterImageToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - - -extern int ppdFilterImageToPS(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - - -extern int ppdFilterPDFToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// (Optional) Specification of output format via -// data->final_content_type is used for determining whether this -// filter function does page logging for CUPS (output of "PAGE: XX YY" -// log messages) or not and also to determine whether the printer or -// driver generates copies or whether we have to send the pages -// repeatedly. -// -// Alternatively, the options "pdf-filter-page-logging", -// "hardware-copies", and "hardware-collate" can be used to manually -// do these selections. - - -extern int ppdFilterPDFToPS(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - - -extern int ppdFilterPSToPS(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - - -extern int ppdFilterRasterToPS(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - - -extern int ppdFilterUniversal(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters); - -// Requires specification of input format via data->content_type and -// job's final output format via data->final_content_type -// -// Parameters: cf_filter_universal_parameter_t -// -// Contains: actual_output_type: Format which the filter should -// actually produce if different from job's final output -// format, or NULL to auto-determine the needed output -// format from the PPDs "cupsFilter2: ..." lines. Default -// is the job's final output format. -// texttopdf_params: parameters for texttopdf - - -extern void ppdFilterSetCommonOptions(ppd_file_t *ppd, - int num_options, - cups_option_t *options, - int change_size, - int *Orientation, - int *Duplex, - int *LanguageLevel, - int *ColorDevice, - float *PageLeft, - float *PageRight, - float *PageTop, - float *PageBottom, - float *PageWidth, - float *PageLength, - cf_logfunc_t log, - void *ld); - - -extern void ppdFilterUpdatePageVars(int Orientation, - float *PageLeft, float *PageRight, - float *PageTop, float *PageBottom, - float *PageWidth, float *PageLength); - - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_PPD_PPD_FILTER_H_ diff --git a/ppd/ppd-generator.c b/ppd/ppd-generator.c deleted file mode 100644 index b175c6496..000000000 --- a/ppd/ppd-generator.c +++ /dev/null @@ -1,2609 +0,0 @@ -// -// PWG Raster/Apple Raster/PCLm/PDF/IPP legacy PPD generator for libppd. -// -// Copyright 2016-2019 by Till Kamppeter. -// Copyright 2017-2019 by Sahil Arora. -// Copyright 2018-2019 by Deepak Patankar. -// -// The PPD generator is based on the PPD generator for the CUPS -// "lpadmin -m everywhere" functionality in the cups/ppd-cache.c -// file. The copyright of this file is: -// -// Copyright 2010-2016 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include -#include -#include -#include -#include -#include - - -// -// Include necessary headers. -// - -#include -#include -#include -#include - - -// -// Macros to work around typos in older libcups version -// - -#if (CUPS_VERSION_MAJOR < 2) || ((CUPS_VERSION_MAJOR == 2) && ((CUPS_VERSION_MINOR < 3) || ((CUPS_VERSION_MINOR == 3) && (CUPS_VERSION_PATCH < 1)))) -#define IPP_FINISHINGS_CUPS_FOLD_ACCORDION IPP_FINISHINGS_CUPS_FOLD_ACCORDIAN -#define IPP_FINISHINGS_FOLD_ACCORDION IPP_FINISHINGS_FOLD_ACCORDIAN -#endif - - -// -// The code below is borrowed from the CUPS 2.2.x upstream repository -// (via patches attached to https://www.cups.org/str.php?L4258). This -// allows for automatic PPD generation already with CUPS versions older -// than CUPS 2.2.x. We have also an additional test and development -// platform for this code. Taken from cups/ppd-cache.c, -// cups/string-private.h, cups/string.c. -// -// The advantage of PPD generation instead of working with System V -// interface scripts is that the print dialogs of the clients do not -// need to ask the printer for its options via IPP. So we have access -// to the options with the current PPD-based dialogs and can even share -// the automatically created print queues to other CUPS-based machines -// without problems. -// - - -char ppdgenerator_msg[1024]; - - -// -// Human-readable strings in the PPDs generated by the PPD generator for -// using driverless IPP printers with CUPS -// -// To allow for translated/localized PPDs we use the standard set -// of human-readable strings from the PWG: -// -// https://ftp.pwg.org/pub/pwg/ipp/examples/ipp.pot -// https://ftp.pwg.org/pub/pwg/ipp/examples/ipp.strings -// -// Creating those with translated human-readable strings would allow us to -// easily produce PPDs in other languges. -// -// Translations are supposed to go here (but none are available yet): -// -// https://github.com/istopwg/ippregistry/tree/master/localizations -// -// These standard strings are also part of CUPS' translation files: -// -// https://github.com/OpenPrinting/cups/tree/master/locale -// -// Here translations take actually place as part of the translations -// of CUPS itself. -// -// We take the files from the CUPS installed into the system (as the PPD -// generator actually does not make sense without CUPS) but currently -// only use the English version. It is not complicated to check the user -// language in the printer's IPP response via the -// attributes-natural-language attribute and then request an appropriate -// language version of the files if available. The printer-specific -// strings are downloaded from the printer following the URI in the -// printer-strings-uri attribute and are in the selected language. -// -// It is not clear whether PPD translation will get fixed in the PPD -// generator as the need of PPDs in CUPS will go away with version -// 3.x. -// -// See also: -// -// https://lists.linuxfoundation.org/pipermail/printing-architecture/2021/003992.html -// https://lists.linuxfoundation.org/pipermail/printing-architecture/2021/003995.html -// - - -// -// 'ppdCreatePPDFromIPP()' - Create a PPD file describing the capabilities -// of an IPP printer, using info from DNS-SD record -// as fallback (for poor IPP responses, especially -// IPP 1.x legacy) -// - -char * // O - PPD filename or NULL - // on error -ppdCreatePPDFromIPP(char *buffer, // I - Filename buffer - size_t bufsize, // I - Size of filename - // buffer - ipp_t *response, // I - Get-Printer-Attributes - // response - const char *make_model, // I - Make and model from - // DNS-SD - const char *pdl, // I - List of PDLs from - // DNS-SD - int color, // I - Color printer? (from - // DNS-SD) - int duplex, // I - Duplex printer? (from - // DNS-SD) - char *status_msg, // I - Status message buffer, - // NULL to ignore - // message - size_t status_msg_size) // I - Size of status message - // buffer -{ - return ppdCreatePPDFromIPP2(buffer, bufsize, response, make_model, pdl, - color, duplex, NULL, NULL, NULL, NULL, - status_msg, status_msg_size); -} - -// -// 'ppdCreatePPDFromIPP2()' - Create a PPD file describing the -// capabilities of an IPP printer, with -// extra parameters for PPDs from a merged -// IPP record for printer clusters -// - -char * // O - PPD filename or NULL - // on error -ppdCreatePPDFromIPP2(char *buffer, // I - Filename buffer - size_t bufsize, // I - Size of filename - // buffer - ipp_t *response, // I - Get-Printer- - // Attributes response - const char *make_model, // I - Make and model from - // DNS-SD - const char *pdl, // I - List of PDLs from - // DNS-SD - int color, // I - Color printer? (from - // DNS-SD) - int duplex, // I - Duplex printer? (from - // DNS-SD) - cups_array_t *conflicts, // I - Array of - // constraints - cups_array_t *sizes, // I - Media sizes we've - // added - char* default_pagesize, // I - Default page size - const char *default_cluster_color, // I - cluster def - // color (if cluster's - // attributes are - // returned) - char *status_msg, // I - Status message - // buffer, NULL to - // ignore message - size_t status_msg_size) // I - Size of status - // message buffer -{ - cups_file_t *fp; // PPD file - cups_array_t *printer_sizes; // Media sizes we've added - cups_size_t *size; // Current media size - ipp_attribute_t *attr, // xxx-supported - *attr2, - *defattr, // xxx-default - *quality, // print-quality-supported - *x_dim, *y_dim; // Media dimensions - ipp_t *media_col, // Media collection - *media_size; // Media size collection - char make[256], // Make and model - *model, // Model name - ppdname[PPD_MAX_NAME]; - // PPD keyword - int i, j, // Looping vars - count = 0, // Number of values - bottom, // Largest bottom margin - left, // Largest left margin - right, // Largest right margin - top, // Largest top margin - max_length = 0, // Maximum custom size - max_width = 0, - min_length = INT_MAX, - // Minimum custom size - min_width = INT_MAX, - is_apple = 0, // Does the printer support Apple - // Raster? - is_pwg = 0, // Does the printer support PWG - // Raster? - is_pclm = 0, // Does the printer support PCLm? - is_pdf = 0; // Does the printer support PDF? - pwg_media_t *pwg; // PWG media size - int xres, yres; // Resolution values - cups_array_t *common_res, // Common resolutions of all PDLs - *current_res, // Resolutions of current PDL - *pdl_list; // List of PDLs - cf_res_t *common_def, // Common default resolution - *current_def, // Default resolution of current PDL - *min_res, // Minimum common resolution - *max_res; // Maximum common resolution - struct lconv *loc = localeconv(); - // Locale data - cups_array_t *opt_strings_catalog = NULL; - // Standard option UI strings - cups_array_t *printer_opt_strings_catalog = NULL; - // Printer-specific option UI strings - char *human_readable, - *human_readable2; - const char *keyword; // Keyword value - cups_array_t *fin_options = NULL; - // Finishing options - char buf[256]; - char *defaultoutbin = NULL; - const char *outbin; - char outbin_properties[1024]; - int octet_str_len; - void *outbin_properties_octet; - int outputorderinfofound = 0, - faceupdown = 1, - firsttolast = 1; - int manual_copies = -1, - is_fax = 0; - - // - // Range check input... - // - - if (buffer) - *buffer = '\0'; - - if (!buffer || bufsize < 1) - { - if (status_msg && status_msg_size) - snprintf(status_msg, status_msg_size, "%s", strerror(EINVAL)); - return (NULL); - } - - if (!response) - { - if (status_msg && status_msg_size) - snprintf(status_msg, status_msg_size, "No IPP attributes."); - return (NULL); - } - - // - // Open a temporary file for the PPD... - // - - if ((fp = cupsTempFile2(buffer, (int)bufsize)) == NULL) - { - if (status_msg && status_msg_size) - snprintf(status_msg, status_msg_size, "%s", strerror(errno)); - return (NULL); - } - - // - // Standard stuff for PPD file... - // - - cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n"); - cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n"); - cupsFilePrintf(fp, "*FileVersion: \"%s\"\n", VERSION); - cupsFilePuts(fp, "*LanguageVersion: English\n"); - cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n"); - cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n"); - cupsFilePuts(fp, "*LanguageLevel: \"3\"\n"); - cupsFilePuts(fp, "*FileSystem: False\n"); - cupsFilePuts(fp, "*PCFileName: \"drvless.ppd\"\n"); - - if ((attr = ippFindAttribute(response, "ipp-features-supported", - IPP_TAG_KEYWORD)) != NULL && - ippContainsString(attr, "faxout")) - { - attr = ippFindAttribute(response, "printer-uri-supported", - IPP_TAG_URI); - if (attr) - { - ippAttributeString(attr, buf, sizeof(buf)); - if (strcasestr(buf, "faxout")) - is_fax = 1; - } - } - - if ((attr = ippFindAttribute(response, "printer-make-and-model", - IPP_TAG_TEXT)) != NULL) - strlcpy(make, ippGetString(attr, 0, NULL), sizeof(make)); - else if (make_model && make_model[0] != '\0') - strlcpy(make, make_model, sizeof(make)); - else - strlcpy(make, "Unknown Printer", sizeof(make)); - - if (!strncasecmp(make, "Hewlett Packard ", 16) || - !strncasecmp(make, "Hewlett-Packard ", 16)) - { - model = make + 16; - strlcpy(make, "HP", sizeof(make)); - } - else if ((model = strchr(make, ' ')) != NULL) - *model++ = '\0'; - else - model = make; - - cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make); - cupsFilePrintf(fp, "*ModelName: \"%s %s\"\n", make, model); - cupsFilePrintf(fp, "*Product: \"(%s %s)\"\n", make, model); - cupsFilePrintf(fp, "*NickName: \"%s %s, %sdriverless, cups-filters %s\"\n", - make, model, (is_fax ? "Fax, " : ""), VERSION); - cupsFilePrintf(fp, "*ShortNickName: \"%s %s\"\n", make, model); - - // Which is the default output bin? - if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO)) - != NULL) - defaultoutbin = strdup(ippGetString(attr, 0, NULL)); - // Find out on which position of the list of output bins the default one is, - // if there is no default bin, take the first of this list - i = 0; - if ((attr = ippFindAttribute(response, "output-bin-supported", - IPP_TAG_ZERO)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - outbin = ippGetString(attr, i, NULL); - if (outbin == NULL) - continue; - if (defaultoutbin == NULL) - { - defaultoutbin = strdup(outbin); - break; - } else if (strcasecmp(outbin, defaultoutbin) == 0) - break; - } - } - if ((attr = ippFindAttribute(response, "printer-output-tray", - IPP_TAG_STRING)) != NULL && - i < ippGetCount(attr)) - { - outbin_properties_octet = ippGetOctetString(attr, i, &octet_str_len); - memset(outbin_properties, 0, sizeof(outbin_properties)); - memcpy(outbin_properties, outbin_properties_octet, - ((size_t)octet_str_len < sizeof(outbin_properties) - 1 ? - (size_t)octet_str_len : sizeof(outbin_properties) - 1)); - if (strcasestr(outbin_properties, "pagedelivery=faceUp")) - { - outputorderinfofound = 1; - faceupdown = -1; - } - if (strcasestr(outbin_properties, "stackingorder=lastToFirst")) - firsttolast = -1; - } - if (outputorderinfofound == 0 && defaultoutbin && - strcasestr(defaultoutbin, "face-up")) - faceupdown = -1; - if (defaultoutbin) - free (defaultoutbin); - if (firsttolast * faceupdown < 0) - cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n"); - else - cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n"); - - // Do we have a color printer? - if (((attr = ippFindAttribute(response, - "color-supported", IPP_TAG_BOOLEAN)) != NULL && - ippGetBoolean(attr, 0)) || - color) - cupsFilePuts(fp, "*ColorDevice: True\n"); - else - cupsFilePuts(fp, "*ColorDevice: False\n"); - - cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, - CUPS_VERSION_MINOR); - cupsFilePuts(fp, "*cupsSNMPSupplies: False\n"); - cupsFilePuts(fp, "*cupsLanguages: \"en\"\n"); - - if ((attr = ippFindAttribute(response, "printer-more-info", IPP_TAG_URI)) != - NULL) - cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL)); - - if ((attr = ippFindAttribute(response, "printer-charge-info-uri", - IPP_TAG_URI)) != NULL) - cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0, - NULL)); - - // Message catalogs for UI strings - opt_strings_catalog = cfCatalogOptionArrayNew(); - cfCatalogLoad(NULL, opt_strings_catalog); - if ((attr = ippFindAttribute(response, "printer-strings-uri", - IPP_TAG_URI)) != NULL) - { - printer_opt_strings_catalog = cfCatalogOptionArrayNew(); - cfCatalogLoad(ippGetString(attr, 0, NULL), - printer_opt_strings_catalog); - if (cupsArrayCount(printer_opt_strings_catalog) > 0) - cupsFilePrintf(fp, "*cupsStringsURI: \"%s\"\n", ippGetString(attr, 0, - NULL)); - } - - // - // Accounting... - // - - if (ippGetBoolean(ippFindAttribute(response, "job-account-id-supported", - IPP_TAG_BOOLEAN), 0)) - cupsFilePuts(fp, "*cupsJobAccountId: True\n"); - - if (ippGetBoolean(ippFindAttribute(response, - "job-accounting-user-id-supported", - IPP_TAG_BOOLEAN), 0)) - cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n"); - - if ((attr = ippFindAttribute(response, "printer-privacy-policy-uri", - IPP_TAG_URI)) != NULL) - cupsFilePrintf(fp, "*cupsPrivacyURI: \"%s\"\n", - ippGetString(attr, 0, NULL)); - - if ((attr = ippFindAttribute(response, "printer-mandatory-job-attributes", - IPP_TAG_KEYWORD)) != NULL) - { - char prefix = '\"'; // Prefix for string - - cupsFilePuts(fp, "*cupsMandatory: \""); - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - - if (strcmp(keyword, "attributes-charset") && - strcmp(keyword, "attributes-natural-language") && - strcmp(keyword, "printer-uri")) - { - cupsFilePrintf(fp, "%c%s", prefix, keyword); - prefix = ','; - } - } - cupsFilePuts(fp, "\"\n"); - } - - if ((attr = ippFindAttribute(response, "printer-requested-job-attributes", - IPP_TAG_KEYWORD)) != NULL) - { - char prefix = '\"'; // Prefix for string - - cupsFilePuts(fp, "*cupsRequested: \""); - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - - if (strcmp(keyword, "attributes-charset") && - strcmp(keyword, "attributes-natural-language") && - strcmp(keyword, "printer-uri")) - { - cupsFilePrintf(fp, "%c%s", prefix, keyword); - prefix = ','; - } - } - cupsFilePuts(fp, "\"\n"); - } - - // - // Password/PIN printing... - // - - if ((attr = ippFindAttribute(response, "job-password-supported", - IPP_TAG_INTEGER)) != NULL) - { - char pattern[33]; // Password pattern - int maxlen = ippGetInteger(attr, 0); - // Maximum length - const char *repertoire = - ippGetString(ippFindAttribute(response, - "job-password-repertoire-configured", - IPP_TAG_KEYWORD), 0, NULL); - // Type of password - - if (maxlen > (int)(sizeof(pattern) - 1)) - maxlen = (int)sizeof(pattern) - 1; - - if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits")) - memset(pattern, '1', (size_t)maxlen); - else if (!strcmp(repertoire, "iana_us-ascii_letters")) - memset(pattern, 'A', (size_t)maxlen); - else if (!strcmp(repertoire, "iana_us-ascii_complex")) - memset(pattern, 'C', (size_t)maxlen); - else if (!strcmp(repertoire, "iana_us-ascii_any")) - memset(pattern, '.', (size_t)maxlen); - else if (!strcmp(repertoire, "iana_utf-8_digits")) - memset(pattern, 'N', (size_t)maxlen); - else if (!strcmp(repertoire, "iana_utf-8_letters")) - memset(pattern, 'U', (size_t)maxlen); - else - memset(pattern, '*', (size_t)maxlen); - - pattern[maxlen] = '\0'; - - cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern); - } - - // - // PDLs and common resolutions ... - // - - common_res = NULL; - current_res = NULL; - common_def = NULL; - current_def = NULL; - min_res = NULL; - max_res = NULL; - // Put all available PDls into a simple case-insensitevely searchable - // sorted string list - if ((pdl_list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - goto bad_ppd; - int formatfound = 0; - - if (((attr = ippFindAttribute(response, "document-format-supported", - IPP_TAG_MIMETYPE)) != NULL) || - (pdl && pdl[0] != '\0')) - { - const char *format = pdl; - i = 0; - count = ippGetCount(attr); - while ((attr && i < count) || // Go through formats in attribute - (!attr && pdl && pdl[0] != '\0' && format[0] != '\0')) - { - // Go through formats in pdl string (from DNS-SD record) - - // Pick next format from attribute - if (attr) format = ippGetString(attr, i, NULL); - // Add format to list of supported PDLs, skip duplicates - if (!cupsArrayFind(pdl_list, (void *)format)) - cupsArrayAdd(pdl_list, (void *)format); - if (attr) - // Next format in attribute - i ++; - else { - // Find the next format in the string pdl, if there is none left, - // go to the terminating zero - while (!isspace(*format) && *format != ',' && *format != '\0') - format ++; - while ((isspace(*format) || *format == ',') && *format != '\0') - format ++; - } - } - } - - // - // Fax - // - - if (is_fax) - { - cupsFilePuts(fp, "*cupsFax: True\n"); - cupsFilePuts(fp, "*cupsIPPFaxOut: True\n"); - } - - // - // Check for each CUPS/cups-filters-supported PDL, starting with the - // most desirable going to the least desirable. If a PDL requires a - // certain set of resolutions (the raster-based PDLs), find the - // resolutions and find out which are the common resolutions of all - // supported PDLs. Choose the default resolution from the most - // desirable of all resolution-requiring PDLs if it is common in all - // of them. Skip a resolution-requiring PDL if its resolution list - // attribute is missing or contains only broken entries. Use the - // general resolution list and default resolution of the printer - // only if it does not support any resolution-requiring PDL. Use 300 - // dpi if there is no resolution info at all in the attributes. - // In case of PDF as PDL check whether also the - // "application/vnd.cups-pdf" MIME type is accepted. In this case - // our printer is a remote CUPS queue which already runs the - // pdftopdf filter on the server, so let the PPD take - // "application/pdf" as input format so that pdftopdf does not also - // get executed on the client, applying option settings twice. See - // https://github.com/apple/cups/issues/5361 - // - - if (cupsArrayFind(pdl_list, "application/vnd.cups-pdf")) - { - // Remote CUPS queue - cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n"); - manual_copies = 0; - formatfound = 1; - is_pdf = 1; - } -#ifdef CUPS_RASTER_HAVE_APPLERASTER - else if (cupsArrayFind(pdl_list, "image/urf")) - { - if ((attr = ippFindAttribute(response, "urf-supported", - IPP_TAG_KEYWORD)) != NULL) - { - int lowdpi = 0, hidpi = 0; // Lower and higher resolution - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - const char *rs = ippGetString(attr, i, NULL); // RS value - if (strncasecmp(rs, "RS", 2)) - continue; - lowdpi = atoi(rs + 2); - if ((rs = strrchr(rs, '-')) != NULL) - hidpi = atoi(rs + 1); - else - hidpi = lowdpi; - break; - } - if (lowdpi == 0) - { - // Invalid "urf-supported" value... - goto bad_ppd; - } - else - { - if ((current_res = cfNewResolutionArray()) != NULL) - { - if ((current_def = cfNewResolution(lowdpi, lowdpi)) != NULL) - { - cupsArrayAdd(current_res, current_def); - cfFreeResolution(current_def, NULL); - } - if (hidpi != lowdpi && - (current_def = cfNewResolution(hidpi, hidpi)) != NULL) - { - cupsArrayAdd(current_res, current_def); - cfFreeResolution(current_def, NULL); - } - current_def = NULL; - if (cupsArrayCount(current_res) > 0 && - cfJoinResolutionArrays(&common_res, ¤t_res, &common_def, - ¤t_def)) { - cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 0 -\"\n"); - manual_copies = 1; - formatfound = 1; - is_apple = 1; - } - } - } - } - } -#endif - else if (cupsArrayFind(pdl_list, "application/pdf")) - { - // PDF printer - cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 0 -\"\n"); - manual_copies = 0; - formatfound = 1; - is_pdf = 1; - } - else if (cupsArrayFind(pdl_list, "image/pwg-raster")) - { - if ((attr = ippFindAttribute(response, - "pwg-raster-document-resolution-supported", - IPP_TAG_RESOLUTION)) != NULL) - { - current_def = NULL; - if ((current_res = cfIPPAttrToResolutionArray(attr)) != NULL && - cfJoinResolutionArrays(&common_res, ¤t_res, &common_def, - ¤t_def)) - { - cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 0 -\"\n"); - if (formatfound == 0) manual_copies = 1; - formatfound = 1; - is_pwg = 1; - } - } - } - else if (cupsArrayFind(pdl_list, "application/PCLm")) - { - if ((attr = ippFindAttribute(response, "pclm-source-resolution-supported", - IPP_TAG_RESOLUTION)) != NULL) - { - if ((defattr = ippFindAttribute(response, - "pclm-source-resolution-default", - IPP_TAG_RESOLUTION)) != NULL) - current_def = cfIPPResToResolution(defattr, 0); - else - current_def = NULL; - if ((current_res = cfIPPAttrToResolutionArray(attr)) != NULL && - cfJoinResolutionArrays(&common_res, ¤t_res, &common_def, - ¤t_def)) - { - cupsFilePuts(fp, "*cupsFilter2: \"application/PCLm application/PCLm 0 -\"\n"); - if (formatfound == 0) manual_copies = 1; - formatfound = 1; - is_pclm = 1; - } - } - } - // Legacy formats only if we have no driverless format - else if (cupsArrayFind(pdl_list, "application/vnd.hp-pclxl")) - { - cupsFilePrintf(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/vnd.hp-pclxl 100 gstopxl\"\n"); - if (formatfound == 0) - manual_copies = 1; - formatfound = 1; - } - else if (cupsArrayFind(pdl_list, "application/postscript")) - { - // Higher cost value as PostScript interpreters are often buggy - cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-postscript application/postscript 0 -\"\n"); - if (formatfound == 0) - manual_copies = 0; - formatfound = 1; - } - else if (cupsArrayFind(pdl_list, "application/vnd.hp-pcl")) - { - cupsFilePrintf(fp, "*cupsFilter2: \"application/vnd.cups-raster application/vnd.hp-pcl 100 rastertopclx\"\n"); - if (formatfound == 0) - manual_copies = 1; - formatfound = 1; - } - if (cupsArrayFind(pdl_list, "image/jpeg")) - cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n"); - if (cupsArrayFind(pdl_list, "image/png")) - cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n"); - cupsArrayDelete(pdl_list); - if (manual_copies < 0) - manual_copies = 1; - if (formatfound == 0) - goto bad_ppd; - - // For the case that we will print in a raster format and not in a high-level - // format, we need to create multiple copies on the client. We add a line to - // the PPD which tells the pdftopdf filter to generate the copies - if (manual_copies == 1) - cupsFilePuts(fp, "*cupsManualCopies: True\n"); - - // No resolution requirements by any of the supported PDLs? - // Use "printer-resolution-supported" attribute - if (common_res == NULL) - { - if ((attr = ippFindAttribute(response, "printer-resolution-supported", - IPP_TAG_RESOLUTION)) != NULL) - { - if ((defattr = ippFindAttribute(response, "printer-resolution-default", - IPP_TAG_RESOLUTION)) != NULL) - current_def = cfIPPResToResolution(defattr, 0); - else - current_def = NULL; - if ((current_res = cfIPPAttrToResolutionArray(attr)) != NULL) - cfJoinResolutionArrays(&common_res, ¤t_res, &common_def, - ¤t_def); - } - } - // Still no resolution found? Default to 300 dpi - if (common_res == NULL) - { - if ((common_res = cfNewResolutionArray()) != NULL) - { - if ((current_def = cfNewResolution(300, 300)) != NULL) - { - cupsArrayAdd(common_res, current_def); - cfFreeResolution(current_def, NULL); - } - current_def = NULL; - } else - goto bad_ppd; - } - // No default resolution determined yet - if (common_def == NULL) - { - if ((defattr = ippFindAttribute(response, "printer-resolution-default", - IPP_TAG_RESOLUTION)) != NULL) - { - common_def = cfIPPResToResolution(defattr, 0); - if (!cupsArrayFind(common_res, common_def)) - { - free(common_def); - common_def = NULL; - } - } - if (common_def == NULL) { - count = cupsArrayCount(common_res); - common_def = - cfCopyResolution(cupsArrayIndex(common_res, count / 2), NULL); - } - } - // Get minimum and maximum resolution - min_res = cfCopyResolution(cupsArrayFirst(common_res), NULL); - max_res = cfCopyResolution(cupsArrayLast(common_res), NULL); - cupsArrayDelete(common_res); - - // - // Generically check for Raster-format-related attributes in IPP - // response and ppdize them one by one - // - - attr = ippFirstAttribute(response); // first attribute - while (attr) // loop through all the attributes - { - if ((is_apple && strncasecmp(ippGetName(attr), "urf-", 4) == 0) || - (is_pwg && strncasecmp(ippGetName(attr), "pwg-raster-", 11) == 0) || - (is_pclm && strncasecmp(ippGetName(attr), "pclm-", 5) == 0)) - { - ppdPwgPpdizeName(ippGetName(attr), ppdname, sizeof(ppdname)); - cupsFilePrintf(fp, "*cups%s: ", ppdname); - ipp_tag_t tag = ippGetValueTag(attr); - count = ippGetCount(attr); - - if (tag == IPP_TAG_RESOLUTION) // ppdize values of type resolution - { - if ((current_res = cfIPPAttrToResolutionArray(attr)) != NULL) - { - count = cupsArrayCount(current_res); - if (count > 1) - cupsFilePuts(fp, "\""); - for (i = 0, current_def = cupsArrayFirst(current_res); - current_def; - i ++, current_def = cupsArrayNext(current_res)) - { - int x = current_def->x; - int y = current_def->y; - if (x == y) - cupsFilePrintf(fp, "%ddpi", x); - else - cupsFilePrintf(fp, "%dx%ddpi", x, y); - if (i < count - 1) - cupsFilePuts(fp, ","); - } - if (count > 1) - cupsFilePuts(fp, "\""); - cupsFilePuts(fp, "\n"); - } else - cupsFilePuts(fp, "\"\"\n"); - cupsArrayDelete(current_res); - } - else - { - ippAttributeString(attr, buf, sizeof(buf)); - if (count > 1 || // quotes around multi-valued and string - // attributes - tag == IPP_TAG_STRING || - tag == IPP_TAG_TEXT || - tag == IPP_TAG_TEXTLANG) - cupsFilePrintf(fp, "\"%s\"\n", buf); - else - cupsFilePrintf(fp, "%s\n", buf); - } - } - attr = ippNextAttribute(response); - } - - // - // PageSize/PageRegion/ImageableArea/PaperDimension - // - - cfGenerateSizes(response, CF_GEN_SIZES_DEFAULT, &printer_sizes, &defattr, - NULL, NULL, NULL, NULL, NULL, NULL, - &min_width, &min_length, - &max_width, &max_length, - &left, &bottom, &right, &top, ppdname, NULL); - if (sizes == NULL) - sizes = printer_sizes; - else - strcpy(ppdname, default_pagesize); - - if (cupsArrayCount(sizes) > 0) - { - // - // List all of the standard sizes... - // - - char tleft[256], // Left string - tbottom[256], // Bottom string - tright[256], // Right string - ttop[256], // Top string - twidth[256], // Width string - tlength[256], // Length string - ppdsizename[128]; - char *ippsizename, - *suffix; - int all_borderless = 1; - - // Find a page size without ".Borderless" suffix - // (if all are ".Borderless" we drop the suffix in the PPD) - for (size = (cups_size_t *)cupsArrayFirst(sizes); size; - size = (cups_size_t *)cupsArrayNext(sizes)) - if (strcasestr(size->media, ".Borderless") == NULL) - break; - if (size) - all_borderless = 0; - - if (all_borderless) - { - suffix = strcasestr(ppdname, ".Borderless"); - if (suffix) - *suffix = '\0'; - } - - cupsFilePrintf(fp, "*OpenUI *PageSize/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *PageSize\n" - "*DefaultPageSize: %s\n", "Media Size", ppdname); - for (size = (cups_size_t *)cupsArrayFirst(sizes); size; - size = (cups_size_t *)cupsArrayNext(sizes)) - { - cfStrFormatd(twidth, twidth + sizeof(twidth), - size->width * 72.0 / 2540.0, loc); - cfStrFormatd(tlength, tlength + sizeof(tlength), - size->length * 72.0 / 2540.0, loc); - strlcpy(ppdsizename, size->media, sizeof(ppdsizename)); - if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) - { - *ippsizename = '\0'; - ippsizename ++; - } - - if (ippsizename) - human_readable = cfCatalogLookUpChoice(ippsizename, "media", - opt_strings_catalog, - printer_opt_strings_catalog); - else - human_readable = NULL; - if (!human_readable) - { - pwg = pwgMediaForSize(size->width, size->length); - if (pwg) - human_readable = cfCatalogLookUpChoice((char *)pwg->pwg, "media", - opt_strings_catalog, - printer_opt_strings_catalog); - } - - if (all_borderless) - { - suffix = strcasestr(ppdsizename, ".Borderless"); - if (suffix) - *suffix = '\0'; - } - - cupsFilePrintf(fp, "*PageSize %s%s%s%s: \"<>setpagedevice\"\n", - ppdsizename, - (human_readable ? "/" : ""), - (human_readable ? human_readable : ""), - (human_readable && strstr(ppdsizename, ".Borderless") ? - " (Borderless)" : ""), - twidth, tlength); - } - cupsFilePuts(fp, "*CloseUI: *PageSize\n"); - - cupsFilePrintf(fp, "*OpenUI *PageRegion/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *PageRegion\n" - "*DefaultPageRegion: %s\n", "Media Size", ppdname); - for (size = (cups_size_t *)cupsArrayFirst(sizes); size; - size = (cups_size_t *)cupsArrayNext(sizes)) - { - cfStrFormatd(twidth, twidth + sizeof(twidth), - size->width * 72.0 / 2540.0, loc); - cfStrFormatd(tlength, tlength + sizeof(tlength), - size->length * 72.0 / 2540.0, loc); - strlcpy(ppdsizename, size->media, sizeof(ppdsizename)); - if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) - { - *ippsizename = '\0'; - ippsizename ++; - } - - if (ippsizename) - human_readable = cfCatalogLookUpChoice(ippsizename, "media", - opt_strings_catalog, - printer_opt_strings_catalog); - else - human_readable = NULL; - if (!human_readable) - { - pwg = pwgMediaForSize(size->width, size->length); - if (pwg) - human_readable = cfCatalogLookUpChoice((char *)pwg->pwg, "media", - opt_strings_catalog, - printer_opt_strings_catalog); - } - - if (all_borderless) - { - suffix = strcasestr(ppdsizename, ".Borderless"); - if (suffix) - *suffix = '\0'; - } - - cupsFilePrintf(fp, "*PageRegion %s%s%s%s: \"<>setpagedevice\"\n", - ppdsizename, - (human_readable ? "/" : ""), - (human_readable ? human_readable : ""), - (human_readable && strstr(ppdsizename, ".Borderless") ? - " (Borderless)" : ""), - twidth, tlength); - } - cupsFilePuts(fp, "*CloseUI: *PageRegion\n"); - - cupsFilePrintf(fp, "*DefaultImageableArea: %s\n" - "*DefaultPaperDimension: %s\n", ppdname, ppdname); - - for (size = (cups_size_t *)cupsArrayFirst(sizes); size; - size = (cups_size_t *)cupsArrayNext(sizes)) - { - cfStrFormatd(tleft, tleft + sizeof(tleft), - size->left * 72.0 / 2540.0, loc); - cfStrFormatd(tbottom, tbottom + sizeof(tbottom), - size->bottom * 72.0 / 2540.0, loc); - cfStrFormatd(tright, tright + sizeof(tright), - (size->width - size->right) * 72.0 / 2540.0, loc); - cfStrFormatd(ttop, ttop + sizeof(ttop), - (size->length - size->top) * 72.0 / 2540.0, loc); - cfStrFormatd(twidth, twidth + sizeof(twidth), - size->width * 72.0 / 2540.0, loc); - cfStrFormatd(tlength, tlength + sizeof(tlength), - size->length * 72.0 / 2540.0, loc); - strlcpy(ppdsizename, size->media, sizeof(ppdsizename)); - if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) - *ippsizename = '\0'; - - if (all_borderless) - { - suffix = strcasestr(ppdsizename, ".Borderless"); - if (suffix) - *suffix = '\0'; - } - - cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", ppdsizename, - tleft, tbottom, tright, ttop); - cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", ppdsizename, - twidth, tlength); - } - - // - // Custom size support... - // - - if (max_width > 0 && min_width < INT_MAX && max_length > 0 && - min_length < INT_MAX) - { - char tmax[256], tmin[256]; // Min/max values - - cfStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc); - cfStrFormatd(tbottom, tbottom + sizeof(tbottom), - bottom * 72.0 / 2540.0, loc); - cfStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0, - loc); - cfStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc); - - cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom, - tright, ttop); - - cfStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0, - loc); - cfStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0, - loc); - cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin, - tmax); - - cfStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0, - loc); - cfStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0, - loc); - cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin, - tmax); - - cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n"); - cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n"); - cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n"); - cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <>setpagedevice\"\n"); - } - } - else - { - cupsFilePrintf(fp, - "*%% Printer did not supply page size info via IPP, using defaults\n" - "*OpenUI *PageSize/Media Size: PickOne\n" - "*OrderDependency: 10 AnySetup *PageSize\n" - "*DefaultPageSize: Letter\n" - "*PageSize Letter/US Letter: \"<>setpagedevice\"\n" - "*PageSize Legal/US Legal: \"<>setpagedevice\"\n" - "*PageSize Executive/Executive: \"<>setpagedevice\"\n" - "*PageSize Tabloid/Tabloid: \"<>setpagedevice\"\n" - "*PageSize A3/A3: \"<>setpagedevice\"\n" - "*PageSize A4/A4: \"<>setpagedevice\"\n" - "*PageSize A5/A5: \"<>setpagedevice\"\n" - "*PageSize B5/JIS B5: \"<>setpagedevice\"\n" - "*PageSize EnvISOB5/Envelope B5: \"<>setpagedevice\"\n" - "*PageSize Env10/Envelope #10 : \"<>setpagedevice\"\n" - "*PageSize EnvC5/Envelope C5: \"<>setpagedevice\"\n" - "*PageSize EnvDL/Envelope DL: \"<>setpagedevice\"\n" - "*PageSize EnvMonarch/Envelope Monarch: \"<>setpagedevice\"\n" - "*CloseUI: *PageSize\n" - "*OpenUI *PageRegion/Media Size: PickOne\n" - "*OrderDependency: 10 AnySetup *PageRegion\n" - "*DefaultPageRegion: Letter\n" - "*PageRegion Letter/US Letter: \"<>setpagedevice\"\n" - "*PageRegion Legal/US Legal: \"<>setpagedevice\"\n" - "*PageRegion Executive/Executive: \"<>setpagedevice\"\n" - "*PageRegion Tabloid/Tabloid: \"<>setpagedevice\"\n" - "*PageRegion A3/A3: \"<>setpagedevice\"\n" - "*PageRegion A4/A4: \"<>setpagedevice\"\n" - "*PageRegion A5/A5: \"<>setpagedevice\"\n" - "*PageRegion B5/JIS B5: \"<>setpagedevice\"\n" - "*PageRegion EnvISOB5/Envelope B5: \"<>setpagedevice\"\n" - "*PageRegion Env10/Envelope #10 : \"<>setpagedevice\"\n" - "*PageRegion EnvC5/Envelope C5: \"<>setpagedevice\"\n" - "*PageRegion EnvDL/Envelope DL: \"<>setpagedevice\"\n" - "*PageRegion EnvMonarch/Envelope Monarch: \"<>setpagedevice\"\n" - "*CloseUI: *PageSize\n" - "*DefaultImageableArea: Letter\n" - "*ImageableArea Letter/US Letter: \"18 12 594 780\"\n" - "*ImageableArea Legal/US Legal: \"18 12 594 996\"\n" - "*ImageableArea Executive/Executive: \"18 12 504 744\"\n" - "*ImageableArea Tabloid/Tabloid: \"18 12 774 1212\"\n" - "*ImageableArea A3/A3: \"18 12 824 1179\"\n" - "*ImageableArea A4/A4: \"18 12 577 830\"\n" - "*ImageableArea A5/A5: \"18 12 402 583\"\n" - "*ImageableArea B5/JIS B5: \"18 12 498 717\"\n" - "*ImageableArea EnvISOB5/Envelope B5: \"18 12 481 697\"\n" - "*ImageableArea Env10/Envelope #10 : \"18 12 279 672\"\n" - "*ImageableArea EnvC5/Envelope C5: \"18 12 441 637\"\n" - "*ImageableArea EnvDL/Envelope DL: \"18 12 294 612\"\n" - "*ImageableArea EnvMonarch/Envelope Monarch: \"18 12 261 528\"\n" - "*DefaultPaperDimension: Letter\n" - "*PaperDimension Letter/US Letter: \"612 792\"\n" - "*PaperDimension Legal/US Legal: \"612 1008\"\n" - "*PaperDimension Executive/Executive: \"522 756\"\n" - "*PaperDimension Tabloid/Tabloid: \"792 1224\"\n" - "*PaperDimension A3/A3: \"842 1191\"\n" - "*PaperDimension A4/A4: \"595 842\"\n" - "*PaperDimension A5/A5: \"420 595\"\n" - "*PaperDimension B5/JIS B5: \"516 729\"\n" - "*PaperDimension EnvISOB5/Envelope B5: \"499 709\"\n" - "*PaperDimension Env10/Envelope #10 : \"297 684\"\n" - "*PaperDimension EnvC5/Envelope C5: \"459 649\"\n" - "*PaperDimension EnvDL/Envelope DL: \"312 624\"\n" - "*PaperDimension EnvMonarch/Envelope Monarch: \"279 540\"\n"); - } - - cupsArrayDelete(printer_sizes); - - // - // InputSlot... - // - - if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", - IPP_TAG_KEYWORD)) != NULL) - ppdPwgPpdizeName(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname)); - else - ppdname[0] = '\0'; - - if ((attr = ippFindAttribute(response, "media-source-supported", - IPP_TAG_KEYWORD)) != NULL && - (count = ippGetCount(attr)) > 1) - { - int have_default = ppdname[0] != '\0'; - // Do we have a default InputSlot? - static const char * const sources[] = - { // Standard "media-source" strings - "auto", - "main", - "alternate", - "large-capacity", - "manual", - "envelope", - "disc", - "photo", - "hagaki", - "main-roll", - "alternate-roll", - "top", - "middle", - "bottom", - "side", - "left", - "right", - "center", - "rear", - "by-pass-tray", - "tray-1", - "tray-2", - "tray-3", - "tray-4", - "tray-5", - "tray-6", - "tray-7", - "tray-8", - "tray-9", - "tray-10", - "tray-11", - "tray-12", - "tray-13", - "tray-14", - "tray-15", - "tray-16", - "tray-17", - "tray-18", - "tray-19", - "tray-20", - "roll-1", - "roll-2", - "roll-3", - "roll-4", - "roll-5", - "roll-6", - "roll-7", - "roll-8", - "roll-9", - "roll-10" - }; - - human_readable = cfCatalogLookUpOption("media-source", opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *InputSlot/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *InputSlot\n", - (human_readable ? human_readable : "Media Source")); - if (have_default) - cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname); - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - - ppdPwgPpdizeName(keyword, ppdname, sizeof(ppdname)); - - if (i == 0 && !have_default) - cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname); - - human_readable = cfCatalogLookUpChoice((char *)keyword, "media-source", - opt_strings_catalog, - printer_opt_strings_catalog); - for (j = (int)(sizeof(sources) / sizeof(sources[0])) - 1; j >= 0; j --) - if (!strcmp(sources[j], keyword)) - break; - if (j >= 0) - cupsFilePrintf(fp, "*InputSlot %s%s%s: \"<>setpagedevice\"\n", - ppdname, - (human_readable ? "/" : ""), - (human_readable ? human_readable : ""), j); - else - cupsFilePrintf(fp, "*InputSlot %s%s%s: \"\"\n", - ppdname, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - } - cupsFilePuts(fp, "*CloseUI: *InputSlot\n"); - } - - // - // MediaType... - // - - if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", - IPP_TAG_ZERO)) != NULL) - ppdPwgPpdizeName(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname)); - else - strlcpy(ppdname, "Unknown", sizeof(ppdname)); - - if ((attr = ippFindAttribute(response, "media-type-supported", - IPP_TAG_ZERO)) != NULL && - (count = ippGetCount(attr)) > 1) - { - human_readable = cfCatalogLookUpOption("media-type", opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *MediaType/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *MediaType\n" - "*DefaultMediaType: %s\n", - (human_readable ? human_readable : "Media Type"), - ppdname); - for (i = 0; i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - - ppdPwgPpdizeName(keyword, ppdname, sizeof(ppdname)); - - human_readable = cfCatalogLookUpChoice((char *)keyword, "media-type", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*MediaType %s%s%s: \"<>setpagedevice\"\n", - ppdname, - (human_readable ? "/" : ""), - (human_readable ? human_readable : ""), - ppdname); - } - cupsFilePuts(fp, "*CloseUI: *MediaType\n"); - } - - // - // ColorModel... - // - - if ((defattr = ippFindAttribute(response, "print-color-mode-default", - IPP_TAG_KEYWORD)) == NULL) - defattr = ippFindAttribute(response, "output-mode-default", - IPP_TAG_KEYWORD); - - if ((attr = ippFindAttribute(response, "print-color-mode-supported", - IPP_TAG_KEYWORD)) == NULL) - attr = ippFindAttribute(response, "output-mode-supported", - IPP_TAG_KEYWORD); - - human_readable = cfCatalogLookUpOption("print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - if (attr && ippGetCount(attr) > 0) - { - const char *default_color = NULL; // Default - int first_choice = 1; - - if ((keyword = ippGetString(defattr, 0, NULL)) != NULL) - { - if (!strcmp(keyword, "bi-level")) - default_color = "FastGray"; - else if (!strcmp(keyword, "process-bi-level")) - default_color = "ProcessFastGray"; - else if (!strcmp(keyword, "auto-monochrome")) - default_color = "AutoGray"; - else if (!strcmp(keyword, "monochrome")) - default_color = "Gray"; - else if (!strcmp(keyword, "process-monochrome")) - default_color = "ProcessGray"; - else - default_color = "RGB"; - } - - cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr)); - - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); // Keyword for color/bit depth - - if (!strcmp(keyword, "bi-level")) - { - if (first_choice) - { - first_choice = 0; - cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *ColorModel\n", - (human_readable ? human_readable : "Color Mode")); - } - - human_readable2 = cfCatalogLookUpChoice("bi-level", "print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*ColorModel FastGray/%s: \"\"\n", - (human_readable2 ? human_readable2 : "Text")); - - if (!default_color) - default_color = "FastGray"; - } - else if (!strcmp(keyword, "process-bi-level")) - { - if (first_choice) - { - first_choice = 0; - cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *ColorModel\n", - (human_readable ? human_readable : "Color Mode")); - } - - human_readable2 = cfCatalogLookUpChoice("process-bi-level", - "print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*ColorModel ProcessFastGray/%s: \"\"\n", - (human_readable2 ? human_readable2 : "Process Text")); - } - else if (!strcmp(keyword, "auto-monochrome")) - { - if (first_choice) - { - first_choice = 0; - cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *ColorModel\n", - (human_readable ? human_readable : "Color Mode")); - } - - human_readable2 = cfCatalogLookUpChoice("auto-monochrome", - "print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*ColorModel AutoGray/%s: \"\"\n", - (human_readable2 ? human_readable2 : "Auto Monochrome")); - } - else if (!strcmp(keyword, "monochrome")) - { - if (first_choice) - { - first_choice = 0; - cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *ColorModel\n", - (human_readable ? human_readable : "Color Mode")); - } - - human_readable2 = cfCatalogLookUpChoice("monochrome", - "print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*ColorModel Gray/%s: \"\"\n", - (human_readable2 ? human_readable2 : "Monochrome")); - - if (!default_color || (!defattr && !strcmp(default_color, "FastGray"))) - default_color = "Gray"; - } - else if (!strcmp(keyword, "process-monochrome")) - { - if (first_choice) - { - first_choice = 0; - cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *ColorModel\n", - (human_readable ? human_readable : "Color Mode")); - } - - human_readable2 = cfCatalogLookUpChoice("process-monochrome", - "print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*ColorModel ProcessGray/%s: \"\"\n", - (human_readable2 ? human_readable2 : - "Process Monochrome")); - } - else if (!strcmp(keyword, "color")) - { - if (first_choice) - { - first_choice = 0; - cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *ColorModel\n", - (human_readable ? human_readable : "Color Mode")); - } - - human_readable2 = cfCatalogLookUpChoice("color", "print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*ColorModel RGB/%s: \"\"\n", - (human_readable2 ? human_readable2 : "Color")); - - if (!defattr) - default_color = "RGB"; - - // Apparently some printers only advertise color support, so make sure - // we also do grayscale for these printers... - if (!ippContainsString(attr, "monochrome") && - !ippContainsString(attr, "auto-monochrome") && - !ippContainsString(attr, "process-monochrome") && - !ippContainsString(attr, "bi-level") && - !ippContainsString(attr, "process-bi-level")) - { - human_readable2 = cfCatalogLookUpChoice("monochrome", - "print-color-mode", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*ColorModel Gray/%s: \"\"\n", - (human_readable2 ? human_readable2 : "Grayscale")); - } - } - } - - if (default_pagesize != NULL && (!default_color || !defattr)) - { - // Here we are dealing with a cluster, if the default cluster color - // is not supplied we set it Gray - if (default_cluster_color != NULL) - default_color = default_cluster_color; - else - default_color = "Gray"; - } - - if (default_color) - cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color); - if (!first_choice) - cupsFilePuts(fp, "*CloseUI: *ColorModel\n"); - } - else - { - cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *ColorModel\n", - (human_readable ? human_readable : "Color Mode")); - if (color) - { - // Color printer according to DNS-SD (or unknown) - cupsFilePrintf(fp, "*DefaultColorModel: RGB\n"); - cupsFilePuts(fp, "*ColorModel RGB/Color: \"\"\n"); - } - else - cupsFilePrintf(fp, "*DefaultColorModel: Gray\n"); - cupsFilePuts(fp, "*ColorModel FastGray/Fast Grayscale: \"\"\n"); - cupsFilePuts(fp, "*ColorModel Gray/Grayscale: \"\"\n"); - cupsFilePuts(fp, "*CloseUI: *ColorModel\n"); - } - - // - // Duplex... - // - - if (((attr = ippFindAttribute(response, "sides-supported", - IPP_TAG_KEYWORD)) != NULL && - ippContainsString(attr, "two-sided-long-edge")) || - (attr == NULL && duplex)) - { - human_readable = cfCatalogLookUpOption("sides", opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *Duplex/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *Duplex\n" - "*DefaultDuplex: None\n", - (human_readable ? human_readable : "2-Sided Printing")); - human_readable = cfCatalogLookUpChoice("one-sided", "sides", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*Duplex None/%s: \"<>setpagedevice\"\n", - (human_readable ? human_readable : "Off")); - human_readable = cfCatalogLookUpChoice("two-sided-long-edge", "sides", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*Duplex DuplexNoTumble/%s: \"<>setpagedevice\"\n", - (human_readable ? human_readable : "On (Portrait)")); - human_readable = cfCatalogLookUpChoice("two-sided-short-edge", "sides", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*Duplex DuplexTumble/%s: \"<>setpagedevice\"\n", - (human_readable ? human_readable : "On (Landscape)")); - cupsFilePrintf(fp, "*CloseUI: *Duplex\n"); - - if ((attr = ippFindAttribute(response, "urf-supported", - IPP_TAG_KEYWORD)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - const char *dm = ippGetString(attr, i, NULL); // DM value - - if (!strcasecmp(dm, "DM1")) - { - cupsFilePuts(fp, "*cupsBackSide: Normal\n"); - break; - } - else if (!strcasecmp(dm, "DM2")) - { - cupsFilePuts(fp, "*cupsBackSide: Flipped\n"); - break; - } - else if (!strcasecmp(dm, "DM3")) - { - cupsFilePuts(fp, "*cupsBackSide: Rotated\n"); - break; - } - else if (!strcasecmp(dm, "DM4")) - { - cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n"); - break; - } - } - } - else if ((attr = ippFindAttribute(response, - "pwg-raster-document-sheet-back", - IPP_TAG_KEYWORD)) != NULL) - { - keyword = ippGetString(attr, 0, NULL); // Keyword value - - if (!strcmp(keyword, "flipped")) - cupsFilePuts(fp, "*cupsBackSide: Flipped\n"); - else if (!strcmp(keyword, "manual-tumble")) - cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n"); - else if (!strcmp(keyword, "normal")) - cupsFilePuts(fp, "*cupsBackSide: Normal\n"); - else - cupsFilePuts(fp, "*cupsBackSide: Rotated\n"); - } - } - - // - // Output bin... - // - - if ((attr = ippFindAttribute(response, "output-bin-default", - IPP_TAG_ZERO)) != NULL) - ppdPwgPpdizeName(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname)); - else - strlcpy(ppdname, "Unknown", sizeof(ppdname)); - - if ((attr = ippFindAttribute(response, "output-bin-supported", - IPP_TAG_ZERO)) != NULL && - (count = ippGetCount(attr)) > 0) - { - human_readable = cfCatalogLookUpOption("output-bin", opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *OutputBin/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *OutputBin\n" - "*DefaultOutputBin: %s\n", - (human_readable ? human_readable : "Output Bin"), - ppdname); - attr2 = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING); - for (i = 0; i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - - ppdPwgPpdizeName(keyword, ppdname, sizeof(ppdname)); - - human_readable = cfCatalogLookUpChoice((char *)keyword, "output-bin", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OutputBin %s%s%s: \"\"\n", - ppdname, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - outputorderinfofound = 0; - faceupdown = 1; - firsttolast = 1; - if (attr2 && i < ippGetCount(attr2)) - { - outbin_properties_octet = ippGetOctetString(attr2, i, &octet_str_len); - memset(outbin_properties, 0, sizeof(outbin_properties)); - memcpy(outbin_properties, outbin_properties_octet, - ((size_t)octet_str_len < sizeof(outbin_properties) - 1 ? - (size_t)octet_str_len : sizeof(outbin_properties) - 1)); - if (strcasestr(outbin_properties, "pagedelivery=faceUp")) - { - outputorderinfofound = 1; - faceupdown = -1; - } - else if (strcasestr(outbin_properties, "pagedelivery=faceDown")) - { - outputorderinfofound = 1; - faceupdown = 1; - } - if (strcasestr(outbin_properties, "stackingorder=lastToFirst")) - { - outputorderinfofound = 1; - firsttolast = -1; - } - else if (strcasestr(outbin_properties, "stackingorder=firstToLast")) - { - outputorderinfofound = 1; - firsttolast = 1; - } - } - if (outputorderinfofound == 0) { - if (strcasestr(keyword, "face-up")) - { - outputorderinfofound = 1; - faceupdown = -1; - } - if (strcasestr(keyword, "face-down")) - { - outputorderinfofound = 1; - faceupdown = 1; - } - } - if (outputorderinfofound) - cupsFilePrintf(fp, "*PageStackOrder %s: %s\n", - ppdname, - (firsttolast * faceupdown < 0 ? "Reverse" : "Normal")); - } - cupsFilePuts(fp, "*CloseUI: *OutputBin\n"); - } - - // - // Finishing options... - // - - if ((attr = ippFindAttribute(response, "finishings-supported", - IPP_TAG_ENUM)) != NULL) - { - int value; // Enum value - const char *ppd_keyword; // PPD keyword for enum - cups_array_t *names; // Names we've added - static const char * const base_keywords[] = - { // Base STD 92 keywords - NULL, // none - "SingleAuto", // staple - "SingleAuto", // punch - NULL, // cover - "BindAuto", // bind - "SaddleStitch", // saddle-stitch - "EdgeStitchAuto", // edge-stitch - "Auto", // fold - NULL, // trim - NULL, // bale - NULL, // booklet-maker - NULL, // jog-offset - NULL, // coat - NULL // laminate - }; - - count = ippGetCount(attr); - names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, (cups_afree_func_t)free); - fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL); - - // - // Staple/Bind/Stitch - // - - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (!strncmp(keyword, "staple-", 7) || - !strncmp(keyword, "bind-", 5) || - !strncmp(keyword, "edge-stitch-", 12) || - !strcmp(keyword, "saddle-stitch")) - break; - } - - if (i < count) - { - static const char * const staple_keywords[] = - { // StapleLocation keywords - "SinglePortrait", - "SingleRevLandscape", - "SingleLandscape", - "SingleRevPortrait", - "EdgeStitchPortrait", - "EdgeStitchLandscape", - "EdgeStitchRevPortrait", - "EdgeStitchRevLandscape", - "DualPortrait", - "DualLandscape", - "DualRevPortrait", - "DualRevLandscape", - "TriplePortrait", - "TripleLandscape", - "TripleRevPortrait", - "TripleRevLandscape" - }; - static const char * const bind_keywords[] = - { // StapleLocation binding keywords - "BindPortrait", - "BindLandscape", - "BindRevPortrait", - "BindRevLandscape" - }; - - cupsArrayAdd(fin_options, "*StapleLocation"); - - human_readable = cfCatalogLookUpChoice("staple", "finishing-template", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n", - (human_readable ? human_readable : "Staple")); - cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n"); - cupsFilePuts(fp, "*DefaultStapleLocation: None\n"); - human_readable = cfCatalogLookUpChoice("3", "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n", - (human_readable ? human_readable : "None")); - - for (; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (strncmp(keyword, "staple-", 7) && - strncmp(keyword, "bind-", 5) && - strncmp(keyword, "edge-stitch-", 12) && - strcmp(keyword, "saddle-stitch")) - continue; - - if (cupsArrayFind(names, (char *)keyword)) - continue; // Already did this finishing template - - cupsArrayAdd(names, (char *)keyword); - - if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE) - ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE]; - else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT && - value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM) - ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT]; - else if (value >= IPP_FINISHINGS_BIND_LEFT && - value <= IPP_FINISHINGS_BIND_BOTTOM) - ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT]; - else - ppd_keyword = NULL; - - if (!ppd_keyword) - continue; - - snprintf(buf, sizeof(buf), "%d", value); - human_readable = cfCatalogLookUpChoice(buf, "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*StapleLocation %s%s%s: \"\"\n", ppd_keyword, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", - value, keyword, ppd_keyword); - } - - cupsFilePuts(fp, "*CloseUI: *StapleLocation\n"); - } - - // - // Fold - // - - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (!strncmp(keyword, "cups-fold-", 10) || - !strcmp(keyword, "fold") || - !strncmp(keyword, "fold-", 5)) - break; - } - - if (i < count) - { - static const char * const fold_keywords[] = - { // FoldType keywords - "Accordion", - "DoubleGate", - "Gate", - "Half", - "HalfZ", - "LeftGate", - "Letter", - "Parallel", - "XFold", - "RightGate", - "ZFold", - "EngineeringZ" - }; - - cupsArrayAdd(fin_options, "*FoldType"); - - human_readable = cfCatalogLookUpChoice("fold", "finishing-template", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n", - (human_readable ? human_readable : "Fold")); - cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n"); - cupsFilePuts(fp, "*DefaultFoldType: None\n"); - human_readable = cfCatalogLookUpChoice("3", "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n", - (human_readable ? human_readable : "None")); - - for (; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (!strncmp(keyword, "cups-fold-", 10)) - keyword += 5; - else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5)) - continue; - - if (cupsArrayFind(names, (char *)keyword)) - continue; // Already did this finishing template - - cupsArrayAdd(names, (char *)keyword); - - if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE) - ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE]; - else if (value >= IPP_FINISHINGS_FOLD_ACCORDION && - value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z) - ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION]; - else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION && - value <= IPP_FINISHINGS_CUPS_FOLD_Z) - ppd_keyword = fold_keywords[value - - IPP_FINISHINGS_CUPS_FOLD_ACCORDION]; - else - ppd_keyword = NULL; - - if (!ppd_keyword) - continue; - - snprintf(buf, sizeof(buf), "%d", value); - human_readable = cfCatalogLookUpChoice(buf, "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*FoldType %s%s%s: \"\"\n", ppd_keyword, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", - value, keyword, ppd_keyword); - } - - cupsFilePuts(fp, "*CloseUI: *FoldType\n"); - } - - // - // Punch - // - - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (!strncmp(keyword, "cups-punch-", 11) || - !strncmp(keyword, "punch-", 6)) - break; - } - - if (i < count) - { - static const char * const punch_keywords[] = - { // PunchMedia keywords - "SinglePortrait", - "SingleRevLandscape", - "SingleLandscape", - "SingleRevPortrait", - "DualPortrait", - "DualLandscape", - "DualRevPortrait", - "DualRevLandscape", - "TriplePortrait", - "TripleLandscape", - "TripleRevPortrait", - "TripleRevLandscape", - "QuadPortrait", - "QuadLandscape", - "QuadRevPortrait", - "QuadRevLandscape", - "MultiplePortrait", - "MultipleLandscape", - "MultipleRevPortrait", - "MultipleRevLandscape" - }; - - cupsArrayAdd(fin_options, "*PunchMedia"); - - human_readable = cfCatalogLookUpChoice("punch", "finishing-template", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n", - (human_readable ? human_readable : "Punch")); - cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n"); - cupsFilePuts(fp, "*DefaultPunchMedia: None\n"); - human_readable = cfCatalogLookUpChoice("3", "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n", - (human_readable ? human_readable : "None")); - - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (!strncmp(keyword, "cups-punch-", 11)) - keyword += 5; - else if (strncmp(keyword, "punch-", 6)) - continue; - - if (cupsArrayFind(names, (char *)keyword)) - continue; // Already did this finishing template - - cupsArrayAdd(names, (char *)keyword); - - if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE) - ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE]; - else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT && - value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM) - ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT]; - else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT && - value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM) - ppd_keyword = punch_keywords[value - - IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT]; - else - ppd_keyword = NULL; - - if (!ppd_keyword) - continue; - - snprintf(buf, sizeof(buf), "%d", value); - human_readable = cfCatalogLookUpChoice(buf, "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*PunchMedia %s%s%s: \"\"\n", ppd_keyword, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", - value, keyword, ppd_keyword); - } - - cupsFilePuts(fp, "*CloseUI: *PunchMedia\n"); - } - - // - // Booklet - // - - if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER)) - { - cupsArrayAdd(fin_options, "*Booklet"); - - human_readable = cfCatalogLookUpChoice("booklet-maker", - "finishing-template", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n", - (human_readable ? human_readable : "Booklet")); - cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n"); - cupsFilePuts(fp, "*DefaultBooklet: False\n"); - cupsFilePuts(fp, "*Booklet False: \"\"\n"); - cupsFilePuts(fp, "*Booklet True: \"\"\n"); - cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", - IPP_FINISHINGS_BOOKLET_MAKER); - cupsFilePuts(fp, "*CloseUI: *Booklet\n"); - } - - // - // CutMedia - // - - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5)) - break; - } - - if (i < count) - { - static const char * const trim_keywords[] = - { // CutMedia keywords - "EndOfPage", - "EndOfDoc", - "EndOfSet", - "EndOfJob" - }; - - cupsArrayAdd(fin_options, "*CutMedia"); - - human_readable = cfCatalogLookUpChoice("trim", "finishing-template", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *CutMedia/%s: PickOne\n", - (human_readable ? human_readable : "Cut")); - cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n"); - cupsFilePuts(fp, "*DefaultCutMedia: None\n"); - human_readable = cfCatalogLookUpChoice("3", "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*CutMedia None/%s: \"\"\n", - (human_readable ? human_readable : "None")); - - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - keyword = ippEnumString("finishings", value); - - if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5)) - continue; - - if (cupsArrayFind(names, (char *)keyword)) - continue; // Already did this finishing template - - cupsArrayAdd(names, (char *)keyword); - - if (value == IPP_FINISHINGS_TRIM) - ppd_keyword = "Auto"; - else - ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES]; - - snprintf(buf, sizeof(buf), "%d", value); - human_readable = cfCatalogLookUpChoice(buf, "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*CutMedia %s%s%s: \"\"\n", ppd_keyword, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n", - value, keyword, ppd_keyword); - } - - cupsFilePuts(fp, "*CloseUI: *CutMedia\n"); - } - - cupsArrayDelete(names); - } - - if ((attr = ippFindAttribute(response, "finishings-col-database", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - ipp_t *finishing_col; // Current finishing collection - ipp_attribute_t *finishing_attr; // Current finishing member attribute - cups_array_t *templates; // Finishing templates - - human_readable = cfCatalogLookUpOption("finishing-template", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *cupsFinishingTemplate/%s: PickOne\n", - (human_readable ? human_readable : "Finishing Template")); - cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n"); - cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n"); - human_readable = cfCatalogLookUpChoice("3", "finishings", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*cupsFinishingTemplate None/%s: \"\"\n", - (human_readable ? human_readable : "None")); - - templates = cupsArrayNew((cups_array_func_t)strcmp, NULL); - count = ippGetCount(attr); - - for (i = 0; i < count; i ++) - { - finishing_col = ippGetCollection(attr, i); - keyword = ippGetString(ippFindAttribute(finishing_col, - "finishing-template", - IPP_TAG_ZERO), 0, NULL); - - if (!keyword || cupsArrayFind(templates, (void *)keyword)) - continue; - - if (!strcmp(keyword, "none")) - continue; - - cupsArrayAdd(templates, (void *)keyword); - - human_readable = cfCatalogLookUpChoice((char *)keyword, - "finishing-template", - opt_strings_catalog, - printer_opt_strings_catalog); - if (human_readable == NULL) - human_readable = (char *)keyword; - cupsFilePrintf(fp, "*cupsFinishingTemplate %s/%s: \"\n", keyword, - human_readable); - for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; - finishing_attr = ippNextAttribute(finishing_col)) { - if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION) { - const char *name = ippGetName(finishing_attr); - // Member attribute name - - if (strcmp(name, "media-size")) - cupsFilePrintf(fp, "%% %s\n", name); - } - } - cupsFilePuts(fp, "\"\n"); - cupsFilePuts(fp, "*End\n"); - } - - cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n"); - - if (cupsArrayCount(fin_options)) - { - const char *fin_option; // Current finishing option - - cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate"); - for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; - fin_option = (const char *)cupsArrayNext(fin_options)) - cupsFilePrintf(fp, " %s", fin_option); - cupsFilePuts(fp, "\"\n"); - - cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None"); - for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; - fin_option = (const char *)cupsArrayNext(fin_options)) - cupsFilePrintf(fp, " %s None", fin_option); - cupsFilePuts(fp, "\"\n"); - } - - cupsArrayDelete(templates); - } - - cupsArrayDelete(fin_options); - - // - // DefaultResolution... - // - - xres = common_def->x; - yres = common_def->y; - if (xres == yres) - cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", xres); - else - cupsFilePrintf(fp, "*DefaultResolution: %dx%ddpi\n", xres, yres); - - // - // cupsPrintQuality... - // - - if ((quality = - ippFindAttribute(response, "print-quality-supported", - IPP_TAG_ENUM)) != NULL) - { - human_readable = cfCatalogLookUpOption("print-quality", opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *cupsPrintQuality\n" - "*DefaultcupsPrintQuality: Normal\n", - (human_readable ? human_readable : "Print Quality")); - if (ippContainsInteger(quality, IPP_QUALITY_DRAFT)) - { - human_readable = cfCatalogLookUpChoice("3", "print-quality", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<>setpagedevice\"\n", - (human_readable ? human_readable : "Draft"), - min_res->x, min_res->y); - } - human_readable = cfCatalogLookUpChoice("4", "print-quality", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<>setpagedevice\"\n", - (human_readable ? human_readable : "Normal"), - common_def->x, common_def->y); - if (ippContainsInteger(quality, IPP_QUALITY_HIGH)) - { - human_readable = cfCatalogLookUpChoice("5", "print-quality", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<>setpagedevice\"\n", - (human_readable ? human_readable : "High"), - max_res->x, max_res->y); - } - cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n"); - } - - // Only add these options if jobs get sent to the printer as PDF, - // PWG Raster, or Apple Raster, as only then arbitrary IPP - // attributes get passed through from the filter command line - // to the printer by the "ipp" CUPS backend. - if (is_pdf || is_pwg || is_apple) - { - // - // Print Optimization ... - // - - if ((attr = ippFindAttribute(response, "print-content-optimize-default", - IPP_TAG_ZERO)) != NULL) - strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname)); - else - strlcpy(ppdname, "auto", sizeof(ppdname)); - - if ((attr = ippFindAttribute(response, "print-content-optimize-supported", - IPP_TAG_ZERO)) != NULL && - (count = ippGetCount(attr)) > 1) - { - human_readable = cfCatalogLookUpOption("print-content-optimize", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *print-content-optimize/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *print-content-optimize\n" - "*Defaultprint-content-optimize: %s\n", - (human_readable ? human_readable : "Print Optimization"), - ppdname); - for (i = 0; i < count; i ++) { - keyword = ippGetString(attr, i, NULL); - - human_readable = cfCatalogLookUpChoice((char *)keyword, - "print-content-optimize", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*print-content-optimize %s%s%s: \"\"\n", - keyword, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - } - cupsFilePuts(fp, "*CloseUI: *print-content-optimize\n"); - } - - // - // Print Rendering Intent ... - // - - if ((attr = ippFindAttribute(response, "print-rendering-intent-default", - IPP_TAG_ZERO)) != NULL) - strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname)); - else - strlcpy(ppdname, "auto", sizeof(ppdname)); - - if ((attr = ippFindAttribute(response, "print-rendering-intent-supported", - IPP_TAG_ZERO)) != NULL && - (count = ippGetCount(attr)) > 1) - { - human_readable = cfCatalogLookUpOption("print-rendering-intent", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *cupsRenderingIntent/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *cupsRenderingIntent\n" - "*DefaultcupsRenderingIntent: %s\n", - (human_readable ? human_readable : - "Print Rendering Intent"), - ppdname); - for (i = 0; i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - - human_readable = cfCatalogLookUpChoice((char *)keyword, - "print-rendering-intent", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*cupsRenderingIntent %s%s%s: \"<>setpagedevice\"\n", - keyword, - (human_readable ? "/" : ""), - (human_readable ? human_readable : ""), - keyword); - } - cupsFilePuts(fp, "*CloseUI: *cupsRenderingIntent\n"); - } - - // - // Print Scaling ... - // - - if ((attr = ippFindAttribute(response, "print-scaling-default", - IPP_TAG_ZERO)) != NULL) - strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname)); - else - strlcpy(ppdname, "auto", sizeof(ppdname)); - - if ((attr = ippFindAttribute(response, "print-scaling-supported", - IPP_TAG_ZERO)) != NULL && - (count = ippGetCount(attr)) > 1) - { - human_readable = cfCatalogLookUpOption("print-scaling", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*OpenUI *print-scaling/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *print-scaling\n" - "*Defaultprint-scaling: %s\n", - (human_readable ? human_readable : "Print Scaling"), - ppdname); - for (i = 0; i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - - human_readable = cfCatalogLookUpChoice((char *)keyword, "print-scaling", - opt_strings_catalog, - printer_opt_strings_catalog); - cupsFilePrintf(fp, "*print-scaling %s%s%s: \"\"\n", - keyword, - (human_readable ? "/" : ""), - (human_readable ? human_readable : "")); - } - cupsFilePuts(fp, "*CloseUI: *print-scaling\n"); - } - } - - // - // Phone Options for Fax.. - // - - if (is_fax) - { - human_readable = cfCatalogLookUpOption("Phone", opt_strings_catalog, - printer_opt_strings_catalog); - - cupsFilePrintf(fp, "*OpenUI *phone/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *phone\n" - "*Defaultphone: None\n" - "*phone None: \"\"\n" - "*CloseUI: *phone\n", - (human_readable ? human_readable : "Phone Number")); - cupsFilePrintf(fp,"*Customphone True: \"\"\n" - "*ParamCustomphone Text: 1 string 0 64\n"); - - human_readable = cfCatalogLookUpOption("faxPrefix", opt_strings_catalog, - printer_opt_strings_catalog); - - cupsFilePrintf(fp, "*OpenUI *faxPrefix/%s: PickOne\n" - "*OrderDependency: 10 AnySetup *faxPrefix\n" - "*DefaultfaxPrefix: None\n" - "*faxPrefix None: \"\"\n" - "*CloseUI: *faxPrefix\n", - (human_readable ? human_readable : "Pre-Dial Number")); - cupsFilePrintf(fp,"*CustomfaxPrefix True: \"\"\n" - "*ParamCustomfaxPrefix Text: 1 string 0 64\n"); - } - - // - // Presets... - // - - if ((attr = ippFindAttribute(response, "job-presets-supported", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - ipp_t *preset = ippGetCollection(attr, i); // Preset collection - const char *preset_name = // Preset name - ippGetString(ippFindAttribute(preset, - "preset-name", IPP_TAG_ZERO), 0, NULL), - *localized_name; // Localized preset name - ipp_attribute_t *member; // Member attribute in preset - const char *member_name; // Member attribute name - char member_value[256]; // Member attribute value - - if (!preset || !preset_name) - continue; - - if ((localized_name = - cfCatalogLookUpOption((char *)preset_name, - opt_strings_catalog, - printer_opt_strings_catalog)) == NULL) - cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name); - else - cupsFilePrintf(fp, "*APPrinterPreset %s/%s: \"\n", preset_name, - localized_name); - - for (member = ippFirstAttribute(preset); member; - member = ippNextAttribute(preset)) - { - member_name = ippGetName(member); - - if (!member_name || !strcmp(member_name, "preset-name")) - continue; - - if (!strcmp(member_name, "finishings")) - { - for (i = 0, count = ippGetCount(member); i < count; i ++) - { - const char *option = NULL; // PPD option name - - keyword = ippEnumString("finishings", ippGetInteger(member, i)); - - if (!strcmp(keyword, "booklet-maker")) - { - option = "Booklet"; - keyword = "True"; - } - else if (!strncmp(keyword, "fold-", 5)) - option = "FoldType"; - else if (!strncmp(keyword, "punch-", 6)) - option = "PunchMedia"; - else if (!strncmp(keyword, "bind-", 5) || - !strncmp(keyword, "edge-stitch-", 12) || - !strcmp(keyword, "saddle-stitch") || - !strncmp(keyword, "staple-", 7)) - option = "StapleLocation"; - - if (option && keyword) - cupsFilePrintf(fp, "*%s %s\n", option, keyword); - } - } - else if (!strcmp(member_name, "finishings-col")) - { - ipp_t *fin_col; // finishings-col value - - for (i = 0, count = ippGetCount(member); i < count; i ++) - { - fin_col = ippGetCollection(member, i); - - if ((keyword = - ippGetString(ippFindAttribute(fin_col, - "finishing-template", - IPP_TAG_ZERO), 0, NULL)) != NULL) - cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword); - } - } - else if (!strcmp(member_name, "media")) - { - // - // Map media to PageSize... - // - - if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && - pwg->ppd) - cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd); - } - else if (!strcmp(member_name, "media-col")) - { - media_col = ippGetCollection(member, 0); - - if ((media_size = - ippGetCollection(ippFindAttribute(media_col, - "media-size", - IPP_TAG_BEGIN_COLLECTION), - 0)) != NULL) - { - x_dim = ippFindAttribute(media_size, "x-dimension", - IPP_TAG_INTEGER); - y_dim = ippFindAttribute(media_size, "y-dimension", - IPP_TAG_INTEGER); - if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), - ippGetInteger(y_dim, 0))) != NULL && - pwg->ppd) - cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd); - } - - if ((keyword = ippGetString(ippFindAttribute(media_col, - "media-source", - IPP_TAG_ZERO), 0, - NULL)) != NULL) - { - ppdPwgPpdizeName(keyword, ppdname, sizeof(ppdname)); - cupsFilePrintf(fp, "*InputSlot %s\n", keyword); - } - - if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", - IPP_TAG_ZERO), 0, - NULL)) != NULL) - { - ppdPwgPpdizeName(keyword, ppdname, sizeof(ppdname)); - cupsFilePrintf(fp, "*MediaType %s\n", keyword); - } - } - else if (!strcmp(member_name, "print-quality")) - { - // - // Map print-quality to cupsPrintQuality... - // - - int qval = ippGetInteger(member, 0); - // print-quality value - static const char * const qualities[] = { "Draft", "Normal", "High" }; - // cupsPrintQuality values - - if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH) - cupsFilePrintf(fp, "*cupsPrintQuality %s\n", - qualities[qval - IPP_QUALITY_DRAFT]); - } - else if (!strcmp(member_name, "output-bin")) - { - ppdPwgPpdizeName(ippGetString(member, 0, NULL), ppdname, - sizeof(ppdname)); - cupsFilePrintf(fp, "*OutputBin %s\n", ppdname); - } - else if (!strcmp(member_name, "sides")) - { - keyword = ippGetString(member, 0, NULL); - if (keyword && !strcmp(keyword, "one-sided")) - cupsFilePuts(fp, "*Duplex None\n"); - else if (keyword && !strcmp(keyword, "two-sided-long-edge")) - cupsFilePuts(fp, "*Duplex DuplexNoTumble\n"); - else if (keyword && !strcmp(keyword, "two-sided-short-edge")) - cupsFilePuts(fp, "*Duplex DuplexTumble\n"); - } - else - { - // - // Add attribute name and value as-is... - // - - ippAttributeString(member, member_value, sizeof(member_value)); - cupsFilePrintf(fp, "*%s %s\n", member_name, member_value); - } - } - - cupsFilePuts(fp, "\"\n*End\n"); - } - } - - // - // constraints - // - - if (conflicts != NULL) - { - char* constraint; - for (constraint = (char *)cupsArrayFirst(conflicts); constraint; - constraint = (char *)cupsArrayNext(conflicts)) - cupsFilePrintf(fp, "%s", constraint); - } - - // - // Clean up and return... - // - - free(common_def); - free(min_res); - free(max_res); - - if (status_msg && status_msg_size) - snprintf(status_msg, status_msg_size, - "%s %sPPD generated.", - (is_apple ? "Apple Raster" : - (is_pwg ? "PWG Raster" : - (is_pdf ? "PDF" : - (is_pclm ? "PCLm" : - "Legacy IPP printer")))), - (is_fax ? "Fax " : "")); - - cupsFileClose(fp); - if (opt_strings_catalog) - cupsArrayDelete(opt_strings_catalog); - if (printer_opt_strings_catalog) - cupsArrayDelete(printer_opt_strings_catalog); - - return (buffer); - - // - // If we get here then there was a problem creating the PPD... - // - - bad_ppd: - - if (common_res) cupsArrayDelete(common_res); - if (common_def) free(common_def); - if (min_res) free(min_res); - if (max_res) free(max_res); - - cupsFileClose(fp); - if (printer_opt_strings_catalog) - cupsArrayDelete(printer_opt_strings_catalog); - unlink(buffer); - *buffer = '\0'; - - if (status_msg && status_msg_size) - snprintf(status_msg, status_msg_size, - "Printer does not support required IPP attributes or document formats."); - - return (NULL); -} diff --git a/ppd/ppd-ipp.c b/ppd/ppd-ipp.c deleted file mode 100644 index 90f564c6d..000000000 --- a/ppd/ppd-ipp.c +++ /dev/null @@ -1,1569 +0,0 @@ -// -// PPD options <-> IPP attributes routines for libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers. -// - -#include "ppd.h" -#include "debug-internal.h" -#include -#include - - -// -// Local functions... -// - -static ipp_t *create_media_col(const char *media, const char *source, - const char *type, int width, - int length, int bottom, int left, - int right, int top); -static ipp_t *create_media_size(int width, int length); -static ipp_t *create_media_size_ranges(int min_width, int min_length, - int max_width, - int max_length); - - -// -// 'ppdGetOptions()' - Get the PPD options corresponding to the IPP Job Template -// attributes for a printer with given PPD and given -// printer attributes (generated by ppdLoadAttributes()). -// - -int // O - Number of options -ppdGetOptions(cups_option_t **options, // O - Options - ipp_t *printer_attrs, // I - Printer attributes, from - // ppdLoadAttributes() - ipp_t *job_attrs, // I - Job attributes - ppd_file_t *ppd) // I - PPD file data -{ - ppd_cache_t *ppd_cache; // IPP to PPD cache data - int num_options = 0; // Number of options - ipp_attribute_t *attr; // Job attribute - char valstr[8192]; // Attribute value string - const char *value; // Option value - pwg_media_t *media = NULL; // Media mapping - int num_media_col = 0; // Number of media-col values - cups_option_t *media_col = NULL; // media-col values - const char *choice; // PPD choice - - - // - // No options to start... - // - - *options = NULL; - - // - // Media... - // - - if ((attr = ippFindAttribute(job_attrs, "media", IPP_TAG_ZERO)) == NULL) - if ((attr = ippFindAttribute(job_attrs, "media-col", IPP_TAG_ZERO)) == NULL) - if ((attr = ippFindAttribute(printer_attrs, "media-default", - IPP_TAG_ZERO)) == NULL) - attr = ippFindAttribute(printer_attrs, "media-col-default", - IPP_TAG_ZERO); - - if (attr) - { - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - - if (*value == '{') - { - // - // media-col value... - // - - num_media_col = cupsParseOptions(value, 0, &media_col); - } - else - { - // - // media value - map to media-col.media-size-name... - // - - num_media_col = cupsAddOption("media-size-name", value, 0, &media_col); - } - } - - if ((value = cupsGetOption("media-size-name", num_media_col, media_col)) - != NULL) - { - media = pwgMediaForPWG(value); - } - else if ((value = cupsGetOption("media-size", num_media_col, media_col)) - != NULL) - { - int num_media_size; // Number of media-size values - cups_option_t *media_size; // media-size values - const char *x_dimension, // x-dimension value - *y_dimension; // y-dimension value - - num_media_size = cupsParseOptions(value, 0, &media_size); - - if ((x_dimension = cupsGetOption("x-dimension", - num_media_size, media_size)) != NULL && - (y_dimension = cupsGetOption("y-dimension", - num_media_size, media_size)) != NULL) - media = pwgMediaForSize(atoi(x_dimension), atoi(y_dimension)); - - cupsFreeOptions(num_media_size, media_size); - } - - if (media) - num_options = cupsAddOption("PageSize", media->ppd, num_options, options); - - // - // Generate PPD's corresponding IPP <-> PPD cache data... - // - - if (ppd) - { - if (ppd->cache == NULL) - { - if ((ppd_cache = ppdCacheCreateWithPPD(ppd)) != NULL) - ppd->cache = ppd_cache; - } - else - ppd_cache = ppd->cache; - - // TODO: Fix me - values are names, not numbers... Also need to support - // finishings-col - if ((attr = ippFindAttribute(job_attrs, "finishings", IPP_TAG_ZERO)) - == NULL) - attr = ippFindAttribute(printer_attrs, "finishings-default", - IPP_TAG_ZERO); - - if (attr) - { - char *ptr; // Pointer into value - int fin; // Current value - - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - - for (fin = strtol(value, &ptr, 10); fin > 0; fin = strtol(ptr + 1, &ptr, 10)) - { - num_options = - ppdCacheGetFinishingOptions(ppd_cache, NULL, - (ipp_finishings_t)fin, - num_options, options); - - if (*ptr != ',') - break; - } - } - - if ((value = cupsGetOption("media-source", - num_media_col, media_col)) != NULL) - { - if ((choice = ppdCacheGetInputSlot(ppd_cache, NULL, value)) != NULL) - num_options = cupsAddOption("InputSlot", choice, num_options, options); - } - - if ((value = cupsGetOption("media-type", num_media_col, media_col)) != NULL) - { - if ((choice = ppdCacheGetMediaType(ppd_cache, NULL, value)) != NULL) - num_options = cupsAddOption("MediaType", choice, num_options, options); - } - - if ((attr = ippFindAttribute(job_attrs, "output-bin", IPP_TAG_ZERO)) - == NULL) - attr = ippFindAttribute(printer_attrs, "output-bin-default", - IPP_TAG_ZERO); - - if (attr) - { - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - if ((choice = ppdCacheGetOutputBin(ppd_cache, value)) != NULL) - num_options = cupsAddOption("OutputBin", choice, num_options, options); - } - - if ((attr = ippFindAttribute(job_attrs, "sides", IPP_TAG_ZERO)) == NULL) - attr = ippFindAttribute(printer_attrs, "sides-default", - IPP_TAG_ZERO); - - if (attr && ppd_cache->sides_option) - { - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - if (!strcmp(value, "one-sided") && ppd_cache->sides_1sided) - num_options = cupsAddOption(ppd_cache->sides_option, - ppd_cache->sides_1sided, - num_options, options); - else if (!strcmp(value, "two-sided-long-edge") && - ppd_cache->sides_2sided_long) - num_options = cupsAddOption(ppd_cache->sides_option, - ppd_cache->sides_2sided_long, - num_options, options); - else if (!strcmp(value, "two-sided-short-edge") && - ppd_cache->sides_2sided_short) - num_options = cupsAddOption(ppd_cache->sides_option, - ppd_cache->sides_2sided_short, - num_options, options); - } - - if ((attr = ippFindAttribute(job_attrs, "print-quality", IPP_TAG_ZERO)) - == NULL) - attr = ippFindAttribute(printer_attrs, "print-quality-default", - IPP_TAG_ZERO); - - if (attr) - { - int i; // Looping var - int pq; // Print quality (0-2) - int pcm = 1; // Print color model - // (0 = mono, 1 = color) - int num_presets; // Number of presets - cups_option_t *presets; // Presets - - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - if (!strcmp(value, "draft")) - pq = 0; - else if (!strcmp(value, "high")) - pq = 2; - else - pq = 1; - - if ((attr = ippFindAttribute(job_attrs, "print-color-mode", IPP_TAG_ZERO)) - == NULL) - attr = ippFindAttribute(printer_attrs, "print-color-mode-default", - IPP_TAG_ZERO); - - if (attr) - { - ippAttributeString(attr, valstr, sizeof(valstr)); - value = valstr; - if (value && !strcmp(value, "monochrome")) - pcm = 0; - } - - num_presets = ppd_cache->num_presets[pcm][pq]; - presets = ppd_cache->presets[pcm][pq]; - - for (i = 0; i < num_presets; i ++) - num_options = cupsAddOption(presets[i].name, presets[i].value, - num_options, options); - } - - // - // Mark the PPD with the options... - // - - ppdMarkDefaults(ppd); - ppdMarkOptions(ppd, num_options, *options); - } - - cupsFreeOptions(num_media_col, media_col); - - return (num_options); -} - - -// -// 'ppdLoadAttributes()' - Load IPP attributes from a PPD file to -// create printer IPP attriutes which describe -// the capabilities of the printer. Note that -// this is for telling filters and drivers how -// to print a job on the printer and NOT for a -// PPD retro-fitting Printer Application (or -// any other PPD-supporting IPP print server) -// to answer a get-printer-attributes IPP -// request. -// - -ipp_t * // O - IPP attributes or `NULL` - // on error -ppdLoadAttributes( - ppd_file_t *ppd) // I - PPD file data -{ - int i, j; // Looping vars - char *ptr; - cups_array_t *docformats; // document-format-supported - // values - ipp_t *attrs; // Attributes - ipp_attribute_t *attr; // Current attribute - ipp_attribute_t *aux_attr; // Auxiliary attribute - ipp_t *col; // Current collection value - ppd_attr_t *ppd_attr; // PPD attribute - ppd_option_t *ppd_option; // PPD option - ppd_choice_t *ppd_choice; // PPD choice - ppd_size_t *ppd_size; // Default PPD size - pwg_size_t *pwg_size, // Current PWG size - *default_size = NULL; // Default PWG size - const char *default_source = NULL, // Default media source - *default_type = NULL, // Default media type - *default_output_bin = NULL, // Default output bin - *default_output_order = NULL; // Default output order - pwg_map_t *pwg_map; // Mapping from PWG to PPD keywords - ppd_cache_t *pc; // PPD cache - ppd_pwg_finishings_t *finishings; // Current finishings value - const char *template; // Current finishings-template value - int num_margins; // Number of media-xxx-margin-supported - // values - int margins[10]; // media-xxx-margin-supported values - int xres = 0, // Default horizontal resolution - yres = 0; // Default vertical resolution - int is_texttotext; - int num_items; // Number of IPP attribute values - const char *items[20]; // IPP attribute values - char item_buf[64]; // RS value - int res_x[20], res_y[20], int_item[20]; - char *val, - *def_output_bin = NULL, - buf[1024], - cmd[256], // Device ID CMD: string - *cmdptr; - int def_found, - order, - face_up, - have_custom_size = 0; - cups_page_header2_t header; - static const char * const pdls[][2] = - { // MIME media type to command set - // mapping - { "application/postscript", "POSTSCRIPT,PS" }, - { "application/vnd.cups-postscript", "POSTSCRIPT,PS" }, - { "application/pdf", "PDF" }, - { "application/vnd.cups-pdf", "PDF" }, - { "application/vnd.canon-cpdl", "CPDL" }, - { "application/vnd.canon-lips", "LIPS" }, - { "application/vnd.hp-PCL", "PCL" }, - { "application/vnd.hp-PCLXL", "PCLXL" }, - { "application/vnd.ms-xpsdocument", "XPS" }, - { "image/jpeg", "JPEG" }, - { "image/pwg-raster", "PWGRaster" }, - { "image/urf", "URF,AppleRaster" }, - { "application/PCLm", "PCLM" }, - { "image/tiff", "TIFF" } - }; - static const int orientation_requested_supported[4] = - { // orientation-requested-supported - // values - IPP_ORIENT_PORTRAIT, - IPP_ORIENT_LANDSCAPE, - IPP_ORIENT_REVERSE_LANDSCAPE, - IPP_ORIENT_REVERSE_PORTRAIT - }; - static const char * const overrides_supported[] = - { // overrides-supported - "document-numbers", - "media", - "media-col", - "orientation-requested", - "pages" - }; - static const char * const print_color_mode_supported[] = - { // print-color-mode-supported values - "monochrome" - }; - static const char * const print_color_mode_supported_color[] = - { // print-color-mode-supported values - "auto", - "color", - "monochrome" - }; - static const int print_quality_supported[] = - { // print-quality-supported values - IPP_QUALITY_DRAFT, - IPP_QUALITY_NORMAL, - IPP_QUALITY_HIGH - }; - static const char * const print_content_optimize_supported[] = - { // print-content-optimize-supported - // values - "auto", - "text", - "graphic", - "text-and-graphic", - "photo" - }; - static const char * const sides_supported[] = - { // sides-supported values - "one-sided", - "two-sided-long-edge", - "two-sided-short-edge" - }; - - - // - // Open the PPD file... - // - - if (ppd == NULL) - return (NULL); - - if (ppd->cache == NULL) - { - if ((pc = ppdCacheCreateWithPPD(ppd)) != NULL) - ppd->cache = pc; - else - return (NULL); - } - else - pc = ppd->cache; - - if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL) - { - // - // Look up default size... - // - - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - if (!strcmp(pwg_size->map.ppd, ppd_size->name)) - { - default_size = pwg_size; - break; - } - } - } - - if (!default_size) - { - // - // Default to A4 or Letter... - // - - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - if (!strcmp(pwg_size->map.ppd, "Letter") || - !strcmp(pwg_size->map.ppd, "A4")) - { - default_size = pwg_size; - break; - } - } - - if (!default_size) - default_size = pc->sizes; // Last resort: first size - } - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL) - default_source = ppdCacheGetSource(pc, ppd_choice->choice); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL) - default_type = ppdCacheGetType(pc, ppd_choice->choice); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "OutputOrder")) != NULL) - default_output_order = ppd_choice->choice; - if ((ppd_choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL) - { - default_output_bin = ppd_choice->choice; - if (default_output_order == NULL && - (ppd_attr = ppdFindAttr(ppd, "PageStackOrder", - ppd_choice->choice)) != NULL) - default_output_order = ppd_attr->value; - } - if (default_output_order == NULL && - (ppd_attr = ppdFindAttr(ppd, "DefaultOutputOrder", 0)) != NULL) - default_output_order = ppd_attr->value; - if (default_output_order == NULL) - default_output_order = "Normal"; - - // - // Data formats which the printer understands, if the PPD specifies - // a driver in a "*cupsFilter(2): ..." line, we add the input format - // of the driver, as this is what our filter need to produce. - // - - docformats = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, NULL, - (cups_afree_func_t)free); - is_texttotext = 0; - strcpy(cmd, "CMD:"); - cmdptr = cmd + 4; - if (ppd->num_filters) - { - char *filter; - for (filter = (char *)cupsArrayFirst(pc->filters); - filter; - filter = (char *)cupsArrayNext(pc->filters)) - { - // String of the "*cupsfilter:" or "*cupsfilter2:" line - strncpy(buf, filter, sizeof(buf) - 1); - - // Do not count in "comandto..." filters - if (strstr(buf, " commandto")) - continue; - - // Is the PPD file using the "texttotext" filter? - if (strcmp(buf + strlen(buf) - 10, "texttotext") == 0) - is_texttotext = 1; - - // Separate the first word - ptr = buf; - while (*ptr && !isspace(*ptr)) ptr ++; - if (*ptr) - *ptr = '\0'; - - // Check whether the second word is not the cost value, then we have - // a "*cupsFilter2:* line and the second word is the printer's input - // format - ptr ++; - while (*ptr && isspace(*ptr)) ptr ++; - if (!isdigit(*ptr)) - { - strcpy(buf, ptr); - ptr = buf; - while (*ptr && !isspace(*ptr)) ptr ++; - if (*ptr) - *ptr = '\0'; - } - - // Add it to the list of output formats - cupsArrayAdd(docformats, strdup(buf)); - - // Add it to the CMD: string for the device ID - if (!ppdFindAttr(ppd, "1284DeviceId", NULL)) - { - // See if it is a known MIME media type and map to the corresponding - // 1284 command-set name... - for (i = 0; i < (sizeof(pdls) / sizeof(pdls[0])); i ++) - { - if (!strcasecmp(buf, pdls[i][0]) && - strlen(pdls[i][1]) < sizeof(cmd) - (size_t)(cmdptr - cmd) - 3) - { - // MIME media type matches, append this CMD value... - if (cmdptr > cmd + 4) - *cmdptr++ = ','; - strcpy(cmdptr, pdls[i][1]); - cmdptr += strlen(cmdptr); - } - } - } - } - if (strlen(cmd) > 4) - { - cmdptr[0] = ';'; - cmdptr[1] = '\0'; - } - else - cmd[0] = '\0'; - } - else - { - cupsArrayAdd(docformats, strdup("application/vnd.cups-postscript")); - if (!ppdFindAttr(ppd, "1284DeviceId", NULL)) - strcpy(cmd, "CMD:POSTSCRIPT,PS;"); - } - - // - // Create the attributes... - // - - attrs = ippNew(); - - // - // Properties of supported Raster formats: Convert from strings - // in the PPD files (from driverless PPD auto-generator) into - // printer IPP attributes - // - - for (i = 0; i < ppd->num_attrs; i ++) - { - ppd_attr = ppd->attrs[i]; - if ((cupsArrayFind(docformats, (void *)"image/urf") && - strcasecmp(ppd_attr->name, "cupsUrfSupported") == 0) || - (cupsArrayFind(docformats, (void *)"image/pwg-raster") && - strncasecmp(ppd_attr->name, "cupsPwgRaster", 13) == 0) || - (cupsArrayFind(docformats, (void *)"application/PCLm") && - strncasecmp(ppd_attr->name, "cupsPclm", 8) == 0)) - { - // Convert PPD-style names into IPP-style names - ppdPwgUnppdizeName(ppd_attr->name + 4, item_buf, sizeof(item_buf), NULL); - // Make array from comma-separated list - strncpy(buf, ppd_attr->value, sizeof(buf) - 1); - num_items = 0; - char *p = buf; - do - { - items[num_items ++] = p; - if ((p = strchr(p, ',')) != NULL) - { - *p = '\0'; - p ++; - } - // Default resolution via urf-supported - if (!strcmp(item_buf, "urf-supported") && - (xres == 0 && yres == 0) &&// Default resolution not found in - // other raster-format-related PPD - // attribute yet, use first resolution in - // urf-supported - items[num_items - 1][0] == 'R' && items[num_items - 1][1] == 'S') - xres = yres = atoi(items[num_items - 1] + 2); - } - while (p); - if (strlen(items[0]) > 3 && - strncasecmp(items[0] + strlen(items[0]) - 3, "dpi", 3) == 0 && - isdigit(*(items[0] + strlen(items[0]) - 4))) - { - // Resolutions - for (j = 0; j < num_items; j ++) - if (sscanf(items[j], "%dx%d", &res_x[j], &res_y[j]) == 1) - res_y[j] = res_x[j]; - ippAddResolutions(attrs, IPP_TAG_PRINTER, item_buf, num_items, - IPP_RES_PER_INCH, res_x, res_y); - // Default resolution? - if ((xres == 0 && yres == 0) || // Take first in list if there is no - // separate PPD attribute providing a - // default resolution - strstr(item_buf, "default")) // PPD attribute with default - // resolution, take it, even if we - // already had the list from which we - // have taken the first item. - { - xres = res_x[0]; - yres = res_y[0]; - } - } - else if (strlen(items[0]) > 0 && - ((int)(strtol(items[0], &p, 10) * 0) || - (errno == 0 && *p == '\0'))) - { - // Integer number(s) - for (j = 0; j < num_items; j ++) - int_item[j] = (int)strtol(items[j], NULL, 10); - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, item_buf, - num_items, int_item); - } - else - // General - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, item_buf, - num_items, NULL, items); - } - } - - // - // Resolution - // - - if (xres == 0 && yres == 0) - { - // Ignore error exits of ppdRasterInterpretPPD(), if it found a resolution - // setting before erroring it is OK for us - ppdRasterInterpretPPD(&header, ppd, 0, NULL, NULL); - // 100 dpi is default, this means that if we have 100 dpi here this - // method failed to find the printing resolution - buf[0] = '\0'; - if (header.HWResolution[0] != 100 || header.HWResolution[1] != 100) - { - xres = header.HWResolution[0]; - yres = header.HWResolution[1]; - } - else if ((ppd_choice = ppdFindMarkedChoice(ppd, "Resolution")) != NULL) - strncpy(buf, ppd_choice->choice, sizeof(buf) - 1); - else if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL) - strncpy(buf, ppd_attr->value, sizeof(buf) - 1); - else - // Use default of 300dpi... - xres = yres = 300; - buf[sizeof(buf) - 1] = '\0'; - if (buf[0]) - { - // Use the marked resolution or the default resolution in the PPD... - if ((i = sscanf(buf, "%dx%d", &xres, &yres)) == 1) - yres = xres; - else if (i <= 0) - xres = yres = 300; - } - } - - // Fax out PPD? - if (((ppd_attr = ppdFindAttr(ppd, "cupsFax", NULL)) != NULL && - strcasecmp(ppd_attr->value, "True") == 0) || - ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFaxOut", NULL)) != NULL && - strcasecmp(ppd_attr->value, "True") == 0)) - { - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "ipp-features-supported", NULL, "faxout"); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_URI), - "printer-uri-supported", NULL, "ipp://localhost/ipp/faxout"); - } - - // color-supported - ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", - (char)ppd->color_device); - - // copies-default - if (((ppd_choice = ppdFindMarkedChoice(ppd, "Copies")) == NULL || - (i = atoi(ppd_choice->choice)) <= 0) && - ((ppd_attr = ppdFindAttr(ppd, "DefaultCopies", NULL)) == NULL || - (i = atoi(ppd_attr->value)) <= 0)) - i = 1; - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", i); - - // copies-supported - ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, - pc->max_copies > 0 ? pc->max_copies : 999); - - // document-format-supported - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "document-format-supported", - cupsArrayCount(docformats), NULL, NULL); - for (ptr = (char *)cupsArrayFirst(docformats), i = 0; ptr; - ptr = (char *)cupsArrayNext(docformats), i ++) - ippSetString(attrs, &attr, i, ptr); - - // finishing-template-supported - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "finishing-template-supported", - cupsArrayCount(pc->templates) + 1, NULL, NULL); - ippSetString(attrs, &attr, 0, "none"); - for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); - template; i ++, template = (const char *)cupsArrayNext(pc->templates)) - ippSetString(attrs, &attr, i, template); - - // finishings-col-database - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", - cupsArrayCount(pc->templates) + 1, NULL); - - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", - NULL, "none"); - ippSetCollection(attrs, &attr, 0, col); - ippDelete(col); - - for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); - template; i ++, template = (const char *)cupsArrayNext(pc->templates)) - { - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", - NULL, template); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - // finishings-col-default - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", - NULL, "none"); - ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col); - ippDelete(col); - - // finishings-col-ready - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", - cupsArrayCount(pc->templates) + 1, NULL); - - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", - NULL, "none"); - ippSetCollection(attrs, &attr, 0, col); - ippDelete(col); - - for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; - i ++, template = (const char *)cupsArrayNext(pc->templates)) - { - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", - NULL, template); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - // finishings-col-supported - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "finishings-col-supported", NULL, "finishing-template"); - - // finishings-default - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", - IPP_FINISHINGS_NONE); - - // finishings-ready - attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "finishings-ready", - cupsArrayCount(pc->finishings) + 1, NULL); - ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE); - for (i = 1, - finishings = (ppd_pwg_finishings_t *)cupsArrayFirst(pc->finishings); - finishings; - i ++, - finishings = (ppd_pwg_finishings_t *)cupsArrayNext(pc->finishings)) - ippSetInteger(attrs, &attr, i, (int)finishings->value); - - // finishings-supported - attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "finishings-supported", - cupsArrayCount(pc->finishings) + 1, NULL); - ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE); - for (i = 1, - finishings = (ppd_pwg_finishings_t *)cupsArrayFirst(pc->finishings); - finishings; - i ++, - finishings = (ppd_pwg_finishings_t *)cupsArrayNext(pc->finishings)) - ippSetInteger(attrs, &attr, i, (int)finishings->value); - - // media-bottom-margin-supported - for (i = 0, num_margins = 0, pwg_size = pc->sizes; - i < pc->num_sizes && - num_margins < (int)(sizeof(margins) / sizeof(margins[0])); - i ++, pwg_size ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->bottom) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->bottom; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-bottom-margin-supported", num_margins, margins); - - // landscape-orientation-requested-preferred - if (ppd->landscape < 0) // Direction the printer rotates landscape - // (-90) - { - // Rotate clockwise (reverse landscape) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "landscape-orientation-requested-preferred", 5); - } - else if (ppd->landscape > 0) // (+90) - { - // Rotate counter-clockwise (landscape) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "landscape-orientation-requested-preferred", 4); - } - - // media-col-database, media-col-default, media-col-ready, media-default, - // media-ready - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", - pc->num_sizes, NULL); - if (strncasecmp(ppd_size->name, "Custom", 6) == 0) - { - // media-col-default - Custom size - int w = (int)(ppd_size->width / 72.0 * 2540.0); - int l = (int)(ppd_size->length / 72.0 * 2540.0); - if (w >= pc->custom_min_width && w <= pc->custom_max_width && - l >= pc->custom_min_length && l <= pc->custom_max_length) - { - pwgFormatSizeName(buf, sizeof(buf), NULL, "custom", w, l, NULL); - col = create_media_col(buf, default_source, default_type, w, l, - (int)(ppd_size->bottom / 72.0 * 2540.0), - (int)(ppd_size->left / 72.0 * 2540.0), - w - (int)(ppd_size->right / 72.0 * 2540.0), - l - (int)(ppd_size->top / 72.0 * 2540.0)); - ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col); - ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col); - ippDelete(col); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", - NULL, buf); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", - NULL, buf); - have_custom_size = 1; - } - } - - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - if ((ptr = strchr(pwg_size->map.ppd, '.')) != NULL) - { - // Page size variant, use original size's PWG name but PPD's - // dimensions and margins. - // - // This way we can associate extra size for overspraying when - // printing borderless with the original page size, for example - // for cfFilterPWGToRaster() to correct original-sized PWG - // Raster to overspray-sized CUPS Raster (for HPLIP for - // example). Filters can also switch to overspray size if the - // original size plus zero margins is requested for a job - pwg_size_t *size; - memmove(buf, pwg_size->map.ppd, ptr - pwg_size->map.ppd); - buf[ptr - pwg_size->map.ppd] = '\0'; - for (j = 0, size = pc->sizes; j < pc->num_sizes; j ++, size ++) - // Find entry of original size - if (strcasecmp(buf, size->map.ppd) == 0) - { - ptr = size->map.pwg; - break; - } - if (j == pc->num_sizes) - ptr = pwg_size->map.pwg; - } - else - ptr = pwg_size->map.pwg; - col = create_media_col(ptr, NULL, NULL, pwg_size->width, pwg_size->length, - pwg_size->bottom, pwg_size->left, pwg_size->right, - pwg_size->top); - if (is_texttotext) - { - // Add info about the number of lines and number of columns defined in - // the PPD for each page size to the appropriate media-col-database - // entries - snprintf(buf, sizeof(buf), "%sNumColumns", pwg_size->map.ppd); - if ((ppd_choice = ppdFindMarkedChoice(ppd, buf)) != NULL && - (j = atoi(ppd_choice->choice)) > 0) - ippAddInteger(col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "num-columns", j); - snprintf(buf, sizeof(buf), "%sNumLines", pwg_size->map.ppd); - if ((ppd_choice = ppdFindMarkedChoice(ppd, buf)) != NULL && - (j = atoi(ppd_choice->choice)) > 0) - ippAddInteger(col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "num-lines", j); - } - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - if (!have_custom_size && pwg_size == default_size) - { - // media-col-default - Standard size - col = create_media_col(ptr, default_source, default_type, - pwg_size->width, pwg_size->length, - pwg_size->bottom, pwg_size->left, - pwg_size->right, pwg_size->top); - ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col); - ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col); - ippDelete(col); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, ptr); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, ptr); - } - } - - // media-left-margin-supported - for (i = 0, num_margins = 0, pwg_size = pc->sizes; - i < pc->num_sizes && - num_margins < (int)(sizeof(margins) / sizeof(margins[0])); - i ++, pwg_size ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->left) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->left; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-left-margin-supported", num_margins, margins); - - // media-right-margin-supported - for (i = 0, num_margins = 0, pwg_size = pc->sizes; - i < pc->num_sizes && - num_margins < (int)(sizeof(margins) / sizeof(margins[0])); - i ++, pwg_size ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->right) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->right; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-right-margin-supported", num_margins, margins); - - // media-supported - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-supported", - pc->num_sizes + (ppd->variable_sizes ? 2 : 0), - NULL, NULL); - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - ippSetString(attrs, &attr, i, pwg_size->map.pwg); - if (ppd->variable_sizes) - { - ippSetString(attrs, &attr, pc->num_sizes, pc->custom_min_keyword); - ippSetString(attrs, &attr, pc->num_sizes + 1, pc->custom_max_keyword); - } - - // media-size-supported - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", - pc->num_sizes + (ppd->variable_sizes ? 1 : 0), NULL); - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - col = create_media_size(pwg_size->width, pwg_size->length); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - if (ppd->variable_sizes) - { - col = create_media_size_ranges(pc->custom_min_width, pc->custom_min_length, - pc->custom_max_width, pc->custom_max_length); - ippSetCollection(attrs, &attr, pc->num_sizes, col); - ippDelete(col); - } - - // media-source-supported - if (pc->num_sources > 0) - { - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-source-supported", pc->num_sources, - NULL, NULL); - for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++) - ippSetString(attrs, &attr, i, pwg_map->pwg); - } - else - { - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "media-source-supported", NULL, "auto"); - } - - // media-top-margin-supported - for (i = 0, num_margins = 0, pwg_size = pc->sizes; - i < pc->num_sizes && - num_margins < (int)(sizeof(margins) / sizeof(margins[0])); - i ++, pwg_size ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->top) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->top; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-top-margin-supported", num_margins, margins); - - // media-type-supported - if (pc->num_types > 0) - { - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-type-supported", pc->num_types, NULL, NULL); - for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++) - ippSetString(attrs, &attr, i, pwg_map->pwg); - } - else - { - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "media-type-supported", NULL, "auto"); - } - - // orientation-requested-default - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "orientation-requested-default", IPP_ORIENT_PORTRAIT); - - // orientation-requested-supported - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "orientation-requested-supported", - (int)(sizeof(orientation_requested_supported) / - sizeof(orientation_requested_supported[0])), - orientation_requested_supported); - - // output-bin-supported and output-bin-default - def_found = 0; - if (pc->num_bins > 0) - { - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "output-bin-supported", pc->num_bins, NULL, NULL); - for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++) - { - ippSetString(attrs, &attr, i, pwg_map->pwg); - if (default_output_bin && !strcmp(pwg_map->ppd, default_output_bin)) - { - def_output_bin = pwg_map->pwg; - def_found = 1; - } - if ((ppd_attr = ppdFindAttr(ppd, "PageStackOrder", - pwg_map->ppd)) != NULL) - val = ppd_attr->value; - else - val = NULL; - order = (val && strcasecmp(val, "Normal") == 0 ? 1 : - (val && strcasecmp(val, "Reverse") == 0 ? -1 : 0)); - if (i == 0 || - (!def_found && ((order == 1 && - strcasecmp(default_output_order, "Normal")) || - (order == -1 && - strcasecmp(default_output_order, "Reverse"))))) - def_output_bin = pwg_map->pwg; - face_up = (((strcasestr(pwg_map->pwg, "face") && - strcasestr(pwg_map->pwg, "up")) || - (strcasestr(pwg_map->ppd, "face") && - strcasestr(pwg_map->ppd, "up"))) ? 1 : - (((strcasestr(pwg_map->pwg, "face") && - strcasestr(pwg_map->pwg, "down")) || - (strcasestr(pwg_map->ppd, "face") && - strcasestr(pwg_map->ppd, "down"))) ? -1 : 0)); - if (order == 0) - { - if (face_up != 0) - order = -face_up; - else - order = (strcasecmp(default_output_order, "Normal") == 0 ? 1 : - (strcasecmp(default_output_order, "Reverse") == 0 ? -1 : 0)); - } - if (face_up == 0 && order != 0) - face_up = -order; - snprintf(buf, sizeof(buf), - "type=unknown;maxcapacity=-2;remaining=-2;status=5;stackingorder=%s;pagedelivery=%s;name=%s", - (order == -1 ? "lastToFirst" : - (order == 1 ? "firstToLast" : - "unknown")), - (face_up == -1 ? "faceDown" : - (face_up == -1 ? "faceUp" : - "unknown")), - pwg_map->ppd); - if (i == 0) - aux_attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, - "printer-output-tray", buf, strlen(buf)); - else - ippSetOctetString(attrs, &aux_attr, i, buf, strlen(buf)); - } - } - else - { - order = (strcasecmp(default_output_order, "Normal") == 0 ? 1 : - (strcasecmp(default_output_order, "Reverse") == 0 ? -1 : 1)); - def_output_bin = (order == 1 ? "face-down" : "face-up"); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "output-bin-supported", NULL, def_output_bin); - snprintf(buf, sizeof(buf), - "type=unknown;maxcapacity=-2;remaining=-2;status=5;stackingorder=%s;pagedelivery=%s;name=%s", - (order == -1 ? "lastToFirst" : "firstToLast"), - (order == -1 ? "faceUp" : "faceDown"), - def_output_bin); - ippAddOctetString(attrs, IPP_TAG_PRINTER, - "printer-output-tray", buf, strlen(buf)); - } - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "output-bin-default", NULL, def_output_bin); - - // overrides-supported - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "overrides-supported", - (int)(sizeof(overrides_supported) / - sizeof(overrides_supported[0])), - NULL, overrides_supported); - - // page-ranges-supported - ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1); - - // pages-per-minute - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", - ppd->throughput); - - // pages-per-minute-color - if (ppd->color_device) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "pages-per-minute-color", ppd->throughput); - - // print-color-mode-default - bool mono = - (ppd->color_device && - (((ppd_choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL && - (strcasestr(ppd_choice->choice, "mono") || - strcasestr(ppd_choice->choice, "gray") || - strcasestr(ppd_choice->choice, "bw") || - strcasestr(ppd_choice->choice, "bi-level") || - strcasestr(ppd_choice->choice, "bi_level"))) || - ((ppd_choice = ppdFindMarkedChoice(ppd, "OutputMode")) != NULL && - (strcasestr(ppd_choice->choice, "mono") || - strcasestr(ppd_choice->choice, "gray") || - strcasestr(ppd_choice->choice, "bw") || - strcasestr(ppd_choice->choice, "bi-level") || - strcasestr(ppd_choice->choice, "bi_level"))))); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-color-mode-default", NULL, - ppd->color_device && !mono ? "auto" : "monochrome"); - - // print-color-mode-supported - if (ppd->color_device) - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "print-color-mode-supported", - (int)(sizeof(print_color_mode_supported_color) / - sizeof(print_color_mode_supported_color[0])), - NULL, print_color_mode_supported_color); - else - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "print-color-mode-supported", - (int)(sizeof(print_color_mode_supported) / - sizeof(print_color_mode_supported[0])), - NULL, print_color_mode_supported); - - // print-content-optimize-default - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "print-content-optimize-default", NULL, "auto"); - - // print-content-optimize-supported - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "print-content-optimize-supported", - (int)(sizeof(print_content_optimize_supported) / - sizeof(print_content_optimize_supported[0])), - NULL, print_content_optimize_supported); - - // print-quality-default - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", - IPP_QUALITY_NORMAL); - - // print-quality-supported - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "print-quality-supported", - (int)(sizeof(print_quality_supported) / - sizeof(print_quality_supported[0])), - print_quality_supported); - - // print-rendering-intent-default - if ((ppd_choice = - ppdFindMarkedChoice(ppd, "cupsRenderingIntent")) != NULL || - (ppd_choice = - ppdFindMarkedChoice(ppd, "print-rendering-intent")) != NULL) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-rendering-intent-default", NULL, ppd_choice->choice); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "print-rendering-intent-default", NULL, "auto"); - - // print-rendering-intent-supported - if (((ppd_option = ppdFindOption(ppd, "cupsRenderingIntent")) != NULL || - (ppd_option = ppdFindOption(ppd, "print-rendering-intent")) != NULL) && - ppd_option->num_choices > 0) - { - for (i = 0; i < ppd_option->num_choices && i < sizeof(items); i ++) - items[i] = ppd_option->choices[i].choice; - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-rendering-intent-supported", i, NULL, items); - } - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "print-rendering-intent-supported", NULL, "auto"); - - // printer-device-id - if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL) - { - // - // Use the device ID string from the PPD... - // - - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", - NULL, ppd_attr->value); - } - else - { - // - // Synthesize a device ID string... - // - - char device_id[1024]; // Device ID string - - snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;%s", - ppd->manufacturer, ppd->modelname, cmd); - - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", - NULL, device_id); - } - - // printer-info - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, - ppd->nickname); - - // printer-input-tray - if (pc->num_sources > 0) - { - for (i = 0, attr = NULL; i < pc->num_sources; i ++) - { - char input_tray[1024]; // printer-input-tray value - - if (!strcmp(pc->sources[i].pwg, "manual") || - strstr(pc->sources[i].pwg, "-man") != NULL) - snprintf(input_tray, sizeof(input_tray), - "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", - pc->sources[i].pwg); - else - snprintf(input_tray, sizeof(input_tray), - "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", - pc->sources[i].pwg); - - if (attr) - ippSetOctetString(attrs, &attr, i, input_tray, (int)strlen(input_tray)); - else - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", - input_tray, (int)strlen(input_tray)); - } - } - else - { - static const char *printer_input_tray = - "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto"; - - ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", - printer_input_tray, (int)strlen(printer_input_tray)); - } - - // printer-make-andXS-model - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", - NULL, ppd->nickname); - - // printer-resolution-default - ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", - IPP_RES_PER_INCH, xres, yres); - - // printer-resolution-supported - ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", - IPP_RES_PER_INCH, xres, yres); - - // sides-default - if (pc->sides_option && pc->sides_2sided_long && - ppdIsMarked(ppd, pc->sides_option, pc->sides_2sided_long)) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "sides-default", NULL, "two-sided-long-edge"); - else if (pc->sides_option && pc->sides_2sided_short && - ppdIsMarked(ppd, pc->sides_option, pc->sides_2sided_short)) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "sides-default", NULL, "two-sided-long-short"); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "sides-default", NULL, "one-sided"); - - // sides-supported - if (pc->sides_option && pc->sides_2sided_long) - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "sides-supported", - (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), - NULL, sides_supported); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), - "sides-supported", NULL, "one-sided"); - - // Extra attributes for "texttotext" filter - if (is_texttotext) - { - if ((ppd_choice = ppdFindMarkedChoice(ppd, "OverLongLines")) != NULL && - ppd_choice->choice && ppd_choice->choice[0]) - { - ppdPwgUnppdizeName(ppd_choice->choice, buf, sizeof(buf), NULL); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "over-long-lines-default", NULL, buf); - } - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "TabWidth")) != NULL && - (i = atoi(ppd_choice->choice)) > 0) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "tab-width-default", i); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "Pagination")) != NULL && - ppd_choice->choice && ppd_choice->choice[0]) - { - ppdPwgUnppdizeName(ppd_choice->choice, buf, sizeof(buf), NULL); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "pagination-default", NULL, buf); - } - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "page-left")) != NULL && - (i = atoi(ppd_choice->choice)) > 0) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "page-left-default", i); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "page-right")) != NULL && - (i = atoi(ppd_choice->choice)) > 0) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "page-right-default", i); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "page-top")) != NULL && - (i = atoi(ppd_choice->choice)) > 0) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "page-top-default", i); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "page-bottom")) != NULL && - (i = atoi(ppd_choice->choice)) > 0) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "page-bottom-default", i); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "PrinterEncoding")) != NULL && - ppd_choice->choice && ppd_choice->choice[0]) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "printer-encoding-default", NULL, ppd_choice->choice); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "NewlineCharacters")) != NULL && - ppd_choice->choice && ppd_choice->choice[0]) - { - ppdPwgUnppdizeName(ppd_choice->choice, buf, sizeof(buf), NULL); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "newline-characters-default", NULL, buf); - } - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "SendFF")) != NULL && - ppd_choice->choice && ppd_choice->choice[0]) - { - ppdPwgUnppdizeName(ppd_choice->choice, buf, sizeof(buf), NULL); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "send-ff-default", NULL, buf); - } - - } - - // Clean up - cupsArrayDelete(docformats); - return (attrs); -} - -// -// 'create_media_col()' - Create a media-col value. -// - -static ipp_t * // O - media-col collection -create_media_col(const char *media, // I - Media name - const char *source, // I - Media source, if any - const char *type, // I - Media type, if any - int width, // I - x-dimension in 2540ths - int length, // I - y-dimension in 2540ths - int bottom, // I - Bottom margin in 2540ths - int left, // I - Left margin in 2540ths - int right, // I - Right margin in 2540ths - int top) // I - Top margin in 2540ths -{ - ipp_t *media_col = ippNew(), // media-col value - *media_size = create_media_size(width, length); - // media-size value - char media_key[256]; // media-key value - const char *media_key_suffix = ""; // media-key suffix - - - if (bottom == 0 && left == 0 && right == 0 && top == 0) - media_key_suffix = "_borderless"; - - if (type && source) - snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, - type, media_key_suffix); - else if (type) - snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, - media_key_suffix); - else if (source) - snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, - media_key_suffix); - else - snprintf(media_key, sizeof(media_key), "%s%s", media, media_key_suffix); - - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", - NULL, media_key); - ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size); - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-size-name", NULL, media); - if (bottom >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-bottom-margin", bottom); - if (left >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-left-margin", left); - if (right >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-right-margin", right); - if (top >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-top-margin", top); - if (source) - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-source", NULL, source); - if (type) - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-type", NULL, type); - - ippDelete(media_size); - - return (media_col); -} - - -// -// 'create_media_size()' - Create a media-size value. -// - -static ipp_t * // O - media-col collection -create_media_size(int width, // I - x-dimension in 2540ths - int length) // I - y-dimension in 2540ths -{ - ipp_t *media_size = ippNew(); // media-size value - - - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", - width); - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", - length); - - return (media_size); -} - - -// -// 'create_media_size_range()' - Create a media-size range for custom sizes. -// - -static ipp_t * // O - media-col collection -create_media_size_ranges(int min_width, // I - min x dim in 2540ths - int min_length, // I - nin y dim in 2540ths - int max_width, // I - max x dim in 2540ths - int max_length) // I - max y dim in 2540ths -{ - ipp_t *media_size_ranges = ippNew(); // media-size value - - ippAddRange(media_size_ranges, IPP_TAG_PRINTER, "x-dimension", min_width, - max_width); - ippAddRange(media_size_ranges, IPP_TAG_PRINTER, "y-dimension", min_length, - max_length); - - return (media_size_ranges); -} diff --git a/ppd/ppd-load-profile.c b/ppd/ppd-load-profile.c deleted file mode 100644 index 454334344..000000000 --- a/ppd/ppd-load-profile.c +++ /dev/null @@ -1,894 +0,0 @@ -// -// PPD color profile attribute lookup functions for libppd. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 1993-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// ppdFindColorAttr() - Find a PPD attribute based on the colormodel, -// media, and resolution. -// ppdLutLoad() - Load a LUT from a PPD file. -// ppdRGBLoad() - Load a RGB color profile from a PPD file. -// ppdCMYKLoad() - Load a CMYK color profile from PPD attributes. -// - -// -// Include necessary headers. -// - -#include -#include "ppd.h" -#include -#include -#include -#include - - -// -// 'ppdFindColorAttr()' - Find a PPD attribute based on the colormodel, -// media, and resolution. -// - -ppd_attr_t * // O - Matching attribute or - // NULL -ppdFindColorAttr(ppd_file_t *ppd, // I - PPD file - const char *name, // I - Attribute name - const char *colormodel, // I - Color model - const char *media, // I - Media type - const char *resolution, // I - Resolution - char *spec, // O - Final selection string - int specsize, // I - Size of string buffer - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - ppd_attr_t *attr; // Attribute - - - // - // Range check input... - // - - if (!ppd || !name || !colormodel || !media || !resolution || !spec || - specsize < IPP_MAX_NAME) - return (NULL); - - // - // Look for the attribute with the following keywords: - // - // ColorModel.MediaType.Resolution - // ColorModel.Resolution - // ColorModel - // MediaType.Resolution - // MediaType - // Resolution - // "" - // - - snprintf(spec, specsize, "%s.%s.%s", colormodel, media, resolution); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Looking for \"*%s %s\"...", name, spec); - if ((attr = ppdFindAttr(ppd, name, spec)) != NULL && attr->value != NULL) - return (attr); - - snprintf(spec, specsize, "%s.%s", colormodel, resolution); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Looking for \"*%s %s\"...", name, spec); - if ((attr = ppdFindAttr(ppd, name, spec)) != NULL && attr->value != NULL) - return (attr); - - snprintf(spec, specsize, "%s", colormodel); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Looking for \"*%s %s\"...", name, spec); - if ((attr = ppdFindAttr(ppd, name, spec)) != NULL && attr->value != NULL) - return (attr); - - snprintf(spec, specsize, "%s.%s", media, resolution); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Looking for \"*%s %s\"...", name, spec); - if ((attr = ppdFindAttr(ppd, name, spec)) != NULL && attr->value != NULL) - return (attr); - - snprintf(spec, specsize, "%s", media); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Looking for \"*%s %s\"...", name, spec); - if ((attr = ppdFindAttr(ppd, name, spec)) != NULL && attr->value != NULL) - return (attr); - - snprintf(spec, specsize, "%s", resolution); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Looking for \"*%s %s\"...", name, spec); - if ((attr = ppdFindAttr(ppd, name, spec)) != NULL && attr->value != NULL) - return (attr); - - spec[0] = '\0'; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Looking for \"*%s\"...", name); - if ((attr = ppdFindAttr(ppd, name, spec)) != NULL && attr->value != NULL) - return (attr); - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "No instance of \"*%s\" found...", name); - - return (NULL); -} - - -// -// 'ppdLutLoad()' - Load a LUT from a PPD file. -// - -cf_lut_t * // O - New lookup table -ppdLutLoad(ppd_file_t *ppd, // I - PPD file - const char *colormodel, // I - Color model - const char *media, // I - Media type - const char *resolution, // I - Resolution - const char *ink, // I - Ink name - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - char name[PPD_MAX_NAME], // Attribute name - spec[PPD_MAX_NAME]; // Attribute spec - ppd_attr_t *attr; // Attribute - int nvals; // Number of values - float vals[4]; // Values - - - // - // Range check input... - // - - if (!ppd || !colormodel || !media || !resolution || !ink) - return (NULL); - - // - // Try to find the LUT values... - // - - snprintf(name, sizeof(name), "cups%sDither", ink); - - if ((attr = ppdFindColorAttr(ppd, name, colormodel, media, resolution, spec, - sizeof(spec), log, ld)) == NULL) - attr = ppdFindColorAttr(ppd, "cupsAllDither", colormodel, media, - resolution, spec, sizeof(spec), log, ld); - - if (!attr) - return (NULL); - - vals[0] = 0.0; - vals[1] = 0.0; - vals[2] = 0.0; - vals[3] = 0.0; - nvals = sscanf(attr->value, "%f%f%f", vals + 1, vals + 2, vals + 3) + 1; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "Loaded LUT %s from PPD with values [%.3f %.3f %.3f %.3f]", - name, vals[0], vals[1], vals[2], vals[3]); - - return (cfLutNew(nvals, vals, log, ld)); -} - - -// -// 'ppdRGBLoad()' - Load a RGB color profile from a PPD file. -// - -cf_rgb_t * // O - New color profile -ppdRGBLoad(ppd_file_t *ppd, // I - PPD file - const char *colormodel, // I - Color model - const char *media, // I - Media type - const char *resolution, // I - Resolution - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - int i, // Looping var - cube_size, // Size of color lookup cube - num_channels, // Number of color channels - num_samples; // Number of color samples - cf_sample_t *samples; // Color samples - float values[7]; // Color sample values - char spec[IPP_MAX_NAME]; // Profile name - ppd_attr_t *attr; // Attribute from PPD file - cf_rgb_t *rgbptr; // RGB color profile - - - // - // Find the following attributes: - // - // cupsRGBProfile - Specifies the cube size, number of channels, and - // number of samples - // cupsRGBSample - Specifies an RGB to CMYK color sample - // - - if ((attr = ppdFindColorAttr(ppd, "cupsRGBProfile", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "No cupsRGBProfile attribute found for the current settings!"); - return (NULL); - } - - if (!attr->value || sscanf(attr->value, "%d%d%d", &cube_size, &num_channels, - &num_samples) != 3) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Bad cupsRGBProfile attribute \'%s\'!", - attr->value ? attr->value : "(null)"); - return (NULL); - } - - if (cube_size < 2 || cube_size > 16 || - num_channels < 1 || num_channels > CF_MAX_RGB || - num_samples != (cube_size * cube_size * cube_size)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Bad cupsRGBProfile attribute \'%s\'!", - attr->value); - return (NULL); - } - - // - // Allocate memory for the samples and read them... - // - - if ((samples = calloc(num_samples, sizeof(cf_sample_t))) == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Unable to allocate memory for RGB profile!"); - return (NULL); - } - - // - // Read all of the samples... - // - - for (i = 0; i < num_samples; i ++) - if ((attr = ppdFindNextAttr(ppd, "cupsRGBSample", spec)) == NULL) - break; - else if (!attr->value) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Bad cupsRGBSample value!"); - break; - } - else if (sscanf(attr->value, "%f%f%f%f%f%f%f", values + 0, - values + 1, values + 2, values + 3, values + 4, values + 5, - values + 6) != (3 + num_channels)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Bad cupsRGBSample value!"); - break; - } - else - { - samples[i].rgb[0] = (int)(255.0 * values[0] + 0.5); - samples[i].rgb[1] = (int)(255.0 * values[1] + 0.5); - samples[i].rgb[2] = (int)(255.0 * values[2] + 0.5); - samples[i].colors[0] = (int)(255.0 * values[3] + 0.5); - if (num_channels > 1) - samples[i].colors[1] = (int)(255.0 * values[4] + 0.5); - if (num_channels > 2) - samples[i].colors[2] = (int)(255.0 * values[5] + 0.5); - if (num_channels > 3) - samples[i].colors[3] = (int)(255.0 * values[6] + 0.5); - } - - // - // If everything went OK, create the color profile... - // - - if (i == num_samples) - rgbptr = cfRGBNew(num_samples, samples, cube_size, num_channels); - else - rgbptr = NULL; - - // - // Free the temporary sample array and return... - // - - free(samples); - - return (rgbptr); -} - - -// -// 'ppdCMYKLoad()' - Load a CMYK color profile from PPD attributes. -// - -cf_cmyk_t * // O - CMYK color separation -ppdCMYKLoad(ppd_file_t *ppd, // I - PPD file - const char *colormodel, // I - ColorModel value - const char *media, // I - MediaType value - const char *resolution, // I - Resolution value - cf_logfunc_t log, // I - Log function - void *ld) // I - Log function data -{ - cf_cmyk_t *cmyk; // CMYK color separation - char spec[IPP_MAX_NAME]; // Profile name - ppd_attr_t *attr; // Attribute from PPD file - int num_channels; // Number of color components - float gamval, // Gamma correction value - density, // Density value - light, // Light ink limit - dark, // Light ink cut-off - lower, // Start of black ink - upper; // End of color ink - int num_xypoints; // Number of X,Y points - float xypoints[100 * 2], // X,Y points - *xyptr; // Current X,Y point - - - // - // Range check input... - // - - if (ppd == NULL || colormodel == NULL || resolution == NULL || media == NULL) - return (NULL); - - // - // Find the following attributes: - // - // cupsAllGamma - Set default curve using gamma + density - // cupsAllXY - Set default curve using XY points - // cupsBlackGamma - Set black curve using gamma + density - // cupsBlackGeneration - Set black generation - // cupsBlackLightDark - Set black light/dark transition - // cupsBlackXY - Set black curve using XY points - // cupsCyanGamma - Set cyan curve using gamma + density - // cupsCyanLightDark - Set cyan light/dark transition - // cupsCyanXY - Set cyan curve using XY points - // cupsInkChannels - Set number of color channels - // cupsInkLimit - Set total ink limit - // cupsLightBlackGamma - Set light black curve using gamma + density - // cupsLightBlackXY - Set light black curve using XY points - // cupsLightCyanGamma - Set light cyan curve using gamma + density - // cupsLightCyanXY - Set light cyan curve using XY points - // cupsLightMagentaGamma - Set light magenta curve using gamma + density - // cupsLightMagentaXY - Set light magenta curve using XY points - // cupsMagentaGamma - Set magenta curve using gamma + density - // cupsMagentaLightDark - Set magenta light/dark transition - // cupsMagentaXY - Set magenta curve using XY points - // cupsYellowGamma - Set yellow curve using gamma + density - // cupsYellowXY - Set yellow curve using XY points - // - // The only required attribute is cupsInkChannels. - // - // The *XY attributes have precedence over the *Gamma attributes, and - // the *Light* attributes have precedence over the corresponding - // *LightDark* attributes. - // - - // - // Get the required cupsInkChannels attribute... - // - - if ((attr = - ppdFindColorAttr(ppd, "cupsInkChannels", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) == NULL) - return (NULL); - - num_channels = atoi(attr->value); - - if (num_channels < 1 || num_channels > 7 || num_channels == 5) - return (NULL); - - if ((cmyk = cfCMYKNew(num_channels)) == NULL) - return (NULL); - - // - // Get the optional cupsInkLimit attribute... - // - - if ((attr = ppdFindColorAttr(ppd, "cupsInkLimit", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - cfCMYKSetInkLimit(cmyk, atof(attr->value)); - - // - // Get the optional cupsBlackGeneration attribute... - // - - if ((attr = ppdFindColorAttr(ppd, "cupsBlackGeneration", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - if (sscanf(attr->value, "%f%f", &lower, &upper) == 2) - cfCMYKSetBlack(cmyk, lower, upper, log, ld); - } - - // - // Get the optional cupsBlackXY or cupsBlackGamma attributes... - // - - if (num_channels != 3) - { - if ((attr = ppdFindColorAttr(ppd, "cupsBlackXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsBlackXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - switch (num_channels) - { - case 1 : - case 2 : - cfCMYKSetCurve(cmyk, 0, num_xypoints, xypoints, log, ld); - break; - case 4 : - cfCMYKSetCurve(cmyk, 3, num_xypoints, xypoints, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetCurve(cmyk, 5, num_xypoints, xypoints, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsBlackGamma", colormodel, - media, resolution, spec, - sizeof(spec), log, ld)) != NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - switch (num_channels) - { - case 1 : - case 2 : - cfCMYKSetGamma(cmyk, 0, gamval, density, log, ld); - break; - case 4 : - cfCMYKSetGamma(cmyk, 3, gamval, density, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetGamma(cmyk, 5, gamval, density, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsAllXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - switch (num_channels) - { - case 1 : - case 2 : - cfCMYKSetCurve(cmyk, 0, num_xypoints, xypoints, log, ld); - break; - case 4 : - cfCMYKSetCurve(cmyk, 3, num_xypoints, xypoints, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetCurve(cmyk, 5, num_xypoints, xypoints, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllGamma", colormodel, - media, resolution, spec, - sizeof(spec), log, ld)) != NULL && - num_channels != 3) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - switch (num_channels) - { - case 1 : - case 2 : - cfCMYKSetGamma(cmyk, 0, gamval, density, log, ld); - break; - case 4 : - cfCMYKSetGamma(cmyk, 3, gamval, density, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetGamma(cmyk, 5, gamval, density, log, ld); - break; - } - } - } - - if (num_channels > 2) - { - // - // Get the optional cupsCyanXY or cupsCyanGamma attributes... - // - - if ((attr = ppdFindColorAttr(ppd, "cupsCyanXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsCyanXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - cfCMYKSetCurve(cmyk, 0, num_xypoints, xypoints, log, ld); - } - else if ((attr = ppdFindColorAttr(ppd, "cupsCyanGamma", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - cfCMYKSetGamma(cmyk, 0, gamval, density, log, ld); - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsAllXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - cfCMYKSetCurve(cmyk, 0, num_xypoints, xypoints, log, ld); - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllGamma", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - cfCMYKSetGamma(cmyk, 0, gamval, density, log, ld); - } - - // - // Get the optional cupsMagentaXY or cupsMagentaGamma attributes... - // - - if ((attr = ppdFindColorAttr(ppd, "cupsMagentaXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsMagentaXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetCurve(cmyk, 1, num_xypoints, xypoints, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetCurve(cmyk, 2, num_xypoints, xypoints, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsMagentaGamma", colormodel, - media, resolution, spec, sizeof(spec), - log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetGamma(cmyk, 1, gamval, density, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetGamma(cmyk, 2, gamval, density, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsAllXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetCurve(cmyk, 1, num_xypoints, xypoints, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetCurve(cmyk, 2, num_xypoints, xypoints, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllGamma", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetGamma(cmyk, 1, gamval, density, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetGamma(cmyk, 2, gamval, density, log, ld); - break; - } - } - - // - // Get the optional cupsYellowXY or cupsYellowGamma attributes... - // - - if ((attr = ppdFindColorAttr(ppd, "cupsYellowXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsYellowXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetCurve(cmyk, 2, num_xypoints, xypoints, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetCurve(cmyk, 4, num_xypoints, xypoints, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsYellowGamma", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetGamma(cmyk, 2, gamval, density, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetGamma(cmyk, 4, gamval, density, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsAllXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetCurve(cmyk, 2, num_xypoints, xypoints, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetCurve(cmyk, 4, num_xypoints, xypoints, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsAllGamma", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - switch (num_channels) - { - case 3 : - case 4 : - cfCMYKSetGamma(cmyk, 2, gamval, density, log, ld); - break; - case 6 : - case 7 : - cfCMYKSetGamma(cmyk, 4, gamval, density, log, ld); - break; - } - } - } - - // - // Get the optional cupsLightBlackXY, cupsLightBlackGamma, or - // cupsBlackLtDk attributes... - // - - if (num_channels == 2 || num_channels == 7) - { - if ((attr = ppdFindColorAttr(ppd, "cupsLightBlackXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsLightBlackXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - switch (num_channels) - { - case 2 : - cfCMYKSetCurve(cmyk, 1, num_xypoints, xypoints, log, ld); - break; - case 7 : - cfCMYKSetCurve(cmyk, 6, num_xypoints, xypoints, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsLightBlackGamma", colormodel, - media, resolution, spec, - sizeof(spec), log, ld)) != NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - switch (num_channels) - { - case 2 : - cfCMYKSetGamma(cmyk, 1, gamval, density, log, ld); - break; - case 7 : - cfCMYKSetGamma(cmyk, 6, gamval, density, log, ld); - break; - } - } - else if ((attr = ppdFindColorAttr(ppd, "cupsBlackLtDk", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &light, &dark) == 2) - switch (num_channels) - { - case 2 : - cfCMYKSetLtDk(cmyk, 0, light, dark, log, ld); - break; - case 7 : - cfCMYKSetLtDk(cmyk, 5, light, dark, log, ld); - break; - } - else - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Bad cupsBlackLtDk value \"%s\"!", - attr->value); - } - else - if (log) log(ld, CF_LOGLEVEL_WARN, - "No light black attribute found for %s!", - spec); - } - - if (num_channels >= 6) - { - // - // Get the optional cupsLightCyanXY, cupsLightCyanGamma, or - // cupsCyanLtDk attributes... - // - - if ((attr = ppdFindColorAttr(ppd, "cupsLightCyanXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsLightCyanXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - cfCMYKSetCurve(cmyk, 1, num_xypoints, xypoints, log, ld); - } - else if ((attr = ppdFindColorAttr(ppd, "cupsLightCyanGamma", colormodel, - media, resolution, spec, - sizeof(spec), log, ld)) != NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - cfCMYKSetGamma(cmyk, 1, gamval, density, log, ld); - } - else if ((attr = ppdFindColorAttr(ppd, "cupsCyanLtDk", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &light, &dark) == 2) - cfCMYKSetLtDk(cmyk, 0, light, dark, log, ld); - else - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Bad cupsCyanLtDk value \"%s\"!", - attr->value); - } - else - if (log) log(ld, CF_LOGLEVEL_WARN, - "No light cyan attribute found for %s!", - spec); - - // - // Get the optional cupsLightMagentaXY, cupsLightMagentaGamma, or - // cupsMagentaLtDk attributes... - // - - if ((attr = ppdFindColorAttr(ppd, "cupsLightMagentaXY", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != NULL) - { - for (num_xypoints = 0, xyptr = xypoints; - attr != NULL && attr->value != NULL && num_xypoints < 100; - attr = ppdFindNextAttr(ppd, "cupsLightMagentaXY", spec)) - if (sscanf(attr->value, "%f%f", xyptr, xyptr + 1) == 2) - { - num_xypoints ++; - xyptr += 2; - } - - cfCMYKSetCurve(cmyk, 3, num_xypoints, xypoints, log, ld); - } - else if ((attr = ppdFindColorAttr(ppd, "cupsLightMagentaGamma", colormodel, - media, resolution, spec, - sizeof(spec), log, ld)) != NULL) - { - if (sscanf(attr->value, "%f%f", &gamval, &density) == 2) - cfCMYKSetGamma(cmyk, 3, gamval, density, log, ld); - } - else if ((attr = ppdFindColorAttr(ppd, "cupsMagentaLtDk", colormodel, media, - resolution, spec, sizeof(spec), log, ld)) != - NULL) - { - if (sscanf(attr->value, "%f%f", &light, &dark) == 2) - cfCMYKSetLtDk(cmyk, 2, light, dark, log, ld); - else - if (log) log(ld, CF_LOGLEVEL_ERROR, - "Bad cupsMagentaLtDk value \"%s\"!", - attr->value); - } - else - if (log) log(ld, CF_LOGLEVEL_WARN, - "No light magenta attribute found for %s!", - spec); - } - - // - // Return the new profile... - // - - return (cmyk); -} diff --git a/ppd/ppd-localize.c b/ppd/ppd-localize.c deleted file mode 100644 index a072d48a6..000000000 --- a/ppd/ppd-localize.c +++ /dev/null @@ -1,737 +0,0 @@ -// -// PPD localization routines for libppd. -// -// Copyright 2007-2018 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers. -// - -#include "string-private.h" -#include "language-private.h" -#include "ppd.h" -#include "debug-internal.h" - - -// -// Local functions... -// - -static cups_lang_t *ppd_ll_CC(char *ll_CC, size_t ll_CC_size); - - -// -// 'ppdLocalize()' - Localize the PPD file to the current locale. -// -// All groups, options, and choices are localized, as are ICC profile -// descriptions, printer presets, and custom option parameters. Each -// localized string uses the UTF-8 character encoding. -// -// @since CUPS 1.2/macOS 10.5@ -// - -int // O - 0 on success, -1 on error -ppdLocalize(ppd_file_t *ppd) // I - PPD file -{ - int i, j, k; // Looping vars - ppd_group_t *group; // Current group - ppd_option_t *option; // Current option - ppd_choice_t *choice; // Current choice - ppd_coption_t *coption; // Current custom option - ppd_cparam_t *cparam; // Current custom parameter - ppd_attr_t *attr, // Current attribute - *locattr; // Localized attribute - char ckeyword[PPD_MAX_NAME], // Custom keyword - ll_CC[6]; // Language + country locale - - - // - // Range check input... - // - - DEBUG_printf(("ppdLocalize(ppd=%p)", ppd)); - - if (!ppd) - return (-1); - - // - // Get the default language... - // - - ppd_ll_CC(ll_CC, sizeof(ll_CC)); - - // - // Now lookup all of the groups, options, choices, etc. - // - - for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) - { - if ((locattr = ppdLocalizedAttr(ppd, "Translation", group->name, - ll_CC)) != NULL) - strlcpy(group->text, locattr->text, sizeof(group->text)); - - for (j = group->num_options, option = group->options; j > 0; - j --, option ++) - { - if ((locattr = ppdLocalizedAttr(ppd, "Translation", option->keyword, - ll_CC)) != NULL) - strlcpy(option->text, locattr->text, sizeof(option->text)); - - for (k = option->num_choices, choice = option->choices; - k > 0; - k --, choice ++) - { - if (strcmp(choice->choice, "Custom") || - !ppdFindCustomOption(ppd, option->keyword)) - locattr = ppdLocalizedAttr(ppd, option->keyword, choice->choice, - ll_CC); - else - { - snprintf(ckeyword, sizeof(ckeyword), "Custom%.34s", option->keyword); - - locattr = ppdLocalizedAttr(ppd, ckeyword, "True", ll_CC); - } - - if (locattr) - strlcpy(choice->text, locattr->text, sizeof(choice->text)); - } - } - } - - // - // Translate any custom parameters... - // - - for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions); - coption; - coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions)) - { - for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) - { - snprintf(ckeyword, sizeof(ckeyword), "ParamCustom%.29s", - coption->keyword); - - if ((locattr = ppdLocalizedAttr(ppd, ckeyword, cparam->name, - ll_CC)) != NULL) - strlcpy(cparam->text, locattr->text, sizeof(cparam->text)); - } - } - - // - // Translate ICC profile names... - // - - if ((attr = ppdFindAttr(ppd, "APCustomColorMatchingName", NULL)) != NULL) - { - if ((locattr = ppdLocalizedAttr(ppd, "APCustomColorMatchingName", - attr->spec, ll_CC)) != NULL) - strlcpy(attr->text, locattr->text, sizeof(attr->text)); - } - - for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); - attr; - attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) - { - cupsArraySave(ppd->sorted_attrs); - - if ((locattr = ppdLocalizedAttr(ppd, "cupsICCProfile", attr->spec, - ll_CC)) != NULL) - strlcpy(attr->text, locattr->text, sizeof(attr->text)); - - cupsArrayRestore(ppd->sorted_attrs); - } - - // - // Translate printer presets... - // - - for (attr = ppdFindAttr(ppd, "APPrinterPreset", NULL); - attr; - attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) - { - cupsArraySave(ppd->sorted_attrs); - - if ((locattr = ppdLocalizedAttr(ppd, "APPrinterPreset", attr->spec, - ll_CC)) != NULL) - strlcpy(attr->text, locattr->text, sizeof(attr->text)); - - cupsArrayRestore(ppd->sorted_attrs); - } - - return (0); -} - - -// -// 'ppdLocalizeAttr()' - Localize an attribute. -// -// This function uses the current locale to find the localized attribute for -// the given main and option keywords. If no localized version of the -// attribute exists for the current locale, the unlocalized version is returned. -// - -ppd_attr_t * // O - Localized attribute or - // @code NULL@ if none exists -ppdLocalizeAttr(ppd_file_t *ppd, // I - PPD file - const char *keyword, // I - Main keyword - const char *spec) // I - Option keyword or @code NULL@ - // for none -{ - ppd_attr_t *locattr; // Localized attribute - char ll_CC[6]; // Language + country locale - - - // - // Get the default language... - // - - ppd_ll_CC(ll_CC, sizeof(ll_CC)); - - // - // Find the localized attribute... - // - - if (spec) - locattr = ppdLocalizedAttr(ppd, keyword, spec, ll_CC); - else - locattr = ppdLocalizedAttr(ppd, "Translation", keyword, ll_CC); - - if (!locattr) - locattr = ppdFindAttr(ppd, keyword, spec); - - return (locattr); -} - - -// -// 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason -// attribute. -// -// This function uses the current locale to find the corresponding reason -// text or URI from the attribute value. If "scheme" is NULL or "text", -// the returned value contains human-readable (UTF-8) text from the translation -// string or attribute value. Otherwise the corresponding URI is returned. -// -// If no value of the requested scheme can be found, NULL is returned. -// -// @since CUPS 1.3/macOS 10.5@ -// - -const char * // O - Value or NULL if not found -ppdLocalizeIPPReason( - ppd_file_t *ppd, // I - PPD file - const char *reason, // I - IPP reason keyword to look up - const char *scheme, // I - URI scheme or NULL for text - char *buffer, // I - Value buffer - size_t bufsize) // I - Size of value buffer -{ - cups_lang_t *lang; // Current language - ppd_attr_t *locattr; // Localized attribute - char ll_CC[6], // Language + country locale - *bufptr, // Pointer into buffer - *bufend, // Pointer to end of buffer - *valptr; // Pointer into value - int ch; // Hex-encoded character - size_t schemelen; // Length of scheme name - - - // - // Range check input... - // - - if (buffer) - *buffer = '\0'; - - if (!ppd || !reason || (scheme && !*scheme) || - !buffer || bufsize < PPD_MAX_TEXT) - return (NULL); - - // - // Get the default language... - // - - lang = ppd_ll_CC(ll_CC, sizeof(ll_CC)); - - // - // Find the localized attribute... - // - - if ((locattr = ppdLocalizedAttr(ppd, "cupsIPPReason", reason, - ll_CC)) == NULL) - locattr = ppdFindAttr(ppd, "cupsIPPReason", reason); - - if (!locattr) - { - if (lang && (!scheme || !strcmp(scheme, "text")) && strcmp(reason, "none")) - { - // - // Try to localize a standard printer-state-reason keyword... - // - - char msgid[1024], // State message identifier - *ptr; // Pointer to state suffix - const char *message = NULL; // Localized message - - snprintf(msgid, sizeof(msgid), "printer-state-reasons.%s", reason); - if ((ptr = strrchr(msgid, '-')) != NULL && - (!strcmp(ptr, "-error") || !strcmp(ptr, "-report") || - !strcmp(ptr, "-warning"))) - *ptr = '\0'; - - message = _ppdLangString(lang, msgid); - - if (message && strcmp(message, msgid)) - { - strlcpy(buffer, _ppdLangString(lang, message), bufsize); - return (buffer); - } - } - - return (NULL); - } - - // - // Now find the value we need... - // - - bufend = buffer + bufsize - 1; - - if (!scheme || !strcmp(scheme, "text")) - { - // - // Copy a text value (either the translation text or text:... URIs from - // the value... - // - - strlcpy(buffer, locattr->text, bufsize); - - for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;) - { - if (!strncmp(valptr, "text:", 5)) - { - // - // Decode text: URI and add to the buffer... - // - - valptr += 5; - - while (*valptr && !_ppd_isspace(*valptr) && bufptr < bufend) - { - if (*valptr == '%' && isxdigit(valptr[1] & 255) && - isxdigit(valptr[2] & 255)) - { - // - // Pull a hex-encoded character from the URI... - // - - valptr ++; - - if (isdigit(*valptr & 255)) - ch = (*valptr - '0') << 4; - else - ch = (tolower(*valptr) - 'a' + 10) << 4; - valptr ++; - - if (isdigit(*valptr & 255)) - *bufptr++ = (char)(ch | (*valptr - '0')); - else - *bufptr++ = (char)(ch | (tolower(*valptr) - 'a' + 10)); - valptr ++; - } - else if (*valptr == '+') - { - *bufptr++ = ' '; - valptr ++; - } - else - *bufptr++ = *valptr++; - } - } - else - { - // - // Skip this URI... - // - - while (*valptr && !_ppd_isspace(*valptr)) - valptr++; - } - - // - // Skip whitespace... - // - - while (_ppd_isspace(*valptr)) - valptr ++; - } - - if (bufptr > buffer) - *bufptr = '\0'; - - return (buffer); - } - else - { - // - // Copy a URI... - // - - schemelen = strlen(scheme); - if (scheme[schemelen - 1] == ':') // Force scheme to be just the name - schemelen --; - - for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;) - { - if ((!strncmp(valptr, scheme, schemelen) && valptr[schemelen] == ':') || - (*valptr == '/' && !strcmp(scheme, "file"))) - { - // - // Copy URI... - // - - while (*valptr && !_ppd_isspace(*valptr) && bufptr < bufend) - *bufptr++ = *valptr++; - - *bufptr = '\0'; - - return (buffer); - } - else - { - // - // Skip this URI... - // - - while (*valptr && !_ppd_isspace(*valptr)) - valptr++; - } - - // - // Skip whitespace... - // - - while (_ppd_isspace(*valptr)) - valptr ++; - } - - return (NULL); - } -} - - -// -// 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names -// attribute value. -// -// This function uses the current locale to find the corresponding name -// text from the attribute value. If no localized text for the requested -// name can be found, @code NULL@ is returned. -// -// @since CUPS 1.4/macOS 10.6@ -// - -const char * // O - Value or @code NULL@ if not found -ppdLocalizeMarkerName( - ppd_file_t *ppd, // I - PPD file - const char *name) // I - Marker name to look up -{ - ppd_attr_t *locattr; // Localized attribute - char ll_CC[6]; // Language + country locale - - - // - // Range check input... - // - - if (!ppd || !name) - return (NULL); - - // - // Get the default language... - // - - ppd_ll_CC(ll_CC, sizeof(ll_CC)); - - // - // Find the localized attribute... - // - - if ((locattr = ppdLocalizedAttr(ppd, "cupsMarkerName", name, - ll_CC)) == NULL) - locattr = ppdFindAttr(ppd, "cupsMarkerName", name); - - return (locattr ? locattr->text : NULL); -} - - -// -// 'ppdFreeLanguages()' - Free an array of languages from ppdGetLanguages. -// - -void -ppdFreeLanguages( - cups_array_t *languages) // I - Languages array -{ - char *language; // Current language - - - for (language = (char *)cupsArrayFirst(languages); - language; - language = (char *)cupsArrayNext(languages)) - free(language); - - cupsArrayDelete(languages); -} - - -// -// 'ppdGetLanguages()' - Get an array of languages from a PPD file. -// - -cups_array_t * // O - Languages array -ppdGetLanguages(ppd_file_t *ppd) // I - PPD file -{ - cups_array_t *languages; // Languages array - ppd_attr_t *attr; // cupsLanguages attribute - char *value, // Copy of attribute value - *start, // Start of current language - *ptr; // Pointer into languages - - - // - // See if we have a cupsLanguages attribute... - // - - if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) == NULL || !attr->value) - return (NULL); - - // - // Yes, load the list... - // - - if ((languages = cupsArrayNew((cups_array_func_t)strcmp, NULL)) == NULL) - return (NULL); - - if ((value = strdup(attr->value)) == NULL) - { - cupsArrayDelete(languages); - return (NULL); - } - - for (ptr = value; *ptr;) - { - // - // Skip leading whitespace... - // - - while (_ppd_isspace(*ptr)) - ptr ++; - - if (!*ptr) - break; - - // - // Find the end of this language name... - // - - for (start = ptr; *ptr && !_ppd_isspace(*ptr); ptr ++); - - if (*ptr) - *ptr++ = '\0'; - - if (!strcmp(start, "en")) - continue; - - cupsArrayAdd(languages, strdup(start)); - } - - // - // Free the temporary string and return either an array with one or more - // values or a NULL pointer... - // - - free(value); - - if (cupsArrayCount(languages) == 0) - { - cupsArrayDelete(languages); - return (NULL); - } - else - return (languages); -} - - -// -// 'ppdHashName()' - Generate a hash value for a device or profile name. -// -// This function is primarily used on macOS, but is generally accessible -// since cupstestppd needs to check for profile name collisions in PPD files... -// - -unsigned // O - Hash value -ppdHashName(const char *name) // I - Name to hash -{ - unsigned mult, // Multiplier - hash = 0; // Hash value - - - for (mult = 1; *name && mult <= 128; mult ++, name ++) - hash += (*name & 255) * mult; - - return (hash); -} - - -// -// 'ppdLocalizedAttr()' - Find a localized attribute. -// - -ppd_attr_t * // O - Localized attribute or NULL -ppdLocalizedAttr(ppd_file_t *ppd, // I - PPD file - const char *keyword, // I - Main keyword - const char *spec, // I - Option keyword - const char *ll_CC) // I - Language + country locale -{ - char lkeyword[PPD_MAX_NAME]; // Localization keyword - ppd_attr_t *attr; // Current attribute - - - DEBUG_printf(("4ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", " - "ll_CC=\"%s\")", ppd, keyword, spec, ll_CC)); - - // - // Look for Keyword.ll_CC, then Keyword.ll... - // - - snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll_CC, keyword); - if ((attr = ppdFindAttr(ppd, lkeyword, spec)) == NULL) - { - // - // - // - // Multiple locales need special handling... Sigh... - // - - if (!strcmp(ll_CC, "zh_HK")) - { - snprintf(lkeyword, sizeof(lkeyword), "zh_TW.%s", keyword); - attr = ppdFindAttr(ppd, lkeyword, spec); - } - - if (!attr) - { - snprintf(lkeyword, sizeof(lkeyword), "%2.2s.%s", ll_CC, keyword); - attr = ppdFindAttr(ppd, lkeyword, spec); - } - - if (!attr) - { - if (!strncmp(ll_CC, "ja", 2)) - { - // - // Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese - // PPD files were incorrectly assigned "jp" as the locale name - // instead of "ja". Support both the old (incorrect) and new - // locale names for Japanese... - // - - snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword); - attr = ppdFindAttr(ppd, lkeyword, spec); - } - else if (!strncmp(ll_CC, "nb", 2)) - { - // - // Norway has two languages, "Bokmal" (the primary one) - // and "Nynorsk" (new Norwegian); this code maps from the (currently) - // recommended "nb" to the previously recommended "no"... - // - - snprintf(lkeyword, sizeof(lkeyword), "no.%s", keyword); - attr = ppdFindAttr(ppd, lkeyword, spec); - } - else if (!strncmp(ll_CC, "no", 2)) - { - // - // Norway has two languages, "Bokmal" (the primary one) - // and "Nynorsk" (new Norwegian); we map "no" to "nb" here as - // recommended by the locale folks... - // - - snprintf(lkeyword, sizeof(lkeyword), "nb.%s", keyword); - attr = ppdFindAttr(ppd, lkeyword, spec); - } - } - } - -#ifdef DEBUG - if (attr) - DEBUG_printf(("5ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr->name, - attr->spec, attr->text, attr->value ? attr->value : "")); - else - DEBUG_puts("5ppdLocalizedAttr: NOT FOUND"); -#endif // DEBUG - - return (attr); -} - - -// -// 'ppd_ll_CC()' - Get the current locale names. -// - -static cups_lang_t * // O - Current language -ppd_ll_CC(char *ll_CC, // O - Country-specific locale name - size_t ll_CC_size) // I - Size of country-specific name -{ - cups_lang_t *lang; // Current language - - - // - // Get the current locale... - // - - if ((lang = cupsLangDefault()) == NULL) - { - strlcpy(ll_CC, "en_US", ll_CC_size); - return (NULL); - } - - // - // Copy the locale name... - // - - strlcpy(ll_CC, lang->language, ll_CC_size); - - if (strlen(ll_CC) == 2) - { - // - // Map "ll" to primary/origin country locales to have the best - // chance of finding a match... - // - - if (!strcmp(ll_CC, "cs")) - strlcpy(ll_CC, "cs_CZ", ll_CC_size); - else if (!strcmp(ll_CC, "en")) - strlcpy(ll_CC, "en_US", ll_CC_size); - else if (!strcmp(ll_CC, "ja")) - strlcpy(ll_CC, "ja_JP", ll_CC_size); - else if (!strcmp(ll_CC, "sv")) - strlcpy(ll_CC, "sv_SE", ll_CC_size); - else if (!strcmp(ll_CC, "zh")) // Simplified Chinese - strlcpy(ll_CC, "zh_CN", ll_CC_size); - } - - DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...", - lang->language, ll_CC)); - return (lang); -} diff --git a/ppd/ppd-mark.c b/ppd/ppd-mark.c deleted file mode 100644 index 748530f1f..000000000 --- a/ppd/ppd-mark.c +++ /dev/null @@ -1,1102 +0,0 @@ -// -// Option marking routines for libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "ppd.h" -#include "debug-internal.h" - - -// -// Local functions... -// - -#ifdef DEBUG -static void ppd_debug_marked(ppd_file_t *ppd, const char *title); -#else -# define ppd_debug_marked(ppd,title) -#endif // DEBUG -static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g); -static void ppd_mark_choices(ppd_file_t *ppd, const char *s); -static void ppd_mark_option(ppd_file_t *ppd, const char *option, - const char *choice); - - -// -// 'ppdMarkOptions()' - Mark command-line options in a PPD file. -// -// This function maps the IPP "finishings", "media", "mirror", -// "multiple-document-handling", "output-bin", "print-color-mode", -// "print-quality", "printer-resolution", and "sides" attributes to their -// corresponding PPD options and choices. -// - -int // O - 1 if conflicts exist, 0 otherwise -ppdMarkOptions( - ppd_file_t *ppd, // I - PPD file - int num_options, // I - Number of options - cups_option_t *options) // I - Options -{ - int i, j; // Looping vars - char *ptr, // Pointer into string - s[255]; // Temporary string - const char *val, // Pointer into value - *media, // media option - *output_bin, // output-bin option - *page_size, // PageSize option - *ppd_keyword, // PPD keyword - *print_color_mode, // print-color-mode option - *print_quality, // print-quality option - *sides; // sides option - cups_option_t *optptr; // Current option - ppd_attr_t *attr; // PPD attribute - ppd_cache_t *cache; // PPD cache and mapping data - - - // - // Check arguments... - // - - if (!ppd || num_options <= 0 || !options) - return (0); - - ppd_debug_marked(ppd, "Before..."); - - // - // Do special handling for finishings, media, output-bin, output-mode, - // print-color-mode, print-quality, and PageSize... - // - - media = cupsGetOption("media", num_options, options); - output_bin = cupsGetOption("output-bin", num_options, options); - page_size = cupsGetOption("PageSize", num_options, options); - print_quality = cupsGetOption("print-quality", num_options, options); - sides = cupsGetOption("sides", num_options, options); - - if ((print_color_mode = cupsGetOption("print-color-mode", num_options, - options)) == NULL) - print_color_mode = cupsGetOption("output-mode", num_options, options); - - if ((media || output_bin || print_color_mode || print_quality || sides) && - !ppd->cache) - { - // - // Load PPD cache and mapping data as needed... - // - - ppd->cache = ppdCacheCreateWithPPD(ppd); - } - - cache = ppd->cache; - - if (media) - { - // - // Loop through the option string, separating it at commas and marking each - // individual option as long as the corresponding PPD option (PageSize, - // InputSlot, etc.) is not also set. - // - // For PageSize, we also check for an empty option value since some versions - // of macOS use it to specify auto-selection of the media based solely on - // the size. - // - - for (val = media; *val;) - { - // - // Extract the sub-option from the string... - // - - for (ptr = s; *val && *val != ',' && (size_t)(ptr - s) < (sizeof(s) - 1);) - *ptr++ = *val++; - *ptr++ = '\0'; - - if (*val == ',') - val ++; - - // - // Mark it... - // - - if (!page_size || !page_size[0]) - { - if (!_ppd_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s)) - ppd_mark_option(ppd, "PageSize", s); - else if ((ppd_keyword = ppdCacheGetPageSize(cache, NULL, s, NULL)) != - NULL) - ppd_mark_option(ppd, "PageSize", ppd_keyword); - } - - if (cache && cache->source_option && - !cupsGetOption(cache->source_option, num_options, options) && - (ppd_keyword = ppdCacheGetInputSlot(cache, NULL, s)) != NULL) - ppd_mark_option(ppd, cache->source_option, ppd_keyword); - - if (!cupsGetOption("MediaType", num_options, options) && - (ppd_keyword = ppdCacheGetMediaType(cache, NULL, s)) != NULL) - ppd_mark_option(ppd, "MediaType", ppd_keyword); - } - } - - if (cache) - { - if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat", - num_options, options) && - !cupsGetOption("APPrinterPreset", num_options, options) && - (print_color_mode || print_quality)) - { - // - // Map output-mode and print-quality to a preset... - // - - ppd_pwg_print_color_mode_t pwg_pcm;// print-color-mode index - ppd_pwg_print_quality_t pwg_pq; // print-quality index - cups_option_t *preset;// Current preset option - - if (print_color_mode && !strcmp(print_color_mode, "monochrome")) - pwg_pcm = PPD_PWG_PRINT_COLOR_MODE_MONOCHROME; - else - pwg_pcm = PPD_PWG_PRINT_COLOR_MODE_COLOR; - - if (print_quality) - { - pwg_pq = (ppd_pwg_print_quality_t)(atoi(print_quality) - - IPP_QUALITY_DRAFT); - if (pwg_pq < PPD_PWG_PRINT_QUALITY_DRAFT) - pwg_pq = PPD_PWG_PRINT_QUALITY_DRAFT; - else if (pwg_pq > PPD_PWG_PRINT_QUALITY_HIGH) - pwg_pq = PPD_PWG_PRINT_QUALITY_HIGH; - } - else - pwg_pq = PPD_PWG_PRINT_QUALITY_NORMAL; - - if (cache->num_presets[pwg_pcm][pwg_pq] == 0) - { - // - // Try to find a preset that works so that we maximize the chances of us - // getting a good print using IPP attributes. - // - - if (cache->num_presets[pwg_pcm][PPD_PWG_PRINT_QUALITY_NORMAL] > 0) - pwg_pq = PPD_PWG_PRINT_QUALITY_NORMAL; - else if (cache->num_presets[PPD_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0) - pwg_pcm = PPD_PWG_PRINT_COLOR_MODE_COLOR; - else - { - pwg_pq = PPD_PWG_PRINT_QUALITY_NORMAL; - pwg_pcm = PPD_PWG_PRINT_COLOR_MODE_COLOR; - } - } - - if (cache->num_presets[pwg_pcm][pwg_pq] > 0) - { - // - // Copy the preset options as long as the corresponding names are not - // already defined in the IPP request... - // - - for (i = cache->num_presets[pwg_pcm][pwg_pq], - preset = cache->presets[pwg_pcm][pwg_pq]; - i > 0; - i --, preset ++) - { - if (!cupsGetOption(preset->name, num_options, options)) - ppd_mark_option(ppd, preset->name, preset->value); - } - } - } - - if (output_bin && !cupsGetOption("OutputBin", num_options, options) && - (ppd_keyword = ppdCacheGetOutputBin(cache, output_bin)) != NULL) - { - // - // Map output-bin to OutputBin... - // - - ppd_mark_option(ppd, "OutputBin", ppd_keyword); - } - - if (sides && cache->sides_option && - !cupsGetOption(cache->sides_option, num_options, options)) - { - // - // Map sides to duplex option... - // - - if (!strcmp(sides, "one-sided") && cache->sides_1sided) - ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided); - else if (!strcmp(sides, "two-sided-long-edge") && - cache->sides_2sided_long) - ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long); - else if (!strcmp(sides, "two-sided-short-edge") && - cache->sides_2sided_short) - ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short); - } - } - - // - // Mark other options... - // - - for (i = num_options, optptr = options; i > 0; i --, optptr ++) - { - if (!_ppd_strcasecmp(optptr->name, "media") || - !_ppd_strcasecmp(optptr->name, "output-bin") || - !_ppd_strcasecmp(optptr->name, "output-mode") || - !_ppd_strcasecmp(optptr->name, "print-quality") || - !_ppd_strcasecmp(optptr->name, "sides")) - continue; - else if (!_ppd_strcasecmp(optptr->name, "resolution") || - !_ppd_strcasecmp(optptr->name, "printer-resolution")) - { - ppd_mark_option(ppd, "Resolution", optptr->value); - ppd_mark_option(ppd, "SetResolution", optptr->value); - // Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper - ppd_mark_option(ppd, "JCLResolution", optptr->value); - // HP - ppd_mark_option(ppd, "CNRes_PGP", optptr->value); - // Canon - } - else if (!_ppd_strcasecmp(optptr->name, "multiple-document-handling")) - { - if (!cupsGetOption("Collate", num_options, options) && - ppdFindOption(ppd, "Collate")) - { - if (_ppd_strcasecmp(optptr->value, - "separate-documents-uncollated-copies")) - ppd_mark_option(ppd, "Collate", "True"); - else - ppd_mark_option(ppd, "Collate", "False"); - } - } - else if (!_ppd_strcasecmp(optptr->name, "finishings")) - { - // - // Lookup cupsIPPFinishings attributes for each value... - - - for (ptr = optptr->value; *ptr;) - { - // - // Get the next finishings number... - // - - if (!isdigit(*ptr & 255)) - break; - - if ((j = (int)strtol(ptr, &ptr, 10)) < 3) - break; - - // - // Skip separator as needed... - // - - if (*ptr == ',') - ptr ++; - - // - // Look it up in the PPD file... - // - - sprintf(s, "%d", j); - - if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL) - continue; - - // - // Apply "*Option Choice" settings from the attribute value... - // - - ppd_mark_choices(ppd, attr->value); - } - } - else if (!_ppd_strcasecmp(optptr->name, "APPrinterPreset")) - { - // - // Lookup APPrinterPreset value... - // - - if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL) - { - // - // Apply "*Option Choice" settings from the attribute value... - // - - ppd_mark_choices(ppd, attr->value); - } - } - else if (!_ppd_strcasecmp(optptr->name, "mirror")) - ppd_mark_option(ppd, "MirrorPrint", optptr->value); - else - ppd_mark_option(ppd, optptr->name, optptr->value); - } - - if (print_quality) - { - int pq = atoi(print_quality); // print-quaity value - - if (pq == IPP_QUALITY_DRAFT) - ppd_mark_option(ppd, "cupsPrintQuality", "Draft"); - else if (pq == IPP_QUALITY_HIGH) - ppd_mark_option(ppd, "cupsPrintQuality", "High"); - else - ppd_mark_option(ppd, "cupsPrintQuality", "Normal"); - } - - ppd_debug_marked(ppd, "After..."); - - return (ppdConflicts(ppd) > 0); -} - - -// -// 'ppdFindChoice()' - Return a pointer to an option choice. -// - -ppd_choice_t * // O - Choice pointer or @code 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 || !choice) - return (NULL); - - if (choice[0] == '{' || !_ppd_strncasecmp(choice, "Custom.", 7)) - choice = "Custom"; - - for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) - if (!_ppd_strcasecmp(c->choice, choice)) - return (c); - - return (NULL); -} - - -// -// 'ppdFindMarkedChoice()' - Return the marked choice for the specified option. -// - -ppd_choice_t * // O - Pointer to choice or @code NULL@ -ppdFindMarkedChoice(ppd_file_t *ppd, // I - PPD file - const char *option) // I - Keyword/option name -{ - ppd_choice_t key, // Search key for choice - *marked; // Marked choice - - - DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option)); - - if ((key.option = ppdFindOption(ppd, option)) == NULL) - { - DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL"); - return (NULL); - } - - marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key); - - DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked, - marked ? marked->choice : "NULL")); - - return (marked); -} - - -// -// 'ppdFindOption()' - Return a pointer to the specified option. -// - -ppd_option_t * // O - Pointer to option or @code NULL@ -ppdFindOption(ppd_file_t *ppd, // I - PPD file data - const char *option) // I - Option/Keyword name -{ - // - // Range check input... - // - - if (!ppd || !option) - return (NULL); - - if (ppd->options) - { - // - // Search in the array... - // - - ppd_option_t key; // Option search key - - - strlcpy(key.keyword, option, sizeof(key.keyword)); - - return ((ppd_option_t *)cupsArrayFind(ppd->options, &key)); - } - else - { - // - // Search in each group... - // - - int i, j; // Looping vars - ppd_group_t *group; // Current group - ppd_option_t *optptr; // Current option - - - for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) - for (j = group->num_options, optptr = group->options; - j > 0; - j --, optptr ++) - if (!_ppd_strcasecmp(optptr->keyword, option)) - return (optptr); - - 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_choice_t key, // Search key - *c; // Choice pointer - - - if (!ppd) - return (0); - - if ((key.option = ppdFindOption(ppd, option)) == NULL) - return (0); - - if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL) - return (0); - - return (!strcmp(c->choice, choice)); -} - - -// -// 'ppdMarkDefaults()' - Mark all default options in the PPD file. -// - -void -ppdMarkDefaults(ppd_file_t *ppd) // I - PPD file record -{ - int i; // Looping variables - ppd_group_t *g; // Current group - ppd_choice_t *c; // Current choice - - - if (!ppd) - return; - - // - // Clean out the marked array... - // - - for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); - c; - c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) - { - cupsArrayRemove(ppd->marked, c); - c->marked = 0; - } - - // - // Then repopulate it with the defaults... - // - - for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++) - ppd_defaults(ppd, g); - - // - // Finally, tag any conflicts (API compatibility) once at the end. - // - - ppdConflicts(ppd); -} - - -// -// 'ppdMarkOption()' - Mark an option in a PPD file and return the number of -// conflicts. -// - -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 -{ - DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")", - ppd, option, choice)); - - // - // Range check input... - // - - if (!ppd || !option || !choice) - return (0); - - // - // Mark the option... - // - - ppd_mark_option(ppd, option, choice); - - // - // Return the number of conflicts... - // - - return (ppdConflicts(ppd)); -} - - -// -// 'ppdFirstOption()' - Return the first option in the PPD file. -// -// Options are returned from all groups in ascending alphanumeric order. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_option_t * // O - First option or @code NULL@ -ppdFirstOption(ppd_file_t *ppd) // I - PPD file -{ - if (!ppd) - return (NULL); - else - return ((ppd_option_t *)cupsArrayFirst(ppd->options)); -} - - -// -// 'ppdNextOption()' - Return the next option in the PPD file. -// -// Options are returned from all groups in ascending alphanumeric order. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_option_t * // O - Next option or @code NULL@ -ppdNextOption(ppd_file_t *ppd) // I - PPD file -{ - if (!ppd) - return (NULL); - else - return ((ppd_option_t *)cupsArrayNext(ppd->options)); -} - - -// -// 'ppdParseOptions()' - Parse options from a PPD file. -// -// This function looks for strings of the form: -// -// *option choice ... *optionN choiceN -// property value ... propertyN valueN -// -// It stops when it finds a string that doesn't match this format. -// - -int // O - Number of options -ppdParseOptions( - const char *s, // I - String to parse - int num_options, // I - Number of options - cups_option_t **options, // IO - Options - ppd_parse_t which) // I - What to parse -{ - char option[PPD_MAX_NAME * 2 + 1], // Current option/property - choice[PPD_MAX_NAME], // Current choice/value - *ptr; // Pointer into option or choice - - - if (!s) - return (num_options); - - // - // Read all of the "*Option Choice" and "property value" pairs from the - // string, add them to an options array as we go... - // - - while (*s) - { - // - // Skip leading whitespace... - // - - while (_ppd_isspace(*s)) - s ++; - - // - // Get the option/property name... - // - - ptr = option; - while (*s && !_ppd_isspace(*s) && ptr < (option + sizeof(option) - 1)) - *ptr++ = *s++; - - if (ptr == s || !_ppd_isspace(*s)) - break; - - *ptr = '\0'; - - // - // Get the choice... - // - - while (_ppd_isspace(*s)) - s ++; - - if (!*s) - break; - - ptr = choice; - while (*s && !_ppd_isspace(*s) && ptr < (choice + sizeof(choice) - 1)) - *ptr++ = *s++; - - if (*s && !_ppd_isspace(*s)) - break; - - *ptr = '\0'; - - // - // Add it to the options array... - // - - if (option[0] == '*' && which != PPD_PARSE_PROPERTIES) - num_options = cupsAddOption(option + 1, choice, num_options, options); - else if (option[0] != '*' && which != PPD_PARSE_OPTIONS) - num_options = cupsAddOption(option, choice, num_options, options); - } - - return (num_options); -} - - -#ifdef DEBUG -// -// 'ppd_debug_marked()' - Output the marked array to stdout... -// - -static void -ppd_debug_marked(ppd_file_t *ppd, // I - PPD file data - const char *title) // I - Title for list -{ - ppd_choice_t *c; // Current choice - - - DEBUG_printf(("2ppdMarkOptions: %s", title)); - - for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); - c; - c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) - DEBUG_printf(("2ppdMarkOptions: %s=%s", c->option->keyword, c->choice)); -} -#endif // DEBUG - - -// -// '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 - - - for (i = g->num_options, o = g->options; i > 0; i --, o ++) - if (_ppd_strcasecmp(o->keyword, "PageRegion") != 0) - ppd_mark_option(ppd, o->keyword, o->defchoice); - - for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++) - ppd_defaults(ppd, sg); -} - - -// -// 'ppd_mark_choices()' - Mark one or more option choices from a string. -// - -static void -ppd_mark_choices(ppd_file_t *ppd, // I - PPD file - const char *s) // I - "*Option Choice ..." string -{ - int i, // Looping var - num_options; // Number of options - cups_option_t *options, // Options - *option; // Current option - - - if (!s) - return; - - options = NULL; - num_options = ppdParseOptions(s, 0, &options, 0); - - for (i = num_options, option = options; i > 0; i --, option ++) - ppd_mark_option(ppd, option->name, option->value); - - cupsFreeOptions(num_options, options); -} - - -// -// 'ppd_mark_option()' - Quick mark an option without checking for conflicts. -// - -static void -ppd_mark_option(ppd_file_t *ppd, // I - PPD file - const char *option, // I - Option name - const char *choice) // I - Choice name -{ - int i, j; // Looping vars - ppd_option_t *o; // Option pointer - ppd_choice_t *c, // Choice pointer - *oldc, // Old choice pointer - key; // Search key for choice - struct lconv *loc; // Locale data - - - DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")", - ppd, option, choice)); - - // - // AP_D_InputSlot is the "default input slot" on macOS, and setting - // it clears the regular InputSlot choices... - // - - if (!_ppd_strcasecmp(option, "AP_D_InputSlot")) - { - cupsArraySave(ppd->options); - - if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) - { - key.option = o; - if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) - { - oldc->marked = 0; - cupsArrayRemove(ppd->marked, oldc); - } - } - - cupsArrayRestore(ppd->options); - } - - // - // Check for custom options... - // - - cupsArraySave(ppd->options); - - o = ppdFindOption(ppd, option); - - cupsArrayRestore(ppd->options); - - if (!o) - return; - - loc = localeconv(); - - if (!_ppd_strncasecmp(choice, "Custom.", 7)) - { - // - // Handle a custom option... - // - - if ((c = ppdFindChoice(o, "Custom")) == NULL) - return; - - if (!_ppd_strcasecmp(option, "PageSize")) - { - // - // Handle custom page sizes... - // - - ppdPageSize(ppd, choice); - } - else - { - // - // Handle other custom options... - // - - ppd_coption_t *coption; // Custom option - ppd_cparam_t *cparam; // Custom parameter - char *units; // Custom points units - - - if ((coption = ppdFindCustomOption(ppd, option)) != NULL) - { - if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL) - return; - - switch (cparam->type) - { - case PPD_CUSTOM_UNKNOWN : - break; - - case PPD_CUSTOM_CURVE : - case PPD_CUSTOM_INVCURVE : - case PPD_CUSTOM_REAL : - cparam->current.custom_real = (float)_ppdStrScand(choice + 7, - NULL, loc); - break; - - case PPD_CUSTOM_POINTS : - cparam->current.custom_points = (float)_ppdStrScand(choice + 7, - &units, - loc); - - if (units) - { - if (!_ppd_strcasecmp(units, "cm")) - cparam->current.custom_points *= 72.0f / 2.54f; - else if (!_ppd_strcasecmp(units, "mm")) - cparam->current.custom_points *= 72.0f / 25.4f; - else if (!_ppd_strcasecmp(units, "m")) - cparam->current.custom_points *= 72.0f / 0.0254f; - else if (!_ppd_strcasecmp(units, "in")) - cparam->current.custom_points *= 72.0f; - else if (!_ppd_strcasecmp(units, "ft")) - cparam->current.custom_points *= 12.0f * 72.0f; - } - break; - - case PPD_CUSTOM_INT : - cparam->current.custom_int = atoi(choice + 7); - break; - - case PPD_CUSTOM_PASSCODE : - case PPD_CUSTOM_PASSWORD : - case PPD_CUSTOM_STRING : - if (cparam->current.custom_string) - free(cparam->current.custom_string); - - cparam->current.custom_string = strdup(choice + 7); - break; - } - } - } - - // - // Make sure that we keep the option marked below... - // - - choice = "Custom"; - } - else if (choice[0] == '{') - { - // - // Handle multi-value custom options... - // - - ppd_coption_t *coption; // Custom option - ppd_cparam_t *cparam; // Custom parameter - char *units; // Custom points units - int num_vals; // Number of values - cups_option_t *vals, // Values - *val; // Value - - - if ((c = ppdFindChoice(o, "Custom")) == NULL) - return; - - if ((coption = ppdFindCustomOption(ppd, option)) != NULL) - { - num_vals = cupsParseOptions(choice, 0, &vals); - - for (i = 0, val = vals; i < num_vals; i ++, val ++) - { - if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL) - continue; - - switch (cparam->type) - { - case PPD_CUSTOM_UNKNOWN : - break; - - case PPD_CUSTOM_CURVE : - case PPD_CUSTOM_INVCURVE : - case PPD_CUSTOM_REAL : - cparam->current.custom_real = (float)_ppdStrScand(val->value, - NULL, loc); - break; - - case PPD_CUSTOM_POINTS : - cparam->current.custom_points = (float)_ppdStrScand(val->value, - &units, - loc); - - if (units) - { - if (!_ppd_strcasecmp(units, "cm")) - cparam->current.custom_points *= 72.0f / 2.54f; - else if (!_ppd_strcasecmp(units, "mm")) - cparam->current.custom_points *= 72.0f / 25.4f; - else if (!_ppd_strcasecmp(units, "m")) - cparam->current.custom_points *= 72.0f / 0.0254f; - else if (!_ppd_strcasecmp(units, "in")) - cparam->current.custom_points *= 72.0f; - else if (!_ppd_strcasecmp(units, "ft")) - cparam->current.custom_points *= 12.0f * 72.0f; - } - break; - - case PPD_CUSTOM_INT : - cparam->current.custom_int = atoi(val->value); - break; - - case PPD_CUSTOM_PASSCODE : - case PPD_CUSTOM_PASSWORD : - case PPD_CUSTOM_STRING : - if (cparam->current.custom_string) - free(cparam->current.custom_string); - - cparam->current.custom_string = strdup(val->value); - break; - } - } - - cupsFreeOptions(num_vals, vals); - } - } - else - { - for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) - if (!_ppd_strcasecmp(c->choice, choice)) - break; - - if (!i) - return; - } - - // - // Option found; mark it and then handle unmarking any other options. - // - - if (o->ui != PPD_UI_PICKMANY) - { - // - // Unmark all other choices... - // - - if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL) - { - oldc->marked = 0; - cupsArrayRemove(ppd->marked, oldc); - } - - if (!_ppd_strcasecmp(option, "PageSize") || - !_ppd_strcasecmp(option, "PageRegion")) - { - // - // Mark current page size... - // - - for (j = 0; j < ppd->num_sizes; j ++) - ppd->sizes[j].marked = !_ppd_strcasecmp(ppd->sizes[j].name, - choice); - - // - // Unmark the current PageSize or PageRegion setting, as - // appropriate... - // - - cupsArraySave(ppd->options); - - if (!_ppd_strcasecmp(option, "PageSize")) - { - if ((o = ppdFindOption(ppd, "PageRegion")) != NULL) - { - key.option = o; - if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) - { - oldc->marked = 0; - cupsArrayRemove(ppd->marked, oldc); - } - } - } - else - { - if ((o = ppdFindOption(ppd, "PageSize")) != NULL) - { - key.option = o; - if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) - { - oldc->marked = 0; - cupsArrayRemove(ppd->marked, oldc); - } - } - } - - cupsArrayRestore(ppd->options); - } - else if (!_ppd_strcasecmp(option, "InputSlot")) - { - // - // Unmark ManualFeed option... - // - - cupsArraySave(ppd->options); - - if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL) - { - key.option = o; - if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) - { - oldc->marked = 0; - cupsArrayRemove(ppd->marked, oldc); - } - } - - cupsArrayRestore(ppd->options); - } - else if (!_ppd_strcasecmp(option, "ManualFeed") && - !_ppd_strcasecmp(choice, "True")) - { - // - // Unmark InputSlot option... - // - - cupsArraySave(ppd->options); - - if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) - { - key.option = o; - if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) - { - oldc->marked = 0; - cupsArrayRemove(ppd->marked, oldc); - } - } - - cupsArrayRestore(ppd->options); - } - } - - c->marked = 1; - - cupsArrayAdd(ppd->marked, c); -} diff --git a/ppd/ppd-page.c b/ppd/ppd-page.c deleted file mode 100644 index ba8cf5157..000000000 --- a/ppd/ppd-page.c +++ /dev/null @@ -1,378 +0,0 @@ -// -// Page size functions for libppd. -// -// Copyright 2007-2015 by Apple Inc. -// Copyright 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers... -// - -#include "string-private.h" -#include "debug-internal.h" -#include "ppd.h" - - -// -// 'ppdPageSize()' - Get the page size record for the named 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 - ppd_size_t *size; // Current page size - double w, l; // Width and length of page - char *nameptr; // Pointer into name - struct lconv *loc; // Locale data - ppd_coption_t *coption; // Custom option for page size - ppd_cparam_t *cparam; // Custom option parameter - - - DEBUG_printf(("2ppdPageSize(ppd=%p, name=\"%s\")", ppd, name)); - - if (!ppd) - { - DEBUG_puts("3ppdPageSize: Bad PPD pointer, returning NULL..."); - return (NULL); - } - - if (name) - { - if (!strncmp(name, "Custom.", 7) && ppd->variable_sizes) - { - // - // Find the custom page size... - // - - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - if (!strcmp("Custom", size->name)) - break; - - if (!i) - { - DEBUG_puts("3ppdPageSize: No custom sizes, returning NULL..."); - return (NULL); - } - - // - // Variable size; size name can be one of the following: - // - // Custom.WIDTHxLENGTHin - Size in inches - // Custom.WIDTHxLENGTHft - Size in feet - // Custom.WIDTHxLENGTHcm - Size in centimeters - // Custom.WIDTHxLENGTHmm - Size in millimeters - // Custom.WIDTHxLENGTHm - Size in meters - // Custom.WIDTHxLENGTH[pt] - Size in points - // - - loc = localeconv(); - w = _ppdStrScand(name + 7, &nameptr, loc); - if (!nameptr || *nameptr != 'x') - return (NULL); - - l = _ppdStrScand(nameptr + 1, &nameptr, loc); - if (!nameptr) - return (NULL); - - if (!_ppd_strcasecmp(nameptr, "in")) - { - w *= 72.0; - l *= 72.0; - } - else if (!_ppd_strcasecmp(nameptr, "ft")) - { - w *= 12.0 * 72.0; - l *= 12.0 * 72.0; - } - else if (!_ppd_strcasecmp(nameptr, "mm")) - { - w *= 72.0 / 25.4; - l *= 72.0 / 25.4; - } - else if (!_ppd_strcasecmp(nameptr, "cm")) - { - w *= 72.0 / 2.54; - l *= 72.0 / 2.54; - } - else if (!_ppd_strcasecmp(nameptr, "m")) - { - w *= 72.0 / 0.0254; - l *= 72.0 / 0.0254; - } - - size->width = (float)w; - size->length = (float)l; - size->left = ppd->custom_margins[0]; - size->bottom = ppd->custom_margins[1]; - size->right = (float)(w - ppd->custom_margins[2]); - size->top = (float)(l - ppd->custom_margins[3]); - - // - // Update the custom option records for the page size, too... - // - - if ((coption = ppdFindCustomOption(ppd, "PageSize")) != NULL) - { - if ((cparam = ppdFindCustomParam(coption, "Width")) != NULL) - cparam->current.custom_points = (float)w; - - if ((cparam = ppdFindCustomParam(coption, "Height")) != NULL) - cparam->current.custom_points = (float)l; - } - - // - // Return the page size... - // - - DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size, - size->name, size->width, size->length)); - - return (size); - } - else - { - // - // Lookup by name... - // - - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - if (!_ppd_strcasecmp(name, size->name)) - { - DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size, - size->name, size->width, size->length)); - - return (size); - } - } - } - else - { - // - // Find default... - // - - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - if (size->marked) - { - DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size, - size->name, size->width, size->length)); - - return (size); - } - } - - DEBUG_puts("3ppdPageSize: Size not found, returning NULL"); - - return (NULL); -} - - -// -// 'ppdPageSizeLimits()' - Return the custom page size limits. -// -// This function returns the minimum and maximum custom page sizes and printable -// areas based on the currently-marked (selected) options. -// -// If the specified PPD file does not support custom page sizes, both -// "minimum" and "maximum" are filled with zeroes. -// -// @since CUPS 1.4/macOS 10.6@ -// - -int // O - 1 if custom sizes are supported, 0 otherwise -ppdPageSizeLimits(ppd_file_t *ppd, // I - PPD file record - ppd_size_t *minimum, // O - Minimum custom size - ppd_size_t *maximum) // O - Maximum custom size -{ - ppd_choice_t *qualifier2, // Second media qualifier - *qualifier3; // Third media qualifier - ppd_attr_t *attr; // Attribute - float width, // Min/max width - length; // Min/max length - char spec[PPD_MAX_NAME]; // Selector for min/max - - - // - // Range check input... - // - - if (!ppd || !ppd->variable_sizes || !minimum || !maximum) - { - if (minimum) - memset(minimum, 0, sizeof(ppd_size_t)); - - if (maximum) - memset(maximum, 0, sizeof(ppd_size_t)); - - return (0); - } - - // - // See if we have the cupsMediaQualifier2 and cupsMediaQualifier3 - // attributes... - // - - cupsArraySave(ppd->sorted_attrs); - - if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier2", NULL)) != NULL && - attr->value) - qualifier2 = ppdFindMarkedChoice(ppd, attr->value); - else - qualifier2 = NULL; - - if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier3", NULL)) != NULL && - attr->value) - qualifier3 = ppdFindMarkedChoice(ppd, attr->value); - else - qualifier3 = NULL; - - // - // Figure out the current minimum width and length... - // - - width = ppd->custom_min[0]; - length = ppd->custom_min[1]; - - if (qualifier2) - { - // - // Try getting cupsMinSize... - // - - if (qualifier3) - { - snprintf(spec, sizeof(spec), ".%.19s.%.19s", qualifier2->choice, - qualifier3->choice); - attr = ppdFindAttr(ppd, "cupsMinSize", spec); - } - else - attr = NULL; - - if (!attr) - { - snprintf(spec, sizeof(spec), ".%.38s.", qualifier2->choice); - attr = ppdFindAttr(ppd, "cupsMinSize", spec); - } - - if (!attr && qualifier3) - { - snprintf(spec, sizeof(spec), "..%.38s", qualifier3->choice); - attr = ppdFindAttr(ppd, "cupsMinSize", spec); - } - - if ((attr && attr->value && - sscanf(attr->value, "%f%f", &width, &length) != 2) || !attr) - { - width = ppd->custom_min[0]; - length = ppd->custom_min[1]; - } - } - - minimum->width = width; - minimum->length = length; - minimum->left = ppd->custom_margins[0]; - minimum->bottom = ppd->custom_margins[1]; - minimum->right = width - ppd->custom_margins[2]; - minimum->top = length - ppd->custom_margins[3]; - - // - // Figure out the current maximum width and length... - // - - width = ppd->custom_max[0]; - length = ppd->custom_max[1]; - - if (qualifier2) - { - // - // Try getting cupsMaxSize... - // - - if (qualifier3) - { - snprintf(spec, sizeof(spec), ".%.19s.%.19s", qualifier2->choice, - qualifier3->choice); - attr = ppdFindAttr(ppd, "cupsMaxSize", spec); - } - else - attr = NULL; - - if (!attr) - { - snprintf(spec, sizeof(spec), ".%.38s.", qualifier2->choice); - attr = ppdFindAttr(ppd, "cupsMaxSize", spec); - } - - if (!attr && qualifier3) - { - snprintf(spec, sizeof(spec), "..%.38s", qualifier3->choice); - attr = ppdFindAttr(ppd, "cupsMaxSize", spec); - } - - if (!attr || - (attr->value && sscanf(attr->value, "%f%f", &width, &length) != 2)) - { - width = ppd->custom_max[0]; - length = ppd->custom_max[1]; - } - } - - maximum->width = width; - maximum->length = length; - maximum->left = ppd->custom_margins[0]; - maximum->bottom = ppd->custom_margins[1]; - maximum->right = width - ppd->custom_margins[2]; - maximum->top = length - ppd->custom_margins[3]; - - // - // Return the min and max... - // - - cupsArrayRestore(ppd->sorted_attrs); - - return (1); -} - - -// -// '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); -} diff --git a/ppd/ppd.c b/ppd/ppd.c deleted file mode 100644 index 883c3e77c..000000000 --- a/ppd/ppd.c +++ /dev/null @@ -1,3519 +0,0 @@ -// -// PPD file routines for libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -// -// Include necessary headers. -// - -#include "string-private.h" -#include "language-private.h" -#include "thread-private.h" -#include "ppd.h" -#include "debug-internal.h" - - -// -// Definitions... -// - -#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 - -#define PPD_HASHSIZE 512 // Size of hash - - -// -// Line buffer structure... -// - -typedef struct _ppd_line_s -{ - char *buffer; // Pointer to buffer - size_t bufsize; // Size of the buffer -} _ppd_line_t; - - -// -// Local globals... -// - -static _ppd_threadkey_t ppd_globals_key = _PPD_THREADKEY_INITIALIZER; - // Thread local storage key -#ifdef HAVE_PTHREAD_H -static pthread_once_t ppd_globals_key_once = PTHREAD_ONCE_INIT; - // One-time initialization object -#endif // HAVE_PTHREAD_H - - -// -// Local functions... -// - -static ppd_attr_t *ppd_add_attr(ppd_file_t *ppd, const char *name, - const char *spec, const char *text, - const char *value); -static ppd_choice_t *ppd_add_choice(ppd_option_t *option, const char *name); -static ppd_size_t *ppd_add_size(ppd_file_t *ppd, const char *name); -static int ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b); -static int ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b); -static int ppd_compare_coptions(ppd_coption_t *a, - ppd_coption_t *b); -static int ppd_compare_options(ppd_option_t *a, ppd_option_t *b); -static int ppd_decode(char *string); -static void ppd_free_filters(ppd_file_t *ppd); -static void ppd_free_group(ppd_group_t *group); -static void ppd_free_option(ppd_option_t *option); -static ppd_coption_t *ppd_get_coption(ppd_file_t *ppd, const char *name); -static ppd_cparam_t *ppd_get_cparam(ppd_coption_t *opt, - const char *param, - const char *text); -static ppd_group_t *ppd_get_group(ppd_file_t *ppd, const char *name, - const char *text, ppd_globals_t *pg, - cups_encoding_t encoding); -static ppd_option_t *ppd_get_option(ppd_group_t *group, const char *name); -static ppd_globals_t *ppd_globals_alloc(void); -#if defined(HAVE_PTHREAD_H) || defined(_WIN32) -static void ppd_globals_free(ppd_globals_t *g); -#endif // HAVE_PTHREAD_H || _WIN32 -#ifdef HAVE_PTHREAD_H -static void ppd_globals_init(void); -#endif // HAVE_PTHREAD_H -static int ppd_hash_option(ppd_option_t *option); -static int ppd_read(cups_file_t *fp, _ppd_line_t *line, - char *keyword, char *option, char *text, - char **string, int ignoreblank, - ppd_globals_t *pg); -static int ppd_update_filters(ppd_file_t *ppd, - ppd_globals_t *pg); - - -// -// 'ppdClose()' - Free all memory used by the PPD file. -// - -void -ppdClose(ppd_file_t *ppd) // I - PPD file record -{ - int i; // Looping var - ppd_group_t *group; // Current group - char **font; // Current font - ppd_attr_t **attr; // Current attribute - ppd_coption_t *coption; // Current custom option - ppd_cparam_t *cparam; // Current custom parameter - - - // - // Range check arguments... - // - - if (!ppd) - return; - - // - // Free all strings at the top level... - // - - free(ppd->lang_encoding); - free(ppd->nickname); - free(ppd->patches); - free(ppd->emulations); - free(ppd->jcl_begin); - free(ppd->jcl_end); - free(ppd->jcl_ps); -#if HAVE_CUPS_3_X - free(ppd->jcl_pdf); -#endif - - // - // 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); - - free(ppd->groups); - } - - cupsArrayDelete(ppd->options); - cupsArrayDelete(ppd->marked); - - // - // Free any page sizes... - // - - if (ppd->num_sizes > 0) - free(ppd->sizes); - - // - // Free any constraints... - // - - if (ppd->num_consts > 0) - free(ppd->consts); - - // - // Free any filters... - // - - ppd_free_filters(ppd); - - // - // Free any fonts... - // - - if (ppd->num_fonts > 0) - { - for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++) - free(*font); - - free(ppd->fonts); - } - - // - // Free any profiles... - // - - if (ppd->num_profiles > 0) - free(ppd->profiles); - - // - // Free any attributes... - // - - if (ppd->num_attrs > 0) - { - for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++) - { - free((*attr)->value); - free(*attr); - } - - free(ppd->attrs); - } - - cupsArrayDelete(ppd->sorted_attrs); - - // - // Free custom options... - // - - for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions); - coption; - coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions)) - { - for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) - { - switch (cparam->type) - { - case PPD_CUSTOM_PASSCODE : - case PPD_CUSTOM_PASSWORD : - case PPD_CUSTOM_STRING : - free(cparam->current.custom_string); - break; - - default : - break; - } - - free(cparam); - } - - cupsArrayDelete(coption->params); - - free(coption); - } - - cupsArrayDelete(ppd->coptions); - - // - // Free constraints... - // - - if (ppd->cups_uiconstraints) - { - ppd_cups_uiconsts_t *consts; // Current constraints - - - for (consts = (ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints); - consts; - consts = (ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints)) - { - free(consts->constraints); - free(consts); - } - - cupsArrayDelete(ppd->cups_uiconstraints); - } - - // - // Free any PPD cache/mapping data... - // - - if (ppd->cache) - ppdCacheDestroy(ppd->cache); - - // - // Free the whole record... - // - - free(ppd); -} - - -// -// 'ppdErrorString()' - Returns the text associated with a status. -// -// @since CUPS 1.1.19/macOS 10.3@ -// - -const char * // O - Status string -ppdErrorString(ppd_status_t status) // I - PPD status -{ - static const char * const messages[] =// Status messages - { - _("OK"), - _("Unable to open PPD file"), - _("NULL PPD file pointer"), - _("Memory allocation error"), - _("Missing PPD-Adobe-4.x header"), - _("Missing value string"), - _("Internal error"), - _("Bad OpenGroup"), - _("OpenGroup without a CloseGroup first"), - _("Bad OpenUI/JCLOpenUI"), - _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"), - _("Bad OrderDependency"), - _("Bad UIConstraints"), - _("Missing asterisk in column 1"), - _("Line longer than the maximum allowed (255 characters)"), - _("Illegal control character"), - _("Illegal main keyword string"), - _("Illegal option keyword string"), - _("Illegal translation string"), - _("Illegal whitespace character"), - _("Bad custom parameter"), - _("Missing option keyword"), - _("Bad value string"), - _("Missing CloseGroup"), - _("Bad CloseUI/JCLCloseUI"), - _("Missing CloseUI/JCLCloseUI") - }; - - - if (status < PPD_OK || status >= PPD_MAX_STATUS) - return (_ppdLangString(cupsLangDefault(), _("Unknown"))); - else - return (_ppdLangString(cupsLangDefault(), messages[status])); -} - - -// -// 'ppdGetEncoding()' - Get the CUPS encoding value for the given -// LanguageEncoding. - - -cups_encoding_t // O - CUPS encoding value -ppdGetEncoding(const char *name) // I - LanguageEncoding string -{ - if (!_ppd_strcasecmp(name, "ISOLatin1")) - return (CUPS_ISO8859_1); - else if (!_ppd_strcasecmp(name, "ISOLatin2")) - return (CUPS_ISO8859_2); - else if (!_ppd_strcasecmp(name, "ISOLatin5")) - return (CUPS_ISO8859_5); - else if (!_ppd_strcasecmp(name, "JIS83-RKSJ")) - return (CUPS_JIS_X0213); - else if (!_ppd_strcasecmp(name, "MacStandard")) - return (CUPS_MAC_ROMAN); - else if (!_ppd_strcasecmp(name, "WindowsANSI")) - return (CUPS_WINDOWS_1252); - else - return (CUPS_UTF8); -} - - -// -// 'ppdGlobals()' - Return a pointer to thread local storage -// - -ppd_globals_t * // O - Pointer to global data -ppdGlobals(void) -{ - ppd_globals_t *pg; // Pointer to global data - - -#ifdef HAVE_PTHREAD_H - // - // Initialize the global data exactly once... - // - - pthread_once(&ppd_globals_key_once, ppd_globals_init); -#endif // HAVE_PTHREAD_H - - // - // See if we have allocated the data yet... - // - - if ((pg = (ppd_globals_t *)_ppdThreadGetData(ppd_globals_key)) == NULL) - { - // - // No, allocate memory as set the pointer for the key... - // - - if ((pg = ppd_globals_alloc()) != NULL) - _ppdThreadSetData(ppd_globals_key, pg); - } - - // - // Return the pointer to the data... - // - - return (pg); -} - - -// -// 'ppdLastError()' - Return the status from the last ppdOpen*(). -// -// @since CUPS 1.1.19/macOS 10.3@ -// - -ppd_status_t // O - Status code -ppdLastError(int *line) // O - Line number -{ - ppd_globals_t *pg = ppdGlobals(); - // Global data - - - if (line) - *line = pg->ppd_line; - - return (pg->ppd_status); -} - - -// -// 'ppdOpenWithLocalization()' - Read a PPD file into memory. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_file_t * // O - PPD file record or @code NULL@ if the PPD file could not be opened. -ppdOpenWithLocalization( - cups_file_t *fp, // I - File to read from - ppd_localization_t localization) // I - Localization to load -{ - int i, j, k; // Looping vars - _ppd_line_t line; // Line buffer - 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[PPD_MAX_NAME], - // Keyword from file - name[PPD_MAX_NAME], - // Option from file - text[PPD_MAX_LINE], - // Human-readable text from file - *string, // Code/text from file - *sptr, // Pointer into string - *temp, // Temporary string pointer - **tempfonts; // Temporary fonts pointer - 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 - struct lconv *loc; // Locale data - int ui_keyword; // Is this line a UI keyword? - cups_lang_t *lang; // Language data - cups_encoding_t encoding; // Encoding of PPD file - ppd_globals_t *pg = ppdGlobals(); - // Global data - char custom_name[PPD_MAX_NAME]; - // CustomFoo attribute name - ppd_attr_t *custom_attr; // CustomFoo attribute - char ll[7], // Base language + '.' - ll_CC[7]; // Language w/country + '.' - size_t ll_len = 0, // Base language length - ll_CC_len = 0; // Language w/country length - static const char * const ui_keywords[] = - { -#ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST - // - // Adobe defines some 41 keywords as "UI", meaning that they are - // user interface elements and that they should be treated as such - // even if the PPD creator doesn't use Open/CloseUI around them. - // - // Since this can cause previously invisible options to appear and - // confuse users, the default is to only treat the PageSize and - // PageRegion keywords this way. - // - - // Boolean keywords - "BlackSubstitution", - "Booklet", - "Collate", - "ManualFeed", - "MirrorPrint", - "NegativePrint", - "Sorter", - "TraySwitch", - - // PickOne keywords - "AdvanceMedia", - "BindColor", - "BindEdge", - "BindType", - "BindWhen", - "BitsPerPixel", - "ColorModel", - "CutMedia", - "Duplex", - "FoldType", - "FoldWhen", - "InputSlot", - "JCLFrameBufferSize", - "JCLResolution", - "Jog", - "MediaColor", - "MediaType", - "MediaWeight", - "OutputBin", - "OutputMode", - "OutputOrder", - "PageRegion", - "PageSize", - "Resolution", - "Separations", - "Signature", - "Slipsheet", - "Smoothing", - "StapleLocation", - "StapleOrientation", - "StapleWhen", - "StapleX", - "StapleY" -#else // !CUPS_USE_FULL_UI_KEYWORDS_LIST - "PageRegion", - "PageSize" -#endif // CUPS_USE_FULL_UI_KEYWORDS_LIST - }; - static const char * const color_keywords[] = // Keywords associated with - // color profiles - { - ".cupsICCProfile", - ".ColorModel", - }; - - - DEBUG_printf(("ppdOpenWithLocalization(fp=%p)", fp)); - - // - // Default to "OK" status... - // - - pg->ppd_status = PPD_OK; - pg->ppd_line = 0; - - // - // Range check input... - // - - if (fp == NULL) - { - pg->ppd_status = PPD_NULL_FILE; - return (NULL); - } - - // - // If only loading a single localization set up the strings to match... - // - - if (localization == PPD_LOCALIZATION_DEFAULT) - { - if ((lang = cupsLangDefault()) == NULL) - return (NULL); - - snprintf(ll_CC, sizeof(ll_CC), "%.5s.", lang->language); - - // - // - // - // - // Need to use a different base language for some locales... - // - - if (!strcmp(lang->language, "zh_HK")) - { // Traditional Chinese + variants - strlcpy(ll_CC, "zh_TW.", sizeof(ll_CC)); - strlcpy(ll, "zh_", sizeof(ll)); - } - else if (!strncmp(lang->language, "zh", 2)) - strlcpy(ll, "zh_", sizeof(ll)); // Any Chinese variant - else if (!strncmp(lang->language, "jp", 2)) - { // Any Japanese variant - strlcpy(ll_CC, "ja", sizeof(ll_CC)); - strlcpy(ll, "jp", sizeof(ll)); - } - else if (!strncmp(lang->language, "nb", 2) || - !strncmp(lang->language, "no", 2)) - { // Any Norwegian variant - strlcpy(ll_CC, "nb", sizeof(ll_CC)); - strlcpy(ll, "no", sizeof(ll)); - } - else - snprintf(ll, sizeof(ll), "%2.2s.", lang->language); - - ll_CC_len = strlen(ll_CC); - ll_len = strlen(ll); - - DEBUG_printf(("2ppdOpenWithLocalization: Loading localizations matching \"%s\" and \"%s\"", - ll_CC, ll)); - } - - // - // Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'... - // - - line.buffer = NULL; - line.bufsize = 0; - - mask = ppd_read(fp, &line, keyword, name, text, &string, 0, pg); - - DEBUG_printf(("2ppdOpenWithLocalization: mask=%x, keyword=\"%s\"...", - mask, keyword)); - - if (mask == 0 || - strcmp(keyword, "PPD-Adobe") || - string == NULL || string[0] != '4') - { - // - // Either this is not a PPD file, or it is not a 4.x PPD file. - // - - if (pg->ppd_status == PPD_OK) - pg->ppd_status = PPD_MISSING_PPDADOBE4; - - free(string); - free(line.buffer); - - return (NULL); - } - - DEBUG_printf(("2ppdOpenWithLocalization: keyword=%s, string=%p", - keyword, string)); - - // - // Allocate memory for the PPD file record... - // - - if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - free(string); - free(line.buffer); - - return (NULL); - } - - free(string); - string = NULL; - - ppd->language_level = 2; - ppd->color_device = 0; - ppd->colorspace = PPD_CS_N; - ppd->landscape = -90; - ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions, - NULL); - - // - // Read lines from the PPD file and add them to the file record... - // - - group = NULL; - subgroup = NULL; - option = NULL; - choice = NULL; - ui_keyword = 0; - encoding = CUPS_ISO8859_1; - loc = localeconv(); - - while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, pg)) != 0) - { - DEBUG_printf(("2ppdOpenWithLocalization: mask=%x, keyword=\"%s\", name=\"%s\", " - "text=\"%s\", string=%d chars...", mask, keyword, name, text, - string ? (int)strlen(string) : 0)); - - if (strncmp(keyword, "Default", 7) && !string && - pg->ppd_conform != PPD_CONFORM_RELAXED) - { - // - // Need a string value! - // - - pg->ppd_status = PPD_MISSING_VALUE; - - goto error; - } - else if (!string) - continue; - - // - // Certain main keywords (as defined by the PPD spec) may be used - // without the usual OpenUI/CloseUI stuff. Presumably this is just - // so that Adobe wouldn't completely break compatibility with PPD - // files prior to v4.0 of the spec, but it is hopelessly - // inconsistent... Catch these main keywords and automatically - // create the corresponding option, as needed... - // - - if (ui_keyword) - { - // - // Previous line was a UI keyword... - // - - option = NULL; - ui_keyword = 0; - } - - // - // If we are filtering out keyword localizations, see if this line needs to - // be used... - // - - if (localization != PPD_LOCALIZATION_ALL && - (temp = strchr(keyword, '.')) != NULL && - ((temp - keyword) == 2 || (temp - keyword) == 5) && - _ppd_isalpha(keyword[0]) && - _ppd_isalpha(keyword[1]) && - (keyword[2] == '.' || - (keyword[2] == '_' && _ppd_isalpha(keyword[3]) && - _ppd_isalpha(keyword[4]) && keyword[5] == '.'))) - { - if (localization == PPD_LOCALIZATION_NONE || - (localization == PPD_LOCALIZATION_DEFAULT && - strncmp(ll_CC, keyword, ll_CC_len) && - strncmp(ll, keyword, ll_len))) - { - DEBUG_printf(("2ppdOpenWithLocalization: Ignoring localization: \"%s\"\n", - keyword)); - free(string); - string = NULL; - continue; - } - else if (localization == PPD_LOCALIZATION_ICC_PROFILES) - { - // - // Only load localizations for the color profile related keywords... - // - - for (i = 0; - i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0])); - i ++) - { - if (!_ppd_strcasecmp(temp, color_keywords[i])) - break; - } - - if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0]))) - { - DEBUG_printf(("2ppdOpenWithLocalization: Ignoring localization: \"%s\"\n", keyword)); - free(string); - string = NULL; - continue; - } - } - } - - if (option == NULL && - (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) == - (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) - { - for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++) - if (!strcmp(keyword, ui_keywords[i])) - break; - - if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0]))) - { - // - // Create the option in the appropriate group... - // - - ui_keyword = 1; - - DEBUG_printf(("2ppdOpenWithLocalization: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!", - keyword)); - - if (!group) - { - if ((group = ppd_get_group(ppd, "General", _("General"), pg, - encoding)) == NULL) - goto error; - - DEBUG_printf(("2ppdOpenWithLocalization: Adding to group %s...", - group->text)); - option = ppd_get_option(group, keyword); - group = NULL; - } - else - option = ppd_get_option(group, keyword); - - if (option == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - // - // Now fill in the initial information for the option... - // - - if (!strncmp(keyword, "JCL", 3)) - option->section = PPD_ORDER_JCL; - else - option->section = PPD_ORDER_ANY; - - option->order = 10.0f; - - if (i < 8) - option->ui = PPD_UI_BOOLEAN; - else - option->ui = PPD_UI_PICKONE; - - for (j = 0; j < ppd->num_attrs; j ++) - if (!strncmp(ppd->attrs[j]->name, "Default", 7) && - !strcmp(ppd->attrs[j]->name + 7, keyword) && - ppd->attrs[j]->value) - { - DEBUG_printf(("2ppdOpenWithLocalization: Setting Default%s to %s via attribute...", - option->keyword, ppd->attrs[j]->value)); - strlcpy(option->defchoice, ppd->attrs[j]->value, - sizeof(option->defchoice)); - break; - } - - if (!strcmp(keyword, "PageSize")) - strlcpy(option->text, _("Media Size"), sizeof(option->text)); - else if (!strcmp(keyword, "MediaType")) - strlcpy(option->text, _("Media Type"), sizeof(option->text)); - else if (!strcmp(keyword, "InputSlot")) - strlcpy(option->text, _("Media Source"), sizeof(option->text)); - else if (!strcmp(keyword, "ColorModel")) - strlcpy(option->text, _("Output Mode"), sizeof(option->text)); - else if (!strcmp(keyword, "Resolution")) - strlcpy(option->text, _("Resolution"), sizeof(option->text)); - else - strlcpy(option->text, keyword, sizeof(option->text)); - } - } - - if (!strcmp(keyword, "LanguageLevel")) - ppd->language_level = atoi(string); - else if (!strcmp(keyword, "LanguageEncoding")) - { - // - // Say all PPD files are UTF-8, since we convert to UTF-8... - // - - ppd->lang_encoding = strdup("UTF-8"); - encoding = ppdGetEncoding(string); - } - else if (!strcmp(keyword, "LanguageVersion")) - ppd->lang_version = string; - else if (!strcmp(keyword, "Manufacturer")) - ppd->manufacturer = string; - else if (!strcmp(keyword, "ModelName")) - ppd->modelname = string; - else if (!strcmp(keyword, "Protocols")) - ppd->protocols = string; - else if (!strcmp(keyword, "PCFileName")) - ppd->pcfilename = string; - else if (!strcmp(keyword, "NickName")) - { - if (encoding != CUPS_UTF8) - { - cups_utf8_t utf8[256]; // UTF-8 version of NickName - - - cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding); - ppd->nickname = strdup((char *)utf8); - } - else - ppd->nickname = strdup(string); - } - else if (!strcmp(keyword, "Product")) - ppd->product = string; - else if (!strcmp(keyword, "ShortNickName")) - ppd->shortnickname = string; - else if (!strcmp(keyword, "TTRasterizer")) - ppd->ttrasterizer = string; - else if (!strcmp(keyword, "JCLBegin")) - { - ppd->jcl_begin = strdup(string); - ppd_decode(ppd->jcl_begin); // Decode quoted string - } - else if (!strcmp(keyword, "JCLEnd")) - { - ppd->jcl_end = strdup(string); - ppd_decode(ppd->jcl_end); // Decode quoted string - } - else if (!strcmp(keyword, "JCLToPSInterpreter")) - { - ppd->jcl_ps = strdup(string); - ppd_decode(ppd->jcl_ps); // Decode quoted string - } -#if HAVE_CUPS_3_X - else if (!strcmp(keyword, "JCLToPDFInterpreter")) - { - ppd->jcl_pdf = strdup(string); - ppd_decode(ppd->jcl_pdf); // Decode quoted string - } -#endif - else if (!strcmp(keyword, "AccurateScreensSupport")) - ppd->accurate_screens = !strcasecmp(string, "True"); - else if (!strcmp(keyword, "ColorDevice")) - ppd->color_device = !strcasecmp(string, "True"); - else if (!strcmp(keyword, "ContoneOnly")) - ppd->contone_only = !strcasecmp(string, "True"); - else if (!strcmp(keyword, "cupsFlipDuplex")) - ppd->flip_duplex = !strcasecmp(string, "True"); - else if (!strcmp(keyword, "cupsManualCopies")) - ppd->manual_copies = !strcasecmp(string, "True"); - else if (!strcmp(keyword, "cupsModelNumber")) - ppd->model_number = atoi(string); - else if (!strcmp(keyword, "cupsColorProfile")) - { - if (ppd->num_profiles == 0) - profile = malloc(sizeof(ppd_profile_t)); - else - profile = realloc(ppd->profiles, sizeof(ppd_profile_t) * - (size_t)(ppd->num_profiles + 1)); - - if (!profile) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - ppd->profiles = profile; - profile += ppd->num_profiles; - ppd->num_profiles ++; - - memset(profile, 0, sizeof(ppd_profile_t)); - strlcpy(profile->resolution, name, sizeof(profile->resolution)); - strlcpy(profile->media_type, text, sizeof(profile->media_type)); - - profile->density = (float)_ppdStrScand(string, &sptr, loc); - profile->gamma = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[0][0] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[0][1] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[0][2] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[1][0] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[1][1] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[1][2] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[2][0] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[2][1] = (float)_ppdStrScand(sptr, &sptr, loc); - profile->matrix[2][2] = (float)_ppdStrScand(sptr, &sptr, loc); - } - else if (!strcmp(keyword, "cupsFilter")) - { - if (ppd->num_filters == 0) - filter = malloc(sizeof(char *)); - else - filter = realloc(ppd->filters, sizeof(char *) * - (size_t)(ppd->num_filters + 1)); - - if (filter == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - ppd->filters = filter; - filter += ppd->num_filters; - ppd->num_filters ++; - - // - // Make a copy of the filter string... - // - - *filter = strdup(string); - } - else if (!strcmp(keyword, "Throughput")) - ppd->throughput = atoi(string); - else if (!strcmp(keyword, "Font")) - { - // - // Add this font to the list of available fonts... - // - - if (ppd->num_fonts == 0) - tempfonts = (char **)malloc(sizeof(char *)); - else - tempfonts = (char **)realloc(ppd->fonts, sizeof(char *) * - (size_t)(ppd->num_fonts + 1)); - - if (tempfonts == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - ppd->fonts = tempfonts; - ppd->fonts[ppd->num_fonts] = strdup(name); - ppd->num_fonts ++; - } - else if (!strncmp(keyword, "ParamCustom", 11)) - { - ppd_coption_t *coption; // Custom option - ppd_cparam_t *cparam; // Custom parameter - int corder; // Order number - char ctype[33], // Data type - cminimum[65], // Minimum value - cmaximum[65]; // Maximum value - - - // - // Get the custom option and parameter... - // - - if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - if ((cparam = ppd_get_cparam(coption, name, text)) == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - if (cparam->type != PPD_CUSTOM_UNKNOWN) - { - pg->ppd_status = PPD_BAD_CUSTOM_PARAM; - - goto error; - } - - // - // Get the parameter data... - // - - if (!string || - sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum, - cmaximum) != 4) - { - pg->ppd_status = PPD_BAD_CUSTOM_PARAM; - - goto error; - } - - cparam->order = corder; - - if (!strcmp(ctype, "curve")) - { - cparam->type = PPD_CUSTOM_CURVE; - cparam->minimum.custom_curve = (float)_ppdStrScand(cminimum, NULL, loc); - cparam->maximum.custom_curve = (float)_ppdStrScand(cmaximum, NULL, loc); - } - else if (!strcmp(ctype, "int")) - { - cparam->type = PPD_CUSTOM_INT; - cparam->minimum.custom_int = atoi(cminimum); - cparam->maximum.custom_int = atoi(cmaximum); - } - else if (!strcmp(ctype, "invcurve")) - { - cparam->type = PPD_CUSTOM_INVCURVE; - cparam->minimum.custom_invcurve = (float)_ppdStrScand(cminimum, NULL, - loc); - cparam->maximum.custom_invcurve = (float)_ppdStrScand(cmaximum, NULL, - loc); - } - else if (!strcmp(ctype, "passcode")) - { - cparam->type = PPD_CUSTOM_PASSCODE; - cparam->minimum.custom_passcode = atoi(cminimum); - cparam->maximum.custom_passcode = atoi(cmaximum); - } - else if (!strcmp(ctype, "password")) - { - cparam->type = PPD_CUSTOM_PASSWORD; - cparam->minimum.custom_password = atoi(cminimum); - cparam->maximum.custom_password = atoi(cmaximum); - } - else if (!strcmp(ctype, "points")) - { - cparam->type = PPD_CUSTOM_POINTS; - cparam->minimum.custom_points = (float)_ppdStrScand(cminimum, NULL, - loc); - cparam->maximum.custom_points = (float)_ppdStrScand(cmaximum, NULL, - loc); - } - else if (!strcmp(ctype, "real")) - { - cparam->type = PPD_CUSTOM_REAL; - cparam->minimum.custom_real = (float)_ppdStrScand(cminimum, NULL, loc); - cparam->maximum.custom_real = (float)_ppdStrScand(cmaximum, NULL, loc); - } - else if (!strcmp(ctype, "string")) - { - cparam->type = PPD_CUSTOM_STRING; - cparam->minimum.custom_string = atoi(cminimum); - cparam->maximum.custom_string = atoi(cmaximum); - } - else - { - pg->ppd_status = PPD_BAD_CUSTOM_PARAM; - - goto error; - } - - // - // Now special-case for CustomPageSize... - // - - if (!strcmp(coption->keyword, "PageSize")) - { - if (!strcmp(name, "Width")) - { - ppd->custom_min[0] = cparam->minimum.custom_points; - ppd->custom_max[0] = cparam->maximum.custom_points; - } - else if (!strcmp(name, "Height")) - { - ppd->custom_min[1] = cparam->minimum.custom_points; - ppd->custom_max[1] = cparam->maximum.custom_points; - } - } - } - else if (!strcmp(keyword, "HWMargins")) - { - for (i = 0, sptr = string; i < 4; i ++) - ppd->custom_margins[i] = (float)_ppdStrScand(sptr, &sptr, loc); - } - else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option) - { - ppd_option_t *custom_option; // Custom option - - DEBUG_puts("2ppdOpenWithLocalization: Processing Custom option..."); - - // - // Get the option and custom option... - // - - if (!ppd_get_coption(ppd, keyword + 6)) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - if (option && !_ppd_strcasecmp(option->keyword, keyword + 6)) - custom_option = option; - else - custom_option = ppdFindOption(ppd, keyword + 6); - - if (custom_option) - { - // - // Add the "custom" option... - // - - if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL) - if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL) - { - DEBUG_puts("1ppdOpenWithLocalization: Unable to add Custom choice!"); - - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - strlcpy(choice->text, text[0] ? text : _("Custom"), - sizeof(choice->text)); - - choice->code = strdup(string); - - if (custom_option->section == PPD_ORDER_JCL) - ppd_decode(choice->code); - } - - // - // Now process custom page sizes specially... - // - - if (!strcmp(keyword, "CustomPageSize")) - { - // - // Add a "Custom" page size entry... - // - - ppd->variable_sizes = 1; - - ppd_add_size(ppd, "Custom"); - - if (option && !_ppd_strcasecmp(option->keyword, "PageRegion")) - custom_option = option; - else - custom_option = ppdFindOption(ppd, "PageRegion"); - - if (custom_option) - { - if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL) - if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL) - { - DEBUG_puts("1ppdOpenWithLocalization: Unable to add Custom choice!"); - - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - strlcpy(choice->text, text[0] ? text : _("Custom"), - sizeof(choice->text)); - } - } - } - else if (!strcmp(keyword, "LandscapeOrientation")) - { - if (!strcmp(string, "Minus90")) - ppd->landscape = -90; - else if (!strcmp(string, "Plus90")) - ppd->landscape = 90; - } - else if (!strcmp(keyword, "Emulators") && string && - ppd->num_emulations == 0) - { - // - // Issue #5562: Samsung printer drivers incorrectly use Emulators keyword - // to configure themselves - // - // The Emulators keyword was loaded but never used by anything - // in CUPS, and has no valid purpose in CUPS. The old code was - // removed due to a memory leak (Issue #5475), so the following - // (new) code supports a single name for the Emulators keyword, - // allowing these drivers to work until we remove PPD and driver - // support entirely in a future version of CUPS. - // - - ppd->num_emulations = 1; - ppd->emulations = calloc(1, sizeof(ppd_emul_t)); - - strlcpy(ppd->emulations[0].name, string, sizeof(ppd->emulations[0].name)); - } - else if (!strcmp(keyword, "JobPatchFile")) - { - // - // CUPS STR #3421: Check for "*JobPatchFile: int: string" - // - - if (isdigit(*string & 255)) - { - for (sptr = string + 1; isdigit(*sptr & 255); sptr ++); - - if (*sptr == ':') - { - // - // Found "*JobPatchFile: int: string"... - // - - pg->ppd_status = PPD_BAD_VALUE; - - goto error; - } - } - - if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT) - { - // - // Found "*JobPatchFile: string"... - // - - pg->ppd_status = PPD_MISSING_OPTION_KEYWORD; - - goto error; - } - - if (ppd->patches == NULL) - ppd->patches = strdup(string); - else - { - temp = realloc(ppd->patches, strlen(ppd->patches) + - strlen(string) + 1); - if (temp == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - ppd->patches = temp; - - memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1); - } - } - else if (!strcmp(keyword, "OpenUI")) - { - // - // Don't allow nesting of options... - // - - if (option && pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_NESTED_OPEN_UI; - - goto error; - } - - // - // Add an option record to the current sub-group, group, or file... - // - - DEBUG_printf(("2ppdOpenWithLocalization: name=\"%s\" (%d)", - name, (int)strlen(name))); - - if (name[0] == '*') - _ppd_strcpy(name, name + 1); // Eliminate leading asterisk - - for (i = (int)strlen(name) - 1; i > 0 && _ppd_isspace(name[i]); i --) - name[i] = '\0'; // Eliminate trailing spaces - - DEBUG_printf(("2ppdOpenWithLocalization: OpenUI of %s in group %s...", - name, group ? group->text : "(null)")); - - if (subgroup != NULL) - option = ppd_get_option(subgroup, name); - else if (group == NULL) - { - if ((group = ppd_get_group(ppd, "General", _("General"), pg, - encoding)) == NULL) - goto error; - - DEBUG_printf(("2ppdOpenWithLocalization: Adding to group %s...", - group->text)); - option = ppd_get_option(group, name); - group = NULL; - } - else - option = ppd_get_option(group, name); - - if (option == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - // - // Now fill in the initial information for the option... - // - - if (string && !strcmp(string, "PickMany")) - option->ui = PPD_UI_PICKMANY; - else if (string && !strcmp(string, "Boolean")) - option->ui = PPD_UI_BOOLEAN; - else if (string && !strcmp(string, "PickOne")) - option->ui = PPD_UI_PICKONE; - else if (pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_OPEN_UI; - - goto error; - } - else - option->ui = PPD_UI_PICKONE; - - for (j = 0; j < ppd->num_attrs; j ++) - if (!strncmp(ppd->attrs[j]->name, "Default", 7) && - !strcmp(ppd->attrs[j]->name + 7, name) && - ppd->attrs[j]->value) - { - DEBUG_printf(("2ppdOpenWithLocalization: Setting Default%s to %s via attribute...", - option->keyword, ppd->attrs[j]->value)); - strlcpy(option->defchoice, ppd->attrs[j]->value, - sizeof(option->defchoice)); - break; - } - - if (text[0]) - cupsCharsetToUTF8((cups_utf8_t *)option->text, text, - sizeof(option->text), encoding); - else - { - if (!strcmp(name, "PageSize")) - strlcpy(option->text, _("Media Size"), sizeof(option->text)); - else if (!strcmp(name, "MediaType")) - strlcpy(option->text, _("Media Type"), sizeof(option->text)); - else if (!strcmp(name, "InputSlot")) - strlcpy(option->text, _("Media Source"), sizeof(option->text)); - else if (!strcmp(name, "ColorModel")) - strlcpy(option->text, _("Output Mode"), sizeof(option->text)); - else if (!strcmp(name, "Resolution")) - strlcpy(option->text, _("Resolution"), sizeof(option->text)); - else - strlcpy(option->text, name, sizeof(option->text)); - } - - option->section = PPD_ORDER_ANY; - - free(string); - string = NULL; - - // - // Add a custom option choice if we have already seen a CustomFoo - // attribute... - // - - if (!_ppd_strcasecmp(name, "PageRegion")) - strlcpy(custom_name, "CustomPageSize", sizeof(custom_name)); - else - snprintf(custom_name, sizeof(custom_name), "Custom%.34s", name); - - if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL) - { - if ((choice = ppdFindChoice(option, "Custom")) == NULL) - if ((choice = ppd_add_choice(option, "Custom")) == NULL) - { - DEBUG_puts("1ppdOpenWithLocalization: Unable to add Custom choice!"); - - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - strlcpy(choice->text, - custom_attr->text[0] ? custom_attr->text : _("Custom"), - sizeof(choice->text)); - choice->code = strdup(custom_attr->value); - } - } - else if (!strcmp(keyword, "JCLOpenUI")) - { - // - // Don't allow nesting of options... - // - - if (option && pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_NESTED_OPEN_UI; - - goto error; - } - - // - // Find the JCL group, and add if needed... - // - - group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding); - - if (group == NULL) - goto error; - - // - // Add an option record to the current JCLs... - // - - if (name[0] == '*') - _ppd_strcpy(name, name + 1); - - option = ppd_get_option(group, name); - - if (option == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - // - // Now fill in the initial information for the option... - // - - if (string && !strcmp(string, "PickMany")) - option->ui = PPD_UI_PICKMANY; - else if (string && !strcmp(string, "Boolean")) - option->ui = PPD_UI_BOOLEAN; - else if (string && !strcmp(string, "PickOne")) - option->ui = PPD_UI_PICKONE; - else - { - pg->ppd_status = PPD_BAD_OPEN_UI; - - goto error; - } - - for (j = 0; j < ppd->num_attrs; j ++) - if (!strncmp(ppd->attrs[j]->name, "Default", 7) && - !strcmp(ppd->attrs[j]->name + 7, name) && - ppd->attrs[j]->value) - { - DEBUG_printf(("2ppdOpenWithLocalization: Setting Default%s to %s via attribute...", - option->keyword, ppd->attrs[j]->value)); - strlcpy(option->defchoice, ppd->attrs[j]->value, - sizeof(option->defchoice)); - break; - } - - if (text[0]) - cupsCharsetToUTF8((cups_utf8_t *)option->text, text, - sizeof(option->text), encoding); - else - strlcpy(option->text, name, sizeof(option->text)); - - option->section = PPD_ORDER_JCL; - group = NULL; - - free(string); - string = NULL; - - // - // Add a custom option choice if we have already seen a CustomFoo - // attribute... - // - - snprintf(custom_name, sizeof(custom_name), "Custom%.34s", name); - - if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL) - { - if ((choice = ppd_add_choice(option, "Custom")) == NULL) - { - DEBUG_puts("1ppdOpenWithLocalization: Unable to add Custom choice!"); - - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - strlcpy(choice->text, - custom_attr->text[0] ? custom_attr->text : _("Custom"), - sizeof(choice->text)); - choice->code = strdup(custom_attr->value); - } - } - else if (!strcmp(keyword, "CloseUI")) - { - if ((!option || option->section == PPD_ORDER_JCL) && - pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_CLOSE_UI; - - goto error; - } - - option = NULL; - - free(string); - string = NULL; - } - else if (!strcmp(keyword, "JCLCloseUI")) - { - if ((!option || option->section != PPD_ORDER_JCL) && - pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_CLOSE_UI; - - goto error; - } - - option = NULL; - - free(string); - string = NULL; - } - else if (!strcmp(keyword, "OpenGroup")) - { - // - // Open a new group... - // - - if (group != NULL) - { - pg->ppd_status = PPD_NESTED_OPEN_GROUP; - - goto error; - } - - if (!string) - { - pg->ppd_status = PPD_BAD_OPEN_GROUP; - - goto error; - } - - // - // Separate the group name from the text (name/text)... - // - - if ((sptr = strchr(string, '/')) != NULL) - *sptr++ = '\0'; - else - sptr = string; - - // - // Fix up the text... - // - - ppd_decode(sptr); - - // - // Find/add the group... - // - - group = ppd_get_group(ppd, string, sptr, pg, encoding); - - if (group == NULL) - goto error; - - free(string); - string = NULL; - } - else if (!strcmp(keyword, "CloseGroup")) - { - group = NULL; - - free(string); - string = NULL; - } - else if (!strcmp(keyword, "OrderDependency")) - { - order = (float)_ppdStrScand(string, &sptr, loc); - - if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2) - { - pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY; - - goto error; - } - - if (keyword[0] == '*') - _ppd_strcpy(keyword, keyword + 1); - - if (!strcmp(name, "ExitServer")) - section = PPD_ORDER_EXIT; - else if (!strcmp(name, "Prolog")) - section = PPD_ORDER_PROLOG; - else if (!strcmp(name, "DocumentSetup")) - section = PPD_ORDER_DOCUMENT; - else if (!strcmp(name, "PageSetup")) - section = PPD_ORDER_PAGE; - else if (!strcmp(name, "JCLSetup")) - section = PPD_ORDER_JCL; - else - section = PPD_ORDER_ANY; - - if (option == NULL) - { - ppd_group_t *gtemp; - - - // - // Only valid for Non-UI options... - // - - for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++) - if (gtemp->text[0] == '\0') - break; - - if (i > 0) - for (i = 0; i < gtemp->num_options; i ++) - if (!strcmp(keyword, gtemp->options[i].keyword)) - { - gtemp->options[i].section = section; - gtemp->options[i].order = order; - break; - } - } - else - { - option->section = section; - option->order = order; - } - - free(string); - string = NULL; - } - else if (!strncmp(keyword, "Default", 7)) - { - if (string == NULL) - continue; - - // - // Drop UI text, if any, from value... - // - - if (strchr(string, '/') != NULL) - *strchr(string, '/') = '\0'; - - // - // Assign the default value as appropriate... - // - - if (!strcmp(keyword, "DefaultColorSpace")) - { - // - // Set default colorspace... - // - - if (!strcmp(string, "CMY")) - ppd->colorspace = PPD_CS_CMY; - else if (!strcmp(string, "CMYK")) - ppd->colorspace = PPD_CS_CMYK; - else if (!strcmp(string, "RGB")) - ppd->colorspace = PPD_CS_RGB; - else if (!strcmp(string, "RGBK")) - ppd->colorspace = PPD_CS_RGBK; - else if (!strcmp(string, "N")) - ppd->colorspace = PPD_CS_N; - else - ppd->colorspace = PPD_CS_GRAY; - } - else if (option && !strcmp(keyword + 7, option->keyword)) - { - // - // Set the default as part of the current option... - // - - DEBUG_printf(("2ppdOpenWithLocalization: Setting %s to %s...", - keyword, string)); - - strlcpy(option->defchoice, string, sizeof(option->defchoice)); - - DEBUG_printf(("2ppdOpenWithLocalization: %s is now %s...", - keyword, option->defchoice)); - } - else - { - // - // Lookup option and set if it has been defined... - // - - ppd_option_t *toption; // Temporary option - - - if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL) - { - DEBUG_printf(("2ppdOpenWithLocalization: Setting %s to %s...", - keyword, string)); - strlcpy(toption->defchoice, string, sizeof(toption->defchoice)); - } - } - } - else if (!strcmp(keyword, "UIConstraints") || - !strcmp(keyword, "NonUIConstraints")) - { - if (!string) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - if (ppd->num_consts == 0) - constraint = calloc(2, sizeof(ppd_const_t)); - else - constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * - sizeof(ppd_const_t)); - - if (constraint == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - 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)) - { - default : // Error - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - - case 2 : // Two options... - // - // Check for broken constraints like "* Option"... - // - - if (pg->ppd_conform == PPD_CONFORM_STRICT && - (!strcmp(constraint->option1, "*") || - !strcmp(constraint->choice1, "*"))) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - // - // The following strcpy's are safe, as optionN and - // choiceN are all the same size (size defined by PPD spec...) - // - - if (constraint->option1[0] == '*') - _ppd_strcpy(constraint->option1, constraint->option1 + 1); - else if (pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - if (constraint->choice1[0] == '*') - _ppd_strcpy(constraint->option2, constraint->choice1 + 1); - else if (pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - constraint->choice1[0] = '\0'; - constraint->choice2[0] = '\0'; - break; - - case 3 : // Two options, one choice... - // - // Check for broken constraints like "* Option"... - // - - if (pg->ppd_conform == PPD_CONFORM_STRICT && - (!strcmp(constraint->option1, "*") || - !strcmp(constraint->choice1, "*") || - !strcmp(constraint->option2, "*"))) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - // - // The following _ppd_strcpy's are safe, as optionN and - // choiceN are all the same size (size defined by PPD spec...) - // - - if (constraint->option1[0] == '*') - _ppd_strcpy(constraint->option1, constraint->option1 + 1); - else if (pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - if (constraint->choice1[0] == '*') - { - if (pg->ppd_conform == PPD_CONFORM_STRICT && - constraint->option2[0] == '*') - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - _ppd_strcpy(constraint->choice2, constraint->option2); - _ppd_strcpy(constraint->option2, constraint->choice1 + 1); - constraint->choice1[0] = '\0'; - } - else - { - if (constraint->option2[0] == '*') - _ppd_strcpy(constraint->option2, constraint->option2 + 1); - else if (pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - constraint->choice2[0] = '\0'; - } - break; - - case 4 : // Two options, two choices... - // - // Check for broken constraints like "* Option"... - // - - if (pg->ppd_conform == PPD_CONFORM_STRICT && - (!strcmp(constraint->option1, "*") || - !strcmp(constraint->choice1, "*") || - !strcmp(constraint->option2, "*") || - !strcmp(constraint->choice2, "*"))) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - if (constraint->option1[0] == '*') - _ppd_strcpy(constraint->option1, constraint->option1 + 1); - else if (pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - if (pg->ppd_conform == PPD_CONFORM_STRICT && - constraint->choice1[0] == '*') - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - if (constraint->option2[0] == '*') - _ppd_strcpy(constraint->option2, constraint->option2 + 1); - else if (pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - - if (pg->ppd_conform == PPD_CONFORM_STRICT && - constraint->choice2[0] == '*') - { - pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; - goto error; - } - break; - } - - // - // Don't add this one as an attribute... - // - - free(string); - string = NULL; - } - else if (!strcmp(keyword, "PaperDimension")) - { - if (!_ppd_strcasecmp(name, "custom") || - !_ppd_strncasecmp(name, "custom.", 7)) - { - char cname[PPD_MAX_NAME]; // Rewrite with a leading underscore - snprintf(cname, sizeof(cname), "_%.39s", name); - strlcpy(name, cname, sizeof(name)); - } - - if ((size = ppdPageSize(ppd, name)) == NULL) - size = ppd_add_size(ppd, name); - - if (size == NULL) - { - // - // Unable to add or find size! - // - - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - size->width = (float)_ppdStrScand(string, &sptr, loc); - size->length = (float)_ppdStrScand(sptr, NULL, loc); - - free(string); - string = NULL; - } - else if (!strcmp(keyword, "ImageableArea")) - { - if (!_ppd_strcasecmp(name, "custom") || !_ppd_strncasecmp(name, "custom.", 7)) - { - char cname[PPD_MAX_NAME]; // Rewrite with a leading underscore - snprintf(cname, sizeof(cname), "_%.39s", name); - strlcpy(name, cname, sizeof(name)); - } - - if ((size = ppdPageSize(ppd, name)) == NULL) - size = ppd_add_size(ppd, name); - - if (size == NULL) - { - // - // Unable to add or find size! - // - - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - size->left = (float)_ppdStrScand(string, &sptr, loc); - size->bottom = (float)_ppdStrScand(sptr, &sptr, loc); - size->right = (float)_ppdStrScand(sptr, &sptr, loc); - size->top = (float)_ppdStrScand(sptr, NULL, loc); - - free(string); - string = NULL; - } - else if (option != NULL && - (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) == - (PPD_KEYWORD | PPD_OPTION | PPD_STRING) && - !strcmp(keyword, option->keyword)) - { - DEBUG_printf(("2ppdOpenWithLocalization: group=%p, subgroup=%p", group, subgroup)); - - if (!_ppd_strcasecmp(name, "custom") || !_ppd_strncasecmp(name, "custom.", 7)) - { - char cname[PPD_MAX_NAME]; // Rewrite with a leading underscore - snprintf(cname, sizeof(cname), "_%.39s", name); - strlcpy(name, cname, sizeof(name)); - } - - if (!strcmp(keyword, "PageSize")) - { - // - // Add a page size... - // - - if (ppdPageSize(ppd, name) == NULL) - ppd_add_size(ppd, name); - } - - // - // Add the option choice... - // - - if ((choice = ppd_add_choice(option, name)) == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - goto error; - } - - if (text[0]) - cupsCharsetToUTF8((cups_utf8_t *)choice->text, text, - sizeof(choice->text), encoding); - else if (!strcmp(name, "True")) - strlcpy(choice->text, _("Yes"), sizeof(choice->text)); - else if (!strcmp(name, "False")) - strlcpy(choice->text, _("No"), sizeof(choice->text)); - else - strlcpy(choice->text, name, sizeof(choice->text)); - - if (option->section == PPD_ORDER_JCL) - ppd_decode(string); // Decode quoted string - - choice->code = string; - string = NULL; // Don't add as an attribute below - } - - // - // Add remaining lines with keywords and string values as attributes... - // - - if (string && - (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING)) - ppd_add_attr(ppd, keyword, name, text, string); - else - free(string); - } - - // - // Check for a missing CloseUI/JCLCloseUI... - // - - if (option && pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_MISSING_CLOSE_UI; - goto error; - } - - // - // Check for a missing CloseGroup... - // - - if (group && pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_MISSING_CLOSE_GROUP; - goto error; - } - - free(line.buffer); - - // - // Reset language preferences... - // - -#ifdef DEBUG - if (!cupsFileEOF(fp)) - DEBUG_printf(("1ppdOpenWithLocalization: Premature EOF at %lu...\n", - (unsigned long)cupsFileTell(fp))); -#endif // DEBUG - - if (pg->ppd_status != PPD_OK) - { - // - // Had an error reading the PPD file, cannot continue! - // - - ppdClose(ppd); - - return (NULL); - } - - // - // Update the filters array as needed... - // - - if (!ppd_update_filters(ppd, pg)) - { - ppdClose(ppd); - - return (NULL); - } - - // - // Create the sorted options array and set the option back-pointer for - // each choice and custom option... - // - - ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL, - (cups_ahash_func_t)ppd_hash_option, - PPD_HASHSIZE); - - for (i = ppd->num_groups, group = ppd->groups; - i > 0; - i --, group ++) - { - for (j = group->num_options, option = group->options; - j > 0; - j --, option ++) - { - ppd_coption_t *coption; // Custom option - - - cupsArrayAdd(ppd->options, option); - - for (k = 0; k < option->num_choices; k ++) - option->choices[k].option = option; - - if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL) - coption->option = option; - } - } - - // - // Create an array to track the marked choices... - // - - ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL); - - // - // Return the PPD file structure... - // - - return (ppd); - - // - // Common exit point for errors to save code size... - // - - error: - - free(string); - free(line.buffer); - - ppdClose(ppd); - - return (NULL); -} - - -// -// 'ppdOpen()' - Read a PPD file into memory. -// - -ppd_file_t * // O - PPD file record -ppdOpen(FILE *fp) // I - File to read from -{ - ppd_file_t *ppd; // PPD file record - cups_file_t *cf; // CUPS file - - - // - // Reopen the stdio file as a CUPS file... - // - - if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL) - return (NULL); - - // - // Load the PPD file using the newer API... - // - - ppd = ppdOpenWithLocalization(cf, PPD_LOCALIZATION_DEFAULT); - - // - // Close the CUPS file and return the PPD... - // - - cupsFileClose(cf); - - return (ppd); -} - - -// -// 'ppdOpen2()' - Read a PPD file into memory. -// -// @since CUPS 1.2/macOS 10.5@ -// - -ppd_file_t * // O - PPD file record or @code NULL@ - // if the PPD file could not be - // opened. -ppdOpen2(cups_file_t *fp) // I - File to read from -{ - return ppdOpenWithLocalization(fp, PPD_LOCALIZATION_DEFAULT); -} - - -// -// 'ppdOpenFd()' - Read a PPD file into memory. -// - -ppd_file_t * // O - PPD file record or @code NULL@ - // if the PPD file could not be - // opened. -ppdOpenFd(int fd) // I - File to read from -{ - cups_file_t *fp; // CUPS file pointer - ppd_file_t *ppd; // PPD file record - ppd_globals_t *pg = ppdGlobals(); // Global data - - - // - // Set the line number to 0... - // - - pg->ppd_line = 0; - - // - // Range check input... - // - - if (fd < 0) - { - pg->ppd_status = PPD_NULL_FILE; - - return (NULL); - } - - // - // Try to open the file and parse it... - // - - if ((fp = cupsFileOpenFd(fd, "r")) != NULL) - { - ppd = ppdOpen2(fp); - - cupsFileClose(fp); - } - else - { - pg->ppd_status = PPD_FILE_OPEN_ERROR; - ppd = NULL; - } - - return (ppd); -} - - -// -// 'ppdOpenFileWithLocalization()' - Read a PPD file into memory. -// - -ppd_file_t * // O - PPD file record or @code NULL@ - // if the PPD file could not be - // opened. -ppdOpenFileWithLocalization( - const char *filename, // I - File to read from - ppd_localization_t localization) // I - Localization to load -{ - cups_file_t *fp; // File pointer - ppd_file_t *ppd; // PPD file record - ppd_globals_t *pg = ppdGlobals(); // Global data - - - // - // Set the line number to 0... - // - - pg->ppd_line = 0; - - // - // Range check input... - // - - if (filename == NULL) - { - pg->ppd_status = PPD_NULL_FILE; - - return (NULL); - } - - // - // Try to open the file and parse it... - // - - if ((fp = cupsFileOpen(filename, "r")) != NULL) - { - ppd = ppdOpenWithLocalization(fp, localization); - - cupsFileClose(fp); - } - else - { - pg->ppd_status = PPD_FILE_OPEN_ERROR; - ppd = NULL; - } - - return (ppd); -} - - -// -// 'ppdOpenFile()' - Read a PPD file into memory. -// - -ppd_file_t * // O - PPD file record or @code NULL@ - // if the PPD file could not be - // opened. -ppdOpenFile(const char *filename) // I - File to read from -{ - return ppdOpenFileWithLocalization(filename, PPD_LOCALIZATION_DEFAULT); -} - - -// -// 'ppdSetConformance()' - Set the conformance level for PPD files. -// -// @since CUPS 1.1.20/macOS 10.4@ -// - -void -ppdSetConformance(ppd_conform_t c) // I - Conformance level -{ - ppd_globals_t *pg = ppdGlobals(); // Global data - - - pg->ppd_conform = c; -} - - -// -// 'ppd_add_attr()' - Add an attribute to the PPD data. -// - -static ppd_attr_t * // O - New attribute -ppd_add_attr(ppd_file_t *ppd, // I - PPD file data - const char *name, // I - Attribute name - const char *spec, // I - Specifier string, if any - const char *text, // I - Text string, if any - const char *value) // I - Value of attribute -{ - ppd_attr_t **ptr, // New array - *temp; // New attribute - - - // - // Range check input... - // - - if (ppd == NULL || name == NULL || spec == NULL) - return (NULL); - - // - // Create the array as needed... - // - - if (!ppd->sorted_attrs) - ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs, - NULL); - - // - // Allocate memory for the new attribute... - // - - if (ppd->num_attrs == 0) - ptr = malloc(sizeof(ppd_attr_t *)); - else - ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * - sizeof(ppd_attr_t *)); - - if (ptr == NULL) - return (NULL); - - ppd->attrs = ptr; - ptr += ppd->num_attrs; - - if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL) - return (NULL); - - *ptr = temp; - - ppd->num_attrs ++; - - // - // Copy data over... - // - - strlcpy(temp->name, name, sizeof(temp->name)); - strlcpy(temp->spec, spec, sizeof(temp->spec)); - strlcpy(temp->text, text, sizeof(temp->text)); - temp->value = (char *)value; - - // - // Add the attribute to the sorted array... - // - - cupsArrayAdd(ppd->sorted_attrs, temp); - - // - // Return the attribute... - // - - return (temp); -} - - -// -// '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 - const 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) * - (size_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)); - strlcpy(choice->choice, name, sizeof(choice->choice)); - - 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 - const 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) * - (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)); - strlcpy(size->name, name, sizeof(size->name)); - - return (size); -} - - -// -// 'ppd_compare_attrs()' - Compare two attributes. -// - -static int // O - Result of comparison -ppd_compare_attrs(ppd_attr_t *a, // I - First attribute - ppd_attr_t *b) // I - Second attribute -{ - return (_ppd_strcasecmp(a->name, b->name)); -} - - -// -// 'ppd_compare_choices()' - Compare two choices... -// - -static int // O - Result of comparison -ppd_compare_choices(ppd_choice_t *a, // I - First choice - ppd_choice_t *b) // I - Second choice -{ - return (strcmp(a->option->keyword, b->option->keyword)); -} - - -// -// 'ppd_compare_coptions()' - Compare two custom options. -// - -static int // O - Result of comparison -ppd_compare_coptions(ppd_coption_t *a, // I - First option - ppd_coption_t *b) // I - Second option -{ - return (_ppd_strcasecmp(a->keyword, b->keyword)); -} - - -// -// 'ppd_compare_options()' - Compare two options. -// - -static int // O - Result of comparison -ppd_compare_options(ppd_option_t *a, // I - First option - ppd_option_t *b) // I - Second option -{ - return (_ppd_strcasecmp(a->keyword, b->keyword)); -} - - -// -// 'ppd_decode()' - Decode a string value... -// - -static int // O - Length of decoded string -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] & 255)) - { - // - // Convert hex to 8-bit values... - // - - inptr ++; - while (isxdigit(*inptr & 255)) - { - if (_ppd_isalpha(*inptr)) - *outptr = (char)((tolower(*inptr) - 'a' + 10) << 4); - else - *outptr = (char)((*inptr - '0') << 4); - - inptr ++; - - if (!isxdigit(*inptr & 255)) - break; - - if (_ppd_isalpha(*inptr)) - *outptr |= (char)(tolower(*inptr) - 'a' + 10); - else - *outptr |= (char)(*inptr - '0'); - - inptr ++; - outptr ++; - } - - while (*inptr != '>' && *inptr != '\0') - inptr ++; - while (*inptr == '>') - inptr ++; - } - else - *outptr++ = *inptr++; - - *outptr = '\0'; - - return ((int)(outptr - string)); -} - - -// -// 'ppd_free_filters()' - Free the filters array. -// - -static void -ppd_free_filters(ppd_file_t *ppd) // I - PPD file -{ - int i; // Looping var - char **filter; // Current filter - - - if (ppd->num_filters > 0) - { - for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++) - free(*filter); - - free(ppd->filters); - - ppd->num_filters = 0; - ppd->filters = NULL; - } -} - - -// -// '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); - - free(group->options); - } - - if (group->num_subgroups > 0) - { - for (i = group->num_subgroups, subgroup = group->subgroups; - i > 0; - i --, subgroup ++) - ppd_free_group(subgroup); - - 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 ++) - { - free(choice->code); - } - - free(option->choices); - } -} - - -// -// 'ppd_get_coption()' - Get a custom option record. -// - -static ppd_coption_t * // O - Custom option... -ppd_get_coption(ppd_file_t *ppd, // I - PPD file - const char *name) // I - Name of option -{ - ppd_coption_t *copt; // New custom option - - - // - // See if the option already exists... - // - - if ((copt = ppdFindCustomOption(ppd, name)) != NULL) - return (copt); - - // - // Not found, so create the custom option record... - // - - if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL) - return (NULL); - - strlcpy(copt->keyword, name, sizeof(copt->keyword)); - - copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL); - - cupsArrayAdd(ppd->coptions, copt); - - // - // Return the new record... - // - - return (copt); -} - - -// -// 'ppd_get_cparam()' - Get a custom parameter record. -// - -static ppd_cparam_t * // O - Extended option... -ppd_get_cparam(ppd_coption_t *opt, // I - PPD file - const char *param, // I - Name of parameter - const char *text) // I - Human-readable text -{ - ppd_cparam_t *cparam; // New custom parameter - - - // - // See if the parameter already exists... - // - - if ((cparam = ppdFindCustomParam(opt, param)) != NULL) - return (cparam); - - // - // Not found, so create the custom parameter record... - // - - if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL) - return (NULL); - - cparam->type = PPD_CUSTOM_UNKNOWN; - strlcpy(cparam->name, param, sizeof(cparam->name)); - strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text)); - - // - // Add this record to the array... - // - - cupsArrayAdd(opt->params, cparam); - - // - // Return the new record... - // - - return (cparam); -} - - -// -// '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 - const char *name, // I - Name of group - const char *text, // I - Text for group - ppd_globals_t *pg, // I - Global data - cups_encoding_t encoding) // I - Encoding of text -{ - int i; // Looping var - ppd_group_t *group; // Group - - - DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)", - ppd, name, text, pg)); - - for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) - if (!strcmp(group->name, name)) - break; - - if (i == 0) - { - DEBUG_printf(("8ppd_get_group: Adding group %s...", name)); - - if (pg->ppd_conform == PPD_CONFORM_STRICT && - strlen(text) >= sizeof(group->text)) - { - pg->ppd_status = PPD_ILLEGAL_TRANSLATION; - - return (NULL); - } - - if (ppd->num_groups == 0) - group = malloc(sizeof(ppd_group_t)); - else - group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * - sizeof(ppd_group_t)); - - if (group == NULL) - { - pg->ppd_status = PPD_ALLOC_ERROR; - - return (NULL); - } - - ppd->groups = group; - group += ppd->num_groups; - ppd->num_groups ++; - - memset(group, 0, sizeof(ppd_group_t)); - strlcpy(group->name, name, sizeof(group->name)); - - cupsCharsetToUTF8((cups_utf8_t *)group->text, text, - sizeof(group->text), encoding); - } - - 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 - const char *name) // I - Name of option -{ - int i; // Looping var - ppd_option_t *option; // Option - - - DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")", - group, group->name, name)); - - for (i = group->num_options, option = group->options; i > 0; i --, option ++) - if (!strcmp(option->keyword, name)) - break; - - if (i == 0) - { - if (group->num_options == 0) - option = malloc(sizeof(ppd_option_t)); - else - option = realloc(group->options, (size_t)(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)); - strlcpy(option->keyword, name, sizeof(option->keyword)); - } - - return (option); -} - - -// -// 'ppd_globals_alloc()' - Allocate and initialize global data. -// - -static ppd_globals_t * // O - Pointer to global data -ppd_globals_alloc(void) -{ - return ((ppd_globals_t *)calloc(1, sizeof(ppd_globals_t))); -} - - -// -// 'ppd_globals_free()' - Free global data. -// - -#if defined(HAVE_PTHREAD_H) || defined(_WIN32) -static void -ppd_globals_free(ppd_globals_t *pg) // I - Pointer to global data -{ - free(pg); -} -#endif // HAVE_PTHREAD_H || _WIN32 - - -#ifdef HAVE_PTHREAD_H -// -// 'ppd_globals_init()' - Initialize per-thread globals... -// - -static void -ppd_globals_init(void) -{ - // - // Register the global data for this thread... - // - - pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free); -} -#endif // HAVE_PTHREAD_H - - -// -// 'ppd_hash_option()' - Generate a hash of the option name... -// - -static int // O - Hash index -ppd_hash_option(ppd_option_t *option) // I - Option -{ - int hash = 0; // Hash index - const char *k; // Pointer into keyword - - - for (hash = option->keyword[0], k = option->keyword + 1; *k;) - hash = 33 * hash + *k++; - - return (hash & 511); -} - - -// -// 'ppd_read()' - Read a line from a PPD file, skipping comment lines as -// necessary. -// - -static int // O - Bitmask of fields read -ppd_read(cups_file_t *fp, // I - File to read from - _ppd_line_t *line, // I - Line buffer - 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 ignoreblank, // I - Ignore blank lines? - ppd_globals_t *pg) // I - Global data -{ - int ch, // Character from file - col, // Column in line - colon, // Colon seen? - endquote, // Waiting for an end quote - mask, // Mask to be returned - startline, // Start line - textlen; // Length of text - char *keyptr, // Keyword pointer - *optptr, // Option pointer - *textptr, // Text pointer - *strptr, // Pointer into string - *lineptr; // Current position in line buffer - - - // - // Now loop until we have a valid line... - // - - *string = NULL; - col = 0; - startline = pg->ppd_line + 1; - - if (!line->buffer) - { - line->bufsize = 1024; - line->buffer = malloc(1024); - - if (!line->buffer) - return (0); - } - - do - { - // - // Read the line... - // - - lineptr = line->buffer; - endquote = 0; - colon = 0; - - while ((ch = cupsFileGetChar(fp)) != EOF) - { - if (lineptr >= (line->buffer + line->bufsize - 1)) - { - // - // Expand the line buffer... - // - - char *temp; // Temporary line pointer - - - line->bufsize += 1024; - if (line->bufsize > 262144) - { - // - // Don't allow lines longer than 256k! - // - - pg->ppd_line = startline; - pg->ppd_status = PPD_LINE_TOO_LONG; - - return (0); - } - - temp = realloc(line->buffer, line->bufsize); - if (!temp) - { - pg->ppd_line = startline; - pg->ppd_status = PPD_LINE_TOO_LONG; - - return (0); - } - - lineptr = temp + (lineptr - line->buffer); - line->buffer = temp; - } - - if (ch == '\r' || ch == '\n') - { - // - // Line feed or carriage return... - // - - pg->ppd_line ++; - col = 0; - - if (ch == '\r') - { - // - // Check for a trailing line feed... - // - - if ((ch = cupsFilePeekChar(fp)) == EOF) - { - ch = '\n'; - break; - } - - if (ch == 0x0a) - cupsFileGetChar(fp); - } - - if (lineptr == line->buffer && ignoreblank) - continue; // Skip blank lines - - ch = '\n'; - - if (!endquote) // Continue for multi-line text - break; - - *lineptr++ = '\n'; - } - else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT) - { - // - // Other control characters... - // - - pg->ppd_line = startline; - pg->ppd_status = PPD_ILLEGAL_CHARACTER; - - return (0); - } - else if (ch != 0x1a) - { - // - // Any other character... - // - - *lineptr++ = (char)ch; - col ++; - - if (col > (PPD_MAX_LINE - 1)) - { - // - // Line is too long... - // - - pg->ppd_line = startline; - pg->ppd_status = PPD_LINE_TOO_LONG; - - return (0); - } - - if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0) - colon = 1; - - if (ch == '\"' && colon) - endquote = !endquote; - } - } - - if (endquote) - { - // - // Didn't finish this quoted string... - // - - while ((ch = cupsFileGetChar(fp)) != EOF) - if (ch == '\"') - break; - else if (ch == '\r' || ch == '\n') - { - pg->ppd_line ++; - col = 0; - - if (ch == '\r') - { - // - // Check for a trailing line feed... - // - - if ((ch = cupsFilePeekChar(fp)) == EOF) - break; - if (ch == 0x0a) - cupsFileGetChar(fp); - } - } - else if (ch < ' ' && ch != '\t' && - pg->ppd_conform == PPD_CONFORM_STRICT) - { - // - // Other control characters... - // - - pg->ppd_line = startline; - pg->ppd_status = PPD_ILLEGAL_CHARACTER; - - return (0); - } - else if (ch != 0x1a) - { - col ++; - - if (col > (PPD_MAX_LINE - 1)) - { - // - // Line is too long... - // - - pg->ppd_line = startline; - pg->ppd_status = PPD_LINE_TOO_LONG; - - return (0); - } - } - } - - if (ch != '\n') - { - // - // Didn't finish this line... - // - - while ((ch = cupsFileGetChar(fp)) != EOF) - if (ch == '\r' || ch == '\n') - { - // - // Line feed or carriage return... - // - - pg->ppd_line ++; - col = 0; - - if (ch == '\r') - { - // - // Check for a trailing line feed... - // - - if ((ch = cupsFilePeekChar(fp)) == EOF) - break; - if (ch == 0x0a) - cupsFileGetChar(fp); - } - - break; - } - else if (ch < ' ' && ch != '\t' && - pg->ppd_conform == PPD_CONFORM_STRICT) - { - // - // Other control characters... - // - - pg->ppd_line = startline; - pg->ppd_status = PPD_ILLEGAL_CHARACTER; - - return (0); - } - else if (ch != 0x1a) - { - col ++; - - if (col > (PPD_MAX_LINE - 1)) - { - // - // Line is too long... - // - - pg->ppd_line = startline; - pg->ppd_status = PPD_LINE_TOO_LONG; - - return (0); - } - } - } - - if (lineptr > line->buffer && lineptr[-1] == '\n') - lineptr --; - - *lineptr = '\0'; - - DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer)); - - // - // The dynamically created PPDs for older style macOS - // drivers include a large blob of data inserted as comments - // at the end of the file. As an optimization we can stop - // reading the PPD when we get to the start of this data. - // - - if (!strcmp(line->buffer, "*%APLWORKSET START")) - return (0); - - if (ch == EOF && lineptr == line->buffer) - return (0); - - // - // Now parse it... - // - - mask = 0; - lineptr = line->buffer + 1; - - keyword[0] = '\0'; - option[0] = '\0'; - text[0] = '\0'; - *string = NULL; - - if ((!line->buffer[0] || // Blank line - !strncmp(line->buffer, "*%", 2) || // Comment line - !strcmp(line->buffer, "*End")) && // End of multi-line string - ignoreblank) // Ignore these? - { - startline = pg->ppd_line + 1; - continue; - } - - if (!strcmp(line->buffer, "*")) // (Bad) comment line - { - if (pg->ppd_conform == PPD_CONFORM_RELAXED) - { - startline = pg->ppd_line + 1; - continue; - } - else - { - pg->ppd_line = startline; - pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD; - - return (0); - } - } - - if (line->buffer[0] != '*') // All lines start with an asterisk - { - // - // Allow lines consisting of just whitespace... - // - - for (lineptr = line->buffer; *lineptr; lineptr ++) - if (*lineptr && !_ppd_isspace(*lineptr)) - break; - - if (*lineptr) - { - pg->ppd_status = PPD_MISSING_ASTERISK; - return (0); - } - else if (ignoreblank) - continue; - else - return (0); - } - - // - // Get a keyword... - // - - keyptr = keyword; - - while (*lineptr && *lineptr != ':' && !_ppd_isspace(*lineptr)) - { - if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' || - (keyptr - keyword) >= (PPD_MAX_NAME - 1)) - { - pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD; - return (0); - } - - *keyptr++ = *lineptr++; - } - - *keyptr = '\0'; - - if (!strcmp(keyword, "End")) - continue; - - mask |= PPD_KEYWORD; - - if (_ppd_isspace(*lineptr)) - { - // - // Get an option name... - // - - while (_ppd_isspace(*lineptr)) - lineptr ++; - - optptr = option; - - while (*lineptr && !_ppd_isspace(*lineptr) && *lineptr != ':' && - *lineptr != '/') - { - if (*lineptr <= ' ' || *lineptr > 126 || - (optptr - option) >= (PPD_MAX_NAME - 1)) - { - pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD; - return (0); - } - - *optptr++ = *lineptr++; - } - - *optptr = '\0'; - - if (_ppd_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_ILLEGAL_WHITESPACE; - return (0); - } - - while (_ppd_isspace(*lineptr)) - lineptr ++; - - mask |= PPD_OPTION; - - if (*lineptr == '/') - { - // - // Get human-readable text... - // - - lineptr ++; - - textptr = text; - - while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':') - { - if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') || - (textptr - text) >= (PPD_MAX_LINE - 1)) - { - pg->ppd_status = PPD_ILLEGAL_TRANSLATION; - return (0); - } - - *textptr++ = *lineptr++; - } - - *textptr = '\0'; - textlen = ppd_decode(text); - - if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_ILLEGAL_TRANSLATION; - return (0); - } - - mask |= PPD_TEXT; - } - } - - if (_ppd_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT) - { - pg->ppd_status = PPD_ILLEGAL_WHITESPACE; - return (0); - } - - while (_ppd_isspace(*lineptr)) - lineptr ++; - - if (*lineptr == ':') - { - // - // Get string after triming leading and trailing whitespace... - // - - lineptr ++; - while (_ppd_isspace(*lineptr)) - lineptr ++; - - strptr = lineptr + strlen(lineptr) - 1; - while (strptr >= lineptr && _ppd_isspace(*strptr)) - *strptr-- = '\0'; - - if (*strptr == '\"') - { - // - // Quoted string by itself, remove quotes... - // - - *strptr = '\0'; - lineptr ++; - } - - *string = strdup(lineptr); - - mask |= PPD_STRING; - } - } - while (mask == 0); - - return (mask); -} - - -// -// 'ppd_update_filters()' - Update the filters array as needed. -// -// This function re-populates the filters array with cupsFilter2 entries that -// have been stripped of the destination MIME media types and any maxsize hints. -// -// (All for backwards-compatibility) -// - -static int // O - 1 on success, 0 on failure -ppd_update_filters(ppd_file_t *ppd, // I - PPD file - ppd_globals_t *pg) // I - Global data -{ - ppd_attr_t *attr; // Current cupsFilter2 value - char srcsuper[16], // Source MIME media type - srctype[256], - dstsuper[16], // Destination MIME media type - dsttype[256], - *ptr, // Pointer into command to run - buffer[2048], // Re-written cupsFilter value - **filter; // Current filter - int cost; // Cost of filter - char program[1024] = { 0 }; // Command to run - - - DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg)); - - // - // See if we have any cupsFilter2 lines... - // - - if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL) - { - DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present."); - return (1); - } - - // - // Yes, free the cupsFilter-defined filters and re-build... - // - - ppd_free_filters(ppd); - - do - { - // - // Parse the cupsFilter2 string: - // - // src/type dst/type cost program - // src/type dst/type cost maxsize(n) program - // - - DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value)); - - if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]", - srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6) - { - DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line."); - pg->ppd_status = PPD_BAD_VALUE; - - return (0); - } - - DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", " - "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"", - srcsuper, srctype, dstsuper, dsttype, cost, program)); - - if (!strncmp(program, "maxsize(", 8) && - (ptr = strchr(program + 8, ')')) != NULL) - { - DEBUG_puts("5ppd_update_filters: Found maxsize(nnn)."); - - ptr ++; - while (_ppd_isspace(*ptr)) - ptr ++; - - _ppd_strcpy(program, ptr); - DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program)); - } - - // - // Convert to cupsFilter format: - // - // src/type cost program - // - - snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost, - program); - DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer)); - - // - // Add a cupsFilter-compatible string to the filters array. - // - - if (ppd->num_filters == 0) - filter = malloc(sizeof(char *)); - else - filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1)); - - if (filter == NULL) - { - DEBUG_puts("5ppd_update_filters: Out of memory."); - pg->ppd_status = PPD_ALLOC_ERROR; - - return (0); - } - - ppd->filters = filter; - filter += ppd->num_filters; - ppd->num_filters ++; - - *filter = strdup(buffer); - } - while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL); - - DEBUG_puts("5ppd_update_filters: Completed OK."); - return (1); -} diff --git a/ppd/ppd.h b/ppd/ppd.h deleted file mode 100644 index a7a92d11f..000000000 --- a/ppd/ppd.h +++ /dev/null @@ -1,889 +0,0 @@ -// -// Main PPD file handling API definitions for libppd. -// -// PPD FILES ARE DEPRECATED. libppd IS ONLY INTENDED FOR LEGACY IMPORT OF -// CLASSIC CUPS DRIVERS AND POSTSCRIPT PRINTER PPD FILES. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products, all rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// PostScript is a trademark of Adobe Systems, Inc. -// - -#ifndef _PPD_PPD_H_ -# define _PPD_PPD_H_ - -// -// Include necessary headers... -// - -# include -# include -# include -# include -# include - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - -// -// Constants... -// - -# define PPD_CACHE_VERSION 20 // Version number in cache file - - -// -// PPD version... -// - -# define PPD_VERSION 4.3 // Kept in sync with Adobe version - // number - - -// -// PPD size limits (defined in Adobe spec) -// - -# define PPD_MAX_NAME 41 // Maximum size of name + 1 for nul -# define PPD_MAX_TEXT 81 // Maximum size of text + 1 for nul -# define PPD_MAX_LINE 256 // Maximum size of line + 1 for nul - - -// **** New in cups-filters 2.0.0: Ovetaken from cups-driverd **** - -// -// PPD collection entry data -// - -# define PPD_SYNC 0x50504441 // Sync word for ppds.dat (PPDA) -# define PPD_MAX_LANG 32 // Maximum languages -# define PPD_MAX_PROD 32 // Maximum products -# define PPD_MAX_VERS 32 // Maximum versions - -# define PPD_TYPE_POSTSCRIPT 0 // PostScript PPD -# define PPD_TYPE_PDF 1 // PDF PPD -# define PPD_TYPE_RASTER 2 // CUPS raster PPD -# define PPD_TYPE_FAX 3 // Facsimile/MFD PPD -# define PPD_TYPE_UNKNOWN 4 // Other/hybrid PPD -# define PPD_TYPE_DRV 5 // Driver info file -# define PPD_TYPE_ARCHIVE 6 // Archive file - - -// -// Types and structures... -// - -typedef int (*cups_interpret_cb_t)(cups_page_header2_t *header, int preferred_bits); - // **** cupsRasterInterpretPPD callback - // function **** - // - // This function is called by - // @link cupsRasterInterpretPPD@ to - // validate (and update, as needed) - // the page header attributes. The - // "preferred_bits" argument provides - // the value of the - // @code cupsPreferredBitsPerColor@ - // key from the PostScript page device - // dictionary and is 0 if undefined. - // - -typedef enum ppd_ui_e // **** 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 ppd_section_e // *** 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 ppd_cs_e // **** 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 enum ppd_status_e // **** Status Codes **** -{ - PPD_OK = 0, // OK - PPD_FILE_OPEN_ERROR, // Unable to open PPD file - PPD_NULL_FILE, // NULL PPD file pointer - PPD_ALLOC_ERROR, // Memory allocation error - PPD_MISSING_PPDADOBE4, // Missing PPD-Adobe-4.x header - PPD_MISSING_VALUE, // Missing value string - PPD_INTERNAL_ERROR, // Internal error - PPD_BAD_OPEN_GROUP, // Bad OpenGroup - PPD_NESTED_OPEN_GROUP, // OpenGroup without a CloseGroup - // first - PPD_BAD_OPEN_UI, // Bad OpenUI/JCLOpenUI - PPD_NESTED_OPEN_UI, // OpenUI/JCLOpenUI without a - // CloseUI/JCLCloseUI first - PPD_BAD_ORDER_DEPENDENCY, // Bad OrderDependency - PPD_BAD_UI_CONSTRAINTS, // Bad UIConstraints - PPD_MISSING_ASTERISK, // Missing asterisk in column 0 - PPD_LINE_TOO_LONG, // Line longer than 255 chars - PPD_ILLEGAL_CHARACTER, // Illegal control character - PPD_ILLEGAL_MAIN_KEYWORD, // Illegal main keyword string - PPD_ILLEGAL_OPTION_KEYWORD, // Illegal option keyword string - PPD_ILLEGAL_TRANSLATION, // Illegal translation string - PPD_ILLEGAL_WHITESPACE, // Illegal whitespace character - PPD_BAD_CUSTOM_PARAM, // Bad custom parameter - PPD_MISSING_OPTION_KEYWORD, // Missing option keyword - PPD_BAD_VALUE, // Bad value string - PPD_MISSING_CLOSE_GROUP, // Missing CloseGroup - PPD_BAD_CLOSE_UI, // Bad CloseUI/JCLCloseUI - PPD_MISSING_CLOSE_UI, // Missing CloseUI/JCLCloseUI - PPD_MAX_STATUS // @private@ -} ppd_status_t; - -enum ppd_conform_e // **** Conformance Levels **** -{ - PPD_CONFORM_RELAXED, // Relax whitespace and control char - PPD_CONFORM_STRICT // Require strict conformance -}; - -typedef enum ppd_conform_e ppd_conform_t; - // **** Conformance Levels **** - -typedef struct ppd_attr_s // **** PPD Attribute Structure **** -{ - char name[PPD_MAX_NAME]; // Name of attribute (cupsXYZ) - char spec[PPD_MAX_NAME]; // Specifier string, if any - char text[PPD_MAX_TEXT]; // Human-readable text, if any - char *value; // Value string -} ppd_attr_t; - -typedef struct ppd_option_s ppd_option_t; - // **** Options **** - -typedef struct ppd_choice_s // **** Option choices **** -{ - char marked; // 0 if not selected, 1 otherwise - char choice[PPD_MAX_NAME]; // Computer-readable option name - char text[PPD_MAX_TEXT]; // Human-readable option name - char *code; // Code to send for this option - ppd_option_t *option; // Pointer to parent option structure -} ppd_choice_t; - -struct ppd_option_s // **** Options **** -{ - char conflicted; // 0 if no conflicts exist, 1 - // otherwise - char keyword[PPD_MAX_NAME]; // Option keyword name ("PageSize", - // etc.) - char defchoice[PPD_MAX_NAME];// Default option choice - char text[PPD_MAX_TEXT]; // 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 -}; - -typedef struct ppd_group_s // **** Groups **** -{ - // **** Group text strings are limited to 39 chars + nul in order to - // **** preserve binary compatibility and allow applications to get - // **** the group's keyword name. - - char text[PPD_MAX_TEXT - PPD_MAX_NAME]; - // Human-readable group name - char name[PPD_MAX_NAME]; // Group name @since CUPS 1.1.18/macOS - // 10.3@ - int num_options; // Number of options - ppd_option_t *options; // Options - int num_subgroups; // Number of sub-groups - struct ppd_group_s *subgroups; // Sub-groups (max depth = 1) -} ppd_group_t; - -typedef struct ppd_const_s // **** Constraints **** -{ - char option1[PPD_MAX_NAME]; // First keyword - char choice1[PPD_MAX_NAME]; // First option/choice (blank for all) - char option2[PPD_MAX_NAME]; // Second keyword - char choice2[PPD_MAX_NAME]; // Second option/choice (blank for all) -} ppd_const_t; - -typedef struct ppd_size_s // **** Page Sizes **** -{ - int marked; // Page size selected? - char name[PPD_MAX_NAME]; // Media size option - float width; // Width of media in points - float length; // Length of media in points - float left; // Left printable margin in points - float bottom; // Bottom printable margin in points - float right; // Right printable margin in points - float top; // Top printable margin in points -} ppd_size_t; - -typedef struct ppd_emul_s // **** Emulators **** -{ - char name[PPD_MAX_NAME]; // Emulator name - char *start; // Code to switch to this emulation - char *stop; // Code to stop this emulation -} ppd_emul_t; - -typedef struct ppd_profile_s // **** sRGB Color Profiles **** -{ - char resolution[PPD_MAX_NAME]; - // Resolution or "-" - char media_type[PPD_MAX_NAME]; - // Media type or "-" - float density; // Ink density to use - float gamma; // Gamma correction to use - float matrix[3][3]; // Transform matrix -} ppd_profile_t; - -// **** New in CUPS 1.2/macOS 10.5 **** -typedef enum ppd_cptype_e // **** Custom Parameter Type **** -{ - PPD_CUSTOM_UNKNOWN = -1, // Unknown type (error) - PPD_CUSTOM_CURVE, // Curve value for f(x) = x^value - PPD_CUSTOM_INT, // Integer number value - PPD_CUSTOM_INVCURVE, // Curve value for f(x) = x^(1/value) - PPD_CUSTOM_PASSCODE, // String of (hidden) numbers - PPD_CUSTOM_PASSWORD, // String of (hidden) characters - PPD_CUSTOM_POINTS, // Measurement value in points - PPD_CUSTOM_REAL, // Real number value - PPD_CUSTOM_STRING // String of characters -} ppd_cptype_t; - -typedef union ppd_cplimit_u // **** Custom Parameter Limit **** -{ - float custom_curve; // Gamma value - int custom_int; // Integer value - float custom_invcurve; // Gamma value - int custom_passcode; // Passcode length - int custom_password; // Password length - float custom_points; // Measurement value - float custom_real; // Real value - int custom_string; // String length -} ppd_cplimit_t; - -typedef union ppd_cpvalue_u // **** Custom Parameter Value **** -{ - float custom_curve; // Gamma value - int custom_int; // Integer value - float custom_invcurve; // Gamma value - char *custom_passcode; // Passcode value - char *custom_password; // Password value - float custom_points; // Measurement value - float custom_real; // Real value - char *custom_string; // String value -} ppd_cpvalue_t; - -typedef struct ppd_cparam_s // **** Custom Parameter **** -{ - char name[PPD_MAX_NAME]; // Parameter name - char text[PPD_MAX_TEXT]; // Human-readable text - int order; // Order (0 to N) - ppd_cptype_t type; // Parameter type - ppd_cplimit_t minimum, // Minimum value - maximum; // Maximum value - ppd_cpvalue_t current; // Current value -} ppd_cparam_t; - -typedef struct ppd_coption_s // **** Custom Option **** -{ - char keyword[PPD_MAX_NAME]; // Name of option that is being - // extended... - ppd_option_t *option; // Option that is being extended... - int marked; // Extended option is marked - cups_array_t *params; // Parameters -} ppd_coption_t; - -typedef struct ppd_globals_s // **** CUPS PPD global state data **** -{ - // ppd.c - ppd_status_t ppd_status; // Status of last ppdOpen*() - int ppd_line; // Current line number - ppd_conform_t ppd_conform; // Level of conformance required - - // ppd-util.c - char ppd_filename[HTTP_MAX_URI]; - // PPD filename -} ppd_globals_t; - -typedef enum ppd_localization_e// **** Selector for ppdOpenWithLocalization **** -{ - PPD_LOCALIZATION_DEFAULT, // Load only the default localization - PPD_LOCALIZATION_ICC_PROFILES, // Load only the color profile - // localization - PPD_LOCALIZATION_NONE, // Load no localizations - PPD_LOCALIZATION_ALL // Load all localizations -} ppd_localization_t; - -typedef enum ppd_parse_e // **** Selector for ppdParseOptions **** -{ - PPD_PARSE_OPTIONS, // Parse only the options - PPD_PARSE_PROPERTIES, // Parse only the properties - PPD_PARSE_ALL // Parse everything -} ppd_parse_t; - -typedef struct ppd_cups_uiconst_s // **** Constraint from - // cupsUIConstraints **** -{ - ppd_option_t *option; // Constrained option - ppd_choice_t *choice; // Constrained choice or @code NULL@ - int installable; // Installable option? -} ppd_cups_uiconst_t; - -typedef struct ppd_cups_uiconsts_s // **** cupsUIConstraints **** -{ - char resolver[PPD_MAX_NAME]; // Resolver name - int installable, // Constrained against any installable - // options? - num_constraints; // Number of constraints - ppd_cups_uiconst_t *constraints; // Constraints -} ppd_cups_uiconsts_t; - -typedef enum ppd_pwg_print_color_mode_e// **** PWG print-color-mode indices **** -{ - PPD_PWG_PRINT_COLOR_MODE_MONOCHROME = 0, // print-color-mode=monochrome - PPD_PWG_PRINT_COLOR_MODE_COLOR, // print-color-mode=color - // Other values are not supported by CUPS yet. - PPD_PWG_PRINT_COLOR_MODE_MAX -} ppd_pwg_print_color_mode_t; - -typedef enum ppd_pwg_print_quality_e // **** PWG print-quality values **** -{ - PPD_PWG_PRINT_QUALITY_DRAFT = 0, // print-quality=3 - PPD_PWG_PRINT_QUALITY_NORMAL, // print-quality=4 - PPD_PWG_PRINT_QUALITY_HIGH, // print-quality=5 - PPD_PWG_PRINT_QUALITY_MAX -} ppd_pwg_print_quality_t; - -typedef enum ppd_pwg_print_content_optimize_e // * PWG print-content-optimize * -{ - PPD_PWG_PRINT_CONTENT_OPTIMIZE_AUTO = 0, // print-content-optimize=auto - PPD_PWG_PRINT_CONTENT_OPTIMIZE_PHOTO, // print-content-optimize=photo - PPD_PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS,// print-content-optimize=graphics - PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT, // print-content-optimize=text - PPD_PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS, // ...=text-and-graphics - PPD_PWG_PRINT_CONTENT_OPTIMIZE_MAX -} ppd_pwg_print_content_optimize_t; - -typedef struct ppd_pwg_finishings_s // **** PWG finishings mapping data **** -{ - ipp_finishings_t value; // finishings value - int num_options; // Number of options to apply - cups_option_t *options; // Options to apply -} ppd_pwg_finishings_t; - -struct ppd_cache_s // **** PPD cache and PWG conversion data **** -{ - int num_bins; // Number of output bins - pwg_map_t *bins; // Output bins - int num_sizes; // Number of media sizes - pwg_size_t *sizes; // Media sizes - int custom_max_width, // Maximum custom width in 2540ths - custom_max_length, // Maximum custom length in 2540ths - custom_min_width, // Minimum custom width in 2540ths - custom_min_length; // Minimum custom length in 2540ths - char *custom_max_keyword, // Maximum custom size PWG keyword - *custom_min_keyword, // Minimum custom size PWG keyword - custom_ppd_size[41]; // Custom PPD size name - pwg_size_t custom_size; // Custom size record - char *source_option; // PPD option for media source - int num_sources; // Number of media sources - pwg_map_t *sources; // Media sources - int num_types; // Number of media types - pwg_map_t *types; // Media types - int num_presets[PPD_PWG_PRINT_COLOR_MODE_MAX][PPD_PWG_PRINT_QUALITY_MAX]; - // Number of print-color-mode/ - // print-quality options - cups_option_t *presets[PPD_PWG_PRINT_COLOR_MODE_MAX][PPD_PWG_PRINT_QUALITY_MAX]; - // print-color-mode/ - // print-quality options - int num_optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_MAX]; - // Number of print-content-optimize - // options - cups_option_t *optimize_presets[PPD_PWG_PRINT_CONTENT_OPTIMIZE_MAX]; - // print-content-optimize options - char *sides_option, // PPD option for sides - *sides_1sided, // Choice for one-sided - *sides_2sided_long, // Choice for two-sided-long-edge - *sides_2sided_short; // Choice for two-sided-short-edge - char *product; // Product value - cups_array_t *filters, // cupsFilter/cupsFilter2 values - *prefilters; // cupsPreFilter values - int single_file; // cupsSingleFile value - cups_array_t *finishings; // cupsIPPFinishings values - cups_array_t *templates; // cupsFinishingTemplate values - int max_copies, // cupsMaxCopies value - account_id, // cupsJobAccountId value - accounting_user_id; // cupsJobAccountingUserId value - char *password; // cupsJobPassword value - cups_array_t *mandatory; // cupsMandatory value - char *charge_info_uri; // cupsChargeInfoURI value - cups_array_t *strings; // Localization strings - cups_array_t *support_files; // Support files - ICC profiles, etc. -}; -typedef struct ppd_cache_s ppd_cache_t; - // **** PPD cache and mapping data **** - -typedef struct ppd_file_s // **** PPD File **** -{ - int language_level; // Language level of device - int color_device; // 1 = color device, 0 = grayscale - int variable_sizes; // 1 = supports variable sizes, - // 0 = doesn't - int accurate_screens; // 1 = supports accurate screens, - // 0 = not - int contone_only; // 1 = continuous tone only, 0 = not - int landscape; // -90 or 90 - int model_number; // Device-specific model number - int manual_copies; // 1 = Copies done manually, - // 0 = hardware - int 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 - // (no longer supported) @private@ - ppd_emul_t *emulations; // Emulations and the code to invoke - // them (no longer supported) - // @private@ - char *jcl_begin; // Start JCL commands - char *jcl_ps; // Enter PostScript interpreter -#if HAVE_CUPS_3_X - char *jcl_pdf; // Enter PDF interpreter -#endif - char *jcl_end; // End JCL commands - char *lang_encoding; // Language encoding - char *lang_version; // Language version (English, Spanish, - // etc.) - char *modelname; // Model name (general) - char *ttrasterizer; // Truetype rasterizer - char *manufacturer; // Manufacturer name - char *product; // Product name (from PS - // RIP/interpreter) - char *nickname; // Nickname (specific) - char *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 - float custom_max[2]; // Maximum variable page size - float 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... - - // *** New in CUPS 1.1 **** - int flip_duplex; // 1 = Flip page for back sides - - // **** New in CUPS 1.1.19 **** - char *protocols; // Protocols (BCP, TBCP) string - // @since CUPS 1.1.19/macOS 10.3@ - char *pcfilename; // PCFileName string @since - // CUPS 1.1.19/macOS 10.3@ - int num_attrs; // Number of attributes @since - // CUPS 1.1.19/macOS 10.3@ @private@ - int cur_attr; // Current attribute @since - // CUPS 1.1.19/macOS 10.3@ @private@ - ppd_attr_t **attrs; // Attributes @since CUPS 1.1.19/macOS - // 10.3@ @private@ - - // **** New in CUPS 1.2/macOS 10.5 **** - cups_array_t *sorted_attrs; // Attribute lookup array @since - // CUPS 1.2/macOS 10.5@ @private@ - cups_array_t *options; // Option lookup array @since - // CUPS 1.2/macOS 10.5@ @private@ - cups_array_t *coptions; // Custom options array @since - // CUPS 1.2/macOS 10.5@ @private@ - - // **** New in CUPS 1.3/macOS 10.5 **** - cups_array_t *marked; // Marked choices - // @since CUPS 1.3/macOS 10.5@ - // @private@ - - // **** New in CUPS 1.4/macOS 10.6 **** - cups_array_t *cups_uiconstraints; // cupsUIConstraints @since - // CUPS 1.4/macOS 10.6@ @private@ - - // **** New in CUPS 1.5 **** - ppd_cache_t *cache; // PPD cache and mapping data @since - // CUPS 1.5/macOS 10.7@ @private@ -} ppd_file_t; - -// **** New in cups-filters 2.0.0: Ovetaken from cups-driverd **** -typedef struct // **** PPD record **** -{ - time_t mtime; // Modification time - off_t size; // Size in bytes - int model_number; // cupsModelNumber - int type; // ppd-type - char filename[512], // Filename - name[256], // PPD name - languages[PPD_MAX_LANG][6], - // LanguageVersion/cupsLanguages - products[PPD_MAX_PROD][128], - // Product strings - psversions[PPD_MAX_VERS][32], - // PSVersion strings - make[128], // Manufacturer - make_and_model[128], // NickName/ModelName - device_id[256], // IEEE 1284 Device ID - scheme[128]; // PPD scheme -} ppd_rec_t; - -typedef struct // **** In-memory record **** -{ - int found; // 1 if PPD is found - int matches; // Match count - ppd_rec_t record; // PPDs.dat record -} ppd_info_t; - -typedef struct -{ - char *name; // Name for PPD collection - char *path; // Directory where PPD collection is - // located -} ppd_collection_t; - - - -// -// Prototypes... -// - -// cupsMarkOptions replaced by ppdMarkOptions in libppd -// extern int cupsMarkOptions(ppd_file_t *ppd, int num_options, -// cups_option_t *options); -// Definition for backward compatibility, will be removed soon -#define cupsMarkOptions cupsMarkOptions_USE_ppdMarkOptions_INSTEAD - -extern void ppdClose(ppd_file_t *ppd); -extern int ppdCollect(ppd_file_t *ppd, ppd_section_t section, - ppd_choice_t ***choices); -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 ppdEmitJCL(ppd_file_t *ppd, FILE *fp, int job_id, - const char *user, const char *title); -extern ppd_choice_t *ppdFindChoice(ppd_option_t *o, const char *choice); -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 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_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); - -// **** New in CUPS 1.1.19 **** -extern const char *ppdErrorString(ppd_status_t status); -extern ppd_attr_t *ppdFindAttr(ppd_file_t *ppd, const char *name, - const char *spec); -extern ppd_attr_t *ppdFindNextAttr(ppd_file_t *ppd, const char *name, - const char *spec); -extern ppd_status_t ppdLastError(int *line); - -// **** New in CUPS 1.1.20 **** -extern void ppdSetConformance(ppd_conform_t c); - -// **** New in CUPS 1.2 **** -// cupsRasterInterpretPPD replaced by ppdRasterInterpretPPD in libppd -// extern int cupsRasterInterpretPPD(cups_page_header2_t *h, -// ppd_file_t *ppd, -// int num_options, -// cups_option_t *options, -// cups_interpret_cb_t func); -// Definition for backward compatibility, will be removed soon -#define cupsRasterInterpretPPD cupsRasterInterpretPPD_USE_ppdRasterInterpretPPD_INSTEAD - -extern int ppdCollect2(ppd_file_t *ppd, ppd_section_t section, - float min_order, ppd_choice_t ***choices); -extern int ppdEmitAfterOrder(ppd_file_t *ppd, FILE *fp, - ppd_section_t section, int limit, - float min_order); -extern int ppdEmitJCLEnd(ppd_file_t *ppd, FILE *fp); -extern char *ppdEmitString(ppd_file_t *ppd, ppd_section_t section, - float min_order); -extern ppd_coption_t *ppdFindCustomOption(ppd_file_t *ppd, - const char *keyword); -extern ppd_cparam_t *ppdFindCustomParam(ppd_coption_t *opt, - const char *name); -extern ppd_cparam_t *ppdFirstCustomParam(ppd_coption_t *opt); -extern ppd_option_t *ppdFirstOption(ppd_file_t *ppd); -extern ppd_cparam_t *ppdNextCustomParam(ppd_coption_t *opt); -extern ppd_option_t *ppdNextOption(ppd_file_t *ppd); -extern int ppdLocalize(ppd_file_t *ppd); -extern ppd_file_t *ppdOpen2(cups_file_t *fp); - -// **** New in CUPS 1.3/macOS 10.5 **** -extern const char *ppdLocalizeIPPReason(ppd_file_t *ppd, - const char *reason, - const char *scheme, - char *buffer, - size_t bufsize); - -// **** New in CUPS 1.4/macOS 10.6 **** -// cupsGetConflicts replaced by ppdGetConflicts in libppd -// extern int cupsGetConflicts(ppd_file_t *ppd, const char *option, -// const char *choice, -// cups_option_t **options); -// Definition for backward compatibility, will be removed soon -#define cupsGetConflicts cupsGetConflicts_USE_ppdGetConflicts_INSTEAD - -// cupsResolveConflicts replaced by ppdResolveConflicts in libppd -// extern int cupsResolveConflicts(ppd_file_t *ppd, -// const char *option, -// const char *choice, -// int *num_options, -// cups_option_t **options); -// Definition for backward compatibility, will be removed soon -#define cupsResolveConflicts cupsResolveConflicts_USE_ppdResolveConflicts_INSTEAD - -extern int ppdInstallableConflict(ppd_file_t *ppd, - const char *option, - const char *choice); -extern ppd_attr_t *ppdLocalizeAttr(ppd_file_t *ppd, const char *keyword, - const char *spec); -extern const char *ppdLocalizeMarkerName(ppd_file_t *ppd, - const char *name); -extern int ppdPageSizeLimits(ppd_file_t *ppd, - ppd_size_t *minimum, - ppd_size_t *maximum); - -// **** New in cups-filters 2.0.0: Renamed functions from original CUPS API **** -extern int ppdMarkOptions(ppd_file_t *ppd, - int num_options, - cups_option_t *options); -extern int ppdRasterInterpretPPD(cups_page_header2_t *h, - ppd_file_t *ppd, - int num_options, - cups_option_t *options, - cups_interpret_cb_t func); -extern int ppdGetConflicts(ppd_file_t *ppd, const char *option, - const char *choice, - cups_option_t **options); -extern int ppdResolveConflicts(ppd_file_t *ppd, - const char *option, - const char *choice, - int *num_options, - cups_option_t **options); - -// **** New in cups-filters 2.0.0: Formerly CUPS-private functions **** -extern int ppdConvertOptions(ipp_t *request, - ppd_file_t *ppd, - ppd_cache_t *pc, - ipp_attribute_t *media_col_sup, - ipp_attribute_t *doc_handling_sup, - ipp_attribute_t *print_color_mode_sup, - const char *user, - const char *format, - int copies, - int num_options, - cups_option_t *options); -extern int ppdRasterExecPS(cups_page_header2_t *h, - int *preferred_bits, - const char *code); -extern int ppdRasterInterpretPPD(cups_page_header2_t *h, - ppd_file_t *ppd, - int num_options, - cups_option_t *options, - cups_interpret_cb_t func); -extern ppd_cache_t *ppdCacheCreateWithFile(const char *filename, - ipp_t **attrs); -extern ppd_cache_t *ppdCacheCreateWithPPD(ppd_file_t *ppd); -extern void ppdCacheDestroy(ppd_cache_t *pc); -extern const char *ppdCacheGetBin(ppd_cache_t *pc, - const char *output_bin); -extern int ppdCacheGetFinishingOptions(ppd_cache_t *pc, - ipp_t *job, - ipp_finishings_t value, - int num_options, - cups_option_t **options); -extern int ppdCacheGetFinishingValues(ppd_file_t *ppd, - ppd_cache_t *pc, - int max_values, - int *values); -extern const char *ppdCacheGetInputSlot(ppd_cache_t *pc, ipp_t *job, - const char *keyword); -extern const char *ppdCacheGetMediaType(ppd_cache_t *pc, ipp_t *job, - const char *keyword); -extern const char *ppdCacheGetOutputBin(ppd_cache_t *pc, - const char *keyword); -extern const char *ppdCacheGetPageSize(ppd_cache_t *pc, ipp_t *job, - const char *keyword, int *exact); -extern pwg_size_t *ppdCacheGetSize(ppd_cache_t *pc, - const char *page_size); -extern const char *ppdCacheGetSource(ppd_cache_t *pc, - const char *input_slot); -extern const char *ppdCacheGetType(ppd_cache_t *pc, - const char *media_type); -extern int ppdCacheWriteFile(ppd_cache_t *pc, - const char *filename, ipp_t *attrs); -extern void ppdFreeLanguages(cups_array_t *languages); -extern cups_encoding_t ppdGetEncoding(const char *name); -extern cups_array_t *ppdGetLanguages(ppd_file_t *ppd); -extern ppd_globals_t *ppdGlobals(void); -extern void ppdHandleMedia(ppd_file_t *ppd); -extern unsigned ppdHashName(const char *name); -extern ppd_attr_t *ppdLocalizedAttr(ppd_file_t *ppd, const char *keyword, - const char *spec, const char *ll_CC); -extern char *ppdNormalizeMakeAndModel(const char *make_and_model, - char *buffer, - size_t bufsize); -extern ppd_file_t *ppdOpenWithLocalization(cups_file_t *fp, - ppd_localization_t localization); -extern ppd_file_t *ppdOpenFileWithLocalization(const char *filename, - ppd_localization_t localization); -extern int ppdParseOptions(const char *s, int num_options, - cups_option_t **options, - ppd_parse_t which); -extern const char *ppdPwgInputSlotForSource(const char *media_source, - char *name, size_t namesize); -extern const char *ppdPwgMediaTypeForType(const char *media_type, - char *name, size_t namesize); -extern const char *ppdPwgPageSizeForMedia(pwg_media_t *media, - char *name, size_t namesize); -extern void ppdPwgPpdizeName(const char *ipp, char *name, - size_t namesize); -extern void ppdPwgPpdizeResolution(ipp_attribute_t *attr, - int element, int *xres, - int *yres, char *name, - size_t namesize); -extern void ppdPwgUnppdizeName(const char *ppd, char *name, - size_t namesize, - const char *dashchars); - -// **** New in cups-filters 2.0.0: Overtaken from ippeveprinter **** -extern ipp_t *ppdLoadAttributes(ppd_file_t *ppd); - -// **** New in cups-filters 2.0.0: Overtaken from ippeveps **** -extern int ppdGetOptions(cups_option_t **options, - ipp_t *printer_attrs, - ipp_t *job_attrs, - ppd_file_t *ppd); - -// **** New in cups-filters 2.0.0: Added for pclmtoraster filter **** -extern int ppdRasterMatchPPDSize(cups_page_header2_t *header, - ppd_file_t *ppd, - double margins[4], - double dimensions[2], - int *image_fit, - int *landscape); - -// **** New in cups-filters 2.0.0: Ovetaken from cups-driverd **** -extern cups_array_t *ppdCollectionListPPDs(cups_array_t *ppd_collections, - int limit, - int num_options, - cups_option_t *options, - cf_logfunc_t log, - void *ld); -extern cups_file_t *ppdCollectionGetPPD(const char *name, - cups_array_t *ppd_collections, - cf_logfunc_t log, - void *ld); -extern int ppdCollectionDumpCache(const char *filename, - cf_logfunc_t log, - void *ld); - -// **** New in cups-filters 2.0.0: For PPD retro-fit Printer Applications **** -extern void ppdCacheAssignPresets(ppd_file_t *ppd, ppd_cache_t *pc); - -// **** New in cups-filters 2.0.0: JCL for PDF printers, for -// ppdFilterPDFToPDF() and ppdFilterImageToPDF() **** -extern int ppdEmitJCLPDF(ppd_file_t *ppd, FILE *fp, - int job_id, const char *user, - const char *title, - int hw_copies, bool hw_collate); - -// **** New in cups-filters 2.0.0: PPD file generator for CUPS queues for -// driverless printers, compared to the one of CUPS this one supports -// also clusters composed of different printers as composed by -// cups-browsed, was ppdCreateFromIPP(2)() in libcupsfilters 1.x **** -char *ppdCreatePPDFromIPP(char *buffer, size_t bufsize, - ipp_t *response, const char *make_model, - const char *pdl, int color, int duplex, - char *status_msg, size_t status_msg_size); -char *ppdCreatePPDFromIPP2(char *buffer, size_t bufsize, - ipp_t *response, const char *make_model, - const char *pdl, int color, int duplex, - cups_array_t* conflicts, - cups_array_t *sizes, - char* default_pagesize, - const char *default_cluster_color, - char *status_msg, size_t status_msg_size); - -// **** New in cups-filters 2.0.0: Functions to load color profile data from -// PPD files, from driver.h **** -extern ppd_attr_t *ppdFindColorAttr(ppd_file_t *ppd, const char *name, - const char *colormodel, - const char *media, - const char *resolution, - char *spec, int specsize, - cf_logfunc_t log, - void *ld); -extern cf_lut_t *ppdLutLoad(ppd_file_t *ppd, - const char *colormodel, - const char *media, - const char *resolution, - const char *ink, - cf_logfunc_t log, - void *ld); -extern cf_rgb_t *ppdRGBLoad(ppd_file_t *ppd, - const char *colormodel, - const char *media, - const char *resolution, - cf_logfunc_t log, - void *ld); -extern cf_cmyk_t *ppdCMYKLoad(ppd_file_t *ppd, - const char *colormodel, - const char *media, - const char *resolution, - cf_logfunc_t log, - void *ld); - - -// -// C++ magic... -// - -# ifdef __cplusplus -} -# endif // __cplusplus -#endif // !_PPD_PPD_H_ diff --git a/ppd/ppdc-array.cxx b/ppd/ppdc-array.cxx deleted file mode 100644 index f6436c4d7..000000000 --- a/ppd/ppdc-array.cxx +++ /dev/null @@ -1,149 +0,0 @@ -// -// Array class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2019 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcArray::ppdcArray()' - Create a new array. -// - -ppdcArray::ppdcArray(ppdcArray *a) - : ppdcShared() -{ - PPDC_NEW; - - if (a) - { - count = a->count; - alloc = count; - - if (count) - { - // Make a copy of the array... - data = new ppdcShared *[count]; - - memcpy(data, a->data, (size_t)count * sizeof(ppdcShared *)); - - for (size_t i = 0; i < count; i ++) - data[i]->retain(); - } - else - data = 0; - } - else - { - count = 0; - alloc = 0; - data = 0; - } - - current = 0; -} - - -// -// 'ppdcArray::~ppdcArray()' - Destroy an array. -// - -ppdcArray::~ppdcArray() -{ - PPDC_DELETE; - - for (size_t i = 0; i < count; i ++) - data[i]->release(); - - if (alloc) - delete[] data; -} - - -// -// 'ppdcArray::add()' - Add an element to an array. -// - -void -ppdcArray::add(ppdcShared *d) -{ - ppdcShared **temp; - - - if (count >= alloc) - { - alloc += 10; - temp = new ppdcShared *[alloc]; - - memcpy(temp, data, (size_t)count * sizeof(ppdcShared *)); - - delete[] data; - data = temp; - } - - data[count++] = d; -} - - -// -// 'ppdcArray::first()' - Return the first element in the array. -// - -ppdcShared * -ppdcArray::first() -{ - current = 0; - - if (current >= count) - return (0); - else - return (data[current ++]); -} - - -// -// 'ppdcArray::next()' - Return the next element in the array. -// - -ppdcShared * -ppdcArray::next() -{ - if (current >= count) - return (0); - else - return (data[current ++]); -} - - -// -// 'ppdcArray::remove()' - Remove an element from the array. -// - -void -ppdcArray::remove(ppdcShared *d) // I - Data element -{ - size_t i; // Looping var - - - for (i = 0; i < count; i ++) - if (d == data[i]) - break; - - if (i >= count) - return; - - count --; - d->release(); - - if (i < count) - memmove(data + i, data + i + 1, (size_t)(count - i) * sizeof(ppdcShared *)); -} diff --git a/ppd/ppdc-attr.cxx b/ppd/ppdc-attr.cxx deleted file mode 100644 index 905ead59a..000000000 --- a/ppd/ppdc-attr.cxx +++ /dev/null @@ -1,51 +0,0 @@ -// -// Attribute class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcAttr::ppdcAttr()' - Create an attribute. -// - -ppdcAttr::ppdcAttr(const char *n, // I - Name - const char *s, // I - Spec string - const char *t, // I - Human-readable text - const char *v, // I - Value - bool loc) // I - Localize this attribute? - : ppdcShared() -{ - PPDC_NEW; - - name = new ppdcString(n); - selector = new ppdcString(s); - text = new ppdcString(t); - value = new ppdcString(v); - localizable = loc; -} - - -// -// 'ppdcAttr::~ppdcAttr()' - Destroy an attribute. -// - -ppdcAttr::~ppdcAttr() -{ - PPDC_DELETE; - - name->release(); - selector->release(); - text->release(); - value->release(); -} diff --git a/ppd/ppdc-catalog.cxx b/ppd/ppdc-catalog.cxx deleted file mode 100644 index 71cd73394..000000000 --- a/ppd/ppdc-catalog.cxx +++ /dev/null @@ -1,873 +0,0 @@ -// -// Shared message catalog class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2017 by Apple Inc. -// Copyright 2002-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// Character encodings... -// - -typedef enum -{ - PPDC_CS_AUTO, - PPDC_CS_UTF8, - PPDC_CS_UTF16BE, - PPDC_CS_UTF16LE -} ppdc_cs_t; - - -// -// Local functions... -// - -static int get_utf8(char *&ptr); -static int get_utf16(cups_file_t *fp, ppdc_cs_t &cs); -static int put_utf8(int ch, char *&ptr, char *end); -static int put_utf16(cups_file_t *fp, int ch); - - -// -// 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog. -// - -ppdcCatalog::ppdcCatalog(const char *l, // I - Locale - const char *f) // I - Message catalog file - : ppdcShared() -{ - PPDC_NEW; - - locale = new ppdcString(l); - filename = new ppdcString(f); - messages = new ppdcArray(); - - if (l && strcmp(l, "en")) - { - // Try loading the base messages for this locale... - char pofile[1024]; // Message catalog file - const char *c; - - // Determine CUPS datadir (usually /usr/share/cups) - if ((c = getenv("CUPS_DATADIR")) == NULL) - c = CUPS_DATADIR; - - snprintf(pofile, sizeof(pofile), "%s/locale/%s/cups_%s.po", c, l, l); - - if (load_messages(pofile) && strchr(l, '_')) - { - // Try the base locale... - char baseloc[3]; // Base locale... - - strncpy(baseloc, l, sizeof(baseloc) - 1); - snprintf(pofile, sizeof(pofile), "%s/locale/%s/cups_%s.po", c, - baseloc, baseloc); - - load_messages(pofile); - } - } - - if (f && *f) - load_messages(f); -} - - -// -// 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog. -// - -ppdcCatalog::~ppdcCatalog() -{ - PPDC_DELETE; - - locale->release(); - filename->release(); - messages->release(); -} - - -// -// 'ppdcCatalog::add_message()' - Add a new message. -// - -void -ppdcCatalog::add_message( - const char *id, // I - Message ID to add - const char *string) // I - Translation string -{ - ppdcMessage *m; // Current message - char text[1024]; // Text to translate - - - // Range check input... - if (!id) - return; - - // Verify that we don't already have the message ID... - for (m = (ppdcMessage *)messages->first(); - m; - m = (ppdcMessage *)messages->next()) - if (!strcmp(m->id->value, id)) - { - if (string) - { - m->string->release(); - m->string = new ppdcString(string); - } - return; - } - - // Add the message... - if (!string) - { - snprintf(text, sizeof(text), "TRANSLATE %s", id); - string = text; - } - - messages->add(new ppdcMessage(id, string)); -} - - -// -// 'ppdcCatalog::find_message()' - Find a message in a catalog... -// - -const char * // O - Message text -ppdcCatalog::find_message( - const char *id) // I - Message ID -{ - ppdcMessage *m; // Current message - - - if (!*id) - return (id); - - for (m = (ppdcMessage *)messages->first(); - m; - m = (ppdcMessage *)messages->next()) - if (!strcmp(m->id->value, id)) - return (m->string->value); - - return (id); -} - - -// -// 'ppdcCatalog::load_messages()' - Load messages from a .po file. -// - -int // O - 0 on success, -1 on failure -ppdcCatalog::load_messages( - const char *f) // I - Message catalog file -{ - cups_file_t *fp; // Message file - char line[4096], // Line buffer - *ptr, // Pointer into buffer - id[4096], // Translation ID - str[4096]; // Translation string - int linenum; // Line number - - - // Open the message catalog file... - if ((fp = cupsFileOpen(f, "r")) == NULL) - return (-1); - - if ((ptr = (char *)strrchr(f, '.')) == NULL) - goto unknown_load_format; - else if (!strcmp(ptr, ".strings")) - { - // - // Read messages in macOS ".strings" format, which are either UTF-8/UTF-16 - // text files of the format: - // - // "id" = "str"; - // - // Strings files can also contain C-style comments. - // - - ppdc_cs_t cs = PPDC_CS_AUTO; // Character set for file - int ch; // Current character from file - char *end; // End of buffer - - - id[0] = '\0'; - str[0] = '\0'; - ptr = NULL; - end = NULL; - - while ((ch = get_utf16(fp, cs)) != 0) - { - if (ptr) - { - if (ch == '\\') - { - if ((ch = get_utf16(fp, cs)) == 0) - break; - - if (ch == 'n') - ch = '\n'; - else if (ch == 't') - ch = '\t'; - } - else if (ch == '\"') - { - *ptr = '\0'; - ptr = NULL; - } - - if (ptr) - put_utf8(ch, ptr, end); - } - else if (ch == '/') - { - // Start of a comment? - if ((ch = get_utf16(fp, cs)) == 0) - break; - - if (ch == '*') - { - // Skip C comment... - int lastch = 0; - - while ((ch = get_utf16(fp, cs)) != 0) - { - if (ch == '/' && lastch == '*') - break; - - lastch = ch; - } - } - else if (ch == '/') - { - // Skip C++ comment... - while ((ch = get_utf16(fp, cs)) != 0) - if (ch == '\n') - break; - } - } - else if (ch == '\"') - { - // Start quoted string... - if (id[0]) - { - ptr = str; - end = str + sizeof(str) - 1; - } - else - { - ptr = id; - end = id + sizeof(id) - 1; - } - } - else if (ch == ';') - { - // Add string... - add_message(id, str); - id[0] = '\0'; - } - } - } - else if (!strcmp(ptr, ".po") || !strcmp(ptr, ".gz")) - { - // - // Read messages from the catalog file until EOF... - // - // The format is the GNU gettext .po format, which is fairly simple: - // - // msgid "some text" - // msgstr "localized text" - // - // The ID and localized text can span multiple lines using the form: - // - // msgid "" - // "some long text" - // msgstr "" - // "localized text spanning " - // "multiple lines" - // - - int which, // In msgid? - haveid, // Did we get a msgid string? - havestr; // Did we get a msgstr string? - - linenum = 0; - id[0] = '\0'; - str[0] = '\0'; - haveid = 0; - havestr = 0; - which = 0; - - while (cupsFileGets(fp, line, sizeof(line))) - { - linenum ++; - - // Skip blank and comment lines... - if (line[0] == '#' || !line[0]) - continue; - - // Strip the trailing quote... - if ((ptr = (char *)strrchr(line, '\"')) == NULL) - { - fprintf(stderr, - _("ppdc: Expected quoted string on line %d of %s.\n"), - linenum, f); - cupsFileClose(fp); - return (-1); - } - - *ptr = '\0'; - - // Find start of value... - if ((ptr = strchr(line, '\"')) == NULL) - { - fprintf(stderr, - _("ppdc: Expected quoted string on line %d of %s.\n"), - linenum, f); - cupsFileClose(fp); - return (-1); - } - - ptr ++; - - // Unquote the text... - char *sptr, *dptr; // Source/destination pointers - - for (sptr = ptr, dptr = ptr; *sptr;) - { - if (*sptr == '\\') - { - sptr ++; - if (isdigit(*sptr)) - { - *dptr = 0; - - while (isdigit(*sptr)) - { - *dptr = *dptr * 8 + *sptr - '0'; - sptr ++; - } - - dptr ++; - } - else - { - if (*sptr == 'n') - *dptr++ = '\n'; - else if (*sptr == 'r') - *dptr++ = '\r'; - else if (*sptr == 't') - *dptr++ = '\t'; - else - *dptr++ = *sptr; - - sptr ++; - } - } - else - *dptr++ = *sptr++; - } - - *dptr = '\0'; - - // Create or add to a message... - if (!strncmp(line, "msgid", 5)) - { - if (haveid && havestr) - add_message(id, str); - - strncpy(id, ptr, sizeof(id) - 1); - str[0] = '\0'; - haveid = 1; - havestr = 0; - which = 1; - } - else if (!strncmp(line, "msgstr", 6)) - { - if (!haveid) - { - fprintf(stderr, - _("ppdc: Need a msgid line before any " - "translation strings on line %d of %s.\n"), - linenum, f); - cupsFileClose(fp); - return (-1); - } - - strncpy(str, ptr, sizeof(str) - 1); - havestr = 1; - which = 2; - } - else if (line[0] == '\"' && which == 2) - strncat(str, ptr, sizeof(str) - 1); - else if (line[0] == '\"' && which == 1) - strncat(id, ptr, sizeof(id) - 1); - else - { - fprintf(stderr, _("ppdc: Unexpected text on line %d of %s.\n"), - linenum, f); - cupsFileClose(fp); - return (-1); - } - } - - if (haveid && havestr) - add_message(id, str); - } - else - goto unknown_load_format; - - // - // Close the file and return... - // - - cupsFileClose(fp); - - return (0); - - // - // Unknown format error... - // - - unknown_load_format: - - fprintf(stderr, - _("ppdc: Unknown message catalog format for \"%s\".\n"), f); - cupsFileClose(fp); - return (-1); -} - - -// -// 'ppdcCatalog::save_messages()' - Save the messages to a .po file. -// - -int // O - 0 on success, -1 on error -ppdcCatalog::save_messages( - const char *f) // I - File to save to -{ - cups_file_t *fp; // Message file - ppdcMessage *m; // Current message - char *ptr; // Pointer into string - int utf16; // Output UTF-16 .strings file? - int ch; // Current character - - - // Open the file... - if ((ptr = (char *)strrchr(f, '.')) == NULL) - return (-1); - - if (!strcmp(ptr, ".gz")) - fp = cupsFileOpen(f, "w9"); - else - fp = cupsFileOpen(f, "w"); - - if (!fp) - return (-1); - - // For .strings files, write a BOM for big-endian output... - utf16 = !strcmp(ptr, ".strings"); - - if (utf16) - put_utf16(fp, 0xfeff); - - // Loop through all of the messages... - for (m = (ppdcMessage *)messages->first(); - m; - m = (ppdcMessage *)messages->next()) - { - if (utf16) - { - put_utf16(fp, '\"'); - - ptr = m->id->value; - while ((ch = get_utf8(ptr)) != 0) - switch (ch) - { - case '\n' : - put_utf16(fp, '\\'); - put_utf16(fp, 'n'); - break; - case '\\' : - put_utf16(fp, '\\'); - put_utf16(fp, '\\'); - break; - case '\"' : - put_utf16(fp, '\\'); - put_utf16(fp, '\"'); - break; - default : - put_utf16(fp, ch); - break; - } - - put_utf16(fp, '\"'); - put_utf16(fp, ' '); - put_utf16(fp, '='); - put_utf16(fp, ' '); - put_utf16(fp, '\"'); - - ptr = m->string->value; - while ((ch = get_utf8(ptr)) != 0) - switch (ch) - { - case '\n' : - put_utf16(fp, '\\'); - put_utf16(fp, 'n'); - break; - case '\\' : - put_utf16(fp, '\\'); - put_utf16(fp, '\\'); - break; - case '\"' : - put_utf16(fp, '\\'); - put_utf16(fp, '\"'); - break; - default : - put_utf16(fp, ch); - break; - } - - put_utf16(fp, '\"'); - put_utf16(fp, ';'); - put_utf16(fp, '\n'); - } - else - { - cupsFilePuts(fp, "msgid \""); - for (ptr = m->id->value; *ptr; ptr ++) - switch (*ptr) - { - case '\n' : - cupsFilePuts(fp, "\\n"); - break; - case '\\' : - cupsFilePuts(fp, "\\\\"); - break; - case '\"' : - cupsFilePuts(fp, "\\\""); - break; - default : - cupsFilePutChar(fp, *ptr); - break; - } - cupsFilePuts(fp, "\"\n"); - - cupsFilePuts(fp, "msgstr \""); - for (ptr = m->string->value; *ptr; ptr ++) - switch (*ptr) - { - case '\n' : - cupsFilePuts(fp, "\\n"); - break; - case '\\' : - cupsFilePuts(fp, "\\\\"); - break; - case '\"' : - cupsFilePuts(fp, "\\\""); - break; - default : - cupsFilePutChar(fp, *ptr); - break; - } - cupsFilePuts(fp, "\"\n"); - - cupsFilePutChar(fp, '\n'); - } - } - - cupsFileClose(fp); - - return (0); -} - - -// -// 'get_utf8()' - Get a UTF-8 character. -// - -static int // O - Unicode character or 0 on EOF -get_utf8(char *&ptr) // IO - Pointer to character -{ - int ch; // Current character - - - if ((ch = *ptr++ & 255) < 0xc0) - return (ch); - - if ((ch & 0xe0) == 0xc0) - { - // Two-byte UTF-8... - if ((*ptr & 0xc0) != 0x80) - return (0); - - ch = ((ch & 0x1f) << 6) | (*ptr++ & 0x3f); - } - else if ((ch & 0xf0) == 0xe0) - { - // Three-byte UTF-8... - if ((*ptr & 0xc0) != 0x80) - return (0); - - ch = ((ch & 0x0f) << 6) | (*ptr++ & 0x3f); - - if ((*ptr & 0xc0) != 0x80) - return (0); - - ch = (ch << 6) | (*ptr++ & 0x3f); - } - else if ((ch & 0xf8) == 0xf0) - { - // Four-byte UTF-8... - if ((*ptr & 0xc0) != 0x80) - return (0); - - ch = ((ch & 0x07) << 6) | (*ptr++ & 0x3f); - - if ((*ptr & 0xc0) != 0x80) - return (0); - - ch = (ch << 6) | (*ptr++ & 0x3f); - - if ((*ptr & 0xc0) != 0x80) - return (0); - - ch = (ch << 6) | (*ptr++ & 0x3f); - } - - return (ch); -} - - -// -// 'get_utf16()' - Get a UTF-16 character... -// - -static int // O - Unicode character or 0 on EOF -get_utf16(cups_file_t *fp, // I - File to read from - ppdc_cs_t &cs) // IO - Character set of file -{ - int ch; // Current character - unsigned char buffer[3]; // Bytes - - - if (cs == PPDC_CS_AUTO) - { - // Get byte-order-mark, if present... - if (cupsFileRead(fp, (char *)buffer, 2) != 2) - return (0); - - if (buffer[0] == 0xfe && buffer[1] == 0xff) - { - // Big-endian UTF-16... - cs = PPDC_CS_UTF16BE; - - if (cupsFileRead(fp, (char *)buffer, 2) != 2) - return (0); - } - else if (buffer[0] == 0xff && buffer[1] == 0xfe) - { - // Little-endian UTF-16... - cs = PPDC_CS_UTF16LE; - - if (cupsFileRead(fp, (char *)buffer, 2) != 2) - return (0); - } - else if (buffer[0] == 0x00 && buffer[1] != 0x00) - { - // No BOM, assume big-endian UTF-16... - cs = PPDC_CS_UTF16BE; - } - else if (buffer[0] != 0x00 && buffer[1] == 0x00) - { - // No BOM, assume little-endian UTF-16... - cs = PPDC_CS_UTF16LE; - } - else - { - // No BOM, assume UTF-8... - cs = PPDC_CS_UTF8; - - cupsFileRewind(fp); - } - } - else if (cs != PPDC_CS_UTF8) - { - if (cupsFileRead(fp, (char *)buffer, 2) != 2) - return (0); - } - - if (cs == PPDC_CS_UTF8) - { - // UTF-8 character... - if ((ch = cupsFileGetChar(fp)) < 0) - return (0); - - if ((ch & 0xe0) == 0xc0) - { - // Two-byte UTF-8... - if (cupsFileRead(fp, (char *)buffer, 1) != 1) - return (0); - - if ((buffer[0] & 0xc0) != 0x80) - return (0); - - ch = ((ch & 0x1f) << 6) | (buffer[0] & 0x3f); - } - else if ((ch & 0xf0) == 0xe0) - { - // Three-byte UTF-8... - if (cupsFileRead(fp, (char *)buffer, 2) != 2) - return (0); - - if ((buffer[0] & 0xc0) != 0x80 || - (buffer[1] & 0xc0) != 0x80) - return (0); - - ch = ((((ch & 0x0f) << 6) | (buffer[0] & 0x3f)) << 6) | - (buffer[1] & 0x3f); - } - else if ((ch & 0xf8) == 0xf0) - { - // Four-byte UTF-8... - if (cupsFileRead(fp, (char *)buffer, 3) != 3) - return (0); - - if ((buffer[0] & 0xc0) != 0x80 || - (buffer[1] & 0xc0) != 0x80 || - (buffer[2] & 0xc0) != 0x80) - return (0); - - ch = ((((((ch & 0x07) << 6) | (buffer[0] & 0x3f)) << 6) | - (buffer[1] & 0x3f)) << 6) | (buffer[2] & 0x3f); - } - } - else - { - // UTF-16 character... - if (cs == PPDC_CS_UTF16BE) - ch = (buffer[0] << 8) | buffer[1]; - else - ch = (buffer[1] << 8) | buffer[0]; - - if (ch >= 0xd800 && ch <= 0xdbff) - { - // Handle multi-word encoding... - int lch; - - if (cupsFileRead(fp, (char *)buffer, 2) != 2) - return (0); - - if (cs == PPDC_CS_UTF16BE) - lch = (buffer[0] << 8) | buffer[1]; - else - lch = (buffer[1] << 8) | buffer[0]; - - if (lch < 0xdc00 || lch >= 0xdfff) - return (0); - - ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; - } - } - - return (ch); -} - - -// -// 'put_utf8()' - Add a UTF-8 character to a string. -// - -static int // O - 0 on success, -1 on failure -put_utf8(int ch, // I - Unicode character - char *&ptr, // IO - String pointer - char *end) // I - End of buffer -{ - if (ch < 0x80) - { - // One-byte ASCII... - if (ptr >= end) - return (-1); - - *ptr++ = (char)ch; - } - else if (ch < 0x800) - { - // Two-byte UTF-8... - if ((ptr + 1) >= end) - return (-1); - - *ptr++ = (char)(0xc0 | (ch >> 6)); - *ptr++ = (char)(0x80 | (ch & 0x3f)); - } - else if (ch < 0x10000) - { - // Three-byte UTF-8... - if ((ptr + 2) >= end) - return (-1); - - *ptr++ = (char)(0xe0 | (ch >> 12)); - *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f)); - *ptr++ = (char)(0x80 | (ch & 0x3f)); - } - else - { - // Four-byte UTF-8... - if ((ptr + 3) >= end) - return (-1); - - *ptr++ = (char)(0xf0 | (ch >> 18)); - *ptr++ = (char)(0x80 | ((ch >> 12) & 0x3f)); - *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f)); - *ptr++ = (char)(0x80 | (ch & 0x3f)); - } - - return (0); -} - - -// -// 'put_utf16()' - Write a UTF-16 character to a file. -// - -static int // O - 0 on success, -1 on failure -put_utf16(cups_file_t *fp, // I - File to write to - int ch) // I - Unicode character -{ - unsigned char buffer[4]; // Output buffer - - - if (ch < 0x10000) - { - // One-word UTF-16 big-endian... - buffer[0] = (unsigned char)(ch >> 8); - buffer[1] = (unsigned char)ch; - - if (cupsFileWrite(fp, (char *)buffer, 2) == 2) - return (0); - } - else - { - // Two-word UTF-16 big-endian... - ch -= 0x10000; - - buffer[0] = (unsigned char)(0xd8 | (ch >> 18)); - buffer[1] = (unsigned char)(ch >> 10); - buffer[2] = (unsigned char)(0xdc | ((ch >> 8) & 0x03)); - buffer[3] = (unsigned char)ch; - - if (cupsFileWrite(fp, (char *)buffer, 4) == 4) - return (0); - } - - return (-1); -} diff --git a/ppd/ppdc-choice.cxx b/ppd/ppdc-choice.cxx deleted file mode 100644 index 6ec2e7ddd..000000000 --- a/ppd/ppdc-choice.cxx +++ /dev/null @@ -1,46 +0,0 @@ -// -// Option choice class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcChoice::ppdcChoice()' - Create a new option choice. -// - -ppdcChoice::ppdcChoice(const char *n, // I - Name of choice - const char *t, // I - Text of choice - const char *c) // I - Code of choice - : ppdcShared() -{ - PPDC_NEW; - - name = new ppdcString(n); - text = new ppdcString(t); - code = new ppdcString(c); -} - - -// -// 'ppdcChoice::~ppdcChoice()' - Destroy an option choice. -// - -ppdcChoice::~ppdcChoice() -{ - PPDC_DELETE; - - name->release(); - text->release(); - code->release(); -} diff --git a/ppd/ppdc-constraint.cxx b/ppd/ppdc-constraint.cxx deleted file mode 100644 index b5abc8f2f..000000000 --- a/ppd/ppdc-constraint.cxx +++ /dev/null @@ -1,49 +0,0 @@ -// -// Contraint class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcConstraint::ppdcConstraint()' - Create a constraint. -// - -ppdcConstraint::ppdcConstraint(const char *o1, // I - First option - const char *c1, // I - First choice - const char *o2, // I - Second option - const char *c2) // I - Second choice - : ppdcShared() -{ - PPDC_NEW; - - option1 = new ppdcString(o1); - choice1 = new ppdcString(c1); - option2 = new ppdcString(o2); - choice2 = new ppdcString(c2); -} - - -// -// 'ppdcConstraint::~ppdcConstraint()' - Destroy a constraint. -// - -ppdcConstraint::~ppdcConstraint() -{ - PPDC_DELETE; - - option1->release(); - choice1->release(); - option2->release(); - choice2->release(); -} diff --git a/ppd/ppdc-driver.cxx b/ppd/ppdc-driver.cxx deleted file mode 100644 index 3dcfb6067..000000000 --- a/ppd/ppdc-driver.cxx +++ /dev/null @@ -1,1324 +0,0 @@ -// -// PPD file compiler definitions for the CUPS PPD Compiler in libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 2002-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include "string-private.h" - - -// -// 'ppdcDriver::ppdcDriver()' - Create a new printer driver. -// - -ppdcDriver::ppdcDriver(ppdcDriver *d) // I - Printer driver template - : ppdcShared() -{ - ppdcGroup *g; // Current group - - - PPDC_NEW; - - if (d) - { - // Bump the use count of any strings we inherit... - if (d->manufacturer) - d->manufacturer->retain(); - if (d->version) - d->version->retain(); - if (d->default_font) - d->default_font->retain(); - if (d->default_size) - d->default_size->retain(); - if (d->custom_size_code) - d->custom_size_code->retain(); - - // Copy all of the data from the driver template... - copyright = new ppdcArray(d->copyright); - manufacturer = d->manufacturer; - model_name = 0; - file_name = 0; - pc_file_name = 0; - type = d->type; - version = d->version; - model_number = d->model_number; - manual_copies = d->manual_copies; - color_device = d->color_device; - throughput = d->throughput; - attrs = new ppdcArray(d->attrs); - constraints = new ppdcArray(d->constraints); - filters = new ppdcArray(d->filters); - fonts = new ppdcArray(d->fonts); - profiles = new ppdcArray(d->profiles); - sizes = new ppdcArray(d->sizes); - default_font = d->default_font; - default_size = d->default_size; - variable_paper_size = d->variable_paper_size; - custom_size_code = d->custom_size_code; - left_margin = d->left_margin; - bottom_margin = d->bottom_margin; - right_margin = d->right_margin; - top_margin = d->top_margin; - max_width = d->max_width; - max_length = d->max_length; - min_width = d->min_width; - min_length = d->min_length; - - // Then copy the groups manually, since we want separate copies - // of the groups and options... - groups = new ppdcArray(); - - for (g = (ppdcGroup *)d->groups->first(); g; g = (ppdcGroup *)d->groups->next()) - groups->add(new ppdcGroup(g)); - } - else - { - // Zero all of the data in the driver... - copyright = new ppdcArray(); - manufacturer = 0; - model_name = 0; - file_name = 0; - pc_file_name = 0; - version = 0; - type = PPDC_DRIVER_CUSTOM; - model_number = 0; - manual_copies = 0; - color_device = 0; - throughput = 1; - attrs = new ppdcArray(); - constraints = new ppdcArray(); - fonts = new ppdcArray(); - filters = new ppdcArray(); - groups = new ppdcArray(); - profiles = new ppdcArray(); - sizes = new ppdcArray(); - default_font = 0; - default_size = 0; - variable_paper_size = 0; - custom_size_code = 0; - left_margin = 0; - bottom_margin = 0; - right_margin = 0; - top_margin = 0; - max_width = 0; - max_length = 0; - min_width = 0; - min_length = 0; - } -} - - -// -// 'ppdcDriver::~ppdcDriver()' - Destroy a printer driver. -// - -ppdcDriver::~ppdcDriver() -{ - PPDC_DELETE; - - copyright->release(); - - if (manufacturer) - manufacturer->release(); - if (model_name) - model_name->release(); - if (file_name) - file_name->release(); - if (pc_file_name) - pc_file_name->release(); - if (version) - version->release(); - if (default_font) - default_font->release(); - if (default_size) - default_size->release(); - if (custom_size_code) - custom_size_code->release(); - - attrs->release(); - constraints->release(); - filters->release(); - fonts->release(); - groups->release(); - profiles->release(); - sizes->release(); -} - - -// -// 'ppdcDriver::find_attr()' - Find an attribute. -// - -ppdcAttr * // O - Attribute or NULL -ppdcDriver::find_attr(const char *k, // I - Keyword string - const char *s) // I - Spec string -{ - ppdcAttr *a; // Current attribute - - - for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next()) - if (!strcmp(a->name->value, k) && - ((!s && (!a->selector->value || !a->selector->value[0])) || - (s && a->selector->value && !strcmp(a->selector->value, s)))) - return (a); - - return (NULL); -} - - -// -// 'ppdcDriver::find_group()' - Find a group. -// - -ppdcGroup * // O - Matching group or NULL -ppdcDriver::find_group(const char *n) // I - Group name -{ - ppdcGroup *g; // Current group - - - for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next()) - if (!strcasecmp(n, g->name->value)) - return (g); - - return (0); -} - - -// -// 'ppdcDriver::find_option()' - Find an option. -// - -ppdcOption * // O - Matching option or NULL -ppdcDriver::find_option(const char *n) // I - Option name -{ - return (find_option_group(n, (ppdcGroup **)0)); -} - - -// -// 'ppdcDriver::find_option_group()' - Find an option and its group. -// - -ppdcOption * // O - Matching option or NULL -ppdcDriver::find_option_group( - const char *n, // I - Option name - ppdcGroup **mg) // O - Matching group or NULL -{ - ppdcGroup *g; // Current group - ppdcOption *o; // Current option - - - for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next()) - for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next()) - if (!strcasecmp(n, o->name->value)) - { - if (mg) - *mg = g; - - return (o); - } - - if (mg) - *mg = (ppdcGroup *)0; - - return (0); -} - - -// -// 'ppdcDriver::set_custom_size_code()' - Set the custom page size code. -// - -void -ppdcDriver::set_custom_size_code( - const char *c) // I - CustomPageSize code -{ - if (custom_size_code) - custom_size_code->release(); - - custom_size_code = new ppdcString(c); -} - - -// -// 'ppdcDriver::set_default_font()' - Set the default font name. -// - -void -ppdcDriver::set_default_font( - ppdcFont *f) // I - Font -{ - if (default_font) - default_font->release(); - - if (f) - { - f->name->retain(); - default_font = f->name; - } - else - default_font = 0; -} - - -// -// 'ppdcDriver::set_default_size()' - Set the default size name. -// - -void -ppdcDriver::set_default_size( - ppdcMediaSize *m) // I - Media size -{ - if (default_size) - default_size->release(); - - if (m) - { - m->name->retain(); - default_size = m->name; - } - else - default_size = 0; -} - - -// -// 'ppdcDriver::set_file_name()' - Set the full filename. -// - -void -ppdcDriver::set_file_name(const char *f)// I - Filename -{ - if (file_name) - file_name->release(); - - file_name = new ppdcString(f); -} - - -// -// 'ppdcDriver::set_manufacturer()' - Set the manufacturer name. -// - -void -ppdcDriver::set_manufacturer( - const char *m) // I - Model name -{ - if (manufacturer) - manufacturer->release(); - - manufacturer = new ppdcString(m); -} - - -// -// 'ppdcDriver::set_model_name()' - Set the model name. -// - -void -ppdcDriver::set_model_name( - const char *m) // I - Model name -{ - if (model_name) - model_name->release(); - - model_name = new ppdcString(m); -} - - -// -// 'ppdcDriver::set_pc_file_name()' - Set the PC filename. -// - -void -ppdcDriver::set_pc_file_name( - const char *f) // I - Filename -{ - if (pc_file_name) - pc_file_name->release(); - - pc_file_name = new ppdcString(f); -} - - -// -// 'ppdcDriver::set_version()' - Set the version string. -// - -void -ppdcDriver::set_version(const char *v) // I - Version -{ - if (version) - version->release(); - - version = new ppdcString(v); -} - - -// -// 'ppdcDriver::write_ppd_file()' - Write a PPD file... -// - -int // O - 0 on success, -1 on failure -ppdcDriver::write_ppd_file( - cups_file_t *fp, // I - PPD file - ppdcCatalog *catalog, // I - Message catalog - ppdcArray *locales, // I - Additional languages to add - ppdcSource *src, // I - Driver source - ppdcLineEnding le) // I - Line endings to use -{ - bool delete_cat; // Delete the catalog when we are done? - char query[42], // Query attribute - custom[42]; // Custom attribute - ppdcString *s; // Copyright string - ppdcGroup *g; // Current group - ppdcOption *o; // Current option - ppdcChoice *c; // Current choice - ppdcMediaSize *m; // Current media size - ppdcProfile *p; // Current color profile - ppdcFilter *f; // Current filter - ppdcFont *fn, // Current font - *bfn; // Current base font - ppdcConstraint *cn; // Current constraint - ppdcAttr *a; // Current attribute - const char *lf; // Linefeed character to use - - - // If we don't have a message catalog, use an empty (English) one... - if (!catalog) - { - catalog = new ppdcCatalog(NULL); - delete_cat = true; - } - else - delete_cat = false; - - // Figure out the end-of-line string... - if (le == PPDC_LFONLY) - lf = "\n"; - else if (le == PPDC_CRONLY) - lf = "\r"; - else - lf = "\r\n"; - - // Write the standard header stuff... - cupsFilePrintf(fp, "*PPD-Adobe: \"4.3\"%s", lf); - cupsFilePrintf(fp, "*%%%%%%%% PPD file for %s with CUPS.%s", - model_name->value, lf); - cupsFilePrintf(fp, - "*%%%%%%%% Created by the PPD Compiler of cups-filters " - PACKAGE_VERSION ".%s", lf); - for (s = (ppdcString *)copyright->first(); - s; - s = (ppdcString *)copyright->next()) - cupsFilePrintf(fp, "*%% %s%s", catalog->find_message(s->value), lf); - cupsFilePrintf(fp, "*FormatVersion: \"4.3\"%s", lf); - cupsFilePrintf(fp, "*FileVersion: \"%s\"%s", version->value, lf); - - a = find_attr("LanguageVersion", NULL); - cupsFilePrintf(fp, "*LanguageVersion: %s%s", - catalog->find_message(a ? a->value->value : "English"), lf); - - a = find_attr("LanguageEncoding", NULL); - cupsFilePrintf(fp, "*LanguageEncoding: %s%s", - catalog->find_message(a ? a->value->value : "ISOLatin1"), lf); - - cupsFilePrintf(fp, "*PCFileName: \"%s\"%s", pc_file_name->value, lf); - - for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next()) - if (!strcmp(a->name->value, "Product")) - break; - - if (a) - { - for (; a; a = (ppdcAttr *)attrs->next()) - if (!strcmp(a->name->value, "Product")) - cupsFilePrintf(fp, "*Product: \"%s\"%s", a->value->value, lf); - } - else - cupsFilePrintf(fp, "*Product: \"(%s)\"%s", model_name->value, lf); - - cupsFilePrintf(fp, "*Manufacturer: \"%s\"%s", - catalog->find_message(manufacturer->value), lf); - - if ((a = find_attr("ModelName", NULL)) != NULL) - cupsFilePrintf(fp, "*ModelName: \"%s\"%s", - catalog->find_message(a->value->value), lf); - else if (strncasecmp(model_name->value, manufacturer->value, - strlen(manufacturer->value))) - cupsFilePrintf(fp, "*ModelName: \"%s %s\"%s", - catalog->find_message(manufacturer->value), - catalog->find_message(model_name->value), lf); - else - cupsFilePrintf(fp, "*ModelName: \"%s\"%s", - catalog->find_message(model_name->value), lf); - - if ((a = find_attr("ShortNickName", NULL)) != NULL) - cupsFilePrintf(fp, "*ShortNickName: \"%s\"%s", - catalog->find_message(a->value->value), lf); - else if (strncasecmp(model_name->value, manufacturer->value, - strlen(manufacturer->value))) - cupsFilePrintf(fp, "*ShortNickName: \"%s %s\"%s", - catalog->find_message(manufacturer->value), - catalog->find_message(model_name->value), lf); - else - cupsFilePrintf(fp, "*ShortNickName: \"%s\"%s", - catalog->find_message(model_name->value), lf); - - if ((a = find_attr("NickName", NULL)) != NULL) - cupsFilePrintf(fp, "*NickName: \"%s\"%s", - catalog->find_message(a->value->value), lf); - else if (strncasecmp(model_name->value, manufacturer->value, - strlen(manufacturer->value))) - cupsFilePrintf(fp, "*NickName: \"%s %s, %s\"%s", - catalog->find_message(manufacturer->value), - catalog->find_message(model_name->value), version->value, - lf); - else - cupsFilePrintf(fp, "*NickName: \"%s, %s\"%s", - catalog->find_message(model_name->value), version->value, - lf); - - for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next()) - if (!strcmp(a->name->value, "PSVersion")) - break; - - if (a) - { - for (; a; a = (ppdcAttr *)attrs->next()) - if (!strcmp(a->name->value, "PSVersion")) - cupsFilePrintf(fp, "*PSVersion: \"%s\"%s", a->value->value, lf); - } - else - cupsFilePrintf(fp, "*PSVersion: \"(3010.000) 0\"%s", lf); - - if ((a = find_attr("LanguageLevel", NULL)) != NULL) - cupsFilePrintf(fp, "*LanguageLevel: \"%s\"%s", a->value->value, lf); - else - cupsFilePrintf(fp, "*LanguageLevel: \"3\"%s", lf); - - cupsFilePrintf(fp, "*ColorDevice: %s%s", color_device ? "True" : "False", lf); - - if ((a = find_attr("DefaultColorSpace", NULL)) != NULL) - cupsFilePrintf(fp, "*DefaultColorSpace: %s%s", a->value->value, lf); - else - cupsFilePrintf(fp, "*DefaultColorSpace: %s%s", - color_device ? "RGB" : "Gray", lf); - - if ((a = find_attr("FileSystem", NULL)) != NULL) - cupsFilePrintf(fp, "*FileSystem: %s%s", a->value->value, lf); - else - cupsFilePrintf(fp, "*FileSystem: False%s", lf); - - cupsFilePrintf(fp, "*Throughput: \"%d\"%s", throughput, lf); - - if ((a = find_attr("LandscapeOrientation", NULL)) != NULL) - cupsFilePrintf(fp, "*LandscapeOrientation: %s%s", a->value->value, lf); - else - cupsFilePrintf(fp, "*LandscapeOrientation: Plus90%s", lf); - - if ((a = find_attr("TTRasterizer", NULL)) != NULL) - cupsFilePrintf(fp, "*TTRasterizer: %s%s", a->value->value, lf); - else if (type != PPDC_DRIVER_PS) - cupsFilePrintf(fp, "*TTRasterizer: Type42%s", lf); - - struct lconv *loc = localeconv(); - - if (attrs->count) - { - // Write driver-defined attributes... - cupsFilePrintf(fp, "*%% Driver-defined attributes...%s", lf); - for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next()) - { - if (!strcmp(a->name->value, "Product") || - !strcmp(a->name->value, "PSVersion") || - !strcmp(a->name->value, "LanguageLevel") || - !strcmp(a->name->value, "DefaultColorSpace") || - !strcmp(a->name->value, "FileSystem") || - !strcmp(a->name->value, "LandscapeOrientation") || - !strcmp(a->name->value, "TTRasterizer") || - !strcmp(a->name->value, "LanguageVersion") || - !strcmp(a->name->value, "LanguageEncoding") || - !strcmp(a->name->value, "ModelName") || - !strcmp(a->name->value, "NickName") || - !strcmp(a->name->value, "ShortNickName") || - !strcmp(a->name->value, "cupsVersion")) - continue; - - if (a->name->value[0] == '?' && - (find_option(a->name->value + 1) || - !strcmp(a->name->value, "?ImageableArea") || - !strcmp(a->name->value, "?PageRegion") || - !strcmp(a->name->value, "?PageSize") || - !strcmp(a->name->value, "?PaperDimension"))) - continue; - - if (!strncmp(a->name->value, "Custom", 6) && - find_option(a->name->value + 6)) - continue; - - if (!strncmp(a->name->value, "ParamCustom", 11) && - find_option(a->name->value + 11)) - continue; - - if (!a->selector->value || !a->selector->value[0]) - cupsFilePrintf(fp, "*%s", a->name->value); - else if (!a->text->value || !a->text->value[0]) - cupsFilePrintf(fp, "*%s %s", a->name->value, a->selector->value); - else - cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value, - a->text->value); - - if (strcmp(a->value->value, "False") && - strcmp(a->value->value, "True") && - strcmp(a->name->value, "1284Modes") && - strcmp(a->name->value, "InkName") && - strcmp(a->name->value, "PageStackOrder") && - strncmp(a->name->value, "ParamCustom", 11) && - strcmp(a->name->value, "Protocols") && - strcmp(a->name->value, "ReferencePunch") && - strncmp(a->name->value, "Default", 7)) - { - cupsFilePrintf(fp, ": \"%s\"%s", a->value->value, lf); - - if (strchr(a->value->value, '\n') || strchr(a->value->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - else - cupsFilePrintf(fp, ": %s%s", a->value->value, lf); - } - } - - if (type != PPDC_DRIVER_PS || filters->count) - { - if ((a = find_attr("cupsVersion", NULL)) != NULL) - cupsFilePrintf(fp, "*cupsVersion: %s%s", a->value->value, lf); - else - cupsFilePrintf(fp, "*cupsVersion: %s%s", PACKAGE_VERSION, lf); - cupsFilePrintf(fp, "*cupsModelNumber: %d%s", model_number, lf); - cupsFilePrintf(fp, "*cupsManualCopies: %s%s", - manual_copies ? "True" : "False", lf); - - if (filters->count) - { - for (f = (ppdcFilter *)filters->first(); - f; - f = (ppdcFilter *)filters->next()) - cupsFilePrintf(fp, "*cupsFilter: \"%s %d %s\"%s", f->mime_type->value, - f->cost, f->program->value, lf); - } - else - { - switch (type) - { - case PPDC_DRIVER_LABEL : - cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 " - "rastertolabel\"%s", lf); - break; - - case PPDC_DRIVER_EPSON : - cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 " - "rastertoepson\"%s", lf); - break; - - case PPDC_DRIVER_ESCP : - cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-command 50 " - "commandtoescpx\"%s", lf); - cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 " - "rastertoescpx\"%s", lf); - break; - - case PPDC_DRIVER_HP : - cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 " - "rastertohp\"%s", lf); - break; - - case PPDC_DRIVER_PCL : - cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-command 50 " - "commandtopclx\"%s", lf); - cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 " - "rastertopclx\"%s", lf); - break; - - default : - break; - } - } - - for (p = (ppdcProfile *)profiles->first(); - p; - p = (ppdcProfile *)profiles->next()) - { - char density[255], gamma[255], profile[9][255]; - - _ppdStrFormatd(density, density + sizeof(density), p->density, loc); - _ppdStrFormatd(gamma, gamma + sizeof(gamma), p->gamma, loc); - - for (int i = 0; i < 9; i ++) - _ppdStrFormatd(profile[i], profile[i] + sizeof(profile[0]), - p->profile[i], loc); - - cupsFilePrintf(fp, - "*cupsColorProfile %s/%s: \"%s %s %s %s %s %s %s %s %s %s " - "%s\"%s", p->resolution->value, p->media_type->value, - density, gamma, profile[0], profile[1], profile[2], - profile[3], profile[4], profile[5], profile[6], profile[7], - profile[8], lf); - } - } - - if (locales) - { - // Add localizations for additional languages... - ppdcString *locale; // Locale name - ppdcCatalog *locatalog; // Message catalog for locale - - - // Write the list of languages... - cupsFilePrintf(fp, "*cupsLanguages: \"en"); - - for (locale = (ppdcString *)locales->first(); - locale; - locale = (ppdcString *)locales->next()) - { - // Skip (US) English... - if (!strcmp(locale->value, "en") || !strcmp(locale->value, "en_US")) - continue; - - // See if we have a po file for this language... - if (!src->find_po(locale->value)) - { - // No, see if we can use the base file? - locatalog = new ppdcCatalog(locale->value); - - if (locatalog->messages->count == 0) - { - // No, skip this one... - fprintf(stderr, - _("ppdc: No message catalog provided for locale " - "%s.\n"), locale->value); - delete locatalog; - continue; - } - - // Add the base file to the list... - src->po_files->add(locatalog); - } - - cupsFilePrintf(fp, " %s", locale->value); - } - - cupsFilePrintf(fp, "\"%s", lf); - } - - for (cn = (ppdcConstraint *)constraints->first(); - cn; - cn = (ppdcConstraint *)constraints->next()) - { - // First constrain 1 against 2... - if (!strncmp(cn->option1->value, "*Custom", 7) || - !strncmp(cn->option2->value, "*Custom", 7)) - cupsFilePuts(fp, "*NonUIConstraints: "); - else - cupsFilePuts(fp, "*UIConstraints: "); - - if (cn->option1->value[0] != '*') - cupsFilePutChar(fp, '*'); - - cupsFilePuts(fp, cn->option1->value); - - if (cn->choice1->value) - cupsFilePrintf(fp, " %s", cn->choice1->value); - - cupsFilePutChar(fp, ' '); - - if (cn->option2->value[0] != '*') - cupsFilePutChar(fp, '*'); - - cupsFilePuts(fp, cn->option2->value); - - if (cn->choice2->value) - cupsFilePrintf(fp, " %s", cn->choice2->value); - - cupsFilePuts(fp, lf); - - // Then constrain 2 against 1... - if (!strncmp(cn->option1->value, "*Custom", 7) || - !strncmp(cn->option2->value, "*Custom", 7)) - cupsFilePuts(fp, "*NonUIConstraints: "); - else - cupsFilePuts(fp, "*UIConstraints: "); - - if (cn->option2->value[0] != '*') - cupsFilePutChar(fp, '*'); - - cupsFilePuts(fp, cn->option2->value); - - if (cn->choice2->value) - cupsFilePrintf(fp, " %s", cn->choice2->value); - - cupsFilePutChar(fp, ' '); - - if (cn->option1->value[0] != '*') - cupsFilePutChar(fp, '*'); - - cupsFilePuts(fp, cn->option1->value); - - if (cn->choice1->value) - cupsFilePrintf(fp, " %s", cn->choice1->value); - - cupsFilePuts(fp, lf); - } - - // PageSize option... - cupsFilePrintf(fp, "*OpenUI *PageSize/Media Size: PickOne%s", lf); - cupsFilePrintf(fp, "*OrderDependency: 10 AnySetup *PageSize%s", lf); - cupsFilePrintf(fp, "*DefaultPageSize: %s%s", - default_size ? default_size->value : "Letter", lf); - - for (m = (ppdcMediaSize *)sizes->first(); - m; - m = (ppdcMediaSize *)sizes->next()) - if (m->size_code->value) - { - cupsFilePrintf(fp, "*PageSize %s/%s: \"%s\"%s", - m->name->value, catalog->find_message(m->text->value), - m->size_code->value, lf); - - if (strchr(m->size_code->value, '\n') || - strchr(m->size_code->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - else - cupsFilePrintf(fp, - "*PageSize %s/%s: \"<>setpagedevice\"%s", - m->name->value, catalog->find_message(m->text->value), - m->width, m->length, lf); - - if ((a = find_attr("?PageSize", NULL)) != NULL) - { - cupsFilePrintf(fp, "*?PageSize: \"%s\"%s", a->value->value, lf); - - if (strchr(a->value->value, '\n') || - strchr(a->value->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - - cupsFilePrintf(fp, "*CloseUI: *PageSize%s", lf); - - // PageRegion option... - cupsFilePrintf(fp, "*OpenUI *PageRegion/Media Size: PickOne%s", lf); - cupsFilePrintf(fp, "*OrderDependency: 10 AnySetup *PageRegion%s", lf); - cupsFilePrintf(fp, "*DefaultPageRegion: %s%s", - default_size ? default_size->value : "Letter", lf); - - for (m = (ppdcMediaSize *)sizes->first(); - m; - m = (ppdcMediaSize *)sizes->next()) - if (m->region_code->value) - { - cupsFilePrintf(fp, "*PageRegion %s/%s: \"%s\"%s", - m->name->value, catalog->find_message(m->text->value), - m->region_code->value, lf); - - if (strchr(m->region_code->value, '\n') || - strchr(m->region_code->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - else - cupsFilePrintf(fp, - "*PageRegion %s/%s: \"<>setpagedevice\"%s", - m->name->value, catalog->find_message(m->text->value), - m->width, m->length, lf); - - if ((a = find_attr("?PageRegion", NULL)) != NULL) - { - cupsFilePrintf(fp, "*?PageRegion: \"%s\"%s", a->value->value, lf); - - if (strchr(a->value->value, '\n') || - strchr(a->value->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - - cupsFilePrintf(fp, "*CloseUI: *PageRegion%s", lf); - - // ImageableArea info... - cupsFilePrintf(fp, "*DefaultImageableArea: %s%s", - default_size ? default_size->value : "Letter", lf); - - char left[255], right[255], bottom[255], top[255]; - - for (m = (ppdcMediaSize *)sizes->first(); - m; - m = (ppdcMediaSize *)sizes->next()) - { - _ppdStrFormatd(left, left + sizeof(left), m->left, loc); - _ppdStrFormatd(bottom, bottom + sizeof(bottom), m->bottom, loc); - _ppdStrFormatd(right, right + sizeof(right), m->width - m->right, loc); - _ppdStrFormatd(top, top + sizeof(top), m->length - m->top, loc); - - cupsFilePrintf(fp, "*ImageableArea %s/%s: \"%s %s %s %s\"%s", - m->name->value, catalog->find_message(m->text->value), - left, bottom, right, top, lf); - } - - if ((a = find_attr("?ImageableArea", NULL)) != NULL) - { - cupsFilePrintf(fp, "*?ImageableArea: \"%s\"%s", a->value->value, lf); - - if (strchr(a->value->value, '\n') || - strchr(a->value->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - - // PaperDimension info... - cupsFilePrintf(fp, "*DefaultPaperDimension: %s%s", - default_size ? default_size->value : "Letter", lf); - - char width[255], length[255]; - - for (m = (ppdcMediaSize *)sizes->first(); - m; - m = (ppdcMediaSize *)sizes->next()) - { - _ppdStrFormatd(width, width + sizeof(width), m->width, loc); - _ppdStrFormatd(length, length + sizeof(length), m->length, loc); - - cupsFilePrintf(fp, "*PaperDimension %s/%s: \"%s %s\"%s", - m->name->value, catalog->find_message(m->text->value), - width, length, lf); - } - - if ((a = find_attr("?PaperDimension", NULL)) != NULL) - { - cupsFilePrintf(fp, "*?PaperDimension: \"%s\"%s", a->value->value, lf); - - if (strchr(a->value->value, '\n') || - strchr(a->value->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - - // Custom size support... - if (variable_paper_size) - { - _ppdStrFormatd(width, width + sizeof(width), max_width, loc); - _ppdStrFormatd(length, length + sizeof(length), max_length, loc); - - _ppdStrFormatd(left, left + sizeof(left), left_margin, loc); - _ppdStrFormatd(bottom, bottom + sizeof(bottom), bottom_margin, loc); - _ppdStrFormatd(right, right + sizeof(right), right_margin, loc); - _ppdStrFormatd(top, top + sizeof(top), top_margin, loc); - - cupsFilePrintf(fp, "*MaxMediaWidth: \"%s\"%s", width, lf); - cupsFilePrintf(fp, "*MaxMediaHeight: \"%s\"%s", length, lf); - cupsFilePrintf(fp, "*HWMargins: %s %s %s %s%s", left, bottom, right, top, - lf); - - if (custom_size_code && custom_size_code->value) - { - cupsFilePrintf(fp, "*CustomPageSize True: \"%s\"%s", - custom_size_code->value, lf); - - if (strchr(custom_size_code->value, '\n') || - strchr(custom_size_code->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - else - cupsFilePrintf(fp, - "*CustomPageSize True: \"pop pop pop <>setpagedevice\"%s", lf); - - if ((a = find_attr("ParamCustomPageSize", "Width")) != NULL) - cupsFilePrintf(fp, "*ParamCustomPageSize Width: %s%s", a->value->value, - lf); - else - { - char width0[255]; - - _ppdStrFormatd(width0, width0 + sizeof(width0), min_width, loc); - _ppdStrFormatd(width, width + sizeof(width), max_width, loc); - - cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s%s", - width0, width, lf); - } - - if ((a = find_attr("ParamCustomPageSize", "Height")) != NULL) - cupsFilePrintf(fp, "*ParamCustomPageSize Height: %s%s", a->value->value, - lf); - else - { - char length0[255]; - - _ppdStrFormatd(length0, length0 + sizeof(length0), min_length, loc); - _ppdStrFormatd(length, length + sizeof(length), max_length, loc); - - cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s%s", - length0, length, lf); - } - - if ((a = find_attr("ParamCustomPageSize", "WidthOffset")) != NULL) - cupsFilePrintf(fp, "*ParamCustomPageSize WidthOffset: %s%s", - a->value->value, lf); - else - cupsFilePrintf(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0%s", lf); - - if ((a = find_attr("ParamCustomPageSize", "HeightOffset")) != NULL) - cupsFilePrintf(fp, "*ParamCustomPageSize HeightOffset: %s%s", - a->value->value, lf); - else - cupsFilePrintf(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0%s", lf); - - if ((a = find_attr("ParamCustomPageSize", "Orientation")) != NULL) - cupsFilePrintf(fp, "*ParamCustomPageSize Orientation: %s%s", - a->value->value, lf); - else - cupsFilePrintf(fp, "*ParamCustomPageSize Orientation: 5 int 0 0%s", lf); - } - - // All other options... - for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next()) - { - if (!g->options->count) - continue; - - if (strcasecmp(g->name->value, "General")) - cupsFilePrintf(fp, "*OpenGroup: %s/%s%s", g->name->value, - catalog->find_message(g->text->value), lf); - - for (o = (ppdcOption *)g->options->first(); - o; - o = (ppdcOption *)g->options->next()) - { - if (!o->choices->count) - continue; - - if (o->section == PPDC_SECTION_JCL) - { - if (!o->text->value) - cupsFilePrintf(fp, "*JCLOpenUI *%s/%s: ", o->name->value, - catalog->find_message(o->name->value)); - else - cupsFilePrintf(fp, "*JCLOpenUI *%s/%s: ", o->name->value, - catalog->find_message(o->text->value)); - } - else if (!o->text->value) - cupsFilePrintf(fp, "*OpenUI *%s/%s: ", o->name->value, - catalog->find_message(o->name->value)); - else - cupsFilePrintf(fp, "*OpenUI *%s/%s: ", o->name->value, - catalog->find_message(o->text->value)); - - switch (o->type) - { - case PPDC_BOOLEAN : - cupsFilePrintf(fp, "Boolean%s", lf); - break; - default : - cupsFilePrintf(fp, "PickOne%s", lf); - break; - case PPDC_PICKMANY : - cupsFilePrintf(fp, "PickMany%s", lf); - break; - } - - char order[255]; - _ppdStrFormatd(order, order + sizeof(order), o->order, loc); - - cupsFilePrintf(fp, "*OrderDependency: %s ", order); - switch (o->section) - { - default : - cupsFilePrintf(fp, "AnySetup"); - break; - case PPDC_SECTION_DOCUMENT : - cupsFilePrintf(fp, "DocumentSetup"); - break; - case PPDC_SECTION_EXIT : - cupsFilePrintf(fp, "ExitServer"); - break; - case PPDC_SECTION_JCL : - cupsFilePrintf(fp, "JCLSetup"); - break; - case PPDC_SECTION_PAGE : - cupsFilePrintf(fp, "PageSetup"); - break; - case PPDC_SECTION_PROLOG : - cupsFilePrintf(fp, "Prolog"); - break; - } - - cupsFilePrintf(fp, " *%s%s", o->name->value, lf); - - if (o->defchoice) - { - // Use the programmer-supplied default... - cupsFilePrintf(fp, "*Default%s: %s%s", o->name->value, - o->defchoice->value, lf); - } - else - { - // Make the first choice the default... - c = (ppdcChoice *)o->choices->first(); - cupsFilePrintf(fp, "*Default%s: %s%s", o->name->value, c->name->value, - lf); - } - - for (c = (ppdcChoice *)o->choices->first(); - c; - c = (ppdcChoice *)o->choices->next()) - { - // Write this choice... - if (!c->text->value) - cupsFilePrintf(fp, "*%s %s/%s: \"%s\"%s", o->name->value, - c->name->value, catalog->find_message(c->name->value), - c->code->value, lf); - else - cupsFilePrintf(fp, "*%s %s/%s: \"%s\"%s", o->name->value, - c->name->value, catalog->find_message(c->text->value), - c->code->value, lf); - - // Multi-line commands need a *End line to terminate them. - if (strchr(c->code->value, '\n') || - strchr(c->code->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - - snprintf(query, sizeof(query), "?%s", o->name->value); - - if ((a = find_attr(query, NULL)) != NULL) - { - cupsFilePrintf(fp, "*%s: \"%s\"%s", query, a->value->value, lf); - - if (strchr(a->value->value, '\n') || - strchr(a->value->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - } - - if (o->section == PPDC_SECTION_JCL) - cupsFilePrintf(fp, "*JCLCloseUI: *%s%s", o->name->value, lf); - else - cupsFilePrintf(fp, "*CloseUI: *%s%s", o->name->value, lf); - - snprintf(custom, sizeof(custom), "Custom%s", o->name->value); - if ((a = find_attr(custom, "True")) != NULL) - { - // Output custom option information... - cupsFilePrintf(fp, "*%s True: \"%s\"%s", custom, a->value->value, lf); - if (strchr(a->value->value, '\n') || strchr(a->value->value, '\r')) - cupsFilePrintf(fp, "*End%s", lf); - - snprintf(custom, sizeof(custom), "ParamCustom%s", o->name->value); - for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next()) - { - if (strcmp(a->name->value, custom)) - continue; - - if (!a->selector->value || !a->selector->value[0]) - cupsFilePrintf(fp, "*%s", a->name->value); - else if (!a->text->value || !a->text->value[0]) - cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value, - catalog->find_message(a->selector->value)); - else - cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value, - catalog->find_message(a->text->value)); - - cupsFilePrintf(fp, ": %s%s", a->value->value, lf); - } - } - } - - if (strcasecmp(g->name->value, "General")) - cupsFilePrintf(fp, "*CloseGroup: %s%s", g->name->value, lf); - } - - if (locales) - { - // Add localizations for additional languages... - ppdcString *locale; // Locale name - ppdcCatalog *locatalog; // Message catalog for locale - - - // Write the translation strings for each language... - for (locale = (ppdcString *)locales->first(); - locale; - locale = (ppdcString *)locales->next()) - { - // Skip (US) English... - if (!strcmp(locale->value, "en") || !strcmp(locale->value, "en_US")) - continue; - - // Skip missing languages... - if ((locatalog = src->find_po(locale->value)) == NULL) - continue; - - // Do the core stuff first... - cupsFilePrintf(fp, "*%s.Translation Manufacturer/%s: \"\"%s", - locale->value, - locatalog->find_message(manufacturer->value), lf); - - if ((a = find_attr("ModelName", NULL)) != NULL) - cupsFilePrintf(fp, "*%s.Translation ModelName/%s: \"\"%s", - locale->value, - locatalog->find_message(a->value->value), lf); - else if (strncasecmp(model_name->value, manufacturer->value, - strlen(manufacturer->value))) - cupsFilePrintf(fp, "*%s.Translation ModelName/%s %s: \"\"%s", - locale->value, - locatalog->find_message(manufacturer->value), - locatalog->find_message(model_name->value), lf); - else - cupsFilePrintf(fp, "*%s.Translation ModelName/%s: \"\"%s", - locale->value, - locatalog->find_message(model_name->value), lf); - - if ((a = find_attr("ShortNickName", NULL)) != NULL) - cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s: \"\"%s", - locale->value, - locatalog->find_message(a->value->value), lf); - else if (strncasecmp(model_name->value, manufacturer->value, - strlen(manufacturer->value))) - cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s %s: \"\"%s", - locale->value, - locatalog->find_message(manufacturer->value), - locatalog->find_message(model_name->value), lf); - else - cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s: \"\"%s", - locale->value, - locatalog->find_message(model_name->value), lf); - - if ((a = find_attr("NickName", NULL)) != NULL) - cupsFilePrintf(fp, "*%s.Translation NickName/%s: \"\"%s", - locale->value, - locatalog->find_message(a->value->value), lf); - else if (strncasecmp(model_name->value, manufacturer->value, - strlen(manufacturer->value))) - cupsFilePrintf(fp, "*%s.Translation NickName/%s %s, %s: \"\"%s", - locale->value, - locatalog->find_message(manufacturer->value), - locatalog->find_message(model_name->value), - version->value, lf); - else - cupsFilePrintf(fp, "*%s.Translation NickName/%s, %s: \"\"%s", - locale->value, - locatalog->find_message(model_name->value), - version->value, lf); - - // Then the page sizes... - cupsFilePrintf(fp, "*%s.Translation PageSize/%s: \"\"%s", locale->value, - locatalog->find_message("Media Size"), lf); - - for (m = (ppdcMediaSize *)sizes->first(); - m; - m = (ppdcMediaSize *)sizes->next()) - { - cupsFilePrintf(fp, "*%s.PageSize %s/%s: \"\"%s", locale->value, - m->name->value, locatalog->find_message(m->text->value), - lf); - } - - // Next the groups and options... - for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next()) - { - if (!g->options->count) - continue; - - if (strcasecmp(g->name->value, "General")) - cupsFilePrintf(fp, "*%s.Translation %s/%s: \"\"%s", locale->value, - g->name->value, - locatalog->find_message(g->text->value), lf); - - for (o = (ppdcOption *)g->options->first(); - o; - o = (ppdcOption *)g->options->next()) - { - if (!o->choices->count) - continue; - - cupsFilePrintf(fp, "*%s.Translation %s/%s: \"\"%s", locale->value, - o->name->value, - locatalog->find_message(o->text->value ? - o->text->value : - o->name->value), lf); - - for (c = (ppdcChoice *)o->choices->first(); - c; - c = (ppdcChoice *)o->choices->next()) - { - // Write this choice... - cupsFilePrintf(fp, "*%s.%s %s/%s: \"\"%s", locale->value, - o->name->value, c->name->value, - locatalog->find_message(c->text->value ? - c->text->value : - c->name->value), lf); - } - } - } - - // Finally the localizable attributes... - for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next()) - { - if (!a->localizable && - (!a->text || !a->text->value || !a->text->value[0]) && - strcmp(a->name->value, "APCustomColorMatchingName") && - strcmp(a->name->value, "APPrinterPreset") && - strcmp(a->name->value, "cupsICCProfile") && - strcmp(a->name->value, "cupsIPPReason") && - strcmp(a->name->value, "cupsMarkerName") && - strncmp(a->name->value, "Custom", 6) && - strncmp(a->name->value, "ParamCustom", 11)) - continue; - - cupsFilePrintf(fp, "*%s.%s %s/%s: \"%s\"%s", locale->value, - a->name->value, a->selector->value, - locatalog->find_message(a->text && a->text->value ? - a->text->value : a->name->value), - ((a->localizable && a->value->value[0]) || - !strcmp(a->name->value, "cupsIPPReason")) ? - locatalog->find_message(a->value->value) : "", - lf); - } - } - } - - if (default_font && default_font->value) - cupsFilePrintf(fp, "*DefaultFont: %s%s", default_font->value, lf); - else - cupsFilePrintf(fp, "*DefaultFont: Courier%s", lf); - - for (fn = (ppdcFont *)fonts->first(); fn; fn = (ppdcFont *)fonts->next()) - if (!strcmp(fn->name->value, "*")) - { - for (bfn = (ppdcFont *)src->base_fonts->first(); - bfn; - bfn = (ppdcFont *)src->base_fonts->next()) - cupsFilePrintf(fp, "*Font %s: %s \"%s\" %s %s%s", - bfn->name->value, bfn->encoding->value, - bfn->version->value, bfn->charset->value, - bfn->status == PPDC_FONT_ROM ? "ROM" : "Disk", lf); - } - else - cupsFilePrintf(fp, "*Font %s: %s \"%s\" %s %s%s", - fn->name->value, fn->encoding->value, fn->version->value, - fn->charset->value, - fn->status == PPDC_FONT_ROM ? "ROM" : "Disk", lf); - - cupsFilePrintf(fp, "*%% End of %s, %05d bytes.%s", pc_file_name->value, - (int)((size_t)cupsFileTell(fp) + 25 + strlen(pc_file_name->value)), - lf); - - if (delete_cat) - catalog->release(); - - return (0); -} diff --git a/ppd/ppdc-file.cxx b/ppd/ppdc-file.cxx deleted file mode 100644 index a779b2808..000000000 --- a/ppd/ppdc-file.cxx +++ /dev/null @@ -1,93 +0,0 @@ -// -// File class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2010 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcFile::ppdcFile()' - Create (open) a file. -// - -ppdcFile::ppdcFile(const char *f, // I - File to open - cups_file_t *ffp) // I - File pointer to use -{ - if (ffp) - { - fp = ffp; - cupsFileRewind(fp); - } - else - fp = cupsFileOpen(f, "r"); - - close_on_delete = !ffp; - filename = f; - line = 1; - - if (!fp) - fprintf(stderr, _("ppdc: Unable to open %s: %s\n"), f, - strerror(errno)); -} - - -// -// 'ppdcFile::~ppdcFile()' - Delete (close) a file. -// - -ppdcFile::~ppdcFile() -{ - if (close_on_delete && fp) - cupsFileClose(fp); -} - - -// -// 'ppdcFile::get()' - Get a character from a file. -// - -int -ppdcFile::get() -{ - int ch; // Character from file - - - // Return EOF if there is no open file... - if (!fp) - return (EOF); - - // Get the character... - ch = cupsFileGetChar(fp); - - // Update the line number as needed... - if (ch == '\n') - line ++; - - // Return the character... - return (ch); -} - - -// -// 'ppdcFile::peek()' - Look at the next character from a file. -// - -int // O - Next character in file -ppdcFile::peek() -{ - // Return immediaely if there is no open file... - if (!fp) - return (EOF); - - // Otherwise return the next character without advancing... - return (cupsFilePeekChar(fp)); -} diff --git a/ppd/ppdc-filter.cxx b/ppd/ppdc-filter.cxx deleted file mode 100644 index 41a4c5fe4..000000000 --- a/ppd/ppdc-filter.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// -// Filter class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcFilter::ppdcFilter()' - Create a filter. -// - -ppdcFilter::ppdcFilter(const char *t, // I - MIME type - const char *p, // I - Filter program - int c) // I - Relative cost - : ppdcShared() -{ - PPDC_NEW; - - mime_type = new ppdcString(t); - program = new ppdcString(p); - cost = c; -} - - -// -// 'ppdcFilter::~ppdcFilter()' - Destroy a filter. -// - -ppdcFilter::~ppdcFilter() -{ - PPDC_DELETE; - - mime_type->release(); - program->release(); -} diff --git a/ppd/ppdc-font.cxx b/ppd/ppdc-font.cxx deleted file mode 100644 index 8ba5d26bf..000000000 --- a/ppd/ppdc-font.cxx +++ /dev/null @@ -1,51 +0,0 @@ -// -// Shared font class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcFont::ppdcFont()' - Create a shared font. -// - -ppdcFont::ppdcFont(const char *n, // I - Name of font - const char *e, // I - Font encoding - const char *v, // I - Font version - const char *c, // I - Font charset - ppdcFontStatus s) // I - Font status - : ppdcShared() -{ - PPDC_NEW; - - name = new ppdcString(n); - encoding = new ppdcString(e); - version = new ppdcString(v); - charset = new ppdcString(c); - status = s; -} - - -// -// 'ppdcFont::~ppdcFont()' - Destroy a shared font. -// - -ppdcFont::~ppdcFont() -{ - PPDC_DELETE; - - name->release(); - encoding->release(); - version->release(); - charset->release(); -} diff --git a/ppd/ppdc-group.cxx b/ppd/ppdc-group.cxx deleted file mode 100644 index 71e59bb8d..000000000 --- a/ppd/ppdc-group.cxx +++ /dev/null @@ -1,87 +0,0 @@ -// -// Group class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcGroup::ppdcGroup()' - Create a new group. -// - -ppdcGroup::ppdcGroup(const char *n, // I - Name of group - const char *t) // I - Text of group -{ - PPDC_NEWVAL(n); - - name = new ppdcString(n); - text = new ppdcString(t); - options = new ppdcArray(); -} - - -// -// 'ppdcGroup::ppdcGroup()' - Copy a new group. -// - -ppdcGroup::ppdcGroup(ppdcGroup *g) // I - Group template -{ - PPDC_NEWVAL(g->name->value); - - g->name->retain(); - g->text->retain(); - - name = g->name; - text = g->text; - - options = new ppdcArray(); - for (ppdcOption *o = (ppdcOption *)g->options->first(); - o; - o = (ppdcOption *)g->options->next()) - options->add(new ppdcOption(o)); -} - - -// -// 'ppdcGroup::~ppdcGroup()' - Destroy a group. -// - -ppdcGroup::~ppdcGroup() -{ - PPDC_DELETEVAL(name ? name->value : NULL); - - name->release(); - text->release(); - options->release(); - - name = text = 0; - options = 0; -} - - -// -// 'ppdcGroup::find_option()' - Find an option in a group. -// - -ppdcOption * -ppdcGroup::find_option(const char *n) // I - Name of option -{ - ppdcOption *o; // Current option - - - for (o = (ppdcOption *)options->first(); o; o = (ppdcOption *)options->next()) - if (!strcasecmp(n, o->name->value)) - return (o); - - return (0); -} diff --git a/ppd/ppdc-import.cxx b/ppd/ppdc-import.cxx deleted file mode 100644 index 9d3151c71..000000000 --- a/ppd/ppdc-import.cxx +++ /dev/null @@ -1,333 +0,0 @@ -// -// PPD file import methods for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 2002-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include - - -// -// 'ppdcSource::import_ppd()' - Import a PPD file. -// - -int // O - 1 on success, 0 on failure -ppdcSource::import_ppd(const char *f) // I - Filename -{ - int i, j, k; // Looping vars - cups_file_t *fp; // File - char line[256], // Comment line - *ptr; // Pointer into line - int cost; // Cost for filter - ppd_file_t *ppd; // PPD file data - ppd_group_t *group; // PPD group - ppd_option_t *option; // PPD option - ppd_choice_t *choice; // PPD choice - ppd_attr_t *attr; // PPD attribute - ppd_const_t *constraint; // PPD UI constraint - ppd_const_t *constraint2; // Temp PPD UI constraint - ppd_size_t *size; // PPD page size - ppdcDriver *driver; // Driver - ppdcFilter *filter; // Current filter - ppdcFont *font; // Font - ppdcGroup *cgroup; // UI group - ppdcOption *coption; // UI option - ppdcChoice *cchoice; // UI choice - ppdcConstraint *cconstraint; // UI constraint - ppdcMediaSize *csize; // Media size - - - // Try opening the PPD file... - if ((ppd = ppdOpenFile(f)) == NULL) - return (0); - - // All PPD files need a PCFileName attribute... - if (!ppd->pcfilename) - { - ppdClose(ppd); - return (0); - } - - // See if the driver has already been imported... - if (find_driver(ppd->pcfilename)) - { - ppdClose(ppd); - return (1); - } - - // Create a new PPD file... - if ((fp = cupsFileOpen(f, "r")) == NULL) - { - ppdClose(ppd); - return (0); - } - - driver = new ppdcDriver(); - driver->type = PPDC_DRIVER_PS; - - drivers->add(driver); - - // Read the initial comments from the PPD file and use them as the - // copyright/license text... - cupsFileGets(fp, line, sizeof(line)); - // Skip *PPD-Adobe-M.m - - while (cupsFileGets(fp, line, sizeof(line))) - if (strncmp(line, "*%", 2)) - break; - else if (strncmp(line, "*%%%% ", 6)) - { - for (ptr = line + 2; isspace(*ptr); ptr ++); - - driver->add_copyright(ptr); - } - - cupsFileClose(fp); - - // Then add the stuff from the PPD file... - if (ppd->modelname && ppd->manufacturer && - !strncasecmp(ppd->modelname, ppd->manufacturer, - strlen(ppd->manufacturer))) - { - ptr = ppd->modelname + strlen(ppd->manufacturer); - - while (isspace(*ptr)) - ptr ++; - } - else - ptr = ppd->modelname; - - if (ppd->nickname) - driver->add_attr(new ppdcAttr("NickName", NULL, NULL, ppd->nickname)); - - if (ppd->shortnickname) - driver->add_attr(new ppdcAttr("ShortNickName", NULL, NULL, - ppd->shortnickname)); - - driver->manufacturer = new ppdcString(ppd->manufacturer); - driver->model_name = new ppdcString(ptr); - driver->pc_file_name = new ppdcString(ppd->pcfilename); - attr = ppdFindAttr(ppd, "FileVersion", NULL); - driver->version = new ppdcString(attr ? attr->value : NULL); - driver->model_number = ppd->model_number; - driver->manual_copies = ppd->manual_copies; - driver->color_device = ppd->color_device; - driver->throughput = ppd->throughput; - driver->variable_paper_size = ppd->variable_sizes; - driver->max_width = ppd->custom_max[0]; - driver->max_length = ppd->custom_max[1]; - driver->min_width = ppd->custom_min[0]; - driver->min_length = ppd->custom_min[1]; - driver->left_margin = ppd->custom_margins[0]; - driver->bottom_margin = ppd->custom_margins[1]; - driver->right_margin = ppd->custom_margins[2]; - driver->top_margin = ppd->custom_margins[3]; - - for (i = 0; i < ppd->num_filters; i ++) - { - strncpy(line, ppd->filters[i], sizeof(line) - 1); - - for (ptr = line; *ptr; ptr ++) - if (isspace(*ptr & 255)) - break; - *ptr++ = '\0'; - - cost = strtol(ptr, &ptr, 10); - - while (isspace(*ptr & 255)) - ptr ++; - - filter = new ppdcFilter(line, ptr, cost); - driver->add_filter(filter); - } - - attr = ppdFindAttr(ppd, "DefaultFont", NULL); - driver->default_font = new ppdcString(attr ? attr->value : NULL); - - // Collect media sizes... - ppd_option_t *region_option, // PageRegion option - *size_option; // PageSize option - ppd_choice_t *region_choice, // PageRegion choice - *size_choice; // PageSize choice - - region_option = ppdFindOption(ppd, "PageRegion"); - size_option = ppdFindOption(ppd, "PageSize"); - - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - { - // Don't do custom size here... - if (!strcasecmp(size->name, "Custom")) - continue; - - // Get the code for the PageSize and PageRegion options... - region_choice = ppdFindChoice(region_option, size->name); - size_choice = ppdFindChoice(size_option, size->name); - - // Create a new media size record and add it to the driver... - csize = new ppdcMediaSize(size->name, size_choice->text, size->width, - size->length, size->left, size->bottom, - size->width - size->right, - size->length - size->top, - size_choice->code, region_choice->code); - - driver->add_size(csize); - - if (!strcasecmp(size_option->defchoice, size->name)) - driver->set_default_size(csize); - } - - // Now all of the options... - for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) - { - cgroup = new ppdcGroup(group->name, group->text); - driver->add_group(cgroup); - - for (j = group->num_options, option = group->options; j > 0; j --, option ++) - { - if (!strcmp(option->keyword, "PageSize") || !strcmp(option->keyword, "PageRegion")) - continue; - - coption = new ppdcOption((ppdcOptType)option->ui, option->keyword, - option->text, (ppdcOptSection)option->section, - option->order); - cgroup->add_option(coption); - - for (k = option->num_choices, choice = option->choices; k > 0; k --, choice ++) - { - if (!strcmp(choice->choice, "Custom")) - continue; - - cchoice = new ppdcChoice(choice->choice, choice->text, choice->code); - coption->add_choice(cchoice); - - if (!strcasecmp(option->defchoice, choice->choice)) - coption->set_defchoice(cchoice); - } - } - } - - // Now the constraints... - for (i = ppd->num_consts, constraint = ppd->consts; - i > 0; - i --, constraint ++) - { - // Look for mirrored constraints... - for (j = i - 1, constraint2 = constraint + 1; - j > 0; - j --, constraint2 ++) - if (!strcmp(constraint->option1, constraint2->option2) && - !strcmp(constraint->choice1, constraint2->choice2) && - !strcmp(constraint->option2, constraint2->option1) && - !strcmp(constraint->choice2, constraint2->choice1)) - break; - - if (j) - continue; - - cconstraint = new ppdcConstraint(constraint->option2, constraint->choice2, - constraint->option1, constraint->choice1); - driver->add_constraint(cconstraint); - } - - for (i = 0; i < ppd->num_attrs; i ++) - { - attr = ppd->attrs[i]; - - if (!strcmp(attr->name, "Font")) - { - // Font... - char encoding[256], // Encoding string - version[256], // Version string - charset[256], // Charset string - status[256]; // Status string - ppdcFontStatus fstatus; // Status enumeration - - - if (sscanf(attr->value, "%s%*[^\"]\"%[^\"]\"%s%s", encoding, version, - charset, status) != 4) - { - fprintf(stderr, _("ppdc: Bad font attribute: %s\n"), - attr->value); - continue; - } - - if (!strcmp(status, "ROM")) - fstatus = PPDC_FONT_ROM; - else - fstatus = PPDC_FONT_DISK; - - font = new ppdcFont(attr->spec, encoding, version, charset, fstatus); - - driver->add_font(font); - } - else if (!strcmp(attr->name, "CustomPageSize")) - { - driver->set_custom_size_code(attr->value); - } - else if ((strncmp(attr->name, "Default", 7) || - !strcmp(attr->name, "DefaultColorSpace")) && - strcmp(attr->name, "ColorDevice") && - strcmp(attr->name, "Manufacturer") && - strcmp(attr->name, "ModelName") && - strcmp(attr->name, "MaxMediaHeight") && - strcmp(attr->name, "MaxMediaWidth") && - strcmp(attr->name, "NickName") && - strcmp(attr->name, "ParamCustomPageSize") && - strcmp(attr->name, "ShortNickName") && - strcmp(attr->name, "Throughput") && - strcmp(attr->name, "PCFileName") && - strcmp(attr->name, "FileVersion") && - strcmp(attr->name, "FormatVersion") && - strcmp(attr->name, "HWMargins") && - strcmp(attr->name, "VariablePaperSize") && - strcmp(attr->name, "LanguageEncoding") && - strcmp(attr->name, "LanguageVersion") && - strcmp(attr->name, "cupsFilter") && - strcmp(attr->name, "cupsFlipDuplex") && - strcmp(attr->name, "cupsLanguages") && - strcmp(attr->name, "cupsManualCopies") && - strcmp(attr->name, "cupsModelNumber") && - strcmp(attr->name, "cupsVersion")) - { - if ((ptr = strchr(attr->name, '.')) != NULL && - ((ptr - attr->name) == 2 || (ptr - attr->name) == 5)) - { - // Might be a localization attribute; test further... - if (isalpha(attr->name[0] & 255) && - isalpha(attr->name[1] & 255) && - (attr->name[2] == '.' || - (attr->name[2] == '_' && isalpha(attr->name[3] & 255) && - isalpha(attr->name[4] & 255)))) - continue; - } - - // Attribute... - driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text, - attr->value)); - } - else if (!strncmp(attr->name, "Default", 7) && - !ppdFindOption(ppd, attr->name + 7) && - strcmp(attr->name, "DefaultFont") && - strcmp(attr->name, "DefaultImageableArea") && - strcmp(attr->name, "DefaultPaperDimension") && - strcmp(attr->name, "DefaultFont")) - { - // Default attribute... - driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text, - attr->value)); - } - } - - ppdClose(ppd); - - return (1); -} diff --git a/ppd/ppdc-mediasize.cxx b/ppd/ppdc-mediasize.cxx deleted file mode 100644 index 8c5245c92..000000000 --- a/ppd/ppdc-mediasize.cxx +++ /dev/null @@ -1,70 +0,0 @@ -// -// Shared media size class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcMediaSize::ppdcMediaSize()' - Create a new media size. -// - -ppdcMediaSize::ppdcMediaSize(const char *n, // I - Name of media size - const char *t, // I - Text of media size - float w, // I - Width in points - float l, // I - Length in points - float lm, // I - Left margin in points - float bm, // I - Bottom margin in points - float rm, // I - Right margin in points - float tm, // I - Top margin in points - const char *sc, // I - PageSize code, if any - const char *rc) // I - PageRegion code, if any - : ppdcShared() -{ - PPDC_NEW; - - name = new ppdcString(n); - text = new ppdcString(t); - width = w; - length = l; - left = lm; - bottom = bm; - right = rm; - top = tm; - size_code = new ppdcString(sc); - region_code = new ppdcString(rc); - - if (left < 0.0f) - left = 0.0f; - if (bottom < 0.0f) - bottom = 0.0f; - if (right < 0.0f) - right = 0.0f; - if (top < 0.0f) - top = 0.0f; -} - - -// -// 'ppdcMediaSize::~ppdcMediaSize()' - Destroy a media size. -// - -ppdcMediaSize::~ppdcMediaSize() -{ - PPDC_DELETE; - - name->release(); - text->release(); - size_code->release(); - region_code->release(); -} diff --git a/ppd/ppdc-message.cxx b/ppd/ppdc-message.cxx deleted file mode 100644 index 9e84e8930..000000000 --- a/ppd/ppdc-message.cxx +++ /dev/null @@ -1,43 +0,0 @@ -// -// Shared message class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcMessage::ppdcMessage()' - Create a shared message. -// - -ppdcMessage::ppdcMessage(const char *i, // I - ID - const char *s) // I - Text - : ppdcShared() -{ - PPDC_NEW; - - id = new ppdcString(i); - string = new ppdcString(s); -} - - -// -// 'ppdcMessage::~ppdcMessage()' - Destroy a shared message. -// - -ppdcMessage::~ppdcMessage() -{ - PPDC_DELETE; - - id->release(); - string->release(); -} diff --git a/ppd/ppdc-option.cxx b/ppd/ppdc-option.cxx deleted file mode 100644 index 9a63d0ff7..000000000 --- a/ppd/ppdc-option.cxx +++ /dev/null @@ -1,112 +0,0 @@ -// -// Option class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcOption::ppdcOption()' - Create a new option. -// - -ppdcOption::ppdcOption(ppdcOptType ot, // I - Option type - const char *n, // I - Option name - const char *t, // I - Option text - ppdcOptSection s, // I - Section - float o) // I - Ordering number - : ppdcShared() -{ - PPDC_NEW; - - type = ot; - name = new ppdcString(n); - text = new ppdcString(t); - section = s; - order = o; - choices = new ppdcArray(); - defchoice = 0; -} - - -// -// 'ppdcOption::ppdcOption()' - Copy a new option. -// - -ppdcOption::ppdcOption(ppdcOption *o) // I - Template option -{ - PPDC_NEW; - - o->name->retain(); - o->text->retain(); - if (o->defchoice) - o->defchoice->retain(); - - type = o->type; - name = o->name; - text = o->text; - section = o->section; - order = o->order; - choices = new ppdcArray(o->choices); - defchoice = o->defchoice; -} - - -// -// 'ppdcOption::~ppdcOption()' - Destroy an option. -// - -ppdcOption::~ppdcOption() -{ - PPDC_DELETE; - - name->release(); - text->release(); - if (defchoice) - defchoice->release(); - choices->release(); -} - - -// -// 'ppdcOption::find_choice()' - Find an option choice. -// - -ppdcChoice * // O - Choice or NULL -ppdcOption::find_choice(const char *n) // I - Name of choice -{ - ppdcChoice *c; // Current choice - - - for (c = (ppdcChoice *)choices->first(); c; c = (ppdcChoice *)choices->next()) - if (!strcasecmp(n, c->name->value)) - return (c); - - return (0); -} - - -// -// 'ppdcOption::set_defchoice()' - Set the default choice. -// - -void -ppdcOption::set_defchoice(ppdcChoice *c) // I - Choice -{ - if (defchoice) - defchoice->release(); - - if (c->name) - c->name->retain(); - - defchoice = c->name; -} diff --git a/ppd/ppdc-private.h b/ppd/ppdc-private.h deleted file mode 100644 index 00b8b951e..000000000 --- a/ppd/ppdc-private.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// Private definitions for the CUPS PPD Compilerin libppd. -// -// Copyright 2009-2010 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPDC_PRIVATE_H_ -# define _PPDC_PRIVATE_H_ - -// -// Include necessary headers... -// - -# include "ppdc.h" -# include -# include -# include -# include -# include - -// -// Macros... -// - -# ifdef PPDC_DEBUG -# define PPDC_NEW DEBUG_printf(("%s: %p new", class_name(), this)) -# define PPDC_NEWVAL(s) DEBUG_printf(("%s(\"%s\"): %p new", class_name(), s, this)) -# define PPDC_DELETE DEBUG_printf(("%s: %p delete", class_name(), this)) -# define PPDC_DELETEVAL(s) DEBUG_printf(("%s(\"%s\"): %p delete", class_name(), s, this)) -# else -# define PPDC_NEW -# define PPDC_NEWVAL(s) -# define PPDC_DELETE -# define PPDC_DELETEVAL(s) -# endif // PPDC_DEBUG - -// -// Macro for localized text... -// - -# define _(x) x - -#endif // !_PPDC_PRIVATE_H_ diff --git a/ppd/ppdc-profile.cxx b/ppd/ppdc-profile.cxx deleted file mode 100644 index 8939b5c1a..000000000 --- a/ppd/ppdc-profile.cxx +++ /dev/null @@ -1,50 +0,0 @@ -// -// Color profile class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcProfile::ppdcProfile()' - Create a color profile. -// - -ppdcProfile::ppdcProfile(const char *r, // I - Resolution name - const char *m, // I - Media type name - float d, // I - Density - float g, // I - Gamma - const float *p) // I - 3x3 transform matrix - : ppdcShared() -{ - PPDC_NEW; - - resolution = new ppdcString(r); - media_type = new ppdcString(m); - density = d; - gamma = g; - - memcpy(profile, p, sizeof(profile)); -} - - -// -// 'ppdcProfile::~ppdcProfile()' - Destroy a color profile. -// - -ppdcProfile::~ppdcProfile() -{ - PPDC_DELETE; - - resolution->release(); - media_type->release(); -} diff --git a/ppd/ppdc-shared.cxx b/ppd/ppdc-shared.cxx deleted file mode 100644 index f3715c0e1..000000000 --- a/ppd/ppdc-shared.cxx +++ /dev/null @@ -1,67 +0,0 @@ -// -// Shared data class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcShared::ppdcShared()' - Create shared data. -// - -ppdcShared::ppdcShared() -{ - use = 1; -} - - -// -// 'ppdcShared::~ppdcShared()' - Destroy shared data. -// - -ppdcShared::~ppdcShared() -{ -} - - -// -// 'ppdcShared::release()' - Decrement the use count and delete as needed. -// - -void -ppdcShared::release(void) -{ - use --; - -#ifdef DEBUG - if (use < 0) - { - fprintf(stderr, "ERROR: Over-release of %s: %p\n", class_name(), this); - abort(); - } -#endif // DEBUG - - if (use == 0) - delete this; -} - - -// -// 'ppdcShared::retain()' - Increment the use count for this data. -// - -void -ppdcShared::retain() -{ - use ++; -} diff --git a/ppd/ppdc-source.cxx b/ppd/ppdc-source.cxx deleted file mode 100644 index c2fc06fe7..000000000 --- a/ppd/ppdc-source.cxx +++ /dev/null @@ -1,3813 +0,0 @@ -// -// Source class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2018 by Apple Inc. -// Copyright 2002-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include -#include -#include -#include -#include "epson.h" -#include "hp.h" -#include "label.h" -#ifndef _WIN32 -# include -#endif // !_WIN32 - - -// -// Class globals... -// - -ppdcArray *ppdcSource::includes = 0; -const char *ppdcSource::driver_types[] = - { - "custom", - "ps", - "escp", - "pcl", - "label", - "epson", - "hp" - }; - - -// -// 'ppdcSource::ppdcSource()' - Load a driver source file. -// - -ppdcSource::ppdcSource(const char *f, // I - File to read - cups_file_t *ffp)// I - File pointer to use - : ppdcShared() -{ - PPDC_NEW; - - filename = new ppdcString(f); - base_fonts = new ppdcArray(); - drivers = new ppdcArray(); - po_files = new ppdcArray(); - sizes = new ppdcArray(); - vars = new ppdcArray(); - cond_state = PPDC_COND_NORMAL; - cond_current = cond_stack; - cond_stack[0] = PPDC_COND_NORMAL; - - // Add standard #define variables... -#define MAKE_STRING(x) #x - - vars->add(new ppdcVariable("CUPS_VERSION", MAKE_STRING(CUPS_VERSION))); - vars->add(new ppdcVariable("CUPS_VERSION_MAJOR", MAKE_STRING(CUPS_VERSION_MAJOR))); - vars->add(new ppdcVariable("CUPS_VERSION_MINOR", MAKE_STRING(CUPS_VERSION_MINOR))); - vars->add(new ppdcVariable("CUPS_VERSION_PATCH", MAKE_STRING(CUPS_VERSION_PATCH))); - -#ifdef _WIN32 - vars->add(new ppdcVariable("PLATFORM_NAME", "Windows")); - vars->add(new ppdcVariable("PLATFORM_ARCH", "X86")); - -#else - struct utsname name; // uname information - - if (!uname(&name)) - { - vars->add(new ppdcVariable("PLATFORM_NAME", name.sysname)); - vars->add(new ppdcVariable("PLATFORM_ARCH", name.machine)); - } - else - { - vars->add(new ppdcVariable("PLATFORM_NAME", "unknown")); - vars->add(new ppdcVariable("PLATFORM_ARCH", "unknown")); - } -#endif // _WIN32 - - if (f) - read_file(f, ffp); -} - - -// -// 'ppdcSource::~ppdcSource()' - Free a driver source file. -// - -ppdcSource::~ppdcSource() -{ - PPDC_DELETE; - - filename->release(); - base_fonts->release(); - drivers->release(); - po_files->release(); - sizes->release(); - vars->release(); -} - - -// -// 'ppdcSource::add_include()' - Add an include directory. -// - -void -ppdcSource::add_include(const char *d) // I - Include directory -{ - if (!d) - return; - - if (!includes) - includes = new ppdcArray(); - - includes->add(new ppdcString(d)); -} - - -// -// 'ppdcSource::find_driver()' - Find a driver. -// - -ppdcDriver * // O - Driver -ppdcSource::find_driver(const char *f) // I - Driver file name -{ - ppdcDriver *d; // Current driver - - - for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next()) - if (!strcasecmp(f, d->pc_file_name->value)) - return (d); - - return (NULL); -} - - -// -// 'ppdcSource::find_include()' - Find an include file. -// - -char * // O - Found path or NULL -ppdcSource::find_include( - const char *f, // I - Include filename - const char *base, // I - Current directory - char *n, // I - Path buffer - int nlen) // I - Path buffer length -{ - ppdcString *dir; // Include directory - char temp[1024], // Temporary path - *ptr; // Pointer to end of path - const char *c; - - - // Range check input... - if (!f || !*f || !n || nlen < 2) - return (0); - - // Check the first character to see if we have or "name"... - if (*f == '<') - { - // Remove the surrounding <> from the name... - strncpy(temp, f + 1, sizeof(temp) - 1); - ptr = temp + strlen(temp) - 1; - - if (*ptr != '>') - { - fprintf(stderr, - _("ppdc: Invalid #include/#po filename \"%s\".\n"), n); - return (0); - } - - *ptr = '\0'; - f = temp; - } - else - { - // Check for the local file relative to the current directory... - if (base && *base && f[0] != '/') - snprintf(n, (size_t)nlen, "%s/%s", base, f); - else - strncpy(n, f, (size_t)nlen); - - if (!access(n, 0)) - return (n); - else if (*f == '/') - { - // Absolute path that doesn't exist... - return (0); - } - } - - // Search the include directories, if any... - if (includes) - { - for (dir = (ppdcString *)includes->first(); dir; dir = (ppdcString *)includes->next()) - { - snprintf(n, (size_t)nlen, "%s/%s", dir->value, f); - if (!access(n, 0)) - return (n); - } - } - - // Search our own include directory (Usually /usr/share/ppdc) - if ((c = getenv("PPDC_DATADIR")) == NULL) - c = PPDC_DATADIR; - - snprintf(n, (size_t)nlen, "%s/%s", c, f); - if (!access(n, 0)) - return (n); - - snprintf(n, (size_t)nlen, "%s/po/%s", c, f); - if (!access(n, 0)) - return (n); - - // Search the CUPS include directories... - if ((c = getenv("CUPS_DATADIR")) == NULL) - c = CUPS_DATADIR; - - snprintf(n, (size_t)nlen, "%s/ppdc/%s", c, f); - if (!access(n, 0)) - return (n); - - snprintf(n, (size_t)nlen, "%s/po/%s", c, f); - if (!access(n, 0)) - return (n); - - return (0); -} - - -// -// 'ppdcSource::find_po()' - Find a message catalog for the given locale. -// - -ppdcCatalog * // O - Message catalog or NULL -ppdcSource::find_po(const char *l) // I - Locale name -{ - ppdcCatalog *cat; // Current message catalog - - - for (cat = (ppdcCatalog *)po_files->first(); - cat; - cat = (ppdcCatalog *)po_files->next()) - if (!strcasecmp(l, cat->locale->value)) - return (cat); - - return (NULL); -} - - -// -// 'ppdcSource::find_size()' - Find a media size. -// - -ppdcMediaSize * // O - Size -ppdcSource::find_size(const char *s) // I - Size name -{ - ppdcMediaSize *m; // Current media size - - - for (m = (ppdcMediaSize *)sizes->first(); m; m = (ppdcMediaSize *)sizes->next()) - if (!strcasecmp(s, m->name->value)) - return (m); - - return (NULL); -} - - -// -// 'ppdcSource::find_variable()' - Find a variable. -// - -ppdcVariable * // O - Variable -ppdcSource::find_variable(const char *n)// I - Variable name -{ - ppdcVariable *v; // Current variable - - - for (v = (ppdcVariable *)vars->first(); v; v = (ppdcVariable *)vars->next()) - if (!strcasecmp(n, v->name->value)) - return (v); - - return (NULL); -} - - -// -// 'ppdcSource::get_attr()' - Get an attribute. -// - -ppdcAttr * // O - Attribute -ppdcSource::get_attr(ppdcFile *fp, // I - File to read - bool loc) // I - Localize this attribute? -{ - char name[1024], // Name string - selector[1024], // Selector string - *text, // Text string - value[1024]; // Value string - - - // Get the attribute parameters: - // - // Attribute name selector value - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name after %s on line %d of %s.\n"), - loc ? "LocAttribute" : "Attribute", fp->line, fp->filename); - return (0); - } - - if (!get_token(fp, selector, sizeof(selector))) - { - fprintf(stderr, - _("ppdc: Expected selector after %s on line %d of %s.\n"), - loc ? "LocAttribute" : "Attribute", fp->line, fp->filename); - return (0); - } - - if ((text = strchr(selector, '/')) != NULL) - *text++ = '\0'; - - if (!get_token(fp, value, sizeof(value))) - { - fprintf(stderr, - _("ppdc: Expected value after %s on line %d of %s.\n"), - loc ? "LocAttribute" : "Attribute", fp->line, fp->filename); - return (0); - } - - return (new ppdcAttr(name, selector, text, value, loc)); -} - - -// -// 'ppdcSource::get_boolean()' - Get a boolean value. -// - -int // O - Boolean value -ppdcSource::get_boolean(ppdcFile *fp) // I - File to read -{ - char buffer[256]; // String buffer - - - if (!get_token(fp, buffer, sizeof(buffer))) - { - fprintf(stderr, - _("ppdc: Expected boolean value on line %d of %s.\n"), - fp->line, fp->filename); - return (-1); - } - - if (!strcasecmp(buffer, "on") || - !strcasecmp(buffer, "yes") || - !strcasecmp(buffer, "true")) - return (1); - else if (!strcasecmp(buffer, "off") || - !strcasecmp(buffer, "no") || - !strcasecmp(buffer, "false")) - return (0); - else - { - fprintf(stderr, - _("ppdc: Bad boolean value (%s) on line %d of %s.\n"), - buffer, fp->line, fp->filename); - return (-1); - } -} - - -// -// 'ppdcSource::get_choice()' - Get a choice. -// - -ppdcChoice * // O - Choice data -ppdcSource::get_choice(ppdcFile *fp) // I - File to read -{ - char name[1024], // Name - *text, // Text - code[10240]; // Code - - - // Read a choice from the file: - // - // Choice name/text code - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected choice name/text on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - if (!get_token(fp, code, sizeof(code))) - { - fprintf(stderr, _("ppdc: Expected choice code on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - // Return the new choice - return (new ppdcChoice(name, text, code)); -} - - -// -// 'ppdcSource::get_color_model()' - Get an old-style color model option. -// - -ppdcChoice * // O - Choice data -ppdcSource::get_color_model(ppdcFile *fp) - // I - File to read -{ - char name[1024], // Option name - *text, // Text option - temp[256]; // Temporary string - int color_space, // Colorspace - color_order, // Color order - compression; // Compression mode - - - // Get the ColorModel parameters: - // - // ColorModel name/text colorspace colororder compression - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name/text combination for ColorModel on " - "line %d of %s.\n"), fp->line, fp->filename); - return (NULL); - } - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected colorspace for ColorModel on line %d of " - "%s.\n"), fp->line, fp->filename); - return (NULL); - } - - if ((color_space = get_color_space(temp)) < 0) - color_space = get_integer(temp); - - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected color order for ColorModel on line %d of " - "%s.\n"), fp->line, fp->filename); - return (NULL); - } - - if ((color_order = get_color_order(temp)) < 0) - color_order = get_integer(temp); - - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected compression for ColorModel on line %d of " - "%s.\n"), fp->line, fp->filename); - return (NULL); - } - - compression = get_integer(temp); - - snprintf(temp, sizeof(temp), - "<>" - "setpagedevice", - color_space, color_order, compression); - - return (new ppdcChoice(name, text, temp)); -} - - -// -// 'ppdcSource::get_color_order()' - Get an old-style color order value. -// - -int // O - Color order value -ppdcSource::get_color_order( - const char *co) // I - Color order string -{ - if (!strcasecmp(co, "chunked") || - !strcasecmp(co, "chunky")) - return (CUPS_ORDER_CHUNKED); - else if (!strcasecmp(co, "banded")) - return (CUPS_ORDER_BANDED); - else if (!strcasecmp(co, "planar")) - return (CUPS_ORDER_PLANAR); - else - return (-1); -} - - -// -// 'ppdcSource::get_color_profile()' - Get a color profile definition. -// - -ppdcProfile * // O - Color profile -ppdcSource::get_color_profile( - ppdcFile *fp) // I - File to read -{ - char resolution[1024], // Resolution/media type - *media_type; // Media type - int i; // Looping var - float g, // Gamma value - d, // Density value - m[9]; // Transform matrix - - - // Get the ColorProfile parameters: - // - // ColorProfile resolution/mediatype gamma density m00 m01 m02 ... m22 - if (!get_token(fp, resolution, sizeof(resolution))) - { - fprintf(stderr, - _("ppdc: Expected resolution/mediatype following " - "ColorProfile on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if ((media_type = strchr(resolution, '/')) != NULL) - *media_type++ = '\0'; - else - media_type = resolution; - - g = get_float(fp); - d = get_float(fp); - for (i = 0; i < 9; i ++) - m[i] = get_float(fp); - - return (new ppdcProfile(resolution, media_type, d, g, m)); -} - - -// -// 'ppdcSource::get_color_space()' - Get an old-style colorspace value. -// - -int // O - Colorspace value -ppdcSource::get_color_space( - const char *cs) // I - Colorspace string -{ - if (!strcasecmp(cs, "w")) - return (CUPS_CSPACE_W); - else if (!strcasecmp(cs, "rgb")) - return (CUPS_CSPACE_RGB); - else if (!strcasecmp(cs, "rgba")) - return (CUPS_CSPACE_RGBA); - else if (!strcasecmp(cs, "k")) - return (CUPS_CSPACE_K); - else if (!strcasecmp(cs, "cmy")) - return (CUPS_CSPACE_CMY); - else if (!strcasecmp(cs, "ymc")) - return (CUPS_CSPACE_YMC); - else if (!strcasecmp(cs, "cmyk")) - return (CUPS_CSPACE_CMYK); - else if (!strcasecmp(cs, "ymck")) - return (CUPS_CSPACE_YMCK); - else if (!strcasecmp(cs, "kcmy")) - return (CUPS_CSPACE_KCMY); - else if (!strcasecmp(cs, "kcmycm")) - return (CUPS_CSPACE_KCMYcm); - else if (!strcasecmp(cs, "gmck")) - return (CUPS_CSPACE_GMCK); - else if (!strcasecmp(cs, "gmcs")) - return (CUPS_CSPACE_GMCS); - else if (!strcasecmp(cs, "white")) - return (CUPS_CSPACE_WHITE); - else if (!strcasecmp(cs, "gold")) - return (CUPS_CSPACE_GOLD); - else if (!strcasecmp(cs, "silver")) - return (CUPS_CSPACE_SILVER); - else if (!strcasecmp(cs, "CIEXYZ")) - return (CUPS_CSPACE_CIEXYZ); - else if (!strcasecmp(cs, "CIELab")) - return (CUPS_CSPACE_CIELab); - else if (!strcasecmp(cs, "RGBW")) - return (CUPS_CSPACE_RGBW); - else if (!strcasecmp(cs, "ICC1")) - return (CUPS_CSPACE_ICC1); - else if (!strcasecmp(cs, "ICC2")) - return (CUPS_CSPACE_ICC2); - else if (!strcasecmp(cs, "ICC3")) - return (CUPS_CSPACE_ICC3); - else if (!strcasecmp(cs, "ICC4")) - return (CUPS_CSPACE_ICC4); - else if (!strcasecmp(cs, "ICC5")) - return (CUPS_CSPACE_ICC5); - else if (!strcasecmp(cs, "ICC6")) - return (CUPS_CSPACE_ICC6); - else if (!strcasecmp(cs, "ICC7")) - return (CUPS_CSPACE_ICC7); - else if (!strcasecmp(cs, "ICC8")) - return (CUPS_CSPACE_ICC8); - else if (!strcasecmp(cs, "ICC9")) - return (CUPS_CSPACE_ICC9); - else if (!strcasecmp(cs, "ICCA")) - return (CUPS_CSPACE_ICCA); - else if (!strcasecmp(cs, "ICCB")) - return (CUPS_CSPACE_ICCB); - else if (!strcasecmp(cs, "ICCC")) - return (CUPS_CSPACE_ICCC); - else if (!strcasecmp(cs, "ICCD")) - return (CUPS_CSPACE_ICCD); - else if (!strcasecmp(cs, "ICCE")) - return (CUPS_CSPACE_ICCE); - else if (!strcasecmp(cs, "ICCF")) - return (CUPS_CSPACE_ICCF); - else - return (-1); -} - - -// -// 'ppdcSource::get_constraint()' - Get a constraint. -// - -ppdcConstraint * // O - Constraint -ppdcSource::get_constraint(ppdcFile *fp)// I - File to read -{ - char temp[1024], // One string to rule them all - *ptr, // Pointer into string - *option1, // Constraint option 1 - *choice1, // Constraint choice 1 - *option2, // Constraint option 2 - *choice2; // Constraint choice 2 - - - // Read the UIConstaints parameter in one of the following forms: - // - // UIConstraints "*Option1 *Option2" - // UIConstraints "*Option1 Choice1 *Option2" - // UIConstraints "*Option1 *Option2 Choice2" - // UIConstraints "*Option1 Choice1 *Option2 Choice2" - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected constraints string for UIConstraints on " - "line %d of %s.\n"), fp->line, fp->filename); - return (NULL); - } - - for (ptr = temp; isspace(*ptr); ptr ++); - - if (*ptr != '*') - { - fprintf(stderr, - _("ppdc: Option constraint must *name on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - option1 = ptr; - - for (; *ptr && !isspace(*ptr); ptr ++); - for (; isspace(*ptr); *ptr++ = '\0'); - - if (*ptr != '*') - { - choice1 = ptr; - - for (; *ptr && !isspace(*ptr); ptr ++); - for (; isspace(*ptr); *ptr++ = '\0'); - } - else - choice1 = NULL; - - if (*ptr != '*') - { - fprintf(stderr, - _("ppdc: Expected two option names on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - option2 = ptr; - - for (; *ptr && !isspace(*ptr); ptr ++); - for (; isspace(*ptr); *ptr++ = '\0'); - - if (*ptr) - choice2 = ptr; - else - choice2 = NULL; - - return (new ppdcConstraint(option1, choice1, option2, choice2)); -} - - -// -// 'ppdcSource::get_custom_size()' - Get a custom media size definition from a file. -// - -ppdcMediaSize * // O - Media size -ppdcSource::get_custom_size(ppdcFile *fp) - // I - File to read -{ - char name[1024], // Name - *text, // Text - size_code[10240], // PageSize code - region_code[10240]; // PageRegion - float width, // Width - length, // Length - left, // Left margin - bottom, // Bottom margin - right, // Right margin - top; // Top margin - - - // Get the name, text, width, length, margins, and code: - // - // CustomMedia name/text width length left bottom right top size-code region-code - if (!get_token(fp, name, sizeof(name))) - return (NULL); - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - if ((width = get_measurement(fp)) < 0.0f) - return (NULL); - - if ((length = get_measurement(fp)) < 0.0f) - return (NULL); - - if ((left = get_measurement(fp)) < 0.0f) - return (NULL); - - if ((bottom = get_measurement(fp)) < 0.0f) - return (NULL); - - if ((right = get_measurement(fp)) < 0.0f) - return (NULL); - - if ((top = get_measurement(fp)) < 0.0f) - return (NULL); - - if (!get_token(fp, size_code, sizeof(size_code))) - return (NULL); - - if (!get_token(fp, region_code, sizeof(region_code))) - return (NULL); - - // Return the new media size... - return (new ppdcMediaSize(name, text, width, length, left, bottom, - right, top, size_code, region_code)); -} - - -// -// 'ppdcSource::get_duplex()' - Get a duplex option. -// - -void -ppdcSource::get_duplex(ppdcFile *fp, // I - File to read from - ppdcDriver *d) // I - Current driver -{ - char temp[256]; // Duplex keyword - ppdcAttr *attr; // cupsFlipDuplex attribute - ppdcGroup *g; // Current group - ppdcOption *o; // Duplex option - - - // Duplex {boolean|none|normal|flip} - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected duplex type after Duplex on line %d of " - "%s.\n"), fp->line, fp->filename); - return; - } - - if (cond_state) - return; - - if (!strcasecmp(temp, "none") || !strcasecmp(temp, "false") || - !strcasecmp(temp, "no") || !strcasecmp(temp, "off")) - { - g = d->find_group("General"); - if ((o = g->find_option("Duplex")) != NULL) - g->options->remove(o); - - for (attr = (ppdcAttr *)d->attrs->first(); - attr; - attr = (ppdcAttr *)d->attrs->next()) - if (!strcmp(attr->name->value, "cupsFlipDuplex")) - { - d->attrs->remove(attr); - break; - } - } - else if (!strcasecmp(temp, "normal") || !strcasecmp(temp, "true") || - !strcasecmp(temp, "yes") || !strcasecmp(temp, "on") || - !strcasecmp(temp, "flip") || !strcasecmp(temp, "rotated") || - !strcasecmp(temp, "manualtumble")) - { - g = d->find_group("General"); - o = g->find_option("Duplex"); - - if (!o) - { - o = new ppdcOption(PPDC_PICKONE, "Duplex", "2-Sided Printing", - !strcasecmp(temp, "flip") ? PPDC_SECTION_PAGE : - PPDC_SECTION_ANY, 10.0f); - o->add_choice(new ppdcChoice("None", "Off (1-Sided)", - "<>setpagedevice")); - o->add_choice(new ppdcChoice("DuplexNoTumble", "Long-Edge (Portrait)", - "<>setpagedevice")); - o->add_choice(new ppdcChoice("DuplexTumble", "Short-Edge (Landscape)", - "<>setpagedevice")); - - g->add_option(o); - } - - for (attr = (ppdcAttr *)d->attrs->first(); - attr; - attr = (ppdcAttr *)d->attrs->next()) - if (!strcmp(attr->name->value, "cupsFlipDuplex")) - { - if (strcasecmp(temp, "flip")) - d->attrs->remove(attr); - break; - } - - if (!strcasecmp(temp, "flip") && !attr) - d->add_attr(new ppdcAttr("cupsFlipDuplex", NULL, NULL, "true")); - - for (attr = (ppdcAttr *)d->attrs->first(); - attr; - attr = (ppdcAttr *)d->attrs->next()) - if (!strcmp(attr->name->value, "cupsBackSide")) - { - d->attrs->remove(attr); - break; - } - - if (!strcasecmp(temp, "flip")) - d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Flipped")); - else if (!strcasecmp(temp, "rotated")) - d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Rotated")); - else if (!strcasecmp(temp, "manualtumble")) - d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "ManualTumble")); - else - d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Normal")); - } - else - fprintf(stderr, - _("ppdc: Unknown duplex type \"%s\" on line %d of %s.\n"), - temp, fp->line, fp->filename); -} - - -// -// 'ppdcSource::get_filter()' - Get a filter. -// - -ppdcFilter * // O - Filter -ppdcSource::get_filter(ppdcFile *fp) // I - File to read -{ - char type[1024], // MIME type - program[1024], // Filter program - *ptr; // Pointer into MIME type - int cost; // Relative cost - - - // Read filter parameters in one of the following formats: - // - // Filter "type cost program" - // Filter type cost program - - if (!get_token(fp, type, sizeof(type))) - { - fprintf(stderr, - _("ppdc: Expected a filter definition on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if ((ptr = strchr(type, ' ')) != NULL) - { - // Old-style filter definition in one string... - *ptr++ = '\0'; - cost = strtol(ptr, &ptr, 10); - - while (isspace(*ptr)) - ptr ++; - - strncpy(program, ptr, sizeof(program) - 1); - } - else - { - cost = get_integer(fp); - - if (!get_token(fp, program, sizeof(program))) - { - fprintf(stderr, - _("ppdc: Expected a program name on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - } - - if (!type[0]) - { - fprintf(stderr, - _("ppdc: Invalid empty MIME type for filter on line %d of " - "%s.\n"), fp->line, fp->filename); - return (NULL); - } - - if (cost < 0 || cost > 200) - { - fprintf(stderr, - _("ppdc: Invalid cost for filter on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if (!program[0]) - { - fprintf(stderr, - _("ppdc: Invalid empty program name for filter on line %d " - "of %s.\n"), fp->line, fp->filename); - return (NULL); - } - - return (new ppdcFilter(type, program, cost)); -} - - -// -// 'ppdcSource::get_float()' - Get a single floating-point number. -// - -float // O - Number -ppdcSource::get_float(ppdcFile *fp) // I - File to read -{ - char temp[256], // String buffer - *ptr; // Pointer into buffer - float val; // Floating point value - - - // Get the number from the file and range-check... - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, _("ppdc: Expected real number on line %d of %s.\n"), - fp->line, fp->filename); - return (-1.0f); - } - - val = (float)strtod(temp, &ptr); - - if (*ptr) - { - fprintf(stderr, - _("ppdc: Unknown trailing characters in real number \"%s\" " - "on line %d of %s.\n"), temp, fp->line, fp->filename); - return (-1.0f); - } - else - return (val); -} - - -// -// 'ppdcSource::get_font()' - Get a font definition. -// - -ppdcFont * // O - Font data -ppdcSource::get_font(ppdcFile *fp) // I - File to read -{ - char name[256], // Font name - encoding[256], // Font encoding - version[256], // Font version - charset[256], // Font charset - temp[256]; // Font status string - ppdcFontStatus status; // Font status enumeration - - - // Read font parameters as follows: - // - // Font * - // Font name encoding version charset status - // %font name encoding version charset status - // - // "Name" is the PostScript font name. - // - // "Encoding" is the default encoding of the font: Standard, ISOLatin1, - // Special, Expert, ExpertSubset, etc. - // - // "Version" is the version number string. - // - // "Charset" specifies the characters that are included in the font: - // Standard, Special, Expert, Adobe-Identity, etc. - // - // "Status" is the keyword ROM or Disk. - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name after Font on line %d of %s.\n"), - fp->line, fp->filename); - return (0); - } - - if (!strcmp(name, "*")) - { - // Include all base fonts... - encoding[0] = '\0'; - version[0] = '\0'; - charset[0] = '\0'; - status = PPDC_FONT_ROM; - } - else - { - // Load a full font definition... - if (!get_token(fp, encoding, sizeof(encoding))) - { - fprintf(stderr, - _("ppdc: Expected encoding after Font on line %d of " - "%s.\n"), fp->line, fp->filename); - return (0); - } - - if (!get_token(fp, version, sizeof(version))) - { - fprintf(stderr, - _("ppdc: Expected version after Font on line %d of " - "%s.\n"), fp->line, fp->filename); - return (0); - } - - if (!get_token(fp, charset, sizeof(charset))) - { - fprintf(stderr, - _("ppdc: Expected charset after Font on line %d of " - "%s.\n"), fp->line, fp->filename); - return (0); - } - - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected status after Font on line %d of %s.\n"), - fp->line, fp->filename); - return (0); - } - - if (!strcasecmp(temp, "ROM")) - status = PPDC_FONT_ROM; - else if (!strcasecmp(temp, "Disk")) - status = PPDC_FONT_DISK; - else - { - fprintf(stderr, - _("ppdc: Bad status keyword %s on line %d of %s.\n"), - temp, fp->line, fp->filename); - return (0); - } - } - -// printf("Font %s %s %s %s %s\n", name, encoding, version, charset, temp); - - return (new ppdcFont(name, encoding, version, charset, status)); -} - - -// -// 'ppdcSource::get_generic()' - Get a generic old-style option. -// - -ppdcChoice * // O - Choice data -ppdcSource::get_generic(ppdcFile *fp, // I - File to read - const char *keyword, - // I - Keyword name - const char *tattr, - // I - Text attribute - const char *nattr) - // I - Numeric attribute -{ - char name[1024], // Name - *text, // Text - command[2048]; // Command string - int val; // Numeric value - - - // Read one of the following parameters: - // - // Foo name/text - // Foo integer name/text - if (nattr) - val = get_integer(fp); - else - val = 0; - - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name/text after %s on line %d of %s.\n"), - keyword, fp->line, fp->filename); - return (NULL); - } - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - if (nattr) - { - if (tattr) - snprintf(command, sizeof(command), - "<>setpagedevice", - tattr, name, nattr, val); - else - snprintf(command, sizeof(command), - "<>setpagedevice", - nattr, val); - } - else - snprintf(command, sizeof(command), - "<>setpagedevice", - tattr, name); - - return (new ppdcChoice(name, text, command)); -} - - -// -// 'ppdcSource::get_group()' - Get an option group. -// - -ppdcGroup * // O - Group -ppdcSource::get_group(ppdcFile *fp, // I - File to read - ppdcDriver *d) // I - Printer driver -{ - char name[1024], // UI name - *text; // UI text - ppdcGroup *g; // Group - - - // Read the Group parameters: - // - // Group name/text - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected group name/text on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - // See if the group already exists... - if ((g = d->find_group(name)) == NULL) - { - // Nope, add a new one... - g = new ppdcGroup(name, text); - } - - return (g); -} - - -// -// 'ppdcSource::get_installable()' - Get an installable option. -// - -ppdcOption * // O - Option -ppdcSource::get_installable(ppdcFile *fp) - // I - File to read -{ - char name[1024], // Name for installable option - *text; // Text for installable option - ppdcOption *o; // Option - - - // Read the parameter for an installable option: - // - // Installable name/text - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name/text after Installable on line %d " - "of %s.\n"), fp->line, fp->filename); - return (NULL); - } - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - // Create the option... - o = new ppdcOption(PPDC_BOOLEAN, name, text, PPDC_SECTION_ANY, 10.0f); - - // Add the false and true choices... - o->add_choice(new ppdcChoice("False", "Not Installed", "")); - o->add_choice(new ppdcChoice("True", "Installed", "")); - - return (o); -} - - -// -// 'ppdcSource::get_integer()' - Get an integer value from a string. -// - -#define PPDC_XX -1 // Bad -#define PPDC_EQ 0 // == -#define PPDC_NE 1 // != -#define PPDC_LT 2 // < -#define PPDC_LE 3 // <= -#define PPDC_GT 4 // > -#define PPDC_GE 5 // >= - -int // O - Integer value -ppdcSource::get_integer(const char *v) // I - Value string -{ - long val; // Value - long temp, // Temporary value - temp2; // Second temporary value - char *newv, // New value string pointer - ch; // Temporary character - ppdcVariable *var; // #define variable - int compop; // Comparison operator - - - // Parse the value string... - if (!v) - return (-1); - - if (isdigit(*v & 255) || *v == '-' || *v == '+') - { - // Return a simple integer value - val = strtol(v, (char **)&v, 0); - if (*v || val == LONG_MIN) - return (-1); - else - return ((int)val); - } - else if (*v == '(') - { - // Evaluate and expression in any of the following formats: - // - // (number number ... number) Bitwise OR of all numbers - // (NAME == value) 1 if equal, 0 otherwise - // (NAME != value) 1 if not equal, 0 otherwise - // (NAME < value) 1 if less than, 0 otherwise - // (NAME <= value) 1 if less than or equal, 0 otherwise - // (NAME > value) 1 if greater than, 0 otherwise - // (NAME >= value) 1 if greater than or equal, 0 otherwise - - v ++; - val = 0; - - while (*v && *v != ')') - { - // Skip leading whitespace... - while (*v && isspace(*v & 255)) - v ++; - - if (!*v || *v == ')') - break; - - if (isdigit(*v & 255) || *v == '-' || *v == '+') - { - // Bitwise OR a number... - temp = strtol(v, &newv, 0); - - if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') || - temp == LONG_MIN) - return (-1); - } - else - { - // NAME logicop value - for (newv = (char *)v + 1; - *newv && (isalnum(*newv & 255) || *newv == '_'); - newv ++); - // do nothing - - ch = *newv; - *newv = '\0'; - - if ((var = find_variable(v)) != NULL) - { - if (!var->value || !var->value->value || !var->value->value[0]) - temp = 0; - else if (isdigit(var->value->value[0] & 255) || - var->value->value[0] == '-' || - var->value->value[0] == '+') - temp = strtol(var->value->value, NULL, 0); - else - temp = 1; - } - else - temp = 0; - - *newv = ch; - while (isspace(*newv & 255)) - newv ++; - - if (!strncmp(newv, "==", 2)) - { - compop = PPDC_EQ; - newv += 2; - } - else if (!strncmp(newv, "!=", 2)) - { - compop = PPDC_NE; - newv += 2; - } - else if (!strncmp(newv, "<=", 2)) - { - compop = PPDC_LE; - newv += 2; - } - else if (*newv == '<') - { - compop = PPDC_LT; - newv ++; - } - else if (!strncmp(newv, ">=", 2)) - { - compop = PPDC_GE; - newv += 2; - } - else if (*newv == '>') - { - compop = PPDC_GT; - newv ++; - } - else - compop = PPDC_XX; - - if (compop != PPDC_XX) - { - while (isspace(*newv & 255)) - newv ++; - - if (*newv == ')' || !*newv) - return (-1); - - if (isdigit(*newv & 255) || *newv == '-' || *newv == '+') - { - // Get the second number... - temp2 = strtol(newv, &newv, 0); - if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') || - temp == LONG_MIN) - return (-1); - } - else - { - // Lookup the second name... - for (v = newv, newv ++; - *newv && (isalnum(*newv & 255) || *newv == '_'); - newv ++); - - ch = *newv; - *newv = '\0'; - - if ((var = find_variable(v)) != NULL) - { - if (!var->value || !var->value->value || !var->value->value[0]) - temp2 = 0; - else if (isdigit(var->value->value[0] & 255) || - var->value->value[0] == '-' || - var->value->value[0] == '+') - temp2 = strtol(var->value->value, NULL, 0); - else - temp2 = 1; - } - else - temp2 = 0; - - *newv = ch; - } - - // Do the comparison... - switch (compop) - { - case PPDC_EQ : - temp = temp == temp2; - break; - case PPDC_NE : - temp = temp != temp2; - break; - case PPDC_LT : - temp = temp < temp2; - break; - case PPDC_LE : - temp = temp <= temp2; - break; - case PPDC_GT : - temp = temp > temp2; - break; - case PPDC_GE : - temp = temp >= temp2; - break; - } - } - } - - val |= temp; - v = newv; - } - - if (*v == ')' && !v[1]) - return ((int)val); - else - return (-1); - } - else if ((var = find_variable(v)) != NULL) - { - // NAME by itself returns 1 if the #define variable is not blank and - // not "0"... - return (var->value->value && var->value->value[0] && - strcmp(var->value->value, "0")); - } - else - { - // Anything else is an error... - return (-1); - } -} - - -// -// 'ppdcSource::get_integer()' - Get an integer value from a file. -// - -int // O - Integer value -ppdcSource::get_integer(ppdcFile *fp) // I - File to read -{ - char temp[1024]; // String buffer - - - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, _("ppdc: Expected integer on line %d of %s.\n"), - fp->line, fp->filename); - return (-1); - } - else - return (get_integer(temp)); -} - - -// -// 'ppdcSource::get_measurement()' - Get a measurement value. -// - -float // O - Measurement value in points -ppdcSource::get_measurement(ppdcFile *fp) - // I - File to read -{ - char buffer[256], // Number buffer - *ptr; // Pointer into buffer - float val; // Measurement value - - - // Grab a token from the file... - if (!get_token(fp, buffer, sizeof(buffer))) - return (-1.0f); - - // Get the floating point value of "s" and skip all digits and decimal points. - val = (float)strtod(buffer, &ptr); - - // Check for a trailing unit specifier... - if (!strcasecmp(ptr, "mm")) - val *= 72.0f / 25.4f; - else if (!strcasecmp(ptr, "cm")) - val *= 72.0f / 2.54f; - else if (!strcasecmp(ptr, "m")) - val *= 72.0f / 0.0254f; - else if (!strcasecmp(ptr, "in")) - val *= 72.0f; - else if (!strcasecmp(ptr, "ft")) - val *= 72.0f * 12.0f; - else if (strcasecmp(ptr, "pt") && *ptr) - return (-1.0f); - - return (val); -} - - -// -// 'ppdcSource::get_option()' - Get an option definition. -// - -ppdcOption * // O - Option -ppdcSource::get_option(ppdcFile *fp, // I - File to read - ppdcDriver *d, // I - Printer driver - ppdcGroup *g) // I - Current group -{ - char name[1024], // UI name - *text, // UI text - type[256]; // UI type string - ppdcOptType ot; // Option type value - ppdcOptSection section; // Option section - float order; // Option order - ppdcOption *o; // Option - ppdcGroup *mg; // Matching group, if any - - - // Read the Option parameters: - // - // Option name/text type section order - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected option name/text on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - if (!get_token(fp, type, sizeof(type))) - { - fprintf(stderr, _("ppdc: Expected option type on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if (!strcasecmp(type, "boolean")) - ot = PPDC_BOOLEAN; - else if (!strcasecmp(type, "pickone")) - ot = PPDC_PICKONE; - else if (!strcasecmp(type, "pickmany")) - ot = PPDC_PICKMANY; - else - { - fprintf(stderr, - _("ppdc: Invalid option type \"%s\" on line %d of %s.\n"), - type, fp->line, fp->filename); - return (NULL); - } - - if (!get_token(fp, type, sizeof(type))) - { - fprintf(stderr, - _("ppdc: Expected option section on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if (!strcasecmp(type, "AnySetup")) - section = PPDC_SECTION_ANY; - else if (!strcasecmp(type, "DocumentSetup")) - section = PPDC_SECTION_DOCUMENT; - else if (!strcasecmp(type, "ExitServer")) - section = PPDC_SECTION_EXIT; - else if (!strcasecmp(type, "JCLSetup")) - section = PPDC_SECTION_JCL; - else if (!strcasecmp(type, "PageSetup")) - section = PPDC_SECTION_PAGE; - else if (!strcasecmp(type, "Prolog")) - section = PPDC_SECTION_PROLOG; - else - { - fprintf(stderr, - _("ppdc: Invalid option section \"%s\" on line %d of " - "%s.\n"), type, fp->line, fp->filename); - return (NULL); - } - - order = get_float(fp); - - // See if the option already exists... - if ((o = d->find_option_group(name, &mg)) == NULL) - { - // Nope, add a new one... - o = new ppdcOption(ot, name, text, section, order); - } - else if (o->type != ot) - { - fprintf(stderr, - _("ppdc: Option %s redefined with a different type on line " - "%d of %s.\n"), name, fp->line, fp->filename); - return (NULL); - } - else if (g != mg) - { - fprintf(stderr, - _("ppdc: Option %s defined in two different groups on line " - "%d of %s.\n"), name, fp->line, fp->filename); - return (NULL); - } - - return (o); -} - - -// -// 'ppdcSource::get_po()' - Get a message catalog. -// - -ppdcCatalog * // O - Message catalog -ppdcSource::get_po(ppdcFile *fp) // I - File to read -{ - char locale[32], // Locale name - poname[1024], // Message catalog filename - basedir[1024], // Base directory - *baseptr, // Pointer into directory - pofilename[1024]; // Full filename of message catalog - ppdcCatalog *cat; // Message catalog - - - // Read the #po parameters: - // - // #po locale "filename.po" - if (!get_token(fp, locale, sizeof(locale))) - { - fprintf(stderr, - _("ppdc: Expected locale after #po on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if (!get_token(fp, poname, sizeof(poname))) - { - fprintf(stderr, - _("ppdc: Expected filename after #po %s on line %d of " - "%s.\n"), locale, fp->line, fp->filename); - return (NULL); - } - - // See if the locale is already loaded... - if (find_po(locale)) - { - fprintf(stderr, - _("ppdc: Duplicate #po for locale %s on line %d of %s.\n"), - locale, fp->line, fp->filename); - return (NULL); - } - - // Figure out the current directory... - strncpy(basedir, fp->filename, sizeof(basedir) - 1); - - if ((baseptr = strrchr(basedir, '/')) != NULL) - *baseptr = '\0'; - else - strncpy(basedir, ".", sizeof(basedir)); - - // Find the po file... - pofilename[0] = '\0'; - - if (!poname[0] || - find_include(poname, basedir, pofilename, sizeof(pofilename))) - { - // Found it, so load it... - cat = new ppdcCatalog(locale, pofilename); - - // Reset the filename to the name supplied by the user... - cat->filename->release(); - cat->filename = new ppdcString(poname); - - // Return the catalog... - return (cat); - } - else - { - fprintf(stderr, - _("ppdc: Unable to find #po file %s on line %d of %s.\n"), - poname, fp->line, fp->filename); - return (NULL); - } -} - - -// -// 'ppdcSource::get_resolution()' - Get an old-style resolution option. -// - -ppdcChoice * // O - Choice data -ppdcSource::get_resolution(ppdcFile *fp)// I - File to read -{ - char name[1024], // Name - *text, // Text - temp[256], // Temporary string - command[256], // Command string - *commptr; // Pointer into command - int xdpi, ydpi, // X + Y resolution - color_order, // Color order - color_space, // Colorspace - compression, // Compression mode - depth, // Bits per color - row_count, // Row count - row_feed, // Row feed - row_step; // Row step/interval - - - // Read the resolution parameters: - // - // Resolution colorspace bits row-count row-feed row-step name/text - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected override field after Resolution on line " - "%d of %s.\n"), fp->line, fp->filename); - return (NULL); - } - - color_order = get_color_order(temp); - color_space = get_color_space(temp); - compression = get_integer(temp); - - depth = get_integer(fp); - row_count = get_integer(fp); - row_feed = get_integer(fp); - row_step = get_integer(fp); - - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name/text after Resolution on line %d of " - "%s.\n"), fp->line, fp->filename); - return (NULL); - } - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - switch (sscanf(name, "%dx%d", &xdpi, &ydpi)) - { - case 1 : - ydpi = xdpi; - break; - case 2 : - break; - default : - fprintf(stderr, - _("ppdc: Bad resolution name \"%s\" on line %d of " - "%s.\n"), name, fp->line, fp->filename); - break; -} - - // Create the necessary PS commands... - snprintf(command, sizeof(command), - "<= 0) - { - snprintf(commptr, sizeof(command) - (size_t)(commptr - command), - "/cupsColorOrder %d", color_order); - commptr += strlen(commptr); - } - - if (color_space >= 0) - { - snprintf(commptr, sizeof(command) - (size_t)(commptr - command), - "/cupsColorSpace %d", color_space); - commptr += strlen(commptr); - } - - if (compression >= 0) - { - snprintf(commptr, sizeof(command) - (size_t)(commptr - command), - "/cupsCompression %d", compression); - commptr += strlen(commptr); - } - - snprintf(commptr, sizeof(command) - (size_t)(commptr - command), ">>setpagedevice"); - - // Return the new choice... - return (new ppdcChoice(name, text, command)); -} - - -// -// 'ppdcSource::get_simple_profile()' - Get a simple color profile definition. -// - -ppdcProfile * // O - Color profile -ppdcSource::get_simple_profile(ppdcFile *fp) - // I - File to read -{ - char resolution[1024], // Resolution/media type - *media_type; // Media type - float m[9]; // Transform matrix - float kd, rd, g; // Densities and gamma - float red, green, blue; // RGB adjustments - float yellow; // Yellow density - float color; // Color density values - - - // Get the SimpleColorProfile parameters: - // - // SimpleColorProfile resolution/mediatype black-density yellow-density - // red-density gamma red-adjust green-adjust blue-adjust - if (!get_token(fp, resolution, sizeof(resolution))) - { - fprintf(stderr, - _("ppdc: Expected resolution/mediatype following " - "SimpleColorProfile on line %d of %s.\n"), - fp->line, fp->filename); - return (NULL); - } - - if ((media_type = strchr(resolution, '/')) != NULL) - *media_type++ = '\0'; - else - media_type = resolution; - - // Collect the profile parameters... - kd = get_float(fp); - yellow = get_float(fp); - rd = get_float(fp); - g = get_float(fp); - red = get_float(fp); - green = get_float(fp); - blue = get_float(fp); - - // Build the color profile... - color = 0.5f * rd / kd - kd; - m[0] = 1.0f; // C - m[1] = color + blue; // C + M (blue) - m[2] = color - green; // C + Y (green) - m[3] = color - blue; // M + C (blue) - m[4] = 1.0f; // M - m[5] = color + red; // M + Y (red) - m[6] = yellow * (color + green); // Y + C (green) - m[7] = yellow * (color - red); // Y + M (red) - m[8] = yellow; // Y - - if (m[1] > 0.0f) - { - m[3] -= m[1]; - m[1] = 0.0f; - } - else if (m[3] > 0.0f) - { - m[1] -= m[3]; - m[3] = 0.0f; - } - - if (m[2] > 0.0f) - { - m[6] -= m[2]; - m[2] = 0.0f; - } - else if (m[6] > 0.0f) - { - m[2] -= m[6]; - m[6] = 0.0f; - } - - if (m[5] > 0.0f) - { - m[7] -= m[5]; - m[5] = 0.0f; - } - else if (m[7] > 0.0f) - { - m[5] -= m[7]; - m[7] = 0.0f; - } - - // Return the new profile... - return (new ppdcProfile(resolution, media_type, kd, g, m)); -} - - -// -// 'ppdcSource::get_size()' - Get a media size definition from a file. -// - -ppdcMediaSize * // O - Media size -ppdcSource::get_size(ppdcFile *fp) // I - File to read -{ - char name[1024], // Name - *text; // Text - float width, // Width - length; // Length - - - // Get the name, text, width, and length: - // - // #media name/text width length - if (!get_token(fp, name, sizeof(name))) - return (NULL); - - if ((text = strchr(name, '/')) != NULL) - *text++ = '\0'; - else - text = name; - - if ((width = get_measurement(fp)) < 0.0f) - return (NULL); - - if ((length = get_measurement(fp)) < 0.0f) - return (NULL); - - // Return the new media size... - return (new ppdcMediaSize(name, text, width, length, 0.0f, 0.0f, 0.0f, 0.0f)); -} - - -// -// 'ppdcSource::get_token()' - Get a token from a file. -// - -char * // O - Token string or NULL -ppdcSource::get_token(ppdcFile *fp, // I - File to read - char *buffer, // I - Buffer - int buflen) // I - Length of buffer -{ - char *bufptr, // Pointer into string buffer - *bufend; // End of string buffer - int ch, // Character from file - nextch, // Next char in file - quote, // Quote character used... - empty, // Empty input? - startline; // Start line for quote - char name[256], // Name string - *nameptr; // Name pointer - ppdcVariable *var; // Variable pointer - - - // Mark the beginning and end of the buffer... - bufptr = buffer; - bufend = buffer + buflen - 1; - - // Loop intil we've read a token... - quote = 0; - startline = 0; - empty = 1; - - while ((ch = fp->get()) != EOF) - { - if (isspace(ch) && !quote) - { - if (empty) - continue; - else - break; - } - else if (ch == '$') - { - // Variable substitution - empty = 0; - - for (nameptr = name; (ch = fp->peek()) != EOF;) - { - if (!isalnum(ch) && ch != '_') - break; - else if (nameptr < (name + sizeof(name) - 1)) - *nameptr++ = (char)fp->get(); - } - - if (nameptr == name) - { - // Just substitute this character... - if (ch == '$') - { - // $$ = $ - if (bufptr < bufend) - *bufptr++ = (char)fp->get(); - } - else - { - // $ch = $ch - fprintf(stderr, - _("ppdc: Bad variable substitution ($%c) on line %d " - "of %s.\n"), ch, fp->line, fp->filename); - - if (bufptr < bufend) - *bufptr++ = '$'; - } - } - else - { - // Substitute the variable value... - *nameptr = '\0'; - var = find_variable(name); - if (var) - { - strncpy(bufptr, var->value->value, (size_t)(bufend - bufptr + 1)); - bufptr += strlen(bufptr); - } - else - { - if (!(cond_state & PPDC_COND_SKIP)) - fprintf(stderr, - _("ppdc: Undefined variable (%s) on line %d of " - "%s.\n"), name, fp->line, fp->filename); - - snprintf(bufptr, (size_t)(bufend - bufptr + 1), "$%s", name); - bufptr += strlen(bufptr); - } - } - } - else if (ch == '/' && !quote) - { - // Possibly a comment... - nextch = fp->peek(); - - if (nextch == '*') - { - // C comment... - fp->get(); - ch = fp->get(); - while ((nextch = fp->get()) != EOF) - { - if (ch == '*' && nextch == '/') - break; - - ch = nextch; - } - - if (nextch == EOF) - break; - } - else if (nextch == '/') - { - // C++ comment... - while ((nextch = fp->get()) != EOF) - if (nextch == '\n') - break; - - if (nextch == EOF) - break; - } - else - { - // Not a comment... - empty = 0; - - if (bufptr < bufend) - *bufptr++ = (char)ch; - } - } - else if (ch == '\'' || ch == '\"') - { - empty = 0; - - if (quote == ch) - { - // Ending the current quoted string... - quote = 0; - } - else if (quote) - { - // Insert the opposing quote char... - if (bufptr < bufend) - *bufptr++ = (char)ch; - } - else - { - // Start a new quoted string... - startline = fp->line; - quote = ch; - } - } - else if ((ch == '(' || ch == '<') && !quote) - { - empty = 0; - quote = ch; - startline = fp->line; - - if (bufptr < bufend) - *bufptr++ = (char)ch; - } - else if ((ch == ')' && quote == '(') || (ch == '>' && quote == '<')) - { - quote = 0; - - if (bufptr < bufend) - *bufptr++ = (char)ch; - } - else if (ch == '\\') - { - empty = 0; - - if ((ch = fp->get()) == EOF) - break; - - if (bufptr < bufend) - *bufptr++ = (char)ch; - } - else if (bufptr < bufend) - { - empty = 0; - - *bufptr++ = (char)ch; - - if ((ch == '{' || ch == '}') && !quote) - break; - } - } - - if (quote) - { - fprintf(stderr, - _("ppdc: Unterminated string starting with %c on line %d " - "of %s.\n"), quote, startline, fp->filename); - return (NULL); - } - - if (empty) - return (NULL); - else - { - *bufptr = '\0'; - return (buffer); - } -} - - -// -// 'ppdcSource::get_variable()' - Get a variable definition. -// - -ppdcVariable * // O - Variable -ppdcSource::get_variable(ppdcFile *fp) // I - File to read -{ - char name[1024], // Name - value[1024]; // Value - - - // Get the name and value: - // - // #define name value - if (!get_token(fp, name, sizeof(name))) - return (NULL); - - if (!get_token(fp, value, sizeof(value))) - return (NULL); - - // Set the variable... - return (set_variable(name, value)); -} - - -// -// 'ppdcSource::quotef()' - Write a formatted, quoted string... -// - -int // O - Number bytes on success, -1 on failure -ppdcSource::quotef(cups_file_t *fp, // I - File to write to - const char *format, // I - Printf-style format string - ...) // I - Additional args as needed -{ - va_list ap; // Pointer to additional arguments - int bytes; // Bytes written - char 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 fprintf() - char *s; // Pointer to string - int slen; // Length of string - int i; // Looping var - - - // Range check input... - if (!fp || !format) - return (-1); - - // Loop through the format string, formatting as needed... - va_start(ap, format); - - bytes = 0; - - while (*format) - { - if (*format == '%') - { - bufformat = format; - format ++; - - if (*format == '%') - { - cupsFilePutChar(fp, *format++); - bytes ++; - 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++; - else - size = '\0'; - - if (!*format) - break; - - type = *format++; - - switch (type) - { - case 'E' : // Floating point formats - case 'G' : - case 'e' : - case 'f' : - case 'g' : - if ((format - bufformat + 1) > (int)sizeof(tformat)) - break; - - memcpy(tformat, bufformat, (size_t)(format - bufformat)); - tformat[format - bufformat] = '\0'; - - bytes += cupsFilePrintf(fp, tformat, va_arg(ap, double)); - break; - - case 'B' : // Integer formats - case 'X' : - case 'b' : - case 'd' : - case 'i' : - case 'o' : - case 'u' : - case 'x' : - if ((format - bufformat + 1) > (int)sizeof(tformat)) - break; - - memcpy(tformat, bufformat, (size_t)(format - bufformat)); - tformat[format - bufformat] = '\0'; - -# ifdef HAVE_LONG_LONG - if (size == 'L') - bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long long)); - else -# endif // HAVE_LONG_LONG - if (size == 'l') - bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long)); - else - bytes += cupsFilePrintf(fp, tformat, va_arg(ap, int)); - break; - - case 'p' : // Pointer value - if ((format - bufformat + 1) > (int)sizeof(tformat)) - break; - - memcpy(tformat, bufformat, (size_t)(format - bufformat)); - tformat[format - bufformat] = '\0'; - - bytes += cupsFilePrintf(fp, tformat, va_arg(ap, void *)); - break; - - case 'c' : // Character or character array - if (width <= 1) - { - bytes ++; - cupsFilePutChar(fp, va_arg(ap, int)); - } - else - { - cupsFileWrite(fp, va_arg(ap, char *), (size_t)width); - bytes += width; - } - break; - - case 's' : // String - if ((s = va_arg(ap, char *)) == NULL) - s = (char *)"(nil)"; - - slen = (int)strlen(s); - if (slen > width && prec != width) - width = slen; - - if (slen > width) - slen = width; - - if (sign != '-') - { - for (i = width - slen; i > 0; i --, bytes ++) - cupsFilePutChar(fp, ' '); - } - - for (i = slen; i > 0; i --, s ++, bytes ++) - { - if (*s == '\\' || *s == '\"') - { - cupsFilePutChar(fp, '\\'); - bytes ++; - } - - cupsFilePutChar(fp, *s); - } - - if (sign == '-') - { - for (i = width - slen; i > 0; i --, bytes ++) - cupsFilePutChar(fp, ' '); - } - break; - } - } - else - { - cupsFilePutChar(fp, *format++); - bytes ++; - } - } - - va_end(ap); - - // Return the number of characters written. - return (bytes); -} - - -// -// 'ppdcSource::read_file()' - Read a driver source file. -// - -void -ppdcSource::read_file(const char *f, // I - File to read - cups_file_t *ffp) // I - File pointer to use -{ - ppdcFile *fp = new ppdcFile(f, ffp); - scan_file(fp); - delete fp; - - if (cond_current != cond_stack) - fprintf(stderr, _("ppdc: Missing #endif at end of \"%s\".\n"), f); -} - - -// -// 'ppdcSource::scan_file()' - Scan a driver source file. -// - -void -ppdcSource::scan_file(ppdcFile *fp, // I - File to read - ppdcDriver *td, // I - Driver template - bool inc) // I - Including? -{ - ppdcDriver *d; // Current driver - ppdcGroup *g, // Current group - *mg, // Matching group - *general, // General options group - *install; // Installable options group - ppdcOption *o; // Current option - ppdcChoice *c; // Current choice - char temp[256], // Token from file... - *ptr; // Pointer into token - int isdefault; // Default option? - - - // Initialize things as needed... - if (inc && td) - { - d = td; - d->retain(); - } - else - d = new ppdcDriver(td); - - if ((general = d->find_group("General")) == NULL) - { - general = new ppdcGroup("General", NULL); - d->add_group(general); - } - - if ((install = d->find_group("InstallableOptions")) == NULL) - { - install = new ppdcGroup("InstallableOptions", "Installable Options"); - d->add_group(install); - } - - // Loop until EOF or } - o = 0; - g = general; - - while (get_token(fp, temp, sizeof(temp))) - { - if (temp[0] == '*') - { - // Mark the next choice as the default - isdefault = 1; - - for (ptr = temp; ptr[1]; ptr ++) - *ptr = ptr[1]; - - *ptr = '\0'; - } - else - { - // Don't mark the next choice as the default - isdefault = 0; - } - - if (!strcasecmp(temp, "}")) - { - // Close this one out... - break; - } - else if (!strcasecmp(temp, "{")) - { - // Open a new child... - scan_file(fp, d); - } - else if (!strcasecmp(temp, "#if")) - { - if ((cond_current - cond_stack) >= 100) - { - fprintf(stderr, - _("ppdc: Too many nested #if's on line %d of %s.\n"), - fp->line, fp->filename); - break; - } - - cond_current ++; - if (get_integer(fp) > 0) - *cond_current = PPDC_COND_SATISFIED; - else - { - *cond_current = PPDC_COND_SKIP; - cond_state |= PPDC_COND_SKIP; - } - } - else if (!strcasecmp(temp, "#elif")) - { - if (cond_current == cond_stack) - { - fprintf(stderr, _("ppdc: Missing #if on line %d of %s.\n"), - fp->line, fp->filename); - break; - } - - if (*cond_current & PPDC_COND_SATISFIED) - { - get_integer(fp); - *cond_current |= PPDC_COND_SKIP; - } - else if (get_integer(fp) > 0) - { - *cond_current |= PPDC_COND_SATISFIED; - *cond_current &= ~PPDC_COND_SKIP; - } - else - *cond_current |= PPDC_COND_SKIP; - - // Update the current state - int *cond_temp = cond_current; // Temporary stack pointer - - cond_state = PPDC_COND_NORMAL; - while (cond_temp > cond_stack) - if (*cond_temp & PPDC_COND_SKIP) - { - cond_state = PPDC_COND_SKIP; - break; - } - else - cond_temp --; - } - else if (!strcasecmp(temp, "#else")) - { - if (cond_current == cond_stack) - { - fprintf(stderr, _("ppdc: Missing #if on line %d of %s.\n"), - fp->line, fp->filename); - break; - } - - if (*cond_current & PPDC_COND_SATISFIED) - *cond_current |= PPDC_COND_SKIP; - else - { - *cond_current |= PPDC_COND_SATISFIED; - *cond_current &= ~PPDC_COND_SKIP; - } - - // Update the current state - int *cond_temp = cond_current; // Temporary stack pointer - - cond_state = PPDC_COND_NORMAL; - while (cond_temp > cond_stack) - if (*cond_temp & PPDC_COND_SKIP) - { - cond_state = PPDC_COND_SKIP; - break; - } - else - cond_temp --; - } - else if (!strcasecmp(temp, "#endif")) - { - if (cond_current == cond_stack) - { - fprintf(stderr, _("ppdc: Missing #if on line %d of %s.\n"), - fp->line, fp->filename); - break; - } - - cond_current --; - - // Update the current state - int *cond_temp = cond_current; // Temporary stack pointer - - cond_state = PPDC_COND_NORMAL; - while (cond_temp > cond_stack) - if (*cond_temp & PPDC_COND_SKIP) - { - cond_state = PPDC_COND_SKIP; - break; - } - else - cond_temp --; - } - else if (!strcasecmp(temp, "#define")) - { - // Get the variable... - get_variable(fp); - } - else if (!strcasecmp(temp, "#include")) - { - // #include filename - char basedir[1024], // Base directory - *baseptr, // Pointer into directory - inctemp[1024], // Initial filename - incname[1024]; // Include filename - ppdcFile *incfile; // Include file - int *old_current = cond_current; - // Previous current stack - - - // Get the include name... - if (!get_token(fp, inctemp, sizeof(inctemp))) - { - fprintf(stderr, - _("ppdc: Expected include filename on line %d of " - "%s.\n"), fp->line, fp->filename); - break; - } - - if (cond_state) - continue; - - // Figure out the current directory... - strncpy(basedir, fp->filename, sizeof(basedir) - 1); - - if ((baseptr = strrchr(basedir, '/')) != NULL) - *baseptr = '\0'; - else - strncpy(basedir, ".", sizeof(basedir)); - - // Find the include file... - if (find_include(inctemp, basedir, incname, sizeof(incname))) - { - // Open the include file, scan it, and then close it... - incfile = new ppdcFile(incname); - scan_file(incfile, d, true); - delete incfile; - - if (cond_current != old_current) - fprintf(stderr, _("ppdc: Missing #endif at end of \"%s\".\n"), - incname); - } - else - { - // Can't find it! - fprintf(stderr, - _("ppdc: Unable to find include file \"%s\" on line %d " - "of %s.\n"), inctemp, fp->line, fp->filename); - break; - } - } - else if (!strcasecmp(temp, "#media")) - { - ppdcMediaSize *m; // Media size - - - // Get a media size... - m = get_size(fp); - if (m) - { - if (cond_state) - m->release(); - else - sizes->add(m); - } - } - else if (!strcasecmp(temp, "#po")) - { - ppdcCatalog *cat; // Message catalog - - - // Get a message catalog... - cat = get_po(fp); - if (cat) - { - if (cond_state) - cat->release(); - else - po_files->add(cat); - } - } - else if (!strcasecmp(temp, "Attribute") || - !strcasecmp(temp, "LocAttribute")) - { - ppdcAttr *a; // Attribute - - - // Get an attribute... - a = get_attr(fp, !strcasecmp(temp, "LocAttribute")); - if (a) - { - if (cond_state) - a->release(); - else - d->add_attr(a); - } - } - else if (!strcasecmp(temp, "Choice")) - { - // Get a choice... - c = get_choice(fp); - if (!c) - break; - - if (cond_state) - { - c->release(); - continue; - } - - // Add it to the current option... - if (!o) - { - c->release(); - fprintf(stderr, - _("ppdc: Choice found on line %d of %s with no " - "Option.\n"), fp->line, fp->filename); - break; - } - - o->add_choice(c); - - if (isdefault) - o->set_defchoice(c); - } - else if (!strcasecmp(temp, "ColorDevice")) - { - // ColorDevice boolean - if (cond_state) - get_boolean(fp); - else - d->color_device = get_boolean(fp); - } - else if (!strcasecmp(temp, "ColorModel")) - { - // Get the color model - c = get_color_model(fp); - if (!c) - continue; - - if (cond_state) - { - c->release(); - continue; - } - - // Add the choice to the ColorModel option... - if ((o = d->find_option("ColorModel")) == NULL) - { - // Create the ColorModel option... - o = new ppdcOption(PPDC_PICKONE, "ColorModel", "Color Mode", PPDC_SECTION_ANY, 10.0f); - g = general; - g->add_option(o); - } - - o->add_choice(c); - - if (isdefault) - o->set_defchoice(c); - - o = NULL; - } - else if (!strcasecmp(temp, "ColorProfile")) - { - ppdcProfile *p; // Color profile - - - // Get the color profile... - p = get_color_profile(fp); - - if (p) - { - if (cond_state) - p->release(); - else - d->profiles->add(p); - } - } - else if (!strcasecmp(temp, "Copyright")) - { - // Copyright string - char copytemp[8192], // Copyright string - *copyptr, // Pointer into string - *copyend; // Pointer to end of string - - - // Get the copyright string... - if (!get_token(fp, copytemp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected string after Copyright on line %d " - "of %s.\n"), fp->line, fp->filename); - break; - } - - if (cond_state) - continue; - - // Break it up into individual lines... - for (copyptr = copytemp; copyptr; copyptr = copyend) - { - if ((copyend = strchr(copyptr, '\n')) != NULL) - *copyend++ = '\0'; - - d->copyright->add(new ppdcString(copyptr)); - } - } - else if (!strcasecmp(temp, "CustomMedia")) - { - ppdcMediaSize *m; // Media size - - - // Get a custom media size... - m = get_custom_size(fp); - - if (cond_state) - { - m->release(); - continue; - } - - if (m) - d->sizes->add(m); - - if (isdefault) - d->set_default_size(m); - } - else if (!strcasecmp(temp, "Cutter")) - { - // Cutter boolean - int have_cutter; // Have a paper cutter? - - - have_cutter = get_boolean(fp); - if (have_cutter <= 0 || cond_state) - continue; - - if (!d->find_option("CutMedia")) - { - o = new ppdcOption(PPDC_BOOLEAN, "CutMedia", "Cut Media", PPDC_SECTION_ANY, 10.0f); - - g = general; - g->add_option(o); - - c = new ppdcChoice("False", NULL, "<>setpagedevice"); - o->add_choice(c); - o->set_defchoice(c); - - c = new ppdcChoice("True", NULL, "<>setpagedevice"); - o->add_choice(c); - o = NULL; - } - } - else if (!strcasecmp(temp, "Darkness")) - { - // Get the darkness choice... - c = get_generic(fp, "Darkness", NULL, "cupsCompression"); - if (!c) - continue; - - if (cond_state) - { - c->release(); - continue; - } - - // Add the choice to the cupsDarkness option... - if ((o = d->find_option_group("cupsDarkness", &mg)) == NULL) - { - // Create the cupsDarkness option... - o = new ppdcOption(PPDC_PICKONE, "cupsDarkness", "Darkness", PPDC_SECTION_ANY, 10.0f); - g = general; - g->add_option(o); - } - else if (mg != general) - { - fprintf(stderr, - _("ppdc: Option %s defined in two different groups on " - "line %d of %s.\n"), "cupsDarkness", fp->line, - fp->filename); - c->release(); - continue; - } - - o->add_choice(c); - - if (isdefault) - o->set_defchoice(c); - - o = NULL; - } - else if (!strcasecmp(temp, "DriverType")) - { - int i; // Looping var - - - // DriverType keyword - if (!get_token(fp, temp, sizeof(temp))) - { - fprintf(stderr, - _("ppdc: Expected driver type keyword following " - "DriverType on line %d of %s.\n"), - fp->line, fp->filename); - continue; - } - - if (cond_state) - continue; - - for (i = 0; i < (int)(sizeof(driver_types) / sizeof(driver_types[0])); i ++) - if (!strcasecmp(temp, driver_types[i])) - break; - - if (i < (int)(sizeof(driver_types) / sizeof(driver_types[0]))) - d->type = (ppdcDrvType)i; - else if (!strcasecmp(temp, "dymo")) - d->type = PPDC_DRIVER_LABEL; - else - fprintf(stderr, - _("ppdc: Unknown driver type %s on line %d of %s.\n"), - temp, fp->line, fp->filename); - } - else if (!strcasecmp(temp, "Duplex")) - get_duplex(fp, d); - else if (!strcasecmp(temp, "Filter")) - { - ppdcFilter *f; // Filter - - - // Get the filter value... - f = get_filter(fp); - if (f) - { - if (cond_state) - f->release(); - else - d->filters->add(f); - } - } - else if (!strcasecmp(temp, "Finishing")) - { - // Get the finishing choice... - c = get_generic(fp, "Finishing", "OutputType", NULL); - if (!c) - continue; - - if (cond_state) - { - c->release(); - continue; - } - - // Add the choice to the cupsFinishing option... - if ((o = d->find_option_group("cupsFinishing", &mg)) == NULL) - { - // Create the cupsFinishing option... - o = new ppdcOption(PPDC_PICKONE, "cupsFinishing", "Finishing", PPDC_SECTION_ANY, 10.0f); - g = general; - g->add_option(o); - } - else if (mg != general) - { - fprintf(stderr, - _("ppdc: Option %s defined in two different groups on " - "line %d of %s.\n"), "cupsFinishing", fp->line, - fp->filename); - c->release(); - continue; - } - - o->add_choice(c); - - if (isdefault) - o->set_defchoice(c); - - o = NULL; - } - else if (!strcasecmp(temp, "Font") || - !strcasecmp(temp, "#font")) - { - ppdcFont *f; // Font - - - // Get a font... - f = get_font(fp); - if (f) - { - if (cond_state) - f->release(); - else - { - if (!strcasecmp(temp, "#font")) - base_fonts->add(f); - else - d->add_font(f); - - if (isdefault) - d->set_default_font(f); - } - } - } - else if (!strcasecmp(temp, "Group")) - { - // Get a group... - ppdcGroup *tempg = get_group(fp, d); - - if (!tempg) - break; - - if (cond_state) - { - if (!d->find_group(tempg->name->value)) - tempg->release(); - } - else - { - if (!d->find_group(tempg->name->value)) - d->add_group(tempg); - - g = tempg; - } - } - else if (!strcasecmp(temp, "HWMargins")) - { - // HWMargins left bottom right top - d->left_margin = get_measurement(fp); - d->bottom_margin = get_measurement(fp); - d->right_margin = get_measurement(fp); - d->top_margin = get_measurement(fp); - } - else if (!strcasecmp(temp, "InputSlot")) - { - // Get the input slot choice... - c = get_generic(fp, "InputSlot", NULL, "MediaPosition"); - if (!c) - continue; - - if (cond_state) - { - c->release(); - continue; - } - - // Add the choice to the InputSlot option... - - if ((o = d->find_option_group("InputSlot", &mg)) == NULL) - { - // Create the InputSlot option... - o = new ppdcOption(PPDC_PICKONE, "InputSlot", "Media Source", - PPDC_SECTION_ANY, 10.0f); - g = general; - g->add_option(o); - } - else if (mg != general) - { - fprintf(stderr, - _("ppdc: Option %s defined in two different groups on " - "line %d of %s.\n"), "InputSlot", fp->line, - fp->filename); - c->release(); - continue; - } - - o->add_choice(c); - - if (isdefault) - o->set_defchoice(c); - - o = NULL; - } - else if (!strcasecmp(temp, "Installable")) - { - // Get the installable option... - o = get_installable(fp); - - // Add it as needed... - if (o) - { - if (cond_state) - o->release(); - else - install->add_option(o); - - o = NULL; - } - } - else if (!strcasecmp(temp, "ManualCopies")) - { - // ManualCopies boolean - if (cond_state) - get_boolean(fp); - else - d->manual_copies = get_boolean(fp); - } - else if (!strcasecmp(temp, "Manufacturer")) - { - // Manufacturer name - char name[256]; // Model name string - - - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name after Manufacturer on line %d " - "of %s.\n"), fp->line, fp->filename); - break; - } - - if (!cond_state) - d->set_manufacturer(name); - } - else if (!strcasecmp(temp, "MaxSize")) - { - // MaxSize width length - if (cond_state) - { - get_measurement(fp); - get_measurement(fp); - } - else - { - d->max_width = get_measurement(fp); - d->max_length = get_measurement(fp); - } - } - else if (!strcasecmp(temp, "MediaSize")) - { - // MediaSize keyword - char name[41]; // Media size name - ppdcMediaSize *m, // Matching media size... - *dm; // Driver media size... - - - if (get_token(fp, name, sizeof(name)) == NULL) - { - fprintf(stderr, - _("ppdc: Expected name after MediaSize on line %d of " - "%s.\n"), fp->line, fp->filename); - break; - } - - if (cond_state) - continue; - - m = find_size(name); - - if (!m) - { - fprintf(stderr, - _("ppdc: Unknown media size \"%s\" on line %d of " - "%s.\n"), name, fp->line, fp->filename); - break; - } - - // Add this size to the driver... - dm = new ppdcMediaSize(m->name->value, m->text->value, - m->width, m->length, d->left_margin, - d->bottom_margin, d->right_margin, - d->top_margin); - d->sizes->add(dm); - - if (isdefault) - d->set_default_size(dm); - } - else if (!strcasecmp(temp, "MediaType")) - { - // Get the media type choice... - c = get_generic(fp, "MediaType", "MediaType", "cupsMediaType"); - if (!c) - continue; - - if (cond_state) - { - c->release(); - continue; - } - - // Add the choice to the MediaType option... - if ((o = d->find_option_group("MediaType", &mg)) == NULL) - { - // Create the MediaType option... - o = new ppdcOption(PPDC_PICKONE, "MediaType", "Media Type", - PPDC_SECTION_ANY, 10.0f); - g = general; - g->add_option(o); - } - else if (mg != general) - { - fprintf(stderr, - _("ppdc: Option %s defined in two different groups on " - "line %d of %s.\n"), "MediaType", fp->line, - fp->filename); - c->release(); - continue; - } - - o->add_choice(c); - - if (isdefault) - o->set_defchoice(c); - - o = NULL; - } - else if (!strcasecmp(temp, "MinSize")) - { - // MinSize width length - if (cond_state) - { - get_measurement(fp); - get_measurement(fp); - } - else - { - d->min_width = get_measurement(fp); - d->min_length = get_measurement(fp); - } - } - else if (!strcasecmp(temp, "ModelName")) - { - // ModelName name - char name[256]; // Model name string - - - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name after ModelName on line %d of " - "%s.\n"), fp->line, fp->filename); - break; - } - - if (!cond_state) - d->set_model_name(name); - } - else if (!strcasecmp(temp, "ModelNumber")) - { - // ModelNumber number - if (cond_state) - get_integer(fp); - else - d->model_number = get_integer(fp); - } - else if (!strcasecmp(temp, "Option")) - { - // Get an option... - ppdcOption *tempo = get_option(fp, d, g); - - if (!tempo) - break; - - if (cond_state) - { - if (!g->find_option(tempo->name->value)) - tempo->release(); - } - else - { - if (!g->find_option(tempo->name->value)) - g->add_option(tempo); - - o = tempo; - } - } - else if (!strcasecmp(temp, "FileName")) - { - // FileName name - char name[256]; // Filename string - - - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name after FileName on line %d of " - "%s.\n"), fp->line, fp->filename); - break; - } - - if (!cond_state) - d->set_file_name(name); - } - else if (!strcasecmp(temp, "PCFileName")) - { - // PCFileName name - char name[256]; // PC filename string - - - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected name after PCFileName on line %d of " - "%s.\n"), fp->line, fp->filename); - break; - } - - if (!cond_state) - d->set_pc_file_name(name); - } - else if (!strcasecmp(temp, "Resolution")) - { - // Get the resolution choice... - c = get_resolution(fp); - if (!c) - continue; - - if (cond_state) - { - c->release(); - continue; - } - - // Add the choice to the Resolution option... - if ((o = d->find_option_group("Resolution", &mg)) == NULL) - { - // Create the Resolution option... - o = new ppdcOption(PPDC_PICKONE, "Resolution", NULL, PPDC_SECTION_ANY, - 10.0f); - g = general; - g->add_option(o); - } - else if (mg != general) - { - fprintf(stderr, - _("ppdc: Option %s defined in two different groups on " - "line %d of %s.\n"), "Resolution", fp->line, - fp->filename); - c->release(); - continue; - } - - o->add_choice(c); - - if (isdefault) - o->set_defchoice(c); - - o = NULL; - } - else if (!strcasecmp(temp, "SimpleColorProfile")) - { - ppdcProfile *p; // Color profile - - - // Get the color profile... - p = get_simple_profile(fp); - - if (p) - { - if (cond_state) - p->release(); - else - d->profiles->add(p); - } - } - else if (!strcasecmp(temp, "Throughput")) - { - // Throughput number - if (cond_state) - get_integer(fp); - else - d->throughput = get_integer(fp); - } - else if (!strcasecmp(temp, "UIConstraints")) - { - ppdcConstraint *con; // Constraint - - - con = get_constraint(fp); - - if (con) - { - if (cond_state) - con->release(); - else - d->constraints->add(con); - } - } - else if (!strcasecmp(temp, "VariablePaperSize")) - { - // VariablePaperSize boolean - if (cond_state) - get_boolean(fp); - else - d->variable_paper_size = get_boolean(fp); - } - else if (!strcasecmp(temp, "Version")) - { - // Version string - char name[256]; // Model name string - - - if (!get_token(fp, name, sizeof(name))) - { - fprintf(stderr, - _("ppdc: Expected string after Version on line %d of " - "%s.\n"), fp->line, fp->filename); - break; - } - - if (!cond_state) - d->set_version(name); - } - else - { - fprintf(stderr, - _("ppdc: Unknown token \"%s\" seen on line %d of %s.\n"), - temp, fp->line, fp->filename); - break; - } - } - - // Done processing this block, is there anything to save? - if (!inc) - { - if (!d->pc_file_name || !d->model_name || !d->manufacturer || !d->version || - !d->sizes->count) - { - // Nothing to save... - d->release(); - } - else - { - // Got a driver, save it... - drivers->add(d); - } - } - else if (inc && td) - td->release(); -} - - -// -// 'ppdcSource::set_variable()' - Set a variable. -// - -ppdcVariable * // O - Variable -ppdcSource::set_variable( - const char *name, // I - Name - const char *value) // I - Value -{ - ppdcVariable *v; // Variable - - - // See if the variable exists already... - v = find_variable(name); - if (v) - { - // Change the variable value... - v->set_value(value); - } - else - { - // Create a new variable and add it... - v = new ppdcVariable(name, value); - vars->add(v); - } - - return (v); -} - - -// -// 'ppdcSource::write_file()' - Write the current source data to a file. -// - -int // O - 0 on success, -1 on error -ppdcSource::write_file(const char *f) // I - File to write -{ - cups_file_t *fp; // Output file - char bckname[1024]; // Backup file - ppdcDriver *d; // Current driver - ppdcString *st; // Current string - ppdcAttr *a; // Current attribute - ppdcConstraint *co; // Current constraint - ppdcFilter *fi; // Current filter - ppdcFont *fo; // Current font - ppdcGroup *g; // Current group - ppdcOption *o; // Current option - ppdcChoice *ch; // Current choice - ppdcProfile *p; // Current color profile - ppdcMediaSize *si; // Current media size - float left, // Current left margin - bottom, // Current bottom margin - right, // Current right margin - top; // Current top margin - int dtused[PPDC_DRIVER_MAX];// Driver type usage... - - - // Rename the current file, if any, to .bck... - snprintf(bckname, sizeof(bckname), "%s.bck", f); - rename(f, bckname); - - // Open the output file... - fp = cupsFileOpen(f, "w"); - - if (!fp) - { - // Can't create file; restore backup and return... - rename(bckname, f); - return (-1); - } - - cupsFilePuts(fp, "// PPD Compiler of cups-filters " PACKAGE_VERSION "\n\n"); - - // Include standard files... - cupsFilePuts(fp, "// Include necessary files...\n"); - cupsFilePuts(fp, "#include \n"); - cupsFilePuts(fp, "#include \n"); - - memset(dtused, 0, sizeof(dtused)); - - for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next()) - if (d->type > PPDC_DRIVER_PS && !dtused[d->type]) - { - cupsFilePrintf(fp, "#include <%s.h>\n", driver_types[d->type]); - dtused[d->type] = 1; - } - - // Output each driver... - for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next()) - { - // Start the driver... - cupsFilePrintf(fp, "\n// %s %s\n", d->manufacturer->value, - d->model_name->value); - cupsFilePuts(fp, "{\n"); - - // Write the copyright stings... - for (st = (ppdcString *)d->copyright->first(); - st; - st = (ppdcString *)d->copyright->next()) - quotef(fp, " Copyright \"%s\"\n", st->value); - - // Write other strings and values... - if (d->manufacturer && d->manufacturer->value) - quotef(fp, " Manufacturer \"%s\"\n", d->manufacturer->value); - if (d->model_name->value) - quotef(fp, " ModelName \"%s\"\n", d->model_name->value); - if (d->file_name && d->file_name->value) - quotef(fp, " FileName \"%s\"\n", d->file_name->value); - if (d->pc_file_name && d->pc_file_name->value) - quotef(fp, " PCFileName \"%s\"\n", d->pc_file_name->value); - if (d->version && d->version->value) - quotef(fp, " Version \"%s\"\n", d->version->value); - - cupsFilePrintf(fp, " DriverType %s\n", driver_types[d->type]); - - if (d->model_number) - { - switch (d->type) - { - case PPDC_DRIVER_LABEL : - cupsFilePuts(fp, " ModelNumber "); - - switch (d->model_number) - { - case DYMO_3x0 : - cupsFilePuts(fp, "$DYMO_3x0\n"); - break; - - case ZEBRA_EPL_LINE : - cupsFilePuts(fp, "$ZEBRA_EPL_LINE\n"); - break; - - case ZEBRA_EPL_PAGE : - cupsFilePuts(fp, "$ZEBRA_EPL_PAGE\n"); - break; - - case ZEBRA_ZPL : - cupsFilePuts(fp, "$ZEBRA_ZPL\n"); - break; - - case ZEBRA_CPCL : - cupsFilePuts(fp, "$ZEBRA_CPCL\n"); - break; - - case INTELLITECH_PCL : - cupsFilePuts(fp, "$INTELLITECH_PCL\n"); - break; - - default : - cupsFilePrintf(fp, "%d\n", d->model_number); - break; - } - break; - - case PPDC_DRIVER_EPSON : - cupsFilePuts(fp, " ModelNumber "); - - switch (d->model_number) - { - case EPSON_9PIN : - cupsFilePuts(fp, "$EPSON_9PIN\n"); - break; - - case EPSON_24PIN : - cupsFilePuts(fp, "$EPSON_24PIN\n"); - break; - - case EPSON_COLOR : - cupsFilePuts(fp, "$EPSON_COLOR\n"); - break; - - case EPSON_PHOTO : - cupsFilePuts(fp, "$EPSON_PHOTO\n"); - break; - - case EPSON_ICOLOR : - cupsFilePuts(fp, "$EPSON_ICOLOR\n"); - break; - - case EPSON_IPHOTO : - cupsFilePuts(fp, "$EPSON_IPHOTO\n"); - break; - - default : - cupsFilePrintf(fp, "%d\n", d->model_number); - break; - } - break; - - case PPDC_DRIVER_HP : - cupsFilePuts(fp, " ModelNumber "); - switch (d->model_number) - { - case HP_LASERJET : - cupsFilePuts(fp, "$HP_LASERJET\n"); - break; - - case HP_DESKJET : - cupsFilePuts(fp, "$HP_DESKJET\n"); - break; - - case HP_DESKJET2 : - cupsFilePuts(fp, "$HP_DESKJET2\n"); - break; - - default : - cupsFilePrintf(fp, "%d\n", d->model_number); - break; - } - - cupsFilePuts(fp, ")\n"); - break; - - default : - cupsFilePrintf(fp, " ModelNumber %d\n", d->model_number); - break; - } - } - - if (d->manual_copies) - cupsFilePuts(fp, " ManualCopies Yes\n"); - - if (d->color_device) - cupsFilePuts(fp, " ColorDevice Yes\n"); - - if (d->throughput) - cupsFilePrintf(fp, " Throughput %d\n", d->throughput); - - // Output all of the attributes... - for (a = (ppdcAttr *)d->attrs->first(); - a; - a = (ppdcAttr *)d->attrs->next()) - if (a->text->value && a->text->value[0]) - quotef(fp, " Attribute \"%s\" \"%s/%s\" \"%s\"\n", - a->name->value, a->selector->value ? a->selector->value : "", - a->text->value, a->value->value ? a->value->value : ""); - else - quotef(fp, " Attribute \"%s\" \"%s\" \"%s\"\n", - a->name->value, a->selector->value ? a->selector->value : "", - a->value->value ? a->value->value : ""); - - // Output all of the constraints... - for (co = (ppdcConstraint *)d->constraints->first(); - co; - co = (ppdcConstraint *)d->constraints->next()) - { - if (co->option1->value[0] == '*') - cupsFilePrintf(fp, " UIConstraints \"%s %s", co->option1->value, - co->choice1->value ? co->choice1->value : ""); - else - cupsFilePrintf(fp, " UIConstraints \"*%s %s", co->option1->value, - co->choice1->value ? co->choice1->value : ""); - - if (co->option2->value[0] == '*') - cupsFilePrintf(fp, " %s %s\"\n", co->option2->value, - co->choice2->value ? co->choice2->value : ""); - else - cupsFilePrintf(fp, " *%s %s\"\n", co->option2->value, - co->choice2->value ? co->choice2->value : ""); - } - - // Output all of the filters... - for (fi = (ppdcFilter *)d->filters->first(); - fi; - fi = (ppdcFilter *)d->filters->next()) - cupsFilePrintf(fp, " Filter \"%s %d %s\"\n", - fi->mime_type->value, fi->cost, fi->program->value); - - // Output all of the fonts... - for (fo = (ppdcFont *)d->fonts->first(); - fo; - fo = (ppdcFont *)d->fonts->next()) - if (!strcmp(fo->name->value, "*")) - cupsFilePuts(fp, " Font *\n"); - else - cupsFilePrintf(fp, " Font \"%s\" \"%s\" \"%s\" \"%s\" %s\n", - fo->name->value, fo->encoding->value, - fo->version->value, fo->charset->value, - fo->status == PPDC_FONT_ROM ? "ROM" : "Disk"); - - // Output all options... - for (g = (ppdcGroup *)d->groups->first(); - g; - g = (ppdcGroup *)d->groups->next()) - { - if (g->options->count == 0) - continue; - - if (g->text->value && g->text->value[0]) - quotef(fp, " Group \"%s/%s\"\n", g->name->value, g->text->value); - else - cupsFilePrintf(fp, " Group \"%s\"\n", g->name->value); - - for (o = (ppdcOption *)g->options->first(); - o; - o = (ppdcOption *)g->options->next()) - { - if (o->choices->count == 0) - continue; - - if (o->text->value && o->text->value[0]) - quotef(fp, " Option \"%s/%s\"", o->name->value, o->text->value); - else - cupsFilePrintf(fp, " Option \"%s\"", o->name->value); - - cupsFilePrintf(fp, " %s %s %.1f\n", - o->type == PPDC_BOOLEAN ? "Boolean" : - o->type == PPDC_PICKONE ? "PickOne" : "PickMany", - o->section == PPDC_SECTION_ANY ? "AnySetup" : - o->section == PPDC_SECTION_DOCUMENT ? "DocumentSetup" : - o->section == PPDC_SECTION_EXIT ? "ExitServer" : - o->section == PPDC_SECTION_JCL ? "JCLSetup" : - o->section == PPDC_SECTION_PAGE ? "PageSetup" : - "Prolog", - o->order); - - for (ch = (ppdcChoice *)o->choices->first(); - ch; - ch = (ppdcChoice *)o->choices->next()) - { - if (ch->text->value && ch->text->value[0]) - quotef(fp, " %sChoice \"%s/%s\" \"%s\"\n", - o->defchoice == ch->name ? "*" : "", - ch->name->value, ch->text->value, - ch->code->value ? ch->code->value : ""); - else - quotef(fp, " %sChoice \"%s\" \"%s\"\n", - o->defchoice == ch->name ? "*" : "", - ch->name->value, - ch->code->value ? ch->code->value : ""); - } - } - } - - // Output all of the color profiles... - for (p = (ppdcProfile *)d->profiles->first(); - p; - p = (ppdcProfile *)d->profiles->next()) - cupsFilePrintf(fp, " ColorProfile \"%s/%s\" %.3f %.3f " - "%.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n", - p->resolution->value, p->media_type->value, - p->density, p->gamma, - p->profile[0], p->profile[1], p->profile[2], - p->profile[3], p->profile[4], p->profile[5], - p->profile[6], p->profile[7], p->profile[8]); - - // Output all of the media sizes... - left = 0.0; - bottom = 0.0; - right = 0.0; - top = 0.0; - - for (si = (ppdcMediaSize *)d->sizes->first(); - si; - si = (ppdcMediaSize *)d->sizes->next()) - if (si->size_code->value && si->region_code->value) - { - // Output a custom media size... - quotef(fp, " %sCustomMedia \"%s/%s\" %.2f %.2f %.2f %.2f %.2f %.2f \"%s\" \"%s\"\n", - si->name == d->default_size ? "*" : "", si->name->value, - si->text->value, si->width, si->length, si->left, si->bottom, - si->right, si->top, si->size_code->value, - si->region_code->value); - } - else - { - // Output a standard media size... - if (fabs(left - si->left) > 0.1 || - fabs(bottom - si->bottom) > 0.1 || - fabs(right - si->right) > 0.1 || - fabs(top - si->top) > 0.1) - { - cupsFilePrintf(fp, " HWMargins %.2f %.2f %.2f %.2f\n", - si->left, si->bottom, si->right, si->top); - - left = si->left; - bottom = si->bottom; - right = si->right; - top = si->top; - } - - cupsFilePrintf(fp, " %sMediaSize %s\n", - si->name == d->default_size ? "*" : "", - si->name->value); - } - - if (d->variable_paper_size) - { - cupsFilePuts(fp, " VariablePaperSize Yes\n"); - - if (fabs(left - d->left_margin) > 0.1 || - fabs(bottom - d->bottom_margin) > 0.1 || - fabs(right - d->right_margin) > 0.1 || - fabs(top - d->top_margin) > 0.1) - { - cupsFilePrintf(fp, " HWMargins %.2f %.2f %.2f %.2f\n", - d->left_margin, d->bottom_margin, d->right_margin, - d->top_margin); - } - - cupsFilePrintf(fp, " MinSize %.2f %.2f\n", d->min_width, d->min_length); - cupsFilePrintf(fp, " MaxSize %.2f %.2f\n", d->max_width, d->max_length); - } - - // End the driver... - cupsFilePuts(fp, "}\n"); - } - - // Close the file and return... - cupsFileClose(fp); - - return (0); -} diff --git a/ppd/ppdc-string.cxx b/ppd/ppdc-string.cxx deleted file mode 100644 index 5c8d2e18b..000000000 --- a/ppd/ppdc-string.cxx +++ /dev/null @@ -1,49 +0,0 @@ -// -// Shared string class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2012 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcString::ppdcString()' - Create a shared string. -// - -ppdcString::ppdcString(const char *v) // I - String - : ppdcShared() -{ - PPDC_NEWVAL(v); - - if (v) - { - size_t vlen = strlen(v); - - value = new char[vlen + 1]; - memcpy(value, v, vlen + 1); - } - else - value = 0; -} - - -// -// 'ppdcString::~ppdcString()' - Destroy a shared string. -// - -ppdcString::~ppdcString() -{ - PPDC_DELETEVAL(value); - - if (value) - delete[] value; -} diff --git a/ppd/ppdc-variable.cxx b/ppd/ppdc-variable.cxx deleted file mode 100644 index 8e6d9eb66..000000000 --- a/ppd/ppdc-variable.cxx +++ /dev/null @@ -1,55 +0,0 @@ -// -// Variable class for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2009 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" - - -// -// 'ppdcVariable::ppdcVariable()' - Create a variable. -// - -ppdcVariable::ppdcVariable(const char *n, // I - Name of variable - const char *v) // I - Value of variable - : ppdcShared() -{ - PPDC_NEW; - - name = new ppdcString(n); - value = new ppdcString(v); -} - - -// -// 'ppdcVariable::~ppdcVariable()' - Destroy a variable. -// - -ppdcVariable::~ppdcVariable() -{ - PPDC_DELETE; - - name->release(); - value->release(); -} - - -// -// 'ppdcVariable::set_value()' - Set the value of a variable. -// - -void -ppdcVariable::set_value(const char *v) -{ - value->release(); - value = new ppdcString(v); -} diff --git a/ppd/ppdc.cxx b/ppd/ppdc.cxx deleted file mode 100644 index 7808ee542..000000000 --- a/ppd/ppdc.cxx +++ /dev/null @@ -1,465 +0,0 @@ -// -// PPD file compiler main entry for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2014 by Apple Inc. -// Copyright 2002-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include -#include -#include -#include - - -// -// Local globals... -// - -const char *progname; - - -// -// Local functions... -// - -static void usage(void); - - -// -// 'main()' - Main entry for the PPD compiler. -// - -int // O - Exit status -main(int argc, // I - Number of command-line arguments - char *argv[]) // I - Command-line arguments -{ - int i, j; // Looping vars - ppdcCatalog *catalog; // Message catalog - const char *outdir; // Output directory - ppdcSource *src; // PPD source file data - ppdcDriver *d; // Current driver - cups_file_t *fp; // PPD file - char *opt, // Current option - *value, // Value in option - *outname, // Output filename - make_model[1024], - // Make and model - pcfilename[1024], - // Lowercase pcfilename - filename[2048]; // PPD filename - int comp, // Compress - do_test, // Test PPD files - single_language,// Generate single-language files - use_model_name, // Use ModelName for filename - verbose; // Verbosity - ppdcLineEnding le; // Line ending to use - ppdcArray *locales; // List of locales - cups_array_t *filenames; // List of generated filenames - - - // Scan the command-line... - catalog = NULL; - comp = 0; - do_test = 0; - le = PPDC_LFONLY; - locales = NULL; - outdir = "ppd"; - single_language = 0; - src = new ppdcSource(); - use_model_name = 0; - verbose = 0; - filenames = cupsArrayNew((cups_array_func_t)strcasecmp, NULL); - - progname = strrchr(argv[0], '/'); - if (progname) - progname ++; - else - progname = argv[0]; - - for (i = 1; i < argc; i ++) - if (argv[i][0] == '-') - { - for (opt = argv[i] + 1; *opt; opt ++) - switch (*opt) - { - case 'D' : // Define variable - i ++; - if (i >= argc) - usage(); - - if ((value = strchr(argv[i], '=')) != NULL) - { - *value++ = '\0'; - - src->set_variable(argv[i], value); - } - else - src->set_variable(argv[i], "1"); - break; - - case 'I' : // Include directory... - i ++; - if (i >= argc) - usage(); - - if (verbose > 1) - fprintf(stdout, - _("%s: Adding include directory \"%s\".\n"), - progname, argv[i]); - - ppdcSource::add_include(argv[i]); - break; - - case 'c' : // Message catalog... - i ++; - if (i >= argc) - usage(); - - if (verbose > 1) - fprintf(stdout, - _("%s: Loading messages from \"%s\".\n"), - progname, argv[i]); - - if (!catalog) - catalog = new ppdcCatalog("en"); - - if (catalog->load_messages(argv[i])) - { - fprintf(stderr, - _("%s: Unable to load localization file " - "\"%s\" - %s\n"), progname, argv[i], strerror(errno)); - return (1); - } - break; - - case 'd' : // Output directory... - i ++; - if (i >= argc) - usage(); - - if (verbose > 1) - fprintf(stdout, - _("%s: Writing PPD files to directory " - "\"%s\".\n"), progname, argv[i]); - - outdir = argv[i]; - break; - - case 'l' : // Language(s)... - i ++; - if (i >= argc) - usage(); - - if (strchr(argv[i], ',')) - { - // Comma-delimited list of languages... - char temp[1024], // Copy of language list - *start, // Start of current locale name - *end; // End of current locale name - - - locales = new ppdcArray(); - - strncpy(temp, argv[i], sizeof(temp) - 1); - for (start = temp; *start; start = end) - { - if ((end = strchr(start, ',')) != NULL) - *end++ = '\0'; - else - end = start + strlen(start); - - if (end > start) - locales->add(new ppdcString(start)); - } - } - else - { - single_language = 1; - - if (verbose > 1) - fprintf(stdout, - _("%s: Loading messages for locale " - "\"%s\".\n"), progname, argv[i]); - - if (catalog) - catalog->release(); - - catalog = new ppdcCatalog(argv[i]); - - if (catalog->messages->count == 0 && strcmp(argv[i], "en")) - { - fprintf(stderr, - _("%s: Unable to find localization for " - "\"%s\" - %s\n"), progname, argv[i], - strerror(errno)); - return (1); - } - } - break; - - case 'm' : // Use ModelName for filename - use_model_name = 1; - break; - - case 't' : // Test PPDs instead of generating them - do_test = 1; - break; - - case 'v' : // Be verbose... - verbose ++; - break; - - case 'z' : // Compress files... - comp = 1; - break; - - case '-' : // --option - if (!strcmp(opt, "-lf")) - { - le = PPDC_LFONLY; - opt += strlen(opt) - 1; - break; - } - else if (!strcmp(opt, "-cr")) - { - le = PPDC_CRONLY; - opt += strlen(opt) - 1; - break; - } - else if (!strcmp(opt, "-crlf")) - { - le = PPDC_CRLF; - opt += strlen(opt) - 1; - break; - } - - default : // Unknown - usage(); - } - } - else - { - // Open and load the driver info file... - if (verbose > 1) - fprintf(stdout, - _("%s: Loading driver information file \"%s\".\n"), - progname, argv[i]); - - src->read_file(argv[i]); - } - - - if (src->drivers->count > 0) - { - // Create the output directory... - if (mkdir(outdir, 0777)) - { - if (errno != EEXIST) - { - fprintf(stderr, - _("%s: Unable to create output directory %s: %s\n"), - progname, outdir, strerror(errno)); - return (1); - } - } - - // Write PPD files... - for (d = (ppdcDriver *)src->drivers->first(); - d; - d = (ppdcDriver *)src->drivers->next()) - { - if (do_test) - { - // Test the PPD file for this driver... - int pid, // Process ID - fds[2]; // Pipe file descriptors - - - if (pipe(fds)) - { - fprintf(stderr, - _("%s: Unable to create output pipes: %s\n"), - progname, strerror(errno)); - return (1); - } - - if ((pid = fork()) == 0) - { - // Child process comes here... - dup2(fds[0], 0); - - close(fds[0]); - close(fds[1]); - - execlp("cupstestppd", "cupstestppd", "-", (char *)0); - - fprintf(stderr, - _("%s: Unable to execute cupstestppd: %s\n"), - progname, strerror(errno)); - return (errno); - } - else if (pid < 0) - { - fprintf(stderr, _("%s: Unable to execute cupstestppd: %s\n"), - progname, strerror(errno)); - return (errno); - } - - close(fds[0]); - fp = cupsFileOpenFd(fds[1], "w"); - } - else - { - // Write the PPD file for this driver... - if (use_model_name) - { - if (!strncasecmp(d->model_name->value, d->manufacturer->value, - strlen(d->manufacturer->value))) - { - // Model name already starts with the manufacturer... - outname = d->model_name->value; - } - else - { - // Add manufacturer to the front of the model name... - snprintf(make_model, sizeof(make_model), "%s %s", - d->manufacturer->value, d->model_name->value); - outname = make_model; - } - } - else if (d->file_name) - outname = d->file_name->value; - else - outname = d->pc_file_name->value; - - if (strstr(outname, ".PPD")) - { - // Convert PCFileName to lowercase... - for (j = 0; - outname[j] && j < (int)(sizeof(pcfilename) - 1); - j ++) - pcfilename[j] = (char)tolower(outname[j] & 255); - - pcfilename[j] = '\0'; - } - else - { - // Leave PCFileName as-is... - strncpy(pcfilename, outname, sizeof(pcfilename)); - } - - // Open the PPD file for writing... - if (comp) - snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename); - else - snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename); - - if (cupsArrayFind(filenames, filename)) - fprintf(stderr, - _("%s: Warning - overlapping filename \"%s\".\n"), - progname, filename); - else - cupsArrayAdd(filenames, strdup(filename)); - - fp = cupsFileOpen(filename, comp ? "w9" : "w"); - if (!fp) - { - fprintf(stderr, - _("%s: Unable to create PPD file \"%s\" - %s.\n"), - progname, filename, strerror(errno)); - return (1); - } - - if (verbose) - fprintf(stdout, _("%s: Writing %s.\n"), progname, filename); - } - - // - // Write the PPD file... - // - - ppdcArray *templocales = locales; - - if (!templocales && !single_language) - { - templocales = new ppdcArray(); - for (ppdcCatalog *tempcatalog = (ppdcCatalog *)src->po_files->first(); - tempcatalog; - tempcatalog = (ppdcCatalog *)src->po_files->next()) - { - tempcatalog->locale->retain(); - templocales->add(tempcatalog->locale); - } - } - - if (d->write_ppd_file(fp, catalog, templocales, src, le)) - { - cupsFileClose(fp); - return (1); - } - - if (templocales && templocales != locales) - templocales->release(); - - cupsFileClose(fp); - } - } - else - usage(); - - // Delete the printer driver information... - src->release(); - - // Message catalog... - if (catalog) - catalog->release(); - - // Return with no errors. - return (0); -} - - -// -// 'usage()' - Show usage and exit. -// - -static void -usage(void) -{ - fprintf(stdout, _("Usage: %s [options] filename.drv [ ... " - "filenameN.drv ]\n"), progname); - fprintf(stdout, _("Options:\n")); - fprintf(stdout, _(" -D name=value Set named variable to " - "value.\n")); - fprintf(stdout, _(" -I include-dir Add include directory to " - "search path.\n")); - fprintf(stdout, _(" -c catalog.po Load the specified " - "message catalog.\n")); - fprintf(stdout, _(" -d output-dir Specify the output " - "directory.\n")); - fprintf(stdout, _(" -l lang[,lang,...] Specify the output " - "language(s) (locale).\n")); - fprintf(stdout, _(" -m Use the ModelName value " - "as the filename.\n")); - fprintf(stdout, _(" -t Test PPDs instead of " - "generating them.\n")); - fprintf(stdout, _(" -v Be verbose.\n")); - fprintf(stdout, _(" -z Compress PPD files using " - "GNU zip.\n")); - fprintf(stdout, _(" --cr End lines with CR (Mac " - "OS 9).\n")); - fprintf(stdout, _(" --crlf End lines with CR + LF " - "(Windows).\n")); - fprintf(stdout, _(" --lf End lines with LF " - "(UNIX/Linux/macOS).\n")); - - exit(1); -} diff --git a/ppd/ppdc.h b/ppd/ppdc.h deleted file mode 100644 index 0a3a19026..000000000 --- a/ppd/ppdc.h +++ /dev/null @@ -1,524 +0,0 @@ -// -// API definitions for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2019 by Apple Inc. -// Copyright 2002-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPDC_H_ -# define _PPDC_H_ - -// -// Include necessary headers... -// - -# include -# include - - -// -// Macros... -// - -# define PPDC_NAME(s) const char *class_name() { return (s); } - - -// -// Enumerations... -// - -enum ppdcDrvType //// Driver type -{ - PPDC_DRIVER_CUSTOM, // Custom driver - PPDC_DRIVER_PS, // PostScript driver - PPDC_DRIVER_ESCP, // rastertoescpx driver - PPDC_DRIVER_PCL, // rastertopclx driver - PPDC_DRIVER_LABEL, // rastertolabel/rastertodymo driver - PPDC_DRIVER_EPSON, // rastertoepson driver - PPDC_DRIVER_HP, // rastertohp driver - PPDC_DRIVER_MAX // Number of driver types defined -}; - -enum ppdcFontStatus //// Load status of font -{ - PPDC_FONT_ROM, // Font is in ROM - PPDC_FONT_DISK // Font is on disk -}; - -enum ppdcOptSection //// Option section -{ - PPDC_SECTION_ANY, // AnySetup - PPDC_SECTION_DOCUMENT, // DocumentSetup - PPDC_SECTION_EXIT, // ExitServer - PPDC_SECTION_JCL, // JCLSetup - PPDC_SECTION_PAGE, // PageSetup - PPDC_SECTION_PROLOG // Prolog -}; - -enum ppdcOptType //// Option type -{ - PPDC_BOOLEAN, // True/false option - PPDC_PICKONE, // Single choice from list - PPDC_PICKMANY // Multiple choices from list -}; - -enum ppdcLineEnding //// Line endings -{ - PPDC_LFONLY, // LF only - PPDC_CRONLY, // CR only - PPDC_CRLF // CR + LF -}; - -enum ppdcCondFlags //// Condition flags -{ - PPDC_COND_NORMAL = 0, // Normal state - PPDC_COND_SKIP = 1, // Skip state - PPDC_COND_SATISFIED = 2 // At least one condition satisfied -}; - - -// -// Printer description data... -// - -class ppdcShared //// Shared Data Value -{ - private: - - int use; // Use count (delete when 0) - - public: - - ppdcShared(); - virtual ~ppdcShared(); - - virtual const char *class_name() = 0; - - void retain(); - void release(); -}; - -class ppdcArray //// Shared Array - : public ppdcShared -{ - public: - - size_t count, // Number of elements - alloc, // Allocated elements - current; // Current element - ppdcShared **data; // Elements - - ppdcArray(ppdcArray *a = 0); - ~ppdcArray(); - - PPDC_NAME("ppdcArray") - - void add(ppdcShared *d); - ppdcShared *first(); - ppdcShared *next(); - void remove(ppdcShared *d); -}; - -class ppdcString //// Shared String - : public ppdcShared -{ - public: - - char *value; // String value - - ppdcString(const char *v); - ~ppdcString(); - - PPDC_NAME("ppdcString") -}; - -class ppdcInteger //// Shared integer - : public ppdcShared -{ - public: - - int *value; // Integer value - - ppdcInteger(int *v) { value = v; } - - PPDC_NAME("ppdcInteger") -}; - -class ppdcMessage //// Translation message - : public ppdcShared -{ - public: - - ppdcString *id, // Translation ID - *string; // Translation string - - ppdcMessage(const char *i, const char *s); - ~ppdcMessage(); - - PPDC_NAME("ppdcMessage") -}; - -class ppdcCatalog //// Translation catalog - : public ppdcShared -{ - public: - - ppdcString *locale; // Name of locale - ppdcString *filename; // Name of translation file - ppdcArray *messages; // Array of translation messages - - ppdcCatalog(const char *l, const char *f = 0); - ~ppdcCatalog(); - - PPDC_NAME("ppdcCatalog") - - void add_message(const char *id, const char *string = NULL); - const char *find_message(const char *id); - int load_messages(const char *f); - int save_messages(const char *f); -}; - -class ppdcAttr //// Attribute - : public ppdcShared -{ - public: - - ppdcString *name, // Name of attribute - *selector, // Selector string - *text, // Text string - *value; // Value string - bool localizable; // Should this attribute be localized? - - ppdcAttr(const char *n, const char *s, const char *t, const char *v, - bool loc = false); - ~ppdcAttr(); - - PPDC_NAME("ppdcAttr") -}; - -class ppdcFont //// Shared Font - : public ppdcShared -{ - public: - - ppdcString *name, // Font name - *encoding, // Font base encoding - *version, // Font version - *charset; // Font charset - ppdcFontStatus status; // Font status (ROM or Disk) - - ppdcFont(const char *n, const char *e, const char *v, const char *c, - ppdcFontStatus s); - ~ppdcFont(); - - PPDC_NAME("ppdcFont") -}; - -class ppdcChoice //// Option Choice - : public ppdcShared -{ - public: - - ppdcString *name, // Name of choice - *text, // Human-readable text of choice - *code; // PS code of choice - - ppdcChoice(const char *n, const char *t, const char *c); - ~ppdcChoice(); - - PPDC_NAME("ppdcChoice") -}; - -class ppdcOption //// Option - : public ppdcShared -{ - public: - - ppdcOptType type; // Type of option - ppdcString *name, // Name of option - *text; // Human-readable text of option - ppdcOptSection section; // Section for option code - float order; // Order number - ppdcArray *choices; // Choices - ppdcString *defchoice; // Default choice - - ppdcOption(ppdcOptType ot, const char *n, const char *t, ppdcOptSection s, - float o); - ppdcOption(ppdcOption *o); - ~ppdcOption(); - - PPDC_NAME("ppdcOption") - - void add_choice(ppdcChoice *c) { choices->add(c); } - ppdcChoice *find_choice(const char *n); - void set_defchoice(ppdcChoice *c); -}; - -class ppdcGroup //// Group of Options - : public ppdcShared -{ - public: - - ppdcString *name, // Name of option - *text; // Human-readable text of option - ppdcArray *options; // Options - - ppdcGroup(const char *n, const char *t); - ppdcGroup(ppdcGroup *g); - ~ppdcGroup(); - - PPDC_NAME("ppdcGroup") - - void add_option(ppdcOption *o) { options->add(o); } - ppdcOption *find_option(const char *n); -}; - -class ppdcConstraint //// Constraint - : public ppdcShared -{ - public: - - ppdcString *option1, // First option - *choice1, // First choice - *option2, // Second option - *choice2; // Second choice - - ppdcConstraint(const char *o1, const char *c1, const char *o2, - const char *c2); - ~ppdcConstraint(); - - PPDC_NAME("ppdcConstraint") -}; - -class ppdcFilter //// Filter Program - : public ppdcShared -{ - public: - - ppdcString *mime_type, // MIME type - *program; // Filter program - int cost; // Relative cost of filter - - ppdcFilter(const char *t, const char *p, int c); - ~ppdcFilter(); - - PPDC_NAME("ppdcFilter") -}; - -class ppdcMediaSize //// Media Size - : public ppdcShared -{ - public: - - ppdcString *name, // Name of size - *text; // Human-readable text - float width, // Width in points - length, // Length in points - left, // Left limit in points - bottom, // Bottom limit in points - right, // Right limit in points - top; // Top limit in points - ppdcString *size_code, // PageSize code, if any - *region_code; // PageRegion code, if any - - ppdcMediaSize(const char *n, const char *t, float w, float l, - float lm, float bm, float rm, float tm, - const char *sc = 0, const char *rc = 0); - ~ppdcMediaSize(); - - PPDC_NAME("ppdcMediaSize") -}; - -class ppdcProfile //// Color Profile - : public ppdcShared -{ - public: - - ppdcString *resolution, // Resolution name - *media_type; // Media type name - float density, // Color profile density - gamma, // Color profile gamma - profile[9]; // Color profile matrix - - ppdcProfile(const char *r, const char *m, float d, float g, const float *p); - ~ppdcProfile(); - - PPDC_NAME("ppdcProfile") -}; - -class ppdcSource; - -class ppdcDriver //// Printer Driver Data - : public ppdcShared -{ - public: - - ppdcDrvType type; // Driver type - ppdcArray *copyright; // Copyright strings - ppdcString *manufacturer, // Manufacturer - *model_name, // Name of printer model - *file_name, // Output filename for PPD - *pc_file_name, // 8 character PC filename for PPD - *version; // Version number - int model_number, // Model number for driver - manual_copies, // Do manual copies? - color_device, // Support color? - throughput; // Throughput in pages per minute - ppdcArray *attrs, // Attributes - *constraints, // Constraints - *filters, // Filters - *fonts, // Fonts - *groups, // Option groups - *profiles, // Color profiles - *sizes; // Fixed sizes - ppdcString *default_font, // Default font - *default_size; // Default size option - int variable_paper_size; // Support variable sizes? - ppdcString *custom_size_code; // Custom page size code, if any - float left_margin, // Margins for device in points - bottom_margin, - right_margin, - top_margin, - max_width, // Maximum width (points) - max_length, // Maximum length (points) - min_width, // Minimum width (points) - min_length; // Minimum length (points) - - ppdcDriver(ppdcDriver *d = 0); - ~ppdcDriver(); - - PPDC_NAME("ppdcDriver") - - void add_attr(ppdcAttr *a) { attrs->add(a); } - void add_constraint(ppdcConstraint *c) { constraints->add(c); } - void add_copyright(const char *c) { - copyright->add(new ppdcString(c)); - } - void add_filter(ppdcFilter *f) { filters->add(f); } - void add_font(ppdcFont *f) { fonts->add(f); } - void add_group(ppdcGroup *g) { groups->add(g); } - void add_profile(ppdcProfile *p) { profiles->add(p); } - void add_size(ppdcMediaSize *m) { sizes->add(m); } - - ppdcAttr *find_attr(const char *k, const char *s); - ppdcGroup *find_group(const char *n); - ppdcOption *find_option(const char *n); - ppdcOption *find_option_group(const char *n, ppdcGroup **mg); - - void set_custom_size_code(const char *c); - void set_default_font(ppdcFont *f); - void set_default_size(ppdcMediaSize *m); - void set_file_name(const char *f); - void set_manufacturer(const char *m); - void set_model_name(const char *m); - void set_pc_file_name(const char *f); - void set_version(const char *v); - - int write_ppd_file(cups_file_t *fp, ppdcCatalog *catalog, - ppdcArray *locales, ppdcSource *src, - ppdcLineEnding le); -}; - -class ppdcVariable //// Variable Definition - : public ppdcShared -{ - public: - - ppdcString *name, // Name of variable - *value; // Value of variable - - ppdcVariable(const char *n, const char *v); - ~ppdcVariable(); - - PPDC_NAME("ppdcVariable") - - void set_value(const char *v); -}; - -class ppdcFile //// File -{ - public: - - bool close_on_delete; // Close file on delete? - cups_file_t *fp; // File pointer - const char *filename; // Filename - int line; // Line in file - - ppdcFile(const char *f, cups_file_t *ffp = (cups_file_t *)0); - ~ppdcFile(); - - int get(); - int peek(); -}; - -class ppdcSource //// Source File - : public ppdcShared -{ - public: - - static ppdcArray *includes; // Include directories - static const char *driver_types[]; // Driver types - - ppdcString *filename; // Filename - ppdcArray *base_fonts, // Base fonts - *drivers, // Printer drivers - *po_files, // Message catalogs - *sizes, // Predefined media sizes - *vars; // Defined variables - int cond_state, // Cummulative conditional state - *cond_current, // Current #if state - cond_stack[101]; // #if state stack - - - ppdcSource(const char *f = 0, cups_file_t *ffp = (cups_file_t *)0); - ~ppdcSource(); - - PPDC_NAME("ppdcSource") - - static void add_include(const char *d); - ppdcDriver *find_driver(const char *f); - static char *find_include(const char *f, const char *base, char *n, - int nlen); - ppdcCatalog *find_po(const char *l); - ppdcMediaSize *find_size(const char *s); - ppdcVariable *find_variable(const char *n); - ppdcAttr *get_attr(ppdcFile *fp, bool loc = false); - int get_boolean(ppdcFile *fp); - ppdcChoice *get_choice(ppdcFile *fp); - ppdcChoice *get_color_model(ppdcFile *fp); - int get_color_order(const char *co); - ppdcProfile *get_color_profile(ppdcFile *fp); - int get_color_space(const char *cs); - ppdcConstraint *get_constraint(ppdcFile *fp); - ppdcMediaSize *get_custom_size(ppdcFile *fp); - void get_duplex(ppdcFile *fp, ppdcDriver *d); - ppdcFilter *get_filter(ppdcFile *fp); - float get_float(ppdcFile *fp); - ppdcFont *get_font(ppdcFile *fp); - ppdcChoice *get_generic(ppdcFile *fp, const char *keyword, - const char *tattr, const char *nattr); - ppdcGroup *get_group(ppdcFile *fp, ppdcDriver *d); - ppdcOption *get_installable(ppdcFile *fp); - int get_integer(const char *v); - int get_integer(ppdcFile *fp); - float get_measurement(ppdcFile *fp); - ppdcOption *get_option(ppdcFile *fp, ppdcDriver *d, ppdcGroup *g); - ppdcCatalog *get_po(ppdcFile *fp); - ppdcChoice *get_resolution(ppdcFile *fp); - ppdcProfile *get_simple_profile(ppdcFile *fp); - ppdcMediaSize *get_size(ppdcFile *fp); - char *get_token(ppdcFile *fp, char *buffer, int buflen); - ppdcVariable *get_variable(ppdcFile *fp); - int import_ppd(const char *f); - int quotef(cups_file_t *fp, const char *format, ...); - void read_file(const char *f, cups_file_t *ffp = (cups_file_t *)0); - void scan_file(ppdcFile *fp, ppdcDriver *td = 0, bool inc = false); - ppdcVariable *set_variable(const char *name, const char *value); - int write_file(const char *f); -}; - - -#endif // !_PPDC_H_ diff --git a/ppd/ppdhtml.cxx b/ppd/ppdhtml.cxx deleted file mode 100644 index 1179fbf99..000000000 --- a/ppd/ppdhtml.cxx +++ /dev/null @@ -1,178 +0,0 @@ -// -// PPD to HTML utility for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2015 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include -#include - - -// -// Local functions... -// - -static void usage(void); - - -// -// 'main()' - Main entry for the PPD compiler. -// - -int // O - Exit status -main(int argc, // I - Number of command-line arguments - char *argv[]) // I - Command-line arguments -{ - int i; // Looping var - ppdcSource *src; // PPD source file data - ppdcDriver *d; // Current driver - ppdcGroup *g, // Current group - *composite; // Composite of all drivers - ppdcOption *o, // Current option - *compo; // Composite option - ppdcChoice *c; // Current choice - char *opt; // Current option char - ppdcMediaSize *size; // Current media size - char *value; // Value in option - - - // Scan the command-line... - src = new ppdcSource(); - - for (i = 1; i < argc; i ++) - if (argv[i][0] == '-') - { - for (opt = argv[i] + 1; *opt; opt ++) - switch (*opt) - { - case 'D' : // Define variable - i ++; - if (i >= argc) - usage(); - - if ((value = strchr(argv[i], '=')) != NULL) - { - *value++ = '\0'; - - src->set_variable(argv[i], value); - } - else - src->set_variable(argv[i], "1"); - break; - - case 'I' : // Include directory... - i ++; - if (i >= argc) - usage(); - - ppdcSource::add_include(argv[i]); - break; - - default : // Unknown - usage(); - } - } - else - { - // Open and load the driver info file... - src->read_file(argv[i]); - } - - if ((d = (ppdcDriver *)src->drivers->first()) != NULL) - { - // Create a composite group with all of the features from the - // drivers in the info file... - composite = new ppdcGroup("", ""); - - while (d != NULL) - { - for (g = (ppdcGroup *)d->groups->first(); g; g = (ppdcGroup *)d->groups->next()) - for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next()) - { - if (!composite->find_option(o->name->value)) - composite->add_option(new ppdcOption(o)); - } - - d = (ppdcDriver *)src->drivers->next(); - } - - puts(""); - printf("Driver Summary for %s\n", argv[i]); - printf("

Driver Summary for %s

\n", argv[i]); - printf("

"); - for (compo = (ppdcOption *)composite->options->first(); compo; compo = (ppdcOption *)composite->options->next()) - printf("", compo->text->value); - puts(""); - - // Write HTML summary... - for (d = (ppdcDriver *)src->drivers->first(); d; d = (ppdcDriver *)src->drivers->next()) - { - // Write the summary for this driver... - printf(""); - - for (compo = (ppdcOption *)composite->options->first(); compo; - compo = (ppdcOption *)composite->options->next()) - if ((o = d->find_option(compo->name->value)) != NULL) - { - printf(""); - } - else - printf(""); - - puts(""); - } - - puts("
PrinterMedia Size%s
%s", d->model_name->value); - for (size = (ppdcMediaSize *)d->sizes->first(); size; - size = (ppdcMediaSize *)d->sizes->next()) - printf("%s
", size->text->value); - printf("
"); - for (c = (ppdcChoice *)o->choices->first(); c; - c = (ppdcChoice *)o->choices->next()) - printf("%s
", c->text->value); - printf("
N/A

"); - puts(""); - puts(""); - - // Delete the printer driver information... - composite->release(); - } - else - { - // If no drivers have been loaded, display the program usage message. - usage(); - } - - src->release(); - - // Return with no errors. - return (0); -} - - -// -// 'usage()' - Show usage and exit. -// - -static void -usage(void) -{ - puts(_("Usage: ppdhtml [options] filename.drv " - ">filename.html")); - puts(_("Options:")); - puts(_(" -D name=value Set named variable to " - "value.")); - puts(_(" -I include-dir Add include directory " - "to search path.")); - - exit(1); -} diff --git a/ppd/ppdi.cxx b/ppd/ppdi.cxx deleted file mode 100644 index 056b2e570..000000000 --- a/ppd/ppdi.cxx +++ /dev/null @@ -1,124 +0,0 @@ -// -// PPD file import utility for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2011 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include -#include -#include - - -// -// Local functions... -// - -static void usage(void); - - -// -// 'main()' - Main entry for the PPD import utility. -// - -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; // Current option - const char *srcfile; // Output file - ppdcSource *src; // PPD source file data - - - // Scan the command-line... - srcfile = NULL; - src = NULL; - - for (i = 1; i < argc; i ++) - if (argv[i][0] == '-') - { - for (opt = argv[i] + 1; *opt; opt ++) - switch (*opt) - { - case 'o' : // Output file - if (srcfile || src) - usage(); - - i ++; - if (i >= argc) - usage(); - - srcfile = argv[i]; - break; - - case 'I' : // Include dir - i ++; - if (i >= argc) - usage(); - - ppdcSource::add_include(argv[i]); - break; - - default : // Unknown - usage(); - } - } - else - { - // Open and load the driver info file... - if (!srcfile) - srcfile = "ppdi.drv"; - - if (!src) - { - if (access(srcfile, 0)) - src = new ppdcSource(); - else - src = new ppdcSource(srcfile); - } - - // Import the PPD file... - src->import_ppd(argv[i]); - } - - // If no drivers have been loaded, display the program usage message. - if (!src) - usage(); - - // Write the driver info file back to disk... - src->write_file(srcfile); - - // Delete the printer driver information... - src->release(); - - // Return with no errors. - return (0); -} - - -// -// 'usage()' - Show usage and exit. -// - -static void -usage(void) -{ - puts(_("Usage: ppdi [options] filename.ppd [ ... " - "filenameN.ppd ]")); - puts(_("Options:")); - puts(_(" -I include-dir Add include directory to " - "search path.")); - puts(_(" -o filename.drv Set driver information " - "file (otherwise ppdi.drv).")); - - exit(1); -} diff --git a/ppd/ppdmerge.cxx b/ppd/ppdmerge.cxx deleted file mode 100644 index 41c6516b0..000000000 --- a/ppd/ppdmerge.cxx +++ /dev/null @@ -1,359 +0,0 @@ -// -// PPD file merge utility for the CUPS PPD Compiler in libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 2002-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include "ppd.h" -#include -//#include - - -// -// Local functions... -// - -static const char *ppd_locale(ppd_file_t *ppd); -static void usage(void); - - -// -// 'main()' - Main entry for the PPD merge utility. -// - -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; // Current option - ppd_file_t *ppd; // PPD file - cups_array_t *ppds; // Array of PPD files - const char *inname, // First input filename - *outname; // Output filename (if any) - char bckname[1024]; // Backup filename - cups_file_t *infile, // Input file - *outfile; // Output file - cups_array_t *languages; // Languages in file - const char *locale; // Current locale - char line[1024]; // Line from file - - - // Scan the command-line... - inname = NULL; - outname = NULL; - outfile = NULL; - languages = NULL; - ppds = cupsArrayNew(NULL, NULL); - - for (i = 1; i < argc; i ++) - if (argv[i][0] == '-') - { - for (opt = argv[i] + 1; *opt; opt ++) - switch (*opt) - { - case 'o' : // Output file - if (outname) - usage(); - - i ++; - if (i >= argc) - usage(); - - outname = argv[i]; - break; - - default : // Unknown - usage(); - } - } - else - { - // Open and load the PPD file... - if ((infile = cupsFileOpen(argv[i], "r")) == NULL) - { - fprintf(stderr, _("%s: Unable to open %s: %s"), "ppdmerge", - argv[i], strerror(errno)); - return (1); - } - - // Open the PPD file... - if ((ppd = ppdOpen2(infile)) == NULL) - { - ppd_status_t status; // PPD open status - int curline, // Current line - linenum; // Line number - - - status = ppdLastError(&linenum); - - fprintf(stderr, - _("%s: Unable to open PPD file: %s on line %d."), - "ppdmerge", ppdErrorString(status), linenum); - cupsFileRewind(infile); - - line[0] = '\0'; - curline = 0; - - while (cupsFileGets(infile, line, sizeof(line))) - { - curline ++; - if (curline >= linenum) - break; - } - - fprintf(stderr, "%d: %s", linenum, line); - - cupsFileClose(infile); - return (1); - } - - // Figure out the locale... - if ((locale = ppd_locale(ppd)) == NULL) - { - fprintf(stderr, - _("ppdmerge: Bad LanguageVersion \"%s\" in %s."), - ppd->lang_version, argv[i]); - cupsFileClose(infile); - ppdClose(ppd); - return (1); - } - - if (!strcmp(locale, "en") && !inname && !outfile) - { - // Set the English PPD's filename... - inname = argv[i]; - languages = ppdGetLanguages(ppd); - - if (outname && !strcmp(inname, outname)) - { - // Rename input filename so that we don't overwrite it... - snprintf(bckname, sizeof(bckname), "%s.bck", inname); - - if (rename(inname, bckname)) - { - fprintf(stderr, - _("ppdmerge: Unable to backup %s to %s - %s"), - inname, bckname, strerror(errno)); - return (1); - } - - inname = bckname; - } - } - else if (strcmp(locale, "en")) - { - // Save this PPD for later processing... - cupsArrayAdd(ppds, ppd); - } - else - { - // Don't need this PPD... - fprintf(stderr, _("ppdmerge: Ignoring PPD file %s."), - argv[i]); - ppdClose(ppd); - } - - // Close and move on... - cupsFileClose(infile); - } - - // If no PPDs have been loaded, display the program usage message. - if (!inname) - usage(); - - // Loop through the PPD files we loaded to generate a new language list... - if (!languages) - languages = cupsArrayNew((cups_array_func_t)strcmp, NULL); - - for (ppd = (ppd_file_t *)cupsArrayFirst(ppds); - ppd; - ppd = (ppd_file_t *)cupsArrayNext(ppds)) - { - locale = ppd_locale(ppd); - - if (cupsArrayFind(languages, (void *)locale)) - { - // Already have this language, remove the PPD from the list. - ppdClose(ppd); - cupsArrayRemove(ppds, ppd); - } - else - cupsArrayAdd(languages, (void *)locale); - } - - // Copy the English PPD starting with a cupsLanguages line... - infile = cupsFileOpen(inname, "r"); - - if (outname) - { - const char *ext = strrchr(outname, '.'); - if (ext && !strcmp(ext, ".gz")) - outfile = cupsFileOpen(outname, "w9"); - else - outfile = cupsFileOpen(outname, "w"); - } - else - outfile = cupsFileStdout(); - - cupsFileGets(infile, line, sizeof(line)); - cupsFilePrintf(outfile, "%s\n", line); - if ((locale = (char *)cupsArrayFirst(languages)) != NULL) - { - cupsFilePrintf(outfile, "*cupsLanguages: \"%s", locale); - while ((locale = (char *)cupsArrayNext(languages)) != NULL) - cupsFilePrintf(outfile, " %s", locale); - cupsFilePuts(outfile, "\"\n"); - } - - while (cupsFileGets(infile, line, sizeof(line))) - { - if (strncmp(line, "*cupsLanguages:", 15)) - cupsFilePrintf(outfile, "%s\n", line); - } - - // Loop through the other PPD files we loaded to provide the translations... - for (ppd = (ppd_file_t *)cupsArrayFirst(ppds); - ppd; - ppd = (ppd_file_t *)cupsArrayNext(ppds)) - { - // Output all of the UI text for this language... - int j, k, l; // Looping vars - ppd_group_t *g; // Option group - ppd_option_t *o; // Option - ppd_choice_t *c; // Choice - ppd_coption_t *co; // Custom option - ppd_cparam_t *cp; // Custom parameter - ppd_attr_t *attr; // PPD attribute - - locale = ppd_locale(ppd); - - cupsFilePrintf(outfile, "*%% %s localization\n", ppd->lang_version); - cupsFilePrintf(outfile, "*%s.Translation ModelName/%s: \"\"\n", locale, - ppd->modelname); - - for (j = ppd->num_groups, g = ppd->groups; j > 0; j --, g ++) - { - cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale, - g->name, g->text); - - for (k = g->num_options, o = g->options; k > 0; k --, o ++) - { - cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale, - o->keyword, o->text); - - for (l = o->num_choices, c = o->choices; l > 0; l --, c ++) - cupsFilePrintf(outfile, "*%s.%s %s/%s: \"\"\n", locale, - o->keyword, c->choice, c->text); - - if ((co = ppdFindCustomOption(ppd, o->keyword)) != NULL) - { - snprintf(line, sizeof(line), "Custom%s", o->keyword); - attr = ppdFindAttr(ppd, line, "True"); - cupsFilePrintf(outfile, "*%s.Custom%s True/%s: \"\"\n", locale, - o->keyword, attr->text); - for (cp = ppdFirstCustomParam(co); cp; cp = ppdNextCustomParam(co)) - cupsFilePrintf(outfile, "*%s.ParamCustom%s %s/%s: \"\"\n", locale, - o->keyword, cp->name, cp->text); - } - } - } - - ppdClose(ppd); - } - - cupsArrayDelete(ppds); - - cupsFileClose(outfile); - - // Return with no errors. - return (0); -} - - -// -// 'ppd_locale()' - Return the locale associated with a PPD file. -// - -static const char * // O - Locale string -ppd_locale(ppd_file_t *ppd) // I - PPD file -{ - int i; // Looping var - size_t vlen; // Length of LanguageVersion string - static char locale[256]; // Locale string - static struct // LanguageVersion translation table - { - const char *version, // LanguageVersion string - *language; // Language code - } languages[] = - { - { "chinese", "zh" }, - { "czech", "cs" }, - { "danish", "da" }, - { "dutch", "nl" }, - { "english", "en" }, - { "finnish", "fi" }, - { "french", "fr" }, - { "german", "de" }, - { "greek", "el" }, - { "hungarian", "hu" }, - { "italian", "it" }, - { "japanese", "ja" }, - { "korean", "ko" }, - { "norwegian", "no" }, - { "polish", "pl" }, - { "portuguese", "pt" }, - { "russian", "ru" }, - { "simplified chinese", "zh_CN" }, - { "slovak", "sk" }, - { "spanish", "es" }, - { "swedish", "sv" }, - { "traditional chinese", "zh_TW" }, - { "turkish", "tr" } - }; - - - for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++) - { - vlen = strlen(languages[i].version); - - if (!strncasecmp(ppd->lang_version, languages[i].version, vlen)) - { - if (ppd->lang_version[vlen] == '-' || - ppd->lang_version[vlen] == '_') - snprintf(locale, sizeof(locale), "%s_%s", languages[i].language, - ppd->lang_version + vlen + 1); - else - strncpy(locale, languages[i].language, sizeof(locale) - 1); - - return (locale); - } - } - - return (NULL); -} - -// -// 'usage()' - Show usage and exit. -// - -static void -usage(void) -{ - puts(_("Usage: ppdmerge [options] filename.ppd [ ... " - "filenameN.ppd ]")); - puts(_("Options:")); - puts(_(" -o filename.ppd[.gz] Set output file " - "(otherwise stdout).")); - - exit(1); -} diff --git a/ppd/ppdpo.cxx b/ppd/ppdpo.cxx deleted file mode 100644 index 89528db9c..000000000 --- a/ppd/ppdpo.cxx +++ /dev/null @@ -1,249 +0,0 @@ -// -// PPD file message catalog program for the CUPS PPD Compiler in libppd. -// -// Copyright 2007-2015 by Apple Inc. -// Copyright 2002-2005 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppdc-private.h" -#include -#include - - -// -// Local functions... -// - -static void add_ui_strings(ppdcDriver *d, ppdcCatalog *catalog); -static void usage(void); - - -// -// 'main()' - Main entry for the PPD compiler. -// - -int // O - Exit status -main(int argc, // I - Number of command-line arguments - char *argv[]) // I - Command-line arguments -{ - int i; // Looping var - ppdcCatalog *catalog; // Message catalog - ppdcSource *src; // PPD source file data - ppdcDriver *d; // Current driver - char *opt; // Current option - int verbose; // Verbosity - const char *outfile; // Output file - char *value; // Value in option - - - // Scan the command-line... - catalog = new ppdcCatalog("en"); - src = new ppdcSource(); - verbose = 0; - outfile = 0; - - for (i = 1; i < argc; i ++) - if (argv[i][0] == '-') - { - for (opt = argv[i] + 1; *opt; opt ++) - switch (*opt) - { - case 'D' : // Define variable - i ++; - if (i >= argc) - usage(); - - if ((value = strchr(argv[i], '=')) != NULL) - { - *value++ = '\0'; - - src->set_variable(argv[i], value); - } - else - src->set_variable(argv[i], "1"); - break; - - case 'I' : // Include directory... - i ++; - if (i >= argc) - usage(); - - if (verbose > 1) - printf( - _("ppdc: Adding include directory \"%s\"."), - argv[i]); - - ppdcSource::add_include(argv[i]); - break; - - case 'o' : // Output file... - i ++; - if (i >= argc || outfile) - usage(); - - outfile = argv[i]; - - catalog->load_messages(outfile); - break; - - case 'v' : // Be verbose... - verbose ++; - break; - - default : // Unknown - usage(); - } - } - else - { - // Open and load the driver info file... - if (verbose > 1) - printf( - _("ppdc: Loading driver information file \"%s\"."), - argv[i]); - - src->read_file(argv[i]); - } - - // If no drivers have been loaded, display the program usage message. - if ((d = (ppdcDriver *)src->drivers->first()) != NULL) - { - // Add UI strings... - while (d != NULL) - { - if (verbose) - fprintf(stderr, _("ppdc: Adding/updating UI text from %s."), argv[i]); - - add_ui_strings(d, catalog); - - d = (ppdcDriver *)src->drivers->next(); - } - } - else - usage(); - - // Delete the printer driver information... - src->release(); - - // Write the message catalog... - if (!outfile) - usage(); - else - catalog->save_messages(outfile); - - catalog->release(); - - // Return with no errors. - return (0); -} - - -// -// 'add_ui_strings()' - Add all UI strings from the driver. -// - -static void -add_ui_strings(ppdcDriver *d, // I - Driver data - ppdcCatalog *catalog) // I - Message catalog -{ - // Add the make/model/language strings... - catalog->add_message(d->manufacturer->value); - catalog->add_message(d->model_name->value); - - // Add the media size strings... - ppdcMediaSize *m; // Current media size - - for (m = (ppdcMediaSize *)d->sizes->first(); - m; - m = (ppdcMediaSize *)d->sizes->next()) - catalog->add_message(m->text->value); - - // Add the group/option/choice strings... - ppdcGroup *g; // Current group - ppdcOption *o; // Current option - ppdcChoice *c; // Current choice - - for (g = (ppdcGroup *)d->groups->first(); - g; - g = (ppdcGroup *)d->groups->next()) - { - if (!g->options->count) - continue; - - if (strcasecmp(g->name->value, "General")) - catalog->add_message(g->text->value); - - for (o = (ppdcOption *)g->options->first(); - o; - o = (ppdcOption *)g->options->next()) - { - if (!o->choices->count) - continue; - - if (o->text->value) - catalog->add_message(o->text->value); - else - catalog->add_message(o->name->value); - - for (c = (ppdcChoice *)o->choices->first(); - c; - c = (ppdcChoice *)o->choices->next()) - if (c->text->value) - catalog->add_message(c->text->value); - else - catalog->add_message(c->name->value); - } - } - - // Add profile and preset strings... - ppdcAttr *a; // Current attribute - for (a = (ppdcAttr *)d->attrs->first(); - a; - a = (ppdcAttr *)d->attrs->next()) - if (a->text->value && a->text->value[0] && - (a->localizable || - !strncmp(a->name->value, "Custom", 6) || - !strncmp(a->name->value, "ParamCustom", 11) || - !strcmp(a->name->value, "APCustomColorMatchingName") || - !strcmp(a->name->value, "APPrinterPreset") || - !strcmp(a->name->value, "cupsICCProfile") || - !strcmp(a->name->value, "cupsIPPReason") || - !strcmp(a->name->value, "cupsMarkerName"))) - { - catalog->add_message(a->text->value); - - if ((a->localizable && a->value->value[0]) || - !strcmp(a->name->value, "cupsIPPReason")) - catalog->add_message(a->value->value); - } - else if (!strncmp(a->name->value, "Custom", 6) || - !strncmp(a->name->value, "ParamCustom", 11)) - catalog->add_message(a->name->value); -} - - -// -// 'usage()' - Show usage and exit. -// - -static void -usage(void) -{ - puts(_("Usage: ppdpo [options] -o filename.po filename.drv " - "[ ... filenameN.drv ]")); - puts(_("Options:")); - puts(_(" -D name=value Set named variable to " - "value.")); - puts(_(" -I include-dir Add include directory to " - "search path.")); - puts(_(" -v Be verbose.")); - - exit(1); -} diff --git a/ppd/raster-error.c b/ppd/raster-error.c deleted file mode 100644 index 36d7c21f5..000000000 --- a/ppd/raster-error.c +++ /dev/null @@ -1,131 +0,0 @@ -// -// Raster error handling for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "raster-private.h" -#include "debug-internal.h" - -typedef struct _ppd_raster_error_s // **** Error buffer structure **** -{ - char *start, // Start of buffer - *current, // Current position in buffer - *end; // End of buffer -} _ppd_raster_error_t; - -static _ppd_raster_error_t *buf = NULL; - -// -// '_ppdRasterAddError()' - Add an error message to the error buffer. -// - -void -_ppdRasterAddError(const char *f, // I - Printf-style error message - ...) // I - Additional arguments as needed -{ - va_list ap; // Pointer to additional arguments - char s[2048]; // Message string - ssize_t bytes; // Bytes in message string - - - DEBUG_printf(("_ppdRasterAddError(f=\"%s\", ...)", f)); - - va_start(ap, f); - bytes = vsnprintf(s, sizeof(s), f, ap); - va_end(ap); - - if (bytes <= 0) - return; - - DEBUG_printf(("1_ppdRasterAddError: %s", s)); - - bytes ++; - - if ((size_t)bytes >= sizeof(s)) - return; - - if (bytes > (ssize_t)(buf->end - buf->current)) - { - // - // Allocate more memory... - // - - char *temp; // New buffer - size_t size; // Size of buffer - - - size = (size_t)(buf->end - buf->start + 2 * bytes + 1024); - - if (buf->start) - temp = realloc(buf->start, size); - else - temp = malloc(size); - - if (!temp) - return; - - // - // Update pointers... - // - - buf->end = temp + size; - buf->current = temp + (buf->current - buf->start); - buf->start = temp; - } - - // - // Append the message to the end of the current string... - // - - memcpy(buf->current, s, (size_t)bytes); - buf->current += bytes - 1; -} - - -// -// '_ppdRasterClearError()' - Clear the error buffer. -// - -void -_ppdRasterClearError(void) -{ - if (buf == NULL) - { - buf = malloc(sizeof(_ppd_raster_error_t)); - buf->start = NULL; - buf->end = NULL; - buf->current = NULL; - } - - buf->current = buf->start; - - if (buf->start) - *(buf->start) = '\0'; -} - - -// -// '_ppdRasterErrorString()' - Return the last error from a raster function. -// -// If there are no recent errors, NULL is returned. -// -// @since CUPS 1.3/macOS 10.5@ -// - -const char * // O - Last error -_ppdRasterErrorString(void) -{ - if (buf->current == buf->start) - return (NULL); - else - return (buf->start); -} diff --git a/ppd/raster-interpret.c b/ppd/raster-interpret.c deleted file mode 100644 index efa99d7e6..000000000 --- a/ppd/raster-interpret.c +++ /dev/null @@ -1,1876 +0,0 @@ -// -// PPD command interpreter for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "raster-private.h" -#include "ppd.h" -#include "debug-internal.h" -#include - - -// -// Stack values for the PostScript mini-interpreter... -// - -typedef enum -{ - PPD_PS_NAME, - PPD_PS_NUMBER, - PPD_PS_STRING, - PPD_PS_BOOLEAN, - PPD_PS_NULL, - PPD_PS_START_ARRAY, - PPD_PS_END_ARRAY, - PPD_PS_START_DICT, - PPD_PS_END_DICT, - PPD_PS_START_PROC, - PPD_PS_END_PROC, - PPD_PS_CLEARTOMARK, - PPD_PS_COPY, - PPD_PS_DUP, - PPD_PS_INDEX, - PPD_PS_POP, - PPD_PS_ROLL, - PPD_PS_SETPAGEDEVICE, - PPD_PS_STOPPED, - PPD_PS_OTHER -} _ppd_ps_type_t; - -typedef struct -{ - _ppd_ps_type_t type; // Object type - union - { - int boolean; // Boolean value - char name[64]; // Name value - double number; // Number value - char other[64]; // Other operator - char string[64]; // Sring value - } value; // Value -} _ppd_ps_obj_t; - -typedef struct -{ - int num_objs, // Number of objects on stack - alloc_objs; // Number of allocated objects - _ppd_ps_obj_t *objs; // Objects in stack -} _ppd_ps_stack_t; - - -// -// Local functions... -// - -static int ppd_cleartomark_stack(_ppd_ps_stack_t *st); -static int ppd_copy_stack(_ppd_ps_stack_t *st, int count); -static void ppd_delete_stack(_ppd_ps_stack_t *st); -static void ppd_error_object(_ppd_ps_obj_t *obj); -static void ppd_error_stack(_ppd_ps_stack_t *st, const char *title); -static _ppd_ps_obj_t *ppd_index_stack(_ppd_ps_stack_t *st, int n); -static _ppd_ps_stack_t *ppd_new_stack(void); -static _ppd_ps_obj_t *ppd_pop_stack(_ppd_ps_stack_t *st); -static _ppd_ps_obj_t *ppd_push_stack(_ppd_ps_stack_t *st, - _ppd_ps_obj_t *obj); -static int ppd_roll_stack(_ppd_ps_stack_t *st, int c, int s); -static _ppd_ps_obj_t *ppd_scan_ps(_ppd_ps_stack_t *st, char **ptr); -static int ppd_setpagedevice(_ppd_ps_stack_t *st, - cups_page_header2_t *h, - int *preferred_bits); -#ifdef DEBUG -static void ppd_DEBUG_object(const char *prefix, _ppd_ps_obj_t *obj); -static void ppd_DEBUG_stack(const char *prefix, _ppd_ps_stack_t *st); -#endif // DEBUG - - -// -// 'ppdRasterInterpretPPD()' - Interpret PPD commands to create a page header. -// -// This function is used by raster image processing (RIP) filters like -// cgpdftoraster and imagetoraster when writing CUPS raster data for a page. -// It is not used by raster printer driver filters which only read CUPS -// raster data. -// -// -// @code ppdRasterInterpretPPD@ does not mark the options in the PPD using -// the "num_options" and "options" arguments. Instead, mark the options with -// @code ppdMarkOptions@ and @code ppdMarkOption@ prior to calling it - -// this allows for per-page options without manipulating the options array. -// -// The "func" argument specifies an optional callback function that is -// called prior to the computation of the final raster data. The function -// can make changes to the @link cups_page_header2_t@ data as needed to use a -// supported raster format and then returns 0 on success and -1 if the -// requested attributes cannot be supported. -// -// -// @code ppdRasterInterpretPPD@ supports a subset of the PostScript language. -// Currently only the @code [@, @code ]@, @code <<@, @code >>@, @code {@, -// @code }@, @code cleartomark@, @code copy@, @code dup@, @code index@, -// @code pop@, @code roll@, @code setpagedevice@, and @code stopped@ operators -// are supported. -// -// @since CUPS 1.2/macOS 10.5@ -// - -int // O - 0 on success, -1 on failure -ppdRasterInterpretPPD( - cups_page_header2_t *h, // O - Page header to create - ppd_file_t *ppd, // I - PPD file - int num_options, // I - Number of options - cups_option_t *options, // I - Options - cups_interpret_cb_t func) // I - Optional page header callback - // (@code NULL@ for none) -{ - int status; // Cummulative status - char *code; // Code to run - const char *val; // Option value - ppd_size_t *size; // Current size - float left, // Left position - bottom, // Bottom position - right, // Right position - top, // Top position - temp1, temp2; // Temporary variables for swapping - int preferred_bits; // Preferred bits per color - - - // - // Range check input... - // - - _ppdRasterClearError(); - - if (!h) - { - _ppdRasterAddError("Page header cannot be NULL!\n"); - return (-1); - } - - // - // Reset the page header to the defaults... - // - - memset(h, 0, sizeof(cups_page_header2_t)); - - h->NumCopies = 1; - h->PageSize[0] = 612; - h->PageSize[1] = 792; - h->HWResolution[0] = 100; - h->HWResolution[1] = 100; - h->cupsBitsPerColor = 1; - h->cupsColorOrder = CUPS_ORDER_CHUNKED; - h->cupsColorSpace = CUPS_CSPACE_K; - h->cupsBorderlessScalingFactor = 1.0f; - h->cupsPageSize[0] = 612.0f; - h->cupsPageSize[1] = 792.0f; - h->cupsImagingBBox[0] = 0.0f; - h->cupsImagingBBox[1] = 0.0f; - h->cupsImagingBBox[2] = 612.0f; - h->cupsImagingBBox[3] = 792.0f; - - strlcpy(h->cupsPageSizeName, "Letter", sizeof(h->cupsPageSizeName)); - -#ifdef __APPLE__ - // - // cupsInteger0 is also used for the total page count on macOS; set an - // uncommon default value so we can tell if the driver is using cupsInteger0. - // - - h->cupsInteger[0] = 0x80000000; -#endif // __APPLE__ - - // - // Apply patches and options to the page header... - // - - status = 0; - preferred_bits = 0; - - if (ppd) - { - // - // Apply any patch code (used to override the defaults...) - // - - if (ppd->patches) - status |= ppdRasterExecPS(h, &preferred_bits, ppd->patches); - - // - // Then apply printer options in the proper order... - // - - if ((code = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL) - { - status |= ppdRasterExecPS(h, &preferred_bits, code); - free(code); - } - - if ((code = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL) - { - status |= ppdRasterExecPS(h, &preferred_bits, code); - free(code); - } - - if ((code = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL) - { - status |= ppdRasterExecPS(h, &preferred_bits, code); - free(code); - } - - if ((code = ppdEmitString(ppd, PPD_ORDER_PAGE, 0.0)) != NULL) - { - status |= ppdRasterExecPS(h, &preferred_bits, code); - free(code); - } - } - - // - // Allow option override for page scaling... - // - - if ((val = cupsGetOption("cupsBorderlessScalingFactor", num_options, - options)) != NULL) - { - double sc = atof(val); // Scale factor - - if (sc >= 0.1 && sc <= 2.0) - h->cupsBorderlessScalingFactor = (float)sc; - } - - // - // Get the margins for the current size... - // - - if ((size = ppdPageSize(ppd, NULL)) != NULL) - { - // - // Use the margins from the PPD file... - // - - left = size->left; - bottom = size->bottom; - right = size->right; - top = size->top; - - strlcpy(h->cupsPageSizeName, size->name, sizeof(h->cupsPageSizeName)); - - h->cupsPageSize[0] = size->width; - h->cupsPageSize[1] = size->length; - } - else - { - // - // Use the default margins... - // - - left = 0.0f; - bottom = 0.0f; - right = 612.0f; - top = 792.0f; - } - - // - // Handle orientation... - // - - switch (h->Orientation) - { - case CUPS_ORIENT_0 : - default : - // Do nothing - break; - - case CUPS_ORIENT_90 : - temp1 = h->cupsPageSize[0]; - h->cupsPageSize[0] = h->cupsPageSize[1]; - h->cupsPageSize[1] = temp1; - - temp1 = left; - temp2 = right; - left = h->cupsPageSize[0] - top; - right = h->cupsPageSize[0] - bottom; - bottom = h->cupsPageSize[1] - temp1; - top = h->cupsPageSize[1] - temp2; - break; - - case CUPS_ORIENT_180 : - temp1 = left; - temp2 = bottom; - left = h->cupsPageSize[0] - right; - right = h->cupsPageSize[0] - temp1; - bottom = h->cupsPageSize[1] - top; - top = h->cupsPageSize[1] - temp2; - break; - - case CUPS_ORIENT_270 : - temp1 = h->cupsPageSize[0]; - h->cupsPageSize[0] = h->cupsPageSize[1]; - h->cupsPageSize[1] = temp1; - - temp1 = left; - temp2 = right; - left = bottom; - right = top; - bottom = h->cupsPageSize[1] - temp2; - top = h->cupsPageSize[1] - temp1; - break; - } - - if (left > right) - { - temp1 = left; - left = right; - right = temp1; - } - - if (bottom > top) - { - temp1 = bottom; - bottom = top; - top = temp1; - } - - h->PageSize[0] = (unsigned)(h->cupsPageSize[0] * - h->cupsBorderlessScalingFactor); - h->PageSize[1] = (unsigned)(h->cupsPageSize[1] * - h->cupsBorderlessScalingFactor); - h->Margins[0] = (unsigned)(left * - h->cupsBorderlessScalingFactor); - h->Margins[1] = (unsigned)(bottom * - h->cupsBorderlessScalingFactor); - h->ImagingBoundingBox[0] = (unsigned)(left * - h->cupsBorderlessScalingFactor); - h->ImagingBoundingBox[1] = (unsigned)(bottom * - h->cupsBorderlessScalingFactor); - h->ImagingBoundingBox[2] = (unsigned)(right * - h->cupsBorderlessScalingFactor); - h->ImagingBoundingBox[3] = (unsigned)(top * - h->cupsBorderlessScalingFactor); - h->cupsImagingBBox[0] = (float)left; - h->cupsImagingBBox[1] = (float)bottom; - h->cupsImagingBBox[2] = (float)right; - h->cupsImagingBBox[3] = (float)top; - - // - // Use the callback to validate the page header... - // - - if (func && (*func)(h, preferred_bits)) - { - _ppdRasterAddError("Page header callback returned error.\n"); - return (-1); - } - - // - // Check parameters... - // - - if (!h->HWResolution[0] || !h->HWResolution[1] || - !h->PageSize[0] || !h->PageSize[1] || - (h->cupsBitsPerColor != 1 && h->cupsBitsPerColor != 2 && - h->cupsBitsPerColor != 4 && h->cupsBitsPerColor != 8 && - h->cupsBitsPerColor != 16) || - h->cupsBorderlessScalingFactor < 0.1 || - h->cupsBorderlessScalingFactor > 2.0) - { - _ppdRasterAddError("Page header uses unsupported values.\n"); - return (-1); - } - - // - // Compute the bitmap parameters... - // - - h->cupsWidth = (unsigned)((right - left) * h->cupsBorderlessScalingFactor * - h->HWResolution[0] / 72.0f + 0.5f); - h->cupsHeight = (unsigned)((top - bottom) * h->cupsBorderlessScalingFactor * - h->HWResolution[1] / 72.0f + 0.5f); - - switch (h->cupsColorSpace) - { - case CUPS_CSPACE_W : - case CUPS_CSPACE_K : - case CUPS_CSPACE_WHITE : - case CUPS_CSPACE_GOLD : - case CUPS_CSPACE_SILVER : - case CUPS_CSPACE_SW : - h->cupsNumColors = 1; - h->cupsBitsPerPixel = h->cupsBitsPerColor; - break; - - default : - // - // Ensure that colorimetric colorspaces use at least 8 bits per - // component... - - - if (h->cupsColorSpace >= CUPS_CSPACE_CIEXYZ && - h->cupsBitsPerColor < 8) - h->cupsBitsPerColor = 8; - - // - // Figure out the number of bits per pixel... - // - - if (h->cupsColorOrder == CUPS_ORDER_CHUNKED) - { - if (h->cupsBitsPerColor >= 8) - h->cupsBitsPerPixel = h->cupsBitsPerColor * 3; - else - h->cupsBitsPerPixel = h->cupsBitsPerColor * 4; - } - else - h->cupsBitsPerPixel = h->cupsBitsPerColor; - - h->cupsNumColors = 3; - break; - - case CUPS_CSPACE_KCMYcm : - if (h->cupsBitsPerColor == 1) - { - if (h->cupsColorOrder == CUPS_ORDER_CHUNKED) - h->cupsBitsPerPixel = 8; - else - h->cupsBitsPerPixel = 1; - - h->cupsNumColors = 6; - break; - } - - // - // Fall through to CMYK code... - // - - case CUPS_CSPACE_RGBA : - case CUPS_CSPACE_RGBW : - case CUPS_CSPACE_CMYK : - case CUPS_CSPACE_YMCK : - case CUPS_CSPACE_KCMY : - case CUPS_CSPACE_GMCK : - case CUPS_CSPACE_GMCS : - if (h->cupsColorOrder == CUPS_ORDER_CHUNKED) - h->cupsBitsPerPixel = h->cupsBitsPerColor * 4; - else - h->cupsBitsPerPixel = h->cupsBitsPerColor; - - h->cupsNumColors = 4; - break; - - case CUPS_CSPACE_DEVICE1 : - case CUPS_CSPACE_DEVICE2 : - case CUPS_CSPACE_DEVICE3 : - case CUPS_CSPACE_DEVICE4 : - case CUPS_CSPACE_DEVICE5 : - case CUPS_CSPACE_DEVICE6 : - case CUPS_CSPACE_DEVICE7 : - case CUPS_CSPACE_DEVICE8 : - case CUPS_CSPACE_DEVICE9 : - case CUPS_CSPACE_DEVICEA : - case CUPS_CSPACE_DEVICEB : - case CUPS_CSPACE_DEVICEC : - case CUPS_CSPACE_DEVICED : - case CUPS_CSPACE_DEVICEE : - case CUPS_CSPACE_DEVICEF : - h->cupsNumColors = h->cupsColorSpace - CUPS_CSPACE_DEVICE1 + 1; - - if (h->cupsColorOrder == CUPS_ORDER_CHUNKED) - h->cupsBitsPerPixel = h->cupsBitsPerColor * h->cupsNumColors; - else - h->cupsBitsPerPixel = h->cupsBitsPerColor; - break; - } - - h->cupsBytesPerLine = (h->cupsBitsPerPixel * h->cupsWidth + 7) / 8; - - if (h->cupsColorOrder == CUPS_ORDER_BANDED) - h->cupsBytesPerLine *= h->cupsNumColors; - - return (status); -} - -// -// 'ppdRasterMatchPageSize()' - Match PPD page size to header page size. -// - -int // O - 0 on success, -1 on failure -ppdRasterMatchPPDSize( - cups_page_header2_t *header, // I - Page header to match - ppd_file_t *ppd, // I - PPD file - double margins[4], // O - Margins of media in points - double dimensions[2], // O - Width and Length of media in points - int *image_fit, // O - Imageable Area Fit - int *landscape) // O - Landscape / Portrait Fit -{ - ppd_size_t *size, // Current size - *size_matched = NULL; // Matched size - int i = 0; // Loop variable - char pageSizeRequested[64]; // Requested PageSize - - if (!header) - { - _ppdRasterAddError("Page header cannot be NULL!\n"); - return (-1); - } - if (!ppd) - { - _ppdRasterAddError("PPD info not supplied!\n"); - return (-1); - } - - strncpy(pageSizeRequested, header->cupsPageSizeName, 64); - // Prefer user-selected page size. - memset(dimensions, 0, sizeof(double)*2); - memset(margins, 0, sizeof(double)*4); - size_matched = NULL; - - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - { - // Skip page sizes which conflict with settings of the other options - // Find size of document's page under the PPD page sizes - if (fabs(header->PageSize[1] - size->length) / size->length < 0.01 && - fabs(header->PageSize[0] - size->width) / size->width < 0.01 && - (size_matched == NULL || !strcasecmp(pageSizeRequested, size->name))) - { - size_matched = size; - if (landscape) *landscape = 0; - if (image_fit) *image_fit = 0; - } - } - - if (size_matched == NULL) - // Input page size does not fit any of the PPD's sizes, try to fit - // the input page size into the imageable areas of the PPD's sizes - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - { - if (fabs(header->PageSize[1] - - size->top + size->bottom) / size->length < 0.01 && - fabs(header->PageSize[0] - - size->right + size->left) / size->width < 0.01 && - (size_matched == NULL || !strcasecmp(pageSizeRequested, size->name))) - { - DEBUG_puts("Imageable area fit\n"); - size_matched = size; - if (landscape) *landscape = 0; - if (image_fit) *image_fit = 1; - } - } - - if (size_matched) - { - // - // Standard size... - // - - DEBUG_printf(("PPD matched size = %s", size_matched->name)); - size = size_matched; - dimensions[0] = size->width; - dimensions[1] = size->length; - margins[0] = size->left; - margins[1] = size->bottom; - margins[2] = size->width - size->right; - margins[3] = size->length - size->top; - strncpy(header->cupsPageSizeName, size->name, 64); - } - else - { - // - // No matching portrait size; look for a matching size in - // landscape orientation... - // - - size_matched = 0; - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - if (fabs(header->PageSize[0] - size->length) / size->length < 0.01 && - fabs(header->PageSize[1] - size->width) / size->width < 0.01 && - (size_matched == NULL || !strcasecmp(pageSizeRequested, size->name))) - { - size_matched = size; - if (landscape) *landscape = 1; - if (image_fit) *image_fit = 0; - } - - if (size_matched == NULL) - // Input page size does not fit any of the PPD's sizes, try to fit - // the input page size into the imageable areas of the PPD's sizes - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - { - if (fabs(header->PageSize[0] - - size->top + size->bottom) / size->length < 0.01 && - fabs(header->PageSize[1] - - size->right + size->left) / size->width < 0.01 && - (size_matched == NULL || - !strcasecmp(pageSizeRequested, size->name))) - { - DEBUG_puts("Imageable area fit\n"); - size_matched = size; - if (landscape) *landscape = 1; - if (image_fit) *image_fit = 1; - } - } - } - - if (size_matched) - { - // - // Standard size in landscape orientation... - // - - size = size_matched; - DEBUG_printf(("landscape size = %s", size->name)); - dimensions[0] = size->width; - dimensions[1] = size->length; - margins[0] = size->left; - margins[1] = size->bottom; - margins[2] = size->width - size->right; - margins[3] = size->length - size->top; - strncpy(header->cupsPageSizeName, size->name, 64); - } - else - { - // - // Custom size... - // - - DEBUG_puts("size = Custom\n"); - for (i = 0; i < 2; i ++) - dimensions[i] = header->PageSize[i]; - for (i = 0; i < 4; i ++) - margins[i] = ppd->custom_margins[i]; - snprintf(header->cupsPageSizeName, 64, "Custom.%dx%d", - header->PageSize[0], header->PageSize[1]); - } - - return 0; -} - -// -// 'ppdRasterExecPS()' - Execute PostScript code to initialize a page header. -// - -int // O - 0 on success, -1 on error -ppdRasterExecPS( - cups_page_header2_t *h, // O - Page header - int *preferred_bits,// O - Preferred bits per color - const char *code) // I - PS code to execute -{ - int error = 0; // Error condition? - _ppd_ps_stack_t *st; // PostScript value stack - _ppd_ps_obj_t *obj; // Object from top of stack - char *codecopy, // Copy of code - *codeptr; // Pointer into copy of code - - - DEBUG_printf(("ppdRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n", - h, preferred_bits, code)); - - // - // Copy the PostScript code and create a stack... - // - - if ((codecopy = strdup(code)) == NULL) - { - _ppdRasterAddError("Unable to duplicate code string.\n"); - return (-1); - } - - if ((st = ppd_new_stack()) == NULL) - { - _ppdRasterAddError("Unable to create stack.\n"); - free(codecopy); - return (-1); - } - - // - // Parse the PS string until we run out of data... - // - - codeptr = codecopy; - - while ((obj = ppd_scan_ps(st, &codeptr)) != NULL) - { -#ifdef DEBUG - DEBUG_printf(("ppdRasterExecPS: Stack (%d objects)", st->num_objs)); - ppd_DEBUG_object("ppdRasterExecPS", obj); -#endif // DEBUG - - switch (obj->type) - { - default : - // Do nothing for regular values - break; - - case PPD_PS_CLEARTOMARK : - ppd_pop_stack(st); - - if (ppd_cleartomark_stack(st)) - _ppdRasterAddError("cleartomark: Stack underflow.\n"); - -#ifdef DEBUG - DEBUG_puts("1_ppdRasterExecPS: dup"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - break; - - case PPD_PS_COPY : - ppd_pop_stack(st); - if ((obj = ppd_pop_stack(st)) != NULL) - { - ppd_copy_stack(st, (int)obj->value.number); - -#ifdef DEBUG - DEBUG_puts("ppdRasterExecPS: copy"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - } - break; - - case PPD_PS_DUP : - ppd_pop_stack(st); - ppd_copy_stack(st, 1); - -#ifdef DEBUG - DEBUG_puts("ppdRasterExecPS: dup"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - break; - - case PPD_PS_INDEX : - ppd_pop_stack(st); - if ((obj = ppd_pop_stack(st)) != NULL) - { - ppd_index_stack(st, (int)obj->value.number); - -#ifdef DEBUG - DEBUG_puts("ppdRasterExecPS: index"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - } - break; - - case PPD_PS_POP : - ppd_pop_stack(st); - ppd_pop_stack(st); - -#ifdef DEBUG - DEBUG_puts("ppdRasterExecPS: pop"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - break; - - case PPD_PS_ROLL : - ppd_pop_stack(st); - if ((obj = ppd_pop_stack(st)) != NULL) - { - int c; // Count - - - c = (int)obj->value.number; - - if ((obj = ppd_pop_stack(st)) != NULL) - { - ppd_roll_stack(st, (int)obj->value.number, c); - -#ifdef DEBUG - DEBUG_puts("ppdRasterExecPS: roll"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - } - } - break; - - case PPD_PS_SETPAGEDEVICE : - ppd_pop_stack(st); - ppd_setpagedevice(st, h, preferred_bits); - -#ifdef DEBUG - DEBUG_puts("ppdRasterExecPS: setpagedevice"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - break; - - case PPD_PS_START_PROC : - case PPD_PS_END_PROC : - case PPD_PS_STOPPED : - ppd_pop_stack(st); - break; - - case PPD_PS_OTHER : - _ppdRasterAddError("Unknown operator \"%s\".\n", obj->value.other); - error = 1; - DEBUG_printf(("ppdRasterExecPS: Unknown operator \"%s\".", obj->value.other)); - break; - } - - if (error) - break; - } - - // - // Cleanup... - // - - free(codecopy); - - if (st->num_objs > 0) - { - ppd_error_stack(st, "Stack not empty:"); - -#ifdef DEBUG - DEBUG_puts("ppdRasterExecPS: Stack not empty"); - ppd_DEBUG_stack("ppdRasterExecPS", st); -#endif // DEBUG - - ppd_delete_stack(st); - - return (-1); - } - - ppd_delete_stack(st); - - // - // Return success... - // - - return (0); -} - - -// -// 'ppd_cleartomark_stack()' - Clear to the last mark ([) on the stack. -// - -static int // O - 0 on success, -1 on error -ppd_cleartomark_stack(_ppd_ps_stack_t *st) // I - Stack -{ - _ppd_ps_obj_t *obj; // Current object on stack - - - while ((obj = ppd_pop_stack(st)) != NULL) - if (obj->type == PPD_PS_START_ARRAY) - break; - - return (obj ? 0 : -1); -} - - -// -// 'ppd_copy_stack()' - Copy the top N stack objects. -// - -static int // O - 0 on success, -1 on error -ppd_copy_stack(_ppd_ps_stack_t *st, // I - Stack - int c) // I - Number of objects to copy -{ - int n; // Index - - - if (c < 0) - return (-1); - else if (c == 0) - return (0); - - if ((n = st->num_objs - c) < 0) - return (-1); - - while (c > 0) - { - if (!ppd_push_stack(st, st->objs + n)) - return (-1); - - n ++; - c --; - } - - return (0); -} - - -// -// 'ppd_delete_stack()' - Free memory used by a stack. -// - -static void -ppd_delete_stack(_ppd_ps_stack_t *st) // I - Stack -{ - free(st->objs); - free(st); -} - - -// -// 'ppd_error_object()' - Add an object's value to the current error message. -// - -static void -ppd_error_object(_ppd_ps_obj_t *obj) // I - Object to add -{ - switch (obj->type) - { - case PPD_PS_NAME : - _ppdRasterAddError(" /%s", obj->value.name); - break; - - case PPD_PS_NUMBER : - _ppdRasterAddError(" %g", obj->value.number); - break; - - case PPD_PS_STRING : - _ppdRasterAddError(" (%s)", obj->value.string); - break; - - case PPD_PS_BOOLEAN : - if (obj->value.boolean) - _ppdRasterAddError(" true"); - else - _ppdRasterAddError(" false"); - break; - - case PPD_PS_NULL : - _ppdRasterAddError(" null"); - break; - - case PPD_PS_START_ARRAY : - _ppdRasterAddError(" ["); - break; - - case PPD_PS_END_ARRAY : - _ppdRasterAddError(" ]"); - break; - - case PPD_PS_START_DICT : - _ppdRasterAddError(" <<"); - break; - - case PPD_PS_END_DICT : - _ppdRasterAddError(" >>"); - break; - - case PPD_PS_START_PROC : - _ppdRasterAddError(" {"); - break; - - case PPD_PS_END_PROC : - _ppdRasterAddError(" }"); - break; - - case PPD_PS_COPY : - _ppdRasterAddError(" --copy--"); - break; - - case PPD_PS_CLEARTOMARK : - _ppdRasterAddError(" --cleartomark--"); - break; - - case PPD_PS_DUP : - _ppdRasterAddError(" --dup--"); - break; - - case PPD_PS_INDEX : - _ppdRasterAddError(" --index--"); - break; - - case PPD_PS_POP : - _ppdRasterAddError(" --pop--"); - break; - - case PPD_PS_ROLL : - _ppdRasterAddError(" --roll--"); - break; - - case PPD_PS_SETPAGEDEVICE : - _ppdRasterAddError(" --setpagedevice--"); - break; - - case PPD_PS_STOPPED : - _ppdRasterAddError(" --stopped--"); - break; - - case PPD_PS_OTHER : - _ppdRasterAddError(" --%s--", obj->value.other); - break; - } -} - - -// -// 'ppd_error_stack()' - Add a stack to the current error message... -// - -static void -ppd_error_stack(_ppd_ps_stack_t *st, // I - Stack - const char *title) // I - Title string -{ - int c; // Looping var - _ppd_ps_obj_t *obj; // Current object on stack - - - _ppdRasterAddError("%s", title); - - for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++) - ppd_error_object(obj); - - _ppdRasterAddError("\n"); -} - - -// -// 'ppd_index_stack()' - Copy the Nth value on the stack. -// - -static _ppd_ps_obj_t * // O - New object -ppd_index_stack(_ppd_ps_stack_t *st, // I - Stack - int n) // I - Object index -{ - if (n < 0 || (n = st->num_objs - n - 1) < 0) - return (NULL); - - return (ppd_push_stack(st, st->objs + n)); -} - - -// -// 'ppd_new_stack()' - Create a new stack. -// - -static _ppd_ps_stack_t * // O - New stack -ppd_new_stack(void) -{ - _ppd_ps_stack_t *st; // New stack - - - if ((st = calloc(1, sizeof(_ppd_ps_stack_t))) == NULL) - return (NULL); - - st->alloc_objs = 32; - - if ((st->objs = calloc(32, sizeof(_ppd_ps_obj_t))) == NULL) - { - free(st); - return (NULL); - } - else - return (st); -} - - -// -// 'pop_stock()' - Pop the top object off the stack. -// - -static _ppd_ps_obj_t * // O - Object -ppd_pop_stack(_ppd_ps_stack_t *st) // I - Stack -{ - if (st->num_objs > 0) - { - st->num_objs --; - - return (st->objs + st->num_objs); - } - else - return (NULL); -} - - -// -// 'ppd_push_stack()' - Push an object on the stack. -// - -static _ppd_ps_obj_t * // O - New object -ppd_push_stack(_ppd_ps_stack_t *st, // I - Stack - _ppd_ps_obj_t *obj) // I - Object -{ - _ppd_ps_obj_t *temp; // New object - - - if (st->num_objs >= st->alloc_objs) - { - st->alloc_objs += 32; - - if ((temp = realloc(st->objs, (size_t)st->alloc_objs * - sizeof(_ppd_ps_obj_t))) == NULL) - return (NULL); - - st->objs = temp; - memset(temp + st->num_objs, 0, 32 * sizeof(_ppd_ps_obj_t)); - } - - temp = st->objs + st->num_objs; - st->num_objs ++; - - memcpy(temp, obj, sizeof(_ppd_ps_obj_t)); - - return (temp); -} - - -// -// 'ppd_roll_stack()' - Rotate stack objects. -// - -static int // O - 0 on success, -1 on error -ppd_roll_stack(_ppd_ps_stack_t *st, // I - Stack - int c, // I - Number of objects - int s) // I - Amount to shift -{ - _ppd_ps_obj_t *temp; // Temporary array of objects - int n; // Index into array - - - DEBUG_printf(("3roll_stack(st=%p, s=%d, c=%d)", st, s, c)); - - // - // Range check input... - // - - if (c < 0) - return (-1); - else if (c == 0) - return (0); - - if ((n = st->num_objs - c) < 0) - return (-1); - - s %= c; - - if (s == 0) - return (0); - - // - // Copy N objects and move things around... - // - - if (s < 0) - { - // - // Shift down... - // - - s = -s; - - if ((temp = calloc((size_t)s, sizeof(_ppd_ps_obj_t))) == NULL) - return (-1); - - memcpy(temp, st->objs + n, (size_t)s * sizeof(_ppd_ps_obj_t)); - memmove(st->objs + n, st->objs + n + s, - (size_t)(c - s) * sizeof(_ppd_ps_obj_t)); - memcpy(st->objs + n + c - s, temp, (size_t)s * sizeof(_ppd_ps_obj_t)); - } - else - { - // - // Shift up... - // - - if ((temp = calloc((size_t)s, sizeof(_ppd_ps_obj_t))) == NULL) - return (-1); - - memcpy(temp, st->objs + n + c - s, (size_t)s * sizeof(_ppd_ps_obj_t)); - memmove(st->objs + n + s, st->objs + n, - (size_t)(c - s) * sizeof(_ppd_ps_obj_t)); - memcpy(st->objs + n, temp, (size_t)s * sizeof(_ppd_ps_obj_t)); - } - - free(temp); - - return (0); -} - - -// -// 'ppd_scan_ps()' - Scan a string for the next PS object. -// - -static _ppd_ps_obj_t * // O - New object or NULL on EOF -ppd_scan_ps(_ppd_ps_stack_t *st, // I - Stack - char **ptr) // IO - String pointer -{ - _ppd_ps_obj_t obj; // Current object - char *start, // Start of object - *cur, // Current position - *valptr, // Pointer into value string - *valend; // End of value string - int parens; // Parenthesis nesting level - - - // - // Skip leading whitespace... - // - - for (cur = *ptr; *cur; cur ++) - { - if (*cur == '%') - { - // - // Comment, skip to end of line... - // - - for (cur ++; *cur && *cur != '\n' && *cur != '\r'; cur ++); - - if (!*cur) - cur --; - } - else if (!isspace(*cur & 255)) - break; - } - - if (!*cur) - { - *ptr = NULL; - - return (NULL); - } - - // - // See what we have... - // - - memset(&obj, 0, sizeof(obj)); - - switch (*cur) - { - case '(' : // (string) - obj.type = PPD_PS_STRING; - start = cur; - - for (cur ++, parens = 1, valptr = obj.value.string, - valend = obj.value.string + sizeof(obj.value.string) - 1; - *cur; - cur ++) - { - if (*cur == ')' && parens == 1) - break; - - if (*cur == '(') - parens ++; - else if (*cur == ')') - parens --; - - if (valptr >= valend) - { - *ptr = start; - - return (NULL); - } - - if (*cur == '\\') - { - // - // Decode escaped character... - // - - cur ++; - - if (*cur == 'b') - *valptr++ = '\b'; - else if (*cur == 'f') - *valptr++ = '\f'; - else if (*cur == 'n') - *valptr++ = '\n'; - else if (*cur == 'r') - *valptr++ = '\r'; - else if (*cur == 't') - *valptr++ = '\t'; - else if (*cur >= '0' && *cur <= '7') - { - int ch = *cur - '0'; - - if (cur[1] >= '0' && cur[1] <= '7') - { - cur ++; - ch = (ch << 3) + *cur - '0'; - } - - if (cur[1] >= '0' && cur[1] <= '7') - { - cur ++; - ch = (ch << 3) + *cur - '0'; - } - - *valptr++ = (char)ch; - } - else if (*cur == '\r') - { - if (cur[1] == '\n') - cur ++; - } - else if (*cur != '\n') - *valptr++ = *cur; - } - else - *valptr++ = *cur; - } - - if (*cur != ')') - { - *ptr = start; - - return (NULL); - } - - cur ++; - break; - - case '[' : // Start array - obj.type = PPD_PS_START_ARRAY; - cur ++; - break; - - case ']' : // End array - obj.type = PPD_PS_END_ARRAY; - cur ++; - break; - - case '<' : // Start dictionary or hex string - if (cur[1] == '<') - { - obj.type = PPD_PS_START_DICT; - cur += 2; - } - else - { - obj.type = PPD_PS_STRING; - start = cur; - - for (cur ++, valptr = obj.value.string, - valend = obj.value.string + sizeof(obj.value.string) - 1; - *cur; - cur ++) - { - int ch; // Current character - - if (*cur == '>') - break; - else if (valptr >= valend || !isxdigit(*cur & 255)) - { - *ptr = start; - return (NULL); - } - - if (*cur >= '0' && *cur <= '9') - ch = (*cur - '0') << 4; - else - ch = (tolower(*cur) - 'a' + 10) << 4; - - if (isxdigit(cur[1] & 255)) - { - cur ++; - - if (*cur >= '0' && *cur <= '9') - ch |= *cur - '0'; - else - ch |= tolower(*cur) - 'a' + 10; - } - - *valptr++ = (char)ch; - } - - if (*cur != '>') - { - *ptr = start; - return (NULL); - } - - cur ++; - } - break; - - case '>' : // End dictionary? - if (cur[1] == '>') - { - obj.type = PPD_PS_END_DICT; - cur += 2; - } - else - { - obj.type = PPD_PS_OTHER; - obj.value.other[0] = *cur; - - cur ++; - } - break; - - case '{' : // Start procedure - obj.type = PPD_PS_START_PROC; - cur ++; - break; - - case '}' : // End procedure - obj.type = PPD_PS_END_PROC; - cur ++; - break; - - case '-' : // Possible number - case '+' : - if (!isdigit(cur[1] & 255) && cur[1] != '.') - { - obj.type = PPD_PS_OTHER; - obj.value.other[0] = *cur; - - cur ++; - break; - } - - case '0' : // Number - case '1' : - case '2' : - case '3' : - case '4' : - case '5' : - case '6' : - case '7' : - case '8' : - case '9' : - case '.' : - obj.type = PPD_PS_NUMBER; - - start = cur; - for (cur ++; *cur; cur ++) - if (!isdigit(*cur & 255)) - break; - - if (*cur == '#') - { - // - // Integer with radix... - // - - obj.value.number = strtol(cur + 1, &cur, atoi(start)); - break; - } - else if (strchr(".Ee()<>[]{}/%", *cur) || isspace(*cur & 255)) - { - // - // Integer or real number... - // - - obj.value.number = _ppdStrScand(start, &cur, localeconv()); - break; - } - else - cur = start; - - default : // Operator/variable name - start = cur; - - if (*cur == '/') - { - obj.type = PPD_PS_NAME; - valptr = obj.value.name; - valend = obj.value.name + sizeof(obj.value.name) - 1; - cur ++; - } - else - { - obj.type = PPD_PS_OTHER; - valptr = obj.value.other; - valend = obj.value.other + sizeof(obj.value.other) - 1; - } - - while (*cur) - { - if (strchr("()<>[]{}/%", *cur) || isspace(*cur & 255)) - break; - else if (valptr < valend) - *valptr++ = *cur++; - else - { - *ptr = start; - return (NULL); - } - } - - if (obj.type == PPD_PS_OTHER) - { - if (!strcmp(obj.value.other, "true")) - { - obj.type = PPD_PS_BOOLEAN; - obj.value.boolean = 1; - } - else if (!strcmp(obj.value.other, "false")) - { - obj.type = PPD_PS_BOOLEAN; - obj.value.boolean = 0; - } - else if (!strcmp(obj.value.other, "null")) - obj.type = PPD_PS_NULL; - else if (!strcmp(obj.value.other, "cleartomark")) - obj.type = PPD_PS_CLEARTOMARK; - else if (!strcmp(obj.value.other, "copy")) - obj.type = PPD_PS_COPY; - else if (!strcmp(obj.value.other, "dup")) - obj.type = PPD_PS_DUP; - else if (!strcmp(obj.value.other, "index")) - obj.type = PPD_PS_INDEX; - else if (!strcmp(obj.value.other, "pop")) - obj.type = PPD_PS_POP; - else if (!strcmp(obj.value.other, "roll")) - obj.type = PPD_PS_ROLL; - else if (!strcmp(obj.value.other, "setpagedevice")) - obj.type = PPD_PS_SETPAGEDEVICE; - else if (!strcmp(obj.value.other, "stopped")) - obj.type = PPD_PS_STOPPED; - } - break; - } - - // - // Save the current position in the string and return the new object... - // - - *ptr = cur; - - return (ppd_push_stack(st, &obj)); -} - - -// -// 'ppd_setpagedevice()' - Simulate the PostScript setpagedevice operator. -// - -static int // O - 0 on success, -1 on error -ppd_setpagedevice( - _ppd_ps_stack_t *st, // I - Stack - cups_page_header2_t *h, // O - Page header - int *preferred_bits)// O - Preferred bits per color -{ - int i; // Index into array - _ppd_ps_obj_t *obj, // Current object - *end; // End of dictionary - const char *name; // Attribute name - - - // - // Make sure we have a dictionary on the stack... - // - - if (st->num_objs == 0) - return (-1); - - obj = end = st->objs + st->num_objs - 1; - - if (obj->type != PPD_PS_END_DICT) - return (-1); - - obj --; - - while (obj > st->objs) - { - if (obj->type == PPD_PS_START_DICT) - break; - - obj --; - } - - if (obj < st->objs) - return (-1); - - // - // Found the start of the dictionary, empty the stack to this point... - // - - st->num_objs = (int)(obj - st->objs); - - // - // Now pull /name and value pairs from the dictionary... - // - - DEBUG_puts("3ppd_setpagedevice: Dictionary:"); - - for (obj ++; obj < end; obj ++) - { - // - // Grab the name... - // - - if (obj->type != PPD_PS_NAME) - return (-1); - - name = obj->value.name; - obj ++; - -#ifdef DEBUG - DEBUG_printf(("4ppd_setpagedevice: /%s ", name)); - ppd_DEBUG_object("setpagedevice", obj); -#endif // DEBUG - - // - // Then grab the value... - // - - if (!strcmp(name, "MediaClass") && obj->type == PPD_PS_STRING) - strlcpy(h->MediaClass, obj->value.string, sizeof(h->MediaClass)); - else if (!strcmp(name, "MediaColor") && obj->type == PPD_PS_STRING) - strlcpy(h->MediaColor, obj->value.string, sizeof(h->MediaColor)); - else if (!strcmp(name, "MediaType") && obj->type == PPD_PS_STRING) - strlcpy(h->MediaType, obj->value.string, sizeof(h->MediaType)); - else if (!strcmp(name, "OutputType") && obj->type == PPD_PS_STRING) - strlcpy(h->OutputType, obj->value.string, sizeof(h->OutputType)); - else if (!strcmp(name, "AdvanceDistance") && obj->type == PPD_PS_NUMBER) - h->AdvanceDistance = (unsigned)obj->value.number; - else if (!strcmp(name, "AdvanceMedia") && obj->type == PPD_PS_NUMBER) - h->AdvanceMedia = (unsigned)obj->value.number; - else if (!strcmp(name, "Collate") && obj->type == PPD_PS_BOOLEAN) - h->Collate = (unsigned)obj->value.boolean; - else if (!strcmp(name, "CutMedia") && obj->type == PPD_PS_NUMBER) - h->CutMedia = (cups_cut_t)(unsigned)obj->value.number; - else if (!strcmp(name, "Duplex") && obj->type == PPD_PS_BOOLEAN) - h->Duplex = (unsigned)obj->value.boolean; - else if (!strcmp(name, "HWResolution") && obj->type == PPD_PS_START_ARRAY) - { - if (obj[1].type == PPD_PS_NUMBER && obj[2].type == PPD_PS_NUMBER && - obj[3].type == PPD_PS_END_ARRAY) - { - h->HWResolution[0] = (unsigned)obj[1].value.number; - h->HWResolution[1] = (unsigned)obj[2].value.number; - obj += 3; - } - else - return (-1); - } - else if (!strcmp(name, "InsertSheet") && obj->type == PPD_PS_BOOLEAN) - h->InsertSheet = (unsigned)obj->value.boolean; - else if (!strcmp(name, "Jog") && obj->type == PPD_PS_NUMBER) - h->Jog = (unsigned)obj->value.number; - else if (!strcmp(name, "LeadingEdge") && obj->type == PPD_PS_NUMBER) - h->LeadingEdge = (unsigned)obj->value.number; - else if (!strcmp(name, "ManualFeed") && obj->type == PPD_PS_BOOLEAN) - h->ManualFeed = (unsigned)obj->value.boolean; - else if ((!strcmp(name, "cupsMediaPosition") || - !strcmp(name, "MediaPosition")) && obj->type == PPD_PS_NUMBER) - { - // - // cupsMediaPosition is supported for backwards compatibility only. - // We added it back in the Ghostscript 5.50 days to work around a - // bug in Ghostscript WRT handling of MediaPosition and setpagedevice. - // - // All new development should set MediaPosition... - // - - h->MediaPosition = (unsigned)obj->value.number; - } - else if (!strcmp(name, "MediaWeight") && obj->type == PPD_PS_NUMBER) - h->MediaWeight = (unsigned)obj->value.number; - else if (!strcmp(name, "MirrorPrint") && obj->type == PPD_PS_BOOLEAN) - h->MirrorPrint = (unsigned)obj->value.boolean; - else if (!strcmp(name, "NegativePrint") && obj->type == PPD_PS_BOOLEAN) - h->NegativePrint = (unsigned)obj->value.boolean; - else if (!strcmp(name, "NumCopies") && obj->type == PPD_PS_NUMBER) - h->NumCopies = (unsigned)obj->value.number; - else if (!strcmp(name, "Orientation") && obj->type == PPD_PS_NUMBER) - h->Orientation = (unsigned)obj->value.number; - else if (!strcmp(name, "OutputFaceUp") && obj->type == PPD_PS_BOOLEAN) - h->OutputFaceUp = (unsigned)obj->value.boolean; - else if (!strcmp(name, "PageSize") && obj->type == PPD_PS_START_ARRAY) - { - if (obj[1].type == PPD_PS_NUMBER && obj[2].type == PPD_PS_NUMBER && - obj[3].type == PPD_PS_END_ARRAY) - { - h->cupsPageSize[0] = (float)obj[1].value.number; - h->cupsPageSize[1] = (float)obj[2].value.number; - - h->PageSize[0] = (unsigned)obj[1].value.number; - h->PageSize[1] = (unsigned)obj[2].value.number; - - obj += 3; - } - else - return (-1); - } - else if (!strcmp(name, "Separations") && obj->type == PPD_PS_BOOLEAN) - h->Separations = (unsigned)obj->value.boolean; - else if (!strcmp(name, "TraySwitch") && obj->type == PPD_PS_BOOLEAN) - h->TraySwitch = (unsigned)obj->value.boolean; - else if (!strcmp(name, "Tumble") && obj->type == PPD_PS_BOOLEAN) - h->Tumble = (unsigned)obj->value.boolean; - else if (!strcmp(name, "cupsMediaType") && obj->type == PPD_PS_NUMBER) - h->cupsMediaType = (unsigned)obj->value.number; - else if (!strcmp(name, "cupsBitsPerColor") && obj->type == PPD_PS_NUMBER) - h->cupsBitsPerColor = (unsigned)obj->value.number; - else if (!strcmp(name, "cupsPreferredBitsPerColor") && - obj->type == PPD_PS_NUMBER) - *preferred_bits = (int)obj->value.number; - else if (!strcmp(name, "cupsColorOrder") && obj->type == PPD_PS_NUMBER) - h->cupsColorOrder = (cups_order_t)(unsigned)obj->value.number; - else if (!strcmp(name, "cupsColorSpace") && obj->type == PPD_PS_NUMBER) - h->cupsColorSpace = (cups_cspace_t)(unsigned)obj->value.number; - else if (!strcmp(name, "cupsCompression") && obj->type == PPD_PS_NUMBER) - h->cupsCompression = (unsigned)obj->value.number; - else if (!strcmp(name, "cupsRowCount") && obj->type == PPD_PS_NUMBER) - h->cupsRowCount = (unsigned)obj->value.number; - else if (!strcmp(name, "cupsRowFeed") && obj->type == PPD_PS_NUMBER) - h->cupsRowFeed = (unsigned)obj->value.number; - else if (!strcmp(name, "cupsRowStep") && obj->type == PPD_PS_NUMBER) - h->cupsRowStep = (unsigned)obj->value.number; - else if (!strcmp(name, "cupsBorderlessScalingFactor") && - obj->type == PPD_PS_NUMBER) - h->cupsBorderlessScalingFactor = (float)obj->value.number; - else if (!strncmp(name, "cupsInteger", 11) && obj->type == PPD_PS_NUMBER) - { - if ((i = atoi(name + 11)) < 0 || i > 15) - return (-1); - - h->cupsInteger[i] = (unsigned)obj->value.number; - } - else if (!strncmp(name, "cupsReal", 8) && obj->type == PPD_PS_NUMBER) - { - if ((i = atoi(name + 8)) < 0 || i > 15) - return (-1); - - h->cupsReal[i] = (float)obj->value.number; - } - else if (!strncmp(name, "cupsString", 10) && obj->type == PPD_PS_STRING) - { - if ((i = atoi(name + 10)) < 0 || i > 15) - return (-1); - - strlcpy(h->cupsString[i], obj->value.string, sizeof(h->cupsString[i])); - } - else if (!strcmp(name, "cupsMarkerType") && obj->type == PPD_PS_STRING) - strlcpy(h->cupsMarkerType, obj->value.string, sizeof(h->cupsMarkerType)); - else if (!strcmp(name, "cupsPageSizeName") && obj->type == PPD_PS_STRING) - strlcpy(h->cupsPageSizeName, obj->value.string, - sizeof(h->cupsPageSizeName)); - else if (!strcmp(name, "cupsRenderingIntent") && - obj->type == PPD_PS_STRING) - strlcpy(h->cupsRenderingIntent, obj->value.string, - sizeof(h->cupsRenderingIntent)); - else - { - // - // Ignore unknown name+value... - // - - DEBUG_printf(("4ppd_setpagedevice: Unknown name (\"%s\") or value...\n", - name)); - - while (obj[1].type != PPD_PS_NAME && obj < end) - obj ++; - } - } - - return (0); -} - - -#ifdef DEBUG -// -// 'ppd_DEBUG_object()' - Print an object's value... -// - -static void -ppd_DEBUG_object(const char *prefix, // I - Prefix string - _ppd_ps_obj_t *obj) // I - Object to print -{ - switch (obj->type) - { - case PPD_PS_NAME : - DEBUG_printf(("4%s: /%s\n", prefix, obj->value.name)); - break; - - case PPD_PS_NUMBER : - DEBUG_printf(("4%s: %g\n", prefix, obj->value.number)); - break; - - case PPD_PS_STRING : - DEBUG_printf(("4%s: (%s)\n", prefix, obj->value.string)); - break; - - case PPD_PS_BOOLEAN : - if (obj->value.boolean) - DEBUG_printf(("4%s: true", prefix)); - else - DEBUG_printf(("4%s: false", prefix)); - break; - - case PPD_PS_NULL : - DEBUG_printf(("4%s: null", prefix)); - break; - - case PPD_PS_START_ARRAY : - DEBUG_printf(("4%s: [", prefix)); - break; - - case PPD_PS_END_ARRAY : - DEBUG_printf(("4%s: ]", prefix)); - break; - - case PPD_PS_START_DICT : - DEBUG_printf(("4%s: <<", prefix)); - break; - - case PPD_PS_END_DICT : - DEBUG_printf(("4%s: >>", prefix)); - break; - - case PPD_PS_START_PROC : - DEBUG_printf(("4%s: {", prefix)); - break; - - case PPD_PS_END_PROC : - DEBUG_printf(("4%s: }", prefix)); - break; - - case PPD_PS_CLEARTOMARK : - DEBUG_printf(("4%s: --cleartomark--", prefix)); - break; - - case PPD_PS_COPY : - DEBUG_printf(("4%s: --copy--", prefix)); - break; - - case PPD_PS_DUP : - DEBUG_printf(("4%s: --dup--", prefix)); - break; - - case PPD_PS_INDEX : - DEBUG_printf(("4%s: --index--", prefix)); - break; - - case PPD_PS_POP : - DEBUG_printf(("4%s: --pop--", prefix)); - break; - - case PPD_PS_ROLL : - DEBUG_printf(("4%s: --roll--", prefix)); - break; - - case PPD_PS_SETPAGEDEVICE : - DEBUG_printf(("4%s: --setpagedevice--", prefix)); - break; - - case PPD_PS_STOPPED : - DEBUG_printf(("4%s: --stopped--", prefix)); - break; - - case PPD_PS_OTHER : - DEBUG_printf(("4%s: --%s--", prefix, obj->value.other)); - break; - } -} - - -// -// 'ppd_DEBUG_stack()' - Print a stack... -// - -static void -ppd_DEBUG_stack(const char *prefix, // I - Prefix string - _ppd_ps_stack_t *st) // I - Stack -{ - int c; // Looping var - _ppd_ps_obj_t *obj; // Current object on stack - - - for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++) - ppd_DEBUG_object(prefix, obj); -} -#endif // DEBUG diff --git a/ppd/raster-private.h b/ppd/raster-private.h deleted file mode 100644 index 1ee279ad6..000000000 --- a/ppd/raster-private.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// Private image library definitions for libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1993-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. - - -#ifndef _PPD_RASTER_PRIVATE_H_ -# define _PPD_RASTER_PRIVATE_H_ - -// -// Include necessary headers... -// - -# include -# include "debug-private.h" -# include "string-private.h" -# ifdef _WIN32 -# include -# include // for htonl() definition -# else -# include -# include -# endif // _WIN32 - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// Prototypes... -// - -extern void _ppdRasterAddError(const char *f, ...); -extern void _ppdRasterClearError(void); -extern const char *_ppdRasterErrorString(void); - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_PPD_RASTER_PRIVATE_H_ diff --git a/ppd/raster.defs b/ppd/raster.defs deleted file mode 100644 index 278aac049..000000000 --- a/ppd/raster.defs +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file contains the standard definitions for enumerated attributes - * in the CUPS raster page device dictionary. - * - * Copyright © 2007-2018 by Apple Inc. - * Copyright © 1997-2005 by Easy Software Products. - * - * Licensed under Apache License v2.0. See the file "LICENSE" for more - * information. - */ - -/* Jog values */ -#define CUPS_JOG_NONE 0 /* Never move pages */ -#define CUPS_JOG_FILE 1 /* Move pages after this file */ -#define CUPS_JOG_JOB 2 /* Move pages after this job */ -#define CUPS_JOG_SET 3 /* Move pages after this set */ - -/* Orientation values */ -#define CUPS_ORIENT_0 0 /* Don't rotate the page */ -#define CUPS_ORIENT_90 1 /* Rotate the page counter-clockwise */ -#define CUPS_ORIENT_180 2 /* Turn the page upside down */ -#define CUPS_ORIENT_270 3 /* Rotate the page clockwise */ - -/* CutMedia values */ -#define CUPS_CUT_NONE 0 /* Never cut the roll */ -#define CUPS_CUT_FILE 1 /* Cut the roll after this file */ -#define CUPS_CUT_JOB 2 /* Cut the roll after this job */ -#define CUPS_CUT_SET 3 /* Cut the roll after this set */ -#define CUPS_CUT_PAGE 4 /* Cut the roll after this page */ - -/* AdvanceMedia values */ -#define CUPS_ADVANCE_NONE 0 /* Never advance the roll */ -#define CUPS_ADVANCE_FILE 1 /* Advance the roll after this file */ -#define CUPS_ADVANCE_JOB 2 /* Advance the roll after this job */ -#define CUPS_ADVANCE_SET 3 /* Advance the roll after this set */ -#define CUPS_ADVANCE_PAGE 4 /* Advance the roll after this page */ - -/* LeadingEdge values */ -#define CUPS_EDGE_TOP 0 /* Leading edge is the top of the page */ -#define CUPS_EDGE_RIGHT 1 /* Leading edge is the right of the page */ -#define CUPS_EDGE_BOTTOM 2 /* Leading edge is the bottom of the page */ -#define CUPS_EDGE_LEFT 3 /* Leading edge is the left of the page */ - -/* cupsColorOrder values */ -#define CUPS_ORDER_CHUNKED 0 /* CMYK CMYK CMYK ... */ -#define CUPS_ORDER_BANDED 1 /* CCC MMM YYY KKK ... */ -#define CUPS_ORDER_PLANAR 2 /* CCC ... MMM ... YYY ... KKK ... */ - -/* cupsColorSpace values */ -#define CUPS_CSPACE_W 0 /* Luminance */ -#define CUPS_CSPACE_RGB 1 /* Red, green, blue */ -#define CUPS_CSPACE_RGBA 2 /* Red, green, blue, alpha */ -#define CUPS_CSPACE_K 3 /* Black */ -#define CUPS_CSPACE_CMY 4 /* Cyan, magenta, yellow */ -#define CUPS_CSPACE_YMC 5 /* Yellow, magenta, cyan */ -#define CUPS_CSPACE_CMYK 6 /* Cyan, magenta, yellow, black */ -#define CUPS_CSPACE_YMCK 7 /* Yellow, magenta, cyan, black */ -#define CUPS_CSPACE_KCMY 8 /* Black, cyan, magenta, yellow */ -#define CUPS_CSPACE_KCMYcm 9 /* Black, cyan, magenta, yellow, * - * light-cyan, light-magenta */ -#define CUPS_CSPACE_GMCK 10 /* Gold, magenta, yellow, black */ -#define CUPS_CSPACE_GMCS 11 /* Gold, magenta, yellow, silver */ -#define CUPS_CSPACE_WHITE 12 /* White ink (as black) */ -#define CUPS_CSPACE_GOLD 13 /* Gold foil */ -#define CUPS_CSPACE_SILVER 14 /* Silver foil */ - -#define CUPS_CSPACE_CIEXYZ 15 /* CIE XYZ */ -#define CUPS_CSPACE_CIELab 16 /* CIE Lab */ - -#define CUPS_CSPACE_ICC1 32 /* ICC-based, 1 color */ -#define CUPS_CSPACE_ICC2 33 /* ICC-based, 2 colors */ -#define CUPS_CSPACE_ICC3 34 /* ICC-based, 3 colors */ -#define CUPS_CSPACE_ICC4 35 /* ICC-based, 4 colors */ -#define CUPS_CSPACE_ICC5 36 /* ICC-based, 5 colors */ -#define CUPS_CSPACE_ICC6 37 /* ICC-based, 6 colors */ -#define CUPS_CSPACE_ICC7 38 /* ICC-based, 7 colors */ -#define CUPS_CSPACE_ICC8 39 /* ICC-based, 8 colors */ -#define CUPS_CSPACE_ICC9 40 /* ICC-based, 9 colors */ -#define CUPS_CSPACE_ICCA 41 /* ICC-based, 10 colors */ -#define CUPS_CSPACE_ICCB 42 /* ICC-based, 11 colors */ -#define CUPS_CSPACE_ICCC 43 /* ICC-based, 12 colors */ -#define CUPS_CSPACE_ICCD 44 /* ICC-based, 13 colors */ -#define CUPS_CSPACE_ICCE 45 /* ICC-based, 14 colors */ -#define CUPS_CSPACE_ICCF 46 /* ICC-based, 15 colors */ diff --git a/ppd/rastertops.c b/ppd/rastertops.c deleted file mode 100644 index 1d6f5f041..000000000 --- a/ppd/rastertops.c +++ /dev/null @@ -1,540 +0,0 @@ -// -// Raster file to PostScript filter function for libppd. -// -// Copyright © 2020 by Till Kamppeter -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1993-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "debug-internal.h" -#include - -// -// Types... -// - -typedef struct rastertops_doc_s // **** Document information **** -{ - cups_file_t *inputfp; // Temporary file, if any - FILE *outputfp; // Temporary file, if any - cf_logfunc_t logfunc; // Logging function, NULL for no - // logging - void *logdata; // User data for logging function, can - // be NULL - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled - // function, can be NULL -} rastertops_doc_t; - - -// -// 'write_prolog()' - Writing the PostScript prolog for the file -// - -static void -write_prolog(int width, // I - width of the image in points - int height, // I - height of the image in points - rastertops_doc_t *doc) // I - Document information -{ - // Document header... - fprintf(doc->outputfp, "%%!PS-Adobe-3.0\n"); - fprintf(doc->outputfp, "%%%%BoundingBox: %d %d %d %d\n", 0, 0, width, height); - fprintf(doc->outputfp, "%%%%Creator: cups-filters\n"); - fprintf(doc->outputfp, "%%%%LanguageLevel: 2\n"); - fprintf(doc->outputfp, "%%%%DocumentData: Clean7Bit\n"); - fprintf(doc->outputfp, "%%%%Pages: (atend)\n"); - fprintf(doc->outputfp, "%%%%EndComments\n"); - fprintf(doc->outputfp, "%%%%BeginProlog\n"); - fprintf(doc->outputfp, "%%%%EndProlog\n"); -} - - -// -// 'write_start_page()' - Write the basic page setup -// - -static void -write_start_page(int page, // I - Page to write - int width, // I - Page width in points - int length, // I - Page length in points - rastertops_doc_t *doc) // I - Document information -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", page, 1); - fprintf(doc->outputfp, "%%%%Page: %d %d\n", page, page); - fprintf(doc->outputfp, "%%%%BeginPageSetup\n"); - fprintf(doc->outputfp, "<< /PageSize[%d %d] >> setpagedevice\n", width, - length); - fprintf(doc->outputfp, "%%%%EndPageSetup\n"); -} - - -// -// 'find_bits()' - Finding the number of bits per color -// - -static int // O - Exit status -find_bits(cups_cspace_t mode, // I - Color space of data - int bpc) // I - Original bits per color of data -{ - if (bpc == 1 && - (mode == CUPS_CSPACE_RGB || - mode == CUPS_CSPACE_ADOBERGB || - mode == CUPS_CSPACE_SRGB || - mode == CUPS_CSPACE_CMY)) - return (8); - - if (bpc == 16) - return (8); - - return (bpc); -} - - -// -// 'write_image()' - Write the information regarding the image -// - -static void -write_image(int pagewidth, // I - width of page in points - int pageheight, // I - height of page in points - int bpc, // I - bits per color - int pixwidth, // I - width of image in pixels - int pixheight, // I - height of image in pixels - cups_cspace_t mode, // I - color model of image - rastertops_doc_t *doc) // I - Document information -{ - fprintf(doc->outputfp, "gsave\n"); - - switch (mode) - { - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - fprintf(doc->outputfp, "/DeviceRGB setcolorspace\n"); - break; - - case CUPS_CSPACE_CMYK: - fprintf(doc->outputfp, "/DeviceCMYK setcolorspace\n"); - break; - - default: - case CUPS_CSPACE_K: - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - fprintf(doc->outputfp, "/DeviceGray setcolorspace\n"); - break; - } - - if (bpc == 16) - fprintf(doc->outputfp, "/Input currentfile /FlateDecode filter def\n"); - fprintf(doc->outputfp, "%d %d scale\n", pagewidth, pageheight); - fprintf(doc->outputfp, "<< \n" - "/ImageType 1\n" - "/Width %d\n" - "/Height %d\n" - "/BitsPerComponent %d\n", pixwidth, pixheight, find_bits(mode, bpc)); - - switch (mode) - { - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_CMY: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - fprintf(doc->outputfp, "/Decode [0 1 0 1 0 1]\n"); - break; - - case CUPS_CSPACE_CMYK: - fprintf(doc->outputfp, "/Decode [0 1 0 1 0 1 0 1]\n"); - break; - - case CUPS_CSPACE_SW: - fprintf(doc->outputfp, "/Decode [0 1]\n"); - break; - - default: - case CUPS_CSPACE_K: - case CUPS_CSPACE_W: - fprintf(doc->outputfp, "/Decode [1 0]\n"); - break; - } - - if (bpc == 16) - fprintf(doc->outputfp, - "/DataSource {3 string 0 1 2 {1 index exch Input read {pop}" - "if Input read pop put } for} bind\n"); - else - fprintf(doc->outputfp, "/DataSource currentfile /FlateDecode filter\n"); - - fprintf(doc->outputfp, "/ImageMatrix [%d 0 0 %d 0 %d]\n", pixwidth, - -1 * pixheight, pixheight); - fprintf(doc->outputfp, ">> image\n"); -} - - -// -// 'convert_pixels()'- Convert 1 bpc to 8 bpc -// - -static void -convert_pixels(unsigned char *pixdata, // I - Original pixel data - unsigned char *convertedpix, // I - Buffer for converted data - int width) // I - Width of data -{ - int j, k = 0; // Variables for iteration - unsigned int mask; // Variable for per byte iteration - unsigned char temp; // temporary character - - for(j = 0; j < width; ++j) - { - temp = *(pixdata + j); - for (mask = 0x80; mask != 0; mask >>= 1) - { - if (mask!=0x80 && mask != 0x08) - { - if (temp & mask) - convertedpix[k] = 0xFF; - else - convertedpix[k] = 0; - ++k; - } - } - } -} - - -// -// 'write_flate()' - Write the image data in flate encoded format -// - -static int // O - Error value -write_flate(cups_raster_t *ras, // I - Image data - cups_page_header2_t header, // I - Bytes Per Line - rastertops_doc_t *doc) // I - Document information -{ - int ret, // Return value of this - // function - flush, // Check the end of image - // data - curr_line=1, // Maitining the working - // line of pixels - alloc, - flag = 0; - unsigned have; // Bytes available in - // output buffer - z_stream strm; // Structure required - // by deflate - unsigned char *pixdata, - *convertedpix; - unsigned char in[header.cupsBytesPerLine * 6], // Input data buffer - out[header.cupsBytesPerLine * 6]; // Output data buffer - - // allocate deflate state - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, -1); - if (ret != Z_OK) - return (ret); - - if (header.cupsBitsPerColor == 1 && - (header.cupsColorSpace == CUPS_CSPACE_RGB || - header.cupsColorSpace == CUPS_CSPACE_ADOBERGB || - header.cupsColorSpace == CUPS_CSPACE_SRGB)) - flag = 1; - - // compress until end of file - do - { - pixdata = malloc(header.cupsBytesPerLine); - cupsRasterReadPixels(ras, pixdata, header.cupsBytesPerLine); - if (flag) - { - convertedpix = malloc(header.cupsBytesPerLine * 6); - convert_pixels(pixdata,convertedpix, header.cupsBytesPerLine); - alloc = header.cupsBytesPerLine * 6; - } - else - { - convertedpix = malloc(header.cupsBytesPerLine); - memcpy(convertedpix, pixdata, header.cupsBytesPerLine); - alloc = header.cupsBytesPerLine; - } - - if(curr_line == header.cupsHeight) - flush = Z_FINISH; - else - flush = Z_NO_FLUSH; - curr_line++; - memcpy(in, convertedpix, alloc); - strm.avail_in = alloc; - strm.next_in = in; - - // run deflate() on input until output buffer not full, finish - // compression if all of source has been read in - do - { - strm.avail_out = alloc; - strm.next_out = out; - - // Run the deflate algorithm on the data - ret = deflate(&strm, flush); - - // check whether state is not clobbered - DEBUG_assert(ret != Z_STREAM_ERROR); - have = alloc - strm.avail_out; - if (fwrite(out, 1, have, doc->outputfp) != have) - { - (void)deflateEnd(&strm); - if (convertedpix != NULL) - free(convertedpix); - return (Z_ERRNO); - } - } - while (strm.avail_out == 0); - - // all input will be used - DEBUG_assert(strm.avail_in == 0); - - // done when last data in file processed - free(pixdata); - free(convertedpix); - } - while (flush != Z_FINISH); - - // stream will be complete - DEBUG_assert(ret == Z_STREAM_END); - - // clean up and return - (void)deflateEnd(&strm); - return (Z_OK); -} - -// -// 'z_error()' - Report a zlib or i/o error -// - -static void -z_error(int ret, // I - Return status of deflate - rastertops_doc_t *doc) // I - Document information -{ - switch (ret) - { - case Z_ERRNO: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "ppdFilterRasterToPS: zpipe - error in source data or output file"); - break; - case Z_STREAM_ERROR: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "ppdFilterRasterToPS: zpipe - invalid compression level"); - break; - case Z_DATA_ERROR: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "ppdFilterRasterToPS: zpipe - invalid or incomplete deflate data"); - break; - case Z_MEM_ERROR: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "ppdFilterRasterToPS: zpipe - out of memory"); - break; - case Z_VERSION_ERROR: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "ppdFilterRasterToPS: zpipe - zlib version mismatch!"); - } -} - - -// -// 'write_end_page()' - Show the current page. -// - -static void -write_end_page(rastertops_doc_t *doc) // I - Document information -{ - fprintf(doc->outputfp, "\ngrestore\n"); - fprintf(doc->outputfp, "showpage\n"); - fprintf(doc->outputfp, "%%%%PageTrailer\n"); -} - -// -// 'write_trailer()' - Write the PostScript trailer. -// - -static void -write_trailer(int pages, // I - Number of pages - rastertops_doc_t *doc) // I - Document information -{ - fprintf(doc->outputfp, "%%%%Trailer\n"); - fprintf(doc->outputfp, "%%%%Pages: %d\n", pages); - fprintf(doc->outputfp, "%%%%EOF\n"); -} - -// -// 'ppdFilterRasterToPS()' - Filter function to convert PWG raster input -// to PostScript -// - -int // O - Error status -ppdFilterRasterToPS(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - rastertops_doc_t doc; // Document information - cups_file_t *inputfp; // Print file - FILE *outputfp; // Output data stream - cups_raster_t *ras; // Raster stream for printing - cups_page_header2_t header; // Page header from file - int empty, // Is the input empty? - Page = 0, // variable for counting the pages - ret; // Return value of deflate compression - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - - (void)inputseekable; - (void)parameters; - - // - // Open the input data stream specified by the inputfd... - // - - if ((inputfp = cupsFileOpenFd(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterRasterToPS: Unable to open input data stream."); - } - - return (1); - } - - // - // Open the output data stream specified by the outputfd... - // - - if ((outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterRasterToPS: Unable to open output data stream."); - } - - cupsFileClose(inputfp); - - return (1); - } - - doc.inputfp = inputfp; - doc.outputfp = outputfp; - // Logging function - doc.logfunc = log; - doc.logdata = ld; - // Job-is-canceled function - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - ras = cupsRasterOpen(inputfd, CUPS_RASTER_READ); - - // - // Process pages as needed... - // - - Page = 0; - empty = 1; - - while (cupsRasterReadHeader2(ras, &header)) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterRasterToPS: Job canceled"); - break; - } - - // - // Write the prolog for PS only once - // - - if (empty) - { - empty = 0; - write_prolog(header.PageSize[0], header.PageSize[1], &doc); - } - - // - // Write a status message with the page number and number of copies. - // - - Page ++; - - if (log) log(ld, CF_LOGLEVEL_INFO, - "ppdFilterRasterToPS: Starting page %d.", Page); - - // - // Write the starting of the page - // - - write_start_page(Page, header.PageSize[0], header.PageSize[1], &doc); - - // - // Write the information regarding the image - // - - write_image(header.PageSize[0], header.PageSize[1], - header.cupsBitsPerColor, - header.cupsWidth, header.cupsHeight, - header.cupsColorSpace, &doc); - - // Write the compressed image data - ret = write_flate(ras, header, &doc); - if (ret != Z_OK) - z_error(ret, &doc); - write_end_page(&doc); - } - - if (empty) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "ppdFilterRasterToPS: Input is empty, outputting empty file."); - cupsRasterClose(ras); - return (0); - } - - write_trailer(Page, &doc); - - cupsRasterClose(ras); - - cupsFileClose(inputfp); - - fclose(outputfp); - close(outputfd); - - return (0); -} diff --git a/ppd/snprintf.c b/ppd/snprintf.c deleted file mode 100644 index 89f4f914f..000000000 --- a/ppd/snprintf.c +++ /dev/null @@ -1,343 +0,0 @@ -// -// snprintf functions for libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "string-private.h" - - -#ifndef HAVE_VSNPRINTF -// -// '_ppd_vsnprintf()' - Format a string into a fixed size buffer. -// - -int // O - Number of bytes formatted -_ppd_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 - type; // Format type character - int width, // Width of field - prec; // Number of characters of precision - char tformat[100], // Temporary format string for sprintf() - *tptr, // Pointer into temporary format - temp[1024]; // Buffer for formatted numbers - size_t templen; // Length of "temp" - char *s; // Pointer to string - int slen; // Length of string - int bytes; // Total number of bytes needed - - - // - // Loop through the format string, formatting as needed... - // - - bufptr = buffer; - bufend = buffer + bufsize - 1; - bytes = 0; - - while (*format) - { - if (*format == '%') - { - tptr = tformat; - *tptr++ = *format++; - - if (*format == '%') - { - if (bufptr && bufptr < bufend) *bufptr++ = *format; - bytes ++; - format ++; - continue; - } - else if (strchr(" -+#\'", *format)) - { - *tptr++ = *format; - sign = *format++; - } - else - sign = 0; - - if (*format == '*') - { - // - // Get width from argument... - // - - format ++; - width = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); - tptr += strlen(tptr); - } - else - { - width = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - width = width * 10 + *format++ - '0'; - } - } - - if (*format == '.') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - format ++; - - if (*format == '*') - { - // - // Get precision from argument... - // - - format ++; - prec = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); - tptr += strlen(tptr); - } - else - { - prec = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - prec = prec * 10 + *format++ - '0'; - } - } - } - else - prec = -1; - - if (*format == 'l' && format[1] == 'l') - { - if (tptr < (tformat + sizeof(tformat) - 2)) - { - *tptr++ = 'l'; - *tptr++ = 'l'; - } - - format += 2; - } - else if (*format == 'h' || *format == 'l' || *format == 'L') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - } - - if (!*format) - break; - - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - type = *format++; - *tptr = '\0'; - - switch (type) - { - case 'E' : // Floating point formats - case 'G' : - case 'e' : - case 'f' : - case 'g' : - if ((width + 2) > sizeof(temp)) - break; - - sprintf(temp, tformat, va_arg(ap, double)); - templen = strlen(temp); - - bytes += (int)templen; - - if (bufptr) - { - if ((bufptr + templen) > bufend) - { - strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr = bufend; - } - else - { - memcpy(bufptr, temp, templen + 1); - bufptr += templen; - } - } - break; - - case 'B' : // Integer formats - case 'X' : - case 'b' : - case 'd' : - case 'i' : - case 'o' : - case 'u' : - case 'x' : - if ((width + 2) > sizeof(temp)) - break; - - sprintf(temp, tformat, va_arg(ap, int)); - templen = strlen(temp); - - bytes += (int)templen; - - if (bufptr) - { - if ((bufptr + templen) > bufend) - { - strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr = bufend; - } - else - { - memcpy(bufptr, temp, templen + 1); - bufptr += templen; - } - } - break; - - case 'p' : // Pointer value - if ((width + 2) > sizeof(temp)) - break; - - sprintf(temp, tformat, va_arg(ap, void *)); - templen = strlen(temp); - - bytes += (int)templen; - - if (bufptr) - { - if ((bufptr + templen) > bufend) - { - strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr = bufend; - } - else - { - memcpy(bufptr, temp, templen + 1); - bufptr += templen; - } - } - break; - - case 'c' : // Character or character array - bytes += width; - - if (bufptr) - { - if (width <= 1) - *bufptr++ = va_arg(ap, int); - else - { - if ((bufptr + width) > bufend) - width = (int)(bufend - bufptr); - - memcpy(bufptr, va_arg(ap, char *), (size_t)width); - bufptr += width; - } - } - break; - - case 's' : // String - if ((s = va_arg(ap, char *)) == NULL) - s = "(null)"; - - slen = (int)strlen(s); - if (slen > width && prec != width) - width = slen; - - bytes += width; - - if (bufptr) - { - if ((bufptr + width) > bufend) - width = (int)(bufend - bufptr); - - if (slen > width) - slen = width; - - if (sign == '-') - { - memcpy(bufptr, s, (size_t)slen); - memset(bufptr + slen, ' ', (size_t)(width - slen)); - } - else - { - memset(bufptr, ' ', (size_t)(width - slen)); - memcpy(bufptr + width - slen, s, (size_t)slen); - } - - bufptr += width; - } - break; - - case 'n' : // Output number of chars so far - *(va_arg(ap, int *)) = bytes; - break; - } - } - else - { - bytes ++; - - if (bufptr && bufptr < bufend) - *bufptr++ = *format; - - format ++; - } - } - - // - // Nul-terminate the string and return the number of characters needed. - // - - *bufptr = '\0'; - - return (bytes); -} -#endif // !HAVE_VSNPRINT - - -#ifndef HAVE_SNPRINTF -// -// '_ppd_snprintf()' - Format a string into a fixed size buffer. -// - -int // O - Number of bytes formatted -_ppd_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 diff --git a/ppd/string-private.h b/ppd/string-private.h deleted file mode 100644 index 9652755ec..000000000 --- a/ppd/string-private.h +++ /dev/null @@ -1,212 +0,0 @@ -// -// Private string definitions for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_STRING_PRIVATE_H_ -# define _PPD_STRING_PRIVATE_H_ - -// -// Include necessary headers... -// - -# include "config.h" -# include -# include -# include -# include -# include -# include -# include - -# ifdef HAVE_STRING_H -# include -# endif // HAVE_STRING_H - -# ifdef HAVE_STRINGS_H -# include -# endif // HAVE_STRINGS_H - -# ifdef HAVE_BSTRING_H -# include -# endif // HAVE_BSTRING_H - -# if defined(_WIN32) && !defined(__CUPS_SSIZE_T_DEFINED) -# define __CUPS_SSIZE_T_DEFINED -# include -// Windows does not support the ssize_t type, so map it to long... -typedef long ssize_t; // @private@ -# endif // _WIN32 && !__CUPS_SSIZE_T_DEFINED - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -// -// String pool structures... -// - -# define _PPD_STR_GUARD 0x56788765 - -typedef struct _ppd_sp_item_s // **** String Pool Item **** -{ -# ifdef DEBUG_GUARDS - unsigned int guard; // Guard word -# endif // DEBUG_GUARDS - unsigned int ref_count; // Reference count - char str[1]; // String -} _ppd_sp_item_t; - - -// -// Replacements for the ctype macros that are not affected by locale, since we -// really only care about testing for ASCII characters when parsing files, etc. -// -// The _PPD_INLINE definition controls whether we get an inline function body, -// and external function body, or an external definition. -// - -# if defined(__GNUC__) || __STDC_VERSION__ >= 199901L -# define _PPD_INLINE static inline -# elif defined(_MSC_VER) -# define _PPD_INLINE static __inline -# elif defined(_PPD_STRING_C_) -# define _PPD_INLINE -# endif // __GNUC__ || __STDC_VERSION__ - -# ifdef _PPD_INLINE -_PPD_INLINE int // O - 1 on match, 0 otherwise -_ppd_isalnum(int ch) // I - Character to test -{ - return ((ch >= '0' && ch <= '9') || - (ch >= 'A' && ch <= 'Z') || - (ch >= 'a' && ch <= 'z')); -} - -_PPD_INLINE int // O - 1 on match, 0 otherwise -_ppd_isalpha(int ch) // I - Character to test -{ - return ((ch >= 'A' && ch <= 'Z') || - (ch >= 'a' && ch <= 'z')); -} - -_PPD_INLINE int // O - 1 on match, 0 otherwise -_ppd_islower(int ch) // I - Character to test -{ - return (ch >= 'a' && ch <= 'z'); -} - -_PPD_INLINE int // O - 1 on match, 0 otherwise -_ppd_isspace(int ch) // I - Character to test -{ - return (ch == ' ' || ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || - ch == '\v'); -} - -_PPD_INLINE int // O - 1 on match, 0 otherwise -_ppd_isupper(int ch) // I - Character to test -{ - return (ch >= 'A' && ch <= 'Z'); -} - -_PPD_INLINE int // O - Converted character -_ppd_tolower(int ch) // I - Character to convert -{ - return (_ppd_isupper(ch) ? ch - 'A' + 'a' : ch); -} - -_PPD_INLINE int // O - Converted character -_ppd_toupper(int ch) // I - Character to convert -{ - return (_ppd_islower(ch) ? ch - 'a' + 'A' : ch); -} -# else -extern int _ppd_isalnum(int ch); -extern int _ppd_isalpha(int ch); -extern int _ppd_islower(int ch); -extern int _ppd_isspace(int ch); -extern int _ppd_isupper(int ch); -extern int _ppd_tolower(int ch); -extern int _ppd_toupper(int ch); -# endif // _PPD_INLINE - - -// -// Prototypes... -// - -extern ssize_t _ppd_safe_vsnprintf(char *buffer, size_t bufsize, - const char *format, va_list args); -extern void _ppd_strcpy(char *dst, const char *src); - -# ifndef HAVE_STRDUP -extern char *_ppd_strdup(const char *); -# define strdup _ppd_strdup -# endif // !HAVE_STRDUP - -extern int _ppd_strcasecmp(const char *, const char *); - -extern int _ppd_strncasecmp(const char *, const char *, size_t n); - -# ifndef HAVE_STRLCAT -extern size_t _ppd_strlcat(char *, const char *, size_t); -# define strlcat _ppd_strlcat -# endif // !HAVE_STRLCAT - -# ifndef HAVE_STRLCPY -extern size_t _ppd_strlcpy(char *, const char *, size_t); -# define strlcpy _ppd_strlcpy -# endif // !HAVE_STRLCPY - -# ifndef HAVE_SNPRINTF -extern int _ppd_snprintf(char *, size_t, const char *, ...); -# define snprintf _ppd_snprintf -# endif // !HAVE_SNPRINTF - -# ifndef HAVE_VSNPRINTF -extern int _ppd_vsnprintf(char *, size_t, const char *, va_list); -# define vsnprintf _ppd_vsnprintf -# endif // !HAVE_VSNPRINTF - - -// -// String pool functions... -// - -extern char *_ppdStrAlloc(const char *s); -extern void _ppdStrFlush(void); -extern void _ppdStrFree(const char *s); -extern char *_ppdStrRetain(const char *s); -extern size_t _ppdStrStatistics(size_t *alloc_bytes, size_t *total_bytes); - - -// -// Floating point number functions... -// - -extern char *_ppdStrFormatd(char *buf, char *bufend, double number, - struct lconv *loc); -extern double _ppdStrScand(const char *buf, char **bufptr, - struct lconv *loc); - - -// -// C++ magic... -// - -# ifdef __cplusplus -} -# endif // __cplusplus - -#endif // !_PPD_STRING_H_ diff --git a/ppd/string.c b/ppd/string.c deleted file mode 100644 index 3dbf5d84a..000000000 --- a/ppd/string.c +++ /dev/null @@ -1,742 +0,0 @@ -// -// String functions for libppd. -// -// Copyright © 2007-2019 by Apple Inc. -// Copyright © 1997-2007 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#define _PPD_STRING_C_ -#include "string-private.h" -#include "array-private.h" -#include "thread-private.h" -#include "debug-internal.h" -#include -#include - - -// -// Local globals... -// - -static _ppd_mutex_t sp_mutex = _PPD_MUTEX_INITIALIZER; - // Mutex to control access to pool -static cups_array_t *stringpool = NULL; - // Global string pool - - -// -// Local functions... -// - -static int ppd_compare_sp_items(_ppd_sp_item_t *a, _ppd_sp_item_t *b); - - -// -// '_ppdStrAlloc()' - Allocate/reference a string. -// - -char * // O - String pointer -_ppdStrAlloc(const char *s) // I - String -{ - size_t slen; // Length of string - _ppd_sp_item_t *item, // String pool item - *key; // Search key - - - // - // Range check input... - // - - if (!s) - return (NULL); - - // - // Get the string pool... - // - - _ppdMutexLock(&sp_mutex); - - if (!stringpool) - stringpool = cupsArrayNew((cups_array_func_t)ppd_compare_sp_items, NULL); - - if (!stringpool) - { - _ppdMutexUnlock(&sp_mutex); - - return (NULL); - } - - // - // See if the string is already in the pool... - // - - key = (_ppd_sp_item_t *)(s - offsetof(_ppd_sp_item_t, str)); - - if ((item = (_ppd_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL) - { - // - // Found it, return the cached string... - // - - item->ref_count ++; - -#ifdef DEBUG_GUARDS - DEBUG_printf(("5_ppdStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, " - "ref_count=%d", item, item->str, s, item->guard, - item->ref_count)); - - if (item->guard != _PPD_STR_GUARD) - { - _ppdMutexUnlock(&sp_mutex); - abort(); - } -#endif // DEBUG_GUARDS - - _ppdMutexUnlock(&sp_mutex); - - return (item->str); - } - - // - // Not found, so allocate a new one... - // - - slen = strlen(s); - item = (_ppd_sp_item_t *)calloc(1, sizeof(_ppd_sp_item_t) + slen); - if (!item) - { - _ppdMutexUnlock(&sp_mutex); - - return (NULL); - } - - item->ref_count = 1; - memcpy(item->str, s, slen + 1); - -#ifdef DEBUG_GUARDS - item->guard = _PPD_STR_GUARD; - - DEBUG_printf(("5_ppdStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, " - "ref_count=%d", item, item->str, s, item->guard, - item->ref_count)); -#endif // DEBUG_GUARDS - - // - // Add the string to the pool and return it... - // - - cupsArrayAdd(stringpool, item); - - _ppdMutexUnlock(&sp_mutex); - - return (item->str); -} - - -// -// '_ppdStrFlush()' - Flush the string pool. -// - -void -_ppdStrFlush(void) -{ - _ppd_sp_item_t *item; // Current item - - - DEBUG_printf(("4_ppdStrFlush: %d strings in array", - cupsArrayCount(stringpool))); - - _ppdMutexLock(&sp_mutex); - - for (item = (_ppd_sp_item_t *)cupsArrayFirst(stringpool); - item; - item = (_ppd_sp_item_t *)cupsArrayNext(stringpool)) - free(item); - - cupsArrayDelete(stringpool); - stringpool = NULL; - - _ppdMutexUnlock(&sp_mutex); -} - - -// -// '_ppdStrFormatd()' - Format a floating-point number. -// - -char * // O - Pointer to end of string -_ppdStrFormatd(char *buf, // I - String - char *bufend, // I - End of string buffer - double number, // I - Number to format - struct lconv *loc) // I - Locale data -{ - char *bufptr, // Pointer into buffer - temp[1024], // Temporary string - *tempdec, // Pointer to decimal point - *tempptr; // Pointer into temporary string - const char *dec; // Decimal point - int declen; // Length of decimal point - - - // - // Format the number using the "%.12f" format and then eliminate - // unnecessary trailing 0's. - // - - snprintf(temp, sizeof(temp), "%.12f", number); - for (tempptr = temp + strlen(temp) - 1; - tempptr > temp && *tempptr == '0'; - *tempptr-- = '\0'); - - // - // Next, find the decimal point... - // - - if (loc && loc->decimal_point) - { - dec = loc->decimal_point; - declen = (int)strlen(dec); - } - else - { - dec = "."; - declen = 1; - } - - if (declen == 1) - tempdec = strchr(temp, *dec); - else - tempdec = strstr(temp, dec); - - // - // Copy everything up to the decimal point... - // - - if (tempdec) - { - for (tempptr = temp, bufptr = buf; - tempptr < tempdec && bufptr < bufend; - *bufptr++ = *tempptr++); - - tempptr += declen; - - if (*tempptr && bufptr < bufend) - { - *bufptr++ = '.'; - - while (*tempptr && bufptr < bufend) - *bufptr++ = *tempptr++; - } - - *bufptr = '\0'; - } - else - { - strlcpy(buf, temp, (size_t)(bufend - buf + 1)); - bufptr = buf + strlen(buf); - } - - return (bufptr); -} - - -// -// '_ppdStrFree()' - Free/dereference a string. -// - -void -_ppdStrFree(const char *s) // I - String to free -{ - _ppd_sp_item_t *item, // String pool item - *key; // Search key - - - // - // Range check input... - // - - if (!s) - return; - - // - // Check the string pool... - // - // We don't need to lock the mutex yet, as we only want to know if - // the stringpool is initialized. The rest of the code will still - // work if it is initialized before we lock... - // - - if (!stringpool) - return; - - // - // See if the string is already in the pool... - // - - _ppdMutexLock(&sp_mutex); - - key = (_ppd_sp_item_t *)(s - offsetof(_ppd_sp_item_t, str)); - - if ((item = (_ppd_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL && - item == key) - { - // - // Found it, dereference... - // - -#ifdef DEBUG_GUARDS - if (key->guard != _PPD_STR_GUARD) - { - DEBUG_printf(("5_ppdStrFree: Freeing string %p(%s), guard=%08x, ref_count=%d", key, key->str, key->guard, key->ref_count)); - _ppdMutexUnlock(&sp_mutex); - abort(); - } -#endif // DEBUG_GUARDS - - item->ref_count --; - - if (!item->ref_count) - { - // - // Remove and free... - // - - cupsArrayRemove(stringpool, item); - - free(item); - } - } - - _ppdMutexUnlock(&sp_mutex); -} - - -// -// '_ppdStrRetain()' - Increment the reference count of a string. -// -// Note: This function does not verify that the passed pointer is in the -// string pool, so any calls to it MUST know they are passing in a -// good pointer. -// - -char * // O - Pointer to string -_ppdStrRetain(const char *s) // I - String to retain -{ - _ppd_sp_item_t *item; // Pointer to string pool item - - - if (s) - { - item = (_ppd_sp_item_t *)(s - offsetof(_ppd_sp_item_t, str)); - -#ifdef DEBUG_GUARDS - if (item->guard != _PPD_STR_GUARD) - { - DEBUG_printf(("5_ppdStrRetain: Retaining string %p(%s), guard=%08x, " - "ref_count=%d", item, s, item->guard, item->ref_count)); - abort(); - } -#endif // DEBUG_GUARDS - - _ppdMutexLock(&sp_mutex); - - item->ref_count ++; - - _ppdMutexUnlock(&sp_mutex); - } - - return ((char *)s); -} - - -// -// '_ppdStrScand()' - Scan a string for a floating-point number. -// -// This function handles the locale-specific BS so that a decimal -// point is always the period (".")... -// - -double // O - Number -_ppdStrScand(const char *buf, // I - Pointer to number - char **bufptr, // O - New pointer or NULL on error - struct lconv *loc) // I - Locale data -{ - char temp[1024], // Temporary buffer - *tempptr; // Pointer into temporary buffer - - - // - // Range check input... - // - - if (!buf) - return (0.0); - - // - // Skip leading whitespace... - // - - while (_ppd_isspace(*buf)) - buf ++; - - // - // Copy leading sign, numbers, period, and then numbers... - // - - tempptr = temp; - if (*buf == '-' || *buf == '+') - *tempptr++ = *buf++; - - while (isdigit(*buf & 255)) - if (tempptr < (temp + sizeof(temp) - 1)) - *tempptr++ = *buf++; - else - { - if (bufptr) - *bufptr = NULL; - - return (0.0); - } - - if (*buf == '.') - { - // - // Read fractional portion of number... - // - - buf ++; - - if (loc && loc->decimal_point) - { - strlcpy(tempptr, loc->decimal_point, - sizeof(temp) - (size_t)(tempptr - temp)); - tempptr += strlen(tempptr); - } - else if (tempptr < (temp + sizeof(temp) - 1)) - *tempptr++ = '.'; - else - { - if (bufptr) - *bufptr = NULL; - - return (0.0); - } - - while (isdigit(*buf & 255)) - if (tempptr < (temp + sizeof(temp) - 1)) - *tempptr++ = *buf++; - else - { - if (bufptr) - *bufptr = NULL; - - return (0.0); - } - } - - if (*buf == 'e' || *buf == 'E') - { - // - // Read exponent... - // - - if (tempptr < (temp + sizeof(temp) - 1)) - *tempptr++ = *buf++; - else - { - if (bufptr) - *bufptr = NULL; - - return (0.0); - } - - if (*buf == '+' || *buf == '-') - { - if (tempptr < (temp + sizeof(temp) - 1)) - *tempptr++ = *buf++; - else - { - if (bufptr) - *bufptr = NULL; - - return (0.0); - } - } - - while (isdigit(*buf & 255)) - if (tempptr < (temp + sizeof(temp) - 1)) - *tempptr++ = *buf++; - else - { - if (bufptr) - *bufptr = NULL; - - return (0.0); - } - } - - // - // Nul-terminate the temporary string and return the value... - // - - if (bufptr) - *bufptr = (char *)buf; - - *tempptr = '\0'; - - return (strtod(temp, NULL)); -} - - -// -// '_ppdStrStatistics()' - Return allocation statistics for string pool. -// - -size_t // O - Number of strings -_ppdStrStatistics(size_t *alloc_bytes, // O - Allocated bytes - size_t *total_bytes) // O - Total string bytes -{ - size_t count, // Number of strings - abytes, // Allocated string bytes - tbytes, // Total string bytes - len; // Length of string - _ppd_sp_item_t *item; // Current item - - - // - // Loop through strings in pool, counting everything up... - // - - _ppdMutexLock(&sp_mutex); - - for (count = 0, abytes = 0, tbytes = 0, - item = (_ppd_sp_item_t *)cupsArrayFirst(stringpool); - item; - item = (_ppd_sp_item_t *)cupsArrayNext(stringpool)) - { - // - // Count allocated memory, using a 64-bit aligned buffer as a basis. - // - - count += item->ref_count; - len = (strlen(item->str) + 8) & (size_t)~7; - abytes += sizeof(_ppd_sp_item_t) + len; - tbytes += item->ref_count * len; - } - - _ppdMutexUnlock(&sp_mutex); - - // - // Return values... - // - - if (alloc_bytes) - *alloc_bytes = abytes; - - if (total_bytes) - *total_bytes = tbytes; - - return (count); -} - - -// -// '_ppd_strcpy()' - Copy a string allowing for overlapping strings. -// - -void -_ppd_strcpy(char *dst, // I - Destination string - const char *src) // I - Source string -{ - while (*src) - *dst++ = *src++; - - *dst = '\0'; -} - - -// -// '_ppd_strdup()' - Duplicate a string. -// - -#ifndef HAVE_STRDUP -char * // O - New string pointer -_ppd_strdup(const char *s) // I - String to duplicate -{ - char *t; // New string pointer - size_t slen; // Length of string - - - if (!s) - return (NULL); - - slen = strlen(s); - if ((t = malloc(slen + 1)) == NULL) - return (NULL); - - return (memcpy(t, s, slen + 1)); -} -#endif // !HAVE_STRDUP - - -// -// '_ppd_strcasecmp()' - Do a case-insensitive comparison. -// - -int // O - Result of comparison (-1, 0, or 1) -_ppd_strcasecmp(const char *s, // I - First string - const char *t) // I - Second string -{ - while (*s != '\0' && *t != '\0') - { - if (_ppd_tolower(*s) < _ppd_tolower(*t)) - return (-1); - else if (_ppd_tolower(*s) > _ppd_tolower(*t)) - return (1); - - s ++; - t ++; - } - - if (*s == '\0' && *t == '\0') - return (0); - else if (*s != '\0') - return (1); - else - return (-1); -} - - -// -// '_ppd_strncasecmp()' - Do a case-insensitive comparison on up to N chars. -// - -int // O - Result of comparison - // (-1, 0, or 1) -_ppd_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 (_ppd_tolower(*s) < _ppd_tolower(*t)) - return (-1); - else if (_ppd_tolower(*s) > _ppd_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); -} - - -#ifndef HAVE_STRLCAT -// -// '_ppd_strlcat()' - Safely concatenate two strings. -// - -size_t // O - Length of string -_ppd_strlcat(char *dst, // O - Destination string - const char *src, // I - Source string - size_t size) // I - Size of destination string buffer -{ - size_t srclen; // Length of source string - size_t dstlen; // Length of destination string - - - // - // Figure out how much room is left... - // - - dstlen = strlen(dst); - - if (size < (dstlen + 1)) - return (dstlen); // No room, return immediately... - - size -= dstlen + 1; - - // - // Figure out how much room is needed... - // - - srclen = strlen(src); - - // - // Copy the appropriate amount... - // - - if (srclen > size) - srclen = size; - - memmove(dst + dstlen, src, srclen); - dst[dstlen + srclen] = '\0'; - - return (dstlen + srclen); -} -#endif // !HAVE_STRLCAT - - -#ifndef HAVE_STRLCPY -// -// '_ppd_strlcpy()' - Safely copy two strings. -// - -size_t // O - Length of string -_ppd_strlcpy(char *dst, // O - Destination string - const char *src, // I - Source string - size_t size) // I - Size of destination string buffer -{ - size_t srclen; // Length of source string - - - // - // Figure out how much room is needed... - // - - size --; - - srclen = strlen(src); - - // - // Copy the appropriate amount... - // - - if (srclen > size) - srclen = size; - - memmove(dst, src, srclen); - dst[srclen] = '\0'; - - return (srclen); -} -#endif // !HAVE_STRLCPY - - -// -// 'ppd_compare_sp_items()' - Compare two string pool items... -// - -static int // O - Result of comparison -ppd_compare_sp_items(_ppd_sp_item_t *a, // I - First item - _ppd_sp_item_t *b) // I - Second item -{ - return (strcmp(a->str, b->str)); -} diff --git a/ppd/test.ppd b/ppd/test.ppd deleted file mode 100644 index 00bbe9b53..000000000 --- a/ppd/test.ppd +++ /dev/null @@ -1,263 +0,0 @@ -*PPD-Adobe: "4.3" -*% -*% Test PPD file for CUPS. -*% -*% This file is used to test the CUPS PPD API functions and cannot be -*% used with any known printers. Look on the CUPS web site for working PPD -*% files. -*% -*% If you are a PPD file developer, consider using the PPD compiler (ppdc) -*% to create your PPD files - not only will it save you time, it produces -*% consistently high-quality files. -*% -*% Copyright (c) 2007-2018 by Apple Inc. -*% Copyright (c) 2002-2006 by Easy Software Products. -*% -*% Licensed under Apache License v2.0. See the file "LICENSE" for more -*% information. -*FormatVersion: "4.3" -*FileVersion: "2.3" -*LanguageVersion: English -*LanguageEncoding: ISOLatin1 -*PCFileName: "TEST.PPD" -*Manufacturer: "Apple" -*Product: "(Test)" -*cupsVersion: 2.3 -*ModelName: "Test" -*ShortNickName: "Test" -*NickName: "Test for CUPS" -*PSVersion: "(3010.000) 0" -*LanguageLevel: "3" -*ColorDevice: True -*DefaultColorSpace: RGB -*FileSystem: False -*Throughput: "1" -*LandscapeOrientation: Plus90 -*TTRasterizer: Type42 -*cupsFilter: "application/vnd.cups-raster 0 -" -*RequiresPageRegion All: True - -*% These constraints are used to test ppdConflicts() and cupsResolveConflicts() -*UIConstraints: *PageSize Letter *InputSlot Envelope -*UIConstraints: *InputSlot Envelope *PageSize Letter -*UIConstraints: *PageRegion Letter *InputSlot Envelope -*UIConstraints: *InputSlot Envelope *PageRegion Letter - -*% These constraints are used to test ppdInstallableConflict() -*UIConstraints: "*Duplex *InstalledDuplexer False" -*UIConstraints: "*InstalledDuplexer False *Duplex" - -*% These attributes test ppdFindAttr/ppdFindNext... -*cupsTest Foo/I Love Foo: "" -*cupsTest Bar/I Love Bar: "" - -*% For PageSize, we have put all of the translations in-line... -*OpenUI *PageSize/Page Size: PickOne -*fr.Translation PageSize/French Page Size: "" -*fr_CA.Translation PageSize/French Canadian Page Size: "" -*OrderDependency: 10 AnySetup *PageSize -*DefaultPageSize: Letter -*PageSize Letter/US Letter: "PageSize=Letter" -*fr.PageSize Letter/French US Letter: "" -*fr_CA.PageSize Letter/French Canadian US Letter: "" -*PageSize Letter.Banner/US Letter Banner: "PageSize=Letter.Banner" -*fr.PageSize Letter.Banner/French US Letter Banner: "" -*fr_CA.PageSize Letter.Banner/French Canadian US Letter Banner: "" -*PageSize Letter.Fullbleed/US Letter Borderless: "PageSize=Letter.Fullbleed" -*fr.PageSize Letter.Fullbleed/French US Letter Borderless: "" -*fr_CA.PageSize Letter.Fullbleed/French Canadian US Letter Borderless: "" -*PageSize A4/A4: "PageSize=A4" -*fr.PageSize A4/French A4: "" -*fr_CA.PageSize A4/French Canadian A4: "" -*PageSize Env10/#10 Envelope: "PageSize=Env10" -*fr.PageSize Env10/French #10 Envelope: "" -*fr_CA.PageSize Env10/French Canadian #10 Envelope: "" -*CloseUI: *PageSize - -*% For PageRegion, we have separated the translations... -*OpenUI *PageRegion/Page Region: PickOne -*OrderDependency: 10 AnySetup *PageRegion -*DefaultPageRegion: Letter -*PageRegion Letter/US Letter: "PageRegion=Letter" -*PageRegion Letter.Banner/US Letter Banner: "PageRegion=Letter.Fullbleed" -*PageRegion Letter.Fullbleed/US Letter Borderless: "PageRegion=Letter.Fullbleed" -*PageRegion A4/A4: "PageRegion=A4" -*PageRegion Env10/#10 Envelope: "PageRegion=Env10" -*CloseUI: *PageRegion - -*fr.Translation PageRegion/French Page Region: "" -*fr.PageRegion Letter/French US Letter: "" -*fr.PageRegion Letter.Banner/French US Letter Banner: "" -*fr.PageRegion Letter.Fullbleed/French US Letter Borderless: "" -*fr.PageRegion A4/French A4: "" -*fr.PageRegion Env10/French #10 Envelope: "" - -*fr_CA.Translation PageRegion/French Canadian Page Region: "" -*fr_CA.PageRegion Letter/French Canadian US Letter: "" -*fr_CA.PageRegion Letter.Banner/French Canadian US Letter Banner: "" -*fr_CA.PageRegion Letter.Fullbleed/French Canadian US Letter Borderless: "" -*fr_CA.PageRegion A4/French Canadian A4: "" -*fr_CA.PageRegion Env10/French Canadian #10 Envelope: "" - -*DefaultImageableArea: Letter -*ImageableArea Letter: "18 36 594 756" -*ImageableArea Letter.Banner: "18 0 594 792" -*ImageableArea Letter.Fullbleed: "0 0 612 792" -*ImageableArea A4: "18 36 577 806" -*ImageableArea Env10: "18 36 279 648" - -*DefaultPaperDimension: Letter -*PaperDimension Letter: "612 792" -*PaperDimension Letter.Banner: "612 792" -*PaperDimension Letter.Fullbleed: "612 792" -*PaperDimension A4: "595 842" -*PaperDimension Env10: "297 684" - -*% Custom page size support -*HWMargins: 0 0 0 0 -*NonUIOrderDependency: 100 AnySetup *CustomPageSize True -*CustomPageSize True/Custom Page Size: "PageSize=Custom" -*ParamCustomPageSize Width: 1 points 36 1080 -*ParamCustomPageSize Height: 2 points 36 86400 -*ParamCustomPageSize WidthOffset/Width Offset: 3 points 0 0 -*ParamCustomPageSize HeightOffset/Height Offset: 4 points 0 0 -*ParamCustomPageSize Orientation: 5 int 0 0 - -*OpenUI *InputSlot/Input Slot: PickOne -*OrderDependency: 20 AnySetup *InputSlot -*DefaultInputSlot: Tray -*InputSlot Tray/Tray: "InputSlot=Tray" -*InputSlot Manual/Manual Feed: "InputSlot=Manual" -*InputSlot Envelope/Envelope Feed: "InputSlot=Envelope" -*CloseUI: *InputSlot - -*OpenUI *MediaType/Media Type: PickOne -*OrderDependency: 25 AnySetup *MediaType -*DefaultMediaType: Plain -*MediaType Plain/Plain Paper: "MediaType=Plain" -*MediaType Matte/Matte Photo: "MediaType=Matte" -*MediaType Glossy/Glossy Photo: "MediaType=Glossy" -*MediaType Transparency/Transparency Film: "MediaType=Transparency" -*CloseUI: *MediaType - -*OpenUI *OutputBin/Output Tray: PickOne -*OrderDependency: 25 AnySetup *OutputBin -*DefaultOutputBin: Tray1 -*OutputBin Auto/Automatic Tray: "OutputBin=Auto" -*OutputBin Tray1/Tray 1: "OutputBin=Tray1" -*OutputBin Tray2/Tray 2: "OutputBin=Tray2" -*OutputBin MainTray/Main Tray: "OutputBin=MainTray" -*CloseUI: *OutputBin - -*OpenUI *Duplex/2-Sided Printing: PickOne -*OrderDependency: 10 DocumentSetup *Duplex -*DefaultDuplex: None -*Duplex None/Off: "Duplex=None" -*Duplex DuplexNoTumble/Long Edge: "Duplex=DuplexNoTumble" -*Duplex DuplexTumble/Short Edge: "Duplex=DuplexTumble" -*CloseUI: *Duplex - -*% Installable option... -*OpenGroup: InstallableOptions/Installable Options -*OpenUI InstalledDuplexer/Duplexer Installed: Boolean -*DefaultInstalledDuplexer: False -*InstalledDuplexer False: "" -*InstalledDuplexer True: "" -*CloseUI: *InstalledDuplexer -*CloseGroup: InstallableOptions - -*% Custom options... -*OpenGroup: Extended/Extended Options - -*OpenUI IntOption/Integer: PickOne -*OrderDependency: 30 AnySetup *IntOption -*DefaultIntOption: None -*IntOption None: "" -*IntOption 1: "IntOption=1" -*IntOption 2: "IntOption=2" -*IntOption 3: "IntOption=3" -*CloseUI: *IntOption - -*CustomIntOption True/Custom Integer: "IntOption=Custom" -*ParamCustomIntOption Integer: 1 int -100 100 - -*OpenUI StringOption/String: PickOne -*OrderDependency: 40 AnySetup *StringOption -*DefaultStringOption: None -*StringOption None: "" -*StringOption foo: "StringOption=foo" -*StringOption bar: "StringOption=bar" -*CloseUI: *StringOption - -*CustomStringOption True/Custom String: "StringOption=Custom" -*ParamCustomStringOption String1: 2 string 1 10 -*ParamCustomStringOption String2: 1 string 1 10 - -*CloseGroup: Extended - -*% IPP reasons for ppdLocalizeIPPReason tests -*cupsIPPReason foo/Foo Reason: "http://foo/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/foo/bar.html" -*End -*fr.cupsIPPReason foo/La Foo Reason: "text:La%20Long%20 -text:Foo%20Reason -http://foo/fr/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/fr/foo/bar.html" -*End -*zh_TW.cupsIPPReason foo/Number 1 Foo Reason: "text:Number%201%20 -text:Foo%20Reason -http://foo/zh_TW/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/zh_TW/foo/bar.html" -*End -*zh.cupsIPPReason foo/Number 2 Foo Reason: "text:Number%202%20 -text:Foo%20Reason -http://foo/zh/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/zh/foo/bar.html" -*End - -*% Marker names for ppdLocalizeMarkerName tests -*cupsMarkerName cyan/Cyan Toner: "" -*fr.cupsMarkerName cyan/La Toner Cyan: "" -*zh_TW.cupsMarkerName cyan/Number 1 Cyan Toner: "" -*zh.cupsMarkerName cyan/Number 2 Cyan Toner: "" - -*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 diff --git a/ppd/test2.ppd b/ppd/test2.ppd deleted file mode 100644 index 4f8d4ea6d..000000000 --- a/ppd/test2.ppd +++ /dev/null @@ -1,244 +0,0 @@ -*PPD-Adobe: "4.3" -*% -*% Test PPD file #2 for CUPS. -*% -*% This file is used to test the CUPS PPD API functions and cannot be -*% used with any known printers. Look on the CUPS web site for working PPD -*% files. -*% -*% If you are a PPD file developer, consider using the PPD compiler (ppdc) -*% to create your PPD files - not only will it save you time, it produces -*% consistently high-quality files. -*% -*% Copyright (c) 2007-2018 by Apple Inc. -*% Copyright (c) 2002-2006 by Easy Software Products. -*% -*% Licensed under Apache License v2.0. See the file "LICENSE" for more -*% information. -*FormatVersion: "4.3" -*FileVersion: "2.3" -*LanguageVersion: English -*LanguageEncoding: ISOLatin1 -*PCFileName: "TEST.PPD" -*Manufacturer: "Apple" -*Product: "(Test2)" -*cupsVersion: 2.3 -*ModelName: "Test2" -*ShortNickName: "Test2" -*NickName: "Test2 for CUPS" -*PSVersion: "(3010.000) 0" -*LanguageLevel: "3" -*ColorDevice: True -*DefaultColorSpace: RGB -*FileSystem: False -*Throughput: "1" -*LandscapeOrientation: Plus90 -*TTRasterizer: Type42 - -*% These constraints are used to test ppdConflicts() and cupsResolveConflicts() -*cupsUIConstraints envelope: "*PageSize Letter *InputSlot Envelope" -*cupsUIConstraints envelope: "*PageSize A4 *InputSlot Envelope" -*cupsUIResolver envelope: "*InputSlot Manual *PageSize Env10" - -*cupsUIConstraints envphoto: "*PageSize Env10 *InputSlot Envelope *Quality Photo" -*cupsUIResolver envphoto: "*Quality Normal" - -*% This constraint is used to test ppdInstallableConflict() -*cupsUIConstraints: "*Duplex *InstalledDuplexer False" - -*% These constraints are used to test the loop detection code in cupsResolveConflicts() -*cupsUIConstraints loop1: "*PageSize A4 *Quality Photo" -*cupsUIResolver loop1: "*Quality Normal" -*cupsUIConstraints loop2: "*PageSize A4 *Quality Normal" -*cupsUIResolver loop2: "*Quality Photo" - -*% For PageSize, we have put all of the translations in-line... -*OpenUI *PageSize/Page Size: PickOne -*fr.Translation PageSize/French Page Size: "" -*fr_CA.Translation PageSize/French Canadian Page Size: "" -*OrderDependency: 10 AnySetup *PageSize -*DefaultPageSize: Letter -*PageSize Letter/US Letter: "PageSize=Letter" -*fr.PageSize Letter/French US Letter: "" -*fr_CA.PageSize Letter/French Canadian US Letter: "" -*PageSize A4/A4: "PageSize=A4" -*fr.PageSize A4/French A4: "" -*fr_CA.PageSize A4/French Canadian A4: "" -*PageSize Env10/#10 Envelope: "PageSize=Env10" -*fr.PageSize Env10/French #10 Envelope: "" -*fr_CA.PageSize Env10/French Canadian #10 Envelope: "" -*CloseUI: *PageSize - -*% For PageRegion, we have separated the translations... -*OpenUI *PageRegion/Page Region: PickOne -*OrderDependency: 10 AnySetup *PageRegion -*DefaultPageRegion: Letter -*PageRegion Letter/US Letter: "PageRegion=Letter" -*PageRegion A4/A4: "PageRegion=A4" -*PageRegion Env10/#10 Envelope: "PageRegion=Env10" -*CloseUI: *PageRegion - -*fr.Translation PageRegion/French Page Region: "" -*fr.PageRegion Letter/French US Letter: "" -*fr.PageRegion A4/French A4: "" -*fr.PageRegion Env10/French #10 Envelope: "" - -*fr_CA.Translation PageRegion/French Canadian Page Region: "" -*fr_CA.PageRegion Letter/French Canadian US Letter: "" -*fr_CA.PageRegion A4/French Canadian A4: "" -*fr_CA.PageRegion Env10/French Canadian #10 Envelope: "" - -*DefaultImageableArea: Letter -*ImageableArea Letter: "18 36 594 756" -*ImageableArea A4: "18 36 577 806" -*ImageableArea Env10: "18 36 279 648" - -*DefaultPaperDimension: Letter -*PaperDimension Letter: "612 792" -*PaperDimension A4: "595 842" -*PaperDimension Env10: "297 684" - -*% Custom page size support -*HWMargins: 0 0 0 0 -*NonUIOrderDependency: 100 AnySetup *CustomPageSize True -*CustomPageSize True/Custom Page Size: "PageSize=Custom" -*ParamCustomPageSize Width: 1 points 36 1080 -*ParamCustomPageSize Height: 2 points 36 86400 -*ParamCustomPageSize WidthOffset/Width Offset: 3 points 0 0 -*ParamCustomPageSize HeightOffset/Height Offset: 4 points 0 0 -*ParamCustomPageSize Orientation: 5 int 0 0 - -*cupsMediaQualifier2: InputSlot -*cupsMediaQualifier3: Quality -*cupsMaxSize .Manual.: "1000 1000" -*cupsMinSize .Manual.: "100 100" -*cupsMinSize .Manual.Photo: "200 200" -*cupsMinSize ..Photo: "300 300" - -*OpenUI *InputSlot/Input Slot: PickOne -*OrderDependency: 20 AnySetup *InputSlot -*DefaultInputSlot: Tray -*InputSlot Tray/Tray: "InputSlot=Tray" -*InputSlot Manual/Manual Feed: "InputSlot=Manual" -*InputSlot Envelope/Envelope Feed: "InputSlot=Envelope" -*CloseUI: *InputSlot - -*OpenUI *Quality/Output Mode: PickOne -*OrderDependency: 20 AnySetup *Quality -*DefaultQuality: Normal -*Quality Draft: "Quality=Draft" -*Quality Normal: "Quality=Normal" -*Quality Photo: "Quality=Photo" -*CloseUI: *Quality - -*OpenUI *Duplex/2-Sided Printing: PickOne -*OrderDependency: 10 DocumentSetup *Duplex -*DefaultDuplex: None -*Duplex None/Off: "Duplex=None" -*Duplex DuplexNoTumble/Long Edge: "Duplex=DuplexNoTumble" -*Duplex DuplexTumble/Short Edge: "Duplex=DuplexTumble" -*CloseUI: *Duplex - -*% Installable option... -*OpenGroup: InstallableOptions/Installable Options -*OpenUI InstalledDuplexer/Duplexer Installed: Boolean -*DefaultInstalledDuplexer: False -*InstalledDuplexer False: "" -*InstalledDuplexer True: "" -*CloseUI: *InstalledDuplexer -*CloseGroup: InstallableOptions - -*% Custom options... -*OpenGroup: Extended/Extended Options - -*OpenUI IntOption/Integer: PickOne -*OrderDependency: 30 AnySetup *IntOption -*DefaultIntOption: None -*IntOption None: "" -*IntOption 1: "IntOption=1" -*IntOption 2: "IntOption=2" -*IntOption 3: "IntOption=3" -*CloseUI: *IntOption - -*CustomIntOption True/Custom Integer: "IntOption=Custom" -*ParamCustomIntOption Integer: 1 int -100 100 - -*OpenUI StringOption/String: PickOne -*OrderDependency: 40 AnySetup *StringOption -*DefaultStringOption: None -*StringOption None: "" -*StringOption foo: "StringOption=foo" -*StringOption bar: "StringOption=bar" -*CloseUI: *StringOption - -*CustomStringOption True/Custom String: "StringOption=Custom" -*ParamCustomStringOption String: 1 string 1 10 - -*CloseGroup: Extended - -*% IPP reasons for ppdLocalizeIPPReason tests -*cupsIPPReason foo/Foo Reason: "http://foo/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/foo/bar.html" -*End -*fr.cupsIPPReason foo/La Foo Reason: "text:La%20Long%20 -text:Foo%20Reason -http://foo/fr/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/fr/foo/bar.html" -*End -*zh_TW.cupsIPPReason foo/Number 1 Foo Reason: "text:Number%201%20 -text:Foo%20Reason -http://foo/zh_TW/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/zh_TW/foo/bar.html" -*End -*zh.cupsIPPReason foo/Number 2 Foo Reason: "text:Number%202%20 -text:Foo%20Reason -http://foo/zh/bar.html -help:anchor='foo'%20bookID=Vendor%20Help -/help/zh/foo/bar.html" -*End - -*% Marker names for ppdLocalizeMarkerName tests -*cupsMarkerName cyan/Cyan Toner: "" -*fr.cupsMarkerName cyan/La Toner Cyan: "" -*zh_TW.cupsMarkerName cyan/Number 1 Cyan Toner: "" -*zh.cupsMarkerName cyan/Number 2 Cyan Toner: "" - -*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 diff --git a/ppd/testdriver.c b/ppd/testdriver.c deleted file mode 100644 index 7a1980442..000000000 --- a/ppd/testdriver.c +++ /dev/null @@ -1,170 +0,0 @@ -// -// Sample/test driver interface program for libppd. -// -// This program handles listing and installing both static PPD files -// in CUPS_DATADIR/model and dynamically generated PPD files using -// the driver helper programs in CUPS_SERVERBIN/driver. -// -// Copyright 2007-2010 by Apple Inc. -// Copyright 1997-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// -// Contents: -// -// main() - Enumerate or display PPD files. -// cat_ppd() - Display a PPD file. -// list_ppds() - List PPDs. -// -// Compile with:gcc -o testdriver testdriver.c -I.. -lppd -lcups -// - -// -// Include necessary headers... -// - -#include -#include -#include - - -// -// Local functions... -// - -static int cat_ppd(const char *uri); -static int list_ppds(const char *name); - - -// -// Sample data... -// - -static const char *models[][2] = - { - { "foojet.ppd", "Foo Printer" }, - { "barjet.ppd", "Bar Printer" }, - { "foobar.ppd", "Foo/Bar Multifunction Printer" } - }; - - -// -// 'main()' - Enumerate or display PPD files. -// - -int // O - Exit status -main(int argc, // I - Number of command-line args - char *argv[]) // I - Command-line arguments -{ - if (argc == 2 && !strcmp(argv[1], "list")) - return (list_ppds(argv[0])); - else if (argc == 3 && !strcmp(argv[1], "cat")) - return (cat_ppd(argv[2])); - - fprintf(stderr, "ERROR: Usage: %s cat URI\n", argv[0]); - fprintf(stderr, "ERROR: Usage: %s list\n", argv[0]); - return (1); -} - - -// -// 'cat_ppd()' - Display a PPD file. -// - -static int // O - Exit status -cat_ppd(const char *uri) // I - PPD URI -{ - int i; // Looping var - char scheme[255], // URI scheme - userpass[255], // Username/password (unused) - hostname[255], // Hostname (unused) - resource[1024]; // Resource name - int port; // Port (unused) - const char *name; // Pointer to name in URI - - - if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), - userpass, sizeof(userpass), hostname, sizeof(hostname), - &port, resource, sizeof(resource)) < HTTP_URI_OK) - { - fprintf(stderr, "ERROR: Bad URI \"%s\"!\n", uri); - return (1); - } - - name = resource + 1; - - for (i = 0 ; i < (int)(sizeof(models) / sizeof(models[0])); i ++) - if (!strcmp(name, models[i][0])) - { - // - // Actually display the PPD file... - // - - puts("*PPD-Adobe: \"4.3\""); - - puts("*LanguageEncoding: ISOLatin1"); - puts("*LanguageVersion: English"); - puts("*Manufacturer: \"Test\""); - puts("*FileVersion: \"1.0\""); - puts("*FormatVersion: \"4.3\""); - puts("*PSVersion: \"(3010) 1\""); - printf("*PCFileName: \"%s\"\n", models[i][0]); - - printf("*Product: \"(%s)\"\n", models[i][1]); - printf("*ModelName: \"Test %s\"\n", models[i][1]); - printf("*NickName: \"Test %s\"\n", models[i][1]); - printf("*ShortNickName: \"Test %s\"\n", models[i][1]); - - puts("*OpenUI PageSize: PickOne"); - puts("*OrderDependency: 10 AnySetup *PageSetup"); - puts("*DefaultPageSize: Letter"); - puts("*PageSize Letter: \"<>setpagedevice\""); - puts("*PageSize A4: \"<>setpagedevice\""); - puts("*CloseUI: *PageSize"); - - puts("*OpenUI PageRegion: PickOne"); - puts("*OrderDependency: 10 AnySetup *PageRegion"); - puts("*DefaultPageRegion: Letter"); - puts("*PageRegion Letter: \"<>setpagedevice\""); - puts("*PageRegion A4: \"<>setpagedevice\""); - puts("*CloseUI: *PageRegion"); - - puts("*DefaultImageableArea: Letter"); - puts("*ImageableArea Letter: \"0 0 612 792\""); - puts("*ImageableArea A4: \"0 0 595 842\""); - - puts("*DefaultPaperDimension: Letter"); - puts("*PaperDimension Letter: \"612 792\""); - puts("*PaperDimension A4: \"595 842\""); - - return (0); - } - - fprintf(stderr, "ERROR: Unknown URI \"%s\"!\n", uri); - return (1); -} - - -// -// 'list_ppds()' - List PPDs. -// - -static int // O - Exit status -list_ppds(const char *name) // I - Program name -{ - int i; // Looping var - const char *base; // Base name of program - - - if ((base = strrchr(name, '/')) != NULL) - base ++; - else - base = name; - - for (i = 0; i < (int)(sizeof(models) / sizeof(models[0])); i ++) - printf("\"%s:///%s\" en \"Test\" \"Test %s\" \"1284 device id\"\n", - base, models[i][0], models[i][1]); - - return (0); -} diff --git a/ppd/testppd.c b/ppd/testppd.c deleted file mode 100644 index 07625b8fb..000000000 --- a/ppd/testppd.c +++ /dev/null @@ -1,1967 +0,0 @@ -// -// PPD test program for libppd. -// -// Copyright © 2007-2018 by Apple Inc. -// Copyright © 1997-2006 by Easy Software Products. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "ppd.h" -#include "array-private.h" -#include "raster-private.h" -#include -#ifdef _WIN32 -# include -#else -# include -# include -#endif // _WIN32 -#include - - -// -// Local functions... -// - -static int do_ppd_tests(const char *filename, int num_options, - cups_option_t *options); -static int do_ps_tests(void); -static void print_changes(cups_page_header2_t *header, cups_page_header2_t *expected); - - -// -// Test data... -// - -static const char *dsc_code = -"[{\n" -"%%BeginFeature: *PageSize Tabloid\n" -"<>setpagedevice\n" -"%%EndFeature\n" -"} stopped cleartomark\n"; -static const char *setpagedevice_code = -"<<" -"/MediaClass(Media Class)" -"/MediaColor((Media Color))" -"/MediaType(Media\\\\Type)" -"/OutputType<416263>" -"/AdvanceDistance 1000" -"/AdvanceMedia 1" -"/Collate false" -"/CutMedia 2" -"/Duplex true" -"/HWResolution[100 200]" -"/InsertSheet true" -"/Jog 3" -"/LeadingEdge 1" -"/ManualFeed true" -"/MediaPosition 8#777" -"/MediaWeight 16#fe01" -"/MirrorPrint true" -"/NegativePrint true" -"/NumCopies 1" -"/Orientation 1" -"/OutputFaceUp true" -"/PageSize[612 792.1]" -"/Separations true" -"/TraySwitch true" -"/Tumble true" -"/cupsMediaType 2" -"/cupsColorOrder 1" -"/cupsColorSpace 1" -"/cupsCompression 1" -"/cupsRowCount 1" -"/cupsRowFeed 1" -"/cupsRowStep 1" -"/cupsBorderlessScalingFactor 1.001" -"/cupsInteger0 1" -"/cupsInteger1 2" -"/cupsInteger2 3" -"/cupsInteger3 4" -"/cupsInteger4 5" -"/cupsInteger5 6" -"/cupsInteger6 7" -"/cupsInteger7 8" -"/cupsInteger8 9" -"/cupsInteger9 10" -"/cupsInteger10 11" -"/cupsInteger11 12" -"/cupsInteger12 13" -"/cupsInteger13 14" -"/cupsInteger14 15" -"/cupsInteger15 16" -"/cupsReal0 1.1" -"/cupsReal1 2.1" -"/cupsReal2 3.1" -"/cupsReal3 4.1" -"/cupsReal4 5.1" -"/cupsReal5 6.1" -"/cupsReal6 7.1" -"/cupsReal7 8.1" -"/cupsReal8 9.1" -"/cupsReal9 10.1" -"/cupsReal10 11.1" -"/cupsReal11 12.1" -"/cupsReal12 13.1" -"/cupsReal13 14.1" -"/cupsReal14 15.1" -"/cupsReal15 16.1" -"/cupsString0(1)" -"/cupsString1(2)" -"/cupsString2(3)" -"/cupsString3(4)" -"/cupsString4(5)" -"/cupsString5(6)" -"/cupsString6(7)" -"/cupsString7(8)" -"/cupsString8(9)" -"/cupsString9(10)" -"/cupsString10(11)" -"/cupsString11(12)" -"/cupsString12(13)" -"/cupsString13(14)" -"/cupsString14(15)" -"/cupsString15(16)" -"/cupsMarkerType(Marker Type)" -"/cupsRenderingIntent(Rendering Intent)" -"/cupsPageSizeName(Letter)" -"/cupsPreferredBitsPerColor 17" -">> setpagedevice"; - -static cups_page_header2_t setpagedevice_header = -{ - "Media Class", // MediaClass - "(Media Color)", // MediaColor - "Media\\Type", // MediaType - "Abc", // OutputType - 1000, // AdvanceDistance - CUPS_ADVANCE_FILE, // AdvanceMedia - CUPS_FALSE, // Collate - CUPS_CUT_JOB, // CutMedia - CUPS_TRUE, // Duplex - { 100, 200 }, // HWResolution - { 0, 0, 0, 0 }, // ImagingBoundingBox - CUPS_TRUE, // InsertSheet - CUPS_JOG_SET, // Jog - CUPS_EDGE_RIGHT, // LeadingEdge - { 0, 0 }, // Margins - CUPS_TRUE, // ManualFeed - 0777, // MediaPosition - 0xfe01, // MediaWeight - CUPS_TRUE, // MirrorPrint - CUPS_TRUE, // NegativePrint - 1, // NumCopies - CUPS_ORIENT_90, // Orientation - CUPS_TRUE, // OutputFaceUp - { 612, 792 }, // PageSize - CUPS_TRUE, // Separations - CUPS_TRUE, // TraySwitch - CUPS_TRUE, // Tumble - 0, // cupsWidth - 0, // cupsHeight - 2, // cupsMediaType - 0, // cupsBitsPerColor - 0, // cupsBitsPerPixel - 0, // cupsBytesPerLine - CUPS_ORDER_BANDED, // cupsColorOrder - CUPS_CSPACE_RGB, // cupsColorSpace - 1, // cupsCompression - 1, // cupsRowCount - 1, // cupsRowFeed - 1, // cupsRowStep - 0, // cupsNumColors - 1.001f, // cupsBorderlessScalingFactor - { 612.0f, 792.1f }, // cupsPageSize - { 0.0f, 0.0f, 0.0f, 0.0f }, // cupsImagingBBox - { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, - // cupsInteger[16] - { 1.1f, 2.1f, 3.1f, 4.1f, 5.1f, 6.1f, 7.1f, 8.1f, 9.1f, 10.1f, 11.1f, 12.1f, 13.1f, 14.1f, 15.1f, 16.1f }, // cupsReal[16] - { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", - "14", "15", "16" }, // cupsString[16] - "Marker Type", // cupsMarkerType - "Rendering Intent", // cupsRenderingIntent - "Letter" // cupsPageSizeName -}; - -static const char *default_code = - "[{\n" - "%%BeginFeature: *InstalledDuplexer False\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *PageRegion Letter\n" - "PageRegion=Letter\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *InputSlot Tray\n" - "InputSlot=Tray\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *OutputBin Tray1\n" - "OutputBin=Tray1\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *MediaType Plain\n" - "MediaType=Plain\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *IntOption None\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *StringOption None\n" - "%%EndFeature\n" - "} stopped cleartomark\n"; - -static const char *custom_code = - "[{\n" - "%%BeginFeature: *InstalledDuplexer False\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *InputSlot Tray\n" - "InputSlot=Tray\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *MediaType Plain\n" - "MediaType=Plain\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *OutputBin Tray1\n" - "OutputBin=Tray1\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *IntOption None\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *CustomStringOption True\n" - "(value\\0502\\051)\n" - "(value 1)\n" - "StringOption=Custom\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *CustomPageSize True\n" - "400\n" - "500\n" - "0\n" - "0\n" - "0\n" - "PageSize=Custom\n" - "%%EndFeature\n" - "} stopped cleartomark\n"; - -static const char *default2_code = - "[{\n" - "%%BeginFeature: *InstalledDuplexer False\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *InputSlot Tray\n" - "InputSlot=Tray\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *Quality Normal\n" - "Quality=Normal\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *IntOption None\n" - "%%EndFeature\n" - "} stopped cleartomark\n" - "[{\n" - "%%BeginFeature: *StringOption None\n" - "%%EndFeature\n" - "} stopped cleartomark\n"; - - -// -// '_ppdRasterColorSpaceString()' - Return the colorspace name for a -// cupsColorSpace value. -// - -const char * -_ppdRasterColorSpaceString( - cups_cspace_t cspace) // I - cupsColorSpace value -{ - static const char * const cups_color_spaces[] = - { // Color spaces - "W", - "RGB", - "RGBA", - "K", - "CMY", - "YMC", - "CMYK", - "YMCK", - "KCMY", - "KCMYcm", - "GMCK", - "GMCS", - "WHITE", - "GOLD", - "SILVER", - "CIEXYZ", - "CIELab", - "RGBW", - "SW", - "SRGB", - "ADOBERGB", - "21", - "22", - "23", - "24", - "25", - "26", - "27", - "28", - "29", - "30", - "31", - "ICC1", - "ICC2", - "ICC3", - "ICC4", - "ICC5", - "ICC6", - "ICC7", - "ICC8", - "ICC9", - "ICCA", - "ICCB", - "ICCC", - "ICCD", - "ICCE", - "ICCF", - "47", - "DEVICE1", - "DEVICE2", - "DEVICE3", - "DEVICE4", - "DEVICE5", - "DEVICE6", - "DEVICE7", - "DEVICE8", - "DEVICE9", - "DEVICEA", - "DEVICEB", - "DEVICEC", - "DEVICED", - "DEVICEE", - "DEVICEF" - }; - - if (cspace < CUPS_CSPACE_W || cspace > CUPS_CSPACE_DEVICEF) - return ("Unknown"); - else - return (cups_color_spaces[cspace]); -} - - -// -// '_log()' - Simple log function -// - -void -_log(void *data, - cf_loglevel_t level, - const char *message, - ...) -{ - va_list arglist; - - (void)data; // No extra data needed - - switch(level) - { - default: - break; - case CF_LOGLEVEL_ERROR: - printf("ERROR: "); - break; - case CF_LOGLEVEL_FATAL: - printf("FATAL: "); - break; - } - va_start(arglist, message); - vprintf(message, arglist); - fflush(stdout); - va_end(arglist); - putchar('\n'); -} - - -// -// '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 - ppd_file_t *ppd = NULL; // PPD file loaded from disk - int status; // Status of tests (0 = success, 1 = fail) - int conflicts; // Number of conflicts - char *s; // String - char buffer[8192]; // String buffer - const char *text, // Localized text - *val; // Option value - int num_options; // Number of options - cups_option_t *options; // Options - ppd_size_t minsize, // Minimum size - maxsize, // Maximum size - *size; // Current size - ppd_attr_t *attr; // Current attribute - ppd_cache_t *pc; // PPD cache - - - status = 0; - - if (argc == 1) - { - // - // Setup directories for locale stuff... - // - - if (access("locale", 0)) - { - mkdir("locale", 0777); - mkdir("locale/fr", 0777); - i = symlink("../../../locale/cups_fr.po", "locale/fr/cups_fr.po"); - mkdir("locale/zh_TW", 0777); - i = symlink("../../../locale/cups_zh_TW.po", "locale/zh_TW/cups_zh_TW.po"); - } - - putenv("LOCALEDIR=locale"); - putenv("SOFTWARE=CUPS"); - - // - // Do tests with test.ppd... - // - - fputs("ppdOpenFile(\"ppd/test.ppd\"): ", stdout); - - if ((ppd = ppdOpenFileWithLocalization("ppd/test.ppd", PPD_LOCALIZATION_ALL)) != NULL) - puts("PASS"); - else - { - ppd_status_t err; // Last error in file - int line; // Line number in file - - - status ++; - err = ppdLastError(&line); - - printf("FAIL (%s on line %d)\n", ppdErrorString(err), line); - } - - fputs("ppdFindAttr(wildcard): ", stdout); - if ((attr = ppdFindAttr(ppd, "cupsTest", NULL)) == NULL) - { - status ++; - puts("FAIL (not found)"); - } - else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo")) - { - status ++; - printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec); - } - else - puts("PASS"); - - fputs("ppdFindNextAttr(wildcard): ", stdout); - if ((attr = ppdFindNextAttr(ppd, "cupsTest", NULL)) == NULL) - { - status ++; - puts("FAIL (not found)"); - } - else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Bar")) - { - status ++; - printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec); - } - else - puts("PASS"); - - fputs("ppdFindAttr(Foo): ", stdout); - if ((attr = ppdFindAttr(ppd, "cupsTest", "Foo")) == NULL) - { - status ++; - puts("FAIL (not found)"); - } - else if (strcmp(attr->name, "cupsTest") || strcmp(attr->spec, "Foo")) - { - status ++; - printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec); - } - else - puts("PASS"); - - fputs("ppdFindNextAttr(Foo): ", stdout); - if ((attr = ppdFindNextAttr(ppd, "cupsTest", "Foo")) != NULL) - { - status ++; - printf("FAIL (got \"%s %s\")\n", attr->name, attr->spec); - } - else - puts("PASS"); - - fputs("ppdMarkDefaults: ", stdout); - ppdMarkDefaults(ppd); - - if ((conflicts = ppdConflicts(ppd)) == 0) - puts("PASS"); - else - { - status ++; - printf("FAIL (%d conflicts)\n", conflicts); - } - - fputs("ppdEmitString (defaults): ", stdout); - if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL && - !strcmp(s, default_code)) - puts("PASS"); - else - { - status ++; - printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0, - (int)strlen(default_code)); - - if (s) - puts(s); - } - - if (s) - free(s); - - fputs("ppdEmitString (custom size and string): ", stdout); - ppdMarkOption(ppd, "PageSize", "Custom.400x500"); - ppdMarkOption(ppd, "StringOption", - "{String1=\"value 1\" String2=value(2)}"); - - if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL && - !strcmp(s, custom_code)) - puts("PASS"); - else - { - status ++; - printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0, - (int)strlen(custom_code)); - - if (s) - puts(s); - } - - if (s) - free(s); - - // - // Test constraints... - // - - fputs("ppdGetConflicts(InputSlot=Envelope): ", stdout); - ppdMarkOption(ppd, "PageSize", "Letter"); - - num_options = ppdGetConflicts(ppd, "InputSlot", "Envelope", &options); - if (num_options != 2 || - (val = cupsGetOption("PageRegion", num_options, options)) == NULL || - _ppd_strcasecmp(val, "Letter") || - (val = cupsGetOption("PageSize", num_options, options)) == NULL || - _ppd_strcasecmp(val, "Letter")) - { - printf("FAIL (%d options:", num_options); - for (i = 0; i < num_options; i ++) - printf(" %s=%s", options[i].name, options[i].value); - puts(")"); - status ++; - } - else - puts("PASS"); - - fputs("ppdConflicts(): ", stdout); - ppdMarkOption(ppd, "InputSlot", "Envelope"); - - if ((conflicts = ppdConflicts(ppd)) == 2) - puts("PASS (2)"); - else - { - printf("FAIL (%d)\n", conflicts); - status ++; - } - - fputs("ppdResolveConflicts(InputSlot=Envelope): ", stdout); - num_options = 0; - options = NULL; - if (!ppdResolveConflicts(ppd, "InputSlot", "Envelope", &num_options, - &options)) - { - puts("FAIL (Unable to resolve)"); - status ++; - } - else if (num_options != 2 || - !cupsGetOption("PageSize", num_options, options)) - { - printf("FAIL (%d options:", num_options); - for (i = 0; i < num_options; i ++) - printf(" %s=%s", options[i].name, options[i].value); - puts(")"); - status ++; - } - else - puts("PASS (Resolved by changing PageSize)"); - - cupsFreeOptions(num_options, options); - - fputs("ppdResolveConflicts(No option/choice): ", stdout); - num_options = 0; - options = NULL; - if (ppdResolveConflicts(ppd, NULL, NULL, &num_options, &options) && - num_options == 1 && !_ppd_strcasecmp(options[0].name, "InputSlot") && - !_ppd_strcasecmp(options[0].value, "Tray")) - puts("PASS (Resolved by changing InputSlot)"); - else if (num_options > 0) - { - printf("FAIL (%d options:", num_options); - for (i = 0; i < num_options; i ++) - printf(" %s=%s", options[i].name, options[i].value); - puts(")"); - status ++; - } - else - { - puts("FAIL (Unable to resolve)"); - status ++; - } - cupsFreeOptions(num_options, options); - - fputs("ppdInstallableConflict(): ", stdout); - if (ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble") && - !ppdInstallableConflict(ppd, "Duplex", "None")) - puts("PASS"); - else if (!ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble")) - { - puts("FAIL (Duplex=DuplexNoTumble did not conflict)"); - status ++; - } - else - { - puts("FAIL (Duplex=None conflicted)"); - status ++; - } - - // - // ppdPageSizeLimits - // - - fputs("ppdPageSizeLimits: ", stdout); - if (ppdPageSizeLimits(ppd, &minsize, &maxsize)) - { - if (fabs(minsize.width - 36.0) > 0.001 || - fabs(minsize.length - 36.0) > 0.001 || - fabs(maxsize.width - 1080.0) > 0.001 || - fabs(maxsize.length - 86400.0) > 0.001) - { - printf("FAIL (got min=%.3fx%.3f, max=%.3fx%.3f, " - "expected min=36x36, max=1080x86400)\n", minsize.width, - minsize.length, maxsize.width, maxsize.length); - status ++; - } - else - puts("PASS"); - } - else - { - puts("FAIL (returned 0)"); - status ++; - } - - // - // ppdMarkOptions with PWG and IPP size names. - // - - fputs("ppdMarkOptions(media=iso-a4): ", stdout); - num_options = cupsAddOption("media", "iso-a4", 0, &options); - ppdMarkOptions(ppd, num_options, options); - cupsFreeOptions(num_options, options); - - size = ppdPageSize(ppd, NULL); - if (!size || strcmp(size->name, "A4")) - { - printf("FAIL (%s)\n", size ? size->name : "unknown"); - status ++; - } - else - puts("PASS"); - - fputs("ppdMarkOptions(media=na_letter_8.5x11in): ", stdout); - num_options = cupsAddOption("media", "na_letter_8.5x11in", 0, &options); - ppdMarkOptions(ppd, num_options, options); - cupsFreeOptions(num_options, options); - - size = ppdPageSize(ppd, NULL); - if (!size || strcmp(size->name, "Letter")) - { - printf("FAIL (%s)\n", size ? size->name : "unknown"); - status ++; - } - else - puts("PASS"); - - fputs("ppdMarkOptions(media=oe_letter-fullbleed_8.5x11in): ", stdout); - num_options = cupsAddOption("media", "oe_letter-fullbleed_8.5x11in", 0, - &options); - ppdMarkOptions(ppd, num_options, options); - cupsFreeOptions(num_options, options); - - size = ppdPageSize(ppd, NULL); - if (!size || strcmp(size->name, "Letter.Fullbleed")) - { - printf("FAIL (%s)\n", size ? size->name : "unknown"); - status ++; - } - else - puts("PASS"); - - fputs("ppdMarkOptions(media=A4): ", stdout); - num_options = cupsAddOption("media", "A4", 0, &options); - ppdMarkOptions(ppd, num_options, options); - cupsFreeOptions(num_options, options); - - size = ppdPageSize(ppd, NULL); - if (!size || strcmp(size->name, "A4")) - { - printf("FAIL (%s)\n", size ? size->name : "unknown"); - status ++; - } - else - puts("PASS"); - - // - // Custom sizes... - // - - fputs("ppdMarkOptions(media=Custom.8x10in): ", stdout); - num_options = cupsAddOption("media", "Custom.8x10in", 0, &options); - ppdMarkOptions(ppd, num_options, options); - cupsFreeOptions(num_options, options); - - size = ppdPageSize(ppd, NULL); - if (!size || strcmp(size->name, "Custom") || - fabs(size->width - 576.0) > 0.001 || - fabs(size->length - 720.0) > 0.001) - { - printf("FAIL (%s - %gx%g)\n", size ? size->name : "unknown", - size ? size->width : 0.0, size ? size->length : 0.0); - status ++; - } - else - puts("PASS"); - - // - // Test localization... - // - - fputs("ppdLocalizeIPPReason(text): ", stdout); - if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) && - !strcmp(buffer, "Foo Reason")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"Foo Reason\")\n", buffer); - } - - fputs("ppdLocalizeIPPReason(http): ", stdout); - if (ppdLocalizeIPPReason(ppd, "foo", "http", buffer, sizeof(buffer)) && - !strcmp(buffer, "http://foo/bar.html")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"http://foo/bar.html\")\n", buffer); - } - - fputs("ppdLocalizeIPPReason(help): ", stdout); - if (ppdLocalizeIPPReason(ppd, "foo", "help", buffer, sizeof(buffer)) && - !strcmp(buffer, "help:anchor='foo'%20bookID=Vendor%20Help")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"help:anchor='foo'%%20bookID=Vendor%%20Help\")\n", buffer); - } - - fputs("ppdLocalizeIPPReason(file): ", stdout); - if (ppdLocalizeIPPReason(ppd, "foo", "file", buffer, sizeof(buffer)) && - !strcmp(buffer, "/help/foo/bar.html")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"/help/foo/bar.html\")\n", buffer); - } - - putenv("LANG=fr"); - putenv("LC_ALL=fr"); - putenv("LC_CTYPE=fr"); - putenv("LC_MESSAGES=fr"); - - fputs("ppdLocalizeIPPReason(fr text): ", stdout); - if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) && - !strcmp(buffer, "La Long Foo Reason")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"La Long Foo Reason\")\n", buffer); - } - - putenv("LANG=zh_TW"); - putenv("LC_ALL=zh_TW"); - putenv("LC_CTYPE=zh_TW"); - putenv("LC_MESSAGES=zh_TW"); - - fputs("ppdLocalizeIPPReason(zh_TW text): ", stdout); - if (ppdLocalizeIPPReason(ppd, "foo", NULL, buffer, sizeof(buffer)) && - !strcmp(buffer, "Number 1 Foo Reason")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"Number 1 Foo Reason\")\n", buffer); - } - - // - // cupsMarkerName localization... - // - - putenv("LANG=en"); - putenv("LC_ALL=en"); - putenv("LC_CTYPE=en"); - putenv("LC_MESSAGES=en"); - - fputs("ppdLocalizeMarkerName(bogus): ", stdout); - - if ((text = ppdLocalizeMarkerName(ppd, "bogus")) != NULL) - { - status ++; - printf("FAIL (\"%s\" instead of NULL)\n", text); - } - else - puts("PASS"); - - fputs("ppdLocalizeMarkerName(cyan): ", stdout); - - if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL && - !strcmp(text, "Cyan Toner")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"Cyan Toner\")\n", - text ? text : "(null)"); - } - - putenv("LANG=fr"); - putenv("LC_ALL=fr"); - putenv("LC_CTYPE=fr"); - putenv("LC_MESSAGES=fr"); - - fputs("ppdLocalizeMarkerName(fr cyan): ", stdout); - if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL && - !strcmp(text, "La Toner Cyan")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"La Toner Cyan\")\n", - text ? text : "(null)"); - } - - putenv("LANG=zh_TW"); - putenv("LC_ALL=zh_TW"); - putenv("LC_CTYPE=zh_TW"); - putenv("LC_MESSAGES=zh_TW"); - - fputs("ppdLocalizeMarkerName(zh_TW cyan): ", stdout); - if ((text = ppdLocalizeMarkerName(ppd, "cyan")) != NULL && - !strcmp(text, "Number 1 Cyan Toner")) - puts("PASS"); - else - { - status ++; - printf("FAIL (\"%s\" instead of \"Number 1 Cyan Toner\")\n", - text ? text : "(null)"); - } - - ppdClose(ppd); - - // - // Test new constraints... - // - - fputs("ppdOpenFile(\"ppd/test2.ppd\"): ", stdout); - - if ((ppd = ppdOpenFile("ppd/test2.ppd")) != NULL) - puts("PASS"); - else - { - ppd_status_t err; // Last error in file - int line; // Line number in file - - - status ++; - err = ppdLastError(&line); - - printf("FAIL (%s on line %d)\n", ppdErrorString(err), line); - } - - fputs("ppdMarkDefaults: ", stdout); - ppdMarkDefaults(ppd); - - if ((conflicts = ppdConflicts(ppd)) == 0) - puts("PASS"); - else - { - status ++; - printf("FAIL (%d conflicts)\n", conflicts); - } - - fputs("ppdEmitString (defaults): ", stdout); - if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL && - !strcmp(s, default2_code)) - puts("PASS"); - else - { - status ++; - printf("FAIL (%d bytes instead of %d)\n", s ? (int)strlen(s) : 0, - (int)strlen(default2_code)); - - if (s) - puts(s); - } - - if (s) - free(s); - - fputs("ppdConflicts(): ", stdout); - ppdMarkOption(ppd, "PageSize", "Env10"); - ppdMarkOption(ppd, "InputSlot", "Envelope"); - ppdMarkOption(ppd, "Quality", "Photo"); - - if ((conflicts = ppdConflicts(ppd)) == 1) - puts("PASS (1)"); - else - { - printf("FAIL (%d)\n", conflicts); - status ++; - } - - fputs("ppdResolveConflicts(Quality=Photo): ", stdout); - num_options = 0; - options = NULL; - if (ppdResolveConflicts(ppd, "Quality", "Photo", &num_options, - &options)) - { - printf("FAIL (%d options:", num_options); - for (i = 0; i < num_options; i ++) - printf(" %s=%s", options[i].name, options[i].value); - puts(")"); - status ++; - } - else - puts("PASS (Unable to resolve)"); - cupsFreeOptions(num_options, options); - - fputs("ppdResolveConflicts(No option/choice): ", stdout); - num_options = 0; - options = NULL; - if (ppdResolveConflicts(ppd, NULL, NULL, &num_options, &options) && - num_options == 1 && !_ppd_strcasecmp(options->name, "Quality") && - !_ppd_strcasecmp(options->value, "Normal")) - puts("PASS"); - else if (num_options > 0) - { - printf("FAIL (%d options:", num_options); - for (i = 0; i < num_options; i ++) - printf(" %s=%s", options[i].name, options[i].value); - puts(")"); - status ++; - } - else - { - puts("FAIL (Unable to resolve!)"); - status ++; - } - cupsFreeOptions(num_options, options); - - fputs("ppdResolveConflicts(loop test): ", stdout); - ppdMarkOption(ppd, "PageSize", "A4"); - ppdMarkOption(ppd, "InputSlot", "Tray"); - ppdMarkOption(ppd, "Quality", "Photo"); - num_options = 0; - options = NULL; - if (!ppdResolveConflicts(ppd, NULL, NULL, &num_options, &options)) - puts("PASS"); - else if (num_options > 0) - { - printf("FAIL (%d options:", num_options); - for (i = 0; i < num_options; i ++) - printf(" %s=%s", options[i].name, options[i].value); - puts(")"); - } - else - puts("FAIL (No conflicts!)"); - - fputs("ppdInstallableConflict(): ", stdout); - if (ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble") && - !ppdInstallableConflict(ppd, "Duplex", "None")) - puts("PASS"); - else if (!ppdInstallableConflict(ppd, "Duplex", "DuplexNoTumble")) - { - puts("FAIL (Duplex=DuplexNoTumble did not conflict)"); - status ++; - } - else - { - puts("FAIL (Duplex=None conflicted)"); - status ++; - } - - // - // ppdPageSizeLimits - // - - ppdMarkDefaults(ppd); - - fputs("ppdPageSizeLimits(default): ", stdout); - if (ppdPageSizeLimits(ppd, &minsize, &maxsize)) - { - if (fabs(minsize.width - 36.0) > 0.001 || - fabs(minsize.length - 36.0) > 0.001 || - fabs(maxsize.width - 1080.0) > 0.001 || - fabs(maxsize.length - 86400.0) > 0.001) - { - printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, " - "expected min=36x36, max=1080x86400)\n", minsize.width, - minsize.length, maxsize.width, maxsize.length); - status ++; - } - else - puts("PASS"); - } - else - { - puts("FAIL (returned 0)"); - status ++; - } - - ppdMarkOption(ppd, "InputSlot", "Manual"); - - fputs("ppdPageSizeLimits(InputSlot=Manual): ", stdout); - if (ppdPageSizeLimits(ppd, &minsize, &maxsize)) - { - if (fabs(minsize.width - 100.0) > 0.001 || - fabs(minsize.length - 100.0) > 0.001 || - fabs(maxsize.width - 1000.0) > 0.001 || - fabs(maxsize.length - 1000.0) > 0.001) - { - printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, " - "expected min=100x100, max=1000x1000)\n", minsize.width, - minsize.length, maxsize.width, maxsize.length); - status ++; - } - else - puts("PASS"); - } - else - { - puts("FAIL (returned 0)"); - status ++; - } - - ppdMarkOption(ppd, "Quality", "Photo"); - - fputs("ppdPageSizeLimits(Quality=Photo): ", stdout); - if (ppdPageSizeLimits(ppd, &minsize, &maxsize)) - { - if (fabs(minsize.width - 200.0) > 0.001 || - fabs(minsize.length - 200.0) > 0.001 || - fabs(maxsize.width - 1000.0) > 0.001 || - fabs(maxsize.length - 1000.0) > 0.001) - { - printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, " - "expected min=200x200, max=1000x1000)\n", minsize.width, - minsize.length, maxsize.width, maxsize.length); - status ++; - } - else - puts("PASS"); - } - else - { - puts("FAIL (returned 0)"); - status ++; - } - - ppdMarkOption(ppd, "InputSlot", "Tray"); - - fputs("ppdPageSizeLimits(Quality=Photo): ", stdout); - if (ppdPageSizeLimits(ppd, &minsize, &maxsize)) - { - if (fabs(minsize.width - 300.0) > 0.001 || - fabs(minsize.length - 300.0) > 0.001 || - fabs(maxsize.width - 1080.0) > 0.001 || - fabs(maxsize.length - 86400.0) > 0.001) - { - printf("FAIL (got min=%.0fx%.0f, max=%.0fx%.0f, " - "expected min=300x300, max=1080x86400)\n", minsize.width, - minsize.length, maxsize.width, maxsize.length); - status ++; - } - else - puts("PASS"); - } - else - { - puts("FAIL (returned 0)"); - status ++; - } - - status += do_ps_tests(); - } - else if (!strcmp(argv[1], "--raster")) - { - for (status = 0, num_options = 0, options = NULL, i = 1; i < argc; i ++) - { - if (argv[i][0] == '-') - { - if (argv[i][1] == 'o') - { - if (argv[i][2]) - num_options = cupsParseOptions(argv[i] + 2, num_options, &options); - else - { - i ++; - if (i < argc) - num_options = cupsParseOptions(argv[i], num_options, &options); - else - { - puts("Usage: testppd --raster [-o name=value ...] [filename.ppd ...]"); - return (1); - } - } - } - else - { - puts("Usage: testppd --raster [-o name=value ...] [filename.ppd ...]"); - return (1); - } - } - else - status += do_ppd_tests(argv[i], num_options, options); - } - - cupsFreeOptions(num_options, options); - } - else if (!strcmp(argv[1], "dump") && argc == 3) - { - ppdCollectionDumpCache(argv[2], _log, NULL); - } - else if (strchr(argv[1], ',') || - (s = strcasestr(argv[1], ".ppd")) == NULL || - s[4] != '\0') - { - // First argument is not FILE.ppd but DIR or DIR,DIR,... -> - // PPD directory list -> See whether we have valid PPD collection - - cups_array_t *dirlist = _ppdArrayNewStrings(argv[1], ','); - cups_array_t *ppd_collections = cupsArrayNew(NULL, NULL); - ppd_collection_t *col; - cups_option_t *options = NULL; - int num_options = 0; - int i; - char *testname = NULL; - - for (s = (char *)cupsArrayFirst(dirlist), i = 1; - s; - s = (char *)cupsArrayNext(dirlist), i ++) - { - col = (ppd_collection_t *)calloc(1, sizeof(ppd_collection_t)); - col->path = s; - printf("Searching directory %d, %s ...\n", i, col->path); - col->name = (char *)calloc(20, sizeof(char)); - snprintf(col->name, 19, "%d", i); - cupsArrayAdd(ppd_collections, col); - } - - for (i = 2; i < argc; i ++) - if (strlen(argv[i]) > 0 && argv[i][0] != '=') - { - if ((s = strchr(argv[i], '=')) == NULL) - s = ""; - else - { - *s = '\0'; - s ++; - } - num_options = cupsAddOption(argv[i], s, num_options, &options); - } - - cups_array_t *ppds = - ppdCollectionListPPDs(ppd_collections, 0, num_options, options, - _log, NULL); - - if (ppds) - { - if (cupsGetOption("only-makes", num_options, options)) - { - printf("Found %d manufacturers.\n\n", cupsArrayCount(ppds)); - for (s = (char *)cupsArrayFirst(ppds); - s; - s = (char *)cupsArrayNext(ppds)) - puts(s); - } - else - { - ppd_info_t *ppd; - printf("Found %d PPD files.\n\n", cupsArrayCount(ppds)); - puts("mtime,size,model_number,type,filename,name,languages0,products0," - "psversions0,make,make_and_model,device_id,scheme"); - for (ppd = (ppd_info_t *)cupsArrayFirst(ppds); - ppd; - ppd = (ppd_info_t *)cupsArrayNext(ppds)) - { - printf("%d,%ld,%d,%d,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"," - "\"%s\",\"%s\"\n", - (int)ppd->record.mtime, (long)ppd->record.size, - ppd->record.model_number, ppd->record.type, - ppd->record.filename, - ppd->record.name, ppd->record.languages[0], - ppd->record.products[0], - ppd->record.psversions[0], ppd->record.make, - ppd->record.make_and_model, ppd->record.device_id, - ppd->record.scheme); - testname = ppd->record.name; - } - } - } - - if (testname) - { - if ((ppd = ppdOpen2(ppdCollectionGetPPD(testname, ppd_collections, - _log, NULL))) == NULL) - { - ppd_status_t err; // Last error in file - int line; // Line number in file - - status ++; - err = ppdLastError(&line); - - printf("%s: %s on line %d\n", argv[1], ppdErrorString(err), line); - } - else - { - printf("PPD %s: %s\n", testname, ppd->nickname); - ppdClose(ppd); - } - } - else - printf("%s: No PPD files found.\n", argv[1]); - - cupsArrayDelete(dirlist); - for (col = (ppd_collection_t *)cupsArrayFirst(ppd_collections); - col; - col = (ppd_collection_t *)cupsArrayNext(ppd_collections)) - { - free(col->name); - free(col); - } - cupsArrayDelete(ppd_collections); - if (ppds) - { - for (s = (char *)cupsArrayFirst(ppds); - s; - s = (char *)cupsArrayNext(ppds)) - free(s); - cupsArrayDelete(ppds); - } - if (num_options) - cupsFreeOptions(num_options, options); - - return (0); - } - else - { - const char *name; // PPD name - char *filename; // Name of actual file containing PPD - struct stat fileinfo; // File information - - name = argv[1]; - filename = strdup(name); - if ((s = strchr(filename, ':')) != NULL) - *s = '\0'; - - if (lstat(filename, &fileinfo)) - { - printf("%s: %s\n", filename, strerror(errno)); - return (1); - } - - if (S_ISLNK(fileinfo.st_mode)) - { - char realfile[1024]; // Real file path - ssize_t realsize; // Size of real file path - - - if ((realsize = readlink(filename, realfile, sizeof(realfile) - 1)) < 0) - strlcpy(realfile, "Unknown", sizeof(realfile)); - else - realfile[realsize] = '\0'; - - if (stat(realfile, &fileinfo)) - printf("%s: symlink to \"%s\", %s\n", filename, realfile, - strerror(errno)); - else - printf("%s: symlink to \"%s\", %ld bytes\n", filename, realfile, - (long)fileinfo.st_size); - } - else - printf("%s: regular file, %ld bytes\n", filename, (long)fileinfo.st_size); - - if (s) - printf("\nPPD file %s from %s:\n\n", s + 1, filename); - else - printf("\nPPD file %s:\n\n", filename); - free(filename); - - if ((ppd = ppdOpen2(ppdCollectionGetPPD(name, NULL, _log, NULL))) == - NULL) - { - ppd_status_t err; // Last error in file - int line; // Line number in file - - - status ++; - err = ppdLastError(&line); - - printf("%s: %s on line %d\n", argv[1], ppdErrorString(err), line); - } - else - { - int j, k; // Looping vars - ppd_group_t *group; // Option group - ppd_option_t *option; // Option - ppd_coption_t *coption; // Custom option - ppd_cparam_t *cparam; // Custom parameter - ppd_const_t *c; // UIConstraints - char lang[255], // LANG environment variable - lc_all[255], // LC_ALL environment variable - lc_ctype[255], // LC_CTYPE environment variable - lc_messages[255];// LC_MESSAGES environment variable - - - if (argc > 2) - { - snprintf(lang, sizeof(lang), "LANG=%s", argv[2]); - putenv(lang); - snprintf(lc_all, sizeof(lc_all), "LC_ALL=%s", argv[2]); - putenv(lc_all); - snprintf(lc_ctype, sizeof(lc_ctype), "LC_CTYPE=%s", argv[2]); - putenv(lc_ctype); - snprintf(lc_messages, sizeof(lc_messages), "LC_MESSAGES=%s", argv[2]); - putenv(lc_messages); - } - - ppdLocalize(ppd); - ppdMarkDefaults(ppd); - - if (argc > 3) - { - text = ppdLocalizeIPPReason(ppd, argv[3], NULL, buffer, sizeof(buffer)); - printf("ppdLocalizeIPPReason(%s)=%s\n", argv[3], - text ? text : "(null)"); - return (text == NULL); - } - - for (i = ppd->num_groups, group = ppd->groups; - i > 0; - i --, group ++) - { - printf("%s (%s):\n", group->name, group->text); - - for (j = group->num_options, option = group->options; - j > 0; - j --, option ++) - { - printf(" %s (%s):\n", option->keyword, option->text); - - for (k = 0; k < option->num_choices; k ++) - printf(" - %s%s (%s)\n", - option->choices[k].marked ? "*" : "", - option->choices[k].choice, option->choices[k].text); - - if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL) - { - for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); - cparam; - cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) - { - switch (cparam->type) - { - case PPD_CUSTOM_UNKNOWN : - printf(" %s(%s): PPD_CUSTOM_UNKNOWN (error)\n", - cparam->name, cparam->text); - break; - - case PPD_CUSTOM_CURVE : - printf(" %s(%s): PPD_CUSTOM_CURVE (%g to %g)\n", - cparam->name, cparam->text, - cparam->minimum.custom_curve, - cparam->maximum.custom_curve); - break; - - case PPD_CUSTOM_INT : - printf(" %s(%s): PPD_CUSTOM_INT (%d to %d)\n", - cparam->name, cparam->text, - cparam->minimum.custom_int, - cparam->maximum.custom_int); - break; - - case PPD_CUSTOM_INVCURVE : - printf(" %s(%s): PPD_CUSTOM_INVCURVE (%g to %g)\n", - cparam->name, cparam->text, - cparam->minimum.custom_invcurve, - cparam->maximum.custom_invcurve); - break; - - case PPD_CUSTOM_PASSCODE : - printf(" %s(%s): PPD_CUSTOM_PASSCODE (%d to %d)\n", - cparam->name, cparam->text, - cparam->minimum.custom_passcode, - cparam->maximum.custom_passcode); - break; - - case PPD_CUSTOM_PASSWORD : - printf(" %s(%s): PPD_CUSTOM_PASSWORD (%d to %d)\n", - cparam->name, cparam->text, - cparam->minimum.custom_password, - cparam->maximum.custom_password); - break; - - case PPD_CUSTOM_POINTS : - printf(" %s(%s): PPD_CUSTOM_POINTS (%g to %g)\n", - cparam->name, cparam->text, - cparam->minimum.custom_points, - cparam->maximum.custom_points); - break; - - case PPD_CUSTOM_REAL : - printf(" %s(%s): PPD_CUSTOM_REAL (%g to %g)\n", - cparam->name, cparam->text, - cparam->minimum.custom_real, - cparam->maximum.custom_real); - break; - - case PPD_CUSTOM_STRING : - printf(" %s(%s): PPD_CUSTOM_STRING (%d to %d)\n", - cparam->name, cparam->text, - cparam->minimum.custom_string, - cparam->maximum.custom_string); - break; - } - } - } - } - } - - puts("\nSizes:"); - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - printf(" %s = %gx%g, [%g %g %g %g]\n", size->name, size->width, - size->length, size->left, size->bottom, size->right, size->top); - - puts("\nConstraints:"); - - for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++) - printf(" *UIConstraints: *%s %s *%s %s\n", c->option1, c->choice1, - c->option2, c->choice2); - if (ppd->num_consts == 0) - puts(" NO CONSTRAINTS"); - - puts("\nFilters:"); - - for (i = 0; i < ppd->num_filters; i ++) - printf(" %s\n", ppd->filters[i]); - - if (ppd->num_filters == 0) - puts(" NO FILTERS"); - - puts("\nAttributes:"); - - for (attr = (ppd_attr_t *)cupsArrayFirst(ppd->sorted_attrs); - attr; - attr = (ppd_attr_t *)cupsArrayNext(ppd->sorted_attrs)) - printf(" *%s %s/%s: \"%s\"\n", attr->name, attr->spec, - attr->text, attr->value ? attr->value : ""); - - puts("\nPPD Cache:"); - if ((pc = ppdCacheCreateWithPPD(ppd)) == NULL) - printf(" Unable to create: %s\n", cupsLastErrorString()); - else - { - ppdCacheWriteFile(pc, "t.cache", NULL); - puts(" Wrote t.cache."); - } - } - - if (!strncmp(argv[1], "-d", 2)) - unlink(filename); - } - -#ifdef __APPLE__ - if (getenv("MallocStackLogging") && getenv("MallocStackLoggingNoCompact")) - { - char command[1024]; // malloc_history command - - snprintf(command, sizeof(command), "malloc_history %d -all_by_size", - getpid()); - fflush(stdout); - system(command); - } -#endif // __APPLE__ - - ppdClose(ppd); - - return (status); -} - - -// -// 'do_ppd_tests()' - Test the default option commands in a PPD file. -// - -static int // O - Number of errors -do_ppd_tests(const char *filename, // I - PPD file - int num_options, // I - Number of options - cups_option_t *options) // I - Options -{ - ppd_file_t *ppd; // PPD file data - cups_page_header2_t header; // Page header - - - printf("\"%s\": ", filename); - fflush(stdout); - - if ((ppd = ppdOpenFile(filename)) == NULL) - { - ppd_status_t status; // Status from PPD loader - int line; // Line number containing error - - - status = ppdLastError(&line); - - puts("FAIL (bad PPD file)"); - printf(" %s on line %d\n", ppdErrorString(status), line); - - return (1); - } - - ppdMarkDefaults(ppd); - ppdMarkOptions(ppd, num_options, options); - - if (ppdRasterInterpretPPD(&header, ppd, 0, NULL, NULL)) - { - puts("FAIL (error from function)"); - puts(_ppdRasterErrorString()); - - return (1); - } - else - { - puts("PASS"); - - return (0); - } -} - - -// -// 'do_ps_tests()' - Test standard PostScript commands. -// - -static int -do_ps_tests(void) -{ - cups_page_header2_t header; // Page header - int preferred_bits; // Preferred bits - int errors = 0; // Number of errors - - - // - // Test PS exec code... - // - - fputs("ppdRasterExecPS(\"setpagedevice\"): ", stdout); - fflush(stdout); - - memset(&header, 0, sizeof(header)); - header.Collate = CUPS_TRUE; - preferred_bits = 0; - - if (ppdRasterExecPS(&header, &preferred_bits, setpagedevice_code)) - { - puts("FAIL (error from function)"); - puts(_ppdRasterErrorString()); - errors ++; - } - else if (preferred_bits != 17 || - memcmp(&header, &setpagedevice_header, sizeof(header))) - { - puts("FAIL (bad header)"); - - if (preferred_bits != 17) - printf(" cupsPreferredBitsPerColor %d, expected 17\n", - preferred_bits); - - print_changes(&setpagedevice_header, &header); - errors ++; - } - else - puts("PASS"); - - fputs("ppdRasterExecPS(\"roll\"): ", stdout); - fflush(stdout); - - if (ppdRasterExecPS(&header, &preferred_bits, - "792 612 0 0 0\n" - "pop pop pop\n" - "<>" - "setpagedevice\n")) - { - puts("FAIL (error from function)"); - puts(_ppdRasterErrorString()); - errors ++; - } - else if (header.PageSize[0] != 792 || header.PageSize[1] != 612) - { - printf("FAIL (PageSize [%d %d], expected [792 612])\n", header.PageSize[0], - header.PageSize[1]); - errors ++; - } - else - puts("PASS"); - - fputs("ppdRasterExecPS(\"dup index\"): ", stdout); - fflush(stdout); - - if (ppdRasterExecPS(&header, &preferred_bits, - "true false dup\n" - "<>setpagedevice\n" - "pop pop pop")) - { - puts("FAIL (error from function)"); - puts(_ppdRasterErrorString()); - errors ++; - } - else - { - if (!header.Collate) - { - printf("FAIL (Collate false, expected true)\n"); - errors ++; - } - - if (header.Duplex) - { - printf("FAIL (Duplex true, expected false)\n"); - errors ++; - } - - if (header.Tumble) - { - printf("FAIL (Tumble true, expected false)\n"); - errors ++; - } - - if(header.Collate && !header.Duplex && !header.Tumble) - puts("PASS"); - } - - fputs("ppdRasterExecPS(\"%%Begin/EndFeature code\"): ", stdout); - fflush(stdout); - - if (ppdRasterExecPS(&header, &preferred_bits, dsc_code)) - { - puts("FAIL (error from function)"); - puts(_ppdRasterErrorString()); - errors ++; - } - else if (header.PageSize[0] != 792 || header.PageSize[1] != 1224) - { - printf("FAIL (bad PageSize [%d %d], expected [792 1224])\n", - header.PageSize[0], header.PageSize[1]); - errors ++; - } - else - puts("PASS"); - - return (errors); -} - - -// -// 'print_changes()' - Print differences in the page header. -// - -static void -print_changes( - cups_page_header2_t *header, // I - Actual page header - cups_page_header2_t *expected) // I - Expected page header -{ - int i; // Looping var - - - if (strcmp(header->MediaClass, expected->MediaClass)) - printf(" MediaClass (%s), expected (%s)\n", header->MediaClass, - expected->MediaClass); - - if (strcmp(header->MediaColor, expected->MediaColor)) - printf(" MediaColor (%s), expected (%s)\n", header->MediaColor, - expected->MediaColor); - - if (strcmp(header->MediaType, expected->MediaType)) - printf(" MediaType (%s), expected (%s)\n", header->MediaType, - expected->MediaType); - - if (strcmp(header->OutputType, expected->OutputType)) - printf(" OutputType (%s), expected (%s)\n", header->OutputType, - expected->OutputType); - - if (header->AdvanceDistance != expected->AdvanceDistance) - printf(" AdvanceDistance %d, expected %d\n", header->AdvanceDistance, - expected->AdvanceDistance); - - if (header->AdvanceMedia != expected->AdvanceMedia) - printf(" AdvanceMedia %d, expected %d\n", header->AdvanceMedia, - expected->AdvanceMedia); - - if (header->Collate != expected->Collate) - printf(" Collate %d, expected %d\n", header->Collate, - expected->Collate); - - if (header->CutMedia != expected->CutMedia) - printf(" CutMedia %d, expected %d\n", header->CutMedia, - expected->CutMedia); - - if (header->Duplex != expected->Duplex) - printf(" Duplex %d, expected %d\n", header->Duplex, - expected->Duplex); - - if (header->HWResolution[0] != expected->HWResolution[0] || - header->HWResolution[1] != expected->HWResolution[1]) - printf(" HWResolution [%d %d], expected [%d %d]\n", - header->HWResolution[0], header->HWResolution[1], - expected->HWResolution[0], expected->HWResolution[1]); - - if (memcmp(header->ImagingBoundingBox, expected->ImagingBoundingBox, - sizeof(header->ImagingBoundingBox))) - printf(" ImagingBoundingBox [%d %d %d %d], expected [%d %d %d %d]\n", - header->ImagingBoundingBox[0], - header->ImagingBoundingBox[1], - header->ImagingBoundingBox[2], - header->ImagingBoundingBox[3], - expected->ImagingBoundingBox[0], - expected->ImagingBoundingBox[1], - expected->ImagingBoundingBox[2], - expected->ImagingBoundingBox[3]); - - if (header->InsertSheet != expected->InsertSheet) - printf(" InsertSheet %d, expected %d\n", header->InsertSheet, - expected->InsertSheet); - - if (header->Jog != expected->Jog) - printf(" Jog %d, expected %d\n", header->Jog, - expected->Jog); - - if (header->LeadingEdge != expected->LeadingEdge) - printf(" LeadingEdge %d, expected %d\n", header->LeadingEdge, - expected->LeadingEdge); - - if (header->Margins[0] != expected->Margins[0] || - header->Margins[1] != expected->Margins[1]) - printf(" Margins [%d %d], expected [%d %d]\n", - header->Margins[0], header->Margins[1], - expected->Margins[0], expected->Margins[1]); - - if (header->ManualFeed != expected->ManualFeed) - printf(" ManualFeed %d, expected %d\n", header->ManualFeed, - expected->ManualFeed); - - if (header->MediaPosition != expected->MediaPosition) - printf(" MediaPosition %d, expected %d\n", header->MediaPosition, - expected->MediaPosition); - - if (header->MediaWeight != expected->MediaWeight) - printf(" MediaWeight %d, expected %d\n", header->MediaWeight, - expected->MediaWeight); - - if (header->MirrorPrint != expected->MirrorPrint) - printf(" MirrorPrint %d, expected %d\n", header->MirrorPrint, - expected->MirrorPrint); - - if (header->NegativePrint != expected->NegativePrint) - printf(" NegativePrint %d, expected %d\n", header->NegativePrint, - expected->NegativePrint); - - if (header->NumCopies != expected->NumCopies) - printf(" NumCopies %d, expected %d\n", header->NumCopies, - expected->NumCopies); - - if (header->Orientation != expected->Orientation) - printf(" Orientation %d, expected %d\n", header->Orientation, - expected->Orientation); - - if (header->OutputFaceUp != expected->OutputFaceUp) - printf(" OutputFaceUp %d, expected %d\n", header->OutputFaceUp, - expected->OutputFaceUp); - - if (header->PageSize[0] != expected->PageSize[0] || - header->PageSize[1] != expected->PageSize[1]) - printf(" PageSize [%d %d], expected [%d %d]\n", - header->PageSize[0], header->PageSize[1], - expected->PageSize[0], expected->PageSize[1]); - - if (header->Separations != expected->Separations) - printf(" Separations %d, expected %d\n", header->Separations, - expected->Separations); - - if (header->TraySwitch != expected->TraySwitch) - printf(" TraySwitch %d, expected %d\n", header->TraySwitch, - expected->TraySwitch); - - if (header->Tumble != expected->Tumble) - printf(" Tumble %d, expected %d\n", header->Tumble, - expected->Tumble); - - if (header->cupsWidth != expected->cupsWidth) - printf(" cupsWidth %d, expected %d\n", header->cupsWidth, - expected->cupsWidth); - - if (header->cupsHeight != expected->cupsHeight) - printf(" cupsHeight %d, expected %d\n", header->cupsHeight, - expected->cupsHeight); - - if (header->cupsMediaType != expected->cupsMediaType) - printf(" cupsMediaType %d, expected %d\n", header->cupsMediaType, - expected->cupsMediaType); - - if (header->cupsBitsPerColor != expected->cupsBitsPerColor) - printf(" cupsBitsPerColor %d, expected %d\n", header->cupsBitsPerColor, - expected->cupsBitsPerColor); - - if (header->cupsBitsPerPixel != expected->cupsBitsPerPixel) - printf(" cupsBitsPerPixel %d, expected %d\n", header->cupsBitsPerPixel, - expected->cupsBitsPerPixel); - - if (header->cupsBytesPerLine != expected->cupsBytesPerLine) - printf(" cupsBytesPerLine %d, expected %d\n", header->cupsBytesPerLine, - expected->cupsBytesPerLine); - - if (header->cupsColorOrder != expected->cupsColorOrder) - printf(" cupsColorOrder %d, expected %d\n", header->cupsColorOrder, - expected->cupsColorOrder); - - if (header->cupsColorSpace != expected->cupsColorSpace) - printf(" cupsColorSpace %s, expected %s\n", - _ppdRasterColorSpaceString(header->cupsColorSpace), - _ppdRasterColorSpaceString(expected->cupsColorSpace)); - - if (header->cupsCompression != expected->cupsCompression) - printf(" cupsCompression %d, expected %d\n", header->cupsCompression, - expected->cupsCompression); - - if (header->cupsRowCount != expected->cupsRowCount) - printf(" cupsRowCount %d, expected %d\n", header->cupsRowCount, - expected->cupsRowCount); - - if (header->cupsRowFeed != expected->cupsRowFeed) - printf(" cupsRowFeed %d, expected %d\n", header->cupsRowFeed, - expected->cupsRowFeed); - - if (header->cupsRowStep != expected->cupsRowStep) - printf(" cupsRowStep %d, expected %d\n", header->cupsRowStep, - expected->cupsRowStep); - - if (header->cupsNumColors != expected->cupsNumColors) - printf(" cupsNumColors %d, expected %d\n", header->cupsNumColors, - expected->cupsNumColors); - - if (fabs(header->cupsBorderlessScalingFactor - - expected->cupsBorderlessScalingFactor) > 0.001) - printf(" cupsBorderlessScalingFactor %g, expected %g\n", - header->cupsBorderlessScalingFactor, - expected->cupsBorderlessScalingFactor); - - if (fabs(header->cupsPageSize[0] - expected->cupsPageSize[0]) > 0.001 || - fabs(header->cupsPageSize[1] - expected->cupsPageSize[1]) > 0.001) - printf(" cupsPageSize [%g %g], expected [%g %g]\n", - header->cupsPageSize[0], header->cupsPageSize[1], - expected->cupsPageSize[0], expected->cupsPageSize[1]); - - if (fabs(header->cupsImagingBBox[0] - expected->cupsImagingBBox[0]) > 0.001 || - fabs(header->cupsImagingBBox[1] - expected->cupsImagingBBox[1]) > 0.001 || - fabs(header->cupsImagingBBox[2] - expected->cupsImagingBBox[2]) > 0.001 || - fabs(header->cupsImagingBBox[3] - expected->cupsImagingBBox[3]) > 0.001) - printf(" cupsImagingBBox [%g %g %g %g], expected [%g %g %g %g]\n", - header->cupsImagingBBox[0], header->cupsImagingBBox[1], - header->cupsImagingBBox[2], header->cupsImagingBBox[3], - expected->cupsImagingBBox[0], expected->cupsImagingBBox[1], - expected->cupsImagingBBox[2], expected->cupsImagingBBox[3]); - - for (i = 0; i < 16; i ++) - if (header->cupsInteger[i] != expected->cupsInteger[i]) - printf(" cupsInteger%d %d, expected %d\n", i, header->cupsInteger[i], - expected->cupsInteger[i]); - - for (i = 0; i < 16; i ++) - if (fabs(header->cupsReal[i] - expected->cupsReal[i]) > 0.001) - printf(" cupsReal%d %g, expected %g\n", i, header->cupsReal[i], - expected->cupsReal[i]); - - for (i = 0; i < 16; i ++) - if (strcmp(header->cupsString[i], expected->cupsString[i])) - printf(" cupsString%d (%s), expected (%s)\n", i, - header->cupsString[i], expected->cupsString[i]); - - if (strcmp(header->cupsMarkerType, expected->cupsMarkerType)) - printf(" cupsMarkerType (%s), expected (%s)\n", header->cupsMarkerType, - expected->cupsMarkerType); - - if (strcmp(header->cupsRenderingIntent, expected->cupsRenderingIntent)) - printf(" cupsRenderingIntent (%s), expected (%s)\n", - header->cupsRenderingIntent, - expected->cupsRenderingIntent); - - if (strcmp(header->cupsPageSizeName, expected->cupsPageSizeName)) - printf(" cupsPageSizeName (%s), expected (%s)\n", - header->cupsPageSizeName, - expected->cupsPageSizeName); -} diff --git a/ppd/thread-private.h b/ppd/thread-private.h deleted file mode 100644 index 5ead90394..000000000 --- a/ppd/thread-private.h +++ /dev/null @@ -1,74 +0,0 @@ -// -// Private threading definitions for libppd. -// -// Copyright 2009-2017 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _PPD_THREAD_PRIVATE_H_ -# define _PPD_THREAD_PRIVATE_H_ - -// -// Include necessary headers... -// - -# include "config.h" - - -// -// C++ magic... -// - -# ifdef __cplusplus -extern "C" { -# endif // __cplusplus - - -# ifdef HAVE_PTHREAD_H // POSIX threading -# include -typedef pthread_mutex_t _ppd_mutex_t; -typedef pthread_key_t _ppd_threadkey_t; -# define _PPD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER -# define _PPD_THREADKEY_INITIALIZER 0 -# define _ppdThreadGetData(k) pthread_getspecific(k) -# define _ppdThreadSetData(k,p) pthread_setspecific(k,p) - -# elif defined(_WIN32) // Windows threading -# include -# include -typedef struct _ppd_mutex_s -{ - int m_init; // Flag for on-demand initialization - CRITICAL_SECTION m_criticalSection; - // Win32 Critical Section -} _ppd_mutex_t; -typedef DWORD _ppd_threadkey_t; -# define _PPD_MUTEX_INITIALIZER { 0, 0 } -# define _PPD_THREADKEY_INITIALIZER 0 -# define _ppdThreadGetData(k) TlsGetValue(k) -# define _ppdThreadSetData(k,p) TlsSetValue(k,p) - -# else // No threading -typedef char _ppd_mutex_t; -typedef void *_ppd_threadkey_t; -# define _PPD_MUTEX_INITIALIZER 0 -# define _PPD_THREADKEY_INITIALIZER (void *)0 -# define _ppdThreadGetData(k) k -# define _ppdThreadSetData(k,p) k=p -# endif // HAVE_PTHREAD_H - - -// -// Functions... -// - -extern void _ppdMutexInit(_ppd_mutex_t *mutex); -extern void _ppdMutexLock(_ppd_mutex_t *mutex); -extern void _ppdMutexUnlock(_ppd_mutex_t *mutex); - -# ifdef __cplusplus -} -# endif // __cplusplus -#endif // !_PPD_THREAD_PRIVATE_H_ diff --git a/ppd/thread.c b/ppd/thread.c deleted file mode 100644 index d62f0011d..000000000 --- a/ppd/thread.c +++ /dev/null @@ -1,141 +0,0 @@ -// -// Threading primitives for libppd. -// -// Copyright © 2009-2018 by Apple Inc. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include "thread-private.h" - - -#if defined(HAVE_PTHREAD_H) -// -// '_ppdMutexInit()' - Initialize a mutex. -// - -void -_ppdMutexInit(_ppd_mutex_t *mutex) // I - Mutex -{ - pthread_mutex_init(mutex, NULL); -} - - -// -// '_ppdMutexLock()' - Lock a mutex. -// - -void -_ppdMutexLock(_ppd_mutex_t *mutex) // I - Mutex -{ - pthread_mutex_lock(mutex); -} - - -// -// '_ppdMutexUnlock()' - Unlock a mutex. -// - -void -_ppdMutexUnlock(_ppd_mutex_t *mutex) // I - Mutex -{ - pthread_mutex_unlock(mutex); -} - - -#elif defined(_WIN32) -# include - -static _ppd_mutex_t ppd_global_mutex = _CUPS_MUTEX_INITIALIZER; - // Global critical section - - -// -// '_ppdMutexInit()' - Initialize a mutex. -// - -void -_ppdMutexInit(_ppd_mutex_t *mutex) // I - Mutex -{ - InitializeCriticalSection(&mutex->m_criticalSection); - mutex->m_init = 1; -} - - -// -// '_ppdMutexLock()' - Lock a mutex. -// - -void -_ppdMutexLock(_ppd_mutex_t *mutex) // I - Mutex -{ - if (!mutex->m_init) - { - EnterCriticalSection(&ppd_global_mutex.m_criticalSection); - - if (!mutex->m_init) - { - InitializeCriticalSection(&mutex->m_criticalSection); - mutex->m_init = 1; - } - - LeaveCriticalSection(&ppd_global_mutex.m_criticalSection); - } - - EnterCriticalSection(&mutex->m_criticalSection); -} - - -// -// '_ppdMutexUnlock()' - Unlock a mutex. -// - -void -_ppdMutexUnlock(_ppd_mutex_t *mutex) // I - Mutex -{ - LeaveCriticalSection(&mutex->m_criticalSection); -} - - -#else // No threading - - -// -// '_ppdMutexInit()' - Initialize a mutex. -// - -void -_ppdMutexInit(_ppd_mutex_t *mutex) // I - Mutex -{ - (void)mutex; -} - - -// -// '_ppdMutexLock()' - Lock a mutex. -// - -void -_ppdMutexLock(_ppd_mutex_t *mutex) // I - Mutex -{ - (void)mutex; -} - - -// -// '_ppdMutexUnlock()' - Unlock a mutex. -// - -void -_ppdMutexUnlock(_ppd_mutex_t *mutex) // I - Mutex -{ - (void)mutex; -} - - -#endif // HAVE_PTHREAD_H diff --git a/utils/cups-browsed-upstart.conf b/utils/cups-browsed-upstart.conf deleted file mode 100644 index 536e1ce98..000000000 --- a/utils/cups-browsed-upstart.conf +++ /dev/null @@ -1,20 +0,0 @@ -# cups-browsed - Bonjour remote printer browsing daemon - -description "cups-browsed - Bonjour remote printer browsing daemon" -author "Till Kamppeter " - -start on (filesystem - and (started cups or runlevel [2345])) -stop on runlevel [016] - -respawn -respawn limit 3 240 - -pre-start script - [ -x /usr/sbin/cups-browsed ] - if [ -x /lib/init/apparmor-profile-load ]; then - /lib/init/apparmor-profile-load usr.sbin.cups-browsed - fi -end script - -exec /usr/sbin/cups-browsed diff --git a/utils/cups-browsed.8 b/utils/cups-browsed.8 deleted file mode 100644 index 73c2f686b..000000000 --- a/utils/cups-browsed.8 +++ /dev/null @@ -1,103 +0,0 @@ -.\"Text automatically generated by txt2man -.TH cups-browsed 8 "29 June 2013" "" "" -.SH NAME -\fBcups-browsed \fP- A daemon for browsing the Bonjour broadcasts of shared, remote CUPS printers -\fB -.SH SYNOPSIS -.nf -.fam C -\fBcups-browsed\fP [\fB-v\fP | \fB-d\fP | \fB--debug\fP] [\fB-c\fP \fIconfig-file\fP] -[\fB-o\fP \fIoption\fB=\fIvalue\fP] [\fB-o\fP '\fIconfig file line\fP'] ... -[\fB--autoshutdown=\fImode\fP] [\fB--autoshutdown-timeout=\fItimeout\fP] -[\fB-h\fP | \fB--help\fP | \fB--version\fP] - -.fam T -.fi -.fam T -.fi -.SH DESCRIPTION -\fBcups-browsed\fP has four independently switchable functions: -.IP 1. 4 -Browse Bonjour broadcasts of remote printers and create/remove local -raw queues pointing to these printers. -.IP 2. 4 -Browse CUPS broadcasts of remote printers and create/remove local raw -queues pointing to these printers. -.IP 3. 4 -Browse an LDAP server for printers and create/remove local raw -queues pointing to these printers. -.IP 4. 4 -Broadcast local queues with the CUPS protocol. -.PP -Note that 2. and 4. are only to allow communication with legacy CUPS servers (1.5.x or older) on the remote machine(s). The standard method to broadcast for shared/network printers to broadcast their presence is Bonjour. The CUPS broadcasting/browsing protocol is deprecated. - -cups-browsed can be run permanently (from system boot to shutdown) or on-demand (for example to save resources on mobile devices). For running it on-demand an auto-shutdown feature can be activated to let cups-browsed terminate when it does not have queues any more to take care of. - -.SH OPTIONS -.TP -.B -\fB-v\fP, \fB-d\fP, \fB--debug\fP -Debug mode, verbose logging to stderr -.TP -.B -\fB-l\fP, \fB--logfile\fP -Debug logging into /var/log/cups/cups-browsed_log file. -.TP -.B -\fB-c\fP \fIconfig-file\fP -Uses the alternative configuration file \fIconfig-file\fP instead of the standard one. -.TP -.B -\fB-o\fP \fIoption\fB=\fIvalue\fB, -o\fP '\fIconfig file line\fP' -Supply configuration options via the command line. You can supply any line which also could be put into the configuration file, but note that due to the spaces the line has to be put into quotes, or for a simple key/value pair the space between key and value can get replaced by '='. If command-line-supplied configuration settings are contradicting with the ones in the configuration file, the ones in the configuration file will get used. -.TP -.B -\fB--autoshutdown=mode\fP -Auto shutdown mode, \fBmode\fP is \fBoff\fP for no auto shutdown, \fBon\fP for auto shutdown being active, and \fBavahi\fP for control by the avahi-daemon being run on-demand, getting auto-shutdown turned off while avahi-daemon is present and on when avahi-daemon is shut down. -.TP -.B -\fB--autoshutdown-on=inactivity-type\fP -What cups-browsed considers as inactivity for auto-shutdown. \fBinactivity-type\fP set to \fBno-queues\fP (the default) means that auto-shutdown is initiated if there are no queues generated by cups-browsed any more, \fBno-jobs\fP means that auto-shutdown will get initiated if all queues generated by cups-browsed are without jobs. -.TP -.B -\fB--autoshutdown-timeout=timeout\fP -\fBtimeout\fP tells after how many seconds cups-browsed should shut down if it has no local queues set up for any discovered remote printer any more or jobs on these. Default is 30 seconds. 0 means immediate shutdown. -.TP -.B -\fB-h, --help, --version\fP -Display usage and version info and do not start the daemon. -.SH FILES -/etc/cups/cups-browsed.conf -.SH SIGNALS -\fISIGINT, SIGTERM\f1: cups-browsed will shutdown. - -\fISIGUSR1\f1: Switches cups-browsed into permanent mode (no auto shutdown). - -\fISIGUSR2\f1: Switches cups-browsed into auto shutdown mode. - -.SH NOTES -Please take references to cups 1.6.x to include newer versions. -Similarly, cups 1.5.x is intended to encompass older versions too. -.PP -In environments with only cups 1.6.x servers and clients (plus -\fBcups-browsed\fP on either server or client or both) the function described in 1. -enables the automatic discovery of remote queues and their display in -printing dialogues of applications and with command line tools. -.PP -The facility provided by 3. allows printers that are registered in an LDAP -server to be added as local queues. CUPS servers 1.5.x are able to automatically -register printers in LDAP. The facility provided by \fBcups-browsed\fP allows -a filter string to further limit the printers that are browsed from LDAP. -.PP -The facility provided by 4. means that servers running cups 1.6.x plus -\fBcups-browsed\fP can broadcast their local queues so that clients with cups -1.5.x get these queues automatically available. The outcome of 2. is -that clients running cups 1.6.x plus \fBcups-browsed\fP can use the CUPS -broadcasts from servers with cups 1.5.x. As with browsing of Bonjour -broadcasts, the created local raw queues are available to applications -and command line tools. -.PP -This manual page was written for the Debian Project, but it may be used by others. -.SH SEE ALSO - -\fBcups-browsed.conf\fP(5) diff --git a/utils/cups-browsed.c b/utils/cups-browsed.c deleted file mode 100644 index c18a6a4ea..000000000 --- a/utils/cups-browsed.c +++ /dev/null @@ -1,14147 +0,0 @@ -// -// This file is part of cups-filters. -// -// Copyright 2012-2021 Till Kamppeter -// Copyright 2013-2015 Tim Waugh -// Copyright 2018-2019 Deepak Patankar -// Copyright 2020 Mohit Mohan -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#if defined(__OpenBSD__) -#include -#endif // __OpenBSD__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef HAVE_AVAHI -#include -#include - -#include -#include -#include -#endif // HAVE_AVAHI - -#include - - -#ifdef HAVE_LDAP -# ifdef __sun -# include -# endif // __sun -# include -# ifdef HAVE_LDAP_SSL_H -# include -# endif // HAVE_LDAP_SSL_H -#endif // HAVE_LDAP - - -#ifdef HAVE_LDAP -LDAP *BrowseLDAPHandle = NULL; - // Handle to LDAP server -char *BrowseLDAPBindDN = NULL, - // LDAP login DN - *BrowseLDAPDN = NULL, - // LDAP search DN - *BrowseLDAPPassword = NULL, - // LDAP login password - *BrowseLDAPServer = NULL, - // LDAP server to use - *BrowseLDAPFilter = NULL; - // LDAP query filter -int BrowseLDAPUpdate = TRUE, - // enables LDAP updates - BrowseLDAPInitialised = FALSE; - // the init stuff has been done -# ifdef HAVE_LDAP_SSL -char *BrowseLDAPCACertFile = NULL; - // LDAP CA CERT file to use -# endif // HAVE_LDAP_SSL -#endif // HAVE_LDAP - - -#ifdef HAVE_LDAP -#define LDAP_BROWSE_FILTER "(objectclass=cupsPrinter)" -static LDAP *ldap_new_connection(void); -static LDAP *ldap_reconnect(void); -static void ldap_disconnect(LDAP *ld); -static int ldap_search_rec(LDAP *ld, char *base, int scope, - char *filter, char *attrs[], - int attrsonly, LDAPMessage **res); -static int ldap_getval_firststring(LDAP *ld, LDAPMessage *entry, - char *attr, char *retval, - unsigned long maxsize); -static void ldap_freeres(LDAPMessage *entry); -# ifdef HAVE_LDAP_REBIND_PROC -# if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -static int ldap_rebind_proc(LDAP *RebindLDAPHandle, - LDAP_CONST char *refsp, - ber_tag_t request, - ber_int_t msgid, - void *params); -# else -static int ldap_rebind_proc(LDAP *RebindLDAPHandle, - char **dnp, - char **passwdp, - int *authmethodp, - int freeit, - void *arg); -# endif // defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -# endif // HAVE_LDAP_REBIND_PROC -#endif // HAVE_LDAP - -#include -#include -#include -#include -#include - -#include "cups-notifier.h" - -// Attribute to mark a CUPS queue as created by us -#define CUPS_BROWSED_MARK "cups-browsed" -#define AUTO_OPTION "auto" - -// Attribute to tell the implicitclass backend the destination queue for -// the current job -#define CUPS_BROWSED_DEST_PRINTER "cups-browsed-dest-printer" - -// Timeout values in sec -#define TIMEOUT_IMMEDIATELY -1 -#define TIMEOUT_CONFIRM 10 -#define TIMEOUT_RETRY 10 -#define TIMEOUT_REMOVE -1 -#define TIMEOUT_CHECK_LIST 2 - -#define CUPS_DBUS_NAME "org.cups.cupsd.Notifier" -#define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier" -#define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier" - -#define DEFAULT_CACHEDIR "/var/cache/cups" -#define DEFAULT_LOGDIR "/var/log/cups" -#define LOCAL_DEFAULT_PRINTER_FILE "/cups-browsed-local-default-printer" -#define REMOTE_DEFAULT_PRINTER_FILE "/cups-browsed-remote-default-printer" -#define SAVE_OPTIONS_FILE "/cups-browsed-options-%s" -#define DEBUG_LOG_FILE "/cups-browsed_log" -#define DEBUG_LOG_FILE_2 "/cups-browsed_previous_logs" - -// Status of remote printer -typedef enum printer_status_e -{ - STATUS_UNCONFIRMED = 0, // Generated in a previous session - STATUS_CONFIRMED, // Avahi confirms UNCONFIRMED printer - STATUS_TO_BE_CREATED, // Scheduled for creation - STATUS_DISAPPEARED, // Scheduled for removal - STATUS_TO_BE_RELEASED // Scheduled for release from cups-browsed -} printer_status_t; - -// Data structure for taking note of each time the remote printer -// appears as a discovered IPP service -typedef struct ipp_discovery_s -{ - char *interface; - char *type; - int family; -} ipp_discovery_t; - -// Data structure for remote printers -typedef struct remote_printer_s -{ - char *queue_name; - char *location; - char *info; - char *uri; - char *make_model; - char *pdl; - int color; - int duplex; - ipp_t *prattrs; - char *nickname; - int num_options; - cups_option_t *options; - printer_status_t status; - time_t timeout; - void *slave_of; - int last_printer; - char *host; - char *ip; - int port; - char *resource; - char *service_name; - char *type; - char *domain; - cups_array_t *ipp_discoveries; - int no_autosave; - int overwritten; - int netprinter; - int is_legacy; - int timeouted; - pthread_rwlock_t lock; - int called; -} remote_printer_t; - -// Data structure for network interfaces -typedef struct netif_s -{ - char *address; - http_addr_t broadcast; -} netif_t; - -// Data structures for browse allow/deny rules -typedef enum browse_order_e -{ - ORDER_ALLOW_DENY, - ORDER_DENY_ALLOW -} browse_order_t; - -typedef enum allow_type_e -{ - ALLOW_IP, - ALLOW_NET, - ALLOW_INVALID -} allow_type_t; - -typedef enum allow_sense_e -{ - ALLOW_ALLOW, - ALLOW_DENY -} allow_sense_t; - -typedef struct allow_s -{ - allow_type_t type; - allow_sense_t sense; - http_addr_t addr; - http_addr_t mask; -} allow_t; - -// Data structures for browse filter rules -typedef enum filter_sense_s -{ - FILTER_MATCH, - FILTER_NOT_MATCH -} filter_sense_t; - -typedef struct browse_filter_s -{ - filter_sense_t sense; - char *field; - char *regexp; - regex_t *cregexp; -} browse_filter_t; - -// Data structure for a printer discovered using BrowsePoll -typedef struct browsepoll_printer_s -{ - char *uri_supported; - char *location; - char *info; -} browsepoll_printer_t; - -// Data structure for a BrowsePoll server -typedef struct browsepoll_s -{ - char *server; - int port; - int major; - int minor; - gboolean can_subscribe; - int subscription_id; - int sequence_number; - - // Remember which printers we discovered. This way we can just ask - // if anything has changed, and if not we know these printers are - // still there. - GList *printers; // of browsepoll_printer_t -} browsepoll_t; - -// Data structure for destination list obtained with cupsEnumDests() -typedef struct dest_list_s -{ - int num_dests; - cups_dest_t *dests; -} dest_list_t; - -// Local printer (key is name) -typedef struct local_printer_s { - char *device_uri; - char *uuid; - gboolean cups_browsed_controlled; -} local_printer_t; - -// Browse data to send for local printer -typedef struct browse_data_s -{ - int type; - int state; - char *uri; - char *location; - char *info; - char *make_model; - char *browse_options; -} browse_data_t; - -// Data structure for manual definition of load-balancing clusters -typedef struct cluster_s -{ - char *local_queue_name; - cups_array_t *members; -} cluster_t; - -// Ways how to represent the remote printer's IP in the device URI -typedef enum ip_based_uris_e -{ - IP_BASED_URIS_NO, - IP_BASED_URIS_ANY, - IP_BASED_URIS_IPV4_ONLY, - IP_BASED_URIS_IPV6_ONLY -} ip_based_uris_t; - -// Ways how to name local queues for remote printers -typedef enum local_queue_naming_e -{ - LOCAL_QUEUE_NAMING_DNSSD, - LOCAL_QUEUE_NAMING_MAKE_MODEL, - LOCAL_QUEUE_NAMING_REMOTE_NAME -} local_queue_naming_t; - -// Automatically create queues for IPP network printers: No, only for -// IPP printers, for all printers -typedef enum create_ipp_printer_queues_e -{ - IPP_PRINTERS_NO, - IPP_PRINTERS_LOCAL_ONLY, - IPP_PRINTERS_PWGRASTER, - IPP_PRINTERS_APPLERASTER, - IPP_PRINTERS_PCLM, - IPP_PRINTERS_PDF, - IPP_PRINTERS_DRIVERLESS, - IPP_PRINTERS_ALL -} create_ipp_printer_queues_t; - -// Ways how we can do load balancing on remote queues with the same name -typedef enum load_balancing_type_e -{ - QUEUE_ON_CLIENT, - QUEUE_ON_SERVERS -} load_balancing_type_t; - -// Ways how inactivity for auto-shutdown is defined -typedef enum autoshutdown_inactivity_type_e -{ - NO_QUEUES, - NO_JOBS -} autoshutdown_inactivity_type_t; - -typedef struct media_size_s -{ - int x; - int y; -} media_size_t; - -typedef struct pagesize_range_s -{ - int x_dim_min; - int x_dim_max; - int y_dim_min; - int y_dim_max; -} pagesize_range_t; - -typedef struct media_col_s -{ - int x,y,top_margin,bottom_margin,left_margin,right_margin; - char *media_source,*media_type; -} media_col_t; - -typedef struct default_str_attribute_s -{ - char* value; - int count; -} default_str_attribute_t; - -typedef struct resolution_count_s -{ - cf_res_t *res; - int count; -} resolution_count_t; - -typedef struct mediacol_count_s -{ - media_col_t *data; - int count; -} mediacol_count_t; - -typedef struct pagesize_count_s -{ - char* pagesize; - int count; -} pagesize_count_t; - -typedef struct resolver_args_s -{ - AvahiServiceResolver *r; - AvahiIfIndex interface; - AvahiProtocol protocol; - AvahiResolverEvent event; - const char *name; - const char *type; - const char *domain; - const char *host_name; - const AvahiAddress *address; - uint16_t port; - AvahiStringList *txt; - AvahiLookupResultFlags flags; - void* userdata; -} resolver_args_t; - -typedef struct create_args_s -{ - char* queue; - char* uri; -} create_args_t; - -cups_array_t *remote_printers; -static char *alt_config_file = NULL; -static cups_array_t *command_line_config; -static cups_array_t *netifs; -static cups_array_t *local_hostnames; -static cups_array_t *browseallow; -static gboolean browseallow_all = FALSE; -static gboolean browsedeny_all = FALSE; -static browse_order_t browse_order; -static cups_array_t *browsefilter; - -static GHashTable *local_printers; -static GHashTable *cups_supported_remote_printers; -static browsepoll_t *local_printers_context = NULL; -static http_t *local_conn = NULL; -static gboolean inhibit_local_printers_update = FALSE; - -static GList *browse_data = NULL; - -static CupsNotifier *cups_notifier = NULL; - -static GMainLoop *gmainloop = NULL; -#ifdef HAVE_AVAHI -static AvahiGLibPoll *glib_poll = NULL; -static AvahiClient *client = NULL; -static AvahiServiceBrowser *sb1 = NULL, *sb2 = NULL; -static int avahi_present = 0; -#endif // HAVE_AVAHI -#ifdef HAVE_LDAP -static const char * const ldap_attrs[] =// CUPS LDAP attributes - { - "printerDescription", - "printerLocation", - "printerMakeAndModel", - "printerType", - "printerURI", - NULL - }; -#endif // HAVE_LDAP -static guint queues_timer_id = 0; -static int browsesocket = -1; - -#define BROWSE_DNSSD (1<<0) -#define BROWSE_CUPS (1<<1) -#define BROWSE_LDAP (1<<2) -static unsigned int BrowseLocalProtocols = 0; -static unsigned int BrowseRemoteProtocols = BROWSE_DNSSD; -static unsigned int BrowseInterval = 60; -static unsigned int BrowseTimeout = 300; -static uint16_t BrowsePort = 631; -static browsepoll_t **BrowsePoll = NULL; -static unsigned int NewBrowsePollQueuesShared = 0; -static unsigned int AllowResharingRemoteCUPSPrinters = 0; -static unsigned int DebugLogFileSize = 300; -static size_t NumBrowsePoll = 0; -static guint update_netifs_sourceid = 0; -static char local_server_str[1024]; -static char *DomainSocket = NULL; -static int cannot_create = 0; -static int cups_queues_updated = 0; -static int update_count = 0; -static unsigned int HttpLocalTimeout = 5; -static unsigned int HttpRemoteTimeout = 10; -static unsigned int HttpMaxRetries = 5; -static unsigned int DNSSDBasedDeviceURIs = 1; -static ip_based_uris_t IPBasedDeviceURIs = IP_BASED_URIS_NO; -#ifdef NAMING_MAKE_MODEL -static local_queue_naming_t LocalQueueNamingRemoteCUPS = - LOCAL_QUEUE_NAMING_MAKE_MODEL; -#else -# ifdef NAMING_REMOTE_NAME -static local_queue_naming_t LocalQueueNamingRemoteCUPS = - LOCAL_QUEUE_NAMING_REMOTE_NAME; -# else -static local_queue_naming_t LocalQueueNamingRemoteCUPS = - LOCAL_QUEUE_NAMING_DNSSD; -# endif -#endif -static local_queue_naming_t LocalQueueNamingIPPPrinter=LOCAL_QUEUE_NAMING_DNSSD; -static unsigned int OnlyUnsupportedByCUPS = 0; -static unsigned int UseCUPSGeneratedPPDs = 0; -static unsigned int CreateRemoteRawPrinterQueues = 0; -static unsigned int CreateRemoteCUPSPrinterQueues = 1; -#ifdef ONLY_LOCAL_IPP_PRINTERS_AUTO_SETUP -static create_ipp_printer_queues_t CreateIPPPrinterQueues = - IPP_PRINTERS_LOCAL_ONLY; -#else -#ifdef ONLY_DRIVERLESS_IPP_PRINTERS_AUTO_SETUP -static create_ipp_printer_queues_t CreateIPPPrinterQueues = - IPP_PRINTERS_DRIVERLESS; -#else -static create_ipp_printer_queues_t CreateIPPPrinterQueues = - IPP_PRINTERS_ALL; -#endif -#endif -#ifdef SAVING_CREATED_QUEUES -static unsigned int KeepGeneratedQueuesOnShutdown = 1; -#else -static unsigned int KeepGeneratedQueuesOnShutdown = 0; -#endif -static int NewIPPPrinterQueuesShared = 0; -static int AutoClustering = 1; -static cups_array_t *clusters; -static load_balancing_type_t LoadBalancingType = QUEUE_ON_CLIENT; -static char *DefaultOptions = NULL; -static int update_cups_queues_max_per_call = 10; -static int pause_between_cups_queue_updates = 1; -static remote_printer_t *deleted_master = NULL; -static int terminating = 0; // received SIGTERM, ignore callbacks, - // break loops -static int in_shutdown = 0; -static int autoshutdown = 0; -static int autoshutdown_avahi = 0; -static int autoshutdown_timeout = 30; -static autoshutdown_inactivity_type_t autoshutdown_on = NO_QUEUES; -static guint autoshutdown_exec_id = 0; -static const char *default_printer = NULL; -static unsigned int notify_lease_duration = 86400; -#ifdef FREQUENT_NETIF_UPDATE -static int FrequentNetifUpdate = 1; -#else -static int FrequentNetifUpdate = 0; -#endif - -static int debug_stderr = 0; -static int debug_logfile = 0; -static FILE *lfp = NULL; - -static char cachedir[1024]; -static char logdir[1024]; -static char local_default_printer_file[2048]; -static char remote_default_printer_file[2048]; -static char save_options_file[2048]; -static char debug_log_file[2048]; -static char debug_log_file_bckp[2048]; - -// Contains ppd keywords which are written by ppdgenerator.c in the ppd file. -static char* ppd_keywords[] = - { - "PageSize", - "PageRegion", - "InputSlot", - "MediaType", - "ColorModel", - "Duplex", - "OutputBin", - "StapleLocation", - "FoldType", - "PunchMedia", - "Booklet", - "cupsFinishingTemplate", - "cupsPrintQuality", - "print-content-optimize", - "print-rendering-intent", - "print-scaling", - }; - -// Static global variable for indicating we have reached the HTTP timeout -static int timeout_reached = 0; - -// read-write locks -pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; -pthread_rwlock_t loglock = PTHREAD_RWLOCK_INITIALIZER; - -pthread_rwlock_t resolvelock = PTHREAD_RWLOCK_INITIALIZER; -pthread_rwlock_t netiflock = PTHREAD_RWLOCK_INITIALIZER; -pthread_rwlock_t update_lock = PTHREAD_RWLOCK_INITIALIZER; - - -static void recheck_timer (void); -static void browse_poll_create_subscription (browsepoll_t *context, - http_t *conn); -static gboolean browse_poll_get_notifications (browsepoll_t *context, - http_t *conn); -static remote_printer_t -*examine_discovered_printer_record(const char *host, - const char *ip, - uint16_t port, - char *resource, - const char *service_name, - const char *location, - const char *info, - const char *type, - const char *domain, - const char *interface, - int family, - void *txt); - - -static void -start_debug_logging() -{ - if (debug_log_file[0] == '\0') - return; - if (lfp == NULL) - lfp = fopen(debug_log_file, "a+"); - if (lfp == NULL) - { - fprintf(stderr, "cups-browsed: ERROR: Failed creating debug log file %s\n", - debug_log_file); - exit(1); - } -} - - -static void -stop_debug_logging() -{ - debug_logfile = 0; - if (lfp) - fclose(lfp); - lfp = NULL; -} - - -// Returns the size of debug log file -static long int -findLogFileSize() -{ - FILE* fp = fopen(debug_log_file, "r"); - if (fp == NULL) - { - return (-1); - } - fseek(fp, 0L, SEEK_END); - long int res = ftell(fp); - fclose(fp); - return (res); -} - - -static void -copyToFile(FILE **fp1, - FILE **fp2) -{ - int buffer_size = 2048; - char *buf = (char*) malloc(sizeof(char) * buffer_size); - if (!buf) - { - fprintf(stderr,"Error creating buffer for debug logging\n"); - return; - } - fseek(*fp1, 0, SEEK_SET); - size_t r; - do - { - r = fread(buf, sizeof(char), buffer_size, *fp1); - fwrite(buf, sizeof(char), r, *fp2); - } - while(r == buffer_size); -} - - -static void -debug_printf(const char *format, ...) -{ - pthread_rwlock_wrlock(&loglock); - if (debug_stderr || debug_logfile) - { - time_t curtime = time(NULL); - char buf[64]; - ctime_r(&curtime, buf); - while(isspace(buf[strlen(buf)-1])) - buf[strlen(buf) - 1] = '\0'; - va_list arglist; - if (debug_stderr) - { - va_start(arglist, format); - fprintf(stderr, "%s ", buf); - fprintf(stderr, "%ld ", pthread_self()); - vfprintf(stderr, format, arglist); - fflush(stderr); - va_end(arglist); - } - if (debug_logfile && lfp) - { - va_start(arglist, format); - fprintf(lfp, "%s ", buf); - fprintf(lfp, "%ld ", pthread_self()); - vfprintf(lfp, format, arglist); - fflush(lfp); - va_end(arglist); - } - - long int log_file_size = findLogFileSize(); - if (DebugLogFileSize > 0 && - log_file_size > (long int)DebugLogFileSize * 1024) - { - fclose(lfp); - FILE *fp1 = fopen(debug_log_file, "r"); - FILE *fp2 = fopen(debug_log_file_bckp, "w"); - copyToFile(&fp1, &fp2); - fclose(fp1); - fclose(fp2); - lfp = fopen(debug_log_file, "w"); - } - } - pthread_rwlock_unlock(&loglock); -} - - -static void -debug_log_out(char *log) -{ - pthread_rwlock_wrlock(&loglock); - if (debug_stderr || debug_logfile) - { - time_t curtime = time(NULL); - char buf[64]; - char *ptr1, *ptr2; - ctime_r(&curtime, buf); - while(isspace(buf[strlen(buf)-1])) buf[strlen(buf)-1] = '\0'; - ptr1 = log; - while(ptr1) - { - ptr2 = strchr(ptr1, '\n'); - if (ptr2) *ptr2 = '\0'; - if (debug_stderr) - fprintf(stderr, "%s %s\n", buf, ptr1); - if (debug_logfile && lfp) - fprintf(lfp, "%s %s\n", buf, ptr1); - if (ptr2) *ptr2 = '\n'; - ptr1 = ptr2 ? (ptr2 + 1) : NULL; - } - } - pthread_rwlock_unlock(&loglock); -} - - -// -// 'create_media_size()' - Create a media-size value. -// - -static ipp_t * // O - media-col collection -create_media_size(int width, // I - x-dimension in 2540ths - int length) // I - y-dimension in 2540ths -{ - ipp_t *media_size = ippNew(); // media-size value - - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", - width); - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", - length); - - return (media_size); -} - - -// -// 'create_media_range()' - Create a pagesize-range value. -// - -static ipp_t * -create_media_range(int x_dim_min_width, - int x_dim_max_width, - int y_dim_min_height, - int y_dim_max_height) -{ - ipp_t *media_size = ippNew(); - ippAddRange(media_size, IPP_TAG_PRINTER, "x-dimension", - x_dim_min_width, x_dim_max_width); - ippAddRange(media_size, IPP_TAG_PRINTER, "y-dimension", - y_dim_min_height, y_dim_max_height); - return (media_size); -} - - -static void * -copy_media_size(void *size, - void *user_data) -{ - media_size_t *data = (media_size_t *)size; - media_size_t *copy; - - copy = (media_size_t *)calloc(1, sizeof(media_size_t)); - if (copy) - { - copy->x = data->x; - copy->y = data->y; - } - return (copy); -} - - -static void * -copy_range_size(void *range, - void* user_data) -{ - pagesize_range_t *data = (pagesize_range_t *)range; - pagesize_range_t *copy; - - copy = (pagesize_range_t *)calloc(1, sizeof(pagesize_range_t)); - if (copy) - { - copy->x_dim_min = data->x_dim_min; - copy->x_dim_max = data->x_dim_max; - copy->y_dim_min = data->y_dim_min; - copy->y_dim_max = data->y_dim_max; - } - return (copy); -} - - -static void * -copy_media(void *media, - void *user_data) -{ - media_col_t *data = (media_col_t *)media; - media_col_t *copy; - - copy = (media_col_t *)calloc(1, sizeof(media_col_t)); - if (copy) - { - copy->x = data->x; - copy->y = data->y; - copy->left_margin=data->left_margin; - copy->right_margin=data->right_margin; - copy->top_margin=data->top_margin; - copy->bottom_margin=data->bottom_margin; - copy->media_source = NULL; - copy->media_type = NULL; - if (data->media_source != NULL) - { - copy->media_source = (char *)malloc(sizeof(char)*32); - strcpy(copy->media_source, data->media_source); - } - if (data->media_type != NULL) - { - copy->media_type = (char *)malloc(sizeof(char)*32);; - strcpy(copy->media_type, data->media_type); - } - } - return (copy); -} - - -// -// 'create_media_col()' - Create a media-col value. -// - -static ipp_t * -create_media_col(int width, - int length, - int left_margin, - int right_margin, - int top_margin, - int bottom_margin, - char *media_source, - char *media_type) -{ - ipp_t *media_col = ippNew(), // media-col value - *media_size = create_media_size(width, length); - - ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size); - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-bottom-margin",bottom_margin); - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-left-margin", left_margin); - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-right-margin",right_margin); - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "media-top-margin", top_margin); - if (media_source != NULL) - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-source", NULL,media_source); - if (media_type != NULL) - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-type", NULL,media_type); - ippDelete(media_size); - - return (media_col); -} - - -static int -compare_mediasize(void *media_a, void *media_b, - void *user_data) -{ - media_size_t *a = (media_size_t *)media_a; - media_size_t *b = (media_size_t *)media_b; - - if (a->x < b->x) - return (-1); - else if (a->x > b->x) - return (1); - else - { - if (a->y == b->y) - return (0); - else if (a->y < b->y) - return (-1); - return (1); - } -} - - -static int -compare_int(int a, - int b) -{ - if (a < b) - return (-1); - else if (a > b) - return (1); - return (0); -} - - -static int -compare_rangesize(void *range_a,void *range_b, - void *user_data) -{ - pagesize_range_t *a = (pagesize_range_t *)range_a; - pagesize_range_t *b = (pagesize_range_t *)range_b; - int value; - - if ((value = compare_int(a->x_dim_min, b->x_dim_min)) == 0) - { - if ((value = compare_int(a->x_dim_max, b->x_dim_max)) == 0) - { - if ((value = compare_int(a->y_dim_min, b->y_dim_min)) == 0) - { - if ((value = compare_int(a->y_dim_max, b->y_dim_max)) == 0) - return (0); - } - } - } - return (value); -} - - -static int -compare_media(void *media_a, - void *media_b, - void *user_data) -{ - media_col_t *a = (media_col_t *)media_a; - media_col_t *b = (media_col_t *)media_b; - int value; - - if ((value = compare_int(a->x, b->x)) == 0) - { - if ((value = compare_int(a->y, b->y)) == 0) - { - if ((value = compare_int(a->top_margin, b->top_margin)) == 0) - { - if ((value = compare_int(a->bottom_margin, b->bottom_margin)) == 0) - { - if ((value = compare_int(a->right_margin, b->right_margin)) == 0) - { - if ((value = compare_int(a->left_margin, b->left_margin)) == 0) - { - if (a->media_source == NULL && b->media_source == NULL) - { - if (a->media_type == NULL && b->media_type == NULL) - return (0); - if (a->media_type == NULL) - return (-1); - if (b->media_type == NULL) - return (1); - return (strcmp(a->media_type, b->media_type)); - } - if (a->media_source == NULL) - return (-1); - if (b->media_source == NULL) - return (1); - if (!strcmp(a->media_source, b->media_source)) - { - if (a->media_type == NULL && b->media_type == NULL) - return (0); - if (a->media_type == NULL) - return (-1); - if (b->media_type==NULL) - return (1); - return (strcmp(a->media_type, - b->media_type)); - } - else - return (strcmp(a->media_source, - b->media_source)); - } - } - } - } - } - } - return (value); -} - - -// -// pwg_compare_sizes()' - Compare two media sizes... -// - -static int // O - Result of comparison -pwg_compare_sizes(cups_size_t *a, // I - First media size - cups_size_t *b) // I - Second media size -{ - return (strcmp(a->media, b->media)); -} - - -// -// 'pwg_copy_size()' - Copy a media size. -// - -static cups_size_t * // O - New media size -pwg_copy_size(cups_size_t *size) // I - Media size to copy -{ - cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t)); - // New media size - - if (newsize) - memcpy(newsize, size, sizeof(cups_size_t)); - - return (newsize); -} - - -// Function returns number of jobs queued on printer -static int // O - Number of jobs -get_number_of_jobs(http_t *http, // I - Connection to server - const char *uri, // I - uri of printer - int myjobs, // I - 0 = all users, 1 = mine - int whichjobs) // I - CUPS_WHICHJOBS_ALL, - // CUPS_WHICHJOBS_ACTIVE, or - // CUPS_WHICHJOBS_COMPLETED -{ - int n; // Number of jobs - ipp_t *request, // IPP Request - *response; // IPP Response - ipp_attribute_t *attr; // Current attribute - int id; // job-id - static const char * const attrs[] = // Requested attributes - { - "job-id" - }; - - httpReconnect2(http, 30000, NULL); - - // - // Build an IPP_GET_JOBS request, which requires the following - // attributes: - // - // attributes-charset - // attributes-natural-language - // printer-uri - // requesting-user-name - // which-jobs - // my-jobs - // requested-attributes - // - - // Generating IPP Request - request = ippNewRequest(IPP_OP_GET_JOBS); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - if (myjobs) - ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); - if (whichjobs == CUPS_WHICHJOBS_COMPLETED) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "which-jobs", NULL, "completed"); - else if (whichjobs == CUPS_WHICHJOBS_ALL) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "which-jobs", NULL, "all"); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", sizeof(attrs) / sizeof(attrs[0]), - NULL, attrs); - - // Do the request and get back a response... - n = 0; - if ((response = cupsDoRequest(http, request, "/")) != NULL) - { - for (attr = ippFirstAttribute(response); attr; - attr = ippNextAttribute(response)) - { - // Skip leading attributes until we hit a job... - while (attr && ippGetGroupTag(attr) != IPP_TAG_JOB) - attr = ippNextAttribute(response); - - if (!attr) - break; - // Pull the needed attributes from this job - id = 0; - while (attr && ippGetGroupTag(attr) == IPP_TAG_JOB) - { - if (!strcmp(ippGetName(attr), "job-id") && - ippGetValueTag(attr) == IPP_TAG_INTEGER) - id = ippGetInteger(attr, 0); - attr = ippNextAttribute(response); - } - - // See if we have everything needed - if (!id) - { - if (!attr) - break; - else - continue; - } - - // Incrementing number of jobs - n ++; - if (!attr) - break; - } - - ippDelete(response); - } - - if (n == 0) - return (-1); - else - return (n); -} - - -static const char * -password_callback (const char *prompt, - http_t *http, - const char *method, - const char *resource, - void *user_data) -{ - return (NULL); -} - - -static http_t * -httpConnectEncryptShortTimeout(const char *host, - int port, - http_encryption_t encryption) -{ - return (httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 3000, - NULL)); -} - - -static int -http_timeout_cb(http_t *http, - void *user_data) -{ - debug_printf("HTTP timeout! (consider increasing HttpLocalTimeout/HttpRemoteTimeout value)\n"); - timeout_reached = 1; - return (0); -} - - -static http_t * -http_connect_local (void) -{ - const char *server = cupsServer(); - int port = ippPort(); - - if (!local_conn) - { - if (server[0] == '/') - debug_printf("cups-browsed: Creating http connection to local CUPS daemon via domain socket: %s\n", - server); - else - debug_printf("cups-browsed: Creating http connection to local CUPS daemon: %s:%d\n", - server, port); - local_conn = httpConnectEncryptShortTimeout(server, port, - cupsEncryption()); - } - if (local_conn) - httpSetTimeout(local_conn, HttpLocalTimeout, http_timeout_cb, NULL); - else - { - if (server[0] == '/') - debug_printf("cups-browsed: Failed creating http connection to local CUPS daemon via domain socket: %s\n", - server); - else - debug_printf("cups-browsed: Failed creating http connection to local CUPS daemon: %s:%d\n", - server, port); - } - - return (local_conn); -} - - -static void -http_close_local (void) -{ - if (local_conn) - { - httpClose (local_conn); - local_conn = NULL; - } -} - - -static void -pwg_ppdize_name(const char *ipp, // I - IPP keyword - char *name, // I - Name buffer - size_t namesize) // I - Size of name buffer -{ - char *ptr, // Pointer into name buffer - *end; // End of name buffer - - *name = (char)toupper(*ipp++); - - for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;) - { - if (*ipp == '-' && - ((ipp[1] >= 'A' && ipp[1] <= 'Z') || - (ipp[1] >= 'a' && ipp[1] <= 'z'))) - { - ipp ++; - *ptr++ = (char)toupper(*ipp++ & 255); - } - else - *ptr++ = *ipp++; - } - - *ptr = '\0'; -} - - -static void -add_mimetype_attributes(char *cluster_name, - ipp_t **merged_attributes) -{ - int count, i; - remote_printer_t *p; - const char *str; - char *q; - cups_array_t *list; - ipp_attribute_t *attr; - int num_value, attr_no; - char* attributes[] = - { - "document-format-supported" - }; - - for (attr_no = 0; attr_no < 1; attr_no++) - { - if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return; - - num_value = 0; - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no], - IPP_TAG_MIMETYPE)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - str = ippGetString(attr, i, NULL); - if (!cupsArrayFind(list, (void *)str)) - { - cupsArrayAdd(list, (void *)str); - num_value++; - } - } - } - } - if (num_value != 0) - { - char *values[num_value]; - for (q = (char *)cupsArrayFirst(list),i=0; - q; - q = (char *)cupsArrayNext(list),i++) - { - values[i]=malloc(sizeof(char) * (strlen(q) + 1)); - snprintf(values[i], strlen(q) + 1, "%s", q); - } - ippAddStrings(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_MIMETYPE, - attributes[attr_no], num_value, NULL, - (const char * const *)values); - - for (int k = 0; k < i; k++) - { - free(values[k]); - values[k] = NULL; - } - } - cupsArrayDelete(list); - list = NULL; - } -} - - -// add_tagzero_attributes - Adds attribute to the merged_attribute variable for -// the cluster. This function adds attribute with value -// tag IPP_TAG_ZERO -static void -add_tagzero_attributes(char* cluster_name, - ipp_t **merged_attributes) -{ - int count, i; - remote_printer_t *p; - const char *str; - char *q; - cups_array_t *list; - ipp_attribute_t *attr; - int num_value, attr_no; - char* attributes[] = - { - "media-supported", - "output-bin-supported", - "print-content-optimize-supported", - "print-rendering-intent-supported", - "print-scaling-supported" - }; - - for (attr_no = 0; attr_no < 5; attr_no++) - { - // Cups Array to store the values for the attribute - if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return ; - - num_value = 0; - // Iterating over all the printers in the cluster - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no], - IPP_TAG_ZERO)) != NULL) - { - count = ippGetCount(attr); - for(i = 0; i < count; i ++) - { - // Pick next format from attribute - str = ippGetString(attr, i, NULL); - // Add format to list, skip duplicates - if (!cupsArrayFind(list, (void *)str)) - { - cupsArrayAdd(list, (void *)str); - num_value ++; - } - } - } - } - if (num_value != 0) - { - char *values[num_value]; - // Transferring attributes value from cups Array to char* array - for (q = (char *)cupsArrayFirst(list), i = 0; q; - q = (char *)cupsArrayNext(list), i ++) - { - values[i] = malloc(sizeof(char) * (strlen(q) + 1)); - snprintf(values[i], strlen(q) + 1, "%s", q); - } - ippAddStrings(*merged_attributes, IPP_TAG_PRINTER, - IPP_TAG_KEYWORD, attributes[attr_no], - num_value, NULL, - (const char * const *)values); - - for (int k = 0; k < i; k++) - { - free(values[k]); - values[k] = NULL; - } - } - cupsArrayDelete(list); - list = NULL; - } -} - -// add_keyword_attributes - Adds attributes to the merged_attribute variable for -// the cluster. This function adds attributes with -// value tag IPP_TAG_KEYWORD -static void -add_keyword_attributes(char* cluster_name, - ipp_t **merged_attributes) -{ - int count, i; - remote_printer_t *p; - const char *str; - char *q; - cups_array_t *list; - ipp_attribute_t *attr; - int num_value, attr_no; - char* attributes[] = - { - "output-mode-supported", - "urf-supported", - "pwg-raster-document-type-supported", - "media-source-supported", - "media-type-supported", - "print-color-mode-supported", - "sides-supported" - }; - - for (attr_no = 0; attr_no < 7; attr_no ++) - { - if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return; - - num_value = 0; - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no], - IPP_TAG_KEYWORD)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i++) - { - str = ippGetString(attr, i, NULL); - if (!cupsArrayFind(list, (void *)str)) - { - cupsArrayAdd(list, (void *)str); - num_value ++; - } - } - } - } - if (num_value != 0) - { - char *values[num_value]; - for (q = (char *)cupsArrayFirst(list), i=0; - q; - q = (char *)cupsArrayNext(list), i ++) - { - values[i] = malloc(sizeof(char) * (strlen(q) + 1)); - snprintf(values[i], strlen(q) + 1, "%s", q); - } - ippAddStrings(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - attributes[attr_no], num_value, NULL, - (const char * const *)values); - - for (int k = 0; k < i; k++) - { - free(values[k]); - values[k] = NULL; - } - } - cupsArrayDelete(list); - list = NULL; - } -} - - -// add_enum_attributes - Adds attributes to the merged_attribute variable for -// the cluster. This function adds attributes with value -// tag IPP_TAG_BEGIN_ENUM -static void -add_enum_attributes(char* cluster_name, - ipp_t **merged_attributes) -{ - int count, i, value; - remote_printer_t *p; - char *str = NULL; - char *q; - cups_array_t *list; - ipp_attribute_t *attr; - int num_value, attr_no; - char* attributes[] = { - "finishings-supported", - "print-quality-supported", - "finishing-template", - "finishings-col-database" - }; - - for (attr_no = 0; attr_no < 4; attr_no ++) - { - if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return ; - str = malloc(sizeof(char) * 10); - num_value = 0; - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no], - IPP_TAG_ENUM)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - sprintf(str,"%d",value); - if (!cupsArrayFind(list, (void *)str)) - { - cupsArrayAdd(list, (void *)str); - num_value++; - } - } - } - } - - if (num_value != 0) - { - int values[num_value]; - for (q = (char *)cupsArrayFirst(list), i = 0; q; - q = (char *)cupsArrayNext(list), i++) - values[i] = atoi(q); - ippAddIntegers(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_ENUM, - attributes[attr_no], num_value,values); - } - - if (str != NULL) - { - free(str); - str = NULL; - } - cupsArrayDelete(list); - list = NULL; - } -} - - -// add_margin_attribute - Adds margin attributes to the merged_attribute -// variable for the cluster. -static void -add_margin_attributes(char* cluster_name, - ipp_t **merged_attributes) -{ - int count, i, value; - remote_printer_t *p; - char *str; - char *q; - cups_array_t *list; - ipp_attribute_t *attr; - int num_value, attr_no; - char* attributes[] = - { - "media-bottom-margin-supported", - "media-left-margin-supported", - "media-top-margin-supported", - "media-right-margin-supported" - }; - - for (attr_no = 0; attr_no < 4; attr_no++) - { - if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return ; - str = malloc(sizeof(char)*10); - num_value = 0; - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no], - IPP_TAG_INTEGER)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i++) - { - value = ippGetInteger(attr, i); - sprintf(str,"%d",value); - if (!cupsArrayFind(list, (void *)str)) - { - cupsArrayAdd(list, (void *)str); - num_value++; - } - } - } - } - - if (num_value != 0) - { - int values[num_value]; - for (q = (char *)cupsArrayFirst(list),i=0; q; - q = (char *)cupsArrayNext(list),i++) - values[i] = atoi(q); - ippAddIntegers(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_INTEGER, - attributes[attr_no], num_value,values); - } - - if (str != NULL) - { - free(str); - str = NULL; - } - cupsArrayDelete(list); - list = NULL; - } -} - - -// add_resolution_attributes - Adds resolution attributes to the -// merged_attribute for the cluster -static void -add_resolution_attributes(char* cluster_name, - ipp_t **merged_attributes) -{ - int count, i; - remote_printer_t *p; - ipp_attribute_t *attr; - int num_resolution, attr_no; - cups_array_t *res_array; - cf_res_t *res, *resolution; - char* attributes[] = { - "printer-resolution-supported", - "pwg-raster-document-resolution-supported", - "pclm-source-resolution-supported" - }; - - for (attr_no = 0; attr_no < 3; attr_no ++) - { - res_array = NULL; - res_array = cfNewResolutionArray(); - num_resolution = 0; - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no], - IPP_TAG_RESOLUTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - if ((res = cfIPPResToResolution(attr, i)) != NULL) - { - if (cupsArrayFind(res_array, res) == NULL) - { - cupsArrayAdd(res_array, res); - num_resolution ++; - } - cfFreeResolution(res, NULL); - } - } - } - } - if (num_resolution) - { - int xres[num_resolution], yres[num_resolution]; - for (i = 0, resolution = cupsArrayFirst(res_array); resolution; - i ++, resolution = cupsArrayNext(res_array)) - { - xres[i] = resolution->x; - yres[i] = resolution->y; - } - ippAddResolutions(*merged_attributes, IPP_TAG_PRINTER, - attributes[attr_no], num_resolution, - IPP_RES_PER_INCH, xres, yres); - } - cupsArrayDelete(res_array); - res_array = NULL; - } -} - - -// add_mediasize_attribute - Adds media sizes to the merged_attribute for the -// printer -static void -add_mediasize_attributes(char* cluster_name, - ipp_t **merged_attributes) -{ - int count, i = 0; - remote_printer_t *p; - ipp_attribute_t *attr, *media_size_supported, *x_dim, *y_dim; - int num_sizes, attr_no, num_ranges; - ipp_t *media_size; - cups_array_t *sizes, *size_ranges; - media_size_t *temp, *media_s; - pagesize_range_t *temp_range = NULL, *range = NULL; - char* attributes[] = { - "media-size-supported", - }; - - sizes = cupsArrayNew3((cups_array_func_t)compare_mediasize, NULL, NULL, 0, - (cups_acopy_func_t)copy_media_size, - (cups_afree_func_t)free); - size_ranges = cupsArrayNew3((cups_array_func_t)compare_rangesize, NULL, NULL, - 0, - (cups_acopy_func_t)copy_range_size, - (cups_afree_func_t)free); - temp = (media_size_t *)malloc(sizeof(media_size_t)); - temp_range = (pagesize_range_t *)malloc(sizeof(pagesize_range_t)); - for (attr_no = 0; attr_no < 1; attr_no ++) - { - num_sizes = 0; - num_ranges = 0; - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no], - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - media_size = ippGetCollection(attr, i); - x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO); - y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO); - if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || - ippGetValueTag(y_dim) == IPP_TAG_RANGE) - { - if (ippGetValueTag(x_dim) == IPP_TAG_RANGE) - temp_range->x_dim_min = ippGetRange(x_dim, 0, - &temp_range->x_dim_max); - else - temp_range->x_dim_min = temp_range->x_dim_max = - ippGetInteger(x_dim, 0); - - if (ippGetValueTag(y_dim) == IPP_TAG_RANGE) - temp_range->y_dim_min = ippGetRange(y_dim, 0, - &temp_range->y_dim_max); - else - temp_range->y_dim_min = temp_range->y_dim_max = - ippGetInteger(y_dim, 0); - if (!cupsArrayFind(size_ranges, temp_range)) - { - cupsArrayAdd(size_ranges, temp_range); - num_ranges++; - } - } - else - { - temp->x = ippGetInteger(x_dim, 0); - temp->y = ippGetInteger(y_dim, 0); - if (!cupsArrayFind(sizes, temp)) - { - cupsArrayAdd(sizes, temp); - num_sizes++; - } - } - } - } - } - media_size_supported = - ippAddCollections(*merged_attributes, - IPP_TAG_PRINTER, attributes[attr_no], - num_sizes+num_ranges, NULL); - if (num_sizes) - { - for (i = 0, media_s = cupsArrayFirst(sizes); - media_s; i ++, media_s = cupsArrayNext(sizes)) - { - ipp_t *size = create_media_size(media_s->x, media_s->y); - ippSetCollection(*merged_attributes, &media_size_supported, i, size); - ippDelete(size); - } - } - if (num_ranges) - { - for (range = cupsArrayFirst(size_ranges); range; - i++, range = cupsArrayNext(size_ranges)) - { - ipp_t *size_range = create_media_range(range->x_dim_min, - range->x_dim_max, - range->y_dim_min, - range->y_dim_max); - ippSetCollection(*merged_attributes, &media_size_supported, i, - size_range); - ippDelete(size_range); - } - } - } - - free(temp); - free(temp_range); - cupsArrayDelete(sizes); - cupsArrayDelete(size_ranges); -} - - -// add_mediadatabase_attribute - Adds media-col-database attributes for the -// cluster -static void -add_mediadatabase_attributes(char* cluster_name, - ipp_t **merged_attributes) -{ - int count, i; - remote_printer_t *p; - ipp_attribute_t *attr, *media_attr; - int num_database, attr_no; - cups_array_t *media_database; - media_col_t *temp, *media_data; - ipp_t *media_col, - *media_size, *current_media; - ipp_attribute_t *media_col_database; - char media_source[32], media_type[32]; - char* attributes[] = { - "media-col-database", - }; - temp = (media_col_t *)malloc(sizeof(media_col_t)); - media_database = cupsArrayNew3((cups_array_func_t)compare_media, - NULL, NULL, 0, - (cups_acopy_func_t)copy_media, - (cups_afree_func_t)free); - for (attr_no = 0; attr_no < 1; attr_no ++) - { - num_database = 0; - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no], - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - media_col = ippGetCollection(attr, i); - media_size = - ippGetCollection(ippFindAttribute(media_col, - "media-size", - IPP_TAG_BEGIN_COLLECTION), 0); - temp->x = ippGetInteger(ippFindAttribute(media_size, "x-dimension", - IPP_TAG_ZERO),0); - temp->y = ippGetInteger(ippFindAttribute(media_size, "y-dimension", - IPP_TAG_ZERO),0); - temp->top_margin = - ippGetInteger(ippFindAttribute(media_col, - "media-top-margin", - IPP_TAG_INTEGER), - 0); - temp->bottom_margin = - ippGetInteger(ippFindAttribute(media_col, - "media-bottom-margin", - IPP_TAG_INTEGER), - 0); - temp->left_margin = - ippGetInteger(ippFindAttribute(media_col, - "media-left-margin", - IPP_TAG_INTEGER), - 0); - temp->right_margin = - ippGetInteger(ippFindAttribute(media_col, - "media-right-margin", - IPP_TAG_INTEGER), - 0); - media_type[0] = '\0'; - media_source[0] = '\0'; - temp->media_source = NULL; - temp->media_type = NULL; - if ((media_attr = ippFindAttribute(media_col, - "media-type", - IPP_TAG_KEYWORD)) != NULL) - pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_type, - sizeof(media_type)); - if (strlen(media_type) > 1) - { - temp->media_type = (char*)malloc(sizeof(char) * 32); - strcpy(temp->media_type, media_type); - } - if ((media_attr = ippFindAttribute(media_col, "media-source", - IPP_TAG_KEYWORD)) != NULL) - { - pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_source, - sizeof(media_source)); - } - if(strlen(media_source) > 1) - { - temp->media_source = (char*)malloc(sizeof(char) * 32); - strcpy(temp->media_source, media_source); - } - - if (!cupsArrayFind(media_database, temp)) - { - cupsArrayAdd(media_database, temp); - num_database ++; - } - } - } - } - - if (num_database != 0) - { - media_col_database = ippAddCollections(*merged_attributes, - IPP_TAG_PRINTER, - attributes[attr_no], - num_database, NULL); - for (i = 0, media_data = cupsArrayFirst(media_database); media_data; - i ++, media_data = cupsArrayNext(media_database)) - { - current_media = create_media_col(media_data->x, media_data->y, - media_data->left_margin, - media_data->right_margin, - media_data->top_margin, - media_data->bottom_margin, - media_data->media_source, - media_data->media_type); - ippSetCollection(*merged_attributes, &media_col_database, i, - current_media); - ippDelete(current_media); - } - } - } - - free(temp); - cupsArrayDelete(media_database); -} - - -// add_jobpresets_attribute - Adds presets attributes for the cluster -static void -add_jobpresets_attribute(char* cluster_name, - ipp_t ** merged_attributes) -{ - int count, i, num_preset = 0, preset_no = 0; - remote_printer_t *p; - cups_array_t *list, *added_presets; - ipp_t *preset; - ipp_attribute_t *attr; - const char *preset_name; - ipp_attribute_t *preset_attribute; - - if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return; - - if ((added_presets = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return; - - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, "job-presets-supported", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - preset = ippGetCollection(attr, i); - preset_name = ippGetString(ippFindAttribute(preset, "preset-name", - IPP_TAG_ZERO), 0, NULL); - if (!cupsArrayFind(list, (void *)preset_name)) - { - cupsArrayAdd(list, (void *)preset_name); - num_preset++; - } - } - } - } - - if (num_preset == 0) - { - cupsArrayDelete(list); - cupsArrayDelete(added_presets); - return; - } - - preset_attribute = ippAddCollections(*merged_attributes, IPP_TAG_PRINTER, - "job-presets-supported", num_preset, - NULL); - - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if ((attr = ippFindAttribute(p->prattrs, "job-presets-supported", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - preset = ippGetCollection(attr, i); - preset_name = ippGetString(ippFindAttribute(preset, "preset-name", - IPP_TAG_ZERO), 0, NULL); - if (!cupsArrayFind(added_presets, (void *)preset_name)) - { - cupsArrayAdd(added_presets, (void *)preset_name); - ippSetCollection(*merged_attributes, &preset_attribute, i, preset); - preset_no++; - } - else - continue; - } - } - } - - cupsArrayDelete(list); - cupsArrayDelete(added_presets); -} - - -// get_pagesize - Function returns the standard/custom page size using -// the cfGenerateSizes() function from libcupsfilters -static cups_array_t * -get_pagesize(ipp_t *printer_attributes) -{ - cups_array_t *sizes, *page_media; - cups_size_t *size; - char *ppdsizename, *ptr; - - ppdsizename = (char *)malloc(sizeof(char) * 128); - cfGenerateSizes(printer_attributes, CF_GEN_SIZES_DEFAULT, - &sizes, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL); - if ((page_media = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - for (size = (cups_size_t *)cupsArrayFirst(sizes); size; - size = (cups_size_t *)cupsArrayNext(sizes)) - { - strcpy(ppdsizename, size->media); - if ((ptr = strchr(ppdsizename, ' ')) != NULL) - *ptr = '\0'; - cupsArrayAdd(page_media, ppdsizename); - } - free(ppdsizename); - cupsArrayDelete(sizes); - - return (page_media); -} - - -// get_mediadata - This function extracts the MediaType, InputSlot and OutputBin -// supported, using IPP Response message of the printer -static cups_array_t * -get_mediadata(ipp_t *printer_attributes, - char* requested_attr) -{ - ipp_attribute_t *attr; - int count, i; - cups_array_t *media_data; - const char *keyword; // Keyword value - char ppdname[41]; - char requested_option[30]; - - if (!strcmp(requested_attr, "MediaType")) - strcpy(requested_option, "media-type-supported"); - else if (!strcmp(requested_attr, "InputSlot")) - strcpy(requested_option, "media-source-supported"); - else if (!strcmp(requested_attr, "OutputBin")) - strcpy(requested_option, "output-bin-supported"); - else - return (NULL); - - if ((media_data = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, requested_option, - IPP_TAG_ZERO)) != NULL - && (count = ippGetCount(attr)) > 1) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - pwg_ppdize_name(keyword, ppdname, sizeof(ppdname)); - cupsArrayAdd(media_data, ppdname); - } - } - return (media_data); -} - - -#if 0 -// get_mimetype_attributes - Adds attributes to the merged_attribute -// variable for the cluster. This function -// adds attribute with value tag -// IPP_TAG_MIMETYPE -static cups_array_t * -get_mimetype_attributes(ipp_t *printer_attributes) -{ - int count, i; - const char *str; - cups_array_t *document_formats; - ipp_attribute_t *attr; - - if ((document_formats = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - - if ((attr = ippFindAttribute(printer_attributes, "document-format-supported", - IPP_TAG_MIMETYPE)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - str = ippGetString(attr, i, NULL); - if (!cupsArrayFind(document_formats, (void *)str)) - cupsArrayAdd(document_formats, (void *)str); - } - } - return (document_formats); -} -#endif // 0 - - -// get_staplelocation: This function returns the supported staple locations of -// the printer -static cups_array_t * -get_staplelocation(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - int count, value, i; - const char *name; - cups_array_t *staplelocation; - - if ((staplelocation = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "finishings-supported", - IPP_TAG_ENUM)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - name = ippEnumString("finishings", value); - if (!strncmp(name, "staple-", 7) || !strncmp(name, "bind-", 5) || - !strncmp(name, "edge-stitch-", 12) || !strcmp(name, "saddle-stitch")) - if (!cupsArrayFind(staplelocation, (void*)name)) - cupsArrayAdd(staplelocation, (void*)name); - } - } - return (staplelocation); -} - - -// get_foldtype - Function returns the supported foldtype for the printer -static cups_array_t * -get_foldtype(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - int count, value, i; - const char *name; - cups_array_t *foldtype; - - if ((foldtype = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "finishings-supported", - IPP_TAG_ENUM)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - name = ippEnumString("finishings", value); - if (!strncmp(name, "fold-", 5)) - if (!cupsArrayFind(foldtype, (void*)name)) - cupsArrayAdd(foldtype, (void*)name); - } - } - return (foldtype); -} - - -// get_finishings - Function returns the supported finishings for the printer -static cups_array_t * -get_finishings(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - int count, value, i; - const char *name; - cups_array_t *finishings; - - if ((finishings = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "finishings-supported", - IPP_TAG_ENUM)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - name = ippEnumString("finishings", value); - if (!cupsArrayFind(finishings, (void*)name)) - cupsArrayAdd(finishings, (void*)name); - } - } - return (finishings); -} - - -// get_punchmedia - Returns the puchmedia supported by the printer -static cups_array_t * -get_punchmedia(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - int count, value, i; - const char *name; - cups_array_t *punchmedia; - - if ((punchmedia = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "finishings-supported", - IPP_TAG_ENUM)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - value = ippGetInteger(attr, i); - name = ippEnumString("finishings", value); - if (!strncmp(name, "punch-", 6)) - if (!cupsArrayFind(punchmedia, (void*)name)) - cupsArrayAdd(punchmedia, (void*)name); - } - } - return (punchmedia); -} - - -// get_duplex - Function returns whether the printer support Duplex, -// DuplexTumble, DuplexNoTumble using attributes returned by the -// IPP Request -static cups_array_t * -get_duplex(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - int count, i; - cups_array_t *duplex_options; - const char *str; - - if ((duplex_options = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "sides-supported", - IPP_TAG_KEYWORD)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - str = ippGetString(attr, i, NULL); - if (!strcmp(str, "one-sided")) - cupsArrayAdd(duplex_options, "None"); - else if (!strcmp(str, "two-sided-long-edge")) - cupsArrayAdd(duplex_options, "DuplexNoTumble"); - else if (!strcmp(str, "two-sided-short-edge")) - cupsArrayAdd(duplex_options, "DuplexTumble"); - } - } - return (duplex_options); -} - - -// get_colormodel - Returns the colormodel supported by the printer -static cups_array_t * -get_colormodel(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - int count, i; - cups_array_t *colormodel; - const char *keyword; - int have_bi_level = 0, - have_mono = 0; - - if ((colormodel = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "urf-supported", - IPP_TAG_KEYWORD)) == NULL) - if ((attr = ippFindAttribute(printer_attributes, - "pwg-raster-document-type-supported", - IPP_TAG_KEYWORD)) == NULL) - if ((attr = ippFindAttribute(printer_attributes, - "print-color-mode-supported", - IPP_TAG_KEYWORD)) == NULL) - attr = ippFindAttribute(printer_attributes, "output-mode-supported", - IPP_TAG_KEYWORD); - - if (attr && ippGetCount(attr) > 0) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - if (!have_bi_level && (!strcasecmp(keyword, "black_1") || - !strcmp(keyword, "bi-level") || - !strcmp(keyword, "process-bi-level"))) - { - cupsArrayAdd(colormodel, "FastGray"); - have_bi_level = 1; - } - else if (!have_mono && (!strcasecmp(keyword, "sgray_8") || - !strncmp(keyword, "W8", 2) || - !strcmp(keyword, "monochrome") || - !strcmp(keyword, "process-monochrome"))) - { - have_mono = 1; - cupsArrayAdd(colormodel, "Gray"); - } - else if (!strcasecmp(keyword, "sgray_16") || - !strncmp(keyword, "W8-16", 5) || !strncmp(keyword, "W16", 3)) - cupsArrayAdd(colormodel, "Gray16"); - else if (!strcasecmp(keyword, "srgb_8") || - !strncmp(keyword, "SRGB24", 6) || !strcmp(keyword, "color")) - cupsArrayAdd(colormodel, "RGB"); - else if ((!strcasecmp(keyword, "srgb_16") || - !strncmp(keyword, "SRGB48", 6)) && - !ippContainsString(attr, "srgb_8")) - cupsArrayAdd(colormodel, "RGB"); - else if (!strcasecmp(keyword, "adobe-rgb_16") || - !strncmp(keyword, "ADOBERGB48", 10) || - !strncmp(keyword, "ADOBERGB24-48", 13)) - cupsArrayAdd(colormodel, "AdobeRGB"); - else if ((!strcasecmp(keyword, "adobe-rgb_8") || - !strcmp(keyword, "ADOBERGB24")) && - !ippContainsString(attr, "adobe-rgb_16")) - cupsArrayAdd(colormodel, "AdobeRGB"); - else if ((!strcasecmp(keyword, "black_8") && - !ippContainsString(attr, "black_16")) || - !strcmp(keyword, "DEVW8")) - cupsArrayAdd(colormodel, "DeviceGray"); - else if (!strcasecmp(keyword, "black_16") || - !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16")) - cupsArrayAdd(colormodel, "DeviceGray"); - else if ((!strcasecmp(keyword, "cmyk_8") && - !ippContainsString(attr, "cmyk_16")) || - !strcmp(keyword, "DEVCMYK32")) - cupsArrayAdd(colormodel, "CMYK"); - else if (!strcasecmp(keyword, "cmyk_16") || - !strcmp(keyword, "DEVCMYK32-64") || - !strcmp(keyword, "DEVCMYK64")) - cupsArrayAdd(colormodel, "CMYK"); - else if ((!strcasecmp(keyword, "rgb_8") && - !ippContainsString(attr, "rgb_16")) || - !strcmp(keyword, "DEVRGB24")) - cupsArrayAdd(colormodel, "DeviceRGB"); - else if (!strcasecmp(keyword, "rgb_16") || - !strcmp(keyword, "DEVRGB24-48") || - !strcmp(keyword, "DEVRGB48")) - cupsArrayAdd(colormodel, "DeviceRGB"); - } - } - return (colormodel); -} - - -// get_printquality - Returns the print qualities supported by the printer -static cups_array_t * -get_printquality(ipp_t *printer_attributes) -{ - ipp_attribute_t *quality; - cups_array_t *print_qualities; - - if ((print_qualities = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((quality=ippFindAttribute(printer_attributes, "print-quality-supported", - IPP_TAG_ENUM)) != NULL) - { - if (ippContainsInteger(quality, IPP_QUALITY_DRAFT)) - cupsArrayAdd(print_qualities, "3"); - if (ippContainsInteger(quality, IPP_QUALITY_HIGH)) - cupsArrayAdd(print_qualities, "5"); - cupsArrayAdd(print_qualities, "4"); - } - return (print_qualities); -} - - -// get_job_data - Returns the job_sheets,multiple-document-handling supported -// by the printer -static cups_array_t * -get_job_data(ipp_t *printer_attributes, - char* requested_attr) -{ - ipp_attribute_t *attr; - cups_array_t *job_data; - int i, count; - const char* str; - - if ((job_data = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, requested_attr, - IPP_TAG_KEYWORD)) != NULL) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - str = ippGetString(attr, i, NULL); - if (!cupsArrayFind(job_data, (void *)str)) - cupsArrayAdd(job_data, (void*)str); - } - } - return (job_data); -} - - -// get_finishingtemplate - Returns the Finishing Templates supported by the -// printer -static cups_array_t * -get_finishingtemplate(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - cups_array_t *finishing_templates; - ipp_t *finishing_col; // Current finishing collection - int count, i; - const char *keyword; - - if ((finishing_templates = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "finishings-col-database", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - count = ippGetCount(attr); - for (i = 0; i < count; i ++) - { - finishing_col = ippGetCollection(attr, i); - keyword = ippGetString(ippFindAttribute(finishing_col, - "finishing-template", - IPP_TAG_ZERO), 0, NULL); - if (!keyword || cupsArrayFind(finishing_templates, (void *)keyword)) - continue; - if (strncmp(keyword, "fold-", 5) && (strstr(keyword, "-bottom") || - strstr(keyword, "-left") || - strstr(keyword, "-right") || - strstr(keyword, "-top"))) - continue; - cupsArrayAdd(finishing_templates, (void*)keyword); - } - } - return (finishing_templates); -} - - -// get_printing_data - Returns the print-content-optimize,print-rendering-intent -// and print-scaling attributes for the printer -static cups_array_t * -get_printing_data(ipp_t *printer_attributes, - char* requested_attr) -{ - ipp_attribute_t *attr; - int count, i; - cups_array_t *printing_support; - const char *keyword; - char requested_option[40]; - - if(!strcmp(requested_attr, "print-content-optimize")) - strcpy(requested_option, "print-content-optimize-supported"); - else if (!strcmp(requested_attr, "print-rendering-intent")) - strcpy(requested_option, "print-rendering-intent-supported"); - else if(!strcmp(requested_attr, "print-scaling")) - strcpy(requested_option, "print-scaling-supported"); - else if (!strcmp(requested_attr, "job-sheets-supported")) - strcpy(requested_option, "job-sheets-supported"); - else - return (NULL); - - if ((printing_support = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, requested_option, - IPP_TAG_ZERO)) != NULL && - (count = ippGetCount(attr)) > 1) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - cupsArrayAdd(printing_support, (void *)keyword); - } - } - return (printing_support); -} - - -// get_presets - Returns a list of presets name supported by the printer -static cups_array_t * -get_presets(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - int count, i; - cups_array_t *presets; - ipp_t *preset; - const char *preset_name; - - if ((presets = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "job-presets-supported", - IPP_TAG_BEGIN_COLLECTION)) != NULL && - (count = ippGetCount(attr)) > 1) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - preset = ippGetCollection(attr, i); - preset_name = ippGetString(ippFindAttribute(preset, "preset-name", - IPP_TAG_ZERO), 0, NULL); - if(!cupsArrayFind(presets, (void*)preset_name)) - cupsArrayAdd(presets, (void *)preset_name); - } - } - return (presets); -} - - -// get_booklet - Returns True if booklet is supported -static cups_array_t * -get_booklet(ipp_t *printer_attributes) -{ - ipp_attribute_t *attr; - cups_array_t *booklet; - - if ((booklet = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - if ((attr = ippFindAttribute(printer_attributes, "finishings-supported", - IPP_TAG_ENUM)) != NULL) - { - if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER)) - { - // Assuming that the printer which supports Booklet also supports - // printing without Booklet, so for this printer we will return - // both "True" and "False" - cupsArrayAdd(booklet, "True"); - } - } - cupsArrayAdd(booklet, "False"); - return (booklet); -} - - -// get_supported_options - Function returns various attributes supported by the -// printer, such as PageSize,ColorModel etc. -static cups_array_t * -get_supported_options(ipp_t *printer_attributes, - char* option) -{ - if (!strcmp(option, "PageSize") || !strcmp(option, "PageRegion")) - return (get_pagesize(printer_attributes)); - else if (!strcmp(option, "MediaType") || !strcmp(option, "InputSlot") || - !strcmp(option, "OutputBin")) - return (get_mediadata(printer_attributes, option)); - else if (!strcmp(option, "StapleLocation")) - return (get_staplelocation(printer_attributes)); - else if (!strcmp(option, "FoldType")) - return (get_foldtype(printer_attributes)); - else if (!strcmp(option, "PunchMedia")) - return (get_punchmedia(printer_attributes)); - else if (!strcmp(option, "cupsFinishingTemplate")) - return (get_finishingtemplate(printer_attributes)); - else if (!strcmp(option, "cupsPrintQuality")) - return (get_printquality(printer_attributes)); - else if (!strcmp(option, "job-sheets-supported") || - !strcmp(option, "print-content-optimize") || - !strcmp(option, "print-rendering-intent") || - !strcmp(option, "print-scaling")) - return (get_printing_data(printer_attributes, option)); - else if (!strcmp(option, "APPrinterPreset")) - return (get_presets(printer_attributes)); - else if(!strcmp(option, "Booklet")) - return (get_booklet(printer_attributes)); - else if(!strcmp(option, "ColorModel")) - return (get_colormodel(printer_attributes)); - else if (!strcmp(option, "Duplex")) - return (get_duplex(printer_attributes)); - else if (!strcmp(option, "multiple-document-handling-supported") || - !strcmp(option, "cover-back-supported") || - !strcmp(option, "cover-front-supported") || - !strcmp(option, "cover-type-supported") || - !strcmp(option, "media-type-supported")) - return (get_job_data(printer_attributes, option)); - else if (!strcmp(option,"finishings-supported")) - return (get_finishings(printer_attributes)); - return (NULL); -} - - -// check_printer_with_options - Checks whether a printer in an cluster supports -// option1 for keyword at value idx_option1 in -// ppd_keywords[] and option2 for keyword at value -// idx_option2 -static int -check_printer_with_options(char* cluster_name, int idx_option1, - char* option1, int idx_option2, char* option2) -{ - remote_printer_t *p; - cups_array_t *first_attributes_value; - cups_array_t *second_attributes_value; - char *borderless_pagesize = NULL; - int option1_is_size = 0, option2_is_size = 0; - unsigned long int max_length = 0, option1_len = 0, option2_len = 0, - t_len = 0; - char t[] = ".Borderless"; - - t_len = strlen(t); - if (option1) - option1_len = strlen(option1); - if (option2) - option2_len = strlen(option2); - - // Seems to be possible to have both options... - max_length = option1_len + option2_len + (2 * t_len) + 1; - - borderless_pagesize = (char *)malloc(sizeof(char) * max_length); - if (borderless_pagesize == NULL) - { - debug_printf("check_printer_with_options: Run out of memory.\n"); - return (0); - } - memset(borderless_pagesize, 0, max_length); - - if (!strcmp(ppd_keywords[idx_option1], "PageSize") || - !strcmp(ppd_keywords[idx_option1], "PageRegion")) - { - // Check that we are generating .Borderless for the correct size, i.e We - // are generating 4x5.Borderless for 4x5 and not generating - // 4x5.Borderless.Borderless for 4x5.Borderless - if (option1_len >= 11 && - !strcmp(&option1[option1_len - t_len], t)) - { - } - else - { - strcat(borderless_pagesize, option1); - strcat(borderless_pagesize, t); - option1_is_size = 1; - } - } - if (!strcmp(ppd_keywords[idx_option2], "PageSize") || - !strcmp(ppd_keywords[idx_option2], "PageRegion")) - { - if (option2_len >=11 && - !strcmp(&option2[option2_len - t_len], t)) - { - } - else - { - strcat(borderless_pagesize, option2); - strcat(borderless_pagesize, t); - option2_is_size = 1; - } - } - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - first_attributes_value = get_supported_options(p->prattrs, - ppd_keywords[idx_option1]); - if (cupsArrayFind(first_attributes_value, (void*)option1) || - (option1_is_size && cupsArrayFind(first_attributes_value, - (void*)borderless_pagesize))) - { - second_attributes_value = - get_supported_options(p->prattrs, - ppd_keywords[idx_option2]); - if (cupsArrayFind(second_attributes_value,(void*)option2) || - (option2_is_size && cupsArrayFind(second_attributes_value, - (void*)borderless_pagesize))) - { - free(borderless_pagesize); - return (1); - } - } - } - free(borderless_pagesize); - return (0); -} - - -// The function returns a array containint the sizes supported by the cluster -static cups_array_t * -get_cluster_sizes(char *cluster_name) -{ - cups_array_t *sizes = NULL; - cups_array_t *cluster_sizes = NULL, - *sizes_ppdname; - cups_size_t *size; - remote_printer_t *p; - char pagesize[128]; - char* first_space; - - cluster_sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, - NULL, NULL, 0, - (cups_acopy_func_t)pwg_copy_size, - (cups_afree_func_t)free); - sizes_ppdname = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free); - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (!strcmp(p->queue_name, cluster_name)) - { - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - cfGenerateSizes(p->prattrs, CF_GEN_SIZES_DEFAULT, - &sizes, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL); - for (size = (cups_size_t *)cupsArrayFirst(sizes); - size; size = (cups_size_t *)cupsArrayNext(sizes)) - { - if (!cupsArrayFind(cluster_sizes, size)) - { - strcpy(pagesize, size->media); - if ((first_space = strchr(pagesize, ' ')) != NULL) - *first_space = '\0'; - if (!cupsArrayFind(sizes_ppdname, pagesize)) - { - cupsArrayAdd(cluster_sizes, size); - cupsArrayAdd(sizes_ppdname, pagesize); - } - } - } - - cupsArrayDelete(sizes); - sizes = NULL; - } - } - - cupsArrayDelete(sizes_ppdname); - - return (cluster_sizes); -} - - -// generate_cluster_conflicts - Function generates conflicts for the cluster -static cups_array_t * -generate_cluster_conflicts(char *cluster_name, - ipp_t *merged_attributes) -{ - remote_printer_t *p; - cups_array_t *conflict_pairs = NULL; - int i, k, j, no_of_printers = 0, no_of_ppd_keywords; - cups_array_t *printer_first_options = NULL, - *printer_second_options = NULL; - char *opt1, *opt2, constraint[100], *ppdsizename, *temp; - cups_array_t *sizes = NULL, *pagesizes; - cups_size_t *size; - - // Cups Array to store the conflicts - ppdsizename = (char *)malloc(sizeof(char) * 128); - if ((conflict_pairs = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - - // Storing all the values supported by the cluster in cluster_options - no_of_ppd_keywords = sizeof(ppd_keywords) / sizeof(ppd_keywords[0]); - cups_array_t *cluster_options[no_of_ppd_keywords]; - for(i = 0; i < no_of_ppd_keywords; i ++) - { - if (strcmp(ppd_keywords[i], "PageSize") && - strcmp(ppd_keywords[i], "PageRegion")) - cluster_options[i] = - get_supported_options(merged_attributes,ppd_keywords[i]); - else - { - sizes = get_cluster_sizes(cluster_name); - if ((pagesizes = - cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - return (NULL); - for (size = (cups_size_t *)cupsArrayFirst(sizes); size; - size = (cups_size_t *)cupsArrayNext(sizes)) - { - strcpy(ppdsizename, size->media); - if ((temp = strchr(ppdsizename, ' ')) != NULL) - *temp = '\0'; - cupsArrayAdd(pagesizes, ppdsizename); - } - cluster_options[i] = pagesizes; - - cupsArrayDelete(sizes); - sizes = NULL; - } - } - - // Algorithm to find constraints: We iterate over printer, if we - // find a value for a keyword which is supported by the cluster but - // not by the printer, that value can be part of the conflict. With - // this value v and a new value (for an different keyword, at index - // more than the index of first keyword), we generate a pair (v,u) - // and then we check whether some printer satisfy this pair, if no - // such printer exists then the pair is a conflict, we add it to - // conflict_pairs array - - no_of_printers = cupsArrayCount(remote_printers); - for (j = 0; j < no_of_printers; j ++) - { - p = (remote_printer_t *)cupsArrayIndex(remote_printers, j); - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - for (i = 0; i < no_of_ppd_keywords; i ++) - { - printer_first_options = - get_supported_options(p->prattrs, ppd_keywords[i]); - if (i == 0) - for (opt1 = cupsArrayFirst(cluster_options[i]); opt1; - opt1 = cupsArrayNext(cluster_options[i])) - { - if (cupsArrayFind(printer_first_options, opt1)) - continue; - for (k = i + 1; k < no_of_ppd_keywords; k++) - { - if (!strcmp(ppd_keywords[i], "PageSize") && - !strcmp(ppd_keywords[k], "PageRegion")) - continue; - printer_second_options = get_supported_options(p->prattrs, - ppd_keywords[k]); - for (opt2 = cupsArrayFirst(printer_second_options); opt2; - opt2 = cupsArrayNext(printer_second_options)) - { - if (check_printer_with_options(cluster_name, i, opt1, k, opt2)) - continue; - if (!strcasecmp(opt1, AUTO_OPTION) || - !strcasecmp(opt2, AUTO_OPTION)) - continue; - if (!strcmp(opt1, "Gray") || !strcmp(opt2, "Gray")) - continue; - sprintf(constraint, "*UIConstraints: *%s %s *%s %s\n", - ppd_keywords[i], - opt1,ppd_keywords[k], opt2); - if (!cupsArrayFind(conflict_pairs, constraint)) - cupsArrayAdd(conflict_pairs, constraint); - sprintf(constraint, "*UIConstraints: *%s %s *%s %s\n", - ppd_keywords[k], - opt2, ppd_keywords[i], opt1); - if (!cupsArrayFind(conflict_pairs, constraint)) - cupsArrayAdd(conflict_pairs, constraint); - } - - cupsArrayDelete(printer_second_options); - printer_second_options = NULL; - } - } - - cupsArrayDelete(printer_first_options); - printer_first_options = NULL; - } - } - - for (i = 0; i < no_of_ppd_keywords; i ++) - cupsArrayDelete(cluster_options[i]); - - free(ppdsizename); - return (conflict_pairs); -} - - -// get_cluster_attributes - Returns ipp_t* containing the options supplied by -// all the printers in the cluster, which can be sent -// to ppdCreatePPDFromIPP2() to generate the PPD file -static ipp_t * -get_cluster_attributes(char* cluster_name) -{ - remote_printer_t *p; - ipp_t *merged_attributes = NULL; - char printer_make_and_model[256]; - ipp_attribute_t *attr; - int color_supported = 0, make_model_done = 0, i; - char valuebuffer[65536]; - merged_attributes = ippNew(); - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if (!make_model_done) - { - strcpy(printer_make_and_model, "Cluster "); - strcat(printer_make_and_model, cluster_name); - make_model_done = 1; - } - if (((attr = ippFindAttribute(p->prattrs, "color-supported", - IPP_TAG_BOOLEAN)) != NULL && - ippGetBoolean(attr, 0))) - color_supported = 1; - } - - ippAddString(merged_attributes, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "printer-make-and-model", - NULL, printer_make_and_model); - ippAddBoolean(merged_attributes, IPP_TAG_PRINTER, "color-supported", - color_supported); - - add_keyword_attributes(cluster_name, &merged_attributes); - add_mimetype_attributes(cluster_name, &merged_attributes); - add_tagzero_attributes(cluster_name, &merged_attributes); - add_enum_attributes(cluster_name, &merged_attributes); - add_resolution_attributes(cluster_name, &merged_attributes); - add_margin_attributes(cluster_name, &merged_attributes); - add_mediasize_attributes(cluster_name, &merged_attributes); - add_mediadatabase_attributes(cluster_name, &merged_attributes); - add_jobpresets_attribute(cluster_name, &merged_attributes); - attr = ippFirstAttribute(merged_attributes); - // Printing merged attributes - debug_printf("Merged attributes for the cluster %s : \n", cluster_name); - while (attr) - { - debug_printf(" Attr: %s\n", - ippGetName(attr)); - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - debug_printf(" Value: %s\n", valuebuffer); - const char *kw; - for (i = 0; i < ippGetCount(attr); i ++) - if ((kw = ippGetString(attr, i, NULL)) != NULL) - debug_printf(" Keyword: %s\n", kw); - attr = ippNextAttribute(merged_attributes); - } - return (merged_attributes); -} - - -static int -cluster_supports_given_attribute(char* cluster_name, - ipp_tag_t tag, - const char* attribute) -{ - remote_printer_t *p; - ipp_attribute_t *attr; - int count; - - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(cluster_name, p->queue_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute(p->prattrs, attribute, tag)) != NULL && - (count = ippGetCount(attr)) > 1) - return (1); - } - return (0); -} - - -// Generating the default values for the cluster -static void -get_cluster_default_attributes(ipp_t** merged_attributes, - char* cluster_name, - char* default_pagesize, - const char **default_color) -{ - int max_pages_per_min = 0, pages_per_min; - remote_printer_t *p, *def_printer = NULL; - int i, count; - ipp_attribute_t *attr, *media_attr, *media_col_default; - ipp_t *media_col, - *media_size, *current_media=NULL; - char media_source[32], media_type[32]; - const char *str; - media_col_t *temp; - const char *keyword; - cf_res_t *res; - int xres, yres; - char ppdname[41]; - - // The printer with the maximum Throughtput(pages_per_min) is selected as - // the default printer - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(p->queue_name, cluster_name)) - continue; - if (p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED || - p->status == STATUS_TO_BE_RELEASED) - continue; - if ((attr = ippFindAttribute (p->prattrs, "pages-per-minute", - IPP_TAG_INTEGER)) != NULL) - { - pages_per_min = ippGetInteger (attr, 0); - if (pages_per_min > max_pages_per_min) - { - max_pages_per_min = pages_per_min; - def_printer = p; - } - } - } - - // If none of the printer in the cluster has "pages-per-minute" in the ipp - // response message, then select the first printer in the cluster - if (!def_printer) - { - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (strcmp(p->queue_name, cluster_name)) - continue; - else - { - def_printer = p; - break; - } - } - } - - debug_printf("Selecting printer (%s) as the default for the cluster %s\n", - def_printer->uri, cluster_name); - debug_printf("Default Attributes of the cluster %s are : \n", cluster_name); - - // Generating the default pagesize for the cluster - cfGenerateSizes(def_printer->prattrs, CF_GEN_SIZES_DEFAULT, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - ppdname, NULL); - strcpy(default_pagesize, ppdname); - debug_printf("Default PageSize : %s\n", default_pagesize); - - // Generating the default media-col for the cluster - if ((attr = ippFindAttribute(def_printer->prattrs, "media-col-default", - IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - media_col = ippGetCollection(attr, 0); - media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", - IPP_TAG_BEGIN_COLLECTION), - 0); - temp = (media_col_t *)malloc(sizeof(media_col_t)); - temp->x = ippGetInteger(ippFindAttribute(media_size, "x-dimension", - IPP_TAG_ZERO), 0); - temp->y = ippGetInteger(ippFindAttribute(media_size, "y-dimension", - IPP_TAG_ZERO), 0); - temp->top_margin = ippGetInteger(ippFindAttribute(media_col, - "media-top-margin", - IPP_TAG_INTEGER), 0); - temp->bottom_margin = ippGetInteger(ippFindAttribute(media_col, - "media-bottom-margin", - IPP_TAG_INTEGER), 0); - temp->left_margin = ippGetInteger(ippFindAttribute(media_col, - "media-left-margin", - IPP_TAG_INTEGER), 0); - temp->right_margin = ippGetInteger(ippFindAttribute(media_col, - "media-right-margin", - IPP_TAG_INTEGER), 0); - media_type[0] = '\0'; - media_source[0] = '\0'; - temp->media_source = NULL; - temp->media_type = NULL; - - if ((media_attr = ippFindAttribute(media_col, "media-type", - IPP_TAG_KEYWORD)) != NULL) - pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_type, - sizeof(media_type)); - - if (strlen(media_type) > 1) - { - temp->media_type = (char*)malloc(sizeof(char)*32); - strcpy(temp->media_type, media_type); - debug_printf("Default MediaType: %s\n", media_type); - } - - if (temp->media_type == NULL) - { - if (cluster_supports_given_attribute(cluster_name, IPP_TAG_KEYWORD, - "media-type-supported")) - { - temp->media_type = (char*)malloc(sizeof(char)*32); - strcpy(temp->media_type, AUTO_OPTION); - debug_printf("Default MediaType: " AUTO_OPTION "\n"); - } - } - - if ((media_attr = ippFindAttribute(media_col, "media-source", - IPP_TAG_KEYWORD)) != NULL) - pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_source, - sizeof(media_source)); - - if (strlen(media_source) > 1) - { - temp->media_source = (char*)malloc(sizeof(char)*32); - strcpy(temp->media_source, media_source); - debug_printf("Default MediaSource: %s\n", media_source); - } - - if (temp->media_source == NULL) - { - if (cluster_supports_given_attribute(cluster_name, IPP_TAG_KEYWORD, - "media-source-supported")) - { - temp->media_source = (char*)malloc(sizeof(char) * 32); - strcpy(temp->media_source, AUTO_OPTION); - debug_printf("Default MediaSource: " AUTO_OPTION "\n"); - } - } - - media_col_default = ippAddCollection(*merged_attributes, IPP_TAG_PRINTER, - "media-col-default", NULL); - current_media = create_media_col(temp->x, temp->y, temp->left_margin, - temp->right_margin, temp->top_margin, - temp->bottom_margin, - temp->media_source, temp->media_type); - ippSetCollection(*merged_attributes, &media_col_default, 0, current_media); - - free(temp->media_source); - free(temp->media_type); - free(temp); - ippDelete(current_media); - } - - //Finding the default colormodel for the cluster - if ((attr = ippFindAttribute(def_printer->prattrs, "urf-supported", - IPP_TAG_KEYWORD)) == NULL) - if ((attr = ippFindAttribute(def_printer->prattrs, - "pwg-raster-document-type-supported", - IPP_TAG_KEYWORD)) == NULL) - if ((attr = ippFindAttribute(def_printer->prattrs, - "print-color-mode-supported", - IPP_TAG_KEYWORD)) == NULL) - attr = ippFindAttribute(def_printer->prattrs, "output-mode-supported", - IPP_TAG_KEYWORD); - - if (attr && ippGetCount(attr) > 0) - { - *default_color = NULL; - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - keyword = ippGetString(attr, i, NULL); - if ((!strcasecmp(keyword, "black_1") || - !strcmp(keyword, "bi-level") || - !strcmp(keyword, "process-bi-level"))) - { - if (!*default_color) - *default_color = "FastGray"; - } - else if ((!strcasecmp(keyword, "sgray_8") || - !strncmp(keyword, "W8", 2) || - !strcmp(keyword, "monochrome") || - !strcmp(keyword, "process-monochrome"))) - { - if (!*default_color || !strcmp(*default_color, "FastGray")) - *default_color = "Gray"; - } - else if (!strcasecmp(keyword, "sgray_16") || - !strncmp(keyword, "W8-16", 5) || - !strncmp(keyword, "W16", 3)) - { - if (!*default_color || !strcmp(*default_color, "FastGray")) - *default_color = "Gray16"; - } - else if (!strcasecmp(keyword, "srgb_8") || - !strncmp(keyword, "SRGB24", 6) || - !strcmp(keyword, "color")) - { - *default_color = "RGB"; - } - else if ((!strcasecmp(keyword, "srgb_16") || - !strncmp(keyword, "SRGB48", 6)) && - !ippContainsString(attr, "srgb_8")) - { - *default_color = "RGB"; - } - else if (!strcasecmp(keyword, "adobe-rgb_16") || - !strncmp(keyword, "ADOBERGB48", 10) || - !strncmp(keyword, "ADOBERGB24-48", 13)) - { - if (!*default_color) - *default_color = "AdobeRGB"; - } - else if ((!strcasecmp(keyword, "adobe-rgb_8") || - !strcmp(keyword, "ADOBERGB24")) && - !ippContainsString(attr, "adobe-rgb_16")) - { - if (!*default_color) - *default_color = "AdobeRGB"; - } - } - if (*default_color) - debug_printf("Default ColorModel : %s\n", *default_color); - } - - if ((attr = ippFindAttribute(def_printer->prattrs, "output-bin-default", - IPP_TAG_ZERO)) != NULL) - { - str = ippGetString(attr, 0, NULL); - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "output-bin-default", NULL, str); - debug_printf("Default OutputBin: %s\n", str); - } - else - { - if (cluster_supports_given_attribute(cluster_name,IPP_TAG_ZERO, - "output-bin-supported")) - { - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "output-bin-default", NULL, AUTO_OPTION); - debug_printf("Default OutputBin: %s\n", AUTO_OPTION); - } - } - - if ((attr = ippFindAttribute(def_printer->prattrs, - "print-content-optimize-default", - IPP_TAG_ZERO)) != NULL) - { - str = ippGetString(attr, 0, NULL); - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-content-optimize-default", NULL, str); - debug_printf("Default print-content-optimize: %s\n", str); - } - else - { - if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO, - "print-content-optimize-default")) - { - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-content-optimize-default", NULL, AUTO_OPTION); - debug_printf("Default print-content-optimize: %s\n", AUTO_OPTION); - } - } - - if ((attr = ippFindAttribute(def_printer->prattrs, - "print-rendering-intent-default", - IPP_TAG_ZERO)) != NULL) - { - str = ippGetString(attr, 0, NULL); - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-rendering-intent-default", NULL, str); - debug_printf("Default print-rendering-intent: %s\n", str); - } - else - { - if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO, - "print-rendering-intent-default")) - { - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-rendering-intent-default", NULL, AUTO_OPTION); - debug_printf("Default print-rendering-intent: %s\n", AUTO_OPTION); - } - } - - if ((attr = ippFindAttribute(def_printer->prattrs, "print-scaling-default", - IPP_TAG_ZERO)) != NULL) - { - str = ippGetString(attr, 0, NULL); - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-scaling-default", NULL, str); - debug_printf("Default print-scaling: %s\n",str); - } - else - { - if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO, - "print-scaling-default")) - { - ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "print-scaling-default", NULL, AUTO_OPTION); - debug_printf("Default print-scaling: %s\n", AUTO_OPTION); - } - } - - if ((attr = ippFindAttribute(def_printer->prattrs, - "printer-resolution-default", - IPP_TAG_ZERO)) != NULL) - { - if ((res = cfIPPResToResolution(attr, 0)) != NULL) - { - xres = res->x; - yres = res->y; - ippAddResolution(*merged_attributes, IPP_TAG_PRINTER, - "printer-resolution-default", - IPP_RES_PER_INCH, xres, yres); - debug_printf("Default Resolution : %dx%d\n", xres, yres); - cfFreeResolution(res, NULL); - } - } -} - - -// Function to see which printer in the cluster supports the -// requested job attributes -static int -supports_job_attributes_requested(const gchar* printer, - int printer_index, - int job_id, - int *print_quality) -{ - char uri[1024]; - http_t *http = NULL; - ipp_attribute_t *attr, *attr1; - ipp_t *request, *response = NULL; - const char *str, *side, *resource; - cups_array_t *job_sheet_supported = NULL, - *multiple_doc_supported = NULL, - *print_qualities = NULL, - *media_type_supported = NULL, - *staplelocation_supported = NULL, - *foldtype_supported = NULL, - *punchmedia_supported = NULL, - *color_supported = NULL; - remote_printer_t *p; - int i, count, side_found, orien_req, orien, - orien_found; - cups_array_t *sizes = NULL; - int ret = 1; - - p = (remote_printer_t *)cupsArrayIndex(remote_printers, printer_index); - static const char * const jattrs[] = // Job attributes we want - { - "all" - }; - - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - - // Getting the resource - resource = uri + (strlen(uri) - strlen(printer) - 10); - - http = http_connect_local(); - request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, - uri); - ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", - (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs); - - response = cupsDoRequest(http, request,resource); - attr = ippFirstAttribute(response); - - // Document Format -#if 0 - if ((attr = ippFindAttribute(response, "document-format-detected", - IPP_TAG_MIMETYPE)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The job-document is of the format %s\n.",str); - formats_supported = get_mimetype_attributes(p->prattrs); - if (!cupsArrayFind(formats_supported, (void *)str)) - { - debug_printf("Printer %s doesn't support the document format %s\n", - printer, str); - return (0); - } - } -#endif // 0 - - // Job Sheets - if ((attr = ippFindAttribute(response, "job-sheets", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The job-sheets %s is requested for the job\n", str); - job_sheet_supported = get_supported_options(p->prattrs, - "job-sheets-supported"); - if (str) - { - if (!cupsArrayFind(job_sheet_supported, (void *)str) && - strcasecmp(str,"none")) - { - debug_printf("Printer %s doesn't support the job-sheet %s\n", printer, - str); - ret = 0; - goto cleanup; - } - } - } - - // Multiple document handling - // Can't get multiple-document-handling data from job templates - if ((attr = ippFindAttribute(response, "multiple-document-handling", - IPP_TAG_ZERO)) != NULL && ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The multiple-document-handling type %s is requested\n", str); - if (str) - { - multiple_doc_supported = - get_supported_options(p->prattrs, - "multiple-document-handling-supported"); - if (!cupsArrayFind(multiple_doc_supported, (void *)str)) - { - debug_printf("Printer %s doesn't support the multiple document handling option %s\n", - printer, str); - ret = 0; - goto cleanup; - } - } - } - - // Media Type - if ((attr = ippFindAttribute(response, "MediaType", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The mediatype %s is requested for the job\n", str); - if (str != NULL) - { - media_type_supported = get_supported_options(p->prattrs, - "media-type-supported"); - if (!cupsArrayFind(media_type_supported, (void *)str) && - strcasecmp(str, AUTO_OPTION)) - { - debug_printf("Printer %s doesn't support the media-type %s\n", - printer, str); - ret = 0; - goto cleanup; - } - } - } - - // Staple Location - if ((attr = ippFindAttribute(response, "StapleLocation", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The staple location %s is requested for the job\n", str); - if (str != NULL) - { - staplelocation_supported = - get_supported_options(p->prattrs, "StapleLocation"); - if (!cupsArrayFind(staplelocation_supported, (void *)str) && - strcasecmp(str, "None")) - { - debug_printf("Printer %s doesn't support the staple location %s\n", - printer, str); - ret = 0; - goto cleanup; - } - } - } - - // FoldType - if ((attr = ippFindAttribute(response, "FoldType", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The FoldType %s is requested for the job\n", str); - if (str != NULL) - { - foldtype_supported = get_supported_options(p->prattrs, "FoldType"); - if (!cupsArrayFind(foldtype_supported, (void *)str) && - strcasecmp(str, "None")) - { - debug_printf("Printer %s doesn't support the FoldType %s\n", - printer, str); - ret = 0; - goto cleanup; - } - } - } - - // PunchMedia - if ((attr = ippFindAttribute(response, "PunchMedia", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The PunchMedia %s is requested for the job\n", str); - if (str != NULL) - { - punchmedia_supported = get_supported_options(p->prattrs, "PunchMedia"); - if (!cupsArrayFind(punchmedia_supported, (void *)str) && - strcasecmp(str, "none")) - { - debug_printf("Printer %s doesn't support the PunchMedia %s\n", - printer, str); - ret = 0; - goto cleanup; - } - } - } - - // ColorModel - if ((attr = ippFindAttribute(response, "ColorModel", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - debug_printf("The ColorModel %s is requested for the job\n", str); - if (str != NULL) - { - color_supported = get_supported_options(p->prattrs, "ColorModel"); - if (!cupsArrayFind(color_supported, (void *)str) && - strcasecmp(str,"Gray")) - { - debug_printf("Printer %s doesn't support the ColorModel %s\n", - printer, str); - ret = 0; - goto cleanup; - } - } - } - - // Sides supported - if ((attr = ippFindAttribute(response, "Duplex", - IPP_TAG_ZERO)) != NULL) - { - side_found = 0; - str = ippGetString(attr, 0, NULL); - if (str) - { - if ((attr1 = ippFindAttribute(p->prattrs, "sides-supported", - IPP_TAG_KEYWORD)) != NULL) - { - for (i = 0, count = ippGetCount(attr1); i < count; i++) - { - side = ippGetString(attr1, i, NULL); - debug_printf("The duplex option %s is requested\n", side); - if (!strcasecmp(str, "None") && !strcmp(side, "one-sided")) - { - side_found = 1; - break; - } - else if (!strcmp(str, "DuplexNoTumble") && - !strcmp(side, "two-sided-long-edge")) - { - side_found = 1; - break; - } - else if (!strcmp(str, "DuplexTumble") && - !strcmp(side, "two-sided-short-edge")) - { - side_found = 1; - break; - } - } - if (!side_found) - { - debug_printf("Printer %s doesn't support the required duplex options\n", - printer); - ret = 0; - goto cleanup; - } - } - } - } - - // Orientation Requested - if ((attr = ippFindAttribute(response, "orientation-requested", - IPP_TAG_ENUM)) != NULL) - { - orien_found = 0; - orien_req = ippGetInteger(attr, 0); - if ((attr1 = ippFindAttribute(p->prattrs, - "orientation-requested-supported", - IPP_TAG_ENUM)) != NULL) - { - for (i = 0, count = ippGetCount(attr1); i < count; i ++) - { - orien = ippGetInteger(attr1, i); - if (orien == orien_req) - { - orien_found = 1; - break; - } - } - if (!orien_found) - { - debug_printf("Printer %s doesn't support the requested orientation\n", - printer); - ret = 0; - goto cleanup; - } - } - } - - // Page Size - if ((attr = ippFindAttribute(response, "PageSize", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - str = ippGetString(attr, 0, NULL); - if (str) - { - sizes = get_pagesize(p->prattrs); - if (!cupsArrayFind(sizes, (void*)str)) - { - debug_printf("Printer %s doesn't support %s PageSize\n", p->uri, str); - ret = 0; - goto cleanup; - } - } - } - - // Print Quality - *print_quality = 4; - if ((attr = ippFindAttribute(response, "cupsPrintQuality", - IPP_TAG_ZERO)) != NULL && - ippGetCount(attr) > 0) - { - print_qualities = get_supported_options(p->prattrs, "cupsPrintQuality"); - str = ippGetString(attr, 0, NULL); - debug_printf("%s\n", str); - if (str && !cupsArrayFind(print_qualities, (void*)str)) - { - debug_printf("In\n"); - if(!strcmp(str, "5")) - *print_quality = 5; - else if (!strcmp(str, "3")) - *print_quality = 3; - debug_printf("Printer doesn't support %s print quality\n", - !strcmp(str, "5") ? "HIGH": "DRAFT"); - ret = 0; - goto cleanup; - } - } - - cleanup: - if (response != NULL) - ippDelete(response); - if (job_sheet_supported != NULL) - cupsArrayDelete(job_sheet_supported); - if (multiple_doc_supported) - cupsArrayDelete(multiple_doc_supported); - if (media_type_supported != NULL) - cupsArrayDelete(media_type_supported); - if (staplelocation_supported != NULL) - cupsArrayDelete(staplelocation_supported); - if (foldtype_supported != NULL) - cupsArrayDelete(foldtype_supported); - if (punchmedia_supported != NULL) - cupsArrayDelete(punchmedia_supported); - if (color_supported != NULL) - cupsArrayDelete(color_supported); - if (print_qualities != NULL) - cupsArrayDelete(print_qualities); - if (sizes != NULL) - cupsArrayDelete(sizes); - - return (ret); -} - - -// -// Remove all illegal characters and replace each group of such characters -// by a single separator character (dash or underscore), return a free()-able -// string. -// -// mode = 0: Only allow letters, numbers, dashes, and underscores for -// turning make/model info into a valid print queue name or -// into a string which can be supplied as option value in a -// filter command line without need of quoting. Replace all -// groups of illegal characters by single dashes and remove -// leading and trailing dashes. -// mode = 1: Allow also '/', '.', ',' for cleaning up MIME type -// strings (here available Page Description Languages, PDLs) to -// supply them on a filter command line without quoting. -// Replace all groups of illegal characters by single dashes -// and remove leading and trailing dashes. -// mode = 2: Keep all locale-free alphanumeric characters (a-z, A-Z, 0-9) -// and replace everything else by underscores. Replace all -// groups of illegal characters by single underscores. This is -// for generating print queue names from DNS-SD service names -// to do it exactly as CUPS 2.2.x (or newer) does, so that CUPS -// does not create its own temporary queues in addition. -// -// Especially this prevents from arbitrary code execution by interface scripts -// generated for print queues to native IPP printers when a malicious IPP -// print service with forged PDL and/or make/model info gets broadcasted into -// the local network. -// - -static char * // O - Cleaned string -remove_bad_chars(const char *str_orig, // I - Original string - int mode) // I - 0: Make/Model, queue name - // 1: MIME types/PDLs - // 2: Queue name from DNS-SD - // service name -{ - int i, j; - int havesep = 0; - char sep, *str; - - if (str_orig == NULL) - return (NULL); - - str = strdup(str_orig); - - // for later str[strlen(str)-1] access - if (strlen(str) < 1) - return (str); - - // Select separator character - if (mode == 2) - sep = '_'; - else - sep = '-'; - - for (i = 0, j = 0; i < strlen(str); i++, j++) - { - if (((str[i] >= 'A') && (str[i] <= 'Z')) || - ((str[i] >= 'a') && (str[i] <= 'z')) || - ((str[i] >= '0') && (str[i] <= '9')) || - (mode != 2 && (str[i] == '_' || - str[i] == '.')) || - (mode == 1 && (str[i] == '/' || - str[i] == ','))) - { - // Allowed character, keep it - havesep = 0; - str[j] = str[i]; - } - else - { - // Replace all other characters by a single separator - if (havesep == 1) - j --; - else - { - havesep = 1; - str[j] = sep; - } - } - } - // Add terminating zero - str[j] = '\0'; - - // Cut off trailing separators - while (strlen(str) > 0 && str[strlen(str) - 1] == sep) - str[strlen(str) - 1] = '\0'; - - // Cut off leading separators - i = 0; - while (str[i] == sep) - i ++; - - // Keep a free()-able string. +1 for trailing \0 - return (memmove(str, str + i, strlen(str) - i + 1)); -} - - -static local_printer_t * -new_local_printer (const char *device_uri, - const char *uuid, - gboolean cups_browsed_controlled) -{ - local_printer_t *printer = g_malloc (sizeof (local_printer_t)); - printer->device_uri = strdup (device_uri); - printer->uuid = (char*)uuid; - printer->cups_browsed_controlled = cups_browsed_controlled; - return (printer); -} - - -static void -free_local_printer (gpointer data) -{ - local_printer_t *printer = data; - debug_printf("free_local_printer() in THREAD %ld\n", pthread_self()); - free (printer->device_uri); - if (printer->uuid) free (printer->uuid); - free (printer); -} - - -static gboolean -local_printer_is_same_device (gpointer key, - gpointer value, - gpointer user_data) -{ - local_printer_t *lprinter = value; - remote_printer_t *p = user_data; - char lhost[HTTP_MAX_URI], // Local printer: Hostname - lresource[HTTP_MAX_URI], // Local printer: Resource path - lscheme[32], // Local printer: URI's scheme - lusername[64], // Local printer: URI's username - *ltype = NULL, // Local printer: If URI DNS-SD-based - *ldomain = NULL; // pointers into lhost for components - int lport = 0; // Local printer: URI's port number - - debug_printf("local_printer_is_same_device() in THREAD %ld\n", - pthread_self()); - if (!lprinter || !lprinter->device_uri || !p) - return (0); - // Separate the local printer's URI into their components - memset(lscheme, 0, sizeof(lscheme)); - memset(lusername, 0, sizeof(lusername)); - memset(lhost, 0, sizeof(lhost)); - memset(lresource, 0, sizeof(lresource)); - httpSeparateURI(HTTP_URI_CODING_ALL, lprinter->device_uri, - lscheme, sizeof(lscheme) - 1, - lusername, sizeof(lusername) - 1, - lhost, sizeof(lhost) - 1, - &lport, - lresource, sizeof(lresource) - 1); - if ((ltype = strstr(lhost, "._ipp._tcp.")) != NULL || - (ltype = strstr(lhost, "._ipps._tcp.")) != NULL) - { - *ltype = '\0'; - ltype ++; - ldomain = strchr(ltype + 9, '.'); - *ldomain = '\0'; - ldomain ++; - if (*ldomain && ldomain[strlen(ldomain) - 1] == '.') - ldomain[strlen(ldomain) - 1] = '\0'; - } - // Consider not only absolutely equal URIs as equal - // but alo URIs which differ only by use of IPP or - // IPPS and/or have the IPP standard port 631 - // replaced by the HTTPS standard port 443, as this - // is common on network printers - return ((ltype && p->service_name && p->domain && - g_str_equal(lhost, p->service_name) && - !strncmp(ldomain, p->domain, strlen(ldomain))) || - (!ltype && p->host && p->resource && - (g_str_equal(lscheme, "ipp") || g_str_equal(lscheme, "ipps")) && - !lusername[0] && - g_str_equal(lhost, p->host) && - ((!p->port && (lport == 631 || lport == 443)) || - lport == p->port || - (lport == 631 && p->port == 443) || - (lport == 443 && p->port == 631)) && - g_str_equal(lresource, p->resource))); -} - - -static gboolean -local_printer_has_uuid (gpointer key, - gpointer value, - gpointer user_data) -{ - local_printer_t *printer = value; - char *uuid = user_data; - - debug_printf("local_printer_has_uuid() in THREAD %ld\n", pthread_self()); - return (printer != NULL && printer->uuid != NULL && uuid != NULL && - g_str_equal(printer->uuid, uuid)); -} - - -static gboolean -local_printer_service_name_matches (gpointer key, - gpointer value, - gpointer user_data) -{ - char *queue_name = key; - char *service_name = user_data; - char *p; - debug_printf("local_printer_service_name_matches() in THREAD %ld\n", - pthread_self()); - p = remove_bad_chars(service_name, 2); - if (p && strncasecmp(p, queue_name, 63) == 0) - { - free(p); - return (TRUE); - } - if (p) - free(p); - return (FALSE); -} - - -static void -local_printers_create_subscription (http_t *conn) -{ - char temp[1024]; - if (!local_printers_context) - { - local_printers_context = g_malloc0 (sizeof (browsepoll_t)); - // The httpGetAddr() function was introduced in CUPS 2.0.0 - local_printers_context->server = - strdup(httpAddrString(httpGetAddress(conn), - temp, sizeof(temp))); - local_printers_context->port = httpAddrPort(httpGetAddress(conn)); - local_printers_context->can_subscribe = TRUE; - } - - browse_poll_create_subscription (local_printers_context, conn); -} - - -static int -add_dest_cb(dest_list_t *user_data, - unsigned flags, - cups_dest_t *dest) -{ - if (flags & CUPS_DEST_FLAGS_REMOVED) - // Remove destination from array - user_data->num_dests = - cupsRemoveDest(dest->name, dest->instance, user_data->num_dests, - &(user_data->dests)); - else - // Add destination to array... - user_data->num_dests = - cupsCopyDest(dest, user_data->num_dests, - &(user_data->dests)); - return (1); -} - - -const char * -get_printer_uuid(http_t *http_printer, - const char* raw_uri) -{ - ipp_t *response = NULL; - ipp_attribute_t *attr = NULL; - const char * uuid = NULL; - - const char * const pattrs[] = - { - "printer-uuid", - }; - const char * const req_attrs[] = - { - "printer-uuid", - }; - - if (http_printer == NULL) - debug_printf ("HTTP connection for printer with URI %s not set!\n", - raw_uri); - - if ((response = - cfGetPrinterAttributes2(http_printer, raw_uri, - pattrs, 1, req_attrs, 1, 0)) == NULL) - { - debug_printf ("Printer with URI %s has no \"printer-uuid\" IPP attribute!\n", - raw_uri); - return (NULL); - } - - attr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI); - - - if (attr) - uuid = strdup(ippGetString(attr, 0, NULL) + 9); - else - { - debug_printf("Printer with URI %s: Cannot read \"printer-uuid\" IPP attribute!\n", - raw_uri); - } - - ippDelete(response); - - return (uuid); -} - - -static void -get_local_printers (void) -{ - pthread_rwlock_wrlock(&lock); - - dest_list_t dest_list = {0, NULL}; - http_t *conn = NULL; - - conn = http_connect_local (); - - // We only want to have a list of actually existing CUPS queues, not of - // DNS-SD-discovered printers for which CUPS can auto-setup a driverless - // print queue - if (OnlyUnsupportedByCUPS) - cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, - (cups_dest_cb_t)add_dest_cb, &dest_list); - else - cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, CUPS_PRINTER_LOCAL, - CUPS_PRINTER_DISCOVERED, (cups_dest_cb_t)add_dest_cb, - &dest_list); - debug_printf ("cups-browsed (%s): cupsEnumDests\n", local_server_str); - g_hash_table_remove_all (local_printers); - if (OnlyUnsupportedByCUPS) - g_hash_table_remove_all (cups_supported_remote_printers); - int num_dests = dest_list.num_dests; - cups_dest_t *dests = dest_list.dests; - for (int i = 0; i < num_dests; i++) - { - const char *val; - cups_dest_t *dest = &dests[i]; - local_printer_t *printer; - gboolean cups_browsed_controlled; - gboolean is_temporary; - gboolean is_cups_supported_remote; - char uri[HTTP_MAX_URI]; - - const char *device_uri = cupsGetOption ("device-uri", - dest->num_options, - dest->options); - if (device_uri == NULL) - device_uri = ""; - - // Temporary CUPS queue? - val = cupsGetOption ("printer-is-temporary", - dest->num_options, - dest->options); - is_temporary = (val && (!strcasecmp (val, "yes") || - !strcasecmp (val, "on") || - !strcasecmp (val, "true"))); - - if (OnlyUnsupportedByCUPS) - { - // Printer discovered by DNS-SD and supported by CUPS' temporary - // queues? - val = cupsGetOption ("printer-uri-supported", - dest->num_options, - dest->options); - // Printer has no local CUPS queue but CUPS would create a - // temporary queue on-demand - is_cups_supported_remote = (val == NULL || is_temporary); - } - else - { - is_cups_supported_remote = 0; - if (is_temporary) - continue; - } - - val = cupsGetOption (CUPS_BROWSED_MARK, - dest->num_options, - dest->options); - cups_browsed_controlled = val && (!strcasecmp (val, "yes") || - !strcasecmp (val, "on") || - !strcasecmp (val, "true")); - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", dest->name); - printer = new_local_printer (device_uri, get_printer_uuid(conn, uri), - cups_browsed_controlled); - debug_printf ("Printer %s: %s, %s%s%s\n", - dest->name, device_uri, printer->uuid, - cups_browsed_controlled ? ", cups_browsed" : "", - is_cups_supported_remote ? ", temporary" : ""); - - if (is_cups_supported_remote) - g_hash_table_insert (cups_supported_remote_printers, - g_ascii_strdown (dest->name, -1), - printer); - else - g_hash_table_insert (local_printers, - g_ascii_strdown (dest->name, -1), - printer); - } - - cupsFreeDests (num_dests, dests); - - pthread_rwlock_unlock(&lock); -} - - -static browse_data_t * -new_browse_data (int type, int state, const gchar *uri, - const gchar *location, const gchar *info, - const gchar *make_model, const gchar *browse_options) -{ - browse_data_t *data = g_malloc (sizeof (browse_data_t)); - data->type = type; - data->state = state; - data->uri = g_strdup (uri); - data->location = g_strdup (location); - data->info = g_strdup (info); - data->make_model = g_strdup (make_model); - data->browse_options = g_strdup (browse_options); - return (data); -} - - -static void -browse_data_free (gpointer data) -{ - browse_data_t *bdata = data; - debug_printf("browse_data_free() in THREAD %ld\n", pthread_self()); - g_free (bdata->uri); - g_free (bdata->location); - g_free (bdata->info); - g_free (bdata->make_model); - g_free (bdata->browse_options); - g_free (bdata); -} - - -static void -prepare_browse_data (void) -{ - static const char * const rattrs[] = { "printer-type", - "printer-state", - "printer-uri-supported", - "printer-info", - "printer-location", - "printer-make-and-model", - "auth-info-required", - "printer-uuid", - "job-template" }; - ipp_t *request, *response = NULL; - ipp_attribute_t *attr; - http_t *conn = NULL; - - conn = http_connect_local (); - - if (conn == NULL) - { - debug_printf("Browse send failed to connect to localhost\n"); - goto fail; - } - - request = ippNewRequest(CUPS_GET_PRINTERS); - ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]), - NULL, rattrs); - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser ()); - - debug_printf("preparing browse data\n"); - response = cupsDoRequest (conn, request, "/"); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("browse send failed for localhost: %s\n", - cupsLastErrorString ()); - goto fail; - } - - g_list_free_full (browse_data, browse_data_free); - browse_data = NULL; - for (attr = ippFirstAttribute(response); attr; - attr = ippNextAttribute(response)) - { - int type = -1, state = -1; - const char *uri = NULL; - gchar *location = NULL; - gchar *info = NULL; - gchar *make_model = NULL; - GString *browse_options = g_string_new (""); - - // Skip any non-printer attributes - while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER) - attr = ippNextAttribute(response); - - if (!attr) - break; - - while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) - { - const char *attrname = ippGetName(attr); - int value_tag = ippGetValueTag(attr); - - if (!strcasecmp(attrname, "printer-type") && - value_tag == IPP_TAG_ENUM) - { - type = ippGetInteger(attr, 0); - if (type & CUPS_PRINTER_NOT_SHARED) - { - // Skip CUPS queues not marked as shared - state = -1; - type = -1; - break; - } - } - else if (!strcasecmp(attrname, "printer-state") && - value_tag == IPP_TAG_ENUM) - state = ippGetInteger(attr, 0); - else if (!strcasecmp(attrname, "printer-uri-supported") && - value_tag == IPP_TAG_URI) - uri = ippGetString(attr, 0, NULL); - else if (!strcasecmp(attrname, "printer-location") && - value_tag == IPP_TAG_TEXT) - { - // Remove quotes - gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1); - location = g_strjoinv ("", tokens); - g_strfreev (tokens); - } - else if (!strcasecmp(attrname, "printer-info") && - value_tag == IPP_TAG_TEXT) - { - // Remove quotes - gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1); - info = g_strjoinv ("", tokens); - g_strfreev (tokens); - } - else if (!strcasecmp(attrname, "printer-make-and-model") && - value_tag == IPP_TAG_TEXT) - { - // Remove quotes - gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1); - make_model = g_strjoinv ("", tokens); - g_strfreev (tokens); - } - else if (!strcasecmp(attrname, "auth-info-required") && - value_tag == IPP_TAG_KEYWORD) - { - if (strcasecmp (ippGetString(attr, 0, NULL), "none")) - g_string_append_printf (browse_options, "auth-info-required=%s ", - ippGetString(attr, 0, NULL)); - } - else if (!strcasecmp(attrname, "printer-uuid") && - value_tag == IPP_TAG_URI) - g_string_append_printf (browse_options, "uuid=%s ", - ippGetString(attr, 0, NULL)); - else if (!strcasecmp(attrname, "job-sheets-default") && - value_tag == IPP_TAG_NAME && - ippGetCount(attr) == 2) - g_string_append_printf (browse_options, "job-sheets=%s,%s ", - ippGetString(attr, 0, NULL), - ippGetString(attr, 1, NULL)); - else if (strstr(attrname, "-default")) - { - gchar *name = g_strdup (attrname); - gchar *value = NULL; - *strstr (name, "-default") = '\0'; - - switch (value_tag) - { - gchar **tokens; - - case IPP_TAG_KEYWORD: - case IPP_TAG_STRING: - case IPP_TAG_NAME: - // Escape value - tokens = g_strsplit_set (ippGetString(attr, 0, NULL), - " \"\'\\", -1); - value = g_strjoinv ("\\", tokens); - g_strfreev (tokens); - break; - - default: - // other values aren't needed? - debug_printf("skipping %s (%d)\n", name, value_tag); - break; - } - - if (value) - { - g_string_append_printf (browse_options, "%s=%s ", name, value); - g_free (value); - } - - g_free (name); - } - - attr = ippNextAttribute(response); - } - - if (type != -1 && state != -1 && uri && location && info && make_model) - { - gchar *browse_options_str = g_string_free (browse_options, FALSE); - browse_data_t *data; - browse_options = NULL; - g_strchomp (browse_options_str); - data = new_browse_data (type, state, uri, location, - info, make_model, browse_options_str); - browse_data = g_list_insert (browse_data, data, 0); - g_free (browse_options_str); - } - - if (make_model) - g_free (make_model); - - if (info) - g_free (info); - - if (location) - g_free (location); - - if (browse_options) - g_string_free (browse_options, TRUE); - - if (!attr) - break; - } - - fail: - if (response) - ippDelete(response); -} - - -static void -update_local_printers (void) -{ - gboolean get_printers = FALSE; - http_t *conn; - - if (inhibit_local_printers_update) - return; - - conn = http_connect_local (); - if (conn && - (!local_printers_context || local_printers_context->can_subscribe)) - { - if (!local_printers_context || - local_printers_context->subscription_id == -1) - { - // No subscription yet. First, create the subscription. - local_printers_create_subscription (conn); - get_printers = TRUE; - } - else - // We already have a subscription, so use it. - - // Note: for the moment, browse_poll_get_notifications() just - // tells us whether we should re-fetch the printer list, so it - // is safe to use here. - get_printers = browse_poll_get_notifications (local_printers_context, - conn); - } - else - get_printers = TRUE; - - if (get_printers) - { - get_local_printers (); - - if (BrowseLocalProtocols & BROWSE_CUPS) - prepare_browse_data (); - } -} - - -static int -check_jobs () -{ - int num_jobs = 0; - cups_job_t *jobs = NULL; - remote_printer_t *p; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to check whether there are still jobs.\n"); - return (0); - } - - if (cupsArrayCount(remote_printers) > 0) - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; - p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (!p->slave_of) - { - num_jobs = cupsGetJobs2(conn, &jobs, p->queue_name, 0, - CUPS_WHICHJOBS_ACTIVE); - if (num_jobs > 0) - { - debug_printf("Queue %s still has jobs!\n", p->queue_name); - cupsFreeJobs(num_jobs, jobs); - return (1); - } - } - - debug_printf("All our remote printers are without jobs.\n"); - return (0); -} - - -static gboolean -autoshutdown_execute (gpointer data) -{ - debug_printf("autoshutdown_execute() in THREAD %ld\n", pthread_self()); - // Are we still in auto shutdown mode and are we still without queues or - // jobs - if (autoshutdown && - (cupsArrayCount(remote_printers) == 0 || - (autoshutdown_on == NO_JOBS && check_jobs() == 0))) - { - debug_printf("Automatic shutdown as there are no print queues maintained by us or no jobs on them for %d sec.\n", - autoshutdown_timeout); - g_main_loop_quit(gmainloop); - g_main_context_wakeup(NULL); - } - - // Stop this timeout handler, we needed it only once - return (FALSE); -} - - -#ifdef HAVE_LDAP_REBIND_PROC -# if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -// -// 'ldap_rebind_proc()' - Callback function for LDAP rebind -// - -static int // O - Result code -ldap_rebind_proc(LDAP *RebindLDAPHandle, // I - LDAP handle - LDAP_CONST char *refsp, // I - ??? - ber_tag_t request, // I - ??? - ber_int_t msgid, // I - ??? - void *params) // I - ??? -{ - int rc; // Result code -# if LDAP_API_VERSION > 3000 - struct berval bval; // Bind value -# endif // LDAP_API_VERSION > 3000 - (void)request; - (void)msgid; - (void)params; - - // - // Bind to new LDAP server... - // - - debug_printf("ldap_rebind_proc: Rebind to %s\n", refsp); - -# if LDAP_API_VERSION > 3000 - bval.bv_val = BrowseLDAPPassword; - bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword); - - rc = ldap_sasl_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE, - &bval, NULL, NULL, NULL); -# else - rc = ldap_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, BrowseLDAPPassword, - LDAP_AUTH_SIMPLE); -# endif // LDAP_API_VERSION > 3000 - - return (rc); -} - - -# else // defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -// -// 'ldap_rebind_proc()' - Callback function for LDAP rebind -// - -static int // O - Result code -ldap_rebind_proc(LDAP *RebindLDAPHandle, // I - LDAP handle - char **dnp, // I - ??? - char **passwdp, // I - ??? - int *authmethodp, // I - ??? - int freeit, // I - ??? - void *arg) // I - ??? -{ - switch (freeit) - { - case 1: - // - // Free current values... - // - - debug_printf("ldap_rebind_proc: Free values...\n"); - - if (dnp && *dnp) - free(*dnp); - - if (passwdp && *passwdp) - free(*passwdp); - break; - - case 0: - // - // Return credentials for LDAP referal... - // - - debug_printf("ldap_rebind_proc: Return necessary values...\n"); - - *dnp = strdup(BrowseLDAPBindDN); - *passwdp = strdup(BrowseLDAPPassword); - *authmethodp = LDAP_AUTH_SIMPLE; - break; - - default: - // - // Should never happen... - // - - debug_printf("LDAP rebind has been called with wrong freeit value!\n"); - break; - } - - return (LDAP_SUCCESS); -} -# endif // defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -#endif // HAVE_LDAP_REBIND_PROC - - -#ifdef HAVE_LDAP -// -// 'ldap_new_connection()' - Start new LDAP connection -// - -static LDAP * // O - LDAP handle -ldap_new_connection(void) -{ - int rc; // LDAP API status - int version = 3; // LDAP version - struct berval bv = {0, ""}; // SASL bind value - LDAP *TempBrowseLDAPHandle=NULL; - // Temporary LDAP Handle -# if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) - int ldap_ssl = 0; // LDAP SSL indicator - int ssl_err = 0; // LDAP SSL error value -# endif // defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) - - -# ifdef HAVE_OPENLDAP -# ifdef HAVE_LDAP_SSL - // - // Set the certificate file to use for encrypted LDAP sessions... - // - - if (BrowseLDAPCACertFile) - { - debug_printf("ldap_new_connection: Setting CA certificate file \"%s\"\n", - BrowseLDAPCACertFile); - - if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, - (void *)BrowseLDAPCACertFile)) != LDAP_SUCCESS) - debug_printf("Unable to set CA certificate file for LDAP " - "connections: %d - %s\n", rc, ldap_err2string(rc)); - } -# endif // HAVE_LDAP_SSL - - // - // Initialize OPENLDAP connection... - // LDAP stuff currently only supports ldapi EXTERNAL SASL binds... - // - - if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) - rc = ldap_initialize(&TempBrowseLDAPHandle, "ldapi:///"); - else - rc = ldap_initialize(&TempBrowseLDAPHandle, BrowseLDAPServer); - -# else // HAVE_OPENLDAP - - int ldap_port = 0; // LDAP port - char ldap_protocol[11], // LDAP protocol - ldap_host[255]; // LDAP host - - // - // Split LDAP URI into its components... - // - - if (!BrowseLDAPServer) - { - debug_printf("BrowseLDAPServer not configured!\n"); - debug_printf("Disabling LDAP browsing!\n"); - //BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - return (NULL); - } - - sscanf(BrowseLDAPServer, "%10[^:]://%254[^:/]:%d", ldap_protocol, ldap_host, - &ldap_port); - - if (!strcmp(ldap_protocol, "ldap")) - ldap_ssl = 0; - else if (!strcmp(ldap_protocol, "ldaps")) - ldap_ssl = 1; - else - { - debug_printf("Unrecognized LDAP protocol (%s)!\n", - ldap_protocol); - debug_printf("Disabling LDAP browsing!\n"); - //BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - return (NULL); - } - - if (ldap_port == 0) - { - if (ldap_ssl) - ldap_port = LDAPS_PORT; - else - ldap_port = LDAP_PORT; - } - - debug_printf("ldap_new_connection: PROT:%s HOST:%s PORT:%d\n", - ldap_protocol, ldap_host, ldap_port); - - // - // Initialize LDAP connection... - // - - if (!ldap_ssl) - { - if ((TempBrowseLDAPHandle = ldap_init(ldap_host, ldap_port)) == NULL) - rc = LDAP_OPERATIONS_ERROR; - else - rc = LDAP_SUCCESS; - -# ifdef HAVE_LDAP_SSL - } - else - { - // - // Initialize SSL LDAP connection... - // - - if (BrowseLDAPCACertFile) - { - rc = ldapssl_client_init(BrowseLDAPCACertFile, (void *)NULL); - if (rc != LDAP_SUCCESS) - { - debug_printf("Failed to initialize LDAP SSL client!\n"); - rc = LDAP_OPERATIONS_ERROR; - } - else - { - if ((TempBrowseLDAPHandle = ldapssl_init(ldap_host, ldap_port, - 1)) == NULL) - rc = LDAP_OPERATIONS_ERROR; - else - rc = LDAP_SUCCESS; - } - } - else - { - debug_printf("LDAP SSL certificate file/database not configured!\n"); - rc = LDAP_OPERATIONS_ERROR; - } - -# else // HAVE_LDAP_SSL - - // - // Return error, because client libraries doesn't support SSL - // - - debug_printf("LDAP client libraries do not support SSL\n"); - rc = LDAP_OPERATIONS_ERROR; - -# endif // HAVE_LDAP_SSL - } -# endif // HAVE_OPENLDAP - - // - // Check return code from LDAP initialize... - // - - if (rc != LDAP_SUCCESS) - { - debug_printf("Unable to initialize LDAP!\n"); - - if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) - debug_printf("Temporarily disabling LDAP browsing...\n"); - else - { - debug_printf("Disabling LDAP browsing!\n"); - - //BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - } - - ldap_disconnect(TempBrowseLDAPHandle); - - return (NULL); - } - - // - // Upgrade LDAP version... - // - - if (ldap_set_option(TempBrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION, - (const void *)&version) != LDAP_SUCCESS) - { - debug_printf("Unable to set LDAP protocol version %d!\n", - version); - debug_printf("Disabling LDAP browsing!\n"); - - //BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - ldap_disconnect(TempBrowseLDAPHandle); - - return (NULL); - } - - // - // Register LDAP rebind procedure... - // - -# ifdef HAVE_LDAP_REBIND_PROC -# if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) - - rc = ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, - (void *)NULL); - if (rc != LDAP_SUCCESS) - debug_printf("Setting LDAP rebind function failed with status %d: %s\n", - rc, ldap_err2string(rc)); - -# else - - ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, (void *)NULL); - -# endif // defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -# endif // HAVE_LDAP_REBIND_PROC - - // - // Start LDAP bind... - // - -# if LDAP_API_VERSION > 3000 - struct berval bval; - bval.bv_val = BrowseLDAPPassword; - bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword); - - if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) - rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL, - NULL, NULL); - else - rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN, - LDAP_SASL_SIMPLE, &bval, NULL, NULL, NULL); - -# else - rc = ldap_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN, - BrowseLDAPPassword, LDAP_AUTH_SIMPLE); -# endif // LDAP_API_VERSION > 3000 - - if (rc != LDAP_SUCCESS) - { - debug_printf("LDAP bind failed with error %d: %s\n", - rc, ldap_err2string(rc)); - -# if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) - if (ldap_ssl && (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)) - { - ssl_err = PORT_GetError(); - if (ssl_err != 0) - debug_printf("LDAP SSL error %d: %s\n", ssl_err, - ldapssl_err2string(ssl_err)); - } -# endif // defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) - - ldap_disconnect(TempBrowseLDAPHandle); - - return (NULL); - } - - debug_printf("LDAP connection established\n"); - - return (TempBrowseLDAPHandle); -} - - -// -// 'ldap_reconnect()' - Reconnect to LDAP Server -// - -static LDAP * // O - New LDAP handle -ldap_reconnect(void) -{ - LDAP *TempBrowseLDAPHandle = NULL; // Temp Handle to LDAP server - - // - // Get a new LDAP Handle and replace the global Handle - // if the new connection was successful. - // - - debug_printf("Try LDAP reconnect...\n"); - - TempBrowseLDAPHandle = ldap_new_connection(); - - if (TempBrowseLDAPHandle != NULL) - { - if (BrowseLDAPHandle != NULL) - ldap_disconnect(BrowseLDAPHandle); - - BrowseLDAPHandle = TempBrowseLDAPHandle; - } - - return (BrowseLDAPHandle); -} - - -// -// 'ldap_disconnect()' - Disconnect from LDAP Server -// - -static void -ldap_disconnect(LDAP *ld) // I - LDAP handle -{ - int rc; // Return code - - // - // Close LDAP handle... - // - -# if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - rc = ldap_unbind_ext_s(ld, NULL, NULL); -# else - rc = ldap_unbind_s(ld); -# endif // defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - - if (rc != LDAP_SUCCESS) - debug_printf("Unbind from LDAP server failed with status %d: %s\n", - rc, ldap_err2string(rc)); -} - - -// -// 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP... -// - -static void -cupsdUpdateLDAPBrowse(void) -{ - char uri[HTTP_MAX_URI], // Printer URI - host[HTTP_MAX_URI], // Hostname - resource[HTTP_MAX_URI], // Resource path - local_resource[HTTP_MAX_URI], // Resource path - service_name[4096], - location[1024], // Printer location - info[1024], // Printer information - make_model[1024], // Printer make and model - type_num[30], // Printer type number - scheme[32], // URI's scheme - username[64]; // URI's username - int port; // URI's port number - char *c; - int hl; - int rc; // LDAP status - int limit; // Size limit - LDAPMessage *res, // LDAP search results - *e; // Current entry from search - - debug_printf("UpdateLDAPBrowse\n"); - - // - // Reconnect if LDAP Handle is invalid... - // - - if (! BrowseLDAPHandle) - { - ldap_reconnect(); - return; - } - - // - // Search for cups printers in LDAP directory... - // - - rc = ldap_search_rec(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE, - BrowseLDAPFilter, (char **)ldap_attrs, 0, &res); - - // - // If ldap search was successfull then exit function - // and temporary disable LDAP updates... - // - - if (rc != LDAP_SUCCESS) - { - if (BrowseLDAPUpdate && ((rc == LDAP_SERVER_DOWN) || - (rc == LDAP_CONNECT_ERROR))) - { - BrowseLDAPUpdate = FALSE; - debug_printf("LDAP update temporary disabled\n"); - } - return; - } - - // - // If LDAP updates were disabled, we will reenable them... - // - - if (!BrowseLDAPUpdate) - { - BrowseLDAPUpdate = TRUE; - debug_printf("LDAP update enabled\n"); - } - - // - // Count LDAP entries and return if no entry exist... - // - - limit = ldap_count_entries(BrowseLDAPHandle, res); - debug_printf("LDAP search returned %d entries\n", limit); - if (limit < 1) - { - ldap_freeres(res); - return; - } - - // - // Loop through the available printers... - // - - for (e = ldap_first_entry(BrowseLDAPHandle, res); - e; - e = ldap_next_entry(BrowseLDAPHandle, e)) - { - // - // Get the required values from this entry... - // - - if (ldap_getval_firststring(BrowseLDAPHandle, e, - "printerDescription", info, sizeof(info)) == -1) - continue; - - if (ldap_getval_firststring(BrowseLDAPHandle, e, - "printerLocation", location, - sizeof(location)) == -1) - continue; - - if (ldap_getval_firststring(BrowseLDAPHandle, e, - "printerMakeAndModel", make_model, - sizeof(make_model)) == -1) - continue; - - if (ldap_getval_firststring(BrowseLDAPHandle, e, - "printerType", type_num, - sizeof(type_num)) == -1) - continue; - - if (ldap_getval_firststring(BrowseLDAPHandle, e, - "printerURI", uri, sizeof(uri)) == -1) - continue; - - // - // Process the entry... - // - - memset(scheme, 0, sizeof(scheme)); - memset(username, 0, sizeof(username)); - memset(host, 0, sizeof(host)); - memset(resource, 0, sizeof(resource)); - memset(local_resource, 0, sizeof(local_resource)); - - httpSeparateURI (HTTP_URI_CODING_ALL, uri, - scheme, sizeof(scheme) - 1, - username, sizeof(username) - 1, - host, sizeof(host) - 1, - &port, - resource, sizeof(resource)- 1); - - if (strncasecmp (resource, "/printers/", 10) && - strncasecmp (resource, "/classes/", 9)) - { - debug_printf("don't understand URI: %s\n", uri); - return; - } - - strncpy (local_resource, resource + 1, sizeof (local_resource) - 1); - local_resource[sizeof (local_resource) - 1] = '\0'; - c = strchr (local_resource, '?'); - if (c) - *c = '\0'; - - // Build the DNS-SD service name which CUPS would give to this printer - // when DNS-SD-broadcasting it - snprintf(service_name, sizeof (service_name), "%s @ %s", - (strlen(info) > 0 ? info : strchr(local_resource, '/') + 1), host); - // Cut off trailing ".local" of host name - hl = strlen(service_name); - if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local")) - service_name[hl - 6] = '\0'; - if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local.")) - service_name[hl - 7] = '\0'; - // DNS-SD service name has max. 63 characters - service_name[63] = '\0'; - - debug_printf("LDAP: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n", - host, port, strchr(local_resource, '/') + 1, service_name); - - pthread_rwlock_wrlock(&lock); - examine_discovered_printer_record(host, NULL, port, local_resource, - service_name, location, info, "", "", - "", 0, NULL); - pthread_rwlock_unlock(&lock); - - } - - ldap_freeres(res); -} - - -// -// 'ldap_search_rec()' - LDAP Search with reconnect -// - -static int // O - Return code -ldap_search_rec(LDAP *ld, // I - LDAP handler - char *base, // I - Base dn - int scope, // I - LDAP search scope - char *filter, // I - Filter string - char *attrs[], // I - Requested attributes - int attrsonly, // I - Return only attributes? - LDAPMessage **res) // I - LDAP handler -{ - int rc; // Return code - LDAP *ldr; // LDAP handler after reconnect - - -# if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - rc = ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, NULL, NULL, - NULL, LDAP_NO_LIMIT, res); -# else - rc = ldap_search_s(ld, base, scope, filter, attrs, attrsonly, res); -# endif // defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - - // - // If we have a connection problem try again... - // - - if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) - { - debug_printf("LDAP search failed with status %d: %s\n", - rc, ldap_err2string(rc)); - debug_printf("We try the LDAP search once again after reconnecting to " - "the server\n"); - ldap_freeres(*res); - ldr = ldap_reconnect(); - -# if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - rc = ldap_search_ext_s(ldr, base, scope, filter, attrs, attrsonly, NULL, - NULL, NULL, LDAP_NO_LIMIT, res); -# else - rc = ldap_search_s(ldr, base, scope, filter, attrs, attrsonly, res); -# endif // defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - } - - if (rc == LDAP_NO_SUCH_OBJECT) - debug_printf("ldap_search_rec: LDAP entry/object not found\n"); - else if (rc != LDAP_SUCCESS) - debug_printf("ldap_search_rec: LDAP search failed with status %d: %s\n", - rc, ldap_err2string(rc)); - - if (rc != LDAP_SUCCESS) - ldap_freeres(*res); - - return (rc); -} - - -// -// 'ldap_freeres()' - Free LDAPMessage -// - -static void -ldap_freeres(LDAPMessage *entry) // I - LDAP handler -{ - int rc; // Return value - - rc = ldap_msgfree(entry); - if (rc == -1) - debug_printf("Can't free LDAPMessage!\n"); - else if (rc == 0) - debug_printf("Freeing LDAPMessage was unnecessary\n"); -} - - -// -// 'ldap_getval_char()' - Get first LDAP value and convert to string -// - -static int // O - Return code -ldap_getval_firststring(LDAP *ld, // I - LDAP handler - LDAPMessage *entry, // I - LDAP message or search - // result - char *attr, // I - the wanted attribute - char *retval, // O - String to return - unsigned long maxsize) // I - Max string size -{ - char *dn; // LDAP DN - int rc = 0; // Return code -# if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - struct berval **bval; // LDAP value array - unsigned long size; // String size - - - // - // Get value from LDAPMessage... - // - - if ((bval = ldap_get_values_len(ld, entry, attr)) == NULL) - { - rc = -1; - dn = ldap_get_dn(ld, entry); - debug_printf("Failed to get LDAP value %s for %s!\n", - attr, dn); - ldap_memfree(dn); - } - else - { - // - // Check size and copy value into our string... - // - - size = maxsize; - if (size < (bval[0]->bv_len + 1)) - { - rc = -1; - dn = ldap_get_dn(ld, entry); - debug_printf("Attribute %s is too big! (dn: %s)\n", - attr, dn); - ldap_memfree(dn); - } - else - size = bval[0]->bv_len + 1; - - strncpy(retval, bval[0]->bv_val, size); - if (size > 0) - retval[size - 1] = '\0'; - ldap_value_free_len(bval); - } -# else - char **value; // LDAP value - - // - // Get value from LDAPMessage... - // - - if ((value = (char **)ldap_get_values(ld, entry, attr)) == NULL) - { - rc = -1; - dn = ldap_get_dn(ld, entry); - debug_printf("Failed to get LDAP value %s for %s!\n", - attr, dn); - ldap_memfree(dn); - } - else - { - strncpy(retval, *value, maxsize); - if (maxsize > 0) - retval[maxsize - 1] = '\0'; - ldap_value_free(value); - } -# endif // defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 - - return (rc); -} -#endif // HAVE_LDAP - - -static int -create_subscription () -{ - ipp_t *req; - ipp_t *resp; - ipp_attribute_t *attr; - int id = 0; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to subscribe to notifications.\n"); - return (0); - } - - req = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION); - ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, - "notify-events", NULL, "all"); - ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, - "notify-recipient-uri", NULL, "dbus://"); - ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, - "notify-lease-duration", notify_lease_duration); - - resp = cupsDoRequest (conn, req, "/"); - if (!resp || cupsLastError() != IPP_STATUS_OK) - { - debug_printf ("Error subscribing to CUPS notifications: %s\n", - cupsLastErrorString ()); - return (0); - } - - attr = ippFindAttribute (resp, "notify-subscription-id", IPP_TAG_INTEGER); - if (attr) - id = ippGetInteger (attr, 0); - else - debug_printf ("" - "ipp-create-printer-subscription response doesn't contain " - "subscription id.\n"); - - ippDelete (resp); - return (id); -} - - -static gboolean -renew_subscription (int id) -{ - ipp_t *req; - ipp_t *resp; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to renew subscriptions.\n"); - return (FALSE); - } - - req = ippNewRequest (IPP_RENEW_SUBSCRIPTION); - ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER, - "notify-subscription-id", id); - ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, - "notify-recipient-uri", NULL, "dbus://"); - ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, - "notify-lease-duration", notify_lease_duration); - - resp = cupsDoRequest (conn, req, "/"); - if (!resp || cupsLastError() != IPP_STATUS_OK) - { - debug_printf ("Error renewing CUPS subscription %d: %s\n", - id, cupsLastErrorString ()); - return (FALSE); - } - - ippDelete (resp); - return (TRUE); -} - - -static gboolean -renew_subscription_timeout (gpointer userdata) -{ - int *subscription_id = userdata; - - debug_printf("renew_subscription_timeout() in THREAD %ld\n", pthread_self()); - - if (*subscription_id <= 0 || !renew_subscription (*subscription_id)) - *subscription_id = create_subscription (); - - return (TRUE); -} - - -static void -cancel_subscription (int id) -{ - ipp_t *req; - ipp_t *resp; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to cancel subscriptions.\n"); - return; - } - - if (id <= 0) - return; - - req = ippNewRequest (IPP_CANCEL_SUBSCRIPTION); - ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER, - "notify-subscription-id", id); - - resp = cupsDoRequest (conn, req, "/"); - if (!resp || cupsLastError() != IPP_STATUS_OK) - { - debug_printf ("Error subscribing to CUPS notifications: %s\n", - cupsLastErrorString ()); - return; - } - - ippDelete (resp); -} - - -static int -is_created_by_cups_browsed (const char *printer) -{ - remote_printer_t *p; - - if (printer == NULL) - return (0); - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (!p->slave_of && !strcasecmp(printer, p->queue_name)) - return (1); - - return (0); -} - - -static remote_printer_t * -printer_record (const char *printer) -{ - remote_printer_t *p; - - if (printer == NULL) - return (NULL); - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (!p->slave_of && !strcasecmp(printer, p->queue_name)) - return (p); - - return (NULL); -} - - -static void -log_cluster(remote_printer_t *p) -{ - remote_printer_t *q, *r; - int i; - if (p == NULL || (!debug_stderr && !debug_logfile)) - return; - if (p->slave_of) - q = p->slave_of; - else - q = p; - if (q->queue_name == NULL) - return; - debug_printf("Remote CUPS printers clustered as queue %s:\n", q->queue_name); - for (r = (remote_printer_t *)cupsArrayFirst(remote_printers), i = 0; - r; r = (remote_printer_t *)cupsArrayNext(remote_printers), i ++) - if (r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED && - r->status != STATUS_TO_BE_RELEASED && - (r == q || r->slave_of == q)) - debug_printf(" %s%s%s\n", r->uri, - (r == q ? "*" : ""), - (i == q->last_printer ? " (last job printed)" : "")); -} - - -static void -log_all_printers() -{ - remote_printer_t *p, *q; - if (!debug_stderr && !debug_logfile) - return; - debug_printf("=== Remote printer overview ===\n"); - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - debug_printf("Printer %s (%s, %s): Local queue %s, %s, Slave of %s%s\n", - p->uri, - p->host, (p->ip ? p->ip : "IP not determined"), p->queue_name, - (p->netprinter ? "IPP Printer" : "Remote CUPS Printer"), - ((q = p->slave_of) != NULL ? - (q->uri ? q->uri : "Deleted Printer") : "None"), - (p->status == STATUS_UNCONFIRMED ? " (Unconfirmed)" : - (p->status == STATUS_DISAPPEARED ? " (Disappeared)" : - (p->status == STATUS_TO_BE_RELEASED ? - " (To be released from cups-browsed)" : - (p->status == STATUS_TO_BE_CREATED ? - " (To be created/updated)" : ""))))); - debug_printf("===============================\n"); -} - - -static char * -is_disabled(const char *printer, - const char *reason) -{ - ipp_t *request, *response; - ipp_attribute_t *attr; - const char *pname = NULL; - ipp_pstate_t pstate = IPP_PRINTER_IDLE; - const char *p; - char *pstatemsg = NULL; - static const char *pattrs[] = - { - "printer-name", - "printer-state", - "printer-state-message" - }; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to check whether the printer %s is disabled.\n", - printer); - return (NULL); - } - - request = ippNewRequest(CUPS_GET_PRINTERS); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", - sizeof(pattrs) / sizeof(pattrs[0]), - NULL, pattrs); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", - NULL, cupsUser()); - if ((response = cupsDoRequest(conn, request, "/")) != NULL) - { - for (attr = ippFirstAttribute(response); attr != NULL; - attr = ippNextAttribute(response)) - { - while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER) - attr = ippNextAttribute(response); - if (attr == NULL) - break; - pname = NULL; - pstate = IPP_PRINTER_IDLE; - if (pstatemsg) - { - free(pstatemsg); - pstatemsg = NULL; - } - while (attr != NULL && ippGetGroupTag(attr) == - IPP_TAG_PRINTER) - { - if (!strcmp(ippGetName(attr), "printer-name") && - ippGetValueTag(attr) == IPP_TAG_NAME) - pname = ippGetString(attr, 0, NULL); - else if (!strcmp(ippGetName(attr), "printer-state") && - ippGetValueTag(attr) == IPP_TAG_ENUM) - pstate = (ipp_pstate_t)ippGetInteger(attr, 0); - else if (!strcmp(ippGetName(attr), "printer-state-message") && - ippGetValueTag(attr) == IPP_TAG_TEXT) - { - if (pstatemsg != NULL) - { - free(pstatemsg); - pstatemsg = NULL; - } - p = ippGetString(attr, 0, NULL); - if (p != NULL) - pstatemsg = strdup(p); - } - attr = ippNextAttribute(response); - } - if (pname == NULL) - { - if (attr == NULL) - break; - else - continue; - } - if (!strcasecmp(pname, printer)) - { - switch (pstate) - { - case IPP_PRINTER_IDLE: - case IPP_PRINTER_PROCESSING: - ippDelete(response); - if (pstatemsg != NULL) - { - free(pstatemsg); - pstatemsg = NULL; - } - return (NULL); - case IPP_PRINTER_STOPPED: - ippDelete(response); - if (reason == NULL) - return (pstatemsg); - else if (pstatemsg != NULL && - (strcasestr(pstatemsg, reason) != NULL)) - return (pstatemsg); - else - { - if (pstatemsg != NULL) - { - free(pstatemsg); - pstatemsg = NULL; - } - return (NULL); - } - } - } - } - debug_printf("No information regarding enabled/disabled found about the requested printer '%s'\n", - printer); - ippDelete(response); - if (pstatemsg != NULL) - { - free(pstatemsg); - pstatemsg = NULL; - } - return (NULL); - } - debug_printf("ERROR: Request for printer info failed: %s\n", - cupsLastErrorString()); - if (pstatemsg != NULL) - { - free(pstatemsg); - pstatemsg = NULL; - } - return (NULL); -} - - -static int -enable_printer (const char *printer) -{ - ipp_t *request; - char uri[HTTP_MAX_URI]; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to enable printer %s.\n", - printer); - return (-1); - } - - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - request = ippNewRequest (IPP_RESUME_PRINTER); - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippDelete(cupsDoRequest (conn, request, "/admin/")); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("ERROR: Failed enabling printer '%s': %s\n", - printer, cupsLastErrorString()); - return (-1); - } - debug_printf("Enabled printer '%s'\n", printer); - return (0); -} - - -static int -disable_printer (const char *printer, - const char *reason) -{ - ipp_t *request; - char uri[HTTP_MAX_URI]; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to disable printer %s.\n", - printer); - return (-1); - } - - if (reason == NULL) - reason = "Disabled by cups-browsed"; - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - request = ippNewRequest (IPP_PAUSE_PRINTER); - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT, - "printer-state-message", NULL, reason); - ippDelete(cupsDoRequest (conn, request, "/admin/")); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("ERROR: Failed disabling printer '%s': %s\n", - printer, cupsLastErrorString()); - return (-1); - } - debug_printf("Disabled printer '%s'\n", printer); - return (0); -} - - -static int -set_cups_default_printer(const char *printer) -{ - ipp_t *request; - char uri[HTTP_MAX_URI]; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to subscribe to set printer %s as default printer.\n", - printer); - return (-1); - } - - if (printer == NULL) - return (0); - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - request = ippNewRequest(IPP_OP_CUPS_SET_DEFAULT); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", - NULL, cupsUser()); - ippDelete(cupsDoRequest(conn, request, "/admin/")); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("ERROR: Failed setting CUPS default printer to '%s': %s\n", - printer, cupsLastErrorString()); - return (-1); - } - debug_printf("Successfully set CUPS default printer to '%s'\n", - printer); - return (0); -} - - -static char * -get_cups_default_printer() -{ - ipp_t *request, *response; - ipp_attribute_t *attr; - const char *default_printer_name = NULL; - char *name_string; - http_t *conn = NULL; - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to find out which is the default printer.\n"); - return (NULL); - } - - request = ippNewRequest(CUPS_GET_DEFAULT); - // Default user - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - // Do it - response = cupsDoRequest(conn, request, "/"); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE || !response) - debug_printf("Could not determine system default printer!\n"); - else - { - for (attr = ippFirstAttribute(response); attr != NULL; - attr = ippNextAttribute(response)) - { - while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER) - attr = ippNextAttribute(response); - if (attr) - { - for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER; - attr = ippNextAttribute(response)) - { - if (!strcasecmp(ippGetName(attr), "printer-name") && - ippGetValueTag(attr) == IPP_TAG_NAME) - { - default_printer_name = ippGetString(attr, 0, NULL); - break; - } - } - } - if (default_printer_name) - break; - } - } - - if (default_printer_name != NULL) - name_string = strdup(default_printer_name); - else - name_string = NULL; - - ippDelete(response); - - return (name_string); -} - - -static int -is_cups_default_printer(const char *printer) -{ - if (printer == NULL) - return (0); - char *cups_default = get_cups_default_printer(); - if (cups_default == NULL) - return (0); - if (!strcasecmp(printer, cups_default)) - { - free(cups_default); - return (1); - } - free(cups_default); - return (0); -} - - -static int -invalidate_default_printer(int local) -{ - const char *filename = local ? local_default_printer_file : - remote_default_printer_file; - unlink(filename); - return (0); -} - - -static int -record_default_printer(const char *printer, int local) -{ - FILE *fp = NULL; - const char *filename = local ? local_default_printer_file : - remote_default_printer_file; - - if (printer == NULL || strlen(printer) == 0) - return (invalidate_default_printer(local)); - - fp = fopen(filename, "w+"); - if (fp == NULL) - { - debug_printf("ERROR: Failed creating file %s\n", - filename); - invalidate_default_printer(local); - return (-1); - } - fprintf(fp, "%s", printer); - fclose(fp); - - return (0); -} - - -static char * -retrieve_default_printer(int local) -{ - FILE *fp = NULL; - const char *filename = local ? local_default_printer_file : - remote_default_printer_file; - const char *printer = NULL; - char *p, buf[1024]; - int n; - - fp = fopen(filename, "r"); - if (fp == NULL) - { - debug_printf("Failed reading file %s\n", - filename); - return (NULL); - } - p = buf; - n = fscanf(fp, "%s", p); - if (n == 1) - { - if (strlen(p) > 0) - printer = p; - } - fclose(fp); - - return (printer ? strdup(printer) : NULL); -} - - -static char * -loadPPD(http_t *http, - const char *name) -{ - // This function replaces cupsGetPPD2(), but is much simplified - // (does not support classes) and works with non-standard (!= 631) - // ports - - char uri[HTTP_MAX_URI]; - char *resource; - int fd, status; - char tempfile[1024] = ""; - - // Download URI and resource for the PPD file - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "http", NULL, - "localhost", 0, "/printers/%s.ppd", name); - resource = strstr(uri, "/printers/"); - - // Download the file - fd = cupsTempFd(tempfile, sizeof(tempfile)); - status = cupsGetFd(http, resource, fd); - close(fd); - - // Check for errors - if (status == HTTP_STATUS_OK) - { - if (tempfile[0]) - return(strdup(tempfile)); - } - else if (tempfile[0]) - unlink(tempfile); - return (NULL); -} - - -static int -record_printer_options(const char *printer) -{ - remote_printer_t *p; - char filename[1024]; - FILE *fp = NULL; - char uri[HTTP_MAX_URI], *resource; - ipp_t *request, *response; - ipp_attribute_t *attr; - const char *key; - char buf[65536], *c; - char *ppdname = NULL; - ppd_file_t *ppd; - ppd_option_t *ppd_opt; - cups_option_t *option; - int i; - // List of IPP attributes to get recorded - static const char *attrs_to_record[] = - { - "*-default", - "auth-info-required", - //"device-uri", - "job-quota-period", - "job-k-limit", - "job-page-limit", - //"port-monitor", - "printer-error-policy", - "printer-info", - "printer-is-accepting-jobs", - "printer-is-shared", - "printer-geo-location", - "printer-location", - "printer-op-policy", - "printer-organization", - "printer-organizational-unit", - //"printer-state", - "printer-state-message", - "printer-state-reasons", - "requesting-user-name-allowed", - "requesting-user-name-denied", - NULL - }; - const char **ptr; - http_t *conn = NULL; - - if (printer == NULL || strlen(printer) == 0) - return (0); - - // Get our data about this printer - p = printer_record(printer); - - if (p == NULL) - { - debug_printf("Not recording printer options for %s: Unknown printer!\n", - printer); - return (0); - } - - if (p->status == STATUS_TO_BE_RELEASED) - { - debug_printf("Not recording printer options for externally modified printer %s.\n", - printer); - return (0); - } - - snprintf(filename, sizeof(filename), save_options_file, - printer); - - debug_printf("Recording printer options for %s to %s\n", - printer, filename); - - conn = http_connect_local (); - if (conn) - { - // If there is a PPD file for this printer, we save the local - // settings for the PPD options. - if (cups_notifier != NULL || (p && p->netprinter)) - { - if ((ppdname = loadPPD(conn, printer)) == NULL) - { - debug_printf("Unable to get PPD file for %s: %s\n", - printer, cupsLastErrorString()); - } - else if ((ppd = ppdOpenFile(ppdname)) == NULL) - { - unlink(ppdname); - debug_printf("Unable to open PPD file for %s.\n", - printer); - } - else - { - debug_printf("Recording option settings of the PPD file for %s (%s):\n", - printer, ppd->nickname); - ppdMarkDefaults(ppd); - for (ppd_opt = ppdFirstOption(ppd); ppd_opt; - ppd_opt = ppdNextOption(ppd)) - if (strcasecmp(ppd_opt->keyword, "PageRegion") != 0) - { - debug_printf(" %s=%s\n", - ppd_opt->keyword, ppd_opt->defchoice); - strncpy(buf, ppd_opt->keyword, sizeof(buf)); - p->num_options = cupsAddOption(buf, ppd_opt->defchoice, - p->num_options, &(p->options)); - } - ppdClose(ppd); - unlink(ppdname); - } - } - - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - resource = uri + (strlen(uri) - strlen(printer) - 10); - request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, - uri); - response = cupsDoRequest(conn, request, resource); - - // Write all supported printer attributes - if (response) - { - debug_printf("Recording option settings from the IPP attributes for %s:\n", - printer); - attr = ippFirstAttribute(response); - while (attr) - { - key = ippGetName(attr); - for (ptr = attrs_to_record; *ptr; ptr++) - if (strcasecmp(key, *ptr) == 0 || - (*ptr[0] == '*' && - strcasecmp(key + strlen(key) - strlen(*ptr) + 1, *ptr + 1) == 0)) - break; - if (*ptr != NULL) - { - if (strcasecmp(key, CUPS_BROWSED_DEST_PRINTER "-default") != 0) - { - ippAttributeString(attr, buf, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - c = buf; - while (*c) - { - if (*c == '\\') - memmove(c, c + 1, strlen(c)); - if (*c) c ++; - } - debug_printf(" %s=%s\n", key, buf); - p->num_options = cupsAddOption(key, buf, p->num_options, - &(p->options)); - } - } - attr = ippNextAttribute(response); - } - ippDelete(response); - } - } - else - { - debug_printf("Cannot connect to local CUPS to read out the IPP and PPD attributes for printer %s.\n", - printer); - } - - if (ppdname) - free(ppdname); - - if (p->num_options > 0) - { - fp = fopen(filename, "w+"); - if (fp == NULL) - { - debug_printf("ERROR: Failed creating file %s: %s\n", - filename, strerror(errno)); - return (-1); - } - - for (i = p->num_options, option = p->options; i > 0; i --, option ++) - if (fprintf (fp, "%s=%s\n", option->name, option->value) < 0) - { - debug_printf("ERROR: Failed to write into file %s: %s\n", - filename, strerror(errno)); - fclose(fp); - return (-1); - } - - fclose(fp); - - return (0); - } - else - return (-1); -} - - -static int -load_printer_options(const char *printer, - int num_options, - cups_option_t **options) -{ - char filename[1024]; - FILE *fp = NULL; - char *opt = NULL, *val; - size_t optlen = 0; - - if (printer == NULL || strlen(printer) == 0 || options == NULL) - return (0); - - // Prepare reading file with saved option settings - snprintf(filename, sizeof(filename), save_options_file, - printer); - - debug_printf("Loading saved printer options for %s from %s\n", - printer, filename); - - // Open the file with the saved option settings for this print queue - fp = fopen(filename, "r"); - if (fp == NULL) - { - debug_printf("Failed reading file %s, probably no options recorded yet\n", - filename); - } - else - { - // Now read the lines of the file and add each setting to our request - errno = 0; - debug_printf("Loading following option settings for printer %s:\n", - printer); - while (getline(&opt, &optlen, fp) != -1) - { - if (strlen(opt) > 1 && (val = strchr(opt, '=')) != NULL) - { - *val = '\0'; - val ++; - val[strlen(val)-1] = '\0'; - debug_printf(" %s=%s\n", opt, val); - num_options = cupsAddOption(opt, val, num_options, options); - } - } - debug_printf("\n"); - if (errno != 0) - debug_printf("Failed reading saved options file %s: %s\n", - filename, strerror(errno)); - free(opt); - fclose(fp); - } - return (num_options); -} - - -static int -queue_creation_handle_default(const char *printer) -{ - // No default printer management if we cannot get D-Bus notifications - // from CUPS - if (cups_notifier == NULL) - return (0); - // If this queue is recorded as the former default queue (and the current - // default is local), set it as default (the CUPS notification handler - // will record the local default printer then) - char *recorded_default = retrieve_default_printer(0); - if (recorded_default == NULL || strcasecmp(recorded_default, printer)) - { - if (recorded_default) - free(recorded_default); - return (0); - } - free(recorded_default); - char *current_default = get_cups_default_printer(); - if (current_default == NULL || !is_created_by_cups_browsed(current_default)) - { - if (set_cups_default_printer(printer) < 0) - { - debug_printf("ERROR: Could not set former default printer %s as default again.\n", - printer); - free(current_default); - return (-1); - } - else - { - debug_printf("Former default printer %s re-appeared, set as default again.\n", - printer); - invalidate_default_printer(0); - } - } - free(current_default); - return (0); -} - - -static int -queue_removal_handle_default(const char *printer) -{ - // No default printer management if we cannot get D-Bus notifications - // from CUPS - if (cups_notifier == NULL) - return (0); - // If the queue is the default printer, get back - // to the recorded local default printer, record this queue for getting the - // default set to this queue again if it re-appears. - // We call this also if a queue is only conserved because on cups-browsed - // shutdown it still has jobs - if (!is_cups_default_printer(printer)) - return (0); - // Record the fact that this printer was default - if (record_default_printer(default_printer, 0) < 0) - { - // Delete record file if recording failed - debug_printf("ERROR: Failed recording remote default printer (%s). Removing the file with possible old recording.\n", - printer); - invalidate_default_printer(0); - } - else - debug_printf("Recorded the fact that the current printer (%s) is the default printer before deleting the queue and returning to the local default printer.\n", - printer); - // Switch back to a recorded local printer, if available - char *local_default = retrieve_default_printer(1); - if (local_default != NULL) - { - if (set_cups_default_printer(local_default) >= 0) - { - debug_printf("Switching back to %s as default printer.\n", - local_default); - free(local_default); - } - else - { - debug_printf("ERROR: Unable to switch back to %s as default printer.\n", - local_default); - free(local_default); - return (-1); - } - } - invalidate_default_printer(1); - return (0); -} - - -static char * -get_local_queue_name(const char *service_name, - const char *make_model, - const char *resource, - const char *remote_host, - int *is_cups_queue, - const char *exclude) -{ - char *queue_name = NULL, *backup_queue_name = NULL, - *local_queue_name = NULL, *local_queue_name_lower = NULL; - local_printer_t *local_printer = NULL; - cluster_t *cluster = NULL; - char *member = NULL, *str = NULL; - - if (*is_cups_queue) - { - // This is a remote CUPS printer - // Determine the queue name - if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_MAKE_MODEL && - make_model) - // Works only with DNS-SD-discovered queues as otherwise we have no - // make/model info - queue_name = remove_bad_chars(make_model, 0); - else if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_REMOTE_NAME) - // Not directly used in script generation input later, but taken from - // packet, so better safe than sorry. (consider second loop with - // backup_queue_name) - queue_name = remove_bad_chars(strrchr(resource, '/') + 1, 0); - else - // Convert DNS-SD service name into a CUPS queue name exactly - // as CUPS would do it, to override CUPS' own temporary queue - // generation mechanism - queue_name = remove_bad_chars(service_name, 2); - } - else - { - // This is an IPP-based network printer - // Determine the queue name - if (LocalQueueNamingIPPPrinter == LOCAL_QUEUE_NAMING_MAKE_MODEL && - make_model) - // Works only if we actually have make/model info in the DNS-SD record - queue_name = remove_bad_chars(make_model, 0); - else - // Convert DNS-SD service name into a CUPS queue name exactly - // as CUPS would do it, to override CUPS' own temporary queue - // generation mechanism - queue_name = remove_bad_chars(service_name, 2); - } - // Check if there exists already a CUPS queue with the - // requested name Try name@host in such a case and if - // this is also taken, ignore the printer - - // Get available CUPS queues - update_local_printers (); - - // We skip trying to use the queue name purely derived from the - // remote CUPS queue name or make and model for remote CUPS queues - // when automatic clustering of remote CUPS queues is turned off, - // to directly create queues with names containing the server name - // to avoid name clashes and with this remote queues skipped by - // cups-browsed. - if ((!*is_cups_queue || - AutoClustering == 1 || - LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_DNSSD) && - (!exclude || strcasecmp(queue_name, exclude))) - { - // Is there a local queue with the name of the remote queue? - local_queue_name_lower = g_ascii_strdown(queue_name, -1); - local_printer = g_hash_table_lookup (local_printers, - local_queue_name_lower); - free(local_queue_name_lower); - // To decide on whether the queue name is already taken, only - // consider CUPS queues not created by us. - if (local_printer && !local_printer->cups_browsed_controlled) - { - debug_printf("Queue name %s already taken.\n", - queue_name); - local_queue_name = NULL; - } - else - local_queue_name = strdup(queue_name); - } - // Use the originally chosen queue name plus the server name if the - // original name is already taken or if we had skipped using it. Do - // this only if we do not use DNS-SD-service-name-based naming. - if (!local_queue_name && - (!*is_cups_queue || - LocalQueueNamingRemoteCUPS != LOCAL_QUEUE_NAMING_DNSSD) && - (is_cups_queue || - LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD)) - { - if ((backup_queue_name = malloc((strlen(queue_name) + - strlen(remote_host) + 2) * - sizeof(char))) == NULL) - { - debug_printf("ERROR: Unable to allocate memory.\n"); - exit(1); - } - sprintf(backup_queue_name, "%s@%s", queue_name, remote_host); - local_queue_name = backup_queue_name; - debug_printf("Using fallback queue name: %s\n", - local_queue_name); - // Is there a local queue with the name @? - local_queue_name_lower = g_ascii_strdown(local_queue_name, -1); - local_printer = g_hash_table_lookup (local_printers, - local_queue_name_lower); - free(local_queue_name_lower); - if ((local_printer && !local_printer->cups_browsed_controlled) || - (exclude && !strcasecmp(local_queue_name, exclude))) - { - // Found also a local queue with name @ (or - // this name is explicitly excluded), so ignore this remote - // printer - debug_printf("%s also taken, printer ignored.\n", - local_queue_name); - free(backup_queue_name); - local_queue_name = NULL; - } - } - free(queue_name); - if (!local_queue_name) - { - debug_printf("No suitable local queue name found, printer ignored.\n"); - return (NULL); - } - - // Check whether our new printer matches one of the user-defined - // printer clusters - for (cluster = cupsArrayFirst(clusters); - cluster; - cluster = cupsArrayNext(clusters)) - { - if (exclude && !strcasecmp(cluster->local_queue_name, exclude)) - continue; - local_queue_name_lower = g_ascii_strdown(cluster->local_queue_name, -1); - local_printer = g_hash_table_lookup (local_printers, - local_queue_name_lower); - free(local_queue_name_lower); - if (local_printer && !local_printer->cups_browsed_controlled) - continue; - for (member = cupsArrayFirst(cluster->members); - member; - member = cupsArrayNext(cluster->members)) - { - // Match remote CUPS queue name - if ((str = strrchr(resource, '/')) != NULL && strlen(str) > 1) - { - str = remove_bad_chars(str + 1, 2); - if (strcasecmp(member, str) == 0) // Match - break; - free(str); - } - // Match make and model - if (make_model) - { - str = remove_bad_chars(make_model, 2); - if (strcasecmp(member, str) == 0) // Match - break; - free(str); - } - // Match DNS-SD service name - if (service_name) - { - str = remove_bad_chars(service_name, 2); - if (strcasecmp(member, str) == 0) // Match - break; - free(str); - } - } - if (member) - break; - } - if (cluster) - { - local_queue_name = strdup(cluster->local_queue_name); - *is_cups_queue = 2; - free(str); - } - else if (AutoClustering) - { - // If we do automatic clustering by matching queue names, do not - // add a queue to a manually defined cluster because it matches - // the cluster's local queue name. Manually defined clusters can - // only be joined by printers which match one of the cluster's - // member names - for (cluster = cupsArrayFirst(clusters); - cluster; - cluster = cupsArrayNext(clusters)) - { - if (strcasecmp(local_queue_name, cluster->local_queue_name) == 0) - { - debug_printf("We have already a manually defined printer cluster with the name %s. Automatic clustering does not add this printer to this cluster as it does not match any of the cluster's member names. Skipping this printer.\n", - local_queue_name); - debug_printf("In cups-browsed.conf try \"LocalQueueNamingRemoteCUPS DNS-SD\" or give another name to your manually defined cluster (\"Cluster\" directive) to avoid name clashes.\n"); - free(local_queue_name); - return (NULL); - } - } - } - return (local_queue_name); -} - - -static int -join_cluster_if_needed(remote_printer_t *p, - int is_cups_queue) -{ - // is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue, - // 2: Remote CUPS queue in user-defined cluster - - remote_printer_t *q; - - for (q = (remote_printer_t *)cupsArrayFirst(remote_printers); - q; - q = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (q != p && - !strcasecmp(q->queue_name, p->queue_name) && // Queue with same name - // on server - !q->slave_of) // Find the master of the queues with this name, - // to avoid "daisy chaining" - break; - if (q && AutoClustering == 0 && (is_cups_queue == 1 || is_cups_queue == 0)) - { - debug_printf("We have already created a queue with the name %s for another remote CUPS printer but automatic clustering of equally named printers is turned off nor did we find a manually defined cluster this printer belongs to. Skipping this printer.\n", p->queue_name); - debug_printf("In cups-browsed.conf try setting \"AutoClustering On\" to cluster equally-named remote CUPS printers, \"LocalQueueNamingRemoteCUPS DNS-SD\" to avoid queue name clashes, or define clusters with the \"Cluster\" directive.\n"); - return (-1); - } - - p->slave_of = (q && q->status != STATUS_DISAPPEARED && - q->status != STATUS_UNCONFIRMED && - q->status != STATUS_TO_BE_RELEASED) ? q : NULL; - if (p->slave_of) - { - debug_printf("Printer %s already available through host %s, port %d.\n", - p->queue_name, q->host, q->port); - // Update q - q->status = STATUS_TO_BE_CREATED; - q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - log_cluster(p); - } - else if (q) - { - q->slave_of = p; - debug_printf("Unconfirmed/disappeared printer %s already available through host %s, port %d, marking that printer a slave of the newly found one.\n", - p->queue_name, q->host, q->port); - log_cluster(p); - } - return (q ? 1 : 0); -} - - -static void -on_printer_state_changed (CupsNotifier *object, - const gchar *text, - const gchar *printer_uri, - const gchar *printer, - guint printer_state, - const gchar *printer_state_reasons, - gboolean printer_is_accepting_jobs, - gpointer user_data) -{ - char *ptr, buf[2048]; - - debug_printf("on_printer_state_changed() in THREAD %ld\n", pthread_self()); - - debug_printf("[CUPS Notification] Printer state change on printer %s: %s\n", - printer, text); - debug_printf("[CUPS Notification] Printer state reasons: %s\n", - printer_state_reasons); - - if (terminating) - { - debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n"); - return; - } - - if (autoshutdown && autoshutdown_on == NO_JOBS) - { - if (check_jobs() == 0) - { - // If auto shutdown is active for triggering on no jobs being left, we - // schedule the shutdown in autoshutdown_timeout seconds - if (!autoshutdown_exec_id) - { - debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n", - autoshutdown_timeout); - autoshutdown_exec_id = - g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, - NULL); - } - } - else - { - // If auto shutdown is active for triggering on no jobs being left, we - // cancel a shutdown in autoshutdown_timeout seconds as there are jobs - // again. - if (autoshutdown_exec_id) - { - debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n"); - g_source_remove(autoshutdown_exec_id); - autoshutdown_exec_id = 0; - } - } - } - - if ((ptr = strstr(text, " is now the default printer")) != NULL) - { - // Default printer has changed, we are triggered by the new default - // printer - strncpy(buf, text, ptr - text); - buf[ptr - text] = '\0'; - debug_printf("[CUPS Notification] Default printer changed from %s to %s.\n", - default_printer, buf); - if (is_created_by_cups_browsed(default_printer)) - { - // Previous default printer created by cups-browsed - if (!is_created_by_cups_browsed(buf)) - { - // New default printer local - // Removed backed-up local default printer as we do not have a - // remote printer as default any more - invalidate_default_printer(1); - debug_printf("Manually switched default printer from a cups-browsed-generated one to a local printer.\n"); - } - } - else - { - // Previous default printer local - if (is_created_by_cups_browsed(buf)) - { - // New default printer created by cups-browsed - // Back up the local default printer to be able to return to it - // if the remote printer disappears - if (record_default_printer(default_printer, 1) < 0) - { - // Delete record file if recording failed - debug_printf("ERROR: Failed recording local default printer. Removing the file with possible old recording.\n"); - invalidate_default_printer(1); - } - else - debug_printf("Recorded previous default printer so that if the currently selected cups-browsed-generated one disappears, we can return to the old local one.\n"); - // Remove a recorded remote printer as after manually selecting - // another one as default this one is not relevant any more - invalidate_default_printer(0); - } - } - if (default_printer != NULL) - free((void *)default_printer); - default_printer = strdup(buf); - } - else if ((ptr = strstr(text, " is no longer the default printer")) - != NULL) - { - // Default printer has changed, we are triggered by the former default - // printer - strncpy(buf, text, ptr - text); - buf[ptr - text] = '\0'; - debug_printf("[CUPS Notification] %s not default printer any more.\n", buf); - } -} - - -static void -on_job_state (CupsNotifier *object, - const gchar *text, - const gchar *printer_uri, - const gchar *printer, - guint printer_state, - const gchar *printer_state_reasons, - gboolean printer_is_accepting_jobs, - guint job_id, - guint job_state, - const gchar *job_state_reasons, - const gchar *job_name, - guint job_impressions_completed, - gpointer user_data) -{ - int i, count; - char buf[2048]; - remote_printer_t *p, *q, *r, *s=NULL; - http_t *http = NULL; - ipp_t *request, *response, *printer_attributes = NULL; - ipp_attribute_t *attr; - const char *pname = NULL; - ipp_pstate_t pstate = IPP_PRINTER_IDLE; - int paccept = 0; - int num_jobs, min_jobs = 99999999; - char destination_uri[1024]; - const char *dest_host = NULL; - int dest_index = 0; - int valid_dest_found = 0; - char uri[HTTP_MAX_URI]; - int num_options; - cups_option_t *options; - int num_of_printers; - char* document_format; - int print_quality = 0; - const char *pdl = NULL; - cups_array_t *pdl_list; - char resolution[32]; - cf_res_t *max_res = NULL, *min_res = NULL, *res = NULL; - int xres, yres; - int got_printer_info; - static const char *pattrs[] = - { - "printer-name", - "printer-state", - "printer-is-accepting-jobs" - }; - http_t *conn = NULL; - - debug_printf("on_job_state() in THREAD %ld\n", pthread_self()); - - debug_printf("[CUPS Notification] Job state changed on printer %s: %s\n", - printer, text); - debug_printf("[CUPS Notification] Printer state reasons: %s\n", - printer_state_reasons); - debug_printf("[CUPS Notification] Job ID: %d\n", - job_id); - debug_printf("[CUPS Notification] Job State: %s\n", - job_state_reasons); - debug_printf("[CUPS Notification] Job is processing: %s\n", - job_state == IPP_JOB_PROCESSING ? "Yes" : "No"); - - if (terminating) - { - debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n"); - return; - } - - if (autoshutdown && autoshutdown_on == NO_JOBS) - { - if (check_jobs() == 0) - { - // If auto shutdown is active for triggering on no jobs being left, we - // schedule the shutdown in autoshutdown_timeout seconds - if (!autoshutdown_exec_id) - { - debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n", autoshutdown_timeout); - autoshutdown_exec_id = - g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, - NULL); - } - } - else - { - // If auto shutdown is active for triggering on no jobs being left, we - // cancel a shutdown in autoshutdown_timeout seconds as there are jobs - // again. - if (autoshutdown_exec_id) - { - debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n"); - g_source_remove(autoshutdown_exec_id); - autoshutdown_exec_id = 0; - } - } - } - - if (job_id != 0 && job_state == IPP_JOB_PROCESSING) - { - // Printer started processing a job, check if it uses the implicitclass - // backend and if so, we select the remote queue to which to send the job - // in a way so that we get load balancing between all remote queues - // associated with this queue. - // - // There are two methods to do that (configurable in cups-browsed.conf): - // - // Queuing of jobs on the client (LoadBalancingType = QUEUE_ON_CLIENT): - // - // Here we check all remote printers assigned to this printer and to its - // slaves which is currently accepting jobs and idle. If all are busy, - // we send a failure message and the backend will close with an error code - // after some seconds of delay, to make the job getting retried making us - // checking again here. If we find a destination, we tell the backend - // which remote queue this destination is, making the backend printing the - // job there immediately. - // - // With this all waiting jobs get queued up on the client, on the servers - // there will only be the jobs which are actually printing, as we do not - // send jobs to a server which is already printing. This is also the - // method which CUPS uses for classes. Advantage is a more even - // distribution of the job workload on the servers, and if a server fails, - // there are not several jobs stuck or lost. Disadvantage is that if one - // takes the client (laptop, mobile phone, ...) out of the local network, - // printing stops with the jobs waiting in the local queue. - // - // Queuing of jobs on the servers (LoadBalancingType = QUEUE_ON_SERVERS): - // - // Here we check all remote printers assigned to this printer and to its - // slaves which is currently accepting jobs and find the one with the - // lowest amount of jobs waiting and send the job to there. So on the - // local queue we have never jobs waiting if at least one remote printer - // accepts jobs. - // - // Not having jobs waiting locally has the advantage that we can take the - // local machine from the network and all jobs get printed. Disadvantage - // is that if a server with a full queue of jobs goes away, the jobs go - // away, too. - // - // Default is queuing the jobs on the client as this is what CUPS does - // with classes. - - debug_printf("[CUPS Notification] %s starts processing a job.\n", printer); - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to set destination for job in the load-balanced cluster %s.\n", - printer); - return; - } - q = printer_record(printer); - // If we hit a slave and not the master, switch to the master - if (q && q->slave_of) - q = q->slave_of; - if (q && q->queue_name) - { - // We have remote CUPS queue(s) and so are using the implicitclass - // backend - debug_printf("[CUPS Notification] %s is using the \"implicitclass\" CUPS backend, so let us search for a destination for this job.\n", printer); - - // We keep track of the printer which we used last time and start - // checking with the next printer this time, to get a "round robin" - // type of printer usage instead of having most jobs going to the first - // printer in the list. Method taken from the cupsdFindAvailablePrinter() - // function of the scheduler/classes.c file of CUPS. - - if (q->last_printer < 0 || - q->last_printer >= cupsArrayCount(remote_printers)) - q->last_printer = 0; - log_cluster(q); - for (i = q->last_printer + 1; ; i++) - { - if (i >= cupsArrayCount(remote_printers)) - i = 0; - p = (remote_printer_t *)cupsArrayIndex(remote_printers, i); - if (!strcasecmp(p->queue_name, printer) && - p->status == STATUS_CONFIRMED) - { - num_of_printers = 0; - for (r = (remote_printer_t *)cupsArrayFirst(remote_printers); - r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (!strcmp(r->queue_name, q->queue_name)) - { - if(r->status == STATUS_DISAPPEARED || - r->status == STATUS_UNCONFIRMED || - r->status == STATUS_TO_BE_RELEASED ) - continue; - num_of_printers ++; - } - } - - // If we are in a cluster, see whether the printer supports the - // requested job attributes - if (num_of_printers > 1) - { - if (!supports_job_attributes_requested(printer, i, job_id, - &print_quality)) - { - debug_printf("Printer with uri %s in cluster %s doesn't support the requested job attributes\n", - p->uri, p->queue_name); - if (i == q->last_printer) - break; - else - continue; - } - } - debug_printf("Checking state of remote printer %s on host %s, IP %s, port %d.\n", - p->uri, p->host, p->ip, p->port); - - // Check whether the printer is idle, processing, or disabled - debug_printf("HTTP connection to %s:%d established.\n", p->host, - p->port); - response = cfGetPrinterAttributes(p->uri, pattrs, - sizeof(pattrs) / sizeof(pattrs[0]), - NULL, 0, 0); - debug_log_out(cf_get_printer_attributes_log); - if (response != NULL) - { - debug_printf("IPP request to %s:%d successful.\n", p->host, - p->port); - pname = NULL; - pstate = IPP_PRINTER_IDLE; - paccept = 0; - for (attr = ippFirstAttribute(response); attr != NULL; - attr = ippNextAttribute(response)) - { - while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER) - attr = ippNextAttribute(response); - if (attr == NULL) - break; - pname = NULL; - pstate = IPP_PRINTER_IDLE; - paccept = 0; - got_printer_info = 0; - while (attr != NULL && ippGetGroupTag(attr) == - IPP_TAG_PRINTER) - { - if (!strcmp(ippGetName(attr), "printer-name") && - ippGetValueTag(attr) == IPP_TAG_NAME) - pname = ippGetString(attr, 0, NULL); - else if (!strcmp(ippGetName(attr), "printer-state") && - ippGetValueTag(attr) == IPP_TAG_ENUM) - pstate = (ipp_pstate_t)ippGetInteger(attr, 0); - else if (!strcmp(ippGetName(attr), - "printer-is-accepting-jobs") && - ippGetValueTag(attr) == IPP_TAG_BOOLEAN) - { - paccept = ippGetBoolean(attr, 0); - got_printer_info = 1; - } - attr = ippNextAttribute(response); - } - if (got_printer_info == 0) - { - if (attr == NULL) - break; - else - continue; - } - debug_printf("IPP Response contains attributes values printer-name %s, accepting-job %d\n", - (pname ? pname : "(Not reported)"), paccept); - if (paccept) - { - debug_printf("Printer %s on host %s, port %d is accepting jobs.\n", - p->uri, p->host, p->port); - switch (pstate) - { - case IPP_PRINTER_IDLE: - valid_dest_found = 1; - dest_host = p->ip ? p->ip : p->host; - strncpy(destination_uri, p->uri, - sizeof(destination_uri) - 1); - printer_attributes = p->prattrs; - pdl = p->pdl; - s = p; - dest_index = i; - debug_printf("Printer %s on host %s, port %d is idle, take this as destination and stop searching.\n", - p->uri, p->host, p->port); - break; - case IPP_PRINTER_PROCESSING: - valid_dest_found = 1; - if (LoadBalancingType == QUEUE_ON_SERVERS) - { - num_jobs = 0; - http = - httpConnectEncryptShortTimeout - (p->ip ? p->ip : p->host, p->port, - HTTP_ENCRYPT_IF_REQUESTED); - if (http) - { - num_jobs = get_number_of_jobs(http, p->uri, 0, - CUPS_WHICHJOBS_ACTIVE); - if (num_jobs >= 0 && num_jobs < min_jobs) - { - min_jobs = num_jobs; - dest_host = p->ip ? p->ip : p->host; - strncpy(destination_uri, p->uri, - sizeof(destination_uri) - 1); - printer_attributes = p->prattrs; - pdl = p->pdl; - s = p; - dest_index = i; - } - debug_printf("Printer %s on host %s, port %d is printing and it has %d jobs.\n", - p->uri, p->host, p->port, - num_jobs); - httpClose(http); - http = NULL; - } - } - else - debug_printf("Printer %s on host %s, port %d is printing.\n", - p->uri, p->host, p->port); - break; - case IPP_PRINTER_STOPPED: - debug_printf("Printer %s on host %s, port %d is disabled, skip it.\n", - p->uri, p->host, p->port); - break; - } - } - else - { - debug_printf("Printer %s on host %s, port %d is not accepting jobs, skip it.\n", - p->uri, p->host, p->port); - } - break; - } - - ippDelete(response); - response = NULL; - - if (pstate == IPP_PRINTER_IDLE && paccept) - { - q->last_printer = i; - break; - } - } - else - debug_printf("IPP request to %s:%d failed.\n", p->host, - p->port); - } - if (i == q->last_printer) - break; - } - - // Write the selected destination host into an option of our implicit - // class queue (cups-browsed-dest-printer="") so that the - // implicitclass backend will pick it up - - if ((pdl_list = cupsArrayNew3((cups_array_func_t)strcasecmp, - NULL, NULL, 0, - (cups_acopy_func_t)strdup, - (cups_afree_func_t)free)) == NULL) - { - debug_printf("Could Not allocate memory for cups Array \n"); - return; - } - - // Finding the best pdl supported by the printer, we need to send the - // document format to the implictclass backend - if (((attr = ippFindAttribute(printer_attributes, - "document-format-supported", - IPP_TAG_MIMETYPE)) != NULL) || - (pdl && pdl[0] != '\0')) - { - const char *format = pdl; - i = 0; - count = ippGetCount(attr); - while ((attr && i < count) || // Go through formats in attribute - (!attr && pdl && pdl[0] != '\0' && format[0] != '\0')) - { - // Go through formats in pdl string (from DNS-SD record) - // Pick next format from attribute - if (attr) format = ippGetString(attr, i, NULL); - // Add format to list of supported PDLs, skip duplicates - if (!cupsArrayFind(pdl_list, (void *)format)) - cupsArrayAdd(pdl_list, (void *)format); - if (attr) - // Next format in attribute - i ++; - else - { - // Find the next format in the string pdl, if there is none left, - // go to the terminating zero - while (!isspace(*format) && *format != ',' && *format != '\0') - format ++; - while ((isspace(*format) || *format == ',') && *format != '\0') - format ++; - } - } - } - - // The priority order for the PDLs is the same as in the - // PPD generator in cupsfilters/ppdgenerator.c - document_format = (char *)malloc(sizeof(char) * 32); - if (cupsArrayFind(pdl_list, "application/vnd.cups-pdf") || - cupsArrayFind(pdl_list, "application/pdf")) - strcpy(document_format, "application/vnd.cups-pdf"); - else if (cupsArrayFind(pdl_list, "image/urf")) - strcpy(document_format, "image/urf"); - else if (cupsArrayFind(pdl_list, "image/pwg-raster")) - strcpy(document_format, "image/pwg-raster"); - else if (cupsArrayFind(pdl_list, "application/PCLm")) - strcpy(document_format, "application/PCLm"); - else if (cupsArrayFind(pdl_list, "application/vnd.hp-pclxl")) - strcpy(document_format, "application/vnd.hp-pclxl"); - else if (cupsArrayFind(pdl_list, "application/vnd.cups-postscript") || - cupsArrayFind(pdl_list, "application/postscript")) - strcpy(document_format, "application/postscript"); - else if (cupsArrayFind(pdl_list, "application/vnd.hp-pcl") || - cupsArrayFind(pdl_list, "application/pcl") || - cupsArrayFind(pdl_list, "application/x-pcl")) - strcpy(document_format, "application/pcl"); - - if (pdl_list) - cupsArrayDelete(pdl_list); - - // Deciding the resolution to be sent with the job - // Finding the minimum and maximum resolution supported by the printer - - max_res = cfNewResolution(0, 0); - min_res = cfNewResolution(0, 0); - - if (s && - ((attr = ippFindAttribute(s->prattrs, "printer-resolution-supported", - IPP_TAG_RESOLUTION)) != NULL)) - { - for (i = 0, count = ippGetCount(attr); i < count; i ++) - { - if ((res = cfIPPResToResolution(attr, i)) != NULL) - { - debug_printf("%d %d\n",res->x,res->y); - if (i == 0) - { - max_res->x = res->x; - max_res->y = res->y; - min_res->x = res->x; - min_res->y = res->y; - } - else - { - if (cfCompareResolutions((void *)res, (void *)max_res, NULL) > 0) - { - max_res->x = res->x; - max_res->y = res->y; - } - if (cfCompareResolutions((void *)res, (void *)min_res, NULL) < 0) - { - min_res->x = res->x; - min_res->y = res->y; - } - } - cfFreeResolution(res, NULL); - res = NULL; - } - } - } - - // If we are requesting normal print quality then send default - // resolution, for draft send minimum resolution and for high, - // send the maximum resolution - // If none of the below dpi is selected then default dpi will be - // sent as 600 - snprintf(resolution,sizeof(resolution), "600dpi"); - if (s && print_quality == 3) - { - if (min_res != NULL) - { - if (min_res->x == min_res->y) - snprintf(resolution,sizeof(resolution), "%ddpi", min_res->x); - else - snprintf(resolution,sizeof(resolution), "%dx%ddpi", min_res->x, - min_res->y); - } - } - else if (s && print_quality == 5) - { - if (max_res != NULL) - { - if (max_res->x == max_res->y) - snprintf(resolution, sizeof(resolution), "%ddpi", max_res->x); - else - snprintf(resolution, sizeof(resolution), "%dx%ddpi", max_res->x, - max_res->y); - } - } - else if (s) - { - if ((attr = ippFindAttribute(s->prattrs, "printer-resolution-default", - IPP_TAG_ZERO)) != NULL) - { - if ((res = cfIPPResToResolution(attr, 0)) != NULL) - { - xres = res->x; - yres = res->y; - if (xres == yres) - snprintf(resolution, sizeof(resolution), "%ddpi", xres); - else - snprintf(resolution, sizeof(resolution), "%dx%ddpi", xres, yres); - cfFreeResolution(res, NULL); - } - } - } - - cfFreeResolution(max_res, NULL); - cfFreeResolution(min_res, NULL); - - request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER); - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - if (dest_host) - { - q->last_printer = dest_index; - snprintf(buf, sizeof(buf), "\"%d %s %s %s\"", job_id, destination_uri, - document_format, resolution); - debug_printf("Destination for job %d to %s: %s\n", - job_id, printer, destination_uri); - } - else if (valid_dest_found == 1) - { - snprintf(buf, sizeof(buf), "\"%d ALL_DESTS_BUSY\"", job_id); - debug_printf("All destinations busy for job %d to %s\n", - job_id, printer); - } - else - { - snprintf(buf, sizeof(buf), "\"%d NO_DEST_FOUND\"", job_id); - debug_printf("No destination found for job %d to %s\n", - job_id, printer); - } - num_options = 0; - options = NULL; - num_options = cupsAddOption(CUPS_BROWSED_DEST_PRINTER "-default", buf, - num_options, &options); - cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION); - cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER); - ippDelete(cupsDoRequest(conn, request, "/admin/")); - - cupsFreeOptions(num_options, options); - free(document_format); - - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("ERROR: Unable to set \"" CUPS_BROWSED_DEST_PRINTER - "-default\" option to communicate the destination server for this job (%s)!\n", - cupsLastErrorString()); - return; - } - } - } -} - - -static void -on_printer_deleted (CupsNotifier *object, - const gchar *text, - const gchar *printer_uri, - const gchar *printer, - guint printer_state, - const gchar *printer_state_reasons, - gboolean printer_is_accepting_jobs, - gpointer user_data) -{ - remote_printer_t *p; - char *r; - char *local_queue_name_lower = NULL; - local_printer_t *local_printer = NULL; - - debug_printf("on_printer_deleted() in THREAD %ld\n", pthread_self()); - - debug_printf("[CUPS Notification] Printer deleted: %s\n", - text); - - if (terminating) - { - debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n"); - return; - } - - if (is_created_by_cups_browsed(printer)) - { - // Get available CUPS queues to check whether the queue did not - // already get re-created - update_local_printers (); - // Look up print queue in the list - local_queue_name_lower = g_ascii_strdown(printer, -1); - local_printer = g_hash_table_lookup (local_printers, - local_queue_name_lower); - free(local_queue_name_lower); - // If the queue is there again, do not re-create it - if (local_printer) - { - debug_printf("Printer %s already re-created.\n", - printer); - return; - } - - // a cups-browsed-generated printer got deleted, re-create it - debug_printf("Printer %s got deleted, re-creating it.\n", - printer); - // If the deleted printer was the default printer, make sure it gets the - // default printer again - if (default_printer && !strcasecmp(printer, default_printer)) - { - if (record_default_printer(printer, 0) < 0) - { - // Delete record file if recording failed - debug_printf("ERROR: Failed recording remote default printer. Removing the file with possible old recording.\n"); - invalidate_default_printer(0); - } - else - debug_printf("Recorded %s as remote default printer so that it gets set as default after re-creating.\n", - printer); - // Make sure that a recorded local default printer does not get lost - // during the recovery operation - if ((r = retrieve_default_printer(1)) != NULL) - { - if (default_printer != NULL) - free((void *)default_printer); - default_printer = r; - } - } - // Schedule for immediate creation of the CUPS queue - p = printer_record(printer); - if (p && p->status != STATUS_DISAPPEARED && - p->status != STATUS_UNCONFIRMED && - p->status != STATUS_TO_BE_RELEASED) - { - p->status = STATUS_TO_BE_CREATED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - if (in_shutdown == 0) - recheck_timer(); - } - } -} - - -static int -queue_overwritten (remote_printer_t *p) -{ - http_t *conn = NULL; - ipp_t *response = NULL; // IPP Response - ipp_attribute_t *attr; // Current attribute - const char *printername, // Print queue name - *uri, // Printer URI - *device, // Printer device URI - *makemodel; // Printer make and model - // (equals PPD NickName) - char local_queue_uri[1024]; - static const char *pattrs[] = // Attributes we need for printers... - { - "printer-name", - "printer-uri-supported", - "device-uri", - "printer-make-and-model" - }; - int overwritten = 0; - - if (p->overwritten) - // We already have discovered that this queue got overwritten - // so we do not repeat the tests and exit positively - return (1); - - if (p->uri[0] == '\0') - // Also skip unconfirmed printer entries from queues of the - // previous session, they do not have a PPD file registered, so we - // cannot compare - return (0); - - // Get the device URI which our CUPS queue actually has now, a - // change of the URI means a modification or replacement of the - // print queue by something user-defined. So we schedule this queue - // for release from handling by cups-browsed. - // - // In a second step get the NickName of the PPD which our CUPS queue - // actually uses now, a change of the NickName means a replacement - // of the PPD of the print queue by a user-selected one. So we - // schedule this queue for release from handling by cups-browsed - // also in this case. - // - // We only need the NickName from the PPD and due to the fact that - // the cupsGetPPD2() function does not work when CUPS is on a - // non-standard port (!= 631, Bug!) and the NickName is also in the - // get-printer-attributes IPP response as "printer-make-and-model", - // we go the IPP way here and do not download the printer's PPD. - - conn = http_connect_local (); - if (conn == NULL) - { - debug_printf("Cannot connect to local CUPS to see whether queue %s got overwritten.\n", - p->queue_name); - return (0); - } - - // URI of the local CUPS queue (not the device URI - httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri, - sizeof(local_queue_uri), - "ipp", NULL, "localhost", 0, - "/printers/%s", p->queue_name); - response = cfGetPrinterAttributes2(conn, local_queue_uri, - pattrs, sizeof(pattrs) / sizeof(pattrs[0]), - pattrs, sizeof(pattrs) / sizeof(pattrs[0]), - 1); - debug_log_out(cf_get_printer_attributes_log); - if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING) - { - debug_printf("lpstat: %s\n", cupsLastErrorString()); - } - else - { - printername = NULL; - device = NULL; - uri = NULL; - makemodel = NULL; - for (attr = ippFirstAttribute(response); attr != NULL; - attr = ippNextAttribute(response)) - { - if (!strcmp(ippGetName(attr), "printer-name") && - ippGetValueTag(attr) == IPP_TAG_NAME) - printername = ippGetString(attr, 0, NULL); - if (!strcmp(ippGetName(attr), "printer-uri-supported") && - ippGetValueTag(attr) == IPP_TAG_URI) - uri = ippGetString(attr, 0, NULL); - if (!strcmp(ippGetName(attr), "device-uri") && - ippGetValueTag(attr) == IPP_TAG_URI) - device = ippGetString(attr, 0, NULL); - if (!strcmp(ippGetName(attr), "printer-make-and-model") && - ippGetValueTag(attr) == IPP_TAG_TEXT) - makemodel = ippGetString(attr, 0, NULL); - } - if (printername != NULL && - strcasecmp(p->queue_name, printername) == 0) - { - if (device == NULL) - device = uri; - // Check device URI - if (device != NULL && - (p->uri == NULL || - (strlen(device) < 16 || - strncmp(device, "implicitclass://", 16)))) - { - // The printer's device URI is different to what we have - // assigned, so we got notified because the queue was - // externally modified and so we will release this printer - // from the control of cups-browsed - debug_printf("Printer %s got modified externally, discovered by a change of its device URI from %s to %s.\n", - p->queue_name, - (p->uri ? (p->netprinter ? p->uri : - "implicitclass://...") : - "(not yet determined)"), - device); - overwritten = 1; - } - // Check NickName - if (p->nickname == NULL || makemodel == NULL || - strcasecmp(p->nickname, makemodel)) - { - // The PPD file of the queue got replaced which we - // discovered by comparing the NickName of the PPD with the - // NickName which the PPD we have used has. So we were - // notified because the queue was externally modified and so - // we will release this printer from the control of - // cups-browsed - debug_printf("Printer %s got modified externally, discovered by the NickName of its PPD file having changed from \"%s\" to \"%s\".\n", - p->queue_name, (p->nickname ? p->nickname : "(no PPD)"), - (makemodel ? makemodel : - "(NickName not readable)")); - overwritten = 1; - } - } - } - if (response) ippDelete(response); - - return (overwritten); -} - - -static void -on_printer_modified (CupsNotifier *object, - const gchar *text, - const gchar *printer_uri, - const gchar *printer, - guint printer_state, - const gchar *printer_state_reasons, - gboolean printer_is_accepting_jobs, - gpointer user_data) -{ - remote_printer_t *p; - http_t *conn = NULL; - ipp_t *request; // IPP Request - int re_create, is_cups_queue; - char *new_queue_name; - cups_array_t *to_be_renamed; - char local_queue_uri[1024]; - char *resolved_uri = NULL; - - debug_printf("on_printer_modified() in THREAD %ld\n", pthread_self()); - - debug_printf("[CUPS Notification] Printer modified: %s\n", - text); - pthread_rwlock_wrlock(&lock); - if (is_created_by_cups_browsed(printer)) - { - p = printer_record(printer); - if (p->overwritten) - // We already have discovered that this queue got overwritten - // and are treating the process appropriately, so return now to - // avoid an infinite recursion - goto end; - - if (queue_overwritten(p)) - { - // Our generated local queue pointing to a remote printer got - // overwritten by an externally created queue with the same - // name. - // We will release control from this queue now and try to - // re-create our queue under a different name, usually - // @. - // If we have slaves, we have to do this for them, too. - - p->overwritten = 1; - - // First, remove the "cups-browsed=true" from the queue's - // options, so that cups-browsed considers this queue as created - // manually - debug_printf("Removing \"cups-browsed=true\" from CUPS queue %s (%s).\n", - p->queue_name, p->uri); - conn = http_connect_local (); - if (conn == NULL) - debug_printf("Browse send failed to connect to localhost\n"); - else - { - request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_PRINTER); - // Printer URI: ipp://localhost/printers/ - httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri, - sizeof(local_queue_uri), - "ipp", NULL, "localhost", 0, - "/printers/%s", p->queue_name); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, local_queue_uri); - // Default user - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - // Option to be removed - ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_DELETEATTR, - CUPS_BROWSED_MARK "-default", 0); - // Do it - ippDelete(cupsDoRequest(conn, request, "/admin/")); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - debug_printf("Unable to remove \"cups-browsed=true\" from CUPS queue!\n"); - } - - // Now try to rename all our printer entries with this - // queue name. Drop entries where renaming fails - to_be_renamed = cupsArrayNew(NULL, NULL); - // Put the printer entries which need attention into - // a separate array, as we cannot run two nested loops - // on one CUPS array, as our printer entry array - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (strcasecmp(p->queue_name, printer) == 0) - { - p->overwritten = 1; - cupsArrayAdd(to_be_renamed, p); - } - for (p = (remote_printer_t *)cupsArrayFirst(to_be_renamed); - p; p = (remote_printer_t *)cupsArrayNext(to_be_renamed)) - { - is_cups_queue = (p->netprinter == 0 ? 1 : 0); - re_create = 1; - // Is there a local queue with the same URI as the remote queue? - if (g_hash_table_find (local_printers, - local_printer_is_same_device, p)) - { - // Found a local queue with the same URI as our discovered printer - // would get, so ignore this remote printer - debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, no replacement queue to be created.\n", - p->uri); - re_create = 0; - } - else if ((new_queue_name = // Try to find a new queue name - get_local_queue_name(p->service_name, p->make_model, - p->resource, p->host, - &is_cups_queue, - p->queue_name)) == NULL) - { - // Not able to find a new name for the queue - debug_printf("No new name for printer found, no replacement queue to be created.\n"); - re_create = 0; - } - else - { - free(p->queue_name); - p->queue_name = new_queue_name; - // Check whether the queue under its new name will be stand-alone or - // part of a cluster - if (join_cluster_if_needed(p, is_cups_queue) < 0) - { - // There are other cups-browsed-generated queues with the new - // name, not able to cluster this queue with them - debug_printf("Not able to cluster this queue with equally-named ones.\n"); - re_create = 0; - } - } - if (resolved_uri) - free(resolved_uri); - if (re_create) - { - p->overwritten = 0; - p->status = STATUS_TO_BE_CREATED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - debug_printf("Released CUPS queue %s from the control of cups-browsed. Printer with URI %s renamed to %s.\n", - printer, p->uri, p->queue_name); - } - else - { - // To remove this entry independent of any other entry we - // set the slave_of to NULL. This does not lead to an - // attempt to remove a CUPS queue as we have the status - // STATUS_TO_BE_RELEASED - p->slave_of = NULL; - p->status = STATUS_TO_BE_RELEASED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - debug_printf("Released CUPS queue %s from the control of cups-browsed. No local queue any more for printer with URI %s.\n", - printer, p->uri); - } - } - cupsArrayDelete(to_be_renamed); - if (in_shutdown == 0) - recheck_timer(); - } - else - { - if (terminating) - { - debug_printf("[CUPS Notification]: Not saving external option changes because cups-browsed is terminating.\n"); - goto end; - } - // The user has changed settings of a printer which we have generated, - // backup the changes for the case of a crash or unclean shutdown of - // cups-browsed. - if (!p->no_autosave) - { - debug_printf("Settings of printer %s got modified, doing backup.\n", - p->queue_name); - p->no_autosave = 1; // Avoid infinite recursion - record_printer_options(p->queue_name); - p->no_autosave = 0; - } - } - } - - end: - pthread_rwlock_unlock(&lock); -} - - -// This compare function makes the "lo" (looback) interface always -// sorted to the beginning of the array, this way one only needs to -// check the first element of the error to find out whether a remote -// printer is already available through the loopback interface (preferred -// interface) or not. -// All other interfaces are sorted alphabetically, the types let IPPS -// appear before IPP, and the families numerically (makes IPv4 appear -// before IPv6). - -static int -ipp_discovery_cmp(void *va, void *vb, void *data) -{ - ipp_discovery_t *a = (ipp_discovery_t *)va; - ipp_discovery_t *b = (ipp_discovery_t *)vb; - int cmp; - - if (!a && !b) - return (0); - if (a && !b) - return (-1); - if (!a && b) - return (1); - - if (!strcasecmp(a->interface, "lo") && strcasecmp(b->interface, "lo")) - return (-1); - if (strcasecmp(a->interface, "lo") && !strcasecmp(b->interface, "lo")) - return (1); - - cmp = strcasecmp(a->interface, b->interface); - if (cmp) - return (cmp); - - if (strcasestr(a->type, "ipps") && !strcasestr(b->type, "ipps")) - return (-1); - if (!strcasestr(a->type, "ipps") && strcasestr(b->type, "ipps")) - return (1); - - cmp = strcasecmp(a->type, b->type); - if (cmp) - return (cmp); - - if (a->family < b->family) - return (-1); - else if (a->family > b->family) - return (1); - else - return (0); -} - - -static void -ipp_discovery_free(void *ve, void *data) -{ - ipp_discovery_t *e = (ipp_discovery_t *)ve; - - if (e) - { - if (e->interface) - free(e->interface); - if (e->type) - free(e->type); - free(e); - } -} - - -static void -ipp_discoveries_list(cups_array_t *a) -{ - ipp_discovery_t *e; - - debug_printf("Printer discovered %d times:\n", cupsArrayCount(a)); - for (e = cupsArrayFirst(a); e; e = cupsArrayNext(a)) - debug_printf(" %s, %s, %s\n", e->interface, e->type, - (e->family == AF_INET ? "IPv4" : - (e->family == AF_INET6 ? "IPv6" : "???"))); -} - - -static int -ipp_discoveries_add(cups_array_t *a, - const char *interface, - const char *type, - int family) -{ - ipp_discovery_t *e; - - if (!interface || !type) - return (0); - if ((e = (ipp_discovery_t *)calloc(1, sizeof(ipp_discovery_t))) == - NULL) - { - debug_printf("ERROR: Unable to allocate memory.\n"); - return (0); - } - e->interface = strdup(interface); - e->type = strdup(type); - e->family = family; - cupsArrayAdd(a, e); - ipp_discoveries_list(a); - return (1); -} - - -static remote_printer_t * -create_remote_printer_entry (const char *queue_name, - const char *location, - const char *info, - const char *uri, - const char *host, - const char *ip, - int port, - const char *resource, - const char *service_name, - const char *type, - const char *domain, - const char *interface, - int family, - const char *pdl, - int color, - int duplex, - const char *make_model, - int is_cups_queue) -{ - remote_printer_t *p; - remote_printer_t *q; - http_t *http_printer = NULL; - int i; - ipp_attribute_t *attr; - char valuebuffer[65536]; - int is_pwgraster = 0; - int is_appleraster = 0; - int is_pclm = 0; - int is_pdf = 0; - - if (!queue_name || !location || !info || !uri || !host || !resource || - !service_name || !type || !domain) - { - debug_printf("ERROR: create_remote_printer_entry(): Input value missing!\n"); - return (NULL); - } - - // Mark this as a queue to be created locally pointing to the printer - if ((p = (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) - { - debug_printf("ERROR: Unable to allocate memory.\n"); - return (NULL); - } - - // Assure that, if we have forgotten to set a field in the printer - // record, that it is set to zero - memset(p, 0, sizeof(remote_printer_t)); - - p->called = 0; - - // Queue name - p->queue_name = strdup(queue_name); - if (!p->queue_name) - goto fail; - - p->location = strdup(location); - if (!p->location) - goto fail; - - p->info = strdup(info); - if (!p->info) - goto fail; - - if (make_model) - p->make_model = strdup(make_model); - else - p->make_model = NULL; - - if (pdl) - p->pdl = strdup(pdl); - else - p->pdl = NULL; - - p->color = color; - - p->duplex = duplex; - - p->uri = strdup(uri); - if (!p->uri) - goto fail; - - p->slave_of = NULL; - p->last_printer = -1; - - p->num_options = 0; - p->options = NULL; - - p->host = strdup (host); - if (!p->host) - goto fail; - - p->ip = (ip != NULL ? strdup (ip) : NULL); - - p->port = (port != 0 ? port : 631); - - p->resource = strdup (resource); - if (!p->resource) - goto fail; - - p->service_name = strdup (service_name); - if (!p->service_name) - goto fail; - - // Record DNS-SD service parameters to identify print queue - // entry for removal when service disappears - p->type = strdup (type); - if (!p->type) - goto fail; - - p->domain = strdup (domain); - if (!p->domain) - goto fail; - - p->ipp_discoveries = - cupsArrayNew3(ipp_discovery_cmp, NULL, NULL, 0, NULL, ipp_discovery_free); - if (p->ipp_discoveries == NULL) - { - debug_printf("ERROR: Unable to allocate memory.\n"); - return (NULL); - } - if (domain != NULL && domain[0] != '\0' && - type != NULL && type[0] != '\0') - ipp_discoveries_add(p->ipp_discoveries, interface, type, family); - - // Schedule for immediate creation of the CUPS queue - p->status = STATUS_TO_BE_CREATED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - - // Flag which can be set to inhibit automatic saving of option settings - // by the on_printer_modified() notification handler function - p->no_autosave = 0; - - // Flag to be set when a local queue generated by us was overwritten - // by an external process. It serves to avoid that the process to - // treat this case is not repeated in an infinite recursion - p->overwritten = 0; - - // Flag to mark whether this printer was discovered through a legacy - // CUPS broadcast (1) or through DNS-SD (0) - p->is_legacy = 0; - - // Initialize field for how many timeouts cups-browsed experienced - // in a row during creation of this printer's queue - p->timeouted = 0; - - // Initialize nickname array for *Nickname directive from PPD - // - either from CUPS server or from our PPD generator - p->nickname = NULL; - - // Remote CUPS printer or local queue remaining from previous cups-browsed - // session - // is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue, - // 2: Remote CUPS queue in user-defined cluster - if (is_cups_queue != 0) - { - if (is_cups_queue > 0 && CreateRemoteCUPSPrinterQueues == 0) - { - debug_printf("Printer %s (%s) is a remote CUPS printer and cups-browsed is not configured to set up such printers automatically, ignoring this printer.\n", - p->queue_name, p->uri); - goto fail; - } - // For a remote CUPS printer our local queue will be raw or get a - // PPD file from the remote CUPS server, so that the driver on the - // remote CUPS server gets used. So we will not generate a PPD file - // or interface script at this point. - p->netprinter = 0; - if (p->uri[0] != '\0') - { - p->prattrs = cfGetPrinterAttributes(p->uri, NULL, 0, NULL, 0, 1); - debug_log_out(cf_get_printer_attributes_log); - if (p->prattrs == NULL) - debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n", - p->queue_name, p->uri); - } - } - else - { - // Non-CUPS printer broadcasts are most probably from printers - // directly connected to the network and using the IPP protocol. - // We check whether we can set them up without a device-specific - // driver, only using page description languages which the - // operating system provides: PCL 5c/5e/6/XL, PostScript, PDF, PWG - // Raster, Apple Raster, PCLm. Especially printers designed for - // driverless printing (DNS-SD + IPP 2.x + at least one of PWG - // Raster, Apple Raster, PCLm, PDF) will work this way. Making - // only driverless queues we can get an easy, configuration-less - // way to print from mobile devices, even if there is no CUPS - // server with shared printers around. - - if (CreateIPPPrinterQueues == IPP_PRINTERS_NO) - { - debug_printf("Printer %s (%s) is an IPP network printer and cups-browsed is not configured to set up such printers automatically, ignoring this printer.\n", - p->queue_name, p->uri); - goto fail; - } - - if (!pdl || pdl[0] == '\0' || - (!strcasestr(pdl, "application/postscript") && - !strcasestr(pdl, "application/pdf") && - !strcasestr(pdl, "image/pwg-raster") && - !strcasestr(pdl, "image/urf") && - !strcasestr(pdl, "application/PCLm") && - ((!strcasestr(pdl, "application/vnd.hp-PCL") && - !strcasestr(pdl, "application/PCL") && - !strcasestr(pdl, "application/x-pcl")) || - ((!strncasecmp(make_model, "HP", 2) || // HP inkjets not supported - !strncasecmp(make_model, "Hewlett Packard", 15) || - !strncasecmp(make_model, "Hewlett-Packard", 15)) && - !strcasestr(make_model, "LaserJet") && - !strcasestr(make_model, "Mopier"))) && - !strcasestr(pdl, "application/vnd.hp-PCLXL"))) - { - debug_printf("Cannot create remote printer %s (URI: %s, Model: %s, Accepted data formats: %s) as its PDLs are not known, ignoring this printer.\n", - p->queue_name, p->uri, make_model, pdl); - debug_printf("Supported PDLs: PWG Raster, %s%sPostScript, PDF, PCL XL, PCL 5c/e (HP inkjets report themselves as PCL printers but their PCL is not supported)\n", - "Apple Raster, ", - "PCLm, " - ); - goto fail; - } - - // Check whether we have an equally named queue already - for (q = (remote_printer_t *)cupsArrayFirst(remote_printers); - q; - q = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (!strcasecmp(q->queue_name, p->queue_name)) // Queue with same name - { - debug_printf("We have already created a queue with the name %s for another printer. Skipping this printer.\n", p->queue_name); - debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n"); - goto fail; - } - - p->slave_of = NULL; - p->netprinter = 1; - p->prattrs = cfGetPrinterAttributes(p->uri, NULL, 0, NULL, 0, 1); - debug_log_out(cf_get_printer_attributes_log); - if (p->prattrs == NULL) - { - debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n", - p->queue_name, p->uri); - goto fail; - } - - // If we have opted for only printers designed for driverless use (PWG - // Raster + Apple Raster + PCLm + PDF) being set up automatically, we check - // first, whether our printer supports IPP 2.0 or newer. If not, we - // skip this printer - if (CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) - { - valuebuffer[0] = '\0'; - debug_printf("Checking whether printer %s supports IPP 2.x or newer:\n", - p->queue_name); - if ((attr = ippFindAttribute(p->prattrs, - "ipp-versions-supported", - IPP_TAG_KEYWORD)) != NULL) - { - debug_printf(" Attr: %s\n", ippGetName(attr)); - for (i = 0; i < ippGetCount(attr); i ++) - { - strncpy(valuebuffer, ippGetString(attr, i, NULL), - sizeof(valuebuffer) - 1); - if (strlen(ippGetString(attr, i, NULL)) > 65535) - valuebuffer[65535] = '\0'; - debug_printf(" Keyword: %s\n", valuebuffer); - if (valuebuffer[0] > '1') - break; - } - } - if (!attr || valuebuffer[0] == '\0' || valuebuffer[0] <= '1') - { - debug_printf(" --> cups-browsed is configured to auto-setup only printers which are designed for driverless printing. These printers require IPP 2.x or newer, but this printer only supports IPP 1.x or older. Skipping.\n"); - goto fail; - } - else - debug_printf(" --> Printer supports IPP 2.x or newer.\n"); - } - - // If we have opted for only PWG Raster printers or for only printers - // designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF) - // being set up automatically, we check whether the printer has a non-empty - // string in its "pwg-raster-document-resolution-supported" IPP attribute - // to see whether we have a PWG Raster printer. - if (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) - { - valuebuffer[0] = '\0'; - debug_printf("Checking whether printer %s understands PWG Raster:\n", - p->queue_name); - if ((attr = ippFindAttribute(p->prattrs, - "pwg-raster-document-resolution-supported", - IPP_TAG_RESOLUTION)) != NULL) - { - debug_printf(" Attr: %s\n", ippGetName(attr)); - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - debug_printf(" Value: %s\n", valuebuffer); - if (valuebuffer[0] == '\0') - { - for (i = 0; i < ippGetCount(attr); i ++) - { - strncpy(valuebuffer, ippGetString(attr, i, NULL), - sizeof(valuebuffer) - 1); - if (strlen(ippGetString(attr, i, NULL)) > 65535) - valuebuffer[65535] = '\0'; - debug_printf(" Keyword: %s\n", valuebuffer); - if (valuebuffer[0] != '\0') - break; - } - } - } - if (attr && valuebuffer[0] != '\0') - is_pwgraster = 1; - debug_printf(" --> Printer %s PWG Raster.\n", - is_pwgraster ? "supports" : "does not support"); - } - - // If we have opted for only Apple Raster printers or for only printers - // designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF) - // being set up automatically, we check whether the printer has a non-empty - // string in its "urf-supported" IPP attribute to see whether we have an - // Apple Raster printer. - if (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) - { - valuebuffer[0] = '\0'; - debug_printf("Checking whether printer %s understands Apple Raster:\n", - p->queue_name); - if ((attr = ippFindAttribute(p->prattrs, "urf-supported", IPP_TAG_KEYWORD)) != NULL) - { - debug_printf(" Attr: %s\n", ippGetName(attr)); - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - debug_printf(" Value: %s\n", valuebuffer); - if (valuebuffer[0] == '\0') - { - for (i = 0; i < ippGetCount(attr); i ++) - { - strncpy(valuebuffer, ippGetString(attr, i, NULL), - sizeof(valuebuffer) - 1); - if (strlen(ippGetString(attr, i, NULL)) > 65535) - valuebuffer[65535] = '\0'; - debug_printf(" Keyword: %s\n", valuebuffer); - if (valuebuffer[0] != '\0') - break; - } - } - } - if (attr && valuebuffer[0] != '\0') - is_appleraster = 1; - debug_printf(" --> Printer %s Apple Raster.\n", - is_appleraster ? "supports" : "does not support"); - } - - // If we have opted for only PCLm printers or for only printers - // designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF) - // being set up automatically, we check whether the printer has a non-empty - // string in its "pclm-compression-method-preferred" IPP attribute to see - // whether we have a PCLm printer. - if (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) - { - valuebuffer[0] = '\0'; - debug_printf("Checking whether printer %s understands PCLm:\n", - p->queue_name); - if ((attr = ippFindAttribute(p->prattrs, - "pclm-compression-method-preferred", - IPP_TAG_KEYWORD)) != NULL) - { - debug_printf(" Attr: %s\n", ippGetName(attr)); - ippAttributeString(attr, valuebuffer, sizeof(valuebuffer)); - debug_printf(" Value: %s\n", p->queue_name, valuebuffer); - if (valuebuffer[0] == '\0') - { - for (i = 0; i < ippGetCount(attr); i ++) - { - strncpy(valuebuffer, ippGetString(attr, i, NULL), - sizeof(valuebuffer) - 1); - if (strlen(ippGetString(attr, i, NULL)) > 65535) - valuebuffer[65535] = '\0'; - debug_printf(" Keyword: %s\n", valuebuffer); - if (valuebuffer[0] != '\0') - break; - } - } - } - if (attr && valuebuffer[0] != '\0') - is_pclm = 1; - debug_printf(" --> Printer %s PCLm.\n", - is_pclm ? "supports" : "does not support"); - } - - // If we have opted for only PDF printers or for only printers - // designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF) - // being set up automatically, we check whether the printer has - // "application/pdf" under its PDLs. - if (CreateIPPPrinterQueues == IPP_PRINTERS_PDF || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) - { - debug_printf("Checking whether printer %s understands PDF: PDLs: %s\n", - p->queue_name, pdl); - if(strcasestr(pdl, "application/pdf")) - is_pdf = 1; - debug_printf(" --> Printer %s PDF.\n", - is_pdf ? "supports" : "does not support"); - } - - // If the printer is not the driverless printer we opted for, we skip - // this printer. - if ((CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS && - is_pwgraster == 0 && is_appleraster == 0 && is_pclm == 0 && - is_pdf == 0) || - (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER && - is_pwgraster == 0) || - (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER && - is_appleraster == 0) || - (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM && - is_pclm == 0) || - (CreateIPPPrinterQueues == IPP_PRINTERS_PDF && - is_pdf == 0)) - { - debug_printf("Printer %s (%s%s%s%s%s%s%s%s%s%s%s%s%s) does not support the driverless printing protocol cups-browsed is configured to accept for setting up such printers automatically, ignoring this printer.\n", - p->queue_name, p->uri, - (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - ", " : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - (is_pwgraster ? "" : "not ") : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - "PWG Raster" : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - ", " : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - (is_appleraster ? "" : "not ") : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - "Apple Raster" : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - ", " : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - (is_pclm ? "" : "not ") : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - "PCLm" : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PDF || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - ", " : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PDF || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - (is_pdf ? "" : "not ") : ""), - (CreateIPPPrinterQueues == IPP_PRINTERS_PDF || - CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ? - "PDF" : "")); - goto fail; - } - } - // Check whether we have an equally named queue already from another - // server and join a cluster if needed - if (join_cluster_if_needed(p, is_cups_queue) < 0) - goto fail; - // Add the new remote printer entry - log_all_printers(); - cupsArrayAdd(remote_printers, p); - log_all_printers(); - - // If auto shutdown is active we have perhaps scheduled a timer to shut down - // due to not having queues any more to maintain, kill the timer now - if (autoshutdown && autoshutdown_exec_id && - autoshutdown_on == NO_QUEUES && - cupsArrayCount(remote_printers) > 0) - { - debug_printf ("New printers there to make available, killing auto shutdown timer.\n"); - g_source_remove(autoshutdown_exec_id); - autoshutdown_exec_id = 0; - } - - if (http_printer) - httpClose(http_printer); - return (p); - - fail: - debug_printf("ERROR: Unable to create print queue, ignoring printer.\n"); - if (p->prattrs) ippDelete(p->prattrs); - if (http_printer) - httpClose(http_printer); - if (p->type) free (p->type); - if (p->service_name) free (p->service_name); - if (p->host) free (p->host); - if (p->resource) free (p->resource); - if (p->domain) free (p->domain); - cupsArrayDelete(p->ipp_discoveries); - if (p->ip) free (p->ip); - cupsFreeOptions(p->num_options, p->options); - if (p->uri) free (p->uri); - if (p->pdl) free (p->pdl); - if (p->make_model) free (p->make_model); - if (p->location) free (p->location); - if (p->info) free (p->info); - if (p->queue_name) free (p->queue_name); - if (p->nickname) free (p->nickname); - free (p); - return (NULL); -} - - -static void -remove_printer_entry(remote_printer_t *p) -{ - remote_printer_t *q = NULL, *r; - - if (p == NULL) - { - debug_printf ("ERROR: remove_printer_entry(): Supplied printer entry is NULL"); - return; - } - - if (!p->slave_of) - { - // Check whether this queue has a slave from another server and - // find it - for (q = (remote_printer_t *)cupsArrayFirst(remote_printers); - q; - q = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (q != p && q->slave_of == p && - q->status != STATUS_DISAPPEARED && q->status != STATUS_UNCONFIRMED && - q->status != STATUS_TO_BE_RELEASED) - break; - } - if (q) - { - // Make q the master of the cluster and p a slave of q. This way - // removal of p does not delete the cluster's CUPS queue and update - // of q makes sure the cluster's queue gets back into working state - for (r = (remote_printer_t *)cupsArrayFirst(remote_printers); - r; - r = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (r != q && r->slave_of == p && - r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED && - r->status != STATUS_TO_BE_RELEASED) - r->slave_of = q; - q->slave_of = NULL; - p->slave_of = q; - q->num_options = p->num_options; - q->options = p->options; - p->num_options = 0; - p->options = NULL; - // Schedule this printer for updating the CUPS queue - q->status = STATUS_TO_BE_CREATED; - q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - debug_printf("Printer %s (%s) diasappeared, replacing by backup on host %s, port %d with URI %s.\n", - p->queue_name, p->uri, q->host, q->port, q->uri); - } - else - debug_printf("Printer %s (Host: %s, Port: %d, URI: %s) disappeared and no slave available (or it is a slave of another printer), removing entry.\n", - p->queue_name, p->host, p->port, p->uri); - - // Schedule entry and its CUPS queue for removal - if (p->status != STATUS_TO_BE_RELEASED) - p->status = STATUS_DISAPPEARED; - p->timeout = time(NULL) + TIMEOUT_REMOVE; -} - - -static void -create_queue(void* arg) -{ - pthread_rwlock_wrlock(&lock); - - create_args_t* a = (create_args_t*)arg; - remote_printer_t *p, *r, *s, *master; - http_t *http; - char uri[HTTP_MAX_URI], device_uri[HTTP_MAX_URI], buf[1024], - line[1024]; - int num_options; - cups_option_t *options; - int num_jobs; - cups_job_t *jobs; - ipp_t *request; - time_t current_time; - int i, ap_remote_queue_id_line_inserted, - new_cupsfilter_line_inserted, want_raw, - num_cluster_printers = 0; - char *disabled_str; - char ppdgenerator_msg[1024]; - char *ppdfile; - char ppdname[1024]; - ipp_attribute_t *attr; - const char *loadedppd = NULL; - ppd_file_t *ppd = NULL; - ppd_choice_t *choice; - cups_file_t *in, *out; - char keyword[1024], *keyptr; - const char *customval; - const char *val = NULL; - cups_dest_t *dest = NULL; - int is_shared; - cups_array_t *conflicts = NULL; - ipp_t *printer_attributes = NULL; - cups_array_t *sizes=NULL; - ipp_t *printer_ipp_response; - char *make_model = NULL; - const char *pdl=NULL; - int color; - int duplex; - char *default_pagesize = NULL; - const char *default_color = NULL; - - debug_printf("create_queue() in THREAD %ld\n", pthread_self()); - - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if(!strcmp(p->queue_name, a->queue) && p->status == STATUS_TO_BE_CREATED) - break; - } - - pthread_rwlock_unlock(&lock); - - if (!p || (p && p->status!=STATUS_TO_BE_CREATED)) - return; - - pthread_rwlock_wrlock(&lock); - - current_time = time(NULL); - - if (p->slave_of) - { - master = p->slave_of; - if (master->queue_name) - { - p->status = STATUS_CONFIRMED; - master->status = STATUS_TO_BE_CREATED; - master->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - if (p->is_legacy) - { - p->timeout = time(NULL) + BrowseTimeout; - debug_printf("starting BrowseTimeout timer for %s (%ds)\n", - p->queue_name, BrowseTimeout); - } - else - p->timeout = (time_t) -1; - } - else - { - debug_printf("Master for slave %s is invalid (deleted?)\n", - p->queue_name); - p->status = STATUS_DISAPPEARED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - } - goto end; - } - - // Only act if the timeout has passed - if (p->timeout > current_time) - goto end; - - // cups-browsed tried to add this print queue unsuccessfully for too - // many times due to timeouts - Skip print queue creation for this one - if (p->timeouted >= HttpMaxRetries) - { - fprintf(stderr, "Max number of retries (%d) for creating print queue %s reached, skipping it.\n", - HttpMaxRetries, p->queue_name); - goto end; - } - - debug_printf("Creating/Updating CUPS queue %s\n", - p->queue_name); - - // Make sure to have a connection to the local CUPS daemon - if ((http = http_connect_local ()) == NULL) - { - debug_printf("Unable to connect to CUPS!\n"); - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - goto end; - } - httpSetTimeout(http, HttpLocalTimeout, http_timeout_cb, NULL); - - // Do not auto-save option settings due to the print queue creation - // process - p->no_autosave = 1; - - // Printer URI: ipp://localhost/printers/ - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", p->queue_name); - - ppdfile = NULL; - - // Check whether there is a temporary CUPS queue which we would - // overwrite - dest = NULL; - if (OnlyUnsupportedByCUPS == 0) - dest = cupsGetNamedDest(http, p->queue_name, NULL); - if (dest) - { - // CUPS has found a queue with this name. - // Either CUPS generates a temporary queue here or we have already - // made this queue permanent. In any case, load the PPD from this - // queue to conserve the PPD which CUPS has originally generated. - if (p->netprinter == 1 && UseCUPSGeneratedPPDs) - { - if (LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD) - { - debug_printf("Local queue %s: We can replace temporary CUPS queues and keep their PPD file only when we name our queues like them, to avoid duplicate queues to the same printer.\n", - p->queue_name); - debug_printf("Not loading PPD from temporary CUPS queue for this printer.\n"); - debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n"); - } - else - { - // This call makes CUPS actually create the queue so that we can - // grab the PPD. We discard the result of the call. - debug_printf("Establishing dummy connection to make CUPS create the temporary queue.\n"); - cups_dinfo_t *dinfo = cupsCopyDestInfo(http, dest); - if (dinfo == NULL) - debug_printf("Unable to connect to destination.\n"); - else - { - debug_printf("Temporary queue created, grabbing the PPD.\n"); - cupsFreeDestInfo(dinfo); - loadedppd = NULL; - if ((loadedppd = loadPPD(http, p->queue_name)) == NULL) - debug_printf("Unable to load PPD from local temporary queue %s!\n", - p->queue_name); - else - { - ppdfile = strdup(loadedppd); - debug_printf("Loaded PPD file %s from local temporary queue %s.\n", - ppdfile, p->queue_name); - } - } - } - } - // If we have already a temporary CUPS queue our local queue we - // are creating would overwrite the temporary queue, and so the - // resulting queue will still be considered temporary by CUPS and - // removed after one minute of inactivity. To avoid this we need - // to convert the queue into a permanent one and CUPS does this - // only by sharing the queue (setting its boolean printer-is-shared - // option. We unset the bit right after that to not actually share - // the queue (if we want to share the queue we take care about this - // later). - // Note that we cannot reliably determine whether we have a - // temporary queue via the printer-is-temporary attribute, - // therefore we consider only shared queues as for sure - // permanent and not shared queues as possibly temporary. To - // assure we have a permanent queue in the end we set and - // remove the shared bit on any queue which is not shared. - // If the temporary queue is pointing to a remote CUPS printer - // we cannot modify its printer-is-shared option as CUPS prevents - // this. In this case we remove the temporary queue so that we - // create a fresh one which will always be permanent. - // If the temporary queue has still jobs we will not remove it to - // not loose the jobs and wait with creating our new queue until - // the jobs are done. - val = cupsGetOption ("printer-is-shared", - dest->num_options, - dest->options); - is_shared = val && (!strcasecmp (val, "yes") || - !strcasecmp (val, "on") || - !strcasecmp (val, "true")); - cupsFreeDests(1, dest); - if (!is_shared) - { - debug_printf("Our new queue overwrites the possibly temporary CUPS queue %s, so we need to assure the queue gets permanent.\n", - p->queue_name); - // We need to modify the printer-is-shared bit twice if we need to - // make a temporary queue permanent but not share this queue - for (i = 0; i <= 1; i ++) - { - if (i == 0) - debug_printf("Setting printer-is-shared bit to make this queue permanent.\n"); - else - debug_printf("Unsetting printer-is-shared bit.\n"); - request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - num_options = 0; - options = NULL; - num_options = cupsAddOption("printer-is-shared", - (i == 0 ? "true" : "false"), - num_options, &options); - num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true", - num_options, &options); - cupsEncodeOptions2(request, num_options, options, - IPP_TAG_OPERATION); - cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER); - // - // Do IPP request for printer-is-shared option only when we have - // network printer or if we have remote CUPS queue. - // When you have remote queue, clean up and break from the loop. - // - if (p->netprinter != 0 || - AllowResharingRemoteCUPSPrinters) - ippDelete(cupsDoRequest(http, request, "/admin/")); - else - { - ippDelete(request); - cupsFreeOptions(num_options, options); - break; - } - cupsFreeOptions(num_options, options); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("Unable change printer-is-shared bit to %s (%s)!\n", - (i == 0 ? "true" : "false"), - cupsLastErrorString()); - break; - } - } - // Error on modifying printer-is-shared bit, removing possibly - // temporary queue - if (i <= 1) - { - debug_printf("Removing the possibly temporary CUPS queue.\n"); - // Check whether there are still jobs and do not remove the queue - // then - num_jobs = 0; - jobs = NULL; - num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0, - CUPS_WHICHJOBS_ACTIVE); - if (num_jobs > 0) // there are still jobs - { - debug_printf("Temporary queue has still jobs or CUPS error, retrying later.\n"); - cupsFreeJobs(num_jobs, jobs); - // Schedule the removal of the queue for later - if (in_shutdown == 0) - { - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - } - goto end; - } - // No jobs, remove the CUPS queue - request = ippNewRequest(CUPS_DELETE_PRINTER); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - ippDelete(cupsDoRequest(http, request, "/admin/")); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE && - cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) - { - debug_printf("Unable to remove temporary CUPS queue (%s), retrying later\n", - cupsLastErrorString()); - if (in_shutdown == 0) - { - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - goto end; - } - } - } - } - else - debug_printf("Creating/Updating permanent CUPS queue %s.\n", - p ->queue_name); - } - else - debug_printf("Creating permanent CUPS queue %s.\n", - p->queue_name); - - // If we did not already obtain a PPD file from the temporary CUPS queue - // for our IPP network printer, we proceed here - if (p->netprinter == 1) - { - if (p->prattrs == NULL) - { - p->prattrs = cfGetPrinterAttributes(p->uri, NULL, 0, NULL, 0, 1); - debug_log_out(cf_get_printer_attributes_log); - } - if (p->prattrs == NULL) - { - debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n", - p->queue_name, p->uri); - p->status = STATUS_DISAPPEARED; - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_IMMEDIATELY; - cannot_create = 1; - goto end; - } - num_cluster_printers = 0; - for (s = (remote_printer_t *)cupsArrayFirst(remote_printers); - s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (!strcmp(s->queue_name, p->queue_name)) - { - if (s->status == STATUS_DISAPPEARED || - s->status == STATUS_UNCONFIRMED || - s->status == STATUS_TO_BE_RELEASED) - goto end; - num_cluster_printers ++; - } - } - - if (num_cluster_printers == 1) - { - printer_attributes = p->prattrs; - conflicts = NULL; - default_pagesize = NULL; - default_color = NULL; - make_model = p->make_model; - pdl = p->pdl; - color = p->color; - duplex = p->duplex; - sizes = NULL; - } - else - { - make_model = (char*)malloc(sizeof(char) * 256); - printer_attributes = get_cluster_attributes(p->queue_name); - if ((attr = ippFindAttribute(printer_attributes, - "printer-make-and-model", - IPP_TAG_TEXT)) != NULL) - strncpy(make_model, ippGetString(attr, 0, NULL), - sizeof(make_model) - 1); - color = 0; - duplex = 0; - for (r = (remote_printer_t *)cupsArrayFirst(remote_printers); - r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (!strcmp(p->queue_name, r->queue_name)) - { - if (r->color == 1) - color = 1; - if (r->duplex == 1) - duplex = 1; - } - } - default_pagesize = (char *)malloc(sizeof(char)*32); - debug_printf("Generated Merged Attributes for local queue %s\n", - p->queue_name); - conflicts = generate_cluster_conflicts(p->queue_name, - printer_attributes); - debug_printf("Generated Constraints for queue %s\n", - p->queue_name); - sizes = get_cluster_sizes(p->queue_name); - get_cluster_default_attributes(&printer_attributes, - p->queue_name, default_pagesize, - &default_color); - debug_printf("Generated Default Attributes for local queue %s\n", - p->queue_name); - } - if (ppdfile == NULL) - { - // If we do not want CUPS-generated PPDs or we cannot obtain a - // CUPS-generated PPD, for example if CUPS does not create a - // temporary queue for this printer, we generate a PPD by - // ourselves - printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs : - printer_attributes; - if (!ppdCreatePPDFromIPP2(ppdname, sizeof(ppdname), printer_ipp_response, - make_model, - pdl, color, duplex, conflicts, sizes, - default_pagesize, default_color, - ppdgenerator_msg, sizeof(ppdgenerator_msg))) - { - if (errno != 0) - debug_printf("Unable to create PPD file: %s\n", - strerror(errno)); - else - debug_printf("Unable to create PPD file: %s\n", - ppdgenerator_msg); - p->status = STATUS_DISAPPEARED; - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_IMMEDIATELY; - cannot_create = 1; - goto end; - } - else - { - debug_printf("PPD generation successful: %s\n", ppdgenerator_msg); - debug_printf("Created temporary PPD file: %s\n", ppdname); - ppdfile = strdup(ppdname); - } - } - - if (num_cluster_printers != 1) - { - if (default_pagesize != NULL) - { - free(default_pagesize); - default_pagesize = NULL; - } - if (make_model != NULL) - { - free(make_model); - make_model = NULL; - } - if (conflicts != NULL) - { - cupsArrayDelete(conflicts); - conflicts = NULL; - } - if (printer_attributes != NULL) - { - ippDelete(printer_attributes); - printer_attributes = NULL; - } - if (sizes != NULL) - { - cupsArrayDelete(sizes); - sizes = NULL; - } - } - } - - // Do we have default option settings in cups-browsed.conf? - if (DefaultOptions) - { - debug_printf("Applying default option settings to printer %s: %s\n", - p->queue_name, DefaultOptions); - p->num_options = cupsParseOptions(DefaultOptions, p->num_options, - &p->options); - } - - // Loading saved option settings from last session - p->num_options = load_printer_options(p->queue_name, p->num_options, - &p->options); - - // Determine whether we have an IPP network printer. If not we - // have remote CUPS queue(s) and so we use an implicit class for - // load balancing. In this case we will assign an - // implicitclass://... device URI, which makes cups-browsed find - // the best destination for each job. - loadedppd = NULL; - if (cups_notifier != NULL && p->netprinter == 0) - { - // We are not an IPP network printer, so we use the device URI - // implicitclass:/// - // We use the httpAssembleURI() function here, to percent-encode - // the queue name in the URI, so that any allowed character in - // a queue name, especially the '@' when we add the server name - // to a remote queue's name, goes safely into the URI. - // The implicitclass backend uses httpSeparateURI() to decode the - // queue name. - // We never use the implicitclass backend if we do not have D-Bus - // notification from CUPS as we cannot assign a destination printer - // to an incoming job then. - httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), - "implicitclass", NULL, p->queue_name, 0, NULL); - debug_printf("Print queue %s is for remote CUPS queue(s) and we get notifications from CUPS, using implicit class device URI %s\n", - p->queue_name, device_uri); - if (!ppdfile) - { - // Having another backend than the CUPS "ipp" backend the - // options from the PPD of the queue on the server are not - // automatically used on the client any more, so we have to - // explicitly load the PPD from one of the servers, apply it - // to our local queue, and replace its "*cupsFilter(2): ..." - // lines by one line making the print data get passed through - // to the server without filtering on the client (where not - // necessarily the right filters/drivers are installed) so - // that it gets filtered on the server. In addition, we prefix - // the PPD's NickName, so that automatic PPD updating by the - // distribution's package installation/update infrastructure - // is suppressed. - // Generating the ppd file for the remote cups queue - if (p->prattrs == NULL) - { - p->prattrs = cfGetPrinterAttributes(p->uri, NULL, 0, NULL, 0, 1); - debug_log_out(cf_get_printer_attributes_log); - } - if (p->prattrs == NULL) - { - debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n", - p->queue_name, p->uri); - cannot_create = 1; - goto end; - } - num_cluster_printers = 0; - for (s = (remote_printer_t *)cupsArrayFirst(remote_printers); - s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (!strcmp(s->queue_name, p->queue_name)) - { - if (s->status == STATUS_DISAPPEARED || - s->status == STATUS_UNCONFIRMED || - s->status == STATUS_TO_BE_RELEASED) - goto end; - num_cluster_printers++; - } - } - if (num_cluster_printers == 1) - { - printer_attributes = p->prattrs; - conflicts = NULL; - default_pagesize = NULL; - default_color = NULL; - make_model = p->make_model; - pdl = p->pdl; - color = p->color; - duplex = p->duplex; - sizes = NULL; - } - else - { - make_model = (char*)malloc(sizeof(char)*256); - printer_attributes = get_cluster_attributes(p->queue_name); - if((attr = ippFindAttribute(printer_attributes, - "printer-make-and-model", - IPP_TAG_TEXT)) != NULL) - strncpy(make_model, ippGetString(attr, 0, NULL), - sizeof(make_model) - 1); - color = 0; - duplex = 0; - for (r = (remote_printer_t *)cupsArrayFirst(remote_printers); - r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (!strcmp(p->queue_name, r->queue_name)) - { - if (r->color == 1) - color = 1; - if (r->duplex == 1) - duplex = 1; - } - } - default_pagesize = (char *)malloc(sizeof(char)*32); - debug_printf("Generated Merged Attributes for local queue %s\n", - p->queue_name); - conflicts = generate_cluster_conflicts(p->queue_name, - printer_attributes); - debug_printf("Generated Constraints for queue %s\n", p->queue_name); - sizes = get_cluster_sizes(p->queue_name); - get_cluster_default_attributes(&printer_attributes, p->queue_name, - default_pagesize, &default_color); - debug_printf("Generated Default Attributes for local queue %s\n", - p->queue_name); - } - if (ppdfile == NULL) - { - // If we do not want CUPS-generated PPDs or we cannot obtain a - // CUPS-generated PPD, for example if CUPS does not create a - // temporary queue for this printer, we generate a PPD by - // ourselves - printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs : - printer_attributes; - if (!ppdCreatePPDFromIPP2(ppdname, sizeof(ppdname), - printer_ipp_response, make_model, - pdl, color, duplex, conflicts, sizes, - default_pagesize, default_color, - ppdgenerator_msg, sizeof(ppdgenerator_msg))) - { - if (errno != 0) - debug_printf("Unable to create PPD file: %s\n", - strerror(errno)); - else - debug_printf("Unable to create PPD file: %s\n", ppdgenerator_msg); - p->status = STATUS_DISAPPEARED; - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_IMMEDIATELY; - cannot_create = 1; - goto end; - } - else - { - debug_printf("PPD generation successful: %s\n", ppdgenerator_msg); - debug_printf("Created temporary PPD file: %s\n", ppdname); - ppdfile = strdup(ppdname); - } - } - } - - if (num_cluster_printers != 1) - { - if (default_pagesize != NULL) - { - free(default_pagesize); - default_pagesize = NULL; - } - if (make_model != NULL) - { - free(make_model); - make_model = NULL; - } - if (conflicts != NULL) - { - cupsArrayDelete(conflicts); - conflicts = NULL; - } - if (printer_attributes != NULL) - { - ippDelete(printer_attributes); - printer_attributes = NULL; - } - if (sizes != NULL) - { - cupsArrayDelete(sizes); - sizes = NULL; - } - } - } - else - { - // Device URI: using implicitclass backend for IPP network printer - httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), - "implicitclass", NULL, p->queue_name, 0, NULL); - if (strlen(device_uri) > HTTP_MAX_URI-1) - device_uri[HTTP_MAX_URI-1] = '\0'; - debug_printf("Print queue %s is for an IPP network printer, using implicitclass backend for the printer: %s\n", - p->queue_name, device_uri); - } - - // PPD readily available - if (ppdfile) - { - debug_printf("Using PPD %s for queue %s.\n", - ppdfile, p->queue_name); - loadedppd = ppdfile; - } - if (loadedppd) - { - if ((ppd = ppdOpenFile(loadedppd)) == NULL) - { - int linenum; // Line number of error - ppd_status_t status = ppdLastError(&linenum); - debug_printf("Unable to open PPD \"%s\": %s on line %d.", - loadedppd, ppdErrorString(status), linenum); - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - unlink(loadedppd); - goto end; - } - ppdMarkDefaults(ppd); - ppdMarkOptions(ppd, p->num_options, p->options); - if ((out = cupsTempFile2(buf, sizeof(buf))) == NULL) - { - debug_printf("Unable to create temporary file!\n"); - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - ppdClose(ppd); - ppd = NULL; - unlink(loadedppd); - goto end; - } - if ((in = cupsFileOpen(loadedppd, "r")) == NULL) - { - debug_printf("Unable to open the downloaded PPD file!\n"); - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - cupsFileClose(out); - ppdClose(ppd); - ppd = NULL; - unlink(loadedppd); - goto end; - } - debug_printf("Editing PPD file %s for printer %s, setting the option defaults of the previous cups-browsed session%s, saving the resulting PPD in %s.\n", - loadedppd, p->queue_name, - " and doing client-side filtering of the job" , - buf); - new_cupsfilter_line_inserted = 0; - ap_remote_queue_id_line_inserted = 0; - while (cupsFileGets(in, line, sizeof(line))) - { - if (!strncmp(line, "*cupsFilter:", 12) || - !strncmp(line, "*cupsFilter2:", 13)) - { - // "*cupfFilter(2): ..." line: Remove it and replace the first - // one by a line which makes the data get converted to PDF - // (application/vnd.cups-pdf, pdftopdf filter applied) before - // being passed on to the backend - if (new_cupsfilter_line_inserted == 0) - { - cupsFilePrintf(out, - "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 0 -\"\n"); - new_cupsfilter_line_inserted = 1; - } - // Find the end of the "*cupsFilter(2): ..." entry in the - // case it spans more than one line - do - { - if (strlen(line) != 0) - { - char *ptr = line + strlen(line) - 1; - while(isspace(*ptr) && ptr > line) - ptr --; - if (*ptr == '"') - break; - } - } - while (cupsFileGets(in, line, sizeof(line))); - } - else if (!strncmp(line, "*Default", 8)) - { - strncpy(keyword, line + 8, sizeof(keyword) - 1); - if ((strlen(line) + 8) > 1023) - keyword[1023] = '\0'; - for (keyptr = keyword; *keyptr; keyptr ++) - if (*keyptr == ':' || isspace(*keyptr & 255)) - break; - *keyptr++ = '\0'; - while (isspace(*keyptr & 255)) - keyptr ++; - if (!strcmp(keyword, "PageRegion") || - !strcmp(keyword, "PageSize") || - !strcmp(keyword, "PaperDimension") || - !strcmp(keyword, "ImageableArea")) - { - if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) == NULL) - choice = ppdFindMarkedChoice(ppd, "PageRegion"); - } - else - choice = ppdFindMarkedChoice(ppd, keyword); - if (choice && strcmp(choice->choice, keyptr)) - { - if (strcmp(choice->choice, "Custom")) - cupsFilePrintf(out, "*Default%s: %s\n", keyword, - choice->choice); - else if ((customval = cupsGetOption(keyword, p->num_options, - p->options)) != NULL) - cupsFilePrintf(out, "*Default%s: %s\n", keyword, customval); - else - cupsFilePrintf(out, "%s\n", line); - } - else - cupsFilePrintf(out, "%s\n", line); - } - else if (strncmp(line, "*End", 4)) - { - // Write an "APRemoteQueueID" line to make this queue marked - // as remote printer by CUPS - if (p->netprinter == 0 && - strncmp(line, "*%", 2) && - strncmp(line, "*PPD-Adobe:", 11) && - ap_remote_queue_id_line_inserted == 0 && - !AllowResharingRemoteCUPSPrinters) - { - ap_remote_queue_id_line_inserted = 1; - cupsFilePrintf(out, "*APRemoteQueueID: \"\"\n"); - } - // Simply write out the line as we read it - cupsFilePrintf(out, "%s\n", line); - } - // Save the NickName of the PPD to check whether external - // manipulations of the print queue have replaced the PPD. - // Check whether nickname is defined too - if (!strncmp(line, "*NickName:", 10) && p->nickname == NULL) - { - char *ptr = NULL; - char *end_ptr = NULL; - int nickname_len = 0; - - ptr = strchr(line, '"'); - - if (ptr == NULL) - { - debug_printf("Malformed *Nickname directive in PPD - no double quote in line.\n"); - continue; - } - - ptr ++; - end_ptr = strchr(ptr, '"'); - - if (end_ptr == NULL) - { - debug_printf("Malformed *Nickname directive in PPD - no ending double quote\n"); - continue; - } - - // both pointers are null terminated, because cupsFileGets() puts - // a null terminator into returned buffer with one line - // here as 'line' array) and those two pointers points on two places - // in the 'line' array. - - nickname_len = strlen(ptr) - strlen(end_ptr); - - if (nickname_len == 0) - { - debug_printf("Malformed *Nickname directive in PPD - empty nickname.\n"); - continue; - } - - // alloc one more space for null terminator, calloc() will initialize - // it to null automatically, so then we only copy a string with - // 'nickname_len' length to get a proper null terminated p->nickname. - - p->nickname = (char*)calloc(nickname_len + 1, sizeof(char)); - - if (p->nickname != NULL) - strncpy(p->nickname, ptr, nickname_len); - } - } - if (new_cupsfilter_line_inserted == 0) - cupsFilePrintf(out, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 0 -\"\n"); - - cupsFileClose(in); - cupsFileClose(out); - ppdClose(ppd); - ppd = NULL; - unlink(loadedppd); - loadedppd = NULL; - if (ppdfile) - { - free(ppdfile); - ppdfile = NULL; - } - ppdfile = strdup(buf); - } - - // Create a new CUPS queue or modify the existing queue - request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - // Default user - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - // Queue should be enabled ... - ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", - IPP_PRINTER_IDLE); - // ... and accepting jobs - ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1); - // Location - ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "printer-location", NULL, p->location); - num_options = 0; - options = NULL; - // Device URI: ipp(s)://:631/printers/ - // OR implicitclass:/// - num_options = cupsAddOption("device-uri", device_uri, - num_options, &options); - // Option cups-browsed=true, marking that we have created this queue - num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true", - num_options, &options); - // Description - num_options = cupsAddOption("printer-info", p->info, - num_options, &options); - - // Default option settings from printer entry - for (i = 0; i < p->num_options; i ++) - if (strcasecmp(p->options[i].name, "printer-is-shared")) - num_options = cupsAddOption(p->options[i].name, - p->options[i].value, - num_options, &options); - // Encode option list into IPP attributes - cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION); - cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER); - // Do it - if (ppdfile) - { - debug_printf("Non-raw queue %s with PPD file: %s\n", - p->queue_name, ppdfile); - ippDelete(cupsDoFileRequest(http, request, "/admin/", ppdfile)); - want_raw = 0; - unlink(ppdfile); - free(ppdfile); - ppdfile = NULL; - } - else - { - if (p->netprinter == 0) - { - debug_printf("Raw queue %s\n", p->queue_name); - want_raw = 1; - } - else - { - debug_printf("Queue %s keeping its current PPD file/interface script\n", - p->queue_name); - want_raw = 0; - } - ippDelete(cupsDoRequest(http, request, "/admin/")); - } - cupsFreeOptions(num_options, options); - cups_queues_updated ++; - - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("Unable to create/modify CUPS queue (%s)!\n", - cupsLastErrorString()); - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - goto end; - } - - // Do not share a queue which serves only to point to a remote CUPS - // printer - // - // We do this in a seperate IPP request as on newer CUPS versions we - // get an error when changing the printer-is-shared bit on a queue - // pointing to a remote CUPS printer, this way we assure all other - // settings be applied amd when setting the printer-is-shared to - // false amd this errors, we can safely ignore the error as on queues - // pointing to remote CUPS printers the bit is set to false by default - // (these printers are never shared) - // - // If our printer is an IPP network printer and not a CUPS queue, we - // keep track of whether the user has changed the printer-is-shared - // bit and recover this setting. The default setting for a new - // queue is configurable via the NewIPPPrinterQueuesShared directive - // in cups-browsed.conf - - request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - num_options = 0; - options = NULL; - if (p->netprinter == 1 && - (val = cupsGetOption("printer-is-shared", p->num_options, - p->options)) != NULL) - { - num_options = cupsAddOption("printer-is-shared", val, - num_options, &options); - debug_printf("Setting printer-is-shared bit to %s.\n", val); - } - else if (p->netprinter == 1 && NewIPPPrinterQueuesShared) - { - num_options = cupsAddOption("printer-is-shared", "true", - num_options, &options); - debug_printf("Setting printer-is-shared bit.\n"); - } - else if (NewBrowsePollQueuesShared && - (val = cupsGetOption("printer-to-be-shared", p->num_options, - p->options)) != NULL) - { - num_options = cupsAddOption("printer-is-shared", "true", - num_options, &options); - debug_printf("Setting printer-is-shared bit.\n"); - } - else - { - num_options = cupsAddOption("printer-is-shared", "false", - num_options, &options); - debug_printf("Unsetting printer-is-shared bit.\n"); - } - cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION); - cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER); - // - // Do IPP request for printer-is-shared option only when we have - // network printer or if we have remote CUPS queue. - // - if (p->netprinter != 0 || AllowResharingRemoteCUPSPrinters) - ippDelete(cupsDoRequest(http, request, "/admin/")); - else - ippDelete(request); - cupsFreeOptions(num_options, options); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - debug_printf("Unable to modify the printer-is-shared bit (%s)!\n", - cupsLastErrorString()); - - // If we are about to create a raw queue or turn a non-raw queue - // into a raw one, we apply the "ppd-name=raw" option to remove any - // existing PPD file assigned to the queue. - // - // Also here we do a separate IPP request as it errors in some - // cases. - if (want_raw) - { - debug_printf("Removing local PPD file for printer %s\n", p->queue_name); - request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - num_options = 0; - options = NULL; - num_options = cupsAddOption("ppd-name", "raw", - num_options, &options); - cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION); - cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER); - ippDelete(cupsDoRequest(http, request, "/admin/")); - cupsFreeOptions(num_options, options); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - debug_printf("Unable to remove PPD file from the print queue (%s)!\n", - cupsLastErrorString()); - } - - // If this queue was the default printer in its previous life, make - // it the default printer again. - queue_creation_handle_default(p->queue_name); - - // If cups-browsed or a failed backend has disabled this - // queue, re-enable it. - if ((disabled_str = is_disabled(p->queue_name, "cups-browsed")) != NULL) - { - enable_printer(p->queue_name); - free(disabled_str); - } - else if ((disabled_str = - is_disabled(p->queue_name, - "Printer stopped due to backend errors")) != - NULL) - { - enable_printer(p->queue_name); - free(disabled_str); - } - - p->status = STATUS_CONFIRMED; - if (p->is_legacy) - { - p->timeout = time(NULL) + BrowseTimeout; - debug_printf("starting BrowseTimeout timer for %s (%ds)\n", - p->queue_name, BrowseTimeout); - } - else - p->timeout = (time_t) -1; - - // Check if an HTTP timeout happened during the print queue creation - // If it does - increment p->timeouted and set status to TO_BE_CREATED - // because the creation can fall through the process, have state changed - // to STATUS_CONFIRMED and experience the timeout - // If no timeout has happened, clear p->timeouted - if (timeout_reached == 1) - { - fprintf(stderr, "Timeout happened during creation of the queue %s, turn on DebugLogging for more info.\n", - p->queue_name); - p->timeouted ++; - debug_printf("The queue %s already timeouted %d times in a row.\n", - p->queue_name, p->timeouted); - p->status = STATUS_TO_BE_CREATED; - p->timeout = current_time + TIMEOUT_RETRY; - } - else if (p->timeouted != 0) - { - debug_printf("Creating the queue %s went smoothly after %d timeouts.\n", - p->queue_name, p->timeouted); - p->timeouted = 0; - } - - p->no_autosave = 0; - - end: - p->called = 0; - pthread_rwlock_unlock(&lock); - free(a->uri); - free(a->queue); - free(a); - - return; -} - - -static gboolean -update_cups_queues(gpointer unused) -{ - - pthread_rwlock_wrlock(&update_lock); - - remote_printer_t *p, *q; - http_t *http; - char uri[HTTP_MAX_URI]; - int num_jobs; - cups_job_t *jobs; - ipp_t *request; - time_t current_time; - - debug_printf("update_cups_queues() in THREAD %ld\n", pthread_self); - update_count++; - - // Create dummy entry to point slaves at when their master is about to - // get removed now (if we point them to NULL, we would try to remove - // the already removed CUPS queue again when it comes to the removal - // of the slave. - if (deleted_master == NULL) - { - if ((deleted_master = - (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) - { - debug_printf("ERROR: Unable to allocate memory.\n"); - if (in_shutdown == 0) - recheck_timer (); - pthread_rwlock_unlock(&update_lock); - return (FALSE); - } - memset(deleted_master, 0, sizeof(remote_printer_t)); - deleted_master->uri = ""; - } - - // Now redirect the slave_of pointers of the masters which get deleted now - // to this dummy entry - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if ((p->status == STATUS_DISAPPEARED || - p->status == STATUS_TO_BE_RELEASED) && - (q = p->slave_of) != NULL && q->queue_name && - (q->status == STATUS_DISAPPEARED || q->status == STATUS_TO_BE_RELEASED)) - p->slave_of = deleted_master; - - debug_printf("Processing printer list ...\n"); - log_all_printers(); - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (cannot_create) goto cannot_create; - - // We need to get the current time as precise as possible for retries - // and reset the timeout flag - current_time = time(NULL); - timeout_reached = 0; - - // terminating means we have received a signal and should shut down. - // in_shutdown means we have exited the main loop. - // update_cups_queues() is called after having exited the main loop - // in order to remove any queues we have set up - if (terminating && !in_shutdown) - { - debug_printf("Stopping processing printer list because cups-browsed is terminating.\n"); - break; - } - - // We do not necessarily update all local CUPS queues which are - // scheduled for creation, update, or removal in a single call of - // the update_cups_queues() function, as then we could be stuck in - // this function for a long time and other tasks of cups-browsed, - // especially directing print jobs to destination printers before - // the implicitclass backend times out, will not get done in time. - // We schedule a new call of update_cups_queues() after a short - // delay to continue with the next local CUPS queues. - if (!in_shutdown && update_cups_queues_max_per_call > 0 && - cups_queues_updated >= update_cups_queues_max_per_call) - { - debug_printf("Stopping processing printer list here because the update_cups_queues() function has reached its per-call limit of %d queue updates. Continuing in further calls.\n", - update_cups_queues_max_per_call); - break; - } - - switch (p->status) - { - // Print queue generated by us in a previous session - case STATUS_UNCONFIRMED: - - // Only act if the timeout has passed - if (p->timeout > current_time) - break; - - // Queue not reported again by DNS-SD, remove it - debug_printf("No remote printer named %s available, removing entry from previous session.\n", - p->queue_name); - remove_printer_entry(p); - - // DNS-SD has reported this printer as disappeared or we have replaced - // this printer by another one - case STATUS_DISAPPEARED: - case STATUS_TO_BE_RELEASED: - - // Only act if the timeout has passed - if (p->timeout > current_time) - break; - - debug_printf("Removing entry %s (%s)%s.\n", p->queue_name, p->uri, - (p->slave_of || - p->status == STATUS_TO_BE_RELEASED ? "" : - " and its CUPS queue")); - - // Slaves do not have a CUPS queue - if ((q = p->slave_of) == NULL) - { - if ((http = http_connect_local ()) == NULL) - { - debug_printf("Unable to connect to CUPS!\n"); - if (in_shutdown == 0) - { - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - } - break; - } - - // Do not auto-save option settings due to the print queue removal - // process or release process - p->no_autosave = 1; - - // Record the option settings to retrieve them when the remote - // queue re-appears later or when cups-browsed gets started again - record_printer_options(p->queue_name); - - if (p->status != STATUS_TO_BE_RELEASED && - !queue_overwritten(p)) - { - // Remove the CUPS queue - - // Check whether there are still jobs and do not remove the queue - // then - num_jobs = 0; - jobs = NULL; - num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0, - CUPS_WHICHJOBS_ACTIVE); - if (num_jobs > 0) // There are still jobs - { - debug_printf("Queue has still jobs or CUPS error!\n"); - cupsFreeJobs(num_jobs, jobs); - // Disable the queue -#ifdef HAVE_AVAHI - if (avahi_present || p->domain == NULL || p->domain[0] == '\0') - // If avahi has got shut down, do not disable queues - // which are, created based on DNS-SD broadcasts as - // the server has most probably not gone away -#endif // HAVE_AVAHI - disable_printer(p->queue_name, - "Printer disappeared or cups-browsed shutdown"); - // Schedule the removal of the queue for later - if (in_shutdown == 0) - { - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - break; - } - else - // Make sure queue's list entry gets freed - goto keep_queue; - } - - // If this queue was the default printer, note that fact - // so that it gets the default printer again when it - // re-appears, also switch back to the last local - // default printer - queue_removal_handle_default(p->queue_name); - - // If we do not have a subscription to CUPS' D-Bus - // notifications and so no default printer management, - // we simply do not remove this CUPS queue if it is the - // default printer, to not cause a change of the default - // printer or the loss of the information that this - // printer is the default printer. - if (cups_notifier == NULL && - is_cups_default_printer(p->queue_name)) - { - // Schedule the removal of the queue for later - if (in_shutdown == 0) - { - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - break; - } - else - // Make sure queue's list entry gets freed - goto keep_queue; - } - - // No jobs, remove the CUPS queue - debug_printf("Removing local CUPS queue %s (%s).\n", - p->queue_name, p->uri); - request = ippNewRequest(CUPS_DELETE_PRINTER); - // Printer URI: ipp://localhost/printers/ - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", - NULL, "localhost", 0, "/printers/%s", - p->queue_name); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, uri); - // Default user - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser()); - // Do it - ippDelete(cupsDoRequest(http, request, "/admin/")); - - cups_queues_updated ++; - - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE && - cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) - { - debug_printf("Unable to remove CUPS queue! (%s)\n", - cupsLastErrorString()); - if (in_shutdown == 0) - { - current_time = time(NULL); - p->timeout = current_time + TIMEOUT_RETRY; - p->no_autosave = 0; - break; - } - } - } - } - - keep_queue: - - // CUPS queue removed or released from cups-browsed, remove the list - // entry - // - // Note that we do not need to break out of the loop passing through - // all elements of a CUPS array when we remove an element via the - // cupsArrayRemove() function, as the function decreases the array- - // internal index by one and so the cupsArrayNext() call gives us - // the element right after the deleted element. So no skipping - // of an element and especially no reading beyond the end of the - // array. - cupsArrayRemove(remote_printers, p); - if (p->queue_name) free (p->queue_name); - if (p->location) free (p->location); - if (p->info) free (p->info); - if (p->make_model) free (p->make_model); - if (p->pdl) free (p->pdl); - if (p->uri) free (p->uri); - cupsFreeOptions(p->num_options, p->options); - if (p->host) free (p->host); - if (p->ip) free (p->ip); - if (p->resource) free (p->resource); - if (p->service_name) free (p->service_name); - if (p->type) free (p->type); - if (p->domain) free (p->domain); - cupsArrayDelete(p->ipp_discoveries); - if (p->prattrs) ippDelete (p->prattrs); - if (p->nickname) free (p->nickname); - free(p); - p = NULL; - - // If auto shutdown is active and all printers we have set - // up got removed again, schedule the shutdown in - // autoshutdown_timeout seconds Note that in this case we - // also do not have jobs any more so if we auto shutdown on - // running out of jobs, trigger it here, too. - if (in_shutdown == 0 && autoshutdown && !autoshutdown_exec_id && - (cupsArrayCount(remote_printers) == 0 || - (autoshutdown_on == NO_JOBS && check_jobs() == 0))) - { - debug_printf("No printers there any more to make available or no jobs, shutting down in %d sec...\n", - autoshutdown_timeout); - autoshutdown_exec_id = - g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, - NULL); - } - - break; - - // DNS-SD has reported a new remote printer, create a CUPS queue - // for it, or upgrade an existing queue, or update a queue to - // use a backup host when it has disappeared on the currently - // used host (...or, we've just received a CUPS Browsing packet - // for this queue) - case STATUS_TO_BE_CREATED: - if (p->called) - break; - - create_args_t* arg = (create_args_t*)malloc(sizeof(create_args_t)); - arg->queue = strdup(p->queue_name); - arg->uri = strdup(p->uri); - - pthread_t id; - p->called = 1; - int err = 0; - if ((err = pthread_create(&id, NULL, (void*)create_queue, - (void*)arg))) - { - debug_printf("Unable to create a new thread, retrying!\n"); - - int attempts = 0; - while (attempts < 5) - { - if ((err = pthread_create(&id, NULL, (void*)create_queue, - (void*)arg))) - debug_printf("Unable to create a new thread, retrying!\n"); - else - break; - attempts++; - } - if (attempts == 5) - { - debug_printf("Could not create new thread even after many attempts for queue %s\n", - p->queue_name); - free(arg); - p->called = 0; - break; - } - } - pthread_detach(id); - - break; - - case STATUS_CONFIRMED: - // Only act if the timeout has passed - if (p->timeout > current_time) - break; - - if (p->is_legacy) - { - // Remove a queue based on a legacy CUPS broadcast when the - // broadcast timeout expires without a new broadcast of this - // queue from the server - remove_printer_entry(p); - } - else - p->timeout = (time_t) -1; - - break; - } - } - - // If we have printer entries which we did not treat yet because of - // update_cups_queues_max_per_call we push their timeouts by the - // value of pause_between_cups_queue_updates into the future, so - // that they only get worked on then. Also printer entries which are - // scheduled in a time less than the value of - // pause_between_cups_queue_updates will be pushed, so that - // update_cups_queues will run the next time only after this - // interval - if (p && !in_shutdown) - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (p->timeout <= current_time + pause_between_cups_queue_updates) - p->timeout = current_time + pause_between_cups_queue_updates; - - cannot_create: - if (p && !in_shutdown) - remove_printer_entry(p); - - log_all_printers(); - pthread_rwlock_unlock(&update_lock); - - if (in_shutdown == 0) - recheck_timer (); - - // Don't run this callback again - return (FALSE); -} - - -static void -recheck_timer(void) -{ - remote_printer_t *p; - time_t timeout = (time_t) -1; - time_t now = time(NULL); - - if (!gmainloop) - return; - - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; - p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (p->called) - continue; - if (p->timeout == (time_t) -1) - continue; - else if (now > p->timeout) - { - timeout = 0; - break; - } - else if (timeout == (time_t) -1 || p->timeout - now < timeout) - timeout = p->timeout - now; - } - - if (queues_timer_id) - g_source_remove (queues_timer_id); - - if (timeout != (time_t) -1) - { - debug_printf("checking queues in %ds\n", timeout); - queues_timer_id = - g_timeout_add_seconds (timeout, update_cups_queues, NULL); - } - else - { - debug_printf("listening\n"); - queues_timer_id = 0; - } -} - - -static gboolean -matched_filters(const char *queue_name, - const char *host, - uint16_t port, - const char *service_name, - const char *domain, - void *txt) -{ - browse_filter_t *filter; - const char *property = NULL; - char buf[10]; -#ifdef HAVE_AVAHI - AvahiStringList *entry = NULL; - char *key = NULL, *value = NULL; -#endif // HAVE_AVAHI - - debug_printf("Matching printer \"%s\" with properties Host = \"%s\", Port = %d, Service Name = \"%s\", Domain = \"%s\" with the BrowseFilter lines in cups-browsed.conf\n", - queue_name, host, port, service_name, domain); - // Go through all BrowseFilter lines and stop if one line does not match, - // rejecting this printer - for (filter = cupsArrayFirst (browsefilter); - filter; - filter = cupsArrayNext (browsefilter)) - { - debug_printf("Matching with line \"BrowseFilter %s%s%s %s\"", - (filter->sense == FILTER_NOT_MATCH ? "NOT " : ""), - (filter->regexp && !filter->cregexp ? "EXACT " : ""), - filter->field, (filter->regexp ? filter->regexp : "")); -#ifdef HAVE_AVAHI - // Go through the TXT record to see whether this rule applies to a field - // in there - if (txt) - { - entry = avahi_string_list_find((AvahiStringList *)txt, filter->field); - if (entry) - { - avahi_string_list_get_pair(entry, &key, &value, NULL); - if (key) - { - debug_printf(", TXT record entry: %s = %s", - key, (value ? value : "")); - if (filter->regexp) - { - // match regexp - if (!value) - value = strdup(""); - if ((filter->cregexp && - regexec(filter->cregexp, value, 0, NULL, 0) == 0) || - (!filter->cregexp && !strcasecmp(filter->regexp, value))) - { - if (filter->sense == FILTER_NOT_MATCH) - { - avahi_free(key); - avahi_free(value); - goto filter_failed; - } - } - else - { - if (filter->sense == FILTER_MATCH) - { - avahi_free(key); - avahi_free(value); - goto filter_failed; - } - } - } - else - { - // match boolean value - if (filter->sense == FILTER_MATCH) - { - if (!value || strcasecmp(value, "T")) - { - avahi_free(key); - avahi_free(value); - goto filter_failed; - } - } - else - { - if (value && !strcasecmp(value, "T")) - { - avahi_free(key); - avahi_free(value); - goto filter_failed; - } - } - } - } - avahi_free(key); - avahi_free(value); - goto filter_matched; - } - } -#endif // HAVE_AVAHI - - // Does one of the properties outside the TXT record match? - property = buf; - buf[0] = '\0'; - if (!strcasecmp(filter->field, "Name") || - !strcasecmp(filter->field, "Printer") || - !strcasecmp(filter->field, "PrinterName") || - !strcasecmp(filter->field, "Queue") || - !strcasecmp(filter->field, "QueueName")) - { - if (queue_name) - property = queue_name; - } - else if (!strcasecmp(filter->field, "Host") || - !strcasecmp(filter->field, "HostName") || - !strcasecmp(filter->field, "RemoteHost") || - !strcasecmp(filter->field, "RemoteHostName") || - !strcasecmp(filter->field, "Server") || - !strcasecmp(filter->field, "ServerName")) - { - if (host) - property = host; - } - else if (!strcasecmp(filter->field, "Port")) - { - if (port) - snprintf(buf, sizeof(buf), "%d", port); - } - else if (!strcasecmp(filter->field, "Service") || - !strcasecmp(filter->field, "ServiceName")) - { - if (service_name) - property = service_name; - } - else if (!strcasecmp(filter->field, "Domain")) - { - if (domain) - property = domain; - } - else - property = NULL; - if (property) - { - if (!filter->regexp) - filter->regexp = ""; - if ((filter->cregexp && - regexec(filter->cregexp, property, 0, NULL, 0) == 0) || - (!filter->cregexp && !strcasecmp(filter->regexp, property))) - { - if (filter->sense == FILTER_NOT_MATCH) - goto filter_failed; - } - else - { - if (filter->sense == FILTER_MATCH) - goto filter_failed; - } - goto filter_matched; - } - - debug_printf(": Field not found --> SKIPPED\n"); - continue; - - filter_matched: - debug_printf(" --> MATCHED\n"); - } - - // All BrowseFilter lines matching, accept this printer - debug_printf("All BrowseFilter lines matched or skipped, accepting printer %s\n", - queue_name); - return (TRUE); - - filter_failed: - debug_printf(" --> FAILED\n"); - debug_printf("One BrowseFilter line did not match, ignoring printer %s\n", - queue_name); - return (FALSE); -} - - -static gboolean -update_netifs (gpointer data) -{ - pthread_rwlock_wrlock(&netiflock); - - struct ifaddrs *ifaddr, *ifa; - netif_t *iface, *iface2; - int i, add_to_netifs, addr_size, dupe, if_found, addr_found; - char *host, buf[HTTP_MAX_HOST], *p, list[65536], *l; - - debug_printf("update_netifs() in THREAD %ld\n", pthread_self()); - - update_netifs_sourceid = 0; - if (getifaddrs (&ifaddr) == -1) - { - debug_printf("unable to get interface addresses: %s\n", - strerror (errno)); - pthread_rwlock_unlock(&netiflock); - return (FALSE); - } - - while ((iface = cupsArrayFirst (netifs)) != NULL) - { - cupsArrayRemove (netifs, iface); - free (iface->address); - free (iface); - } - while ((host = cupsArrayFirst (local_hostnames)) != NULL) - { - cupsArrayRemove (local_hostnames, host); - free (host); - } - - memset(list, 0, sizeof(list)); - snprintf(list, sizeof(list) - 1, "Network interfaces: "); - l = list + strlen(list); - - for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) - { - if_found = 0; - addr_found = 0; - - netif_t *iface; - - add_to_netifs = 1; - - if (ifa->ifa_addr == NULL) - continue; - - if (ifa->ifa_broadaddr == NULL) - add_to_netifs = 0; - - if (ifa->ifa_flags & IFF_LOOPBACK) - add_to_netifs = 0; - - if (!(ifa->ifa_flags & IFF_BROADCAST)) - add_to_netifs = 0; - - if (ifa->ifa_addr->sa_family == AF_INET) - addr_size = sizeof (struct sockaddr_in); - else if (ifa->ifa_addr->sa_family == AF_INET6) - addr_size = sizeof (struct sockaddr_in6); - else - addr_size = 0; - if (addr_size) - { - if (strlen(list) + strlen(ifa->ifa_name) + 1 <= - sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - "%s", ifa->ifa_name); - l = list + strlen(list); - if_found = 1; - } - for (i = 0; i <= 1; i ++) - if (getnameinfo (ifa->ifa_addr, addr_size, - buf, HTTP_MAX_HOST, NULL, 0, - i == 0 ? NI_NUMERICHOST : NI_NAMEREQD) == 0) - if (buf[0]) - { - // Cut off "%..." from IPv6 IP addresses - if (ifa->ifa_addr->sa_family == AF_INET6 && i == 0 && - (p = strchr(buf, '%')) != NULL) - *p = '\0'; - // discard if we already have this name or address - dupe = 0; - for (host = (char *)cupsArrayFirst (local_hostnames); - host != NULL; - host = (char *)cupsArrayNext (local_hostnames)) - if (strcasecmp(buf, host) == 0) - { - dupe = 1; - break; - } - if (dupe == 0) - { - cupsArrayAdd (local_hostnames, strdup(buf)); - if (addr_found == 1 && strlen(list) + 3 <= - sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - ", "); - l = list + strlen(list); - } - if (addr_found == 0 && strlen(list) + 3 <= - sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - " ("); - l = list + strlen(list); - addr_found = 1; - } - if (strlen(list) + strlen(buf) + 1 <= - sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - "%s", buf); - l = list + strlen(list); - } - } - } - } - - if (add_to_netifs == 0) - goto done; - - iface = malloc (sizeof (netif_t)); - if (iface == NULL) - { - debug_printf ("malloc failure\n"); - exit (1); - } - - iface->address = malloc (HTTP_MAX_HOST); - if (iface->address == NULL) - { - free (iface); - debug_printf ("malloc failure\n"); - exit (1); - } - - iface->address[0] = '\0'; - switch (ifa->ifa_addr->sa_family) - { - case AF_INET: - // copy broadcast addr/fill in port first to faciliate dupe compares - memcpy (&iface->broadcast, ifa->ifa_broadaddr, - sizeof (struct sockaddr_in)); - iface->broadcast.ipv4.sin_port = htons (BrowsePort); - // discard if we already have an interface sharing the broadcast - // address - dupe = 0; - for (iface2 = (netif_t *)cupsArrayFirst (netifs); - iface2 != NULL; - iface2 = (netif_t *)cupsArrayNext (netifs)) - { - if (memcmp(&iface2->broadcast, &iface->broadcast, - sizeof(struct sockaddr_in)) == 0) - { - dupe = 1; - break; - } - } - if (dupe) break; - getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in), - iface->address, HTTP_MAX_HOST, - NULL, 0, NI_NUMERICHOST); - break; - - case AF_INET6: - if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *)(ifa->ifa_addr)) - ->sin6_addr)) - break; - - // see above for order - memcpy (&iface->broadcast, ifa->ifa_broadaddr, - sizeof (struct sockaddr_in6)); - iface->broadcast.ipv6.sin6_port = htons (BrowsePort); - // discard alias addresses (identical broadcast) - dupe = 0; - for (iface2 = (netif_t *)cupsArrayFirst (netifs); - iface2 != NULL; - iface2 = (netif_t *)cupsArrayNext (netifs)) - { - if (memcmp(&iface2->broadcast, ifa->ifa_broadaddr, - sizeof(struct sockaddr_in6)) == 0) - { - dupe = 1; - break; - } - } - if (dupe) - break; - getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in6), - iface->address, HTTP_MAX_HOST, NULL, 0, NI_NUMERICHOST); - break; - } - - if (iface->address[0]) - { - cupsArrayAdd (netifs, iface); - if (if_found == 1) - { - if (addr_found == 1 && strlen(list) + 3 <= sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - ", "); - l = list + strlen(list); - } - if (addr_found == 0 && strlen(list) + 3 <= sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - " ("); - l = list + strlen(list); - addr_found = 1; - } - if (strlen(list) + strlen(iface->address) + 2 <= sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - "%s*", iface->address); - l = list + strlen(list); - } - } - } - else - { - free (iface->address); - free (iface); - } - - done: - if (if_found == 1) - { - if (addr_found == 1 && strlen(list) + 2 <= sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - ")"); - l = list + strlen(list); - } - if (strlen(list) + 3 <= sizeof(list)) - { - snprintf(l, sizeof(list) - strlen(list) - 1, - ", "); - l = list + strlen(list); - } - } - } - - if ((l = strrchr(list, ')')) != NULL) - { - if (strlen(list) + 2 <= sizeof(list)) - *(l + 1) = '\0'; - } - else - { - if (strlen(list) + 5 <= sizeof(list)) - snprintf(list + strlen(list), sizeof(list) - strlen(list) - 1, - "None"); - } - debug_printf("%s\n", list); - - freeifaddrs (ifaddr); - pthread_rwlock_unlock(&netiflock); - - // If run as a timeout, don't run it again. - return (FALSE); -} - - -static int -is_local_hostname(const char *host_name) -{ - char *host; - - if (host_name == NULL) - return (0); - - for (host = (char *)cupsArrayFirst (local_hostnames); - host != NULL; - host = (char *)cupsArrayNext (local_hostnames)) - if (strncasecmp(host_name, host, strlen(host)) == 0 && - (strlen(host_name) == strlen(host) || - (strlen(host_name) > strlen(host) && - (strcasecmp(host_name + strlen(host), ".local") == 0 || - strcasecmp(host_name + strlen(host), ".local.") == 0)))) - return (1); - - return (0); -} - - -static remote_printer_t * -examine_discovered_printer_record(const char *host, - const char *ip, - uint16_t port, - char *resource, - const char *service_name, - const char *location, - const char *info, - const char *type, - const char *domain, - const char *interface, - int family, - void *txt) -{ - char uri[HTTP_MAX_URI]; - char *remote_host = NULL, *pdl = NULL, - *make_model = NULL; - int color = 1, duplex = 1; -#ifdef HAVE_AVAHI - char *fields[] = { "product", "usb_MDL", "ty", NULL }, **f; - AvahiStringList *entry = NULL; - char *key = NULL, *value = NULL; - char *note_value = NULL; - char service_host_name[1024]; -#endif // HAVE_AVAHI - remote_printer_t *p = NULL, key_rec; - char *local_queue_name = NULL; - int is_cups_queue; - int raw_queue = 0; - char *ptr; - - if (!host || !resource || !service_name || !location || !info || !type || - !domain) - { - debug_printf("ERROR: examine_discovered_printer_record(): Input value missing!\n"); - return (NULL); - } - - is_cups_queue = 0; - memset(uri, 0, sizeof(uri)); - - // Find the remote host name. - // Used in constructing backup queue name, so need to sanitize. - // strdup() is called inside remove_bad_chars() and result is free()-able. - remote_host = remove_bad_chars(host, 1); - - // If we only want to create queues for printers for which CUPS does - // not already auto-create queues, we check here whether we can skip - // this printer - if (OnlyUnsupportedByCUPS) - { - if (g_hash_table_find (cups_supported_remote_printers, - local_printer_service_name_matches, - (gpointer *)service_name)) - { - // Found a DNS-SD-discovered CUPS-supported printer whose service name - // matches our discovered printer - debug_printf("Printer with DNS-SD service name \"%s\" does not need to be covered by us as it is already supported by CUPS, skipping.\n", - service_name); - goto fail; - } - } - -#ifdef HAVE_AVAHI - if (txt) - { - // Find make and model by the TXT record - for (f = fields; *f; f ++) - { - entry = avahi_string_list_find((AvahiStringList *)txt, *f); - if (entry) - { - avahi_string_list_get_pair(entry, &key, &value, NULL); - if (key && value && !strcasecmp(key, *f) && strlen(value) >= 3) - { - if (!strcasecmp(key, "product")) - { - make_model = strdup(value + 1); - make_model[strlen(make_model) - 1] = '\0'; - } - else - make_model = strdup(value); - avahi_free(key); - avahi_free(value); - break; - } - avahi_free(key); - avahi_free(value); - } - } - // Check by the printer-type TXT field whether the discovered printer is a - // CUPS queue - entry = avahi_string_list_find((AvahiStringList *)txt, "printer-type"); - if (entry) - { - avahi_string_list_get_pair(entry, &key, &value, NULL); - if (key && value && strlen(value) > 1 && - !strcasecmp(key, "printer-type") && value[0] == '0' && - value[1] == 'x') - is_cups_queue = 1; - avahi_free(key); - avahi_free(value); - } - } -#else - // Check by the resource whether the discovered printer is a CUPS queue - if (!strncasecmp(resource, "printers/", 9) || - !strncasecmp(resource, "classes/", 8)) - // This is a remote CUPS queue or class - is_cups_queue = 1; -#endif // HAVE_AVAHI - // If we do not have a TXT record the printer was not discovered via - // DNS-SD but via CUPS legacy or LDAP, so it is a remote CUPS queue - // and not an IPP network printer. - if (txt == NULL) - is_cups_queue = 1; - if (is_cups_queue) - debug_printf("Found CUPS queue/class: %s on host %s.\n", - strrchr(resource, '/') + 1, remote_host); -#ifdef HAVE_AVAHI - if (is_cups_queue) - { - // If the remote queue has a PPD file, the "product" field of the - // TXT record is populated. If it has no PPD file the remote queue - // is a raw queue and so we do not know enough about the printer - // behind it for auto-creating a local queue pointing to it. - if (txt) - { - entry = avahi_string_list_find((AvahiStringList *)txt, "product"); - if (entry) - { - avahi_string_list_get_pair(entry, &key, &value, NULL); - if (!key || !value || strcasecmp(key, "product") || value[0] != '(' || - value[strlen(value) - 1] != ')') - raw_queue = 1; - avahi_free(key); - avahi_free(value); - } - else - raw_queue = 1; - } - else if (domain && domain[0] != '\0') - raw_queue = 1; - if (raw_queue && CreateRemoteRawPrinterQueues == 0) - { - // The remote CUPS queue is raw, ignore it - debug_printf("Remote DNS-SD-advertised CUPS queue %s on host %s is raw, ignored.\n", - strrchr(resource, '/') + 1, remote_host); - free (remote_host); - if (make_model) - free (make_model); - return (NULL); - } - } - else - { - if (txt) - { - // Find out which PDLs the printer understands - entry = avahi_string_list_find((AvahiStringList *)txt, "pdl"); - if (entry) - { - avahi_string_list_get_pair(entry, &key, &value, NULL); - if (key && value && !strcasecmp(key, "pdl") && strlen(value) >= 3) - pdl = remove_bad_chars(value, 1); - avahi_free(key); - avahi_free(value); - } - // Find out if we have a color printer - entry = avahi_string_list_find((AvahiStringList *)txt, "Color"); - if (entry) - { - avahi_string_list_get_pair(entry, &key, &value, NULL); - if (key && value && !strcasecmp(key, "Color")) - { - if (!strcasecmp(value, "T")) color = 1; - if (!strcasecmp(value, "F")) color = 0; - } - avahi_free(key); - avahi_free(value); - } - // Find out if we have a duplex printer - entry = avahi_string_list_find((AvahiStringList *)txt, "Duplex"); - if (entry) - { - avahi_string_list_get_pair(entry, &key, &value, NULL); - if (key && value && !strcasecmp(key, "Duplex")) - { - if (!strcasecmp(value, "T")) duplex = 1; - if (!strcasecmp(value, "F")) duplex = 0; - } - avahi_free(key); - avahi_free(value); - } - } - } - // Extract location from DNS-SD TXT record's "note" field - if (location[0] == '\0') - { - if (txt) - { - entry = avahi_string_list_find((AvahiStringList *)txt, "note"); - if (entry) - { - avahi_string_list_get_pair(entry, &key, ¬e_value, NULL); - if (key && note_value && !strcasecmp(key, "note")) - { - debug_printf("examine_discovered_printer_record: TXT.note: |%s|\n", - note_value); // !! - location = note_value; - } - avahi_free(key); - // don't avahi_free(note_value) here! - } - } - } - // A NULL location is only passed in from resolve_callback(), which is - // HAVE_AVAHI -#endif // HAVE_AVAHI - - // Determine the device URI of the remote printer -#ifdef HAVE_AVAHI - if (txt && DNSSDBasedDeviceURIs) - { - // Printer is DNS-SD-discovered, so we can give a DNS-SD-service-name-based - // device URI to it (only if DNSSDBasedDeviceURIs config option is set) - snprintf(service_host_name, sizeof(service_host_name), "%s.%s.%s", - service_name, type, domain); - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1, - (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL, - service_host_name, 0, "/%s", - (is_cups_queue ? "cups" : "")); - } - else -#endif // HAVE_AVAHI - // Printer is discovered via legacy CUPS or LDAP, so we have to give - // a IP-based/host-name-based URI to it ( or for DNS-SD-discovered - // printers if DNSSDBasedDeviceURIs config option is not set) - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1, - (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL, - (ip != NULL ? ip : host), port, "/%s", resource); - - // Determine the queue name - pthread_rwlock_unlock(&lock); - local_queue_name = get_local_queue_name(service_name, make_model, resource, - remote_host, &is_cups_queue, NULL); - pthread_rwlock_wrlock(&lock); - if (local_queue_name == NULL) - goto fail; - - if (!matched_filters (local_queue_name, remote_host, port, service_name, - domain, txt)) - { - debug_printf("Printer %s does not match BrowseFilter lines in cups-browsed.conf, printer ignored.\n", - local_queue_name); - goto fail; - } - - - // Update network interface info if we were discovered by LDAP - // or legacy CUPS, needed for the is_local_hostname() function calls. - // During DNS-SD discovery the update is already done by the Avahi - // event handler function. - if (FrequentNetifUpdate && (type == NULL || type[0] == '\0')) - update_netifs(NULL); - - // Check if we have already created a queue for the discovered - // printer - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (!strcasecmp(p->queue_name, local_queue_name) && - (p->host[0] == '\0' || - p->status == STATUS_UNCONFIRMED || - p->status == STATUS_DISAPPEARED || - ((!strcasecmp(p->host, remote_host) || - (is_local_hostname(p->host) && is_local_hostname(remote_host))) && - (p->port == port || - (p->port == 631 && port == 443) || - (p->port == 443 && port == 631)) && - (txt || - (strlen(p->uri) - strlen(resource) > 0 && - !strcasecmp(p->uri + strlen(p->uri) - strlen(resource), - resource)))))) - break; - - // Is there a local queue with the same URI as the remote queue? - if (!p) - { - memset(&key_rec, 0, sizeof(key_rec)); - key_rec.uri = uri; - key_rec.host = remote_host; - key_rec.port = port; - key_rec.resource = resource; - key_rec.service_name = (char *)service_name; - key_rec.type = (char *)type; - key_rec.domain = (char *)domain; - if (g_hash_table_find (local_printers, - local_printer_is_same_device, &key_rec)) - { - // Found a local queue with the same URI as our discovered printer - // would get, so ignore this remote printer - debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, printer ignored.\n", - uri); - goto fail; - } - - // We need to create a local queue pointing to the - // discovered printer - p = create_remote_printer_entry(local_queue_name, location, info, uri, - remote_host, ip, port, resource, - service_name ? service_name : "", type, - domain, interface, family, pdl, color, - duplex, make_model, is_cups_queue); - } - else - { - debug_printf("Entry for %s (URI: %s) already exists.\n", - p->queue_name, p->uri); - // We have already created a local queue, check whether the - // discovered service allows us to upgrade the queue to IPPS - // or whether the URI part after ipp(s):// has changed, or - // whether the discovered queue is discovered via DNS-SD - // having more info in contrary to the existing being - // discovered by legacy CUPS or LDAP - - int downgrade = 0, upgrade = 0; - - // Get first element of array of interfaces on which this printer - // got already discovered, as this one is "lo" when it already got - // discovered through the loopback interface (preferred interface) - ipp_discovery_t *ippdis = cupsArrayFirst(p->ipp_discoveries); - - // Force upgrade if the found entry is marked unconfirmed or - // disappeared - if (p->status == STATUS_UNCONFIRMED || - p->status == STATUS_DISAPPEARED) - { - upgrade = 1; - debug_printf("Replacing printer entry %s (Host: %s, Port: %d) as it was marked %s. New URI: %s\n", - p->queue_name, remote_host, port, - (p->status == STATUS_UNCONFIRMED ? "unconfirmed" : - "disappeared"), - uri); - } - // Check if there is a downgrade - // IPPS -> IPP - else if ((ptr = strcasestr(type, "_ipp")) != NULL && - *(ptr + 4) != 's' && - !strncasecmp(p->uri, "ipps:", 5)) - { - downgrade = 1; - debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is only IPP, we have already IPPS, skipping\n", - p->queue_name, remote_host, port, uri); - } - // "lo" -> Any non-"lo" interface - else if (strcasecmp(interface, "lo") && - ippdis && !strcasecmp(ippdis->interface, "lo")) - { - downgrade = 1; - debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is from a non-loopback interface, we have already one from the loopback interface, skipping\n", - p->queue_name, remote_host, port, uri); - } - // DNS-SD -> CUPS Legacy/LDAP - else if (p->domain != NULL && p->domain[0] != '\0' && - (domain == NULL || domain[0] == '\0') && - p->type != NULL && p->type[0] != '\0' && - (type == NULL || type[0] == '\0')) - { - downgrade = 1; - debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is only discovered via legacy CUPS or LDAP, we have already a DNS-SD-discovered one, skipping\n", - p->queue_name, remote_host, port, uri); - } - - if (downgrade == 0) - { - // Check if there is an upgrade - // IPP -> IPPS - if (strcasestr(type, "_ipps") && - !strncasecmp(p->uri, "ipp:", 4)) - { - upgrade = 1; - debug_printf("Upgrading printer %s (Host: %s, Port: %d) to IPPS. New URI: %s\n", - p->queue_name, remote_host, port, uri); - } - // Any non-"lo" interface -> "lo" - else if (!strcasecmp(interface, "lo")) - { - upgrade = 1; - debug_printf("Upgrading printer %s (Host: %s, Port: %d) to use loopback interface \"lo\". New URI: %s\n", - p->queue_name, remote_host, port, uri); - } - // CUPS Legacy/LDAP -> DNS-SD - else if ((p->domain == NULL || p->domain[0] == '\0') && - domain != NULL && domain[0] != '\0' && - (p->type == NULL || p->type[0] == '\0') && - type != NULL && type[0] != '\0') - { - upgrade = 1; - debug_printf("Discovered printer %s (Host: %s, Port: %d, URI: %s) by DNS-SD now.\n", - p->queue_name, remote_host, port, uri); - } - } - - // Switch local queue over to this newly discovered service - if (upgrade == 1) - { - // Remove tiemout of legacy CUPS broadcasting - if (domain != NULL && domain[0] != '\0' && - type != NULL && type[0] != '\0' && - p->is_legacy) - { - p->is_legacy = 0; - if (p->status == STATUS_CONFIRMED) - p->timeout = (time_t) -1; - } - free(p->queue_name); - free(p->location); - free(p->info); - free(p->make_model); - free(p->pdl); - free(p->uri); - free(p->host); - free(p->ip); - free(p->resource); - free(p->service_name); - free(p->type); - free(p->domain); - p->queue_name = strdup(local_queue_name); - p->location = strdup(location); - p->info = strdup(info); - p->make_model = (make_model != NULL ? strdup(make_model) : NULL); - p->pdl = (pdl != NULL ? strdup(pdl) : NULL); - p->color = color; - p->duplex = duplex; - p->uri = strdup(uri); - p->status = STATUS_TO_BE_CREATED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - p->host = strdup(remote_host); - p->ip = (ip != NULL ? strdup(ip) : NULL); - p->port = port; - p->resource = strdup(resource); - p->service_name = strdup(service_name); - p->type = strdup(type); - p->domain = strdup(domain); - debug_printf("Switched over to newly discovered entry for this printer.\n"); - } - else - debug_printf("Staying with previously discovered entry for this printer.\n"); - - // Mark queue entry as confirmed if the entry - // is unconfirmed - if (p->status == STATUS_UNCONFIRMED || - p->status == STATUS_DISAPPEARED) - { - debug_printf("Marking entry for %s (URI: %s) as confirmed.\n", - p->queue_name, p->uri); - p->status = STATUS_CONFIRMED; - if (p->is_legacy) - { - p->timeout = time(NULL) + BrowseTimeout; - debug_printf("starting BrowseTimeout timer for %s (%ds)\n", - p->queue_name, BrowseTimeout); - } - else - p->timeout = (time_t) -1; - // If this queue was the default printer in its previous life, make - // it the default printer again. - queue_creation_handle_default(p->queue_name); - // If this queue is disabled, re-enable it. - enable_printer(p->queue_name); - // Record the options, to record any changes which happened - // while cups-browsed was not running - record_printer_options(p->queue_name); - } - - // Gather extra info from our new discovery - if (p->uri[0] == '\0') - { - free (p->uri); - p->uri = strdup(uri); - } - if (p->location[0] == '\0') - { - free (p->location); - p->location = strdup(location); - } - if (p->info[0] == '\0') - { - free (p->info); - p->info = strdup(info); - } - if (p->make_model == NULL || p->make_model[0] == '\0') - { - if (p->make_model) free (p->make_model); - p->make_model = (make_model != NULL ? strdup(make_model) : NULL); - } - if (p->pdl == NULL || p->pdl[0] == '\0') - { - if (p->pdl) free (p->pdl); - p->pdl = (pdl != NULL ? strdup(pdl) : NULL); - } - p->color = color; - p->duplex = duplex; - if (p->host[0] == '\0') - { - free (p->host); - p->host = strdup(remote_host); - } - if (p->ip == NULL || p->ip[0] == '\0') - { - if (p->ip) free (p->ip); - p->ip = (ip != NULL ? strdup(ip) : NULL); - } - if (p->port == 0) - p->port = port; - if (p->service_name[0] == '\0' && service_name) - { - free (p->service_name); - p->service_name = strdup(service_name); - } - if (p->resource[0] == '\0') - { - free (p->resource); - p->resource = strdup(resource); - } - if (p->type[0] == '\0' && type) - { - free (p->type); - p->type = strdup(type); - } - if (p->domain[0] == '\0' && domain) - { - free (p->domain); - p->domain = strdup(domain); - } - if (domain != NULL && domain[0] != '\0' && - type != NULL && type[0] != '\0') - ipp_discoveries_add(p->ipp_discoveries, interface, type, family); - p->netprinter = is_cups_queue ? 0 : 1; - } - - fail: - free (remote_host); - free (pdl); - free (make_model); - free (local_queue_name); -#ifdef HAVE_AVAHI - if (note_value) avahi_free(note_value); -#endif // HAVE_AVAHI - - if (p) - debug_printf("DNS-SD IDs: Service name: \"%s\", " - "Service type: \"%s\", Domain: \"%s\"\n", - p->service_name, p->type, p->domain); - - return (p); -} - - -static gboolean -allowed (struct sockaddr *srcaddr) -{ - allow_t *allow; - int i; - gboolean server_allowed; - allow_sense_t sense; - - if (browse_order == ORDER_DENY_ALLOW) - // BrowseOrder Deny,Allow: Allow server, then apply BrowseDeny lines, - // after that BrowseAllow lines - server_allowed = TRUE; - else - // BrowseOrder Allow,Deny: Deny server, then apply BrowseAllow lines, - // after that BrowseDeny lines - server_allowed = FALSE; - - for (i = 0; i <= 1; i ++) - { - if (browse_order == ORDER_DENY_ALLOW) - // Treat BrowseDeny lines first, then BrowseAllow lines - sense = (i == 0 ? ALLOW_DENY : ALLOW_ALLOW); - else - // Treat BrowseAllow lines first, then BrowseDeny lines - sense = (i == 0 ? ALLOW_ALLOW : ALLOW_DENY); - - if (server_allowed == (sense == ALLOW_ALLOW ? TRUE : FALSE)) - continue; - - if (browseallow_all && sense == ALLOW_ALLOW) - { - server_allowed = TRUE; - continue; - } - if (browsedeny_all && sense == ALLOW_DENY) - { - server_allowed = FALSE; - continue; - } - - for (allow = cupsArrayFirst (browseallow); - allow; - allow = cupsArrayNext (browseallow)) - { - if (allow->sense != sense) - continue; - - switch (allow->type) - { - case ALLOW_INVALID: - break; - - case ALLOW_IP: - switch (srcaddr->sa_family) - { - case AF_INET: - if (((struct sockaddr_in *) srcaddr)->sin_addr.s_addr == - allow->addr.ipv4.sin_addr.s_addr) - { - server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE); - goto match; - } - break; - - case AF_INET6: - if (!memcmp (&((struct sockaddr_in6 *) srcaddr)->sin6_addr, - &allow->addr.ipv6.sin6_addr, - sizeof (allow->addr.ipv6.sin6_addr))) - { - server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE); - goto match; - } - break; - } - break; - - case ALLOW_NET: - switch (srcaddr->sa_family) - { - struct sockaddr_in6 *src6addr; - - case AF_INET: - if ((((struct sockaddr_in *) srcaddr)->sin_addr.s_addr & - allow->mask.ipv4.sin_addr.s_addr) == - allow->addr.ipv4.sin_addr.s_addr) - { - server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE); - goto match; - } - break; - - case AF_INET6: - src6addr = (struct sockaddr_in6 *) srcaddr; - if (((src6addr->sin6_addr.s6_addr[0] & - allow->mask.ipv6.sin6_addr.s6_addr[0]) == - allow->addr.ipv6.sin6_addr.s6_addr[0]) && - ((src6addr->sin6_addr.s6_addr[1] & - allow->mask.ipv6.sin6_addr.s6_addr[1]) == - allow->addr.ipv6.sin6_addr.s6_addr[1]) && - ((src6addr->sin6_addr.s6_addr[2] & - allow->mask.ipv6.sin6_addr.s6_addr[2]) == - allow->addr.ipv6.sin6_addr.s6_addr[2]) && - ((src6addr->sin6_addr.s6_addr[3] & - allow->mask.ipv6.sin6_addr.s6_addr[3]) == - allow->addr.ipv6.sin6_addr.s6_addr[3])) - { - server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE); - goto match; - } - break; - } - } - } - match: - continue; - } - - return (server_allowed); -} - - -#ifdef HAVE_AVAHI -static void -resolve_callback(void* arg) -{ - resolver_args_t* a = (resolver_args_t*)arg; - - AvahiServiceResolver *r = a->r; - AvahiIfIndex interface = a->interface; - AvahiResolverEvent event = a->event; - const char *name = a->name; - const char *type = a->type; - const char *domain = a->domain; - const char *host_name = a->host_name; - const AvahiAddress *address = a->address; - uint16_t port = a->port; - AvahiStringList *txt = a->txt; - AvahiLookupResultFlags flags = a->flags; - AVAHI_GCC_UNUSED void* userdata = a->userdata; - - char ifname[IF_NAMESIZE]; - AvahiStringList *uuid_entry, *printer_type_entry; - char *uuid_key, *uuid_value; - - debug_printf("resolve_callback() in THREAD %ld\n", pthread_self()); - - if (r == NULL || name == NULL || type == NULL || domain == NULL) - return; - - // Get the interface name - if (!if_indextoname(interface, ifname)) - { - debug_printf("Unable to find interface name for interface %d: %s\n", - interface, strerror(errno)); - strncpy(ifname, "Unknown", sizeof(ifname) - 1); - } - - // Ignore local queues of the cupsd we are serving for, identifying them - // via UUID - - pthread_rwlock_wrlock(&resolvelock); - if (FrequentNetifUpdate) - update_netifs(NULL); - - if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) || !strcasecmp(ifname, "lo") || - is_local_hostname(host_name)) - { - update_local_printers (); - uuid_value = NULL; - if (txt && (uuid_entry = avahi_string_list_find(txt, "UUID"))) - avahi_string_list_get_pair(uuid_entry, &uuid_key, &uuid_value, NULL); - if (uuid_value && g_hash_table_find (local_printers, - local_printer_has_uuid, - uuid_value)) - { - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s) with UUID %s is from local CUPS, ignored (Avahi lookup result or host name of local machine).\n", - name, type, domain, host_name, port, ifname, - (address ? - (address->proto == AVAHI_PROTO_INET ? "IPv4" : - address->proto == AVAHI_PROTO_INET6 ? "IPv6" : - "IPv4/IPv6 Unknown") : - "IPv4/IPv6 Unknown"), uuid_value); - goto ignore; - } - if (txt && - (printer_type_entry = avahi_string_list_find(txt, "printer-type")) && - strcasestr(type, "_ipps")) - { - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s) with UUID %s is from another CUPS instance on the local system and uses IPPS, the local CUPS has problems to print on this printer, so we ignore it (Avahi lookup result or host name of local machine).\n", - name, type, domain, host_name, port, ifname, - (address ? - (address->proto == AVAHI_PROTO_INET ? "IPv4" : - address->proto == AVAHI_PROTO_INET6 ? "IPv6" : - "IPv4/IPv6 Unknown") : - "IPv4/IPv6 Unknown"), - (uuid_value ? uuid_value : "(unknown)")); - goto ignore; - } - } - - // Called whenever a service has been resolved successfully or timed out - - switch (event) - { - - // Resolver error - case AVAHI_RESOLVER_FAILURE: - debug_printf("Avahi-Resolver: Failed to resolve service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s): %s\n", - name, type, domain, host_name, port, ifname, - (address ? - (address->proto == AVAHI_PROTO_INET ? "IPv4" : - address->proto == AVAHI_PROTO_INET6 ? "IPv6" : - "IPv4/IPv6 Unknown") : - "IPv4/IPv6 Unknown"), - avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); - break; - - // New remote printer found - case AVAHI_RESOLVER_FOUND: - { - AvahiStringList *rp_entry, *adminurl_entry; - char *rp_key, *rp_value, *adminurl_key, *adminurl_value; - - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s).\n", - name, type, domain, host_name, port, ifname, - (address ? - (address->proto == AVAHI_PROTO_INET ? "IPv4" : - address->proto == AVAHI_PROTO_INET6 ? "IPv6" : - "IPv4/IPv6 Unknown") : - "IPv4/IPv6 Unknown")); - - // Ignore if terminated (by SIGTERM) - if (terminating) - { - debug_printf("Avahi Resolver: Ignoring because cups-browsed is terminating.\n"); - break; - } - - if (txt && (rp_entry = avahi_string_list_find(txt, "rp"))) - avahi_string_list_get_pair(rp_entry, &rp_key, &rp_value, NULL); - else - { - rp_key = strdup("rp"); - rp_value = strdup(""); - } - if (txt && (adminurl_entry = avahi_string_list_find(txt, "adminurl"))) - avahi_string_list_get_pair(adminurl_entry, &adminurl_key, - &adminurl_value, NULL); - else - { - adminurl_key = strdup("adminurl"); - if (host_name && - (adminurl_value = malloc(strlen(host_name) + 8)) != NULL) - sprintf(adminurl_value, "http://%s", host_name); - else - adminurl_value = strdup(""); - } - - // If we create queues only for local IPP printers (like IPP-over-USB - // with ippusbxd) check whether the entry is local and skip if not. - // We also check for remote CUPS (with "printer-type" TXT field) as this - // option is only for IPP network printers - if (CreateIPPPrinterQueues == IPP_PRINTERS_LOCAL_ONLY && - strcasecmp(ifname, "lo") && - (!txt || avahi_string_list_find(txt, "printer-type") == NULL)) - { - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, not a local service.\n", - name, type, domain); - goto clean_up; - } - - if (txt && rp_key && rp_value && adminurl_key && adminurl_value && - !strcasecmp(rp_key, "rp") && - !strcasecmp(adminurl_key, "adminurl")) - { - char *p, instance[64]; - // Extract instance from DNSSD service name (to serve as info field) - p = strstr(name, " @ "); - if (p) - { - int n; - n = p - name; - if (n >= sizeof(instance)) - n = sizeof(instance) - 1; - strncpy(instance, name, sizeof(instance) - 1); - instance[n] = '\0'; - debug_printf("Avahi-Resolver: Instance: %s\n", instance); // !! - } - else - instance[0] = '\0'; - // Determine the remote printer's IP - if (IPBasedDeviceURIs != IP_BASED_URIS_NO || - (!browseallow_all && cupsArrayCount(browseallow) > 0)) - { - struct sockaddr saddr; - struct sockaddr *addr = &saddr; - char *addrstr; - int addrlen; - int addrfound = 0; - if ((addrstr = calloc(256, sizeof(char))) == NULL) - { - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not allocate memory to determine IP address.\n", - name, type, domain); - goto clean_up; - } - if (address && - address->proto == AVAHI_PROTO_INET && - IPBasedDeviceURIs != IP_BASED_URIS_IPV6_ONLY) - { - avahi_address_snprint(addrstr, 256, address); - addr->sa_family = AF_INET; - if (inet_aton(addrstr, - &((struct sockaddr_in *) addr)->sin_addr) && - allowed(addr)) - addrfound = 1; - } - else if (address && - address->proto == AVAHI_PROTO_INET6 && - interface != AVAHI_IF_UNSPEC && - IPBasedDeviceURIs != IP_BASED_URIS_IPV4_ONLY) - { - strncpy(addrstr, "[v1.", sizeof(addrstr) - 1); - avahi_address_snprint(addrstr + 4, 256 - 6, address); - addrlen = strlen(addrstr + 4); - addr->sa_family = AF_INET6; - if (inet_pton(AF_INET6, addrstr + 4, - &((struct sockaddr_in6 *) addr)->sin6_addr) && - allowed(addr)) - { - if (!strncasecmp(addrstr + 4, "fe", 2) && - (addrstr[6] == '8' || addrstr[6] == '9' || - addrstr[6] == 'A' || addrstr[6] == 'B' || - addrstr[6] == 'a' || addrstr[6] == 'B')) - // Link-local address, needs specification of interface - snprintf(addrstr + addrlen + 4, 256 - - addrlen - 4, "%%%s]", - ifname); - else - { - addrstr[addrlen + 4] = ']'; - addrstr[addrlen + 5] = '\0'; - } - addrfound = 1; - } - } - else - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s': No IP address information available.\n", - name, type, domain); - if (addrfound == 1) - { - // Check remote printer type and create appropriate - // local queue to point to it - if (IPBasedDeviceURIs != IP_BASED_URIS_NO || - !host_name) - { - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with IP address %s.\n", - name, type, domain, addrstr); - pthread_rwlock_wrlock(&lock); - examine_discovered_printer_record((strcasecmp(ifname, "lo") ? - host_name : "localhost"), - addrstr, port, rp_value, - name, "", instance, type, - domain, ifname, - addr->sa_family, txt); - pthread_rwlock_unlock(&lock); - } - else - { - pthread_rwlock_wrlock(&lock); - examine_discovered_printer_record((strcasecmp(ifname, "lo") ? - host_name : "localhost"), - NULL, port, rp_value, - name, "", instance, type, - domain, ifname, - addr->sa_family, txt); - pthread_rwlock_unlock(&lock); - } - } - else - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not determine IP address.\n", - name, type, domain); - free(addrstr); - } - else - { - // Check remote printer type and create appropriate local queue to - // point to it - if (host_name) - { - pthread_rwlock_wrlock(&lock); - examine_discovered_printer_record((strcasecmp(ifname, "lo") ? - host_name : "localhost"), - NULL, port, rp_value, - name, "", instance, type, - domain, ifname, - (address->proto == - AVAHI_PROTO_INET ? AF_INET : - (address->proto == - AVAHI_PROTO_INET6 ? - AF_INET6 : 0)), - txt); - pthread_rwlock_unlock(&lock); - } - else - debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, host name not supplied.\n", - name, type, domain); - } - } - - clean_up: - - // Clean up - - if (rp_entry) - { - avahi_free(rp_key); - avahi_free(rp_value); - } - else - { - free(rp_key); - free(rp_value); - } - if (adminurl_entry) - { - avahi_free(adminurl_key); - avahi_free(adminurl_value); - } - else - { - free(adminurl_key); - free(adminurl_value); - } - break; - } - } - - ignore: - if (a->r) avahi_service_resolver_free(a->r); - if (a->name) free((char*)a->name); - if (a->type) free((char*)a->type); - if (a->domain) free((char*)a->domain); - if (a->host_name) free((char*)a->host_name); - if (a->txt) free(a->txt); - if (a->address) free((AvahiAddress*)a->address); - free(a); - pthread_rwlock_unlock(&resolvelock); - - if (in_shutdown == 0) - recheck_timer (); -} - - -static void -resolver_wrapper(AvahiServiceResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, - const char *name, - const char *type, - const char *domain, - const char *host_name, - const AvahiAddress *address, - uint16_t port, - AvahiStringList *txt, - AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) -{ - debug_printf("resolver_wrapper() in THREAD %ld\n", pthread_self()); - - resolver_args_t *arg = (resolver_args_t*)malloc(sizeof(resolver_args_t)); - AvahiStringList* temp_txt = (AvahiStringList*)malloc(sizeof(AvahiStringList)); - AvahiAddress* temp_addr = (AvahiAddress*)malloc(sizeof(AvahiAddress)); - - temp_txt = avahi_string_list_copy(txt); - - if (address) - { - temp_addr->proto = address->proto; - temp_addr->data = address->data; - } - - arg->r = r; - arg->interface = interface; - arg->protocol = protocol; - arg->event = event; - arg->name = strdup(name); - arg->type = strdup(type); - arg->domain = strdup(domain); - if(host_name) arg->host_name = strdup(host_name); - else arg->host_name = NULL; - arg->address = temp_addr; - arg->port = port; - arg->txt = temp_txt; - arg->flags = flags; - arg->userdata = userdata; - - pthread_t id; - int err; - - if ((err = pthread_create(&id, NULL, (void*)resolve_callback, (void*)arg))) - { - debug_printf("Unable to create a new thread, retrying!\n"); - int attempts = 0; - while (attempts < 5) - { - if ((err = pthread_create(&id, NULL, (void*)resolve_callback, - (void*)arg))) - debug_printf("Unable to create a new thread, retrying!\n"); - else - break; - attempts ++; - } - if (attempts == 5) - { - debug_printf("Could not create new thread even after many attempts, ignoring this entry.\n"); - if (arg->r) avahi_service_resolver_free(arg->r); - if (arg->name) free((char*)arg->name); - if (arg->type) free((char*)arg->type); - if (arg->domain) free((char*)arg->domain); - if (arg->host_name) free((char*)arg->host_name); - if (arg->txt) free(arg->txt); - if (arg->address) free((AvahiAddress*)arg->address); - free(arg); - return; - } - } - pthread_detach(id); -} - - -static void -browse_callback(AvahiServiceBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, - const char *type, - const char *domain, - AvahiLookupResultFlags flags, - void* userdata) -{ - AvahiClient *c = userdata; - char ifname[IF_NAMESIZE]; - - debug_printf("browse_callback() in THREAD %ld\n", pthread_self()); - - if (b == NULL) - return; - - // Called whenever a new services becomes available on the LAN or - // is removed from the LAN - - switch (event) - { - - // Avahi browser error - case AVAHI_BROWSER_FAILURE: - - debug_printf("Avahi Browser: ERROR: %s\n", - avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); - g_main_loop_quit(gmainloop); - g_main_context_wakeup(NULL); - return; - - // New service (remote printer) - case AVAHI_BROWSER_NEW: - - if (c == NULL || name == NULL || type == NULL || domain == NULL) - return; - - // Get the interface name - if (!if_indextoname(interface, ifname)) - { - debug_printf("Unable to find interface name for interface %d: %s\n", - interface, strerror(errno)); - strncpy(ifname, "Unknown", sizeof(ifname) - 1); - } - - debug_printf("Avahi Browser: NEW: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n", - name, type, domain, ifname, - protocol != AVAHI_PROTO_UNSPEC ? - avahi_proto_to_string(protocol) : "Unknown"); - - // Ignore if terminated (by SIGTERM) - if (terminating) - { - debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n"); - break; - } - - // We ignore the returned resolver object. In the callback - // function we free it. If the server is terminated before - // the callback function is called the server will free - // the resolver for us. - - if (!(avahi_service_resolver_new(c, interface, protocol, name, type, - domain, AVAHI_PROTO_UNSPEC, 0, - resolver_wrapper, c))) - debug_printf("Failed to resolve service '%s': %s\n", - name, avahi_strerror(avahi_client_errno(c))); - break; - - // A service (remote printer) has disappeared - case AVAHI_BROWSER_REMOVE: - { - remote_printer_t *p; - - if (name == NULL || type == NULL || domain == NULL) - return; - - // Get the interface name - if (!if_indextoname(interface, ifname)) - { - debug_printf("Unable to find interface name for interface %d: %s\n", - interface, strerror(errno)); - strncpy(ifname, "Unknown", sizeof(ifname) - 1); - } - - debug_printf("Avahi Browser: REMOVE: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n", - name, type, domain, ifname, - protocol != AVAHI_PROTO_UNSPEC ? - avahi_proto_to_string(protocol) : "Unknown"); - - // Ignore if terminated (by SIGTERM) - if (terminating) - { - debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n"); - break; - } - - // Check whether we have listed this printer - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - if (p->status != STATUS_DISAPPEARED && - p->status != STATUS_TO_BE_RELEASED && - !strcasecmp(p->service_name, name) && - !strcasecmp(p->domain, domain)) - break; - if (p) - { - int family = - (protocol == AVAHI_PROTO_INET ? AF_INET : - (protocol == AVAHI_PROTO_INET6 ? AF_INET6 : 0)); - if (p->ipp_discoveries) - { - ipp_discovery_t *ippdis; - for (ippdis = cupsArrayFirst(p->ipp_discoveries); ippdis; - ippdis = cupsArrayNext(p->ipp_discoveries)) - if (!strcasecmp(ippdis->interface, ifname) && - !strcasecmp(ippdis->type, type) && - ippdis->family == family) - { - debug_printf("Discovered instance for printer with Service name \"%s\", Domain \"%s\" unregistered: Interface \"%s\", Service type: \"%s\", Protocol: \"%s\"\n", - p->service_name, p->domain, - ippdis->interface, ippdis->type, - (ippdis->family == AF_INET ? "IPv4" : - (ippdis->family == AF_INET6 ? "IPv6" : "Unknown"))); - cupsArrayRemove(p->ipp_discoveries, (void *)ippdis); - ipp_discoveries_list(p->ipp_discoveries); - break; - } - // Remove the entry if no discovered instances are left - if (cupsArrayCount(p->ipp_discoveries) == 0) - { - debug_printf("Removing printer with Service name \"%s\", Domain \"%s\", all discovered instances disappeared.\n", - p->service_name, p->domain); - remove_printer_entry(p); - } - } - - if (in_shutdown == 0) - recheck_timer (); - } - break; - } - - // All cached Avahi events are treated now - case AVAHI_BROWSER_ALL_FOR_NOW: - case AVAHI_BROWSER_CACHE_EXHAUSTED: - debug_printf("Avahi Browser: %s\n", - event == AVAHI_BROWSER_CACHE_EXHAUSTED ? - "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); - break; - } -} - - -static void -avahi_browser_shutdown() -{ - remote_printer_t *p; - - avahi_present = 0; - - // Remove all queues which we have set up based on DNS-SD discovery - if (cupsArrayCount(remote_printers) > 0) - { - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (p->type && p->type[0]) - { - if (KeepGeneratedQueuesOnShutdown) - { - if (p->status != STATUS_TO_BE_RELEASED && - p->status != STATUS_DISAPPEARED) - { - p->status = STATUS_UNCONFIRMED; - p->timeout = time(NULL) + TIMEOUT_CONFIRM; - } - } - else - { - if (p->status != STATUS_TO_BE_RELEASED) - p->status = STATUS_DISAPPEARED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - } - } - } - if (in_shutdown == 0) - recheck_timer(); - else - update_cups_queues(NULL); - } - - // Free the data structures for DNS-SD browsing - if (sb1) - { - avahi_service_browser_free(sb1); - sb1 = NULL; - } - if (sb2) - { - avahi_service_browser_free(sb2); - sb2 = NULL; - } - - // Switch on auto shutdown mode - if (autoshutdown_avahi && in_shutdown == 0) - { - autoshutdown = 1; - debug_printf("Avahi server disappeared, switching to auto shutdown mode ...\n"); - // If there are no printers or no jobs schedule the shutdown in - // autoshutdown_timeout seconds - if (!autoshutdown_exec_id && - (cupsArrayCount(remote_printers) == 0 || - (autoshutdown_on == NO_JOBS && check_jobs() == 0))) - { - debug_printf ("We entered auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout); - autoshutdown_exec_id = - g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, - NULL); - } - } -} - - -static void -avahi_shutdown() -{ - avahi_browser_shutdown(); - if (client) - { - avahi_client_free(client); - client = NULL; - } - if (glib_poll) - { - avahi_glib_poll_free(glib_poll); - glib_poll = NULL; - } -} - - -static void -client_callback(AvahiClient *c, - AvahiClientState state, - AVAHI_GCC_UNUSED void *userdata) -{ - int error; - - if (c == NULL) - return; - - // Called whenever the client or server state changes - switch (state) - { - - // avahi-daemon available - case AVAHI_CLIENT_S_REGISTERING: - case AVAHI_CLIENT_S_RUNNING: - case AVAHI_CLIENT_S_COLLISION: - - debug_printf("Avahi server connection got available, setting up service browsers.\n"); - - // Create the service browsers - if (!sb1) - if (!(sb1 = - avahi_service_browser_new(c, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_ipp._tcp", NULL, 0, browse_callback, - c))) - { - debug_printf("ERROR: Failed to create service browser for IPP: %s\n", - avahi_strerror(avahi_client_errno(c))); - } - if (!sb2) - if (!(sb2 = - avahi_service_browser_new(c, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - "_ipps._tcp", NULL, 0, - browse_callback, - c))) - { - debug_printf("ERROR: Failed to create service browser for IPPS: %s\n", - avahi_strerror(avahi_client_errno(c))); - } - - avahi_present = 1; - - // switch off auto shutdown mode - if (autoshutdown_avahi) - { - autoshutdown = 0; - debug_printf("Avahi server available, switching to permanent mode ...\n"); - // If there is still an active auto shutdown timer, kill it - if (autoshutdown_exec_id) - { - debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n"); - g_source_remove(autoshutdown_exec_id); - autoshutdown_exec_id = 0; - } - } - - break; - - // Avahi client error - case AVAHI_CLIENT_FAILURE: - - if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) - { - debug_printf("Avahi server disappeared, shutting down service browsers, removing DNS-SD-discovered print queues.\n"); - avahi_browser_shutdown(); - // Renewing client - avahi_client_free(client); - client = avahi_client_new(avahi_glib_poll_get(glib_poll), - AVAHI_CLIENT_NO_FAIL, - client_callback, NULL, &error); - if (!client) - { - debug_printf("ERROR: Failed to create client: %s\n", - avahi_strerror(error)); - BrowseRemoteProtocols &= ~BROWSE_DNSSD; - avahi_shutdown(); - } - } - else - { - debug_printf("ERROR: Avahi server connection failure: %s\n", - avahi_strerror(avahi_client_errno(c))); - g_main_loop_quit(gmainloop); - g_main_context_wakeup(NULL); - } - break; - - default: - break; - } -} - - -static void -avahi_init() -{ - int error; - - if (BrowseRemoteProtocols & BROWSE_DNSSD) - { - // Allocate main loop object - if (!glib_poll) - if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) - { - debug_printf("ERROR: Failed to create glib poll object.\n"); - goto avahi_init_fail; - } - - // Allocate a new client - if (!client) - client = avahi_client_new(avahi_glib_poll_get(glib_poll), - AVAHI_CLIENT_NO_FAIL, - client_callback, NULL, &error); - - // Check wether creating the client object succeeded - if (!client) - { - debug_printf("ERROR: Failed to create client: %s\n", - avahi_strerror(error)); - goto avahi_init_fail; - } - - return; - - avahi_init_fail: - BrowseRemoteProtocols &= ~BROWSE_DNSSD; - avahi_shutdown(); - } -} -#endif // HAVE_AVAHI - - -// -// A CUPS printer has been discovered via CUPS Browsing -// or with BrowsePoll -// - -static void -found_cups_printer(const char *remote_host, - const char *uri, - const char *location, - const char *info) -{ - char scheme[32]; - char username[64]; - char host[HTTP_MAX_HOST]; - char resource[HTTP_MAX_URI]; - int port; - netif_t *iface; - char local_resource[HTTP_MAX_URI]; - char service_name[HTTP_MAX_URI]; - char *c; - int hl; - remote_printer_t *printer; - - memset(scheme, 0, sizeof(scheme)); - memset(username, 0, sizeof(username)); - memset(host, 0, sizeof(host)); - memset(resource, 0, sizeof(resource)); - memset(local_resource, 0, sizeof(local_resource)); - - httpSeparateURI(HTTP_URI_CODING_ALL, uri, - scheme, sizeof(scheme) - 1, - username, sizeof(username) - 1, - host, sizeof(host) - 1, - &port, - resource, sizeof(resource)- 1); - - // Check this isn't one of our own broadcasts - for (iface = cupsArrayFirst (netifs); - iface; - iface = cupsArrayNext (netifs)) - if (!strcasecmp (host, iface->address)) - break; - if (iface) - { - debug_printf("ignoring own broadcast on %s\n", - iface->address); - return; - } - - if (strncasecmp (resource, "/printers/", 10) && - strncasecmp (resource, "/classes/", 9)) - { - debug_printf("Don't understand URI: %s\n", uri); - return; - } - - strncpy (local_resource, resource + 1, sizeof (local_resource) - 1); - local_resource[sizeof (local_resource) - 1] = '\0'; - c = strchr (local_resource, '?'); - if (c) - *c = '\0'; - - // Build the DNS-SD service name which CUPS would give to this printer - // when DNS-SD-broadcasting it - snprintf(service_name, sizeof (service_name), "%s @ %s", - (info ? info : strchr(local_resource, '/') + 1), host); - // Cut off trailing ".local" of host name - hl = strlen(service_name); - if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local")) - service_name[hl - 6] = '\0'; - if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local.")) - service_name[hl - 7] = '\0'; - // DNS-SD service name has max. 63 characters - service_name[63] = '\0'; - - debug_printf("CUPS browsing: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n", - host, port, strchr(local_resource, '/') + 1, service_name); - - pthread_rwlock_wrlock(&lock); - printer = examine_discovered_printer_record(host, NULL, port, local_resource, - service_name, - location ? location : "", - info ? info : "", "", "", "", 0, - NULL); - pthread_rwlock_unlock(&lock); - - if (printer && - (printer->domain == NULL || printer->domain[0] == '\0' || - printer->type == NULL || printer->type[0] == '\0')) - { - printer->is_legacy = 1; - - if (printer->status != STATUS_TO_BE_CREATED) - { - printer->timeout = time(NULL) + BrowseTimeout; - debug_printf("starting BrowseTimeout timer for %s (%ds)\n", - printer->queue_name, BrowseTimeout); - } - } - - if (printer && NewBrowsePollQueuesShared) - printer->num_options = cupsAddOption("printer-to-be-shared", "true", - printer->num_options, - &(printer->options)); - -} - - -static gboolean -process_browse_data (GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - char packet[2048]; - http_addr_t srcaddr; - socklen_t srclen; - ssize_t got; - unsigned int type; - unsigned int state; - char remote_host[256]; - char uri[1024]; - char location[1024]; - char info[1024]; - char *c = NULL, *end = NULL; - - debug_printf("process_browse_data() in THREAD %ld\n", pthread_self()); - - memset(packet, 0, sizeof(packet)); - memset(remote_host, 0, sizeof(remote_host)); - memset(uri, 0, sizeof(uri)); - memset(info, 0, sizeof(info)); - - srclen = sizeof (srcaddr); - got = recvfrom (browsesocket, packet, sizeof (packet) - 1, 0, - &srcaddr.addr, &srclen); - if (got == -1) - { - debug_printf ("cupsd-browsed: error receiving browse packet: %s\n", - strerror (errno)); - // Remove this I/O source - return (FALSE); - } - - packet[got] = '\0'; - httpAddrString (&srcaddr, remote_host, sizeof (remote_host) - 1); - - // Check this packet is allowed - if (!allowed ((struct sockaddr *) &srcaddr)) - { - debug_printf("browse packet from %s disallowed\n", - remote_host); - return (TRUE); - } - - debug_printf("browse packet received from %s\n", - remote_host); - - if (sscanf (packet, "%x%x%1023s", &type, &state, uri) < 3) - { - debug_printf("incorrect browse packet format\n"); - return (TRUE); - } - - info[0] = '\0'; - - // do not read OOB - end = packet + sizeof(packet); - c = strchr (packet, '\"'); - if (c >= end) - return (TRUE); - - if (c) - { - // Extract location field - { - int i; - c++; - for (i = 0; - i < sizeof (location) - 1 && *c != '\"' && c < end; - i++, c++) - location[i] = *c; - location[i] = '\0'; - debug_printf("process_browse_data: location: |%s|\n", location); // !! - } - for (; c < end && *c != '\"'; c++); - - if (c >= end) - return (TRUE); - - if (*c == '\"') - for (c++; c < end && isspace(*c); c++); - - if (c >= end) - return (TRUE); - - // Is there an info field? - if (*c == '\"') - { - int i; - c++; - for (i = 0; - i < sizeof (info) - 1 && *c != '\"' && c < end; - i++, c++) - info[i] = *c; - info[i] = '\0'; - debug_printf("process_browse_data: info: |%s|\n", info); // !! - } - } - if (c >= end) - return (TRUE); - - if (!(type & CUPS_PRINTER_DELETE)) - found_cups_printer (remote_host, uri, location, info); - - if (in_shutdown == 0) - recheck_timer (); - - // Don't remove this I/O source - return (TRUE); -} - - -static void -broadcast_browse_packets (gpointer data, - gpointer user_data) -{ - browse_data_t *bdata = data; - netif_t *browse; - char packet[2048]; - char uri[HTTP_MAX_URI]; - char scheme[32]; - char username[64]; - char host[HTTP_MAX_HOST]; - int port; - char resource[HTTP_MAX_URI]; - - debug_printf("broadcast_browse_packets() in THREAD %ld\n", pthread_self()); - - for (browse = (netif_t *)cupsArrayFirst (netifs); - browse != NULL; - browse = (netif_t *)cupsArrayNext (netifs)) - { - // Replace 'localhost' with our IP address on this interface - httpSeparateURI(HTTP_URI_CODING_ALL, bdata->uri, - scheme, sizeof(scheme), - username, sizeof(username), - host, sizeof(host), - &port, - resource, sizeof(resource)); - httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri), - scheme, username, browse->address, port, resource); - - if (snprintf (packet, sizeof (packet), - "%x " // type - "%x " // state - "%s " // uri - "\"%s\" " // location - "\"%s\" " // info - "\"%s\" " // make-and-model - "lease-duration=%d" // BrowseTimeout - "%s%s" // other browse options - "\n", - bdata->type, - bdata->state, - uri, - bdata->location, - bdata->info, - bdata->make_model, - BrowseTimeout, - bdata->browse_options ? " " : "", - bdata->browse_options ? bdata->browse_options : "") - >= sizeof (packet)) - { - debug_printf ("oversize packet not sent\n"); - continue; - } - - debug_printf("packet to send:\n%s", packet); - - int err = sendto (browsesocket, packet, - strlen (packet), 0, - &browse->broadcast.addr, - httpAddrLength (&browse->broadcast)); - if (err == -1) - debug_printf("cupsd-browsed: sendto returned %d: %s\n", - err, strerror (errno)); - } -} - - -static gboolean -send_browse_data (gpointer data) -{ - debug_printf("send_browse_data() in THREAD %ld\n", pthread_self()); - update_netifs (NULL); - res_init (); - update_local_printers (); - g_list_foreach (browse_data, broadcast_browse_packets, NULL); - g_timeout_add_seconds (BrowseInterval, send_browse_data, NULL); - - // Stop this timeout handler, we called a new one - return (FALSE); -} - - -static browsepoll_printer_t * -new_browsepoll_printer (const char *uri_supported, - const char *location, - const char *info) -{ - browsepoll_printer_t *printer = g_malloc (sizeof (browsepoll_printer_t)); - printer->uri_supported = g_strdup (uri_supported); - printer->location = g_strdup (location); - printer->info = g_strdup (info); - return (printer); -} - - -static void -browsepoll_printer_free (gpointer data) -{ - browsepoll_printer_t *printer = data; - debug_printf("browsepoll_printer_free() in THREAD %ld\n", pthread_self()); - free (printer->uri_supported); - free (printer->location); - free (printer->info); - free (printer); -} - - -static void -browse_poll_get_printers (browsepoll_t *context, - http_t *conn) -{ - static const char * const rattrs[] = { "printer-uri-supported", - "printer-location", - "printer-info"}; - ipp_t *request, *response = NULL; - ipp_attribute_t *attr; - GList *printers = NULL; - - debug_printf ("cups-browsed [BrowsePoll %s:%d]: CUPS-Get-Printers\n", - context->server, context->port); - - request = ippNewRequest(CUPS_GET_PRINTERS); - if (context->major > 0) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n", - context->server, context->port, context->major, - context->minor); - ippSetVersion (request, context->major, context->minor); - } - - ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]), - NULL, - rattrs); - - // Ask the server to exclude printers that are remote or not shared, - // or implicit classes. - ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM, - "printer-type-mask", - CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT | - CUPS_PRINTER_NOT_SHARED); - ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM, - "printer-type", 0); - - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser ()); - - response = cupsDoRequest(conn, request, "/"); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n", - context->server, context->port, cupsLastErrorString ()); - goto fail; - } - - for (attr = ippFirstAttribute(response); attr; - attr = ippNextAttribute(response)) - { - browsepoll_printer_t *printer; - const char *uri, *location, *info; - - while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER) - attr = ippNextAttribute(response); - - if (!attr) - break; - - uri = NULL; - info = NULL; - location = NULL; - while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) - { - if (!strcasecmp (ippGetName(attr), "printer-uri-supported") && - ippGetValueTag(attr) == IPP_TAG_URI) - uri = ippGetString(attr, 0, NULL); - else if (!strcasecmp (ippGetName(attr), "printer-location") && - ippGetValueTag(attr) == IPP_TAG_TEXT) - location = ippGetString(attr, 0, NULL); - else if (!strcasecmp (ippGetName(attr), "printer-info") && - ippGetValueTag(attr) == IPP_TAG_TEXT) - info = ippGetString(attr, 0, NULL); - attr = ippNextAttribute(response); - } - - if (uri) - { - found_cups_printer (context->server, uri, location, info); - printer = new_browsepoll_printer (uri, location, info); - printers = g_list_insert (printers, printer, 0); - } - - if (!attr) - break; - } - - g_list_free_full (context->printers, browsepoll_printer_free); - context->printers = printers; - -fail: - if (response) - ippDelete(response); -} - - -static void -browse_poll_create_subscription (browsepoll_t *context, - http_t *conn) -{ - static const char * const events[] = { "printer-added", - "printer-changed", - "printer-config-changed", - "printer-modified", - "printer-deleted", - "printer-state-changed" }; - ipp_t *request, *response = NULL; - ipp_attribute_t *attr; - - debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Create-Subscription\n", - context->server, context->port); - - request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION); - if (context->major > 0) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n", - context->server, context->port, context->major, - context->minor); - ippSetVersion (request, context->major, context->minor); - } - - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, - "notify-pull-method", NULL, "ippget"); - ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_CHARSET, - "notify-charset", NULL, "utf-8"); - ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser ()); - ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, - "notify-events", sizeof (events) / sizeof (events[0]), - NULL, events); - ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, - "notify-time-interval", BrowseInterval); - - response = cupsDoRequest (conn, request, "/"); - if (!response || - ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n", - context->server, context->port, cupsLastErrorString ()); - context->subscription_id = -1; - context->can_subscribe = FALSE; - goto fail; - } - - for (attr = ippFirstAttribute(response); attr; - attr = ippNextAttribute(response)) - { - if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION) - { - if (ippGetValueTag (attr) == IPP_TAG_INTEGER && - !strcasecmp (ippGetName (attr), "notify-subscription-id")) - { - context->subscription_id = ippGetInteger (attr, 0); - debug_printf("cups-browsed [BrowsePoll %s:%d]: subscription ID=%d\n", - context->server, context->port, context->subscription_id); - break; - } - } - } - - if (!attr) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: no ID returned\n", - context->server, context->port); - context->subscription_id = -1; - context->can_subscribe = FALSE; - } - -fail: - if (response) - ippDelete(response); -} - - -static void -browse_poll_cancel_subscription (browsepoll_t *context) -{ - ipp_t *request, *response = NULL; - http_t *conn = httpConnectEncryptShortTimeout (context->server, context->port, - HTTP_ENCRYPT_IF_REQUESTED); - - if (conn == NULL) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: connection failure " - "attempting to cancel\n", context->server, context->port); - return; - } - - httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL); - - debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Cancel-Subscription\n", - context->server, context->port); - - request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION); - if (context->major > 0) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n", - context->server, context->port, context->major, - context->minor); - ippSetVersion (request, context->major, context->minor); - } - - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser ()); - ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, - "notify-subscription-id", context->subscription_id); - - response = cupsDoRequest (conn, request, "/"); - if (!response || - ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE) - debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n", - context->server, context->port, cupsLastErrorString ()); - - if (response) - ippDelete(response); - if (conn) - httpClose (conn); -} - - -static gboolean -browse_poll_get_notifications (browsepoll_t *context, - http_t *conn) -{ - ipp_t *request, *response = NULL; - ipp_status_t status; - gboolean get_printers = FALSE; - - debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Get-Notifications\n", - context->server, context->port); - - request = ippNewRequest(IPP_GET_NOTIFICATIONS); - if (context->major > 0) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n", - context->server, context->port, context->major, - context->minor); - ippSetVersion (request, context->major, context->minor); - } - - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, cupsUser ()); - ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, - "notify-subscription-ids", context->subscription_id); - ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, - "notify-sequence-numbers", context->sequence_number + 1); - - response = cupsDoRequest (conn, request, "/"); - if (!response) - status = cupsLastError (); - else - status = ippGetStatusCode (response); - - if (status == IPP_STATUS_ERROR_NOT_FOUND) - { - // Subscription lease has expired. - debug_printf ("cups-browsed [BrowsePoll %s:%d]: Lease expired\n", - context->server, context->port); - browse_poll_create_subscription (context, conn); - get_printers = TRUE; - } - else if (status > IPP_STATUS_OK_EVENTS_COMPLETE) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n", - context->server, context->port, cupsLastErrorString ()); - context->can_subscribe = FALSE; - browse_poll_cancel_subscription (context); - context->subscription_id = -1; - context->sequence_number = 0; - get_printers = TRUE; - } - - if (!get_printers) - { - ipp_attribute_t *attr; - gboolean seen_event = FALSE; - int last_seq = context->sequence_number; - if (response == NULL) - return (FALSE); - for (attr = ippFirstAttribute(response); attr; - attr = ippNextAttribute(response)) - if (ippGetGroupTag (attr) == IPP_TAG_EVENT_NOTIFICATION) - { - // There is a printer-* event here. - seen_event = TRUE; - - if (!strcmp (ippGetName (attr), "notify-sequence-number") && - ippGetValueTag (attr) == IPP_TAG_INTEGER) - last_seq = ippGetInteger (attr, 0); - } - - if (seen_event) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: printer-* event\n", - context->server, context->port); - context->sequence_number = last_seq; - get_printers = TRUE; - } - else - debug_printf("cups-browsed [BrowsePoll %s:%d]: no events\n", - context->server, context->port); - } - - if (response) - ippDelete (response); - - return (get_printers); -} - - -static void -browsepoll_printer_keepalive (gpointer data, - gpointer user_data) -{ - browsepoll_printer_t *printer = data; - const char *server = user_data; - debug_printf("browsepoll_printer_keepalive() in THREAD %ld\n", - pthread_self()); - found_cups_printer (server, printer->uri_supported, printer->location, - printer->info); -} - - -static gboolean -browse_poll (gpointer data) -{ - browsepoll_t *context = data; - http_t *conn = NULL; - gboolean get_printers = FALSE; - - debug_printf("browse_poll() in THREAD %ld\n", pthread_self()); - - debug_printf("browse polling %s:%d\n", - context->server, context->port); - - res_init (); - - conn = httpConnectEncryptShortTimeout (context->server, context->port, - HTTP_ENCRYPT_IF_REQUESTED); - if (conn == NULL) - { - debug_printf("cups-browsed [BrowsePoll %s:%d]: failed to connect\n", - context->server, context->port); - goto fail; - } - - httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL); - - if (context->can_subscribe) - { - if (context->subscription_id == -1) - { - // The first time this callback is run we need to create the IPP - // subscription to watch to printer-* events. - browse_poll_create_subscription (context, conn); - get_printers = TRUE; - } - else - // On subsequent runs, check for notifications using our - // subscription. - get_printers = browse_poll_get_notifications (context, conn); - } - else - get_printers = TRUE; - - update_local_printers (); - inhibit_local_printers_update = TRUE; - if (get_printers) - browse_poll_get_printers (context, conn); - else - g_list_foreach (context->printers, browsepoll_printer_keepalive, - context->server); - - inhibit_local_printers_update = FALSE; - - if (in_shutdown == 0) - recheck_timer (); - - fail: - - if (conn) - httpClose (conn); - - // Call a new timeout handler so that we run again - g_timeout_add_seconds (BrowseInterval, browse_poll, data); - - // Stop this timeout handler, we called a new one - return (FALSE); -} - - -#ifdef HAVE_LDAP -static gboolean -browse_ldap_poll (gpointer data) -{ - char *tmpFilter; // Query filter - int filterLen; - - debug_printf("browse_ldap_poll() in THREAD %ld\n", pthread_self()); - - // do real stuff here - if (!BrowseLDAPDN) - { - debug_printf("Need to set BrowseLDAPDN to use LDAP browsing!\n"); - BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - - return (FALSE); - } - else - { - if (!BrowseLDAPInitialised) - { - BrowseLDAPInitialised = TRUE; - // - // Query filter string - // - - if (BrowseLDAPFilter) - filterLen = snprintf(NULL, 0, "(&%s%s)", LDAP_BROWSE_FILTER, - BrowseLDAPFilter); - else - filterLen = strlen(LDAP_BROWSE_FILTER); - - tmpFilter = (char *)malloc(filterLen + 1); - if (!tmpFilter) - { - debug_printf("Could not allocate memory for LDAP browse query filter!\n"); - BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - return (FALSE); - } - - if (BrowseLDAPFilter) - { - snprintf(tmpFilter, filterLen + 1, "(&%s%s)", LDAP_BROWSE_FILTER, - BrowseLDAPFilter); - free(BrowseLDAPFilter); - BrowseLDAPFilter = NULL; - } - else - strcpy(tmpFilter, LDAP_BROWSE_FILTER); - - BrowseLDAPFilter = tmpFilter; - - // - // Open LDAP handle... - // - - BrowseLDAPHandle = ldap_new_connection(); - } - - cupsdUpdateLDAPBrowse(); - if (in_shutdown == 0) - recheck_timer(); - } - - // Call a new timeout handler so that we run again - g_timeout_add_seconds (BrowseInterval, browse_ldap_poll, data); - - // Stop this timeout handler, we called a new one - return (FALSE); -} -#endif // HAVE_LDAP - - -static void -sigterm_handler(int sig) -{ - (void)sig; // remove compiler warnings... - - if (terminating) - { - debug_printf("Caught signal %d while already terminating.\n", sig); - return; - } - terminating = 1; // ignore any further callbacks and break loops - // Flag that we should stop and return... - g_main_loop_quit(gmainloop); - g_main_context_wakeup(NULL); - debug_printf("Caught signal %d, shutting down ...\n", sig); -} - - -static void -sigusr1_handler(int sig) -{ - (void)sig; // remove compiler warnings... - - // Turn off auto shutdown mode... - autoshutdown = 0; - debug_printf("Caught signal %d, switching to permanent mode ...\n", sig); - // If there is still an active auto shutdown timer, kill it - if (autoshutdown_exec_id) - { - debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n"); - g_source_remove(autoshutdown_exec_id); - autoshutdown_exec_id = 0; - } -} - - -static void -sigusr2_handler(int sig) -{ - (void)sig; // remove compiler warnings... - - // Turn on auto shutdown mode... - autoshutdown = 1; - debug_printf("Caught signal %d, switching to auto shutdown mode ...\n", sig); - // If there are no printers or no jobs schedule the shutdown in - // autoshutdown_timeout seconds - if (!autoshutdown_exec_id && - (cupsArrayCount(remote_printers) == 0 || - (autoshutdown_on == NO_JOBS && check_jobs() == 0))) - { - debug_printf ("We entered auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout); - autoshutdown_exec_id = - g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, - NULL); - } -} - - -static int -read_browseallow_value (const char *value, allow_sense_t sense) -{ - char *p; - struct in_addr addr; - allow_t *allow; - - if (value && !strcasecmp (value, "all")) - { - if (sense == ALLOW_ALLOW) - { - browseallow_all = TRUE; - return (0); - } - else if (sense == ALLOW_DENY) - { - browsedeny_all = TRUE; - return (0); - } - else - return (1); - } - - allow = calloc (1, sizeof (allow_t)); - allow->sense = sense; - if (value == NULL) - goto fail; - p = strchr (value, '/'); - if (p) - { - char *s = strdup (value); - s[p - value] = '\0'; - - if (!inet_aton (s, &addr)) - { - free (s); - goto fail; - } - - free (s); - allow->type = ALLOW_NET; - allow->addr.ipv4.sin_addr.s_addr = addr.s_addr; - - p++; - if (strchr (p, '.')) - { - if (inet_aton (p, &addr)) - allow->mask.ipv4.sin_addr.s_addr = addr.s_addr; - else - goto fail; - } - else - { - char *endptr; - unsigned long bits = strtoul (p, &endptr, 10); - if (p == endptr) - goto fail; - - if (bits > 32) - goto fail; - - allow->mask.ipv4.sin_addr.s_addr = htonl (((0xffffffff << (32 - bits)) & - 0xffffffff)); - } - } - else if (inet_aton (value, &addr)) - { - allow->type = ALLOW_IP; - allow->addr.ipv4.sin_addr.s_addr = addr.s_addr; - } - else - goto fail; - - cupsArrayAdd (browseallow, allow); - return (0); - -fail: - allow->type = ALLOW_INVALID; - cupsArrayAdd (browseallow, allow); - return (1); -} - - -static void -read_configuration (const char *filename) -{ - cups_file_t *fp; - int i, linenum = 0; - char line[HTTP_MAX_BUFFER]; - char *value = NULL, *ptr, *ptr2, *start; - const char *delim = " \t,"; - int browse_allow_line_found = 0; - int browse_deny_line_found = 0; - int browse_order_line_found = 0; - int browse_line_found = 0; - browse_filter_t *filter = NULL; - int browse_filter_options, exact_match, err; - char errbuf[1024]; - cluster_t *cluster = NULL; - - if (!filename) - filename = CUPS_SERVERROOT "/cups-browsed.conf"; - - if ((fp = cupsFileOpen(filename, "r")) == NULL) - { - debug_printf("unable to open configuration file; " - "using defaults\n"); - return; - } - - i = 0; - linenum = -1; - // First, we read the option settings supplied on the command line via - // "-o ..." in the order given on the command line, then we read the lines - // of the configuration file. This means that if there are contradicting - // settings on the command line and in the configuration file, the setting - // in the configuration file is used. - while ((i < cupsArrayCount(command_line_config) && - (value = cupsArrayIndex(command_line_config, i++)) && - strncpy(line, value, sizeof(line) - 1) && - ((strlen(value) > HTTP_MAX_BUFFER-1) ? - line[HTTP_MAX_BUFFER-1] = '\0': 1)) || - cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) - { - if (linenum < 0) - { - // We are still reading options from the command line ("-o ..."), - // separate key (line) and value (value) - value = line; - while (*value && !isspace(*value) && !(*value == '=')) - value ++; - if (*value) - { - *value = '\0'; - value ++; - while (*value && (isspace(*value) || (*value == '='))) - value ++; - } - } - - debug_printf("Reading config%s: %s %s\n", - (linenum < 0 ? " (from command line)" : ""), line, value); - if (!strcasecmp(line, "DebugLogging") && value) - { - char *p, *saveptr; - p = strtok_r (value, delim, &saveptr); - while (p) - { - if (!strcasecmp(p, "file")) - { - if (debug_logfile == 0) - { - debug_logfile = 1; - start_debug_logging(); - } - } - else if (!strcasecmp(p, "stderr")) - debug_stderr = 1; - else if (strcasecmp(p, "none")) - debug_printf("Unknown debug logging mode '%s'\n", p); - - p = strtok_r (NULL, delim, &saveptr); - } - } - else if (!strcasecmp(line, "CacheDir") && value) - { - if (value[0] != '\0') - strncpy(cachedir, value, sizeof(cachedir) - 1); - } - else if (!strcasecmp(line, "LogDir") && value) - { - if (value[0] != '\0') - strncpy(logdir, value, sizeof(logdir) - 1); - } - else if ((!strcasecmp(line, "BrowseProtocols") || - !strcasecmp(line, "BrowseLocalProtocols") || - !strcasecmp(line, "BrowseRemoteProtocols")) && value) - { - int protocols = 0; - char *p, *saveptr; - p = strtok_r (value, delim, &saveptr); - while (p) - { - if (!strcasecmp(p, "dnssd")) - protocols |= BROWSE_DNSSD; - else if (!strcasecmp(p, "cups")) - protocols |= BROWSE_CUPS; - else if (!strcasecmp(p, "ldap")) - protocols |= BROWSE_LDAP; - else if (strcasecmp(p, "none")) - debug_printf("Unknown protocol '%s'\n", p); - - p = strtok_r (NULL, delim, &saveptr); - } - - if (!strcasecmp(line, "BrowseLocalProtocols")) - BrowseLocalProtocols = protocols; - else if (!strcasecmp(line, "BrowseRemoteProtocols")) - BrowseRemoteProtocols = protocols; - else - BrowseLocalProtocols = BrowseRemoteProtocols = protocols; - } - else if (!strcasecmp(line, "BrowsePoll") && value) - { - browsepoll_t **old = BrowsePoll; - BrowsePoll = realloc (BrowsePoll, - (NumBrowsePoll + 1) * - sizeof (browsepoll_t *)); - if (!BrowsePoll) - { - debug_printf("unable to realloc: ignoring BrowsePoll line\n"); - BrowsePoll = old; - } - else - { - char *colon, *slash; - browsepoll_t *b = g_malloc0 (sizeof (browsepoll_t)); - debug_printf("Adding BrowsePoll server: %s\n", value); - b->server = strdup (value); - b->port = BrowsePort; - b->can_subscribe = TRUE; // first assume subscriptions work - b->subscription_id = -1; - slash = strchr (b->server, '/'); - if (slash) - { - *slash++ = '\0'; - if (!strcasecmp (slash, "version=1.0")) - { - b->major = 1; - b->minor = 0; - } - else if (!strcasecmp (slash, "version=1.1")) - { - b->major = 1; - b->minor = 1; - } - else if (!strcasecmp (slash, "version=2.0")) - { - b->major = 2; - b->minor = 0; - } - else if (!strcasecmp (slash, "version=2.1")) - { - b->major = 2; - b->minor = 1; - } - else if (!strcasecmp (slash, "version=2.2")) - { - b->major = 2; - b->minor = 2; - } - else - debug_printf ("ignoring unknown server option: %s\n", slash); - } - else - b->major = 0; - - colon = strchr(b->server, ':'); - if (colon) - { - char *endptr; - unsigned long n; - *colon++ = '\0'; - n = strtoul(colon, &endptr, 10); - if (endptr != colon && n < INT_MAX) - b->port = (int)n; - } - BrowsePoll[NumBrowsePoll ++] = b; - } - } - else if (!strcasecmp(line, "BrowseAllow")) - { - if (read_browseallow_value (value, ALLOW_ALLOW)) - debug_printf ("BrowseAllow value \"%s\" not understood\n", - value); - else - { - browse_allow_line_found = 1; - browse_line_found = 1; - } - } - else if (!strcasecmp(line, "BrowseDeny")) - { - if (read_browseallow_value (value, ALLOW_DENY)) - debug_printf ("BrowseDeny value \"%s\" not understood\n", - value); - else - { - browse_deny_line_found = 1; - browse_line_found = 1; - } - } - else if (!strcasecmp(line, "BrowseOrder") && value) - { - if (!strncasecmp(value, "Allow", 5) && - strcasestr(value, "Deny")) // Allow,Deny - { - browse_order = ORDER_ALLOW_DENY; - browse_order_line_found = 1; - browse_line_found = 1; - } - else if (!strncasecmp(value, "Deny", 4) && - strcasestr(value, "Allow")) // Deny,Allow - { - browse_order = ORDER_DENY_ALLOW; - browse_order_line_found = 1; - browse_line_found = 1; - } - else - debug_printf ("BrowseOrder value \"%s\" not understood\n", - value); - } - else if (!strcasecmp(line, "BrowseFilter") && value) - { - ptr = value; - // Skip white space - while (*ptr && isspace(*ptr)) - ptr ++; - // Premature line end - if (!*ptr) - goto browse_filter_fail; - filter = calloc (1, sizeof (browse_filter_t)); - if (!filter) - goto browse_filter_fail; - browse_filter_options = 1; - filter->sense = FILTER_MATCH; - exact_match = 0; - while (browse_filter_options) - { - if (!strncasecmp(ptr, "NOT", 3) && *(ptr + 3) && - isspace(*(ptr + 3))) - { - // Accept remote printers where regexp does NOT match or where - // the boolean field is false - filter->sense = FILTER_NOT_MATCH; - ptr += 4; - // Skip white space until next word - while (*ptr && isspace(*ptr)) - ptr ++; - // Premature line end without field name - if (!*ptr) - goto browse_filter_fail; - } - else if (!strncasecmp(ptr, "EXACT", 5) && *(ptr + 5) && - isspace(*(ptr + 5))) - { - // Consider the rest of the line after the field name a string which - // has to match the field exactly - exact_match = 1; - ptr += 6; - // Skip white space until next word - while (*ptr && isspace(*ptr)) - ptr ++; - // Premature line end without field name - if (!*ptr) - goto browse_filter_fail; - } - else - // No more options, consider next word the name of the field which - // should match the regexp - browse_filter_options = 0; - } - start = ptr; - while (*ptr && !isspace(*ptr)) - ptr ++; - if (*ptr) - { - // Mark end of the field name - *ptr = '\0'; - // Skip white space until regexp or line end - ptr ++; - while (*ptr && isspace(*ptr)) - ptr ++; - } - filter->field = strdup(start); - if (!*ptr) - { - // Only field name and no regexp is given, so this rule is - // about matching a boolean value - filter->regexp = NULL; - filter->cregexp = NULL; - } - else - { - // The rest of the line is the regexp, store and compile it - filter->regexp = strdup(ptr); - if (!exact_match) - { - // Compile the regexp only if the line does not require an exact - // match (using the EXACT option - filter->cregexp = calloc(1, sizeof (regex_t)); - if ((err = regcomp(filter->cregexp, filter->regexp, - REG_EXTENDED | REG_ICASE)) != 0) - { - regerror(err, filter->cregexp, errbuf, sizeof(errbuf)); - debug_printf ("BrowseFilter line with error in regular expression \"%s\": %s\n", - filter->regexp, errbuf); - goto browse_filter_fail; - } - } - else - filter->cregexp = NULL; - } - cupsArrayAdd (browsefilter, filter); - continue; - browse_filter_fail: - if (filter) - { - if (filter->field) - free(filter->field); - if (filter->regexp) - free(filter->regexp); - if (filter->cregexp) - regfree(filter->cregexp); - free(filter); - filter = NULL; - } - } - else if ((!strcasecmp(line, "BrowseInterval") || - !strcasecmp(line, "BrowseTimeout")) && value) - { - int t = atoi(value); - if (t >= 0) - { - if (!strcasecmp(line, "BrowseInterval")) - BrowseInterval = t; - else if (!strcasecmp(line, "BrowseTimeout")) - BrowseTimeout = t; - - debug_printf("Set %s to %d sec.\n", - line, t); - } - else - debug_printf("Invalid %s value: %d\n", - line, t); - } - else if (!strcasecmp(line, "DomainSocket") && value) - { - if (value[0] != '\0') - { - if (DomainSocket != NULL) - free(DomainSocket); - DomainSocket = strdup(value); - } - } - else if ((!strcasecmp(line, "HttpLocalTimeout") || - !strcasecmp(line, "HttpRemoteTimeout")) && value) - { - int t = atoi(value); - if (t >= 0) - { - if (!strcasecmp(line, "HttpLocalTimeout")) - HttpLocalTimeout = t; - else if (!strcasecmp(line, "HttpRemoteTimeout")) - HttpRemoteTimeout = t; - - debug_printf("Set %s to %d sec.\n", - line, t); - } - else - debug_printf("Invalid %s value: %d\n", - line, t); - } - else if (!strcasecmp(line, "NotifLeaseDuration") && value) - { - int t = atoi(value); - if (t >= 300) - { - notify_lease_duration = t; - debug_printf("Set %s to %d sec.\n", - line, t); - } - else - debug_printf("Invalid %s value: %d\n", - line, t); - } - else if (!strcasecmp(line, "HttpMaxRetries") && value) - { - int t = atoi(value); - if (t > 0) - { - HttpMaxRetries = t; - - debug_printf("Set %s to %d retries.\n", - line, t); - } - else - debug_printf("Invalid %s value: %d\n", - line, t); - } - else if (!strcasecmp(line, "DNSSDBasedDeviceURIs") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - DNSSDBasedDeviceURIs = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - DNSSDBasedDeviceURIs = 0; - } - else if (!strcasecmp(line, "IPBasedDeviceURIs") && value) - { - if (!strcasecmp(value, "IPv4") || !strcasecmp(value, "IPv4Only")) - IPBasedDeviceURIs = IP_BASED_URIS_IPV4_ONLY; - else if (!strcasecmp(value, "IPv6") || !strcasecmp(value, "IPv6Only")) - IPBasedDeviceURIs = IP_BASED_URIS_IPV6_ONLY; - else if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1") || - !strcasecmp(value, "IP") || !strcasecmp(value, "IPAddress")) - IPBasedDeviceURIs = IP_BASED_URIS_ANY; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0") || - !strcasecmp(value, "Name") || !strcasecmp(value, "HostName")) - IPBasedDeviceURIs = IP_BASED_URIS_NO; - } - else if (!strcasecmp(line, "LocalQueueNamingRemoteCUPS") && value) - { - if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD")) - LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_DNSSD; - else if (strcasestr(value, "Make") && strcasestr(value, "Model")) - LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_MAKE_MODEL; - else if (strcasestr(value, "Remote") || strcasestr(value, "Name")) - LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_REMOTE_NAME; - } - else if (!strcasecmp(line, "LocalQueueNamingIPPPrinter") && value) - { - if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD")) - LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_DNSSD; - else if (strcasestr(value, "Make") && strcasestr(value, "Model")) - LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_MAKE_MODEL; - } - else if (!strcasecmp(line, "OnlyUnsupportedByCUPS") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - OnlyUnsupportedByCUPS = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - OnlyUnsupportedByCUPS = 0; - } - else if (!strcasecmp(line, "UseCUPSGeneratedPPDs") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - UseCUPSGeneratedPPDs = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - UseCUPSGeneratedPPDs = 0; - } - else if (!strcasecmp(line, "CreateRemoteRawPrinterQueues") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - CreateRemoteRawPrinterQueues = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - CreateRemoteRawPrinterQueues = 0; - } - else if (!strcasecmp(line, "CreateRemoteCUPSPrinterQueues") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - CreateRemoteCUPSPrinterQueues = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - CreateRemoteCUPSPrinterQueues = 0; - } - else if (!strcasecmp(line, "CreateIPPPrinterQueues") && value) - { - if (!strcasecmp(value, "all") || - !strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - CreateIPPPrinterQueues = IPP_PRINTERS_ALL; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - CreateIPPPrinterQueues = IPP_PRINTERS_NO; - else if (strcasestr(value, "local") || strcasestr(value, "usb")) - CreateIPPPrinterQueues = IPP_PRINTERS_LOCAL_ONLY; - else if (strcasestr(value, "driver") && strcasestr(value, "less")) - CreateIPPPrinterQueues = IPP_PRINTERS_DRIVERLESS; - else if (strcasestr(value, "every") || strcasestr(value, "pwg")) - CreateIPPPrinterQueues = IPP_PRINTERS_PWGRASTER; - else if (strcasestr(value, "apple") || strcasestr(value, "air")) - CreateIPPPrinterQueues = IPP_PRINTERS_APPLERASTER; - else if (strcasestr(value, "pclm") || strcasestr(value, "pcl-m")) - CreateIPPPrinterQueues = IPP_PRINTERS_PCLM; - else if (strcasestr(value, "pdf")) - CreateIPPPrinterQueues = IPP_PRINTERS_PDF; - } - else if (!strcasecmp(line, "NewIPPPrinterQueuesShared") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - NewIPPPrinterQueuesShared = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - NewIPPPrinterQueuesShared = 0; - } - else if(!strcasecmp(line, "DebugLogFileSize") && value) - { - int val = atoi(value); - if (val <= 0) - DebugLogFileSize = 0; - else - DebugLogFileSize = val; - } - else if (!strcasecmp(line, "AllowResharingRemoteCUPSPrinters") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - AllowResharingRemoteCUPSPrinters = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - AllowResharingRemoteCUPSPrinters = 0; - } - else if (!strcasecmp(line, "NewBrowsePollQueuesShared") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - NewBrowsePollQueuesShared = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - NewBrowsePollQueuesShared = 0; - } - else if (!strcasecmp(line, "KeepGeneratedQueuesOnShutdown") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - KeepGeneratedQueuesOnShutdown = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - KeepGeneratedQueuesOnShutdown = 0; - } - else if (!strcasecmp(line, "AutoClustering") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - AutoClustering = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - AutoClustering = 0; - } - else if (!strcasecmp(line, "FrequentNetifUpdate") && value) - { - if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || - !strcasecmp(value, "on") || !strcasecmp(value, "1")) - FrequentNetifUpdate = 1; - else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") || - !strcasecmp(value, "off") || !strcasecmp(value, "0")) - FrequentNetifUpdate = 0; - } - else if (!strcasecmp(line, "Cluster") && value) - { - ptr = value; - ptr2 = NULL; - // Skip white space - while (*ptr && isspace(*ptr)) - ptr ++; - // Premature line end - if (!*ptr) - goto cluster_fail; - // Find the local queue name for the cluster - start = ptr; - while (*ptr && !isspace(*ptr) && *ptr != ':') - ptr ++; - if (*ptr) - { - // Mark end of the local queue name - *ptr = '\0'; - // Skip colon and white space until next word or line end - ptr ++; - while (*ptr && (isspace(*ptr) || *ptr == ':')) - ptr ++; - } - // Empty queue name - if (strlen(start) <= 0) - goto cluster_fail; - // Clean queue name - ptr2 = remove_bad_chars(start, 0); - // Check whether we have already a cluster with this name - for (cluster = cupsArrayFirst(clusters); - cluster; - cluster = cupsArrayNext(clusters)) - if (!strcasecmp(ptr2, cluster->local_queue_name)) - { - debug_printf("Duplicate cluster with queue name \"%s\".\n", - ptr2); - cluster = NULL; - goto cluster_fail; - } - // Create the new cluster definition - cluster = calloc (1, sizeof (cluster_t)); - if (!cluster) goto - cluster_fail; - cluster->local_queue_name = ptr2; - cluster->members = cupsArrayNew(NULL, NULL); - ptr2 = NULL; - if (!*ptr) - { - // Only local queue name given, so assume this name as the only - // member name (only remote queues with this name match) - cupsArrayAdd(cluster->members, remove_bad_chars(ptr2, 2)); - } - else - { - // The rest of the line lists one or more member queue names - while (*ptr) - { - start = ptr; - while (*ptr && !isspace(*ptr)) - ptr ++; - if (*ptr) - { - // Mark end of the current word - *ptr = '\0'; - // Skip white space until next word or line end - ptr ++; - while (*ptr && isspace(*ptr)) - ptr ++; - } - // Add member queue name to the list - if (strlen(start) > 0) - cupsArrayAdd(cluster->members, remove_bad_chars(start, 2)); - } - } - cupsArrayAdd (clusters, cluster); - if (ptr2 != NULL) - { - free(ptr2); - ptr2 = NULL; - } - continue; - cluster_fail: - if (cluster) - { - if (cluster->local_queue_name) - free(cluster->local_queue_name); - if (cluster->members) - { - while ((ptr = cupsArrayFirst (cluster->members)) != NULL) - { - cupsArrayRemove (cluster->members, ptr); - free (ptr); - } - cupsArrayDelete (cluster->members); - } - free(cluster); - cluster = NULL; - } - if (ptr2 != NULL) - { - free(ptr2); - ptr2 = NULL; - } - } - else if (!strcasecmp(line, "LoadBalancing") && value) - { - if (!strncasecmp(value, "QueueOnClient", 13)) - LoadBalancingType = QUEUE_ON_CLIENT; - else if (!strncasecmp(value, "QueueOnServers", 14)) - LoadBalancingType = QUEUE_ON_SERVERS; - } - else if (!strcasecmp(line, "DefaultOptions") && value) - { - if (DefaultOptions == NULL && strlen(value) > 0) - DefaultOptions = strdup(value); - } - else if (!strcasecmp(line, "AutoShutdown") && value) - { - char *p, *saveptr; - p = strtok_r (value, delim, &saveptr); - while (p) - { - if (!strcasecmp(p, "On") || !strcasecmp(p, "Yes") || - !strcasecmp(p, "True") || !strcasecmp(p, "1")) - { - autoshutdown = 1; - debug_printf("Turning on auto shutdown mode.\n"); - } - else if (!strcasecmp(p, "Off") || !strcasecmp(p, "No") || - !strcasecmp(p, "False") || !strcasecmp(p, "0")) - { - autoshutdown = 0; - debug_printf("Turning off auto shutdown mode (permanent mode).\n"); - } - else if (!strcasecmp(p, "avahi")) - { - autoshutdown_avahi = 1; - debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n"); - } - else if (strcasecmp(p, "none")) - debug_printf("Unknown mode '%s'\n", p); - p = strtok_r (NULL, delim, &saveptr); - } - } - else if (!strcasecmp(line, "AutoShutdownTimeout") && value) - { - int t = atoi(value); - if (t >= 0) - { - autoshutdown_timeout = t; - debug_printf("Set auto shutdown timeout to %d sec.\n", - t); - } - else - debug_printf("Invalid auto shutdown timeout value: %d\n", - t); - } - else if (!strcasecmp(line, "AutoShutdownOn") && value) - { - int success = 0; - if (!strncasecmp(value, "no", 2)) - { - if (strcasestr(value + 2, "queue")) - { - autoshutdown_on = NO_QUEUES; - success = 1; - } - else if (strcasestr(value + 2, "job")) - { - autoshutdown_on = NO_JOBS; - success = 1; - } - } - if (success) - debug_printf("Set auto shutdown inactivity type to no %s.\n", - autoshutdown_on == NO_QUEUES ? "queues" : "jobs"); - else - debug_printf("Invalid auto shutdown inactivity type value: %s\n", - value); - } - else if (!strcasecmp(line, "UpdateCUPSQueuesMaxPerCall") && value) - { - int n = atoi(value); - if (n >= 0) - { - update_cups_queues_max_per_call = n; - if (n > 0) - debug_printf("Set maximum of CUPS queue updates per call of update_cups_queues() to %d.\n", - n); - else - debug_printf("Do not limit the number of CUPS queue updates per call of update_cups_queues().\n"); - } - else - debug_printf("Invalid value for maximum number of CUPS queue updates per call of update_cups_queues(): %d\n", - n); - } - else if (!strcasecmp(line, "PauseBetweenCUPSQueueUpdates") && value) - { - int t = atoi(value); - if (t >= 0) - { - pause_between_cups_queue_updates = t; - debug_printf("Set pause between calls of update_cups_queues() to %d sec.\n", - t); - } - else - debug_printf("Invalid value for pause between calls of update_cups_queues(): %d\n", - t); - } -#ifdef HAVE_LDAP - else if (!strcasecmp(line, "BrowseLDAPBindDN") && value) - { - if (value[0] != '\0') - BrowseLDAPBindDN = strdup(value); - } -# ifdef HAVE_LDAP_SSL - else if (!strcasecmp(line, "BrowseLDAPCACertFile") && value) - { - if (value[0] != '\0') - BrowseLDAPCACertFile = strdup(value); - } -# endif // HAVE_LDAP_SSL - else if (!strcasecmp(line, "BrowseLDAPDN") && value) - { - if (value[0] != '\0') - BrowseLDAPDN = strdup(value); - } - else if (!strcasecmp(line, "BrowseLDAPPassword") && value) - { - if (value[0] != '\0') - BrowseLDAPPassword = strdup(value); - } - else if (!strcasecmp(line, "BrowseLDAPServer") && value) - { - if (value[0] != '\0') - BrowseLDAPServer = strdup(value); - } - else if (!strcasecmp(line, "BrowseLDAPFilter") && value) - { - if (value[0] != '\0') - BrowseLDAPFilter = strdup(value); - } -#endif // HAVE_LDAP - } - - if (browse_line_found == 0) - { - // No "Browse..." lines at all - browseallow_all = 1; - browse_order = ORDER_DENY_ALLOW; - debug_printf("No \"Browse...\" line at all, accept all servers (\"BrowseOrder Deny,Allow\").\n"); - } - else if (browse_order_line_found == 0) - { - // No "BrowseOrder" line - if (browse_allow_line_found == 0) - { - // Only "BrowseDeny" lines - browse_order = ORDER_DENY_ALLOW; - debug_printf("No \"BrowseOrder\" line and only \"BrowseDeny\" lines, accept all except what matches the \"BrowseDeny\" lines (\"BrowseOrder Deny,Allow\").\n"); - } - else if (browse_deny_line_found == 0) - { - // Only "BrowseAllow" lines - browse_order = ORDER_ALLOW_DENY; - debug_printf("No \"BrowseOrder\" line and only \"BrowseAllow\" lines, deny all except what matches the \"BrowseAllow\" lines (\"BrowseOrder Allow,Deny\").\n"); - } - else - { - // Default for "BrowseOrder" - browse_order = ORDER_DENY_ALLOW; - debug_printf("No \"BrowseOrder\" line, use \"BrowseOrder Deny,Allow\" as default.\n"); - } - } - - cupsFileClose(fp); -} - - -static void -defer_update_netifs (void) -{ - if (update_netifs_sourceid) - g_source_remove (update_netifs_sourceid); - - update_netifs_sourceid = g_timeout_add_seconds (10, update_netifs, NULL); -} - -static void -nm_properties_changed (GDBusProxy *proxy, - GVariant *changed_properties, - const gchar *const *invalidated_properties, - gpointer user_data) -{ - GVariantIter *iter; - const gchar *key; - GVariant *value; - debug_printf("nm_properties_changed() in THREAD %ld\n", pthread_self()); - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) - { - if (!strcmp (key, "ActiveConnections")) - { - debug_printf ("NetworkManager ActiveConnections changed\n"); - defer_update_netifs (); - break; - } - } - - g_variant_iter_free (iter); -} - -static void -find_previous_queue (gpointer key, - gpointer value, - gpointer user_data) -{ - const char *name = key; - const local_printer_t *printer = value; - remote_printer_t *p; - debug_printf("find_previous_queue() in THREAD %ld\n", pthread_self()); - if (printer->cups_browsed_controlled) - { - // Queue found, add to our list - p = create_remote_printer_entry (name, "", "", "", "", "", - 0, "", "", "", "", "", 0, NULL, 0, 0, NULL, - -1); - if (p) - { - // Mark as unconfirmed, if no Avahi report of this queue appears - // in a certain time frame, we will remove the queue - p->status = STATUS_UNCONFIRMED; - - if (BrowseRemoteProtocols & BROWSE_CUPS) - p->timeout = time(NULL) + BrowseInterval * 3 / 2; - else - p->timeout = time(NULL) + TIMEOUT_CONFIRM; - - p->slave_of = NULL; - debug_printf("Found CUPS queue %s (URI: %s) from previous session.\n", - p->queue_name, p->uri); - } - else - debug_printf("ERROR: Unable to create print queue entry for printer of previous session: %s (%s).\n", - name, printer->device_uri); - } -} - - -int -main(int argc, char*argv[]) -{ - int ret = 1; - http_t *http; - int i; - char *val; - remote_printer_t *p; - GDBusProxy *proxy = NULL; - GError *error = NULL; - int subscription_id = 0; - - // Initialise the command_line_config array - command_line_config = cupsArrayNew(NULL, NULL); - - // Initialise the browseallow array - browseallow = cupsArrayNew(NULL, NULL); - - // Initialise the browsefilter array - browsefilter = cupsArrayNew(NULL, NULL); - - // Initialise the clusters array - clusters = cupsArrayNew(NULL, NULL); - - // Read command line options - if (argc >= 2) - { - for (i = 1; i < argc; i++) - if (!strcasecmp(argv[i], "--debug") || !strcasecmp(argv[i], "-d") || - !strncasecmp(argv[i], "-v", 2)) - { - // Turn on debug output mode if requested - debug_stderr = 1; - debug_printf("Reading command line option %s, turning on debug mode (Log on standard error).\n", - argv[i]); - } - else if (!strcasecmp(argv[i], "--logfile") || - !strcasecmp(argv[i], "-l")) - { - // Turn on debug log file mode if requested - if (debug_logfile == 0) - { - debug_logfile = 1; - start_debug_logging(); - debug_printf("Reading command line option %s, turning on debug mode (Log into log file %s).\n", - argv[i], debug_log_file); - } - } - else if (!strncasecmp(argv[i], "-c", 2)) - { - // Alternative configuration file - val = argv[i] + 2; - if (strlen(val) == 0) - { - i ++; - if (i < argc && *argv[i] != '-') - val = argv[i]; - else - val = NULL; - } - if (val) - { - alt_config_file = strdup(val); - debug_printf("Reading command line option -c %s, using alternative configuration file.\n", - alt_config_file); - } - else - { - fprintf(stderr, - "Reading command line option -c, no alternative configuration file name supplied.\n\n"); - goto help; - } - } - else if (!strncasecmp(argv[i], "-o", 2)) - { - // Configuration option via command line - val = argv[i] + 2; - if (strlen(val) == 0) - { - i ++; - if (i < argc && *argv[i] != '-') - val = argv[i]; - else - val = NULL; - } - if (val) - { - cupsArrayAdd (command_line_config, strdup(val)); - debug_printf("Reading command line option -o %s, applying extra configuration option.\n", - val); - } - else - { - fprintf(stderr, - "Reading command line option -o, no extra configuration option supplied.\n\n"); - goto help; - } - } - else if (!strncasecmp(argv[i], "--autoshutdown-timeout", 22)) - { - debug_printf("Reading command line: %s\n", argv[i]); - if (argv[i][22] == '=' && argv[i][23]) - val = argv[i] + 23; - else if (!argv[i][22] && i < argc -1) - { - i++; - debug_printf("Reading command line: %s\n", argv[i]); - val = argv[i]; - } - else - { - fprintf(stderr, "Expected auto shutdown timeout setting after \"--autoshutdown-timeout\" option.\n\n"); - goto help; - } - int t = atoi(val); - if (t >= 0) - { - autoshutdown_timeout = t; - debug_printf("Set auto shutdown timeout to %d sec.\n", - t); - } - else - { - fprintf(stderr, "Invalid auto shutdown timeout value: %d\n\n", - t); - goto help; - } - } - else if (!strncasecmp(argv[i], "--autoshutdown-on", 17)) - { - debug_printf("Reading command line: %s\n", argv[i]); - if (argv[i][17] == '=' && argv[i][18]) - val = argv[i] + 18; - else if (!argv[i][17] && i < argc - 1) - { - i++; - debug_printf("Reading command line: %s\n", argv[i]); - val = argv[i]; - } - else - { - fprintf(stderr, "Expected auto shutdown inactivity type (\"no-queues\" or \"no-jobs\") after \"--autoshutdown-on\" option.\n\n"); - goto help; - } - int success = 0; - if (!strncasecmp(val, "no", 2)) - { - if (strcasestr(val + 2, "queue")) - { - autoshutdown_on = NO_QUEUES; - success = 1; - } - else if (strcasestr(val + 2, "job")) - { - autoshutdown_on = NO_JOBS; - success = 1; - } - } - if (success) - debug_printf("Set auto shutdown inactivity type to no %s.\n", - autoshutdown_on == NO_QUEUES ? "queues" : "jobs"); - else - debug_printf("Invalid auto shutdown inactivity type value: %s\n", - val); - } - else if (!strncasecmp(argv[i], "--autoshutdown", 14)) - { - debug_printf("Reading command line: %s\n", argv[i]); - if (argv[i][14] == '=' && argv[i][15]) - val = argv[i] + 15; - else if (!argv[i][14] && i < argc -1) - { - i++; - debug_printf("Reading command line: %s\n", argv[i]); - val = argv[i]; - } - else - { - fprintf(stderr, "Expected auto shutdown setting after \"--autoshutdown\" option.\n\n"); - goto help; - } - if (!strcasecmp(val, "On") || !strcasecmp(val, "Yes") || - !strcasecmp(val, "True") || !strcasecmp(val, "1")) - { - autoshutdown = 1; - debug_printf("Turning on auto shutdown mode.\n"); - } - else if (!strcasecmp(val, "Off") || !strcasecmp(val, "No") || - !strcasecmp(val, "False") || !strcasecmp(val, "0")) - { - autoshutdown = 0; - debug_printf("Turning off auto shutdown mode (permanent mode).\n"); - } - else if (!strcasecmp(val, "avahi")) - { - autoshutdown_avahi = 1; - debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n"); - } - else if (strcasecmp(val, "none")) - { - fprintf(stderr, "Unknown mode '%s'\n\n", val); - goto help; - } - } - else if (!strcasecmp(argv[i], "--version") || - !strcasecmp(argv[i], "--help") || !strcasecmp(argv[i], "-h")) - { - // Help!! - goto help; - } - else - { - // Unknown option - fprintf(stderr, - "Reading command line option %s, unknown command line option.\n\n", - argv[i]); - goto help; - } - } - - debug_printf("cups-browsed of cups-filters version "VERSION" starting.\n"); - - // Read in cups-browsed.conf - read_configuration (alt_config_file); - - // Set the paths of the auxiliary files - if (cachedir[0] == '\0') - strncpy(cachedir, DEFAULT_CACHEDIR, sizeof(cachedir) - 1); - if (logdir[0] == '\0') - strncpy(logdir, DEFAULT_LOGDIR, sizeof(logdir) - 1); - strncpy(local_default_printer_file, cachedir, - sizeof(local_default_printer_file) - 1); - strncpy(local_default_printer_file + strlen(cachedir), - LOCAL_DEFAULT_PRINTER_FILE, - sizeof(local_default_printer_file) - strlen(cachedir) - 1); - strncpy(remote_default_printer_file, cachedir, - sizeof(remote_default_printer_file) - 1); - strncpy(remote_default_printer_file + strlen(cachedir), - REMOTE_DEFAULT_PRINTER_FILE, - sizeof(remote_default_printer_file) - strlen(cachedir) - 1); - strncpy(save_options_file, cachedir, - sizeof(save_options_file) - 1); - strncpy(save_options_file + strlen(cachedir), - SAVE_OPTIONS_FILE, - sizeof(save_options_file) - strlen(cachedir) - 1); - strncpy(debug_log_file, logdir, - sizeof(debug_log_file) - 1); - strncpy(debug_log_file + strlen(logdir), - DEBUG_LOG_FILE, - sizeof(debug_log_file) - strlen(logdir) - 1); - - strncpy(debug_log_file_bckp, logdir, - sizeof(debug_log_file_bckp) - 1); - strncpy(debug_log_file_bckp + strlen(logdir), - DEBUG_LOG_FILE_2, - sizeof(debug_log_file_bckp) - strlen(logdir) - 1); - - if (debug_logfile == 1) - start_debug_logging(); - - debug_printf("main() in THREAD %ld\n", pthread_self()); - - // If a port is selected via the IPP_PORT environment variable, - // set this first - if (getenv("IPP_PORT") != NULL) - { - snprintf(local_server_str, sizeof(local_server_str) - 1, - "localhost:%s", getenv("IPP_PORT")); - local_server_str[sizeof(local_server_str) - 1] = '\0'; - cupsSetServer(local_server_str); - debug_printf("Set port on which CUPS is listening via env variable: IPP_PORT=%s\n", - getenv("IPP_PORT")); - } - - // Point to selected CUPS server or domain socket via the CUPS_SERVER - // environment variable or DomainSocket configuration file option. - // Default to localhost:631 (and not to CUPS default to override - // client.conf files as cups-browsed works only with a local CUPS - // daemon, not with remote ones. - local_server_str[0] = '\0'; - if (getenv("CUPS_SERVER") != NULL) - { - strncpy(local_server_str, getenv("CUPS_SERVER"), - sizeof(local_server_str) - 1); - local_server_str[sizeof(local_server_str) - 1] = '\0'; - cupsSetServer(local_server_str); - debug_printf("Set host/port/domain socket which CUPS is listening via env variable: CUPS_SERVER=%s\n", - getenv("CUPS_SERVER")); - } - else - { - if (DomainSocket != NULL) - { - debug_printf("Set host/port/domain socket on which CUPS is listening via cups-browsed directive DomainSocket: %s\n", - DomainSocket); - struct stat sockinfo; // Domain socket information - if (strcasecmp(DomainSocket, "None") != 0 && - strcasecmp(DomainSocket, "Off") != 0 && - !stat(DomainSocket, &sockinfo) && - (sockinfo.st_mode & S_IROTH) != 0 && - (sockinfo.st_mode & S_IWOTH) != 0) - { - strncpy(local_server_str, DomainSocket, - sizeof(local_server_str) - 1); - local_server_str[sizeof(local_server_str) - 1] = '\0'; - cupsSetServer(local_server_str); - } - else - debug_printf("DomainSocket %s not accessible: %s\n", - DomainSocket, strerror(errno)); - } - } - - if (local_server_str[0]) - setenv("CUPS_SERVER", local_server_str, 1); - - if (BrowseLocalProtocols & BROWSE_DNSSD) - { - debug_printf("Local support for DNSSD not implemented\n"); - BrowseLocalProtocols &= ~BROWSE_DNSSD; - } - - if (BrowseLocalProtocols & BROWSE_LDAP) - { - debug_printf("Local support for LDAP not implemented\n"); - BrowseLocalProtocols &= ~BROWSE_LDAP; - } - -#ifndef HAVE_AVAHI - if (BrowseRemoteProtocols & BROWSE_DNSSD) - { - debug_printf("Remote support for DNSSD not supported\n"); - BrowseRemoteProtocols &= ~BROWSE_DNSSD; - } -#endif // HAVE_AVAHI - -#ifndef HAVE_LDAP - if (BrowseRemoteProtocols & BROWSE_LDAP) - { - debug_printf("Remote support for LDAP not supported\n"); - BrowseRemoteProtocols &= ~BROWSE_LDAP; - } -#endif // HAVE_LDAP - - // Wait for CUPS daemon to start - while ((http = http_connect_local ()) == NULL) - sleep(1); - - // Initialise the array of network interfaces - netifs = cupsArrayNew(NULL, NULL); - local_hostnames = cupsArrayNew(NULL, NULL); - update_netifs (NULL); - - local_printers = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - free_local_printer); - cups_supported_remote_printers = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - free_local_printer); - - // Read out the currently defined CUPS queues and find the ones which we - // have added in an earlier session - update_local_printers (); - if ((val = get_cups_default_printer()) != NULL) - { - default_printer = strdup(val); - free(val); - } - remote_printers = cupsArrayNew(NULL, NULL); - g_hash_table_foreach (local_printers, find_previous_queue, NULL); - - // Redirect SIGINT and SIGTERM so that we do a proper shutdown, removing - // the CUPS queues which we have created - // Use SIGUSR1 and SIGUSR2 to turn off and turn on auto shutdown mode - // resp. -#ifdef HAVE_SIGSET // Use System V signals over POSIX to avoid bugs - sigset(SIGTERM, sigterm_handler); - sigset(SIGINT, sigterm_handler); - sigset(SIGUSR1, sigusr1_handler); - sigset(SIGUSR2, sigusr2_handler); - debug_printf("Using signal handler SIGSET\n"); -#elif defined(HAVE_SIGACTION) - struct sigaction action; // Actions for POSIX signals - memset(&action, 0, sizeof(action)); - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, SIGTERM); - action.sa_handler = sigterm_handler; - sigaction(SIGTERM, &action, NULL); - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, SIGINT); - action.sa_handler = sigterm_handler; - sigaction(SIGINT, &action, NULL); - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, SIGUSR1); - action.sa_handler = sigusr1_handler; - sigaction(SIGUSR1, &action, NULL); - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, SIGUSR2); - action.sa_handler = sigusr2_handler; - sigaction(SIGUSR2, &action, NULL); - debug_printf("Using signal handler SIGACTION\n"); -#else - signal(SIGTERM, sigterm_handler); - signal(SIGINT, sigterm_handler); - signal(SIGUSR1, sigusr1_handler); - signal(SIGUSR2, sigusr2_handler); - debug_printf("Using signal handler SIGNAL\n"); -#endif // HAVE_SIGSET - -#ifdef HAVE_AVAHI - if (autoshutdown_avahi) - autoshutdown = 1; - avahi_init(); -#endif // HAVE_AVAHI - - if (autoshutdown == 1) - { - // If there are no printers or no jobs schedule the shutdown in - // autoshutdown_timeout seconds - if (!autoshutdown_exec_id && - (cupsArrayCount(remote_printers) == 0 || - (autoshutdown_on == NO_JOBS && check_jobs() == 0))) - { - debug_printf ("We set auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout); - autoshutdown_exec_id = - g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, - NULL); - } - } - - if (BrowseLocalProtocols & BROWSE_CUPS || - BrowseRemoteProtocols & BROWSE_CUPS) - { - // Set up our CUPS Browsing socket - browsesocket = socket (AF_INET, SOCK_DGRAM, 0); - if (browsesocket == -1) - { - debug_printf("failed to create CUPS Browsing socket: %s\n", - strerror (errno)); - } - else - { - struct sockaddr_in addr; - 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))) - { - debug_printf("failed to bind CUPS Browsing socket: %s\n", - strerror (errno)); - close (browsesocket); - browsesocket = -1; - } - else - { - int on = 1; - if (setsockopt (browsesocket, SOL_SOCKET, SO_BROADCAST, - &on, sizeof (on))) - { - debug_printf("failed to allow broadcast: %s\n", - strerror (errno)); - BrowseLocalProtocols &= ~BROWSE_CUPS; - } - } - } - - if (browsesocket == -1) - { - BrowseLocalProtocols &= ~BROWSE_CUPS; - BrowseRemoteProtocols &= ~BROWSE_CUPS; - } - } - - if (BrowseLocalProtocols == 0 && - BrowseRemoteProtocols == 0 && - !BrowsePoll) - { - debug_printf("nothing left to do\n"); - ret = 0; - goto fail; - } - - // Override the default password callback so we don't end up - // prompting for it. - cupsSetPasswordCB2 (password_callback, NULL); - - // Watch NetworkManager for network interface changes - proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, // GDBusInterfaceInfo - "org.freedesktop.NetworkManager", - "/org/freedesktop/NetworkManager", - "org.freedesktop.NetworkManager", - NULL, // GCancellable - NULL); // GError - - if (proxy) - g_signal_connect (proxy, - "g-properties-changed", - G_CALLBACK (nm_properties_changed), - NULL); - - // Run the main loop - gmainloop = g_main_loop_new (NULL, FALSE); - recheck_timer (); - - if (BrowseRemoteProtocols & BROWSE_CUPS) - { - GIOChannel *browse_channel = g_io_channel_unix_new (browsesocket); - g_io_channel_set_close_on_unref (browse_channel, FALSE); - g_io_add_watch (browse_channel, G_IO_IN, process_browse_data, NULL); - } - - if (BrowseLocalProtocols & BROWSE_CUPS) - { - debug_printf ("will send browse data every %ds\n", - BrowseInterval); - g_idle_add (send_browse_data, NULL); - } - -#ifdef HAVE_LDAP - if (BrowseRemoteProtocols & BROWSE_LDAP) - { - debug_printf ("will browse poll LDAP every %ds\n", - BrowseInterval); - g_idle_add (browse_ldap_poll, NULL); - } -#endif // HAVE_LDAP - - if (BrowsePoll) - { - size_t index; - for (index = 0; - index < NumBrowsePoll; - index++) - { - debug_printf ("will browse poll %s every %ds\n", - BrowsePoll[index]->server, BrowseInterval); - g_idle_add (browse_poll, BrowsePoll[index]); - } - } - - // Subscribe to CUPS' D-Bus notifications and create a proxy to receive - // the notifications - subscription_id = create_subscription (); - g_timeout_add_seconds (notify_lease_duration / 2, - renew_subscription_timeout, - &subscription_id); - cups_notifier = cups_notifier_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - 0, - NULL, - CUPS_DBUS_PATH, - NULL, - &error); - if (error) - { - fprintf (stderr, "Error creating cups notify handler: %s", error->message); - g_error_free (error); - cups_notifier = NULL; - } - if (cups_notifier != NULL) - { - g_signal_connect (cups_notifier, "printer-state-changed", - G_CALLBACK (on_printer_state_changed), NULL); - g_signal_connect (cups_notifier, "job-state", - G_CALLBACK (on_job_state), NULL); - g_signal_connect (cups_notifier, "printer-deleted", - G_CALLBACK (on_printer_deleted), NULL); - g_signal_connect (cups_notifier, "printer-modified", - G_CALLBACK (on_printer_modified), NULL); - } - - // If auto shutdown is active and we do not find any printers initially, - // schedule the shutdown in autoshutdown_timeout seconds - if (autoshutdown && !autoshutdown_exec_id && - cupsArrayCount(remote_printers) == 0) - { - debug_printf ("No printers found to make available, shutting down in %d sec...\n", - autoshutdown_timeout); - autoshutdown_exec_id = - g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, NULL); - } - - g_main_loop_run (gmainloop); - - debug_printf("main loop exited\n"); - g_main_loop_unref (gmainloop); - gmainloop = NULL; - ret = 0; - -fail: - - // Clean up things - - in_shutdown = 1; - - if (proxy) - g_object_unref (proxy); - - // Remove all queues which we have set up - if (KeepGeneratedQueuesOnShutdown == 0) - for (p = (remote_printer_t *)cupsArrayFirst(remote_printers); - p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) - { - if (p->status != STATUS_TO_BE_RELEASED) - p->status = STATUS_DISAPPEARED; - p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY; - } - update_cups_queues(NULL); - - cancel_subscription (subscription_id); - if (cups_notifier) - g_object_unref (cups_notifier); - - if (BrowsePoll) - { - size_t index; - for (index = 0; - index < NumBrowsePoll; - index++) - { - if (BrowsePoll[index]->can_subscribe && - BrowsePoll[index]->subscription_id != -1) - browse_poll_cancel_subscription (BrowsePoll[index]); - - free (BrowsePoll[index]->server); - g_list_free_full (BrowsePoll[index]->printers, - browsepoll_printer_free); - free (BrowsePoll[index]); - } - - free (BrowsePoll); - } - - if (local_printers_context) - { - browse_poll_cancel_subscription (local_printers_context); - free(local_printers_context->server); - g_list_free_full (local_printers_context->printers, - browsepoll_printer_free); - free (local_printers_context); - } - - http_close_local (); - -#ifdef HAVE_AVAHI - avahi_shutdown(); -#endif // HAVE_AVAHI - -#ifdef HAVE_LDAP - if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) && - BrowseLDAPHandle) - { - ldap_disconnect(BrowseLDAPHandle); - BrowseLDAPHandle = NULL; - } -#endif // HAVE_LDAP - - if (browsesocket != -1) - close (browsesocket); - - g_hash_table_destroy (local_printers); - g_hash_table_destroy (cups_supported_remote_printers); - - if (BrowseLocalProtocols & BROWSE_CUPS) - g_list_free_full (browse_data, browse_data_free); - - // Close log file if we have one - if (debug_logfile == 1) - stop_debug_logging(); - - if (deleted_master != NULL) - free(deleted_master); - if (DefaultOptions != NULL) - free(DefaultOptions); - if (DomainSocket != NULL) - free(DomainSocket); - - return (ret); - - help: - - fprintf(stderr, - "cups-browsed of cups-filters version "VERSION"\n\n" - "Usage: cups-browsed [options]\n" - "Options:\n" - " -c cups-browsed.conf Set alternative cups-browsed.conf file to use.\n" - " -d\n" - " -v\n" - " --debug Run in debug mode (logging to stderr).\n" - " -l\n" - " --logfile Run in debug mode (logging into file).\n" - " -h\n" - " --help\n" - " --version Show this usage message.\n" - " -o Option=Value Supply configuration option via command line,\n" - " options are the same as in cups-browsed.conf.\n" - " --autoshutdown= Automatically shut down cups-browsed when inactive:\n" - " can be set to Off, On, or avahi, where Off\n" - " means that cups-browsed stays running permanently\n" - " (default), On means that it shuts down after 30\n" - " seconds (or any given timeout) of inactivity, and\n" - " avahi means that cups-browsed shuts down when\n" - " avahi-daemon shuts down.\n" - " --autoshutdown-timout=