diff options
author | Didier Raboud <odyx@debian.org> | 2013-12-05 17:27:40 +0100 |
---|---|---|
committer | Didier Raboud <odyx@debian.org> | 2013-12-05 17:27:40 +0100 |
commit | 372533b24d77236e15db57aeeb858a7f82435b41 (patch) | |
tree | 9655eb4b714f98a13240fbc9aa4c7fcd76eacf2b | |
parent | e16e72cafa4ed8c150fdd5a597b5175fb5a78c65 (diff) |
Imported Upstream version 1.0.42
42 files changed, 9161 insertions, 205 deletions
@@ -1,4 +1,4 @@ -INSTALL - OpenPrinting CUPS Filters v1.0.41 - 2013-10-30 +INSTALL - OpenPrinting CUPS Filters v1.0.42 - 2013-11-29 -------------------------------------------------------- This file describes how to compile and install OpenPrinting CUPS diff --git a/Makefile.am b/Makefile.am index f71f2195a..05739ab34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,6 +80,7 @@ EXTRA_DIST += $(pkgcharset_DATA) # ==================== pkgfiltersincludedir = $(includedir)/cupsfilters pkgfiltersinclude_DATA = \ + cupsfilters/colord.h \ cupsfilters/driver.h \ cupsfilters/image.h \ cupsfilters/raster.h @@ -111,6 +112,7 @@ libcupsfilters_la_SOURCES = \ cupsfilters/attr.c \ cupsfilters/check.c \ cupsfilters/cmyk.c \ + cupsfilters/colord.c \ cupsfilters/dither.c \ cupsfilters/image.c \ cupsfilters/image-bmp.c \ @@ -145,10 +147,13 @@ libcupsfilters_la_CFLAGS = \ $(LIBJPEG_CFLAGS) \ $(LIBPNG_CFLAGS) \ $(TIFF_CFLAGS) - libcupsfilters_la_LDFLAGS = \ -no-undefined \ -version-info 1 +if BUILD_DBUS +libcupsfilters_la_CFLAGS += $(DBUS_CFLAGS) -DHAVE_DBUS +libcupsfilters_la_LIBADD += $(DBUS_LIBS) +endif testcmyk_SOURCES = \ cupsfilters/testcmyk.c \ @@ -403,6 +408,7 @@ pkgfilter_PROGRAMS += \ bannertopdf \ commandtoescpx \ commandtopclx \ + foomatic-rip \ gstoraster \ pdftoijs \ pdftoippprinter \ @@ -467,10 +473,34 @@ commandtopclx_CFLAGS = \ -I$(srcdir)/cupsfilters/ commandtopclx_LDADD = $(CUPS_LIBS) +foomatic_rip_SOURCES = \ + filter/foomatic-rip/foomaticrip.c \ + filter/foomatic-rip/foomaticrip.h \ + filter/foomatic-rip/options.c \ + filter/foomatic-rip/options.h \ + filter/foomatic-rip/pdf.c \ + filter/foomatic-rip/pdf.h \ + filter/foomatic-rip/postscript.c \ + filter/foomatic-rip/postscript.h \ + filter/foomatic-rip/process.c \ + filter/foomatic-rip/process.h \ + filter/foomatic-rip/renderer.c \ + filter/foomatic-rip/renderer.h \ + filter/foomatic-rip/spooler.c \ + filter/foomatic-rip/spooler.h \ + filter/foomatic-rip/util.c \ + filter/foomatic-rip/util.h \ + cupsfilters/colord.h +foomatic_rip_CFLAGS = \ + -DCONFIG_PATH='"$(sysconfdir)/foomatic"' \ + -I$(srcdir)/cupsfilters/ +foomatic_rip_LDADD = \ + -lm \ + libcupsfilters.la + gstoraster_SOURCES = \ filter/gstoraster.c \ - filter/colord.c \ - filter/colord.h \ + cupsfilters/colord.h \ cupsfilters/raster.h gstoraster_CFLAGS = \ $(CUPS_CFLAGS) \ @@ -666,7 +696,8 @@ initrcdir = $(INITDDIR) initrc_SCRIPTS = utils/cups-browsed man_MANS = \ utils/cups-browsed.8 \ - utils/cups-browsed.conf.5 + utils/cups-browsed.conf.5 \ + filter/foomatic-rip/foomatic-rip.1 EXTRA_DIST += utils/cups-browsed.in \ $(man_MANS) diff --git a/Makefile.in b/Makefile.in index 1993998c3..ea00df960 100644 --- a/Makefile.in +++ b/Makefile.in @@ -88,14 +88,17 @@ check_PROGRAMS = test1284$(EXEEXT) testcmyk$(EXEEXT) \ test_pdf1$(EXEEXT) test_pdf2$(EXEEXT) TESTS = testdither$(EXEEXT) test_analyze$(EXEEXT) test_pdf$(EXEEXT) \ test_ps$(EXEEXT) test_pdf1$(EXEEXT) test_pdf2$(EXEEXT) +@BUILD_DBUS_TRUE@am__append_1 = $(DBUS_CFLAGS) -DHAVE_DBUS +@BUILD_DBUS_TRUE@am__append_2 = $(DBUS_LIBS) bin_PROGRAMS = ttfread$(EXEEXT) pkgfilter_PROGRAMS = pdftoopvp$(EXEEXT) pdftopdf$(EXEEXT) \ bannertopdf$(EXEEXT) commandtoescpx$(EXEEXT) \ - commandtopclx$(EXEEXT) gstoraster$(EXEEXT) pdftoijs$(EXEEXT) \ - pdftoippprinter$(EXEEXT) pdftops$(EXEEXT) pdftoraster$(EXEEXT) \ - rastertoescpx$(EXEEXT) rastertopclx$(EXEEXT) \ - texttopdf$(EXEEXT) urftopdf$(EXEEXT) $(am__EXEEXT_1) -@ENABLE_IMAGEFILTERS_TRUE@am__append_1 = \ + commandtopclx$(EXEEXT) foomatic-rip$(EXEEXT) \ + gstoraster$(EXEEXT) pdftoijs$(EXEEXT) pdftoippprinter$(EXEEXT) \ + pdftops$(EXEEXT) pdftoraster$(EXEEXT) rastertoescpx$(EXEEXT) \ + rastertopclx$(EXEEXT) texttopdf$(EXEEXT) urftopdf$(EXEEXT) \ + $(am__EXEEXT_1) +@ENABLE_IMAGEFILTERS_TRUE@am__append_3 = \ @ENABLE_IMAGEFILTERS_TRUE@ imagetopdf \ @ENABLE_IMAGEFILTERS_TRUE@ imagetoraster @@ -107,9 +110,10 @@ DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \ $(srcdir)/config.h.in $(srcdir)/libcupsfilters.pc.in \ $(srcdir)/libfontembed.pc.in $(top_srcdir)/filter/gstopxl.in \ $(top_srcdir)/utils/cups-browsed.in \ - $(top_srcdir)/utils/cups-browsed.conf.in depcomp test-driver \ - COPYING compile config.guess config.sub install-sh missing \ - ltmain.sh + $(top_srcdir)/utils/cups-browsed.conf.in \ + $(top_srcdir)/filter/foomatic-rip/foomatic-rip.1.in depcomp \ + test-driver COPYING compile config.guess config.sub install-sh \ + missing ltmain.sh ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ @@ -124,7 +128,8 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = libcupsfilters.pc libfontembed.pc filter/gstopxl \ - utils/cups-browsed utils/cups-browsed.conf + utils/cups-browsed utils/cups-browsed.conf \ + filter/foomatic-rip/foomatic-rip.1 CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ @@ -157,10 +162,10 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(phpextensiondir)" \ "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgbackenddir)" \ "$(DESTDIR)$(pkgfilterdir)" "$(DESTDIR)$(sbindir)" \ "$(DESTDIR)$(initrcdir)" "$(DESTDIR)$(pkgfilterdir)" \ - "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" \ - "$(DESTDIR)$(docdir)" "$(DESTDIR)$(pkgbannerdir)" \ - "$(DESTDIR)$(pkgcharsetdir)" "$(DESTDIR)$(pkgconfdir)" \ - "$(DESTDIR)$(pkgcupsdatadir)" \ + "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \ + "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docdir)" \ + "$(DESTDIR)$(pkgbannerdir)" "$(DESTDIR)$(pkgcharsetdir)" \ + "$(DESTDIR)$(pkgconfdir)" "$(DESTDIR)$(pkgcupsdatadir)" \ "$(DESTDIR)$(pkgcupsserverrootdir)" \ "$(DESTDIR)$(pkgdriverdir)" \ "$(DESTDIR)$(pkgfiltersincludedir)" \ @@ -170,13 +175,15 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(phpextensiondir)" \ "$(DESTDIR)$(ppddir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(phpextension_LTLIBRARIES) am__DEPENDENCIES_1 = +@BUILD_DBUS_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) libcupsfilters_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) am__objects_1 = am_libcupsfilters_la_OBJECTS = libcupsfilters_la-attr.lo \ libcupsfilters_la-check.lo libcupsfilters_la-cmyk.lo \ - libcupsfilters_la-dither.lo libcupsfilters_la-image.lo \ - libcupsfilters_la-image-bmp.lo \ + libcupsfilters_la-colord.lo libcupsfilters_la-dither.lo \ + libcupsfilters_la-image.lo libcupsfilters_la-image-bmp.lo \ libcupsfilters_la-image-colorspace.lo \ libcupsfilters_la-image-gif.lo libcupsfilters_la-image-jpeg.lo \ libcupsfilters_la-image-photocd.lo \ @@ -246,8 +253,17 @@ cups_browsed_DEPENDENCIES = $(am__DEPENDENCIES_1) \ cups_browsed_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(cups_browsed_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -am_gstoraster_OBJECTS = gstoraster-gstoraster.$(OBJEXT) \ - gstoraster-colord.$(OBJEXT) +am_foomatic_rip_OBJECTS = foomatic_rip-foomaticrip.$(OBJEXT) \ + foomatic_rip-options.$(OBJEXT) foomatic_rip-pdf.$(OBJEXT) \ + foomatic_rip-postscript.$(OBJEXT) \ + foomatic_rip-process.$(OBJEXT) foomatic_rip-renderer.$(OBJEXT) \ + foomatic_rip-spooler.$(OBJEXT) foomatic_rip-util.$(OBJEXT) +foomatic_rip_OBJECTS = $(am_foomatic_rip_OBJECTS) +foomatic_rip_DEPENDENCIES = libcupsfilters.la +foomatic_rip_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(foomatic_rip_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am_gstoraster_OBJECTS = gstoraster-gstoraster.$(OBJEXT) gstoraster_OBJECTS = $(am_gstoraster_OBJECTS) gstoraster_DEPENDENCIES = $(am__DEPENDENCIES_1) libcupsfilters.la gstoraster_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -474,11 +490,11 @@ SOURCES = $(libcupsfilters_la_SOURCES) $(libfontembed_la_SOURCES) \ $(libphpcups_la_SOURCES) $(bannertopdf_SOURCES) \ $(EXTRA_bannertopdf_SOURCES) $(commandtoescpx_SOURCES) \ $(commandtopclx_SOURCES) $(cups_browsed_SOURCES) \ - $(gstoraster_SOURCES) $(imagetopdf_SOURCES) \ - $(imagetoraster_SOURCES) $(parallel_SOURCES) \ - $(pdftoijs_SOURCES) $(pdftoippprinter_SOURCES) \ - $(EXTRA_pdftoippprinter_SOURCES) $(pdftoopvp_SOURCES) \ - $(pdftopdf_SOURCES) $(pdftops_SOURCES) \ + $(foomatic_rip_SOURCES) $(gstoraster_SOURCES) \ + $(imagetopdf_SOURCES) $(imagetoraster_SOURCES) \ + $(parallel_SOURCES) $(pdftoijs_SOURCES) \ + $(pdftoippprinter_SOURCES) $(EXTRA_pdftoippprinter_SOURCES) \ + $(pdftoopvp_SOURCES) $(pdftopdf_SOURCES) $(pdftops_SOURCES) \ $(EXTRA_pdftops_SOURCES) $(pdftoraster_SOURCES) \ $(rastertoescpx_SOURCES) $(rastertopclx_SOURCES) \ $(serial_SOURCES) $(test1284_SOURCES) $(test_analyze_SOURCES) \ @@ -490,11 +506,11 @@ DIST_SOURCES = $(libcupsfilters_la_SOURCES) $(libfontembed_la_SOURCES) \ $(am__libphpcups_la_SOURCES_DIST) $(bannertopdf_SOURCES) \ $(EXTRA_bannertopdf_SOURCES) $(commandtoescpx_SOURCES) \ $(commandtopclx_SOURCES) $(cups_browsed_SOURCES) \ - $(gstoraster_SOURCES) $(imagetopdf_SOURCES) \ - $(imagetoraster_SOURCES) $(parallel_SOURCES) \ - $(pdftoijs_SOURCES) $(pdftoippprinter_SOURCES) \ - $(EXTRA_pdftoippprinter_SOURCES) $(pdftoopvp_SOURCES) \ - $(pdftopdf_SOURCES) $(pdftops_SOURCES) \ + $(foomatic_rip_SOURCES) $(gstoraster_SOURCES) \ + $(imagetopdf_SOURCES) $(imagetoraster_SOURCES) \ + $(parallel_SOURCES) $(pdftoijs_SOURCES) \ + $(pdftoippprinter_SOURCES) $(EXTRA_pdftoippprinter_SOURCES) \ + $(pdftoopvp_SOURCES) $(pdftopdf_SOURCES) $(pdftops_SOURCES) \ $(EXTRA_pdftops_SOURCES) $(pdftoraster_SOURCES) \ $(rastertoescpx_SOURCES) $(rastertopclx_SOURCES) \ $(serial_SOURCES) $(test1284_SOURCES) $(test_analyze_SOURCES) \ @@ -507,6 +523,7 @@ am__can_run_installinfo = \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac +man1dir = $(mandir)/man1 man5dir = $(mandir)/man5 man8dir = $(mandir)/man8 NROFF = nroff @@ -768,6 +785,8 @@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ @@ -988,6 +1007,7 @@ pkgcharset_DATA = \ # ==================== pkgfiltersincludedir = $(includedir)/cupsfilters pkgfiltersinclude_DATA = \ + cupsfilters/colord.h \ cupsfilters/driver.h \ cupsfilters/image.h \ cupsfilters/raster.h @@ -1009,6 +1029,7 @@ libcupsfilters_la_SOURCES = \ cupsfilters/attr.c \ cupsfilters/check.c \ cupsfilters/cmyk.c \ + cupsfilters/colord.c \ cupsfilters/dither.c \ cupsfilters/image.c \ cupsfilters/image-bmp.c \ @@ -1033,19 +1054,10 @@ libcupsfilters_la_SOURCES = \ cupsfilters/srgb.c \ $(pkgfiltersinclude_DATA) -libcupsfilters_la_LIBADD = \ - $(CUPS_LIBS) \ - $(LIBJPEG_LIBS) \ - $(LIBPNG_LIBS) \ - $(TIFF_LIBS) \ - -lm - -libcupsfilters_la_CFLAGS = \ - $(CUPS_CFLAGS) \ - $(LIBJPEG_CFLAGS) \ - $(LIBPNG_CFLAGS) \ - $(TIFF_CFLAGS) - +libcupsfilters_la_LIBADD = $(CUPS_LIBS) $(LIBJPEG_LIBS) $(LIBPNG_LIBS) \ + $(TIFF_LIBS) -lm $(am__append_2) +libcupsfilters_la_CFLAGS = $(CUPS_CFLAGS) $(LIBJPEG_CFLAGS) \ + $(LIBPNG_CFLAGS) $(TIFF_CFLAGS) $(am__append_1) libcupsfilters_la_LDFLAGS = \ -no-undefined \ -version-info 1 @@ -1319,10 +1331,36 @@ commandtopclx_CFLAGS = \ -I$(srcdir)/cupsfilters/ commandtopclx_LDADD = $(CUPS_LIBS) +foomatic_rip_SOURCES = \ + filter/foomatic-rip/foomaticrip.c \ + filter/foomatic-rip/foomaticrip.h \ + filter/foomatic-rip/options.c \ + filter/foomatic-rip/options.h \ + filter/foomatic-rip/pdf.c \ + filter/foomatic-rip/pdf.h \ + filter/foomatic-rip/postscript.c \ + filter/foomatic-rip/postscript.h \ + filter/foomatic-rip/process.c \ + filter/foomatic-rip/process.h \ + filter/foomatic-rip/renderer.c \ + filter/foomatic-rip/renderer.h \ + filter/foomatic-rip/spooler.c \ + filter/foomatic-rip/spooler.h \ + filter/foomatic-rip/util.c \ + filter/foomatic-rip/util.h \ + cupsfilters/colord.h + +foomatic_rip_CFLAGS = \ + -DCONFIG_PATH='"$(sysconfdir)/foomatic"' \ + -I$(srcdir)/cupsfilters/ + +foomatic_rip_LDADD = \ + -lm \ + libcupsfilters.la + gstoraster_SOURCES = \ filter/gstoraster.c \ - filter/colord.c \ - filter/colord.h \ + cupsfilters/colord.h \ cupsfilters/raster.h gstoraster_CFLAGS = \ @@ -1526,7 +1564,8 @@ initrcdir = $(INITDDIR) initrc_SCRIPTS = utils/cups-browsed man_MANS = \ utils/cups-browsed.8 \ - utils/cups-browsed.conf.5 + utils/cups-browsed.conf.5 \ + filter/foomatic-rip/foomatic-rip.1 # === @@ -1618,6 +1657,8 @@ utils/cups-browsed: $(top_builddir)/config.status $(top_srcdir)/utils/cups-brows cd $(top_builddir) && $(SHELL) ./config.status $@ utils/cups-browsed.conf: $(top_builddir)/config.status $(top_srcdir)/utils/cups-browsed.conf.in cd $(top_builddir) && $(SHELL) ./config.status $@ +filter/foomatic-rip/foomatic-rip.1: $(top_builddir)/config.status $(top_srcdir)/filter/foomatic-rip/foomatic-rip.1.in + cd $(top_builddir) && $(SHELL) ./config.status $@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @@ -1919,6 +1960,10 @@ cups-browsed$(EXEEXT): $(cups_browsed_OBJECTS) $(cups_browsed_DEPENDENCIES) $(EX @rm -f cups-browsed$(EXEEXT) $(AM_V_CCLD)$(cups_browsed_LINK) $(cups_browsed_OBJECTS) $(cups_browsed_LDADD) $(LIBS) +foomatic-rip$(EXEEXT): $(foomatic_rip_OBJECTS) $(foomatic_rip_DEPENDENCIES) $(EXTRA_foomatic_rip_DEPENDENCIES) + @rm -f foomatic-rip$(EXEEXT) + $(AM_V_CCLD)$(foomatic_rip_LINK) $(foomatic_rip_OBJECTS) $(foomatic_rip_LDADD) $(LIBS) + gstoraster$(EXEEXT): $(gstoraster_OBJECTS) $(gstoraster_DEPENDENCIES) $(EXTRA_gstoraster_DEPENDENCIES) @rm -f gstoraster$(EXEEXT) $(AM_V_CCLD)$(gstoraster_LINK) $(gstoraster_OBJECTS) $(gstoraster_LDADD) $(LIBS) @@ -2112,8 +2157,15 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/embed_pdf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/embed_sfnt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fontfile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-foomaticrip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-options.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-pdf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-postscript.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-process.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-renderer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-spooler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foomatic_rip-util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frequent.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gstoraster-colord.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gstoraster-gstoraster.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imagetopdf-common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imagetopdf-imagetopdf.Po@am__quote@ @@ -2122,6 +2174,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcupsfilters_la-attr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcupsfilters_la-check.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcupsfilters_la-cmyk.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcupsfilters_la-colord.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcupsfilters_la-dither.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcupsfilters_la-image-bmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcupsfilters_la-image-colorspace.Plo@am__quote@ @@ -2242,6 +2295,13 @@ libcupsfilters_la-cmyk.lo: cupsfilters/cmyk.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcupsfilters_la_CFLAGS) $(CFLAGS) -c -o libcupsfilters_la-cmyk.lo `test -f 'cupsfilters/cmyk.c' || echo '$(srcdir)/'`cupsfilters/cmyk.c +libcupsfilters_la-colord.lo: cupsfilters/colord.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcupsfilters_la_CFLAGS) $(CFLAGS) -MT libcupsfilters_la-colord.lo -MD -MP -MF $(DEPDIR)/libcupsfilters_la-colord.Tpo -c -o libcupsfilters_la-colord.lo `test -f 'cupsfilters/colord.c' || echo '$(srcdir)/'`cupsfilters/colord.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcupsfilters_la-colord.Tpo $(DEPDIR)/libcupsfilters_la-colord.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cupsfilters/colord.c' object='libcupsfilters_la-colord.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcupsfilters_la_CFLAGS) $(CFLAGS) -c -o libcupsfilters_la-colord.lo `test -f 'cupsfilters/colord.c' || echo '$(srcdir)/'`cupsfilters/colord.c + libcupsfilters_la-dither.lo: cupsfilters/dither.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcupsfilters_la_CFLAGS) $(CFLAGS) -MT libcupsfilters_la-dither.lo -MD -MP -MF $(DEPDIR)/libcupsfilters_la-dither.Tpo -c -o libcupsfilters_la-dither.lo `test -f 'cupsfilters/dither.c' || echo '$(srcdir)/'`cupsfilters/dither.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcupsfilters_la-dither.Tpo $(DEPDIR)/libcupsfilters_la-dither.Plo @@ -2536,6 +2596,118 @@ cups_browsed-cups-browsed.obj: utils/cups-browsed.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cups_browsed_CFLAGS) $(CFLAGS) -c -o cups_browsed-cups-browsed.obj `if test -f 'utils/cups-browsed.c'; then $(CYGPATH_W) 'utils/cups-browsed.c'; else $(CYGPATH_W) '$(srcdir)/utils/cups-browsed.c'; fi` +foomatic_rip-foomaticrip.o: filter/foomatic-rip/foomaticrip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-foomaticrip.o -MD -MP -MF $(DEPDIR)/foomatic_rip-foomaticrip.Tpo -c -o foomatic_rip-foomaticrip.o `test -f 'filter/foomatic-rip/foomaticrip.c' || echo '$(srcdir)/'`filter/foomatic-rip/foomaticrip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-foomaticrip.Tpo $(DEPDIR)/foomatic_rip-foomaticrip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/foomaticrip.c' object='foomatic_rip-foomaticrip.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-foomaticrip.o `test -f 'filter/foomatic-rip/foomaticrip.c' || echo '$(srcdir)/'`filter/foomatic-rip/foomaticrip.c + +foomatic_rip-foomaticrip.obj: filter/foomatic-rip/foomaticrip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-foomaticrip.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-foomaticrip.Tpo -c -o foomatic_rip-foomaticrip.obj `if test -f 'filter/foomatic-rip/foomaticrip.c'; then $(CYGPATH_W) 'filter/foomatic-rip/foomaticrip.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/foomaticrip.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-foomaticrip.Tpo $(DEPDIR)/foomatic_rip-foomaticrip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/foomaticrip.c' object='foomatic_rip-foomaticrip.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-foomaticrip.obj `if test -f 'filter/foomatic-rip/foomaticrip.c'; then $(CYGPATH_W) 'filter/foomatic-rip/foomaticrip.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/foomaticrip.c'; fi` + +foomatic_rip-options.o: filter/foomatic-rip/options.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-options.o -MD -MP -MF $(DEPDIR)/foomatic_rip-options.Tpo -c -o foomatic_rip-options.o `test -f 'filter/foomatic-rip/options.c' || echo '$(srcdir)/'`filter/foomatic-rip/options.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-options.Tpo $(DEPDIR)/foomatic_rip-options.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/options.c' object='foomatic_rip-options.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-options.o `test -f 'filter/foomatic-rip/options.c' || echo '$(srcdir)/'`filter/foomatic-rip/options.c + +foomatic_rip-options.obj: filter/foomatic-rip/options.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-options.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-options.Tpo -c -o foomatic_rip-options.obj `if test -f 'filter/foomatic-rip/options.c'; then $(CYGPATH_W) 'filter/foomatic-rip/options.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/options.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-options.Tpo $(DEPDIR)/foomatic_rip-options.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/options.c' object='foomatic_rip-options.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-options.obj `if test -f 'filter/foomatic-rip/options.c'; then $(CYGPATH_W) 'filter/foomatic-rip/options.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/options.c'; fi` + +foomatic_rip-pdf.o: filter/foomatic-rip/pdf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-pdf.o -MD -MP -MF $(DEPDIR)/foomatic_rip-pdf.Tpo -c -o foomatic_rip-pdf.o `test -f 'filter/foomatic-rip/pdf.c' || echo '$(srcdir)/'`filter/foomatic-rip/pdf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-pdf.Tpo $(DEPDIR)/foomatic_rip-pdf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/pdf.c' object='foomatic_rip-pdf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-pdf.o `test -f 'filter/foomatic-rip/pdf.c' || echo '$(srcdir)/'`filter/foomatic-rip/pdf.c + +foomatic_rip-pdf.obj: filter/foomatic-rip/pdf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-pdf.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-pdf.Tpo -c -o foomatic_rip-pdf.obj `if test -f 'filter/foomatic-rip/pdf.c'; then $(CYGPATH_W) 'filter/foomatic-rip/pdf.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/pdf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-pdf.Tpo $(DEPDIR)/foomatic_rip-pdf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/pdf.c' object='foomatic_rip-pdf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-pdf.obj `if test -f 'filter/foomatic-rip/pdf.c'; then $(CYGPATH_W) 'filter/foomatic-rip/pdf.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/pdf.c'; fi` + +foomatic_rip-postscript.o: filter/foomatic-rip/postscript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-postscript.o -MD -MP -MF $(DEPDIR)/foomatic_rip-postscript.Tpo -c -o foomatic_rip-postscript.o `test -f 'filter/foomatic-rip/postscript.c' || echo '$(srcdir)/'`filter/foomatic-rip/postscript.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-postscript.Tpo $(DEPDIR)/foomatic_rip-postscript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/postscript.c' object='foomatic_rip-postscript.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-postscript.o `test -f 'filter/foomatic-rip/postscript.c' || echo '$(srcdir)/'`filter/foomatic-rip/postscript.c + +foomatic_rip-postscript.obj: filter/foomatic-rip/postscript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-postscript.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-postscript.Tpo -c -o foomatic_rip-postscript.obj `if test -f 'filter/foomatic-rip/postscript.c'; then $(CYGPATH_W) 'filter/foomatic-rip/postscript.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/postscript.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-postscript.Tpo $(DEPDIR)/foomatic_rip-postscript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/postscript.c' object='foomatic_rip-postscript.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-postscript.obj `if test -f 'filter/foomatic-rip/postscript.c'; then $(CYGPATH_W) 'filter/foomatic-rip/postscript.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/postscript.c'; fi` + +foomatic_rip-process.o: filter/foomatic-rip/process.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-process.o -MD -MP -MF $(DEPDIR)/foomatic_rip-process.Tpo -c -o foomatic_rip-process.o `test -f 'filter/foomatic-rip/process.c' || echo '$(srcdir)/'`filter/foomatic-rip/process.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-process.Tpo $(DEPDIR)/foomatic_rip-process.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/process.c' object='foomatic_rip-process.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-process.o `test -f 'filter/foomatic-rip/process.c' || echo '$(srcdir)/'`filter/foomatic-rip/process.c + +foomatic_rip-process.obj: filter/foomatic-rip/process.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-process.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-process.Tpo -c -o foomatic_rip-process.obj `if test -f 'filter/foomatic-rip/process.c'; then $(CYGPATH_W) 'filter/foomatic-rip/process.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/process.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-process.Tpo $(DEPDIR)/foomatic_rip-process.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/process.c' object='foomatic_rip-process.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-process.obj `if test -f 'filter/foomatic-rip/process.c'; then $(CYGPATH_W) 'filter/foomatic-rip/process.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/process.c'; fi` + +foomatic_rip-renderer.o: filter/foomatic-rip/renderer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-renderer.o -MD -MP -MF $(DEPDIR)/foomatic_rip-renderer.Tpo -c -o foomatic_rip-renderer.o `test -f 'filter/foomatic-rip/renderer.c' || echo '$(srcdir)/'`filter/foomatic-rip/renderer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-renderer.Tpo $(DEPDIR)/foomatic_rip-renderer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/renderer.c' object='foomatic_rip-renderer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-renderer.o `test -f 'filter/foomatic-rip/renderer.c' || echo '$(srcdir)/'`filter/foomatic-rip/renderer.c + +foomatic_rip-renderer.obj: filter/foomatic-rip/renderer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-renderer.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-renderer.Tpo -c -o foomatic_rip-renderer.obj `if test -f 'filter/foomatic-rip/renderer.c'; then $(CYGPATH_W) 'filter/foomatic-rip/renderer.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/renderer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-renderer.Tpo $(DEPDIR)/foomatic_rip-renderer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/renderer.c' object='foomatic_rip-renderer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-renderer.obj `if test -f 'filter/foomatic-rip/renderer.c'; then $(CYGPATH_W) 'filter/foomatic-rip/renderer.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/renderer.c'; fi` + +foomatic_rip-spooler.o: filter/foomatic-rip/spooler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-spooler.o -MD -MP -MF $(DEPDIR)/foomatic_rip-spooler.Tpo -c -o foomatic_rip-spooler.o `test -f 'filter/foomatic-rip/spooler.c' || echo '$(srcdir)/'`filter/foomatic-rip/spooler.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-spooler.Tpo $(DEPDIR)/foomatic_rip-spooler.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/spooler.c' object='foomatic_rip-spooler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-spooler.o `test -f 'filter/foomatic-rip/spooler.c' || echo '$(srcdir)/'`filter/foomatic-rip/spooler.c + +foomatic_rip-spooler.obj: filter/foomatic-rip/spooler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-spooler.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-spooler.Tpo -c -o foomatic_rip-spooler.obj `if test -f 'filter/foomatic-rip/spooler.c'; then $(CYGPATH_W) 'filter/foomatic-rip/spooler.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/spooler.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-spooler.Tpo $(DEPDIR)/foomatic_rip-spooler.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/spooler.c' object='foomatic_rip-spooler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-spooler.obj `if test -f 'filter/foomatic-rip/spooler.c'; then $(CYGPATH_W) 'filter/foomatic-rip/spooler.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/spooler.c'; fi` + +foomatic_rip-util.o: filter/foomatic-rip/util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-util.o -MD -MP -MF $(DEPDIR)/foomatic_rip-util.Tpo -c -o foomatic_rip-util.o `test -f 'filter/foomatic-rip/util.c' || echo '$(srcdir)/'`filter/foomatic-rip/util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-util.Tpo $(DEPDIR)/foomatic_rip-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/util.c' object='foomatic_rip-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-util.o `test -f 'filter/foomatic-rip/util.c' || echo '$(srcdir)/'`filter/foomatic-rip/util.c + +foomatic_rip-util.obj: filter/foomatic-rip/util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -MT foomatic_rip-util.obj -MD -MP -MF $(DEPDIR)/foomatic_rip-util.Tpo -c -o foomatic_rip-util.obj `if test -f 'filter/foomatic-rip/util.c'; then $(CYGPATH_W) 'filter/foomatic-rip/util.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/foomatic_rip-util.Tpo $(DEPDIR)/foomatic_rip-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/foomatic-rip/util.c' object='foomatic_rip-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(foomatic_rip_CFLAGS) $(CFLAGS) -c -o foomatic_rip-util.obj `if test -f 'filter/foomatic-rip/util.c'; then $(CYGPATH_W) 'filter/foomatic-rip/util.c'; else $(CYGPATH_W) '$(srcdir)/filter/foomatic-rip/util.c'; fi` + gstoraster-gstoraster.o: filter/gstoraster.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gstoraster_CFLAGS) $(CFLAGS) -MT gstoraster-gstoraster.o -MD -MP -MF $(DEPDIR)/gstoraster-gstoraster.Tpo -c -o gstoraster-gstoraster.o `test -f 'filter/gstoraster.c' || echo '$(srcdir)/'`filter/gstoraster.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gstoraster-gstoraster.Tpo $(DEPDIR)/gstoraster-gstoraster.Po @@ -2550,20 +2722,6 @@ gstoraster-gstoraster.obj: filter/gstoraster.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gstoraster_CFLAGS) $(CFLAGS) -c -o gstoraster-gstoraster.obj `if test -f 'filter/gstoraster.c'; then $(CYGPATH_W) 'filter/gstoraster.c'; else $(CYGPATH_W) '$(srcdir)/filter/gstoraster.c'; fi` -gstoraster-colord.o: filter/colord.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gstoraster_CFLAGS) $(CFLAGS) -MT gstoraster-colord.o -MD -MP -MF $(DEPDIR)/gstoraster-colord.Tpo -c -o gstoraster-colord.o `test -f 'filter/colord.c' || echo '$(srcdir)/'`filter/colord.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gstoraster-colord.Tpo $(DEPDIR)/gstoraster-colord.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/colord.c' object='gstoraster-colord.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gstoraster_CFLAGS) $(CFLAGS) -c -o gstoraster-colord.o `test -f 'filter/colord.c' || echo '$(srcdir)/'`filter/colord.c - -gstoraster-colord.obj: filter/colord.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gstoraster_CFLAGS) $(CFLAGS) -MT gstoraster-colord.obj -MD -MP -MF $(DEPDIR)/gstoraster-colord.Tpo -c -o gstoraster-colord.obj `if test -f 'filter/colord.c'; then $(CYGPATH_W) 'filter/colord.c'; else $(CYGPATH_W) '$(srcdir)/filter/colord.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gstoraster-colord.Tpo $(DEPDIR)/gstoraster-colord.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='filter/colord.c' object='gstoraster-colord.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gstoraster_CFLAGS) $(CFLAGS) -c -o gstoraster-colord.obj `if test -f 'filter/colord.c'; then $(CYGPATH_W) 'filter/colord.c'; else $(CYGPATH_W) '$(srcdir)/filter/colord.c'; fi` - imagetopdf-common.o: filter/common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(imagetopdf_CFLAGS) $(CFLAGS) -MT imagetopdf-common.o -MD -MP -MF $(DEPDIR)/imagetopdf-common.Tpo -c -o imagetopdf-common.o `test -f 'filter/common.c' || echo '$(srcdir)/'`filter/common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/imagetopdf-common.Tpo $(DEPDIR)/imagetopdf-common.Po @@ -3461,6 +3619,49 @@ clean-libtool: distclean-libtool: -rm -f libtool config.lt +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-man5: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ @@ -4241,7 +4442,7 @@ all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA) \ install-binPROGRAMS: install-libLTLIBRARIES installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(phpextensiondir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgbackenddir)" "$(DESTDIR)$(pkgfilterdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(initrcdir)" "$(DESTDIR)$(pkgfilterdir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(pkgbannerdir)" "$(DESTDIR)$(pkgcharsetdir)" "$(DESTDIR)$(pkgconfdir)" "$(DESTDIR)$(pkgcupsdatadir)" "$(DESTDIR)$(pkgcupsserverrootdir)" "$(DESTDIR)$(pkgdriverdir)" "$(DESTDIR)$(pkgfiltersincludedir)" "$(DESTDIR)$(pkgfontconfigdir)" "$(DESTDIR)$(pkgfontembedincludedir)" "$(DESTDIR)$(pkgmimedir)" "$(DESTDIR)$(pkgppdcdir)" "$(DESTDIR)$(ppddir)"; do \ + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(phpextensiondir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgbackenddir)" "$(DESTDIR)$(pkgfilterdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(initrcdir)" "$(DESTDIR)$(pkgfilterdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(pkgbannerdir)" "$(DESTDIR)$(pkgcharsetdir)" "$(DESTDIR)$(pkgconfdir)" "$(DESTDIR)$(pkgcupsdatadir)" "$(DESTDIR)$(pkgcupsserverrootdir)" "$(DESTDIR)$(pkgdriverdir)" "$(DESTDIR)$(pkgfiltersincludedir)" "$(DESTDIR)$(pkgfontconfigdir)" "$(DESTDIR)$(pkgfontembedincludedir)" "$(DESTDIR)$(pkgmimedir)" "$(DESTDIR)$(pkgppdcdir)" "$(DESTDIR)$(ppddir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -4329,7 +4530,7 @@ install-info: install-info-am install-info-am: -install-man: install-man5 install-man8 +install-man: install-man1 install-man5 install-man8 install-pdf: install-pdf-am @@ -4373,7 +4574,7 @@ uninstall-am: uninstall-binPROGRAMS uninstall-docDATA \ uninstall-pkgppdcDATA uninstall-ppdDATA uninstall-sbinPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) uninstall-hook -uninstall-man: uninstall-man5 uninstall-man8 +uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8 .MAKE: all check-am install-am install-data-am install-strip \ uninstall-am @@ -4393,23 +4594,23 @@ uninstall-man: uninstall-man5 uninstall-man8 install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-initrcSCRIPTS install-libLTLIBRARIES install-man \ - install-man5 install-man8 install-pdf install-pdf-am \ - install-phpextensionLTLIBRARIES install-pkgbackendPROGRAMS \ - install-pkgbannerDATA install-pkgcharsetDATA \ - install-pkgconfDATA install-pkgcupsdataDATA \ - install-pkgcupsserverrootDATA install-pkgdriverDATA \ - install-pkgfilterPROGRAMS install-pkgfilterSCRIPTS \ - install-pkgfiltersincludeDATA install-pkgfontconfigDATA \ - install-pkgfontembedincludeDATA install-pkgmimeDATA \ - install-pkgppdcDATA install-ppdDATA install-ps install-ps-am \ - install-sbinPROGRAMS install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ + install-man1 install-man5 install-man8 install-pdf \ + install-pdf-am install-phpextensionLTLIBRARIES \ + install-pkgbackendPROGRAMS install-pkgbannerDATA \ + install-pkgcharsetDATA install-pkgconfDATA \ + install-pkgcupsdataDATA install-pkgcupsserverrootDATA \ + install-pkgdriverDATA install-pkgfilterPROGRAMS \ + install-pkgfilterSCRIPTS install-pkgfiltersincludeDATA \ + install-pkgfontconfigDATA install-pkgfontembedincludeDATA \ + install-pkgmimeDATA install-pkgppdcDATA install-ppdDATA \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ recheck tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-docDATA uninstall-hook \ uninstall-initrcSCRIPTS uninstall-libLTLIBRARIES uninstall-man \ - uninstall-man5 uninstall-man8 \ + uninstall-man1 uninstall-man5 uninstall-man8 \ uninstall-phpextensionLTLIBRARIES uninstall-pkgbackendPROGRAMS \ uninstall-pkgbannerDATA uninstall-pkgcharsetDATA \ uninstall-pkgconfDATA uninstall-pkgcupsdataDATA \ @@ -1,6 +1,80 @@ -NEWS - OpenPrinting CUPS Filters v1.0.41 - 2013-10-30 +NEWS - OpenPrinting CUPS Filters v1.0.42 - 2013-11-29 ----------------------------------------------------- +CHANGES IN V1.0.42 + + - pdftoippprinter: Check also the presence of Ghostscript and + use pdftoraster if Ghostscript is missing. + - gstoraster: At build time use the path for Ghostscript which + our build system already finds for pdftops. + - pdftoraster: Take into account rotate field in PDF header, + when pdftopdf rotates a page to fit the paper it sets the + rotate field and does not swap the width and height entries. + - pdftoippprinter: Made PCL 5c/e printing working correctly, + by first adding support for unprintable margins and + defaulting to 12pt margins if no margin info is supplied, + and second, by doing color printing always in RGB color + space. + - rastertopclx: Improved support for PPD-less printing, + allowing color printing with (s)RGB color space. Output is + in sRGB then. + - rastertopclx: Determine page size and select correct PCL + command for the page size more reliably, especially for + landscape-oriented pages. + - gstoraster, pdftoraster: Do correct PWG Raster output, allow + switching to PWG Raster via "media-class=PwgRaster" option + and CUPS Raster via "media-class=". Default for PPD-less + printing is PWG Raster. + - gstoraster, pdftoraster: Support applying unprintable + margins for PPD-less mode with CUPS Raster output. + - libcupsfilters: Added support for "media-left-margin", + "media-right-margin", "media-bottom-margin", and + "media-top-margin" IPP options to specify unprintable + margins in 1/100th of a mm, to allow PPD-less printing with + unprintable margins (esp. PCL 5c/e with rastertopclx). + - libcupsfilters: Do not set "MediaClass" to "PwgRaster" when + we request a CUPS Raster header and not a PWG Raster header. + - libcupsfilters: Fixed typo in a debug message in the colord + support part. + - cupsfilters.convs: Corrected cost factor of + vnd.cups-postscript -> vnd.cups-raster conversion with + gstoraster, so that input data of the type + application/vnd.adobe-reader-postscript is converted + correctly (not via pstotiff). Thanks to Tim Waugh from Red + Hat for this patch + - cups-browsed: Fixed several memory leaks by adding missing + free() calls and removing an unneeded strdup(). Thanks to + Jaromir Koncicky from Red Hat for the patch (Red Hat bug + #1027317). + - Backends parallel and serial: Fixed logical expressions for + error handling (Bug #1172). + - libcupsfilters: Moved filter/colord.[ch] into the library as + this code is now used by both gstoraster and foomatic-rip. + - libcupsfilters: Made the names of the flags to tell that the + header is already included unique in all API header files. + - foomatic-rip: Moved foomatic-rip's upstream home from the + foomatic-filters package to cups-filters, to make it easier + for distributions to ship and maintain a complete printing + stack and also to make upstream maintenance and development + easier. + - foomatic-rip: Removed support for all the non-CUPS printing + environments as they are discontinued upstream. Now + foomatic-rip only works as a CUPS filter and in a spooler-less + direct mode, where the latter is mainly for testing and + debugging. Thanks to Anshul Kushwaha (anshulkushwaha1 at gmail + dot com) for doing this as a Google Summer of Code 2013 project. + - foomatic-rip: Eliminated compiler warnings. + - foomatic-rip: fixed "endswith()" string utility function. + - foomatic-rip: Removed unneeded HAVE_DBUS conditionals. + - gstoraster: Fixed build system for gstoraster use D-Bus for + colord support. + - libcupsfilters: Recognize more Adobe-generated CMYK JPEGs to + take into account their inverted colors (Bug #1169). + - pdftopdf: When checking whether double-sided printing is + chosen, do not only check for the choice names "DuplexNoTumble" + and "DuplexTumble" but also for "LongEdge", "ShortEdge", + "Top", and "Bottom", as CUPS does (Bug #1167). + CHANGES IN V1.0.41 - cups-browsed: Added support for automatic PPD-less setup of @@ -1,4 +1,4 @@ -README - OpenPrinting CUPS Filters v1.0.41 - 2013-10-30 +README - OpenPrinting CUPS Filters v1.0.42 - 2013-11-29 ------------------------------------------------------- Looking for compile instructions? Read the file "INSTALL.txt" diff --git a/backend/parallel.c b/backend/parallel.c index 7acb27552..25aabdae6 100644 --- a/backend/parallel.c +++ b/backend/parallel.c @@ -329,7 +329,7 @@ drain_output(int print_fd, /* I - Print file descriptor */ * Read error - bail if we don't see EAGAIN or EINTR... */ - if (errno != EAGAIN || errno != EINTR) + if (errno != EAGAIN && errno != EINTR) { perror("ERROR: Unable to read print data"); return (-1); @@ -712,7 +712,7 @@ run_loop(int print_fd, /* I - Print file descriptor */ * Read error - bail if we don't see EAGAIN or EINTR... */ - if (errno != EAGAIN || errno != EINTR) + if (errno != EAGAIN && errno != EINTR) { perror("ERROR: Unable to read print data"); return (-1); diff --git a/backend/serial.c b/backend/serial.c index e4dc30638..48acda436 100644 --- a/backend/serial.c +++ b/backend/serial.c @@ -582,7 +582,7 @@ main(int argc, /* I - Number of command-line arguments (6 or 7) */ * Read error - bail if we don't see EAGAIN or EINTR... */ - if (errno != EAGAIN || errno != EINTR) + if (errno != EAGAIN && errno != EINTR) { perror("DEBUG: Unable to read print data"); @@ -751,7 +751,7 @@ drain_output(int print_fd, /* I - Print file descriptor */ * Read error - bail if we don't see EAGAIN or EINTR... */ - if (errno != EAGAIN || errno != EINTR) + if (errno != EAGAIN && errno != EINTR) { perror("ERROR: Unable to read print data"); return (-1); @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for cups-filters 1.0.41. +# Generated by GNU Autoconf 2.69 for cups-filters 1.0.42. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='cups-filters' PACKAGE_TARNAME='cups-filters' -PACKAGE_VERSION='1.0.41' -PACKAGE_STRING='cups-filters 1.0.41' +PACKAGE_VERSION='1.0.42' +PACKAGE_STRING='cups-filters 1.0.42' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -642,6 +642,10 @@ CUPS_PDFTOCAIRO CUPS_PDFTOPS CUPS_GHOSTSCRIPT LARGEFILE +DBUS_LIBS +DBUS_CFLAGS +BUILD_DBUS_FALSE +BUILD_DBUS_TRUE LIBQPDF_LIBS LIBQPDF_CFLAGS ZLIB_LIBS @@ -843,6 +847,7 @@ with_rcdir with_rclevels with_rcstart with_rcstop +enable_dbus enable_largefile with_pdftops with_gs_path @@ -890,7 +895,9 @@ POPPLER_LIBS ZLIB_CFLAGS ZLIB_LIBS LIBQPDF_CFLAGS -LIBQPDF_LIBS' +LIBQPDF_LIBS +DBUS_CFLAGS +DBUS_LIBS' # Initialize some variables set by options. @@ -1431,7 +1438,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures cups-filters 1.0.41 to adapt to many kinds of systems. +\`configure' configures cups-filters 1.0.42 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1501,7 +1508,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of cups-filters 1.0.41:";; + short | recursive ) echo "Configuration of cups-filters 1.0.42:";; esac cat <<\_ACEOF @@ -1522,6 +1529,7 @@ Optional Features: --disable-libtool-lock avoid locking (might break parallel builds) --disable-imagefilters Build the image filters. --disable-avahi Disable DNS Service Discovery support using Avahi. + --enable-dbus enable DBus CMS code --disable-largefile omit support for large files --enable-werror Treat all warnings as errors, useful for development. @@ -1621,6 +1629,8 @@ Some influential environment variables: C compiler flags for LIBQPDF, overriding pkg-config LIBQPDF_LIBS linker flags for LIBQPDF, overriding pkg-config + DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config + DBUS_LIBS linker flags for DBUS, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1688,7 +1698,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -cups-filters configure 1.0.41 +cups-filters configure 1.0.42 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2295,7 +2305,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by cups-filters $as_me 1.0.41, which was +It was created by cups-filters $as_me 1.0.42, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3246,7 +3256,7 @@ fi # Define the identity of the package. PACKAGE='cups-filters' - VERSION='1.0.41' + VERSION='1.0.42' cat >>confdefs.h <<_ACEOF @@ -17771,6 +17781,118 @@ fi +# =============== +# Check for D-Bus +# =============== +# Check whether --enable-dbus was given. +if test "${enable_dbus+set}" = set; then : + enableval=$enable_dbus; enable_dbus=$enableval +else + enable_dbus=yes +fi + + if test x$enable_dbus = xyes; then + BUILD_DBUS_TRUE= + BUILD_DBUS_FALSE='#' +else + BUILD_DBUS_TRUE='#' + BUILD_DBUS_FALSE= +fi + +if test x$enable_dbus = xyes; then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DBUS" >&5 +$as_echo_n "checking for DBUS... " >&6; } + +if test -n "$DBUS_CFLAGS"; then + pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$DBUS_LIBS"; then + pkg_cv_DBUS_LIBS="$DBUS_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1" 2>&1` + else + DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$DBUS_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (dbus-1) were not met: + +$DBUS_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables DBUS_CFLAGS +and DBUS_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables DBUS_CFLAGS +and DBUS_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see <http://pkg-config.freedesktop.org/>. +See \`config.log' for more details" "$LINENO" 5; } +else + DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS + DBUS_LIBS=$pkg_cv_DBUS_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi +fi + # =================================== # Check for large files and long long # =================================== @@ -18653,7 +18775,7 @@ fi # ===================== # Prepare all .in files # ===================== -ac_config_files="$ac_config_files libcupsfilters.pc libfontembed.pc Makefile filter/gstopxl utils/cups-browsed utils/cups-browsed.conf" +ac_config_files="$ac_config_files libcupsfilters.pc libfontembed.pc Makefile filter/gstopxl utils/cups-browsed utils/cups-browsed.conf filter/foomatic-rip/foomatic-rip.1" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -18804,6 +18926,10 @@ if test -z "${RCLINKS_TRUE}" && test -z "${RCLINKS_FALSE}"; then as_fn_error $? "conditional \"RCLINKS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${BUILD_DBUS_TRUE}" && test -z "${BUILD_DBUS_FALSE}"; then + as_fn_error $? "conditional \"BUILD_DBUS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${WITH_PHP_TRUE}" && test -z "${WITH_PHP_FALSE}"; then as_fn_error $? "conditional \"WITH_PHP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -19205,7 +19331,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by cups-filters $as_me 1.0.41, which was +This file was extended by cups-filters $as_me 1.0.42, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -19271,7 +19397,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -cups-filters config.status 1.0.41 +cups-filters config.status 1.0.42 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -19784,6 +19910,7 @@ do "filter/gstopxl") CONFIG_FILES="$CONFIG_FILES filter/gstopxl" ;; "utils/cups-browsed") CONFIG_FILES="$CONFIG_FILES utils/cups-browsed" ;; "utils/cups-browsed.conf") CONFIG_FILES="$CONFIG_FILES utils/cups-browsed.conf" ;; + "filter/foomatic-rip/foomatic-rip.1") CONFIG_FILES="$CONFIG_FILES filter/foomatic-rip/foomatic-rip.1" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -21334,6 +21461,7 @@ Build configuration: test-font: ${with_test_font_path} tiff: ${with_tiff} avahi: ${enable_avahi} + dbus: ${enable_dbus} browsing: ${with_browseremoteprotocols} werror: ${enable_werror} ============================================================================== @@ -21361,6 +21489,7 @@ Build configuration: test-font: ${with_test_font_path} tiff: ${with_tiff} avahi: ${enable_avahi} + dbus: ${enable_dbus} browsing: ${with_browseremoteprotocols} werror: ${enable_werror} ============================================================================== diff --git a/configure.ac b/configure.ac index 62d4c6754..077727f52 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ AC_PREREQ([2.65]) # ==================== m4_define([cups_filters_version_major],[1]) m4_define([cups_filters_version_minor],[0]) -m4_define([cups_filters_version_micro],[41]) +m4_define([cups_filters_version_micro],[42]) m4_define([cups_filters_version],[cups_filters_version_major.cups_filters_version_minor.cups_filters_version_micro]) # ============= @@ -391,6 +391,16 @@ PKG_CHECK_MODULES([LIBQPDF], [libqpdf >= 3.0.2]) # ================ 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.])], []) +# =============== +# 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 # =================================== @@ -612,6 +622,7 @@ AC_CONFIG_FILES([ filter/gstopxl utils/cups-browsed utils/cups-browsed.conf + filter/foomatic-rip/foomatic-rip.1 ]) AC_OUTPUT @@ -641,6 +652,7 @@ Build configuration: test-font: ${with_test_font_path} tiff: ${with_tiff} avahi: ${enable_avahi} + dbus: ${enable_dbus} browsing: ${with_browseremoteprotocols} werror: ${enable_werror} ============================================================================== diff --git a/filter/colord.c b/cupsfilters/colord.c index 8783874b6..0ee23f433 100644 --- a/filter/colord.c +++ b/cupsfilters/colord.c @@ -333,7 +333,7 @@ colord_get_profile_for_device_id (const char *device_id, /* find the device */ device_path = get_device_path_for_device_id (con, device_id); if (device_path == NULL) { - fprintf(stderr, "DEBUG: Failed to get find device %s\n", device_id); + fprintf(stderr, "DEBUG: Failed to get device %s\n", device_id); goto out; } diff --git a/filter/colord.h b/cupsfilters/colord.h index 8f5c1423f..1aec426e9 100644 --- a/filter/colord.h +++ b/cupsfilters/colord.h @@ -25,6 +25,13 @@ MIT Open Source License - http://www.opensource.org/ */ +#ifndef _CUPS_FILTERS_COLORD_H_ +# define _CUPS_FILTERS_COLORD_H_ + +# ifdef __cplusplus +extern "C" { +# endif /* __cplusplus */ + /* Common routines for accessing the colord CMS framework */ #include <cups/raster.h> @@ -33,3 +40,9 @@ char **colord_get_qualifier_for_ppd (ppd_file_t *ppd); char *colord_get_profile_for_device_id (const char *device_id, const char **qualifier_tuple); int colord_get_inhibit_for_device_id (const char *device_id); + +# ifdef __cplusplus +} +# endif /* __cplusplus */ + +#endif /* !_CUPS_FILTERS_COLORD_H_ */ diff --git a/cupsfilters/driver.h b/cupsfilters/driver.h index ed4eab96d..84c98973f 100644 --- a/cupsfilters/driver.h +++ b/cupsfilters/driver.h @@ -13,8 +13,8 @@ * file is missing or damaged, see the license at "http://www.cups.org/". */ -#ifndef _CUPS_DRIVER_H_ -# define _CUPS_DRIVER_H_ +#ifndef _CUPS_FILTERS_DRIVER_H_ +# define _CUPS_FILTERS_DRIVER_H_ # ifdef __cplusplus extern "C" { @@ -241,7 +241,7 @@ extern void cupsCMYKSetLtDk(cups_cmyk_t *cmyk, int channel, } # endif /* __cplusplus */ -#endif /* !_CUPS_DRIVER_H_ */ +#endif /* !_CUPS_FILTERS_DRIVER_H_ */ /* * End of "$Id$". diff --git a/cupsfilters/image-jpeg.c b/cupsfilters/image-jpeg.c index 3c9ebcc3d..4d7663fa7 100644 --- a/cupsfilters/image-jpeg.c +++ b/cupsfilters/image-jpeg.c @@ -78,7 +78,7 @@ _cupsImageReadJPEG( for (marker = cinfo.marker_list; marker; marker = marker->next) if (marker->marker == (JPEG_APP0 + 14) && marker->data_length >= 12 && - !memcmp(marker->data, "Adobe", 5) && marker->data[11] == 2) + !memcmp(marker->data, "Adobe", 5)) { fputs("DEBUG: Adobe CMYK JPEG detected (inverting color values)\n", stderr); diff --git a/cupsfilters/image.h b/cupsfilters/image.h index 7174b5e0d..8a59052fc 100644 --- a/cupsfilters/image.h +++ b/cupsfilters/image.h @@ -15,8 +15,8 @@ * This file is subject to the Apple OS-Developed Software exception. */ -#ifndef _CUPS_IMAGE_H_ -# define _CUPS_IMAGE_H_ +#ifndef _CUPS_FILTERS_IMAGE_H_ +# define _CUPS_FILTERS_IMAGE_H_ /* * Include necessary headers... @@ -123,7 +123,7 @@ extern void cupsImageWhiteToWhite(const cups_ib_t *in, } # endif /* __cplusplus */ -#endif /* !_CUPS_IMAGE_H_ */ +#endif /* !_CUPS_FILTERS_IMAGE_H_ */ /* * End of "$Id$". diff --git a/cupsfilters/raster.c b/cupsfilters/raster.c index f2b9c6347..b742ba7c3 100644 --- a/cupsfilters/raster.c +++ b/cupsfilters/raster.c @@ -86,6 +86,7 @@ cupsRasterParseIPPOptions(cups_page_header2_t *h, /* I - Raster header */ *media_source, /* Media source */ *media_type; /* Media type */ pwg_media_t *size_found; /* page size found for given name */ + float size; /* page size dimension */ /* * Range check input... @@ -165,7 +166,7 @@ cupsRasterParseIPPOptions(cups_page_header2_t *h, /* I - Raster header */ (val = cupsGetOption("MediaClass", num_options, options)) != NULL) _strlcpy(h->MediaClass, val, sizeof(h->MediaClass)); else if (set_defaults) - strcpy(h->MediaClass, "PwgRaster"); + strcpy(h->MediaClass, ""); if ((val = cupsGetOption("media-color", num_options, options)) != NULL || (val = cupsGetOption("MediaColor", num_options, options)) != NULL) @@ -329,13 +330,6 @@ cupsRasterParseIPPOptions(cups_page_header2_t *h, /* I - Raster header */ if (pwg_raster || set_defaults) { - /* TODO - Support for margins */ - h->Margins[0] = 0; - h->Margins[1] = 0; - } - - if (pwg_raster || set_defaults) - { /* TODO - Support for manual feed */ h->ManualFeed = CUPS_FALSE; } @@ -607,6 +601,8 @@ cupsRasterParseIPPOptions(cups_page_header2_t *h, /* I - Raster header */ if (pwg_raster) { /* Set "reserved" fields to 0 */ + h->Margins[0] = 0; + h->Margins[1] = 0; h->ImagingBoundingBox[0] = 0; h->ImagingBoundingBox[1] = 0; h->ImagingBoundingBox[2] = 0; @@ -618,15 +614,58 @@ cupsRasterParseIPPOptions(cups_page_header2_t *h, /* I - Raster header */ } else { - /* TODO - Support for non-zero margins */ - h->ImagingBoundingBox[0] = 0; - h->ImagingBoundingBox[1] = 0; - h->ImagingBoundingBox[2] = h->PageSize[0]; - h->ImagingBoundingBox[3] = h->PageSize[1]; - h->cupsImagingBBox[0] = 0.0; - h->cupsImagingBBox[1] = 0.0; - h->cupsImagingBBox[2] = h->cupsPageSize[0]; - h->cupsImagingBBox[3] = h->cupsPageSize[1]; + if ((val = cupsGetOption("media-left-margin", num_options, options)) + != NULL) + { + size = atol(val) * 72.0 / 2540.0; + h->Margins[0] = (int)size; + h->ImagingBoundingBox[0] = (int)size; + h->cupsImagingBBox[0] = size; + } + else if (set_defaults) + { + h->Margins[0] = 0; + h->ImagingBoundingBox[0] = 0; + h->cupsImagingBBox[0] = 0.0; + } + if ((val = cupsGetOption("media-bottom-margin", num_options, options)) + != NULL) + { + size = atol(val) * 72.0 / 2540.0; + h->Margins[1] = (int)size; + h->ImagingBoundingBox[1] = (int)size; + h->cupsImagingBBox[1] = size; + } + else if (set_defaults) + { + h->Margins[1] = 0; + h->ImagingBoundingBox[1] = 0; + h->cupsImagingBBox[1] = 0.0; + } + if ((val = cupsGetOption("media-right-margin", num_options, options)) + != NULL) + { + size = atol(val) * 72.0 / 2540.0; + h->ImagingBoundingBox[2] = h->PageSize[0] - (int)size; + h->cupsImagingBBox[2] = h->cupsPageSize[0] - size; + } + else if (set_defaults) + { + h->ImagingBoundingBox[2] = h->PageSize[0]; + h->cupsImagingBBox[2] = h->cupsPageSize[0]; + } + if ((val = cupsGetOption("media-top-margin", num_options, options)) + != NULL) + { + size = atol(val) * 72.0 / 2540.0; + h->ImagingBoundingBox[3] = h->PageSize[1] - (int)size; + h->cupsImagingBBox[3] = h->cupsPageSize[1] - size; + } + else if (set_defaults) + { + h->ImagingBoundingBox[3] = h->PageSize[1]; + h->cupsImagingBBox[3] = h->cupsPageSize[1]; + } } if (pwg_raster) diff --git a/cupsfilters/raster.h b/cupsfilters/raster.h index ee890f032..fc82b1ebf 100644 --- a/cupsfilters/raster.h +++ b/cupsfilters/raster.h @@ -7,8 +7,8 @@ * which should have been included with this file. */ -#ifndef _CUPSFILTERS_RASTER_H_ -# define _CUPSFILTERS_RASTER_H_ +#ifndef _CUPS_FILTERS_RASTER_H_ +# define _CUPS_FILTERS_RASTER_H_ # ifdef __cplusplus extern "C" { @@ -47,7 +47,7 @@ extern int cupsRasterParseIPPOptions(cups_page_header2_t *h, } # endif /* __cplusplus */ -#endif /* !_CUPSFILTERS_RASTER_H_ */ +#endif /* !_CUPS_FILTERS_RASTER_H_ */ /* * End diff --git a/filter/foomatic-rip/foomatic-rip.1 b/filter/foomatic-rip/foomatic-rip.1 new file mode 100644 index 000000000..b3fc87de1 --- /dev/null +++ b/filter/foomatic-rip/foomatic-rip.1 @@ -0,0 +1,229 @@ +.\" This -*- nroff -*- source file is part of foomatic. + +.hy 0 +.TH FOOMATIC-RIP 1 "2013-11-06" "cups-filters" +.SH NAME +foomatic-rip \- Universal print filter/RIP wrapper +.SH SYNOPSIS + +.SS \fRGeneral Options: +.BI \fBfoomatic-rip\fR\ \fB[-v]\ [-q]\fP \fI\ <mode-specific\ options> + +.SS \fRSpooler-less printing filter: +.BI \fBfoomatic-rip\fR\ \fB[\fB-P\fR \ \fI<printer>\fR \ +| \ \fB--ppd\fR \ \fI<ppdfile>\fR \fB]\fR \ [\fB-J\fR\ \fI<jobtitle>\fR ] +[\fB-o\fR \ \fI<option>\fB=\fI<value>\fR \ [...]] \ \fB[\fI<files>\fB]\fR + +.SS \fRCUPS filter: +.BI \fBfoomatic-rip\fR\ \fI<jobid>\fR \ \fI<user>\fR \ \fI<jobtitle>\fR \ \fI<numcopies>\fR \ \fI<options>\fR \ \fB[\fI<file>\fB]\fR + +.SH DESCRIPTION +foomatic-rip is a universal print filter which can be used as CUPS filter or +stand-alone for spooler-less, direct printing. It has the following features: + +.Topic +It translates PostScript and PDF from standard input or a file to the printer's +native language on standard output. + +.Topic +The translation is done with an external renderer, usually Ghostscript +(\fBgs(1)\fR). If no translation is needed (PostScript printer) the +renderer's command line reduces to \fBcat(1)\fR. The way how this +translation is done is described in a \fBPPD file\fR. + +.Topic +Printer capabilities, how to handle user options, and how to build the +renderer command line is always described by \fBPPD files\fR, these +PPD files usually come from \fBFoomatic\fR or can be the ones supplied by +the manufacturers of PostScript printers. The PPD files are the same +for both CUPS and direct printing. + +.Topic +foomatic-rip works with \fBCUPS\fR and for direct printing \fBwithout +spooler\fR). The mode is selected by the command line options and +environment variables which are supplied to foomatic-rip. + +.Topic +foomatic-rip does not only apply option settings supplied by the user +through the command line of the printing command, but also searches +the entire job for embedded option settings (only PostScript +jobs). Here not only settings which affect the whole job are taken into +account, but also settings in the page headers, which are only valid +for the page where they were found, so applications which produce +PostScript code with page-specific printer option settings are fully +supported. + +.SH DIRECT, SPOOLER-LESS PRINTING + +.SS Options + +.TP 10 +.B \-v +\fRverbose mode for debugging. +.B WARNING: +This will create a file in /tmp that contains the debugging information. +This opens a security loophole and should not be used in production. + +.TP 10 +.B \-q +\fRquiet mode - minimal information output + +.TP 10 +.BI \-P \ <printer> +\fI<printer>\fR is the configured printer which should be used for this job. + +.TP 10 +.BI \--ppd \ <ppdfile> +The PPD file \fI<ppdfile>\fR should be applied for processing this job. +.TP 10 +.BI \-o \ \fI<option>\fB=\fI<value>\fR +Option settings for this job. +.TP 10 +.BI \fI<files>\fR +The file(s) to be printed. + +.P +\fBfoomatic-rip\fR will print from standard input unless at least one file to +be printed is specified on the command line. +If your printer PPD file is stored as \fI/etc/direct/<printer>.ppd\fR +or \fI~/.foomatic/direct/<printer>.ppd\fR you can use it by simply specifying "-P \fI<printer>\fR". + +Put a line + +\fB*FoomaticRIPPostPipe: "| \fI<command>\fB"\fR +.hy 0 + +into the PPD file, right after \fB*PPD-Adobe: "4.3"\fR, where +\fI<command>\fR is a command into which you want to re-direct the +output data. Due to the restrictions of PPD files \fB<\fR, \fB>\fR, and +\fB"\fR are not allowed in the \fI<command>\fR, replace them as +follows: + +.CodeSkip +.nf +.B Character Replacement +.B --------------------- +.B < < +.B > > +.B " " +.B ' ' +.B & & +.fi + +This way you can print directly to your printer, use + +\fB*FoomaticRIPPostPipe: "| cat > /dev/lp0"\fR + +or + +\fB*FoomaticRIPPostPipe: "| cat > /dev/usb/lp0"\fR + +for local parallel or USB printers. To make normal users able to print +this way add them to the group \fBlp\fR and make sure that the +appropriate printer device file \fI/dev/...\fR is group-writable for +the \fBlp\fR group. + +for a TCP/Socket/JetDirect printer with the host name \fBprinter\fR +listening on port \fB9100\fR you need this: + +\fB*FoomaticRIPPostPipe: "| /usr/bin/nc -w 1 printer 9100"\fR + +Note the "-w 1" in the "nc" command line, it makes "nc" exiting +immediately after the data is transferred to the printer. + +\fB*FoomaticRIPPostPipe: "| rlpr -Plp@printserver"\fR + +directs your jobs to the LPD printer queue \fBlp\fR on the machine +named \fBprintserver\fR. + +See also http://www.openprinting.org/direct-doc.html + +.SH "PRINTING WITH SPOOLER" + +See the documentation on the OpenPrinting Web site: +.ft CW +http://www.openprinting.org/ +\fR + +.SH "CONFIGURATION FILE" + +The file \fB/etc/foomatic/filter.conf\fR is read whenever +foomatic-rip is executed. It allows to configure the behavior of +foomatic-rip as follows (lines beginning with \fB#\fR are comments and +therefore get ignored): + +.TP 10 +.B debug: 0|1 +\fRTurns on (\fB1\fR) or off (\fB0\fR) the debug mode. This is equivalent to +supplying the \fB--debug\fR command line option. Default setting is \fB0\fR. + +.TP 10 +.B ps_accounting: 0|1 +\fRTurns on (\fB1\fR) or off (\fB0\fR) inserting PostScript code for page +accounting into PostScript jobs. The inserted PostScript code makes +Ghostscript generating accounting output on stderr and CUPS can this way +log each page which got printed. The code will only be inserted if CUPS +is the spooler. Default setting is \fB1\fR. + +.TP 10 +.BI echo: \ [<path>/]<executable> +\fRSets the path to an \fBecho(1)\fR executable which supports \fB-n\fR. + +.TP 10 +.BI gspath: \ [<path>/]<executable> +\fRSets the path to the Ghostscript (\fBgs(1)\fR) executable. To be used if +Ghostscript is at a non-standard location or if an alternative Ghostscript +should be used. + +.TP 10 +.BI execpath: \ <path>[:<path>]... +\fRSets the \fB$PATH\fR variable to be used by foomatic-rip. + +.TP 10 +.BI cupsfilterpath: \ <path>[:<path>]... +\fRSets the directories (colon-separated) in which foomatic-rip searches for +CUPS filters. + +.TP 10 +.BI preferred_shell: \ [<path>/]<executable> +\fRSets the preferred shell to use when executing FoomaticRIPCommandLine and +friends. Several PPD files use shell constructs that require a more +modern shell like \fBbash\fR, \fBzsh\fR, or \fBksh\fR. + + +.SH FILES +.PD 0 +.TP 0 +/etc/cups/ppd/<printer>.ppd +.TP 0 +/etc/direct/<printer>.ppd + +The PPD files of the currently defined printers + +.TP 0 +/etc/foomatic/filter.conf + +Configuration file for foomatic-rip + +.PD 0 + +.\".SH SEE ALSO +.\".IR foomatic-XXX (1), + +.SH EXIT STATUS +.B foomatic-rip +returns 0 unless something unexpected happens. + +.SH AUTHOR +Till Kamppeter <\fItill.kamppeter@gmail.com\fR> with parts of Manfred +Wassmanns's <\fImanolo@NCC-1701.B.Shuttle.de\fR> man pages for the +Foomatic 2.0.x filters. + +.SH BUGS +None so far. + +Please send bug reports to the OpenPrinting bug tracker: + +http://bugs.linuxfoundation.org/ + +Use "OpenPrinting" as the product and "cups-filters" as the component. diff --git a/filter/foomatic-rip/foomatic-rip.1.in b/filter/foomatic-rip/foomatic-rip.1.in new file mode 100644 index 000000000..611978b54 --- /dev/null +++ b/filter/foomatic-rip/foomatic-rip.1.in @@ -0,0 +1,229 @@ +.\" This -*- nroff -*- source file is part of foomatic. + +.hy 0 +.TH FOOMATIC-RIP 1 "2013-11-06" "cups-filters" +.SH NAME +foomatic-rip \- Universal print filter/RIP wrapper +.SH SYNOPSIS + +.SS \fRGeneral Options: +.BI \fBfoomatic-rip\fR\ \fB[-v]\ [-q]\fP \fI\ <mode-specific\ options> + +.SS \fRSpooler-less printing filter: +.BI \fBfoomatic-rip\fR\ \fB[\fB-P\fR \ \fI<printer>\fR \ +| \ \fB--ppd\fR \ \fI<ppdfile>\fR \fB]\fR \ [\fB-J\fR\ \fI<jobtitle>\fR ] +[\fB-o\fR \ \fI<option>\fB=\fI<value>\fR \ [...]] \ \fB[\fI<files>\fB]\fR + +.SS \fRCUPS filter: +.BI \fBfoomatic-rip\fR\ \fI<jobid>\fR \ \fI<user>\fR \ \fI<jobtitle>\fR \ \fI<numcopies>\fR \ \fI<options>\fR \ \fB[\fI<file>\fB]\fR + +.SH DESCRIPTION +foomatic-rip is a universal print filter which can be used as CUPS filter or +stand-alone for spooler-less, direct printing. It has the following features: + +.Topic +It translates PostScript and PDF from standard input or a file to the printer's +native language on standard output. + +.Topic +The translation is done with an external renderer, usually Ghostscript +(\fBgs(1)\fR). If no translation is needed (PostScript printer) the +renderer's command line reduces to \fBcat(1)\fR. The way how this +translation is done is described in a \fBPPD file\fR. + +.Topic +Printer capabilities, how to handle user options, and how to build the +renderer command line is always described by \fBPPD files\fR, these +PPD files usually come from \fBFoomatic\fR or can be the ones supplied by +the manufacturers of PostScript printers. The PPD files are the same +for both CUPS and direct printing. + +.Topic +foomatic-rip works with \fBCUPS\fR and for direct printing \fBwithout +spooler\fR). The mode is selected by the command line options and +environment variables which are supplied to foomatic-rip. + +.Topic +foomatic-rip does not only apply option settings supplied by the user +through the command line of the printing command, but also searches +the entire job for embedded option settings (only PostScript +jobs). Here not only settings which affect the whole job are taken into +account, but also settings in the page headers, which are only valid +for the page where they were found, so applications which produce +PostScript code with page-specific printer option settings are fully +supported. + +.SH DIRECT, SPOOLER-LESS PRINTING + +.SS Options + +.TP 10 +.B \-v +\fRverbose mode for debugging. +.B WARNING: +This will create a file in /tmp that contains the debugging information. +This opens a security loophole and should not be used in production. + +.TP 10 +.B \-q +\fRquiet mode - minimal information output + +.TP 10 +.BI \-P \ <printer> +\fI<printer>\fR is the configured printer which should be used for this job. + +.TP 10 +.BI \--ppd \ <ppdfile> +The PPD file \fI<ppdfile>\fR should be applied for processing this job. +.TP 10 +.BI \-o \ \fI<option>\fB=\fI<value>\fR +Option settings for this job. +.TP 10 +.BI \fI<files>\fR +The file(s) to be printed. + +.P +\fBfoomatic-rip\fR will print from standard input unless at least one file to +be printed is specified on the command line. +If your printer PPD file is stored as \fI@sysconfdir@/direct/<printer>.ppd\fR +or \fI~/.foomatic/direct/<printer>.ppd\fR you can use it by simply specifying "-P \fI<printer>\fR". + +Put a line + +\fB*FoomaticRIPPostPipe: "| \fI<command>\fB"\fR +.hy 0 + +into the PPD file, right after \fB*PPD-Adobe: "4.3"\fR, where +\fI<command>\fR is a command into which you want to re-direct the +output data. Due to the restrictions of PPD files \fB<\fR, \fB>\fR, and +\fB"\fR are not allowed in the \fI<command>\fR, replace them as +follows: + +.CodeSkip +.nf +.B Character Replacement +.B --------------------- +.B < < +.B > > +.B " " +.B ' ' +.B & & +.fi + +This way you can print directly to your printer, use + +\fB*FoomaticRIPPostPipe: "| cat > /dev/lp0"\fR + +or + +\fB*FoomaticRIPPostPipe: "| cat > /dev/usb/lp0"\fR + +for local parallel or USB printers. To make normal users able to print +this way add them to the group \fBlp\fR and make sure that the +appropriate printer device file \fI/dev/...\fR is group-writable for +the \fBlp\fR group. + +for a TCP/Socket/JetDirect printer with the host name \fBprinter\fR +listening on port \fB9100\fR you need this: + +\fB*FoomaticRIPPostPipe: "| /usr/bin/nc -w 1 printer 9100"\fR + +Note the "-w 1" in the "nc" command line, it makes "nc" exiting +immediately after the data is transferred to the printer. + +\fB*FoomaticRIPPostPipe: "| rlpr -Plp@printserver"\fR + +directs your jobs to the LPD printer queue \fBlp\fR on the machine +named \fBprintserver\fR. + +See also http://www.openprinting.org/direct-doc.html + +.SH "PRINTING WITH SPOOLER" + +See the documentation on the OpenPrinting Web site: +.ft CW +http://www.openprinting.org/ +\fR + +.SH "CONFIGURATION FILE" + +The file \fB@sysconfdir@/foomatic/filter.conf\fR is read whenever +foomatic-rip is executed. It allows to configure the behavior of +foomatic-rip as follows (lines beginning with \fB#\fR are comments and +therefore get ignored): + +.TP 10 +.B debug: 0|1 +\fRTurns on (\fB1\fR) or off (\fB0\fR) the debug mode. This is equivalent to +supplying the \fB--debug\fR command line option. Default setting is \fB0\fR. + +.TP 10 +.B ps_accounting: 0|1 +\fRTurns on (\fB1\fR) or off (\fB0\fR) inserting PostScript code for page +accounting into PostScript jobs. The inserted PostScript code makes +Ghostscript generating accounting output on stderr and CUPS can this way +log each page which got printed. The code will only be inserted if CUPS +is the spooler. Default setting is \fB1\fR. + +.TP 10 +.BI echo: \ [<path>/]<executable> +\fRSets the path to an \fBecho(1)\fR executable which supports \fB-n\fR. + +.TP 10 +.BI gspath: \ [<path>/]<executable> +\fRSets the path to the Ghostscript (\fBgs(1)\fR) executable. To be used if +Ghostscript is at a non-standard location or if an alternative Ghostscript +should be used. + +.TP 10 +.BI execpath: \ <path>[:<path>]... +\fRSets the \fB$PATH\fR variable to be used by foomatic-rip. + +.TP 10 +.BI cupsfilterpath: \ <path>[:<path>]... +\fRSets the directories (colon-separated) in which foomatic-rip searches for +CUPS filters. + +.TP 10 +.BI preferred_shell: \ [<path>/]<executable> +\fRSets the preferred shell to use when executing FoomaticRIPCommandLine and +friends. Several PPD files use shell constructs that require a more +modern shell like \fBbash\fR, \fBzsh\fR, or \fBksh\fR. + + +.SH FILES +.PD 0 +.TP 0 +@sysconfdir@/cups/ppd/<printer>.ppd +.TP 0 +@sysconfdir@/direct/<printer>.ppd + +The PPD files of the currently defined printers + +.TP 0 +@sysconfdir@/foomatic/filter.conf + +Configuration file for foomatic-rip + +.PD 0 + +.\".SH SEE ALSO +.\".IR foomatic-XXX (1), + +.SH EXIT STATUS +.B foomatic-rip +returns 0 unless something unexpected happens. + +.SH AUTHOR +Till Kamppeter <\fItill.kamppeter@gmail.com\fR> with parts of Manfred +Wassmanns's <\fImanolo@NCC-1701.B.Shuttle.de\fR> man pages for the +Foomatic 2.0.x filters. + +.SH BUGS +None so far. + +Please send bug reports to the OpenPrinting bug tracker: + +http://bugs.linuxfoundation.org/ + +Use "OpenPrinting" as the product and "cups-filters" as the component. diff --git a/filter/foomatic-rip/foomaticrip.c b/filter/foomatic-rip/foomaticrip.c new file mode 100644 index 000000000..a9a7f07ca --- /dev/null +++ b/filter/foomatic-rip/foomaticrip.c @@ -0,0 +1,1065 @@ +/* foomaticrip.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "foomaticrip.h" +#include "util.h" +#include "options.h" +#include "pdf.h" +#include "postscript.h" +#include "process.h" +#include "spooler.h" +#include "renderer.h" + +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <memory.h> +#include <ctype.h> +#include <stdarg.h> +#include <assert.h> +#include <unistd.h> +#include <sys/wait.h> +#include <math.h> +#include <signal.h> +#include <pwd.h> +#include <cupsfilters/colord.h> + +/* Logging */ +FILE* logh = NULL; + +void _logv(const char *msg, va_list ap) +{ + if (!logh) + return; + vfprintf(logh, msg, ap); + fflush(logh); +} + +void _log(const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + _logv(msg, ap); + va_end(ap); +} + +void close_log() +{ + if (logh && logh != stderr) + fclose(logh); +} + +int redirect_log_to_stderr() +{ + if (dup2(fileno(logh), fileno(stderr)) < 0) { + _log("Could not dup logh to stderr\n"); + return 0; + } + return 1; +} + +void rip_die(int status, const char *msg, ...) +{ + va_list ap; + + _log("Process is dying with \""); + va_start(ap, msg); + _logv(msg, ap); + va_end(ap); + _log("\", exit stat %d\n", status); + + _log("Cleaning up...\n"); + kill_all_processes(); + + exit(status); +} + + +jobparams_t *job = NULL; + +jobparams_t * get_current_job() +{ + assert(job); + return job; +} + + +dstr_t *postpipe; /* command into which the output of this filter should be piped */ +FILE *postpipe_fh = NULL; + +FILE * open_postpipe() +{ + const char *p; + + if (postpipe_fh) + return postpipe_fh; + + if (isempty(postpipe->data)) + return stdout; + + /* Delete possible '|' symbol in the beginning */ + p = skip_whitespace(postpipe->data); + if (*p && *p == '|') + p += 1; + + if (start_system_process("postpipe", p, &postpipe_fh, NULL) < 0) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, + "Cannot execute postpipe %s\n", postpipe->data); + + return postpipe_fh; +} + + +char printer_model[256] = ""; +const char *accounting_prolog = NULL; +char attrpath[256] = ""; + + +int spooler = SPOOLER_DIRECT; +int dontparse = 0; +int jobhasjcl; +int pdfconvertedtops; + + +/* These variables were in 'dat' before */ +char colorprofile [128]; +char cupsfilter[256]; +char **jclprepend = NULL; +dstr_t *jclappend; + +/* Set debug to 1 to enable the debug logfile for this filter; it will appear + * as defined by LOG_FILE. It will contain status from this filter, plus the + * renderer's stderr output. You can also add a line "debug: 1" to your + * /etc/foomatic/filter.conf to get all your Foomatic filters into debug mode. + * WARNING: This logfile is a security hole; do not use in production. */ +int debug = 0; + +/* Path to the GhostScript which foomatic-rip shall use */ +char gspath[PATH_MAX] = "gs"; + +/* What 'echo' program to use. It needs -e and -n. Linux's builtin +and regular echo work fine; non-GNU platforms may need to install +gnu echo and put gecho here or something. */ +char echopath[PATH_MAX] = "echo"; + +/* CUPS raster drivers are searched here */ +char cupsfilterpath[PATH_MAX] = "/usr/local/lib/cups/filter:" + "/usr/local/libexec/cups/filter:" + "/opt/cups/filter:" + "/usr/lib/cups/filter"; + +char modern_shell[64] = "/bin/bash"; + +void config_set_option(const char *key, const char *value) +{ + if (strcmp(key, "debug") == 0) + debug = atoi(value); + + /* What path to use for filter programs and such. Your printer driver must be + * in the path, as must be the renderer, $enscriptcommand, and possibly other + * stuff. The default path is often fine on Linux, but may not be on other + * systems. */ + else if (strcmp(key, "execpath") == 0 && !isempty(value)) + setenv("PATH", value, 1); + + else if (strcmp(key, "cupsfilterpath") == 0) + strlcpy(cupsfilterpath, value, PATH_MAX); + else if (strcmp(key, "preferred_shell") == 0) + strlcpy(modern_shell, value, 32); + else if (strcmp(key, "gspath") == 0) + strlcpy(gspath, value, PATH_MAX); + else if (strcmp(key, "echo") == 0) + strlcpy(echopath, value, PATH_MAX); +} + +void config_from_file(const char *filename) +{ + FILE *fh; + char line[256]; + char *key, *value; + + fh = fopen(filename, "r"); + if (fh == NULL) + return; /* no error here, only read config file if it is present */ + + while (fgets(line, 256, fh) != NULL) + { + key = strtok(line, " :\t\r\n"); + if (key == NULL || key[0] == '#') + continue; + value = strtok(NULL, " \t\r\n#"); + config_set_option(key, value); + } + fclose(fh); +} + +const char * get_modern_shell() +{ + return modern_shell; +} + +/* returns position in 'str' after the option */ +char * extract_next_option(char *str, char **pagerange, char **key, char **value) +{ + char *p = str; + char quotechar; + + *pagerange = NULL; + *key = NULL; + *value = NULL; + + if (!str) + return NULL; + + /* skip whitespace and commas */ + while (*p && (isspace(*p) || *p == ',')) p++; + + if (!*p) + return NULL; + + /* read the pagerange if we have one */ + if (prefixcmp(p, "even:") == 0 || prefixcmp(p, "odd:") == 0 || isdigit(*p)) { + *pagerange = p; + p = strchr(p, ':'); + if (!p) + return NULL; + *p = '\0'; + p++; + } + + /* read the key */ + if (*p == '\'' || *p == '\"') { + quotechar = *p; + *key = p +1; + p = strchr(*key, quotechar); + if (!p) + return NULL; + } + else { + *key = p; + while (*p && *p != ':' && *p != '=' && *p != ' ') p++; + } + + if (*p != ':' && *p != '=') { /* no value for this option */ + if (!*p) + return NULL; + else if (isspace(*p)) { + *p = '\0'; + return p +1; + } + return p; + } + + *p++ = '\0'; /* remove the separator sign */ + + if (*p == '\"' || *p == '\'') { + quotechar = *p; + *value = p +1; + p = strchr(*value, quotechar); + if (!p) + return NULL; + *p = '\0'; + p++; + } + else { + *value = p; + while (*p && *p != ' ' && *p != ',') p++; + if (*p == '\0') + return NULL; + *p = '\0'; + p++; + } + + return *p ? p : NULL; +} + +/* processes job->optstr */ +void process_cmdline_options() +{ + char *p, *cmdlineopts, *nextopt, *pagerange, *key, *value; + option_t *opt, *opt2; + int optset; + char tmp [256]; + + _log("Printing system options:\n"); + cmdlineopts = strdup(job->optstr->data); + for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value); + key; + nextopt = extract_next_option(nextopt, &pagerange, &key, &value)) + { + /* Consider only options which are not in the PPD file here */ + if ((opt = find_option(key)) != NULL) continue; + if (value) + _log("Pondering option '%s=%s'\n", key, value); + else + _log("Pondering option '%s'\n", key); + + /* "profile" option to supply a color correction profile to a CUPS raster driver */ + if (!strcmp(key, "profile")) { + strlcpy(colorprofile, value, 128); + continue; + } + /* Solaris options that have no reason to be */ + if (!strcmp(key, "nobanner") || !strcmp(key, "dest") || !strcmp(key, "protocol")) + continue; + + if (pagerange) { + snprintf(tmp, 256, "pages:%s", pagerange); + optset = optionset(tmp); + } + else + optset = optionset("userval"); + + if (value) { + if (strcasecmp(key, "media") == 0) { + /* Standard arguments? + media=x,y,z + sides=one|two-sided-long|short-edge + + Rummage around in the media= option for known media, source, + etc types. + We ought to do something sensible to make the common manual + boolean option work when specified as a media= tray thing. + + Note that this fails miserably when the option value is in + fact a number; they all look alike. It's unclear how many + drivers do that. We may have to standardize the verbose + names to make them work as selections, too. */ + + p = strtok(value, ","); + do { + if ((opt = find_option("PageSize")) && option_accepts_value(opt, p)) + option_set_value(opt, optset, p); + else if ((opt = find_option("MediaType")) && option_has_choice(opt, p)) + option_set_value(opt, optset, p); + else if ((opt = find_option("InputSlot")) && option_has_choice(opt, p)) + option_set_value(opt, optset, p); + else if (!strcasecmp(p, "manualfeed")) { + /* Special case for our typical boolean manual + feeder option if we didn't match an InputSlot above */ + if ((opt = find_option("ManualFeed"))) + option_set_value(opt, optset, "1"); + } + else + _log("Unknown \"media\" component: \"%s\".\n", p); + + } while ((p = strtok(NULL, ","))); + } + else if (!strcasecmp(key, "sides")) { + /* Handle the standard duplex option, mostly */ + if (!prefixcasecmp(value, "two-sided")) { + if ((opt = find_option("Duplex"))) { + /* Default to long-edge binding here, for the case that + there is no binding setting */ + option_set_value(opt, optset, "DuplexNoTumble"); + + /* Check the binding: "long edge" or "short edge" */ + if (strcasestr(value, "long-edge")) { + if ((opt2 = find_option("Binding"))) + option_set_value(opt2, optset, "LongEdge"); + else + option_set_value(opt, optset, "DuplexNoTumble"); + } + else if (strcasestr(value, "short-edge")) { + if ((opt2 = find_option("Binding"))) + option_set_value(opt2, optset, "ShortEdge"); + else + option_set_value(opt, optset, "DuplexNoTumble"); + } + } + } + else if (!prefixcasecmp(value, "one-sided")) { + if ((opt = find_option("Duplex"))) + option_set_value(opt, optset, "0"); + } + + /* TODO + We should handle the other half of this option - the + BindEdge bit. Also, are there well-known ipp/cups options + for Collate and StapleLocation? These may be here... + */ + } + else + _log("Unknown option %s=%s.\n", key, value); + } + /* Custom paper size */ + else if ((opt = find_option("PageSize")) && option_set_value(opt, optset, key)) { + /* do nothing, if the value could be set, it has been set */ + } + else + _log("Unknown boolean option \"%s\".\n", key); + } + free(cmdlineopts); + + _log("Options from the PPD file:\n"); + cmdlineopts = strdup(job->optstr->data); + for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value); + key; + nextopt = extract_next_option(nextopt, &pagerange, &key, &value)) + { + /* Consider only PPD file options here */ + if ((opt = find_option(key)) == NULL) continue; + if (value) + _log("Pondering option '%s=%s'\n", key, value); + else + _log("Pondering option '%s'\n", key); + + if (pagerange) { + snprintf(tmp, 256, "pages:%s", pagerange); + optset = optionset(tmp); + + if (opt && (option_get_section(opt) != SECTION_ANYSETUP && + option_get_section(opt) != SECTION_PAGESETUP)) { + _log("This option (%s) is not a \"PageSetup\" or \"AnySetup\" option, so it cannot be restricted to a page range.\n", key); + continue; + } + } + else + optset = optionset("userval"); + + if (value) { + /* Various non-standard printer-specific options */ + if (!option_set_value(opt, optset, value)) { + _log(" invalid choice \"%s\", using \"%s\" instead\n", + value, option_get_value(opt, optset)); + } + } + /* Standard bool args: + landscape; what to do here? + duplex; we should just handle this one OK now? */ + else if (!prefixcasecmp(key, "no")) + option_set_value(opt, optset, "0"); + else + option_set_value(opt, optset, "1"); + } + free(cmdlineopts); +} + +/* Functions to let foomatic-rip fork to do several tasks in parallel. + +To do the filtering without loading the whole file into memory we work +on a data stream, we read the data line by line analyse it to decide what +filters to use and start the filters if we have found out which we need. +We buffer the data only as long as we didn't determing which filters to +use for this piece of data and with which options. There are no temporary +files used. + +foomatic-rip splits into up to 3 parallel processes to do the whole +filtering (listed in the order of the data flow): + + MAIN: Prepare the job auto-detecting the spooler, reading the PPD, + extracting the options from the command line, and parsing + the job data itself. It analyses the job data to check + whether it is PostScript or PDF, it also stuffs PostScript + code from option settings into the PostScript data stream. + It starts the renderer (KID3/KID4) as soon as it knows its + command line and restarts it when page-specific option + settings need another command line or different JCL commands. + KID3: The rendering process. In most cases Ghostscript, "cat" + for native PostScript printers with their manufacturer's + PPD files. + KID4: Put together the JCL commands and the renderer's output + and send all that either to STDOUT or pipe it into the + command line defined with $postpipe. */ + + + +void write_output(void *data, size_t len) +{ + const char *p = (const char *)data; + size_t left = len; + FILE *postpipe = open_postpipe(); + + /* Remove leading whitespace */ + while (isspace(*p++) && left-- > 0) + ; + + fwrite((void *)p, left, 1, postpipe); + fflush(postpipe); +} + +enum FileType { + UNKNOWN_FILE, + PDF_FILE, + PS_FILE +}; + +int guess_file_type(const char *begin, size_t len, int *startpos) +{ + const char * p, * end; + p = begin; + end = begin + len; + + while (p < end) + { + p = memchr(p, '%', end - p); + if (!p) + return UNKNOWN_FILE; + *startpos = p - begin; + if ((end - p) > 2 && !memcmp(p, "%!", 2)) + return PS_FILE; + else if ((end - p) > 7 && !memcmp(p, "%PDF-1.", 7)) + return PDF_FILE; + ++ p; + } + *startpos = 0; + return UNKNOWN_FILE; +} + +/* + * Prints 'filename'. If 'convert' is true, the file will be converted if it is + * not postscript or pdf + */ +int print_file(const char *filename, int convert) +{ + FILE *file; + char buf[8192]; + int type; + int startpos; + size_t n; + int ret; + + if (!strcasecmp(filename, "<STDIN>")) + file = stdin; + else { + file = fopen(filename, "r"); + if (!file) { + _log("Could not open \"%s\" for reading\n", filename); + return 0; + } + } + + n = fread(buf, 1, sizeof(buf) - 1, file); + buf[n] = '\0'; + type = guess_file_type(buf, n, &startpos); + /* We do not use any JCL preceeded to the inputr data, as it is simply + the PJL commands from the PPD file, and these commands we can also + generate, end we even merge them with PJl from the driver */ + /*if (startpos > 0) { + jobhasjcl = 1; + write_output(buf, startpos); + }*/ + if (file != stdin) + rewind(file); + + if (convert) pdfconvertedtops = 0; + + switch (type) { + case PDF_FILE: + _log("Filetype: PDF\n"); + + if (!ppd_supports_pdf()) + { + char pdf2ps_cmd[PATH_MAX]; + FILE *out, *in; + int renderer_pid; + char tmpfilename[PATH_MAX] = ""; + + _log("Driver does not understand PDF input, " + "converting to PostScript\n"); + + pdfconvertedtops = 1; + + /* If reading from stdin, write everything into a temporary file */ + if (file == stdin) + { + int fd; + FILE *tmpfile; + + snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir()); + fd = mkstemp(tmpfilename); + if (fd < 0) { + _log("Could not create temporary file: %s\n", strerror(errno)); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; + } + tmpfile = fdopen(fd, "r+"); + copy_file(tmpfile, stdin, buf, n); + fclose(tmpfile); + + filename = tmpfilename; + } + + /* If the spooler is CUPS we use the pdftops filter of CUPS, + to have always the same PDF->PostScript conversion method + in the whole printing environment, including incompatibility + workarounds in the CUPS filter (so this way we also have to + maintain all these quirks only once). + + The "-dNOINTERPOLATE" makes Ghostscript rendering + significantly faster. + + Note that Ghostscript's "pswrite" output device turns text + into bitmaps and therefore produces huge PostScript files. + In addition, this output device is deprecated. Therefore + we use "ps2write". + + We give priority to Ghostscript here and use Poppler if + Ghostscript is not available. */ + if (spooler == SPOOLER_CUPS) + snprintf(pdf2ps_cmd, PATH_MAX, + "pdftops '%s' '%s' '%s' '%s' '%s' '%s'", + job->id, job->user, job->title, "1", job->optstr->data, + filename); + else + snprintf(pdf2ps_cmd, PATH_MAX, + "gs -q -sstdout=%%stderr -sDEVICE=ps2write -sOutputFile=- " + "-dBATCH -dNOPAUSE -dPARANOIDSAFER -dNOINTERPOLATE %s 2>/dev/null || " + "pdftops -level2 -origpagesizes %s - 2>/dev/null", + filename, filename); + + renderer_pid = start_system_process("pdf-to-ps", pdf2ps_cmd, &in, &out); + + if (dup2(fileno(out), fileno(stdin)) < 0) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, + "Couldn't dup stdout of pdf-to-ps\n"); + + ret = print_file("<STDIN>", 0); + + wait_for_process(renderer_pid); + return ret; + } + + if (file == stdin) + return print_pdf(stdin, buf, n, filename, startpos); + else + return print_pdf(file, NULL, 0, filename, startpos); + + case PS_FILE: + _log("Filetype: PostScript\n"); + if (file == stdin) + return print_ps(stdin, buf, n, filename); + else + return print_ps(file, NULL, 0, filename); + + case UNKNOWN_FILE: + _log("Cannot process \"%s\": Unknown filetype.\n", filename); + return 0; + } + + fclose(file); + return 1; +} + +void signal_terminate(int signal) +{ + rip_die(EXIT_PRINTED, "Caught termination signal: Job canceled\n"); +} + +jobparams_t * create_job() +{ + jobparams_t *job = calloc(1, sizeof(jobparams_t)); + struct passwd *passwd; + + job->optstr = create_dstr(); + job->time = time(NULL); + strcpy(job->copies, "1"); + gethostname(job->host, 128); + passwd = getpwuid(getuid()); + if (passwd) + strlcpy(job->user, passwd->pw_name, 128); + snprintf(job->title, 128, "%s@%s", job->user, job->host); + + return job; +} + +void free_job(jobparams_t *job) +{ + free_dstr(job->optstr); + free(job); +} + +int main(int argc, char** argv) +{ + int i; + int verbose = 0, quiet = 0; + const char* str; + char *p, *filename; + const char *path; + FILE *ppdfh = NULL; + char tmp[1024], gstoraster[256]; + int havefilter, havegstoraster; + dstr_t *filelist; + list_t * arglist; + + arglist = list_create_from_array(argc -1, (void**)&argv[1]); + + if (argc == 2 && (arglist_find(arglist, "--version") || arglist_find(arglist, "--help") || + arglist_find(arglist, "-v") || arglist_find(arglist, "-h"))) { + printf("foomatic-rip of cups-filters version "VERSION"\n"); + printf("\"man foomatic-rip\" for help.\n"); + list_free(arglist); + return 0; + } + + filelist = create_dstr(); + job = create_job(); + + jclprepend = NULL; + jclappend = create_dstr(); + postpipe = create_dstr(); + + options_init(); + + signal(SIGTERM, signal_terminate); + signal(SIGINT, signal_terminate); + + + config_from_file(CONFIG_PATH "/filter.conf"); + + /* Command line options for verbosity */ + if (arglist_remove_flag(arglist, "-v")) + verbose = 1; + if (arglist_remove_flag(arglist, "-q")) + quiet = 1; + if (arglist_remove_flag(arglist, "--debug")) + debug = 1; + + if (debug) { + int fd = mkstemp (LOG_FILE "-XXXXXX.log"); + if (fd != -1) + logh = fdopen(fd, "w"); + else + logh = stderr; + } else if (quiet && !verbose) + logh = NULL; /* Quiet mode, do not log */ + else + logh = stderr; /* Default: log to stderr */ + + /* Start debug logging */ + if (debug) { + /* If we are not in debug mode, we do this later, as we must find out at + first which spooler is used. When printing without spooler we + suppress logging because foomatic-rip is called directly on the + command line and so we avoid logging onto the console. */ + _log("foomatic-rip version "VERSION" running...\n"); + + /* Print the command line only in debug mode, Mac OS X adds very many + options so that CUPS cannot handle the output of the command line + in its log files. If CUPS encounters a line with more than 1024 + characters sent into its log files, it aborts the job with an error. */ + if (spooler != SPOOLER_CUPS) { + _log("called with arguments: "); + for (i = 1; i < argc -1; i++) + _log("\'%s\', ", argv[i]); + _log("\'%s\'\n", argv[i]); + } + } + + if (getenv("PPD")) { + strncpy(job->ppdfile, getenv("PPD"), 256); + spooler = SPOOLER_CUPS; + } + + /* CUPS calls foomatic-rip only with 5 or 6 positional parameters, + not with named options, like for example "-p <string>". */ + if (spooler != SPOOLER_CUPS) { + + if ((str = arglist_get_value(arglist, "-j")) || (str = arglist_get_value(arglist, "-J"))) { + strncpy_omit(job->title, str, 128, omit_shellescapes); + if (!arglist_remove(arglist, "-j")) + arglist_remove(arglist, "-J"); + } + + /* PPD file name given via the command line + allow duplicates, and use the last specified one */ + while ((str = arglist_get_value(arglist, "-p"))) { + strncpy(job->ppdfile, str, 256); + arglist_remove(arglist, "-p"); + } + while ((str = arglist_get_value(arglist, "--ppd"))) { + strncpy(job->ppdfile, str, 256); + arglist_remove(arglist, "--ppd"); + } + + /* Options for spooler-less printing */ + while ((str = arglist_get_value(arglist, "-o"))) { + strncpy_omit(tmp, str, 1024, omit_shellescapes); + dstrcatf(job->optstr, "%s ", tmp); + arglist_remove(arglist, "-o"); + /* We print without spooler */ + spooler = SPOOLER_DIRECT; + } + + /* Printer for spooler-less printing */ + if ((str = arglist_get_value(arglist, "-d"))) { + strncpy_omit(job->printer, str, 256, omit_shellescapes); + arglist_remove(arglist, "-d"); + } + + /* Printer for spooler-less printing */ + if ((str = arglist_get_value(arglist, "-P"))) { + strncpy_omit(job->printer, str, 256, omit_shellescapes); + arglist_remove(arglist, "-P"); + } + + } + + /* spooler specific initialization */ + switch (spooler) { + + case SPOOLER_CUPS: + init_cups(arglist, filelist, job); + break; + + case SPOOLER_DIRECT: + init_direct(arglist, filelist, job); + break; + } + + /* Files to be printed (can be more than one for spooler-less printing) */ + /* Empty file list -> print STDIN */ + dstrtrim(filelist); + if (filelist->len == 0) + dstrcpyf(filelist, "<STDIN>"); + + /* Check filelist */ + p = strtok(strdup(filelist->data), " "); + while (p) { + if (strcmp(p, "<STDIN>") != 0) { + if (p[0] == '-') + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Invalid argument: %s", p); + else if (access(p, R_OK) != 0) { + _log("File %s does not exist/is not readable\n", p); + strclr(p); + } + } + p = strtok(NULL, " "); + } + + /* When we print without spooler do not log onto STDERR unless + the "-v" ('Verbose') is set or the debug mode is used */ + if (spooler == SPOOLER_DIRECT && !verbose && !debug) { + if (logh && logh != stderr) + fclose(logh); + logh = NULL; + } + + /* If we are in debug mode, we do this earlier. */ + if (!debug) { + _log("foomatic-rip version " VERSION " running...\n"); + /* Print the command line only in debug mode, Mac OS X adds very many + options so that CUPS cannot handle the output of the command line + in its log files. If CUPS encounters a line with more than 1024 + characters sent into its log files, it aborts the job with an error. */ + if (spooler != SPOOLER_CUPS) { + _log("called with arguments: "); + for (i = 1; i < argc -1; i++) + _log("\'%s\', ", argv[i]); + _log("\'%s\'\n", argv[i]); + } + } + + /* PPD File */ + /* Load the PPD file and build a data structure for the renderer's + command line and the options */ + if (!(ppdfh = fopen(job->ppdfile, "r"))) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Unable to open PPD file %s\n", job->ppdfile); + + read_ppd_file(job->ppdfile); + + /* We do not need to parse the PostScript job when we don't have + any options. If we have options, we must check whether the + default settings from the PPD file are valid and correct them + if nexessary. */ + if (option_count() == 0) { + /* We don't have any options, so we do not need to parse the + PostScript data */ + dontparse = 1; + } + + /* Is our PPD for a CUPS raster driver */ + if (!isempty(cupsfilter)) { + /* Search the filter in cupsfilterpath + The %Y is a placeholder for the option settings */ + havefilter = 0; + path = cupsfilterpath; + while ((path = strncpy_tochar(tmp, path, 1024, ":"))) { + strlcat(tmp, "/", 1024); + strlcat(tmp, cupsfilter, 1024); + if (access(tmp, X_OK) == 0) { + havefilter = 1; + strlcpy(cupsfilter, tmp, 256); + strlcat(cupsfilter, " 0 '' '' 0 '%Y%X'", 256); + break; + } + } + + if (!havefilter) { + /* We do not have the required filter, so we assume that + rendering this job is supposed to be done on a remote + server. So we do not define a renderer command line and + embed only the option settings (as we had a PostScript + printer). This way the settings are taken into account + when the job is rendered on the server.*/ + _log("CUPS filter for this PPD file not found - assuming that job will " + "be rendered on a remote server. Only the PostScript of the options" + "will be inserted into the PostScript data stream.\n"); + } + else { + /* use gstoraster filter if available, otherwise run Ghostscript + directly */ + havegstoraster = 0; + path = cupsfilterpath; + while ((path = strncpy_tochar(tmp, path, 1024, ":"))) { + strlcat(tmp, "/gstoraster", 1024); + if (access(tmp, X_OK) == 0) { + havegstoraster = 1; + strlcpy(gstoraster, tmp, 256); + strlcat(gstoraster, " 0 '' '' 0 '%X'", 256); + break; + } + } + if (!havegstoraster) { + const char **qualifier = NULL; + const char *icc_profile = NULL; + + qualifier = get_ppd_qualifier(); + _log("INFO: Using qualifer: '%s.%s.%s'\n", + qualifier[0], qualifier[1], qualifier[2]); + + /* ask colord for the profile */ + icc_profile = colord_get_profile_for_device_id ((const char *) getenv("PRINTER"), + qualifier); + + /* fall back to PPD */ + if (icc_profile == NULL) { + _log("INFO: need to look in PPD for matching qualifer\n"); + icc_profile = get_icc_profile_for_qualifier(qualifier); + } + + if (icc_profile != NULL) + snprintf(cmd, sizeof(cmd), + "-sOutputICCProfile='%s'", icc_profile); + else + cmd[0] = '\0'; + + snprintf(gstoraster, sizeof(gstoraster), "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOINTERPOLATE -dNOMEDIAATTRS -sDEVICE=cups %s -sOutputFile=- -", cmd); + } + + /* build Ghostscript/CUPS driver command line */ + snprintf(cmd, 1024, "%s | %s", gstoraster, cupsfilter); + _log("INFO: Using command line: %s\n", cmd); + + /* Set environment variables */ + setenv("PPD", job->ppdfile, 1); + } + } + + /* Was the RIP command line defined in the PPD file? If not, we assume a PostScript printer + and do not render/translate the input data */ + if (isempty(cmd)) { + strcpy(cmd, "cat%A%B%C%D%E%F%G%H%I%J%K%L%M%Z"); + if (dontparse) { + /* No command line, no options, we have a raw queue, don't check + whether the input is PostScript, simply pass the input data to + the backend.*/ + dontparse = 2; + strcpy(printer_model, "Raw queue"); + } + } + + /* Summary for debugging */ + _log("\nParameter Summary\n" + "-----------------\n\n" + "Spooler: %s\n" + "Printer: %s\n" + "Shell: %s\n" + "PPD file: %s\n" + "ATTR file: %s\n" + "Printer model: %s\n", + spooler_name(spooler), job->printer, get_modern_shell(), job->ppdfile, attrpath, printer_model); + /* Print the options string only in debug mode, Mac OS X adds very many + options so that CUPS cannot handle the output of the option string + in its log files. If CUPS encounters a line with more than 1024 characters + sent into its log files, it aborts the job with an error.*/ + if (debug || spooler != SPOOLER_CUPS) + _log("Options: %s\n", job->optstr->data); + _log("Job title: %s\n", job->title); + _log("File(s) to be printed:\n"); + _log("%s\n\n", filelist->data); + if (getenv("GS_LIB")) + _log("Ghostscript extra search path ('GS_LIB'): %s\n", getenv("GS_LIB")); + + /* Process options from command line */ + optionset_copy_values(optionset("default"), optionset("userval")); + process_cmdline_options(); + + /* no postpipe for CUPS , even if one is defined in the PPD file */ + if (spooler == SPOOLER_CUPS ) + dstrclear(postpipe); + + if (postpipe->len) + _log("Ouput will be redirected to:\n%s\n", postpipe); + + + filename = strtok_r(filelist->data, " ", &p); + while (filename) { + _log("\n================================================\n\n" + "File: %s\n\n" + "================================================\n\n", filename); + + /* Do we have a raw queue? */ + if (dontparse == 2) { + /* Raw queue, simply pass the input into the postpipe (or to STDOUT + when there is no postpipe) */ + _log("Raw printing, executing \"cat %s\"\n\n"); + snprintf(tmp, 1024, "cat %s", postpipe->data); + run_system_process("raw-printer", tmp); + continue; + } + + /* First, for arguments with a default, stick the default in as + the initial value for the "header" option set, this option set + consists of the PPD defaults, the options specified on the + command line, and the options set in the header part of the + PostScript file (all before the first page begins). */ + optionset_copy_values(optionset("userval"), optionset("header")); + + if (!print_file(filename, 1)) + rip_die(EXIT_PRNERR_NORETRY, "Could not print file %s\n", filename); + filename = strtok_r(NULL, " ", &p); + } + + /* Close the last input file */ + fclose(stdin); + + /* TODO dump everything in $dat when debug is turned on (necessary?) */ + + _log("\nClosing foomatic-rip.\n"); + + + /* Cleanup */ + free_job(job); + free_dstr(filelist); + options_free(); + close_log(); + + argv_free(jclprepend); + free_dstr(jclappend); + + list_free(arglist); + + return EXIT_PRINTED; +} + diff --git a/filter/foomatic-rip/foomaticrip.h b/filter/foomatic-rip/foomaticrip.h new file mode 100644 index 000000000..875e6b8fa --- /dev/null +++ b/filter/foomatic-rip/foomaticrip.h @@ -0,0 +1,118 @@ +/* foomaticrip.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef foomatic_h +#define foomatic_h + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "config.h" + +#include <stddef.h> +#include <stdio.h> +#include <time.h> + +/* This is the location of the debug logfile (and also the copy of the + * processed PostScript data) in case you have enabled debugging above. + * The logfile will get the extension ".log", the PostScript data ".ps". + */ +#ifndef LOG_FILE +#define LOG_FILE "/tmp/foomatic-rip" +#endif + + +/* Constants used by this filter + * + * Error codes, as some spoolers behave different depending on the reason why + * the RIP failed, we return an error code. + */ +#define EXIT_PRINTED 0 /* file was printed normally */ +#define EXIT_PRNERR 1 /* printer error occured */ +#define EXIT_PRNERR_NORETRY 2 /* printer error with no hope of retry */ +#define EXIT_JOBERR 3 /* job is defective */ +#define EXIT_SIGNAL 4 /* terminated after catching signal */ +#define EXIT_ENGAGED 5 /* printer is otherwise engaged (connection refused) */ +#define EXIT_STARVED 6 /* starved for system resources */ +#define EXIT_PRNERR_NORETRY_ACCESS_DENIED 7 /* bad password? bad port permissions? */ +#define EXIT_PRNERR_NOT_RESPONDING 8 /* just doesn't answer at all (turned off?) */ +#define EXIT_PRNERR_NORETRY_BAD_SETTINGS 9 /* interface settings are invalid */ +#define EXIT_PRNERR_NO_SUCH_ADDRESS 10 /* address lookup failed, may be transient */ +#define EXIT_PRNERR_NORETRY_NO_SUCH_ADDRESS 11 /* address lookup failed, not transient */ +#define EXIT_INCAPABLE 50 /* printer wants (lacks) features or resources */ + + +/* Supported spoolers are currently: + * + * cups - CUPS - Common Unix Printing System + * direct - Direct, spooler-less printing + */ +#define SPOOLER_CUPS 1 +#define SPOOLER_DIRECT 2 + +/* The spooler from which foomatic-rip was called. set in main() */ +extern int spooler; + + +#define PATH_MAX 65536 + +typedef struct { + char printer[256]; + char id[128]; + char user[128]; + char host[128]; + char title[128]; + char ppdfile[256]; + char copies[128]; + int rbinumcopies; + struct dstr *optstr; + time_t time; +} jobparams_t; + + +jobparams_t * get_current_job(); + +void _log(const char* msg, ...); +int redirect_log_to_stderr(); +void rip_die(int status, const char *msg, ...); + +const char * get_modern_shell(); +FILE * open_postpipe(); + +extern struct dstr *currentcmd; +extern struct dstr *jclappend; +extern char **jclprepend; +extern int jobhasjcl; +extern const char *accounting_prolog; +extern char cupsfilterpath[PATH_MAX]; +extern int debug; +extern int do_docs; +extern char printer_model[]; +extern int dontparse; +extern int pdfconvertedtops; +extern char gspath[PATH_MAX]; +extern char echopath[PATH_MAX]; + +#endif + diff --git a/filter/foomatic-rip/options.c b/filter/foomatic-rip/options.c new file mode 100644 index 000000000..7b5daf0dc --- /dev/null +++ b/filter/foomatic-rip/options.c @@ -0,0 +1,2279 @@ +/* options.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "foomaticrip.h" +#include "options.h" +#include "util.h" +#include <stdlib.h> +#include <ctype.h> +#include <assert.h> +#include <regex.h> +#include <string.h> +#include <math.h> + +/* qualifier -> filename mapping entry */ +typedef struct icc_mapping_entry_s { + char *qualifier; + char *filename; +} icc_mapping_entry_t; + +/* Values from foomatic keywords in the ppd file */ +char printer_model [256]; +char printer_id [256]; +char driver [128]; +char cmd [4096]; +char cmd_pdf [4096]; +dstr_t *postpipe = NULL; /* command into which the output of this + filter should be piped */ +int ps_accounting = 1; +char cupsfilter [256]; +int jobentitymaxlen = 0; +int userentitymaxlen = 0; +int hostentitymaxlen = 0; +int titleentitymaxlen = 0; +int optionsentitymaxlen = 0; + +/* JCL prefix to put before the JCL options + (Can be modified by a "*JCLBegin:" keyword in the ppd file): */ +char jclbegin[256] = "\033%-12345X@PJL\n"; + +/* JCL command to switch the printer to the PostScript interpreter + (Can be modified by a "*JCLToPSInterpreter:" keyword in the PPD file): */ +char jcltointerpreter[256] = ""; + +/* JCL command to close a print job + (Can be modified by a "*JCLEnd:" keyword in the PPD file): */ +char jclend[256] = "\033%-12345X@PJL RESET\n"; + +/* Prefix for starting every JCL command + (Can be modified by "*FoomaticJCLPrefix:" keyword in the PPD file): */ +char jclprefix[256] = "@PJL "; +int jclprefixset = 0; + +dstr_t *prologprepend; +dstr_t *setupprepend; +dstr_t *pagesetupprepend; + + +list_t *qualifier_data = NULL; +char **qualifier = NULL; + +option_t *optionlist = NULL; +option_t *optionlist_sorted_by_order = NULL; + +int optionset_alloc, optionset_count; +char **optionsets; + + +const char * get_icc_profile_for_qualifier(const char **qualifier) +{ + char tmp[1024]; + char *profile = NULL; + listitem_t *i; + icc_mapping_entry_t *entry; + + /* no data */ + if (qualifier_data == NULL) + goto out; + + /* search list for qualifier */ + snprintf(tmp, sizeof(tmp), "%s.%s.%s", + qualifier[0], qualifier[1], qualifier[2]); + for (i = qualifier_data->first; i != NULL; i = i->next) { + entry = (icc_mapping_entry_t *) i->data; + if (strcmp(entry->qualifier, tmp) == 0) { + profile = entry->filename; + break; + } + } +out: + return profile; +} + +/* a selector is a general tri-dotted specification. + * The 2nd and 3rd elements of the qualifier are optionally modified by + * cupsICCQualifier2 and cupsICCQualifier3: + * + * [Colorspace].[{cupsICCQualifier2}].[{cupsICCQualifier3}] + */ +const char ** +get_ppd_qualifier () +{ + return (const char**) qualifier; +} + +const char * type_name(int type) +{ + switch (type) { + case TYPE_NONE: + return "none"; + case TYPE_ENUM: + return "enum"; + case TYPE_PICKMANY: + return "pickmany"; + case TYPE_BOOL: + return "bool"; + case TYPE_INT: + return "int"; + case TYPE_FLOAT: + return "float"; + case TYPE_STRING: + return "string"; + }; + _log("type '%d' does not exist\n", type); + return NULL; +} + +int type_from_string(const char *typestr) +{ + int type = TYPE_NONE; + + /* Official PPD options */ + if (!strcmp(typestr, "PickOne")) + type = TYPE_ENUM; + else if (!strcmp(typestr, "PickMany")) + type = TYPE_PICKMANY; + else if (!strcmp(typestr, "Boolean")) + type = TYPE_BOOL; + + /* FoomaticRIPOption */ + else if (strcasecmp(typestr, "enum") == 0) + type = TYPE_ENUM; + else if (strcasecmp(typestr, "pickmany") == 0) + type = TYPE_PICKMANY; + else if (strcasecmp(typestr, "bool") == 0) + type = TYPE_BOOL; + else if (strcasecmp(typestr, "int") == 0) + type = TYPE_INT; + else if (strcasecmp(typestr, "float") == 0) + type = TYPE_FLOAT; + else if (strcasecmp(typestr, "string") == 0) + type = TYPE_STRING; + else if (strcasecmp(typestr, "password") == 0) + type = TYPE_PASSWORD; + + return type; +} + +char style_from_string(const char *style) +{ + char r = '\0'; + if (strcmp(style, "PS") == 0) + r = 'G'; + else if (strcmp(style, "CmdLine") == 0) + r = 'C'; + else if (strcmp(style, "JCL") == 0) + r = 'J'; + else if (strcmp(style, "Composite") == 0) + r = 'X'; + return r; +} + +int section_from_string(const char *value) +{ + if (!strcasecmp(value, "AnySetup")) + return SECTION_ANYSETUP; + else if (!strcasecmp(value, "PageSetup")) + return SECTION_PAGESETUP; + else if (!strcasecmp(value, "Prolog")) + return SECTION_PROLOG; + else if (!strcasecmp(value, "DocumentSetup")) + return SECTION_DOCUMENTSETUP; + else if (!strcasecmp(value, "JCLSetup")) + return SECTION_JCLSETUP; + + _log("Unknown section: \"%s\"\n", value); + return 0; +} + +void options_init() +{ + optionset_alloc = 8; + optionset_count = 0; + optionsets = calloc(optionset_alloc, sizeof(char *)); + + prologprepend = create_dstr(); + setupprepend = create_dstr(); + pagesetupprepend = create_dstr(); +} + +static void free_param(param_t *param) +{ + if (param->allowedchars) { + regfree(param->allowedchars); + free(param->allowedchars); + } + + if (param->allowedregexp) { + regfree(param->allowedregexp); + free(param->allowedregexp); + } + + free(param); +} + +/* + * Values + */ + +static void free_value(value_t *val) +{ + if (val->value) + free(val->value); + free(val); +} + + +/* + * Options + */ +static void free_option(option_t *opt) +{ + choice_t *choice; + param_t *param; + value_t *value; + + free(opt->custom_command); + free(opt->proto); + + while (opt->valuelist) { + value = opt->valuelist; + opt->valuelist = opt->valuelist->next; + free_value(value); + } + while (opt->choicelist) { + choice = opt->choicelist; + opt->choicelist = opt->choicelist->next; + free(choice); + } + while (opt->paramlist) { + param = opt->paramlist; + opt->paramlist = opt->paramlist->next; + free_param(param); + } + if (opt->foomatic_param) + free_param(opt->foomatic_param); + + free(opt); +} + +void options_free() +{ + option_t *opt; + int i; + listitem_t *item; + icc_mapping_entry_t *entry; + + for (i = 0; i < optionset_count; i++) + free(optionsets[i]); + free(optionsets); + optionsets = NULL; + optionset_alloc = 0; + optionset_count = 0; + + if (qualifier_data) { + for (item = qualifier_data->first; item != NULL; item = item->next) { + entry = (icc_mapping_entry_t *) item->data; + free(entry->qualifier); + free(entry->filename); + free(entry); + } + list_free(qualifier_data); + } + + for (i=0; i<3; i++) + free(qualifier[i]); + free(qualifier); + + while (optionlist) { + opt = optionlist; + optionlist = optionlist->next; + free_option(opt); + } + + if (postpipe) + free_dstr(postpipe); + + free_dstr(prologprepend); + free_dstr(setupprepend); + free_dstr(pagesetupprepend); +} + +size_t option_count() +{ + option_t *opt; + size_t cnt = 0; + + for (opt = optionlist; opt; opt = opt->next) + cnt++; + return cnt; +} + +option_t * find_option(const char *name) +{ + option_t *opt; + + /* PageRegion and PageSize are the same options, just store one of them */ + if (!strcasecmp(name, "PageRegion")) + return find_option("PageSize"); + + for (opt = optionlist; opt; opt = opt->next) { + if ((!strcasecmp(opt->name, name)) || + ((!strcasecmp(opt->name, &name[2])) && + (!prefixcasecmp(name, "no")))) + return opt; + } + return NULL; +} + +option_t * assure_option(const char *name) +{ + option_t *opt, *last; + + if ((opt = find_option(name))) + return opt; + + opt = calloc(1, sizeof(option_t)); + + /* PageRegion and PageSize are the same options, just store one of them */ + if (!strcmp(name, "PageRegion")) + strlcpy(opt->name, "PageSize", 128); + else + strlcpy(opt->name, name, 128); + + /* set varname */ + strcpy(opt->varname, opt->name); + strrepl(opt->varname, "-/.", '_'); + + /* Default execution style is 'G' (PostScript) since all arguments for + which we don't find "*Foomatic..." keywords are usual PostScript options */ + opt->style = 'G'; + + opt->type = TYPE_NONE; + + /* append opt to optionlist */ + if (optionlist) { + for (last = optionlist; last->next; last = last->next); + last->next = opt; + } + else + optionlist = opt; + + /* prepend opt to optionlist_sorted_by_order + (0 is always at the beginning) */ + if (optionlist_sorted_by_order) { + opt->next_by_order = optionlist_sorted_by_order; + optionlist_sorted_by_order = opt; + } + else { + optionlist_sorted_by_order = opt; + } + + _log("Added option %s\n", opt->name); + return opt; +} + +/* This functions checks if "opt" is named "name", or if it has any + alternative names "name" (e.g. PageSize / PageRegion) */ +int option_has_name(option_t *opt, const char *name) +{ + if (!strcmp(opt->name, name)) + return 1; + + if (!strcmp(opt->name, "PageSize") && !strcmp(name, "PageRegion")) + return 1; + + return 0; +} + +int option_is_composite(option_t *opt) +{ + return opt ? (opt->style == 'X') : 0; +} + +int option_is_ps_command(option_t *opt) +{ + return opt->style == 'G'; +} + +int option_is_jcl_arg(option_t *opt) +{ + return opt->style == 'J'; +} + +int option_is_commandline_arg(option_t *opt) +{ + return opt->style == 'C'; +} + +int option_get_section(option_t *opt) +{ + return opt->section; +} + +static value_t * option_find_value(option_t *opt, int optionset) +{ + value_t *val; + + if (!opt) + return NULL; + + for (val = opt->valuelist; val; val = val->next) { + if (val->optionset == optionset) + return val; + } + return NULL; +} + +static value_t * option_assure_value(option_t *opt, int optionset) +{ + value_t *val, *last; + val = option_find_value(opt, optionset); + if (!val) { + val = calloc(1, sizeof(value_t)); + val->optionset = optionset; + + /* append to opt->valuelist */ + if (opt->valuelist) { + for (last = opt->valuelist; last->next; last = last->next); + last->next = val; + } + else + opt->valuelist = val; + } + return val; +} + +static param_t * option_find_param_index(option_t *opt, const char *name, int *idx) +{ + param_t *param; + int i; + for (param = opt->paramlist, i = 0; param; param = param->next, i += 1) { + if (!strcasecmp(param->name, name)) { + if (idx) + *idx = i; + return param; + } + } + if (idx) + *idx = -1; + return 0; +} + +static choice_t * option_find_choice(option_t *opt, const char *name) +{ + choice_t *choice; + assert(opt && name); + for (choice = opt->choicelist; choice; choice = choice->next) { + if (!strcasecmp(choice->value, name)) + return choice; + } + return NULL; +} + +void free_paramvalues(option_t *opt, char **paramvalues) +{ + int i; + if (!paramvalues) + return; + for (i = 0; i < opt->param_count; i++) + free(paramvalues[i]); + free(paramvalues); +} + +char * get_valid_param_string(option_t *opt, param_t *param, const char *str) +{ + char *result; + int i, imin, imax; + float f, fmin, fmax; + size_t len; + + switch (param->type) { + case TYPE_INT: + i = atoi(str); + imin = !isempty(param->min) ? atoi(param->min) : -999999; + imax = !isempty(param->max) ? atoi(param->max) : 1000000; + if (i < imin) { + _log("Value \"%s\" for option \"%s\", parameter \"%s\" is smaller than the minimum value \"%d\"\n", + str, opt->name, param->name, imin); + return NULL; + } + else if (i > imax) { + _log("Value \"%s\" for option \"%s\", parameter \"%s\" is larger than the maximum value \"%d\"\n", + str, opt->name, param->name, imax); + return NULL; + } + result = malloc(32); + snprintf(result, 32, "%d", i); + return result; + + case TYPE_FLOAT: + case TYPE_CURVE: + case TYPE_INVCURVE: + case TYPE_POINTS: + f = atof(str); + fmin = !isempty(param->min) ? atof(param->min) : -999999.0; + fmax = !isempty(param->max) ? atof(param->max) : 1000000.0; + if (f < fmin) { + _log("Value \"%s\" for option \"%s\", parameter \"%s\" is smaller than the minimum value \"%d\"\n", + str, opt->name, param->name, fmin); + return NULL; + } + else if (f > fmax) { + _log("Value \"%s\" for option \"%s\", parameter \"%s\" is larger than the maximum value \"%d\"\n", + str, opt->name, param->name, fmax); + return NULL; + } + result = malloc(32); + snprintf(result, 32, "%f", f); + return result; + + case TYPE_STRING: + case TYPE_PASSWORD: + case TYPE_PASSCODE: + if (param->allowedchars && + regexec(param->allowedchars, str, 0, NULL, 0) != 0) { + _log("Custom string \"%s\" for \"%s\", parameter \"%s\" contains illegal characters.\n", + str, opt->name, param->name); + return NULL; + } + if (param->allowedregexp && + regexec(param->allowedregexp, str, 0, NULL, 0) != 0) { + _log("Custom string \"%s\" for \"%s\", parameter \"%s\" does not match the allowed regexp.\n", + str, opt->name, param->name); + return NULL; + } + len = strlen(str); + if (!isempty(param->min) && len < atoi(param->min)) { + _log("Custom value \"%s\" is too short for option \"%s\", parameter \"%s\".\n", + str, opt->name, param->name); + return NULL; + } + if (!isempty(param->max) && len > atoi(param->max)) { + _log("Custom value \"%s\" is too long for option \"%s\", parameter \"%s\".\n", + str, opt->name, param->name); + return NULL; + } + return strdup(str); + } + return NULL; +} + +char * get_valid_param_string_int(option_t *opt, param_t *param, int value) +{ + char str[20]; + snprintf(str, 20, "%d", value); + return get_valid_param_string(opt, param, str); +} + +char * get_valid_param_string_float(option_t *opt, param_t *param, float value) +{ + char str[20]; + snprintf(str, 20, "%f", value); + return get_valid_param_string(opt, param, str); +} + +float convert_to_points(float f, const char *unit) +{ + if (!strcasecmp(unit, "pt")) + return roundf(f); + if (!strcasecmp(unit, "in")) + return roundf(f * 72.0); + if (!strcasecmp(unit, "cm")) + return roundf(f * 72.0 / 2.54); + if (!strcasecmp(unit, "mm")) + return roundf(f * 72.0 / 25.4); + + _log("Unknown unit: \"%s\"\n", unit); + return roundf(f); +} + +static char ** paramvalues_from_string(option_t *opt, const char *str) +{ + char ** paramvalues; + int n, i; + param_t *param; + char *copy, *cur, *p; + float width, height; + char unit[3]; + + if (!strcmp(opt->name, "PageSize")) + { + if (startswith(str, "Custom.")) + str = &str[7]; + /* 'unit' is optional, if it is not given, 'pt' is assumed */ + n = sscanf(str, "%fx%f%2s", &width, &height, unit); + if (n > 1) { + if (n == 3) { + width = convert_to_points(width, unit); + height = convert_to_points(height, unit); + } + paramvalues = calloc(opt->param_count, sizeof(char*)); + for (param = opt->paramlist, i = 0; param; param = param->next, i++) { + if (!strcasecmp(param->name, "width")) + paramvalues[i] = get_valid_param_string_int(opt, param, (int)width); + else if (!strcasecmp(param->name, "height")) + paramvalues[i] = get_valid_param_string_int(opt, param, (int)height); + else + paramvalues[i] = !isempty(param->min) ? param->min : "-999999"; + if (!paramvalues[i]) { + free_paramvalues(opt, paramvalues); + return NULL; + } + } + return paramvalues; + } + } + + if (opt->param_count == 1) { + paramvalues = malloc(sizeof(char*)); + paramvalues[0] = get_valid_param_string(opt, opt->paramlist, + startswith(str, "Custom.") ? &str[7] : str); + if (!paramvalues[0]) { + free(paramvalues); + return NULL; + } + } + else { + if (!(p = strchr(str, '{'))) + return NULL; + paramvalues = calloc(opt->param_count, sizeof(char*)); + copy = strdup(p +1); + for (cur = strtok(copy, " \t}"); cur; cur = strtok(NULL, " \t}")) { + p = strchr(cur, '='); + if (!p) + continue; + *p++ = '\0'; + if ((param = option_find_param_index(opt, cur, &i))) + paramvalues[i] = get_valid_param_string(opt, param, p); + else + _log("Could not find param \"%s\" for option \"%s\"\n", + cur, opt->name); + } + free(copy); + + /* check if all params have been set */ + for (i = 0; i < opt->param_count; i++) { + if (!paramvalues[i]) { + free_paramvalues(opt, paramvalues); + return NULL; + } + } + } + return paramvalues; +} + +char * paramvalues_to_string(option_t *opt, char **paramvalues) +{ + int i; + param_t *param; + dstr_t *res = create_dstr(); + char *data; + + if (opt->param_count < 1) { + free (res); + return NULL; + } + + if (opt->param_count == 1) { + param = opt->paramlist; + dstrcpyf(res, "Custom.%s", paramvalues[0]); + } + else { + dstrcpyf(res, "{%s=%s", opt->paramlist->name, paramvalues[0]); + param = opt->paramlist->next; + i = 1; + while (param) { + dstrcatf(res, " %s=%s", param->name, paramvalues[i]); + i++; + param = param->next; + } + dstrcat(res, "}"); + } + /* only free dstr struct, NOT the string data */ + data = res->data; + free(res); + return data; +} + +char * get_valid_value_string(option_t *opt, const char *value) +{ + char *res; + choice_t *choice; + char **paramvalues; + + if (!value) + return NULL; + + if (startswith(value, "From") && option_is_composite(find_option(&value[4]))) + return strdup(value); + + if (opt->type == TYPE_BOOL) { + if (is_true_string(value)) + return strdup("1"); + else if (is_false_string(value)) + return strdup("0"); + else { + _log("Could not interpret \"%s\" as boolean value for option \"%s\".\n", value, opt->name); + return NULL; + } + } + + /* Check if "value" is a predefined choice (except for "Custom", which is + * not really a predefined choice, but an error if used without further + * parameters) */ + if ((strcmp(value, "Custom") != 0 || strcmp(opt->name, "PageSize") == 0) && + (choice = option_find_choice(opt, value))) + return strdup(choice->value); + + if (opt->type == TYPE_ENUM) { + if (!strcasecmp(value, "none")) + return strdup("None"); + + /* + * CUPS assumes that options with the choices "Yes", "No", "On", "Off", + * "True", or "False" are boolean options and maps "-o Option=On" to + * "-o Option" and "-o Option=Off" to "-o noOption", which foomatic-rip + * maps to "0" and "1". So when "0" or "1" is unavailable in the + * option, we try "Yes", "No", "On", "Off", "True", and "False". + */ + if (is_true_string(value)) { + for (choice = opt->choicelist; choice; choice = choice->next) { + if (is_true_string(choice->value)) + return strdup(choice->value); + } + } + else if (is_false_string(value)) { + for (choice = opt->choicelist; choice; choice = choice->next) { + if (is_false_string(choice->value)) + return strdup(choice->value); + } + } + } + + /* Custom value */ + if (opt->paramlist) { + paramvalues = paramvalues_from_string(opt, value); + if (paramvalues) { + res = paramvalues_to_string(opt, paramvalues); + free(paramvalues); + return (startswith(res, "Custom.") ? strdup(&res[7]) : strdup(res)); + } + } + else if (opt->foomatic_param) + return get_valid_param_string(opt, opt->foomatic_param, + startswith(value, "Custom.") ? &value[7] : value); + + /* Return the default value */ + return NULL; +} + +/* Returns the current value for 'opt' in 'optionset'. */ +const char * option_get_value(option_t *opt, int optionset) +{ + value_t *val = option_find_value(opt, optionset); + return val ? val->value : NULL; +} + +/* Returns non-zero if the foomatic prototype should be used for that + * optionset, otherwise the custom_command will be used */ +int option_use_foomatic_prototype(option_t *opt) +{ + /* Only PostScript and JCL options can be CUPS custom options */ + if (!option_is_ps_command(opt) && !option_is_jcl_arg(opt)) + return 1; + + /* if only one of them exists, take that one */ + if (opt->custom_command && !opt->proto) + return 0; + if (!opt->custom_command && opt->proto) + return 1; + return 0; +} + +void build_foomatic_custom_command(dstr_t *cmd, option_t *opt, const char *values) +{ + if (!opt->proto && !strcmp(opt->name, "PageSize")) + { + choice_t *choice = option_find_choice(opt, "Custom"); + char ** paramvalues = paramvalues_from_string(opt, values); + char width[30], height[30]; + int pos; + + assert(choice); + + /* Get rid of the trailing ".00000", it confuses ghostscript */ + snprintf(width, 20, "%d", atoi(paramvalues[0])); + snprintf(height, 20, "%d", atoi(paramvalues[1])); + + dstrcpy(cmd, choice->command); + + if ((pos = dstrreplace(cmd, "%0", width, 0)) < 0) + pos = dstrreplace(cmd, "0", width, 0); + + if (dstrreplace(cmd, "%1", height, pos) < 0) + dstrreplace(cmd, "0", height, pos); + + free_paramvalues(opt, paramvalues); + } + else + { + dstrcpy(cmd, opt->proto); + /* use replace instead of printf-style because opt->proto could contain + other format strings */ + dstrreplace(cmd, "%s", values, 0); + } +} + +void build_cups_custom_ps_command(dstr_t *cmd, option_t *opt, const char *values) +{ + param_t *param; + int i; + char **paramvalues = paramvalues_from_string(opt, values); + + dstrclear(cmd); + for (param = opt->paramlist, i = 0; param; param = param->next, i++) + dstrcatf(cmd, "%s ", paramvalues[i]); + dstrcat(cmd, opt->custom_command); + free_paramvalues(opt, paramvalues); +} + +void build_cups_custom_jcl_command(dstr_t *cmd, option_t *opt, const char *values) +{ + param_t *param; + int i; + char orderstr[8]; + char **paramvalues = paramvalues_from_string(opt, values); + + dstrcpy(cmd, opt->custom_command); + for (param = opt->paramlist, i = 0; param; param = param->next, i++) { + snprintf(orderstr, 8, "\\%d", param->order); + dstrreplace(cmd, orderstr, paramvalues[i], 0); + } + free_paramvalues(opt, paramvalues); +} + +int composite_get_command(dstr_t *cmd, option_t *opt, int optionset, int section) +{ + char *copy, *cur, *p; + option_t *dep; + const char * valstr; + dstr_t *depcmd; + + dstrclear(cmd); + if (!option_is_composite(opt)) + return 0; + + if (!(valstr = option_get_value(opt, optionset))) + return 0; + + depcmd = create_dstr(); + copy = strdup(valstr); + /* Dependent options have been set to the right value in composite_set_values, + so just find out which options depend on this composite and get their commands + for "optionset" with option_get_command() */ + for (cur = strtok(copy, " \t"); cur; cur = strtok(NULL, " \t")) { + dstrclear(depcmd); + if ((p = strchr(cur, '='))) { + *p++ = '\0'; + if ((dep = find_option(cur))) + option_get_command(depcmd, dep, optionset, section); + } + else if (startswith(cur, "no") || startswith(cur, "No")) { + if ((dep = find_option(&cur[2]))) + option_get_command(depcmd, dep, optionset, section); + } + else { + if ((dep = find_option(cur))) + option_get_command(depcmd, dep, optionset, section); + } + if (depcmd->len) + dstrcatf(cmd, "%s\n", depcmd->data); + } + free(copy); + free_dstr(depcmd); + return cmd->len != 0; +} + +int option_is_in_section(option_t *opt, int section) +{ + if (opt->section == section) + return 1; + if (opt->section == SECTION_ANYSETUP && (section == SECTION_PAGESETUP || section == SECTION_DOCUMENTSETUP)) + return 1; + return 0; +} + +int option_is_custom_value(option_t *opt, const char *value) +{ + if (opt->type == TYPE_BOOL || opt->type == TYPE_ENUM) + return 0; + + return !option_has_choice(opt, value); +} + +int option_get_command(dstr_t *cmd, option_t *opt, int optionset, int section) +{ + const char *valstr; + choice_t *choice = NULL; + + dstrclear(cmd); + + if (option_is_composite(opt)) + return composite_get_command(cmd, opt, optionset, section); + + if (section >= 0 && !option_is_in_section(opt, section)) + return 1; /* empty command for this section */ + + valstr = option_get_value(opt, optionset); + if (!valstr) + return 0; + + /* If the value is set to a predefined choice */ + choice = option_find_choice(opt, valstr); + if (choice && (*choice->command || + ((opt->type != TYPE_INT) && (opt->type != TYPE_FLOAT)))) { + dstrcpy(cmd, choice->command); + return 1; + } + + /* Consider "None" as the empty string for string and password options */ + if ((opt->type == TYPE_STRING || opt->type == TYPE_PASSWORD) && + !strcasecmp(valstr, "None")) + valstr = ""; + + /* Custom value */ + if (option_use_foomatic_prototype(opt)) + build_foomatic_custom_command(cmd, opt, valstr); + else { + dstrcpy(cmd, opt->custom_command); + if ((option_get_section(opt) == SECTION_JCLSETUP) || + (opt->style == 'J')) + build_cups_custom_jcl_command(cmd, opt, valstr); + else + build_cups_custom_ps_command(cmd, opt, valstr); + } + + return cmd->len != 0; +} + +void composite_set_values(option_t *opt, int optionset, const char *values) +{ + char *copy, *cur, *p; + option_t *dep; + value_t *val; + + copy = strdup(values); + for (cur = strtok(copy, " \t"); cur; cur = strtok(NULL, " \t")) { + if ((p = strchr(cur, '='))) { + *p++ = '\0'; + if ((dep = find_option(cur))) { + val = option_assure_value(dep, optionset); + val->fromoption = opt; + val->value = get_valid_value_string(dep, p); + } + else + _log("Could not find option \"%s\" (set from composite \"%s\")", cur, opt->name); + } + else if (startswith(cur, "no") || startswith(cur, "No")) { + if ((dep = find_option(&cur[2]))) { + val = option_assure_value(dep, optionset); + val->fromoption = opt; + val->value = get_valid_value_string(dep, "0"); + } + } + else { + if ((dep = find_option(cur))) { + val = option_assure_value(dep, optionset); + val->fromoption = opt; + val->value = get_valid_value_string(dep, "1"); + } + } + } + free(copy); +} + +int option_set_value(option_t *opt, int optionset, const char *value) +{ + value_t *val = option_assure_value(opt, optionset); + char *newvalue; + choice_t *choice; + option_t *fromopt; + + newvalue = get_valid_value_string(opt, value); + if (!newvalue) + return 0; + + free(val->value); + val->value = NULL; + + if (startswith(newvalue, "From") && (fromopt = find_option(&newvalue[4])) && + option_is_composite(fromopt)) { + /* TODO only set the changed option, not all of them */ + choice = option_find_choice(fromopt, + option_get_value(fromopt, optionset)); + + composite_set_values(fromopt, optionset, choice->command); + } + else { + val->value = newvalue; + } + + if (option_is_composite(opt)) { + /* set dependent values */ + choice = option_find_choice(opt, value); + if (choice && !isempty(choice->command)) + composite_set_values(opt, optionset, choice->command); + } + return 1; +} + +int option_accepts_value(option_t *opt, const char *value) +{ + char *val = get_valid_value_string(opt, value); + if (!val) + return 0; + free(val); + return 1; +} + +int option_has_choice(option_t *opt, const char *choice) +{ + return option_find_choice(opt, choice) != NULL; +} + +const char * option_text(option_t *opt) +{ + if (isempty(opt->text)) + return opt->text; + return opt->text; +} + +int option_type(option_t *opt) +{ + return opt->type; +} + +void option_set_order(option_t *opt, double order) +{ + option_t *prev; + + /* remove opt from old position */ + if (opt == optionlist_sorted_by_order) + optionlist_sorted_by_order = opt->next_by_order; + else { + for (prev = optionlist_sorted_by_order; + prev && prev->next_by_order != opt; + prev = prev->next_by_order); + prev->next_by_order = opt->next_by_order; + } + + opt->order = order; + + /* insert into new position */ + if (!optionlist_sorted_by_order) + optionlist_sorted_by_order = opt; + else if (optionlist_sorted_by_order->order > opt->order) { + opt->next_by_order = optionlist_sorted_by_order; + optionlist_sorted_by_order = opt; + } + else { + for (prev = optionlist_sorted_by_order; + prev->next_by_order && prev->next_by_order->order < opt->order; + prev = prev->next_by_order); + opt->next_by_order = prev->next_by_order; + prev->next_by_order = opt; + } +} + +/* Set option from *FoomaticRIPOption keyword */ +void option_set_from_string(option_t *opt, const char *str) +{ + char type[32], style[32]; + double order; + int matches; + + matches = sscanf(str, "%31s %31s %c %lf", type, style, &opt->spot, &order); + if (matches < 3) { + _log("Can't read the value of *FoomaticRIPOption for \"%s\"", opt->name); + return; + } + opt->type = type_from_string(type); + opt->style = style_from_string(style); + + if (matches == 4) + option_set_order(opt, order); +} + +static choice_t * option_assure_choice(option_t *opt, const char *name) +{ + choice_t *choice, *last = NULL; + + for (choice = opt->choicelist; choice; choice = choice->next) { + if (!strcasecmp(choice->value, name)) + return choice; + last = choice; + } + if (!choice) { + choice = calloc(1, sizeof(choice_t)); + if (last) + last->next = choice; + else + opt->choicelist = choice; + strlcpy(choice->value, name, 128); + } + return choice; +} + +static void unhtmlify(char *dest, size_t size, const char *src) +{ + jobparams_t *job = get_current_job(); + char *pdest = dest; + const char *psrc = src, *p = NULL; + const char *repl; + struct tm *t = localtime(&job->time); + char tmpstr[10]; + size_t s, l, n; + + while (*psrc && pdest - dest < size - 1) { + + if (*psrc == '&') { + psrc++; + repl = NULL; + p = NULL; + l = 0; + + /* Replace HTML/XML entities by the original characters */ + if (!prefixcmp(psrc, "apos")) { + repl = "\'"; + p = psrc + 4; + } else if (!prefixcmp(psrc, "quot")) { + repl = "\""; + p = psrc + 4; + } else if (!prefixcmp(psrc, "gt")) { + repl = ">"; + p = psrc + 2; + } else if (!prefixcmp(psrc, "lt")) { + repl = "<"; + p = psrc + 2; + } else if (!prefixcmp(psrc, "amp")) { + repl = "&"; + p = psrc + 3; + + /* Replace special entities by job->data */ + } else if (!prefixcmp(psrc, "job")) { + repl = job->id; + p = psrc + 3; + if (jobentitymaxlen != 0) + l = jobentitymaxlen; + } else if (!prefixcmp(psrc, "user")) { + repl = job->user; + p = psrc + 4; + if (userentitymaxlen != 0) + l = userentitymaxlen; + } else if (!prefixcmp(psrc, "host")) { + repl = job->host; + p = psrc + 4; + if (hostentitymaxlen != 0) + l = hostentitymaxlen; + } else if (!prefixcmp(psrc, "title")) { + repl = job->title; + p = psrc + 5; + if (titleentitymaxlen != 0) + l = titleentitymaxlen; + } else if (!prefixcmp(psrc, "copies")) { + repl = job->copies; + p = psrc + 6; + } else if (!prefixcmp(psrc, "rbinumcopies")) { + if (job->rbinumcopies > 0) { + snprintf(tmpstr, 10, "%d", job->rbinumcopies); + repl = tmpstr; + } + else + repl = job->copies; + p = psrc + 12; + } + else if (!prefixcmp(psrc, "options")) { + repl = job->optstr->data; + p = psrc + 7; + if (optionsentitymaxlen != 0) + l = optionsentitymaxlen; + } else if (!prefixcmp(psrc, "year")) { + sprintf(tmpstr, "%04d", t->tm_year + 1900); + repl = tmpstr; + p = psrc + 4; + } + else if (!prefixcmp(psrc, "month")) { + sprintf(tmpstr, "%02d", t->tm_mon + 1); + repl = tmpstr; + p = psrc + 5; + } + else if (!prefixcmp(psrc, "date")) { + sprintf(tmpstr, "%02d", t->tm_mday); + repl = tmpstr; + p = psrc + 4; + } + else if (!prefixcmp(psrc, "hour")) { + sprintf(tmpstr, "%02d", t->tm_hour); + repl = tmpstr; + p = psrc + 4; + } + else if (!prefixcmp(psrc, "min")) { + sprintf(tmpstr, "%02d", t->tm_min); + repl = tmpstr; + p = psrc + 3; + } + else if (!prefixcmp(psrc, "sec")) { + sprintf(tmpstr, "%02d", t->tm_sec); + repl = tmpstr; + p = psrc + 3; + } + if (p) { + n = strtol(p, (char **)(&p), 0); + if (n != 0) + l = n; + if (*p != ';') + repl = NULL; + } else + repl = NULL; + if (repl) { + if ((l == 0) || (l > strlen(repl))) + l = strlen(repl); + s = size - (pdest - dest) - 1; + strncpy(pdest, repl, s); + if (s < l) + pdest += s; + else + pdest += l; + psrc = p + 1; + } + else { + *pdest = '&'; + pdest++; + } + } + else { + *pdest = *psrc; + pdest++; + psrc++; + } + } + *pdest = '\0'; +} + +/* + * Checks whether 'code' contains active PostScript, i.e. not only comments + */ +static int contains_active_postscript(const char *code) +{ + char **line, **lines; + int contains_ps = 0; + + if (!(lines = argv_split(code, "\n", NULL))) + return 0; + + for (line = lines; *line && !contains_ps; line++) + contains_ps = !isempty(*line) && + !startswith(skip_whitespace(*line), "%"); + + argv_free(lines); + return contains_ps; +} + +void option_set_choice(option_t *opt, const char *name, const char *text, + const char *code) +{ + choice_t *choice; + + if (opt->type == TYPE_BOOL) { + if (is_true_string(name)) + choice = option_assure_choice(opt, "1"); + else + choice = option_assure_choice(opt, "0"); + } + else + choice = option_assure_choice(opt, name); + + if (text) + strlcpy(choice->text, text, 128); + + if (!code) + { + _log("Warning: No code for choice \"%s\" of option \"%s\"\n", + choice->text, opt->name); + return; + } + + if (!startswith(code, "%% FoomaticRIPOptionSetting")) + unhtmlify(choice->command, 65536, code); +} + +/* + * Parameters + */ + +int param_set_allowed_chars(param_t *param, const char *value) +{ + char rxstr[128], tmp[128]; + + param->allowedchars = malloc(sizeof(regex_t)); + unhtmlify(tmp, 128, value); + snprintf(rxstr, 128, "^[%s]*$", tmp); + if (regcomp(param->allowedchars, rxstr, 0) != 0) { + regfree(param->allowedchars); + param->allowedchars = NULL; + return 0; + } + return 1; +} + +int param_set_allowed_regexp(param_t *param, const char *value) +{ + char tmp[128]; + + param->allowedregexp = malloc(sizeof(regex_t)); + unhtmlify(tmp, 128, value); + if (regcomp(param->allowedregexp, tmp, 0) != 0) { + regfree(param->allowedregexp); + param->allowedregexp = NULL; + return 0; + } + return 1; +} + +void option_set_custom_command(option_t *opt, const char *cmd) +{ + size_t len = strlen(cmd) + 50; + free(opt->custom_command); + opt->custom_command = malloc(len); + unhtmlify(opt->custom_command, len, cmd); +} + +param_t * option_add_custom_param_from_string(option_t *opt, + const char *name, const char *text, const char *str) +{ + param_t *param = calloc(1, sizeof(param_t)); + param_t *p; + char typestr[33]; + int n; + + strlcpy(param->name, name, 128); + strlcpy(param->text, text, 128); + + n = sscanf(str, "%d%15s%19s%19s", + ¶m->order, typestr, param->min, param->max); + + if (n != 4) { + _log("Could not parse custom parameter for '%s'!\n", opt->name); + free(param); + return NULL; + } + + if (!strcmp(typestr, "curve")) + param->type = TYPE_CURVE; + else if (!strcmp(typestr, "invcurve")) + param->type = TYPE_INVCURVE; + else if (!strcmp(typestr, "int")) + param->type = TYPE_INT; + else if (!strcmp(typestr, "real")) + param->type = TYPE_FLOAT; + else if (!strcmp(typestr, "passcode")) + param->type = TYPE_PASSCODE; + else if (!strcmp(typestr, "password")) + param->type = TYPE_PASSWORD; + else if (!strcmp(typestr, "points")) + param->type = TYPE_POINTS; + else if (!strcmp(typestr, "string")) + param->type = TYPE_STRING; + else { + _log("Unknown custom parameter type for param '%s' for option '%s'\n", param->name, opt->name); + free(param); + return NULL; + } + + param->next = NULL; + + /* Insert param into opt->paramlist, sorted by order */ + if (!opt->paramlist) + opt->paramlist = param; + else if (opt->paramlist->order > param->order) { + param->next = opt->paramlist; + opt->paramlist = param; + } + else { + for (p = opt->paramlist; + p->next && p->next->order < param->order; + p = p->next); + param->next = p->next; + p->next = param; + } + + opt->param_count++; + return param; +} + +param_t * option_assure_foomatic_param(option_t *opt) +{ + param_t *param; + + if (opt->foomatic_param) + return opt->foomatic_param; + + param = calloc(1, sizeof(param_t)); + strcpy(param->name, "foomatic-param"); + param->order = 0; + param->type = opt->type; + + opt->foomatic_param = param; + return param; +} + + +/* + * Optionsets + */ + +const char * optionset_name(int idx) +{ + if (idx < 0 || idx >= optionset_count) { + _log("Optionset with index %d does not exist\n", idx); + return NULL; + } + return optionsets[idx]; +} + +int optionset(const char * name) +{ + int i; + + for (i = 0; i < optionset_count; i++) { + if (!strcmp(optionsets[i], name)) + return i; + } + + if (optionset_count == optionset_alloc) { + optionset_alloc *= 2; + optionsets = realloc(optionsets, optionset_alloc * sizeof(char *)); + for (i = optionset_count; i < optionset_alloc; i++) + optionsets[i] = NULL; + } + + optionsets[optionset_count] = strdup(name); + optionset_count++; + return optionset_count -1; +} + +void optionset_copy_values(int src_optset, int dest_optset) +{ + option_t *opt; + value_t *val; + + for (opt = optionlist; opt; opt = opt->next) { + for (val = opt->valuelist; val; val = val->next) { + if (val->optionset == src_optset) { + option_set_value(opt, dest_optset, val->value); + break; + } + } + } +} + +void optionset_delete_values(int optionset) +{ + option_t *opt; + value_t *val, *prev_val; + + for (opt = optionlist; opt; opt = opt->next) { + val = opt->valuelist; + prev_val = NULL; + while (val) { + if (val->optionset == optionset) { + if (prev_val) + prev_val->next = val->next; + else + opt->valuelist = val->next; + free_value(val); + val = prev_val ? prev_val->next : opt->valuelist; + break; + } else { + prev_val = val; + val = val->next; + } + } + } +} + +int optionset_equal(int optset1, int optset2, int exceptPS) +{ + option_t *opt; + const char *val1, *val2; + + for (opt = optionlist; opt; opt = opt->next) { + if (exceptPS && opt->style == 'G') + continue; + + val1 = option_get_value(opt, optset1); + val2 = option_get_value(opt, optset2); + + if (val1 && val2) { /* both entries exist */ + if (strcmp(val1, val2) != 0) + return 0; /* but aren't equal */ + } + else if (val1 || val2) /* one entry exists --> can't be equal */ + return 0; + /* If no extry exists, the non-existing entries + * are considered as equal */ + } + return 1; +} + +/* + * read_ppd_file() + */ +void read_ppd_file(const char *filename) +{ + FILE *fh; + const char *tmp; + char *icc_qual2 = NULL; + char *icc_qual3 = NULL; + char line [256]; /* PPD line length is max 255 (excl. \0) */ + char *p; + char key[128], name[64], text[64]; + dstr_t *value = create_dstr(); /* value can span multiple lines */ + double order; + value_t *val; + option_t *opt, *current_opt = NULL; + param_t *param; + icc_mapping_entry_t *entry; + + fh = fopen(filename, "r"); + if (!fh) { + _log("error opening %s\n", filename); + exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS); + } + _log("Parsing PPD file ...\n"); + + dstrassure(value, 256); + + qualifier_data = list_create(); + while (!feof(fh)) { + tmp = fgets(line, 256, fh); + + if (line[0] != '*' || startswith(line, "*%")) + continue; + + /* get the key */ + if (!(p = strchr(line, ':'))) + continue; + *p = '\0'; + + key[0] = name[0] = text[0] = '\0'; + sscanf(line, "*%127s%*[ \t]%63[^ \t/=)]%*1[/=]%63[^\n]", key, name, text); + + /* get the value */ + dstrclear(value); + sscanf(p +1, " %255[^\r\n]", value->data); + value->len = strlen(value->data); + if (!value->len) + _log("PPD: Missing value for key \"%s\"\n", line); + + while (1) { + /* "&&" is the continue-on-next-line marker */ + if (dstrendswith(value, "&&")) { + value->len -= 2; + value->data[value->len] = '\0'; + } + /* quoted but quotes are not yet closed */ + else if (value->data[0] == '\"' && !strchr(value->data +1, '\"')) + dstrcat(value, "\n"); /* keep newlines in quoted string*/ + /* not quoted, or quotes already closed */ + else + break; + + tmp = fgets(line, 256, fh); + dstrcat(value, line); + dstrremovenewline(value); + } + + /* remove quotes */ + if (value->data[0] == '\"') { + memmove(value->data, value->data +1, value->len +1); + p = strrchr(value->data, '\"'); + if (!p) { + _log("Invalid line: \"%s: ...\"\n", key); + continue; + } + *p = '\0'; + } + /* remove last newline */ + dstrremovenewline(value); + + /* process key/value pairs */ + if (strcmp(key, "NickName") == 0) { + unhtmlify(printer_model, 256, value->data); + } + else if (strcmp(key, "FoomaticIDs") == 0) { + /* *FoomaticIDs: <printer ID> <driver ID> */ + sscanf(value->data, "%*[ \t]%127[^ \t]%*[ \t]%127[^ \t\n]", + printer_id, driver); + } + else if (strcmp(key, "FoomaticRIPPostPipe") == 0) { + if (!postpipe) + postpipe = create_dstr(); + dstrassure(postpipe, value->len +128); + unhtmlify(postpipe->data, postpipe->alloc, value->data); + } + else if (strcmp(key, "FoomaticRIPCommandLine") == 0) { + unhtmlify(cmd, 4096, value->data); + } + else if (strcmp(key, "FoomaticRIPCommandLinePDF") == 0) { + unhtmlify(cmd_pdf, 4096, value->data); + } + else if (strcmp(key, "FoomaticRIPNoPageAccounting") == 0) { + /* Boolean value */ + if (strcasecmp(value->data, "true") == 0) { + /* Driver is not compatible with page accounting according to the + Foomatic database, so turn it off for this driver */ + ps_accounting = 0; + _log("CUPS page accounting disabled by driver.\n"); + } + } + else if (!strcmp(key, "cupsFilter")) { + /* cupsFilter: <code> */ + /* only save the filter for "application/vnd.cups-raster" */ + if (prefixcmp(value->data, "application/vnd.cups-raster") == 0) { + p = strrchr(value->data, ' '); + if (p) + unhtmlify(cupsfilter, 256, p +1); + } + } + else if (startswith(key, "Custom") && !strcasecmp(name, "true")) { + /* Cups custom option: *CustomFoo True: "command" */ + if (startswith(&key[6], "JCL")) { + opt = assure_option(&key[9]); + opt->style = 'J'; + } + else + opt = assure_option(&key[6]); + option_set_custom_command(opt, value->data); + if (!strcmp(key, "CustomPageSize")) + option_set_custom_command(assure_option("PageRegion"), value->data); + } + else if (startswith(key, "ParamCustom")) { + /* Cups custom parameter: + *ParamCustomFoo Name/Text: order type minimum maximum */ + if (startswith(&key[11], "JCL")) + opt = assure_option(&key[14]); + else + opt = assure_option(&key[11]); + option_add_custom_param_from_string(opt, name, text, value->data); + } + else if (!strcmp(key, "OpenUI") || !strcmp(key, "JCLOpenUI")) { + /* "*[JCL]OpenUI *<option>[/<translation>]: <type>" */ + current_opt = assure_option(&name[1]); + if (!isempty(text)) + strlcpy(current_opt->text, text, 128); + if (startswith(key, "JCL")) + current_opt->style = 'J'; + /* Set the argument type only if not defined yet, + a definition in "*FoomaticRIPOption" has priority */ + if (current_opt->type == TYPE_NONE) + current_opt->type = type_from_string(value->data); + } + else if (!strcmp(key, "CloseUI") || !strcmp(key, "JCLCloseUI")) { + /* *[JCL]CloseUI: *<option> */ + if (!current_opt || !option_has_name(current_opt, value->data +1)) + _log("CloseUI found without corresponding OpenUI (%s).\n", value->data +1); + current_opt = NULL; + } + else if (!strcmp(key, "FoomaticRIPOption")) { + /* "*FoomaticRIPOption <option>: <type> <style> <spot> [<order>]" + <order> only used for 1-choice enum options */ + option_set_from_string(assure_option(name), value->data); + } + else if (!strcmp(key, "FoomaticRIPOptionPrototype")) { + /* "*FoomaticRIPOptionPrototype <option>: <code>" + Used for numerical and string options only */ + opt = assure_option(name); + opt->proto = malloc(65536); + unhtmlify(opt->proto, 65536, value->data); + } + else if (!strcmp(key, "FoomaticRIPOptionRange")) { + /* *FoomaticRIPOptionRange <option>: <min> <max> + Used for numerical options only */ + param = option_assure_foomatic_param(assure_option(name)); + sscanf(value->data, "%19s %19s", param->min, param->max); + } + else if (!strcmp(key, "FoomaticRIPOptionMaxLength")) { + /* "*FoomaticRIPOptionMaxLength <option>: <length>" + Used for string options only */ + param = option_assure_foomatic_param(assure_option(name)); + sscanf(value->data, "%19s", param->max); + } + else if (!strcmp(key, "FoomaticRIPOptionAllowedChars")) { + /* *FoomaticRIPOptionAllowedChars <option>: <code> + Used for string options only */ + param = option_assure_foomatic_param(assure_option(name)); + param_set_allowed_chars(param, value->data); + } + else if (!strcmp(key, "FoomaticRIPOptionAllowedRegExp")) { + /* "*FoomaticRIPOptionAllowedRegExp <option>: <code>" + Used for string options only */ + param = option_assure_foomatic_param(assure_option(name)); + param_set_allowed_regexp(param, value->data); + } + else if (!strcmp(key, "OrderDependency")) { + /* OrderDependency: <order> <section> *<option> */ + /* use 'text' to read <section> */ + sscanf(value->data, "%lf %63s *%63s", &order, text, name); + opt = assure_option(name); + opt->section = section_from_string(text); + option_set_order(opt, order); + } + + /* Default options are not yet validated (not all options/choices + have been read yet) */ + else if (!prefixcmp(key, "Default")) { + /* Default<option>: <value> */ + + opt = assure_option(&key[7]); + val = option_assure_value(opt, optionset("default")); + free(val->value); + val->value = strdup(value->data); + } + else if (!prefixcmp(key, "FoomaticRIPDefault")) { + /* FoomaticRIPDefault<option>: <value> + Used for numerical options only */ + opt = assure_option(&key[18]); + val = option_assure_value(opt, optionset("default")); + free(val->value); + val->value = strdup(value->data); + } + + /* Current argument */ + else if (current_opt && !strcmp(key, current_opt->name)) { + /* *<option> <choice>[/translation]: <code> */ + option_set_choice(current_opt, name, text, value->data); + } + else if (!strcmp(key, "FoomaticRIPOptionSetting")) { + /* "*FoomaticRIPOptionSetting <option>[=<choice>]: <code> + For boolean options <choice> is not given */ + option_set_choice(assure_option(name), + isempty(text) ? "true" : text, NULL, value->data); + } + + /* "*(Foomatic|)JCL(Begin|ToPSInterpreter|End|Prefix): <code>" + The printer supports PJL/JCL when there is such a line */ + else if (!prefixcmp(key, "JCLBegin") || + !prefixcmp(key, "FoomaticJCLBegin")) { + unhexify(jclbegin, 256, value->data); + if (!jclprefixset && strstr(jclbegin, "PJL") == NULL) + jclprefix[0] = '\0'; + } + else if (!prefixcmp(key, "JCLToPSInterpreter") || + !prefixcmp(key, "FoomaticJCLToPSInterpreter")) { + unhexify(jcltointerpreter, 256, value->data); + } + else if (!prefixcmp(key, "JCLEnd") || + !prefixcmp(key, "FoomaticJCLEnd")) { + unhexify(jclend, 256, value->data); + } + else if (!prefixcmp(key, "JCLPrefix") || + !prefixcmp(key, "FoomaticJCLPrefix")) { + unhexify(jclprefix, 256, value->data); + jclprefixset = 1; + } + else if (!prefixcmp(key, "% COMDATA #")) { + /* old foomtic 2.0.x PPD file */ + _log("You are using an old Foomatic 2.0 PPD file, which is no " + "longer supported by Foomatic >4.0. Exiting.\n"); + exit(1); /* TODO exit more gracefully */ + } + else if (!strcmp(key, "FoomaticRIPJobEntityMaxLength")) { + /* "*FoomaticRIPJobEntityMaxLength: <length>" */ + sscanf(value->data, "%d", &jobentitymaxlen); + } + else if (!strcmp(key, "FoomaticRIPUserEntityMaxLength")) { + /* "*FoomaticRIPUserEntityMaxLength: <length>" */ + sscanf(value->data, "%d", &userentitymaxlen); + } + else if (!strcmp(key, "FoomaticRIPHostEntityMaxLength")) { + /* "*FoomaticRIPHostEntityMaxLength: <length>" */ + sscanf(value->data, "%d", &hostentitymaxlen); + } + else if (!strcmp(key, "FoomaticRIPTitleEntityMaxLength")) { + /* "*FoomaticRIPTitleEntityMaxLength: <length>" */ + sscanf(value->data, "%d", &titleentitymaxlen); + } + else if (!strcmp(key, "FoomaticRIPOptionsEntityMaxLength")) { + /* "*FoomaticRIPOptionsEntityMaxLength: <length>" */ + sscanf(value->data, "%d", &optionsentitymaxlen); + } + else if (!strcmp(key, "cupsICCProfile")) { + /* "*cupsICCProfile: <qualifier/Title> <filename>" */ + entry = calloc(1, sizeof(icc_mapping_entry_t)); + entry->qualifier = strdup(name); + entry->filename = strdup(value->data); + list_append (qualifier_data, entry); + } + else if (!strcmp(key, "cupsICCQualifier2")) { + /* "*cupsICCQualifier2: <value>" */ + icc_qual2 = strdup(value->data); + } + else if (!strcmp(key, "cupsICCQualifier3")) { + /* "*cupsICCQualifier3: <value>" */ + icc_qual3 = strdup(value->data); + } + } + + fclose(fh); + free_dstr(value); + + /* Validate default options by resetting them with option_set_value() */ + for (opt = optionlist; opt; opt = opt->next) { + val = option_find_value(opt, optionset("default")); + if (val) { + /* if fromopt is set, this value has already been validated */ + if (!val->fromoption) + option_set_value(opt, optionset("default"), val->value); + } + else + /* Make sure that this option has a default choice, even if none is + defined in the PPD file */ + option_set_value(opt, optionset("default"), opt->choicelist->value); + } + + /* create qualifier for this PPD */ + qualifier = calloc(4, sizeof(char*)); + + /* get colorspace */ + tmp = option_get_value(find_option("ColorSpace"), optionset("default")); + if (tmp == NULL) + tmp = option_get_value(find_option("ColorModel"), optionset("default")); + if (tmp == NULL) + tmp = ""; + qualifier[0] = strdup(tmp); + + /* get selector2 */ + if (icc_qual2 == NULL) + icc_qual2 = strdup("MediaType"); + tmp = option_get_value(find_option(icc_qual2), optionset("default")); + if (tmp == NULL) + tmp = ""; + qualifier[1] = strdup(tmp); + + /* get selectors */ + if (icc_qual3 == NULL) + icc_qual3 = strdup("Resolution"); + tmp = option_get_value(find_option(icc_qual3), optionset("default")); + if (tmp == NULL) + tmp = ""; + qualifier[2] = strdup(tmp); + + free (icc_qual2); + free (icc_qual3); +} + +int ppd_supports_pdf() +{ + option_t *opt; + + /* If at least one option inserts PostScript code, we cannot support PDF */ + for (opt = optionlist; opt; opt = opt->next) + { + choice_t *choice; + + if (!option_is_ps_command(opt) || option_is_composite(opt) || + (opt->type == TYPE_NONE)) + continue; + + for (choice = opt->choicelist; choice; choice = choice->next) + if (contains_active_postscript(choice->command)) { + _log(" PostScript option found: %s=%s: \"%s\"\n", + opt->name, choice->value, choice->command); + return 0; + } + } + + if (!isempty(cmd_pdf)) + return 1; + + /* Ghostscript also accepts PDF, use that if it is in the normal command + * line */ + if (startswith(cmd, "gs")) + { + strncpy(cmd_pdf, cmd, 4096); + return 1; + } + + _log(" Neither PDF renderer command line nor Ghostscript-based renderer command line found\n"); + return 0; +} + +/* build a renderer command line, based on the given option set */ +int build_commandline(int optset, dstr_t *cmdline, int pdfcmdline) +{ + option_t *opt; + const char *userval; + char *s, *p; + dstr_t *cmdvar = create_dstr(); + dstr_t *open = create_dstr(); + dstr_t *close = create_dstr(); + char letters[] = "%A %B %C %D %E %F %G %H %I %J %K %L %M %W %X %Y %Z"; + int jcl = 0; + + dstr_t *local_jclprepend = create_dstr(); + + dstrclear(prologprepend); + dstrclear(setupprepend); + dstrclear(pagesetupprepend); + + if (cmdline) + dstrcpy(cmdline, pdfcmdline ? cmd_pdf : cmd); + + for (opt = optionlist_sorted_by_order; opt; opt = opt->next_by_order) { + /* composite options have no direct influence, and all their dependents + have already been set */ + if (option_is_composite(opt)) + continue; + + userval = option_get_value(opt, optset); + option_get_command(cmdvar, opt, optset, -1); + + /* Insert the built snippet at the correct place */ + if (option_is_ps_command(opt)) { + /* Place this Postscript command onto the prepend queue + for the appropriate section. */ + if (cmdvar->len) { + dstrcpyf(open, "[{\n%%%%BeginFeature: *%s ", opt->name); + if (opt->type == TYPE_BOOL) + dstrcatf(open, is_true_string(userval) ? "True\n" : "False\n"); + else + dstrcatf(open, "%s\n", userval); + dstrcpyf(close, "\n%%%%EndFeature\n} stopped cleartomark\n"); + + switch (option_get_section(opt)) { + case SECTION_PROLOG: + dstrcatf(prologprepend, "%s%s%s", open->data, cmdvar->data, close->data); + break; + + case SECTION_ANYSETUP: + if (optset != optionset("currentpage")) + dstrcatf(setupprepend, "%s%s%s", open->data, cmdvar->data, close->data); + else if (strcmp(option_get_value(opt, optionset("header")), userval) != 0) + dstrcatf(pagesetupprepend, "%s%s%s", open->data, cmdvar->data, close->data); + break; + + case SECTION_DOCUMENTSETUP: + dstrcatf(setupprepend, "%s%s%s", open->data, cmdvar->data, close->data); + break; + + case SECTION_PAGESETUP: + dstrcatf(pagesetupprepend, "%s%s%s", open->data, cmdvar->data, close->data); + break; + + case SECTION_JCLSETUP: /* PCL/JCL argument */ + s = malloc(cmdvar->len +1); + unhexify(s, cmdvar->len +1, cmdvar->data); + dstrcatf(local_jclprepend, "%s", s); + free(s); + break; + + default: + dstrcatf(setupprepend, "%s%s%s", open->data, cmdvar->data, close->data); + } + } + } + else if (option_is_jcl_arg(opt)) { + jcl = 1; + /* Put JCL commands onto JCL stack */ + if (cmdvar->len) { + char *s = malloc(cmdvar->len +1); + unhexify(s, cmdvar->len +1, cmdvar->data); + if (!startswith(cmdvar->data, jclprefix)) + dstrcatf(local_jclprepend, "%s%s\n", jclprefix, s); + else + dstrcat(local_jclprepend, s); + free(s); + } + } + else if (option_is_commandline_arg(opt) && cmdline) { + /* Insert the processed argument in the command line + just before every occurrence of the spot marker. */ + p = malloc(3); + snprintf(p, 3, "%%%c", opt->spot); + s = malloc(cmdvar->len +3); + snprintf(s, cmdvar->len +3, "%s%%%c", cmdvar->data, opt->spot); + dstrreplace(cmdline, p, s, 0); + free(p); + free(s); + } + + /* Insert option into command line of CUPS raster driver */ + if (cmdline && strstr(cmdline->data, "%Y")) { + if (isempty(userval)) + continue; + s = malloc(strlen(opt->name) + strlen(userval) + 20); + sprintf(s, "%s=%s %%Y", opt->name, userval); + dstrreplace(cmdline, "%Y", s, 0); + free(s); + } + } + + /* Tidy up after computing option statements for all of P, J, and C types: */ + + /* C type finishing */ + /* Pluck out all of the %n's from the command line prototype */ + if (cmdline) { + s = strtok(letters, " "); + do { + dstrreplace(cmdline, s, "", 0); + } while ((s = strtok(NULL, " "))); + } + + /* J type finishing */ + /* Compute the proper stuff to say around the job */ + if (jcl && !jobhasjcl) { + /* command to switch to the interpreter */ + dstrcatf(local_jclprepend, "%s", jcltointerpreter); + + /* Arrange for JCL RESET command at the end of job */ + dstrcpy(jclappend, jclend); + + argv_free(jclprepend); + jclprepend = argv_split(local_jclprepend->data, "\r\n", NULL); + } + + free_dstr(cmdvar); + free_dstr(open); + free_dstr(close); + free_dstr(local_jclprepend); + + return !isempty(cmd); +} + +/* if "comments" is set, add "%%BeginProlog...%%EndProlog" */ +void append_prolog_section(dstr_t *str, int optset, int comments) +{ + /* Start comment */ + if (comments) { + _log("\"Prolog\" section is missing, inserting it.\n"); + dstrcat(str, "%%BeginProlog\n"); + } + + /* Generate the option code (not necessary when CUPS is spooler and + PostScript data is not converted from PDF) */ + if ((spooler != SPOOLER_CUPS) || pdfconvertedtops) { + _log("Inserting option code into \"Prolog\" section.\n"); + build_commandline(optset, NULL, 0); + dstrcat(str, prologprepend->data); + } + + /* End comment */ + if (comments) + dstrcat(str, "%%EndProlog\n"); +} + +void append_setup_section(dstr_t *str, int optset, int comments) +{ + /* Start comment */ + if (comments) { + _log("\"Setup\" section is missing, inserting it.\n"); + dstrcat(str, "%%BeginSetup\n"); + } + + /* PostScript code to generate accounting messages for CUPS */ + if (spooler == SPOOLER_CUPS && ps_accounting == 1) { + _log("Inserting PostScript code for CUPS' page accounting\n"); + dstrcat(str, accounting_prolog); + } + + /* Generate the option code (not necessary when CUPS is spooler and + PostScript data is not converted from PDF) */ + if ((spooler != SPOOLER_CUPS) || pdfconvertedtops) { + _log("Inserting option code into \"Setup\" section.\n"); + build_commandline(optset, NULL, 0); + dstrcat(str, setupprepend->data); + } + + /* End comment */ + if (comments) + dstrcat(str, "%%EndSetup\n"); +} + +void append_page_setup_section(dstr_t *str, int optset, int comments) +{ + /* Start comment */ + if (comments) { + _log("\"PageSetup\" section is missing, inserting it.\n"); + dstrcat(str, "%%BeginPageSetup\n"); + } + + /* Generate the option code (not necessary when CUPS is spooler) */ + _log("Inserting option code into \"PageSetup\" section.\n"); + build_commandline(optset, NULL, 0); + dstrcat(str, pagesetupprepend->data); + + /* End comment */ + if (comments) + dstrcat(str, "%%EndPageSetup\n"); +} + + +typedef struct page_range { + short even, odd; + unsigned first, last; + struct page_range *next; +} page_range_t; + +static page_range_t * parse_page_ranges(const char *ranges) +{ + page_range_t *head = NULL, *tail = NULL; + char *tokens, *tok; + int cnt; + + tokens = strdup(ranges); + for (tok = strtok(tokens, ","); tok; tok = strtok(NULL, ",")) { + page_range_t *pr = calloc(1, sizeof(page_range_t)); + + if (startswith(tok, "even")) + pr->even = 1; + else if (startswith(tok, "odd")) + pr->odd = 1; + else if ((cnt = sscanf(tok, "%u-%u", &pr->first, &pr->last))) { + /* If 'last' has not been read, this could mean only one page (no + * hyphen) or all pages to the end */ + if (cnt == 1 && !endswith(tok, "-")) + pr->last = pr->first; + else if (cnt == 2 && pr->first > pr->last) { + unsigned tmp = pr->first; + pr->first = pr->last; + pr->last = tmp; + } + } + else { + printf("Invalid page range: %s\n", tok); + free(pr); + continue; + } + + if (tail) { + tail->next = pr; + tail = pr; + } + else + tail = head = pr; + } + + free(tokens); + return head; +} + +static void free_page_ranges(page_range_t *ranges) +{ + page_range_t *pr; + while (ranges) { + pr = ranges; + ranges = ranges->next; + free(pr); + } +} + +/* Parse a string containing page ranges and either check whether a + given page is in the ranges or, if the given page number is zero, + determine the score how specific this page range string is.*/ +int get_page_score(const char *pages, int page) +{ + page_range_t *ranges = parse_page_ranges(pages); + page_range_t *pr; + int totalscore = 0; + int pageinside = 0; + + for (pr = ranges; pr; pr = pr->next) { + if (pr->even) { + totalscore += 50000; + if (page % 2 == 0) + pageinside = 1; + } + else if (pr->odd) { + totalscore += 50000; + if (page % 2 == 1) + pageinside = 1; + } + else if (pr->first == pr->last) { /* Single page */ + totalscore += 1; + if (page == pr->first) + pageinside = 1; + } + else if (pr->last == 0) { /* To the end of the document */ + totalscore += 100000; + if (page >= pr->first) + pageinside = 1; + } + else { /* Sequence of pages */ + totalscore += pr->last - pr->first +1; + if (page >= pr->first && page <= pr->last) + pageinside = 1; + } + } + + free_page_ranges(ranges); + + if (page == 0 || pageinside) + return totalscore; + + return 0; +} + +/* Set the options for a given page */ +void set_options_for_page(int optset, int page) +{ + int score, bestscore; + option_t *opt; + value_t *val, *bestvalue; + const char *ranges; + const char *optsetname; + + for (opt = optionlist; opt; opt = opt->next) { + + bestscore = 10000000; + bestvalue = NULL; + for (val = opt->valuelist; val; val = val->next) { + + optsetname = optionset_name(val->optionset); + if (!startswith(optsetname, "pages:")) + continue; + + ranges = &optsetname[6]; /* after "pages:" */ + score = get_page_score(ranges, page); + if (score && score < bestscore) { + bestscore = score; + bestvalue = val; + } + } + + if (bestvalue) + option_set_value(opt, optset, bestvalue->value); + } +} + diff --git a/filter/foomatic-rip/options.h b/filter/foomatic-rip/options.h new file mode 100644 index 000000000..242e8f29a --- /dev/null +++ b/filter/foomatic-rip/options.h @@ -0,0 +1,184 @@ +/* options.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef options_h +#define options_h + + +#include <stddef.h> +#include <regex.h> +#include "util.h" + +/* Option types */ +#define TYPE_NONE 0 +#define TYPE_ENUM 1 +#define TYPE_PICKMANY 2 +#define TYPE_BOOL 3 +#define TYPE_INT 4 +#define TYPE_FLOAT 5 +#define TYPE_STRING 6 +#define TYPE_PASSWORD 7 +#define TYPE_CURVE 8 +#define TYPE_INVCURVE 9 +#define TYPE_PASSCODE 10 +#define TYPE_POINTS 11 + +/* Sections */ +#define SECTION_ANYSETUP 1 +#define SECTION_PAGESETUP 2 +#define SECTION_PROLOG 3 +#define SECTION_DOCUMENTSETUP 4 +#define SECTION_JCLSETUP 5 + + + +typedef struct choice_s { + char value [128]; + char text [128]; + char command[65536]; + struct choice_s *next; +} choice_t; + +/* Custom option parameter */ +typedef struct param_s { + char name [128]; + char text [128]; /* formerly comment, changed to 'text' to + be consistent with cups */ + int order; + + int type; + char min[20], max[20]; /* contents depend on 'type' */ + + regex_t *allowedchars; + regex_t *allowedregexp; + + struct param_s *next; +} param_t; + +/* Option */ +typedef struct option_s { + char name [128]; + char text [128]; + char varname [128]; /* clean version of 'name' (no spaces etc.) */ + int type; + int style; + char spot; + double order; + int section; + + int notfirst; /* TODO remove */ + + choice_t *choicelist; + + /* Foomatic PPD extensions */ + char *proto; /* *FoomaticRIPOptionPrototype: if this is set + it will be used with only the first option + in paramlist (there should be only one) */ + param_t *foomatic_param; + + /* CUPS custom options */ + char *custom_command; /* *CustomFoo */ + param_t *paramlist; /* for custom values, sorted by stack order */ + size_t param_count; + + struct value_s *valuelist; + + struct option_s *next; + struct option_s *next_by_order; +} option_t; + + +/* A value for an option */ +typedef struct value_s { + int optionset; + char *value; + option_t *fromoption; /* This is set when this value is set by a composite */ + struct value_s *next; +} value_t; + + +extern option_t *optionlist; +extern option_t *optionlist_sorted_by_order; + +extern char jclbegin[256]; +extern char jcltointerpreter[256]; +extern char jclend[256]; +extern char jclprefix[256]; + +extern char cmd[4096]; +extern char cmd_pdf[4096]; + +extern int ps_accounting; + + +int option_is_composite(option_t *opt); +int option_is_ps_command(option_t *opt); +int option_is_jcl_arg(option_t *opt); +int option_is_commandline_arg(option_t *opt); + + +int option_get_section(option_t *opt); /* TODO deprecated */ + +/* handles ANYSETUP (for (PAGE|DOCUMENT)SETUP) */ +int option_is_in_section(option_t *opt, int section); + +void options_init(); +void options_free(); + +size_t option_count(); +option_t *find_option(const char *name); + +void read_ppd_file(const char *filename); + +int ppd_supports_pdf(); + + +int option_set_value(option_t *opt, int optset, const char *value); +const char * option_get_value(option_t *opt, int optset); + +/* section == -1 for all sections */ +int option_get_command(dstr_t *cmd, option_t *opt, int optset, int section); + +int option_accepts_value(option_t *opt, const char *value); +int option_has_choice(option_t *opt, const char *choice); +int option_is_custom_value(option_t *opt, const char *value); + + +const char * optionset_name(int idx); +int optionset(const char * name); + +void optionset_copy_values(int src_optset, int dest_optset); +int optionset_equal(int optset1, int optset2, int exceptPS); +void optionset_delete_values(int optionset); + +void append_prolog_section(dstr_t *str, int optset, int comments); +void append_setup_section(dstr_t *str, int optset, int comments); +void append_page_setup_section(dstr_t *str, int optset, int comments); +int build_commandline(int optset, dstr_t *cmdline, int pdfcmdline); + +void set_options_for_page(int optset, int page); +const char *get_icc_profile_for_qualifier(const char **qualifier); +const char **get_ppd_qualifier(void); + +#endif + diff --git a/filter/foomatic-rip/pdf.c b/filter/foomatic-rip/pdf.c new file mode 100644 index 000000000..5783e72bb --- /dev/null +++ b/filter/foomatic-rip/pdf.c @@ -0,0 +1,301 @@ +/* pdf.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "foomaticrip.h" +#include "util.h" +#include "options.h" +#include "process.h" +#include "renderer.h" + +#include <stdlib.h> +#include <ctype.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> + +#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) + + +static int wait_for_renderer(); + + +static int pdf_count_pages(const char *filename) +{ + char gscommand[4095]; + char output[31] = ""; + int pagecount; + size_t bytes; + + snprintf(gscommand, 4095, "%s -dNODISPLAY -q -c " + "'/pdffile (%s) (r) file def pdfdict begin pdffile pdfopen begin " + "(PageCount: ) print pdfpagecount == flush currentdict pdfclose " + "end end quit'", + gspath, filename); + + FILE *pd = popen(gscommand, "r"); + if (!pd) + rip_die(EXIT_STARVED, "Failed to execute ghostscript to determine number of input pages!\n"); + + bytes = fread(output, 1, 31, pd); + pclose(pd); + + if (bytes <= 0 || sscanf(output, "PageCount: %d", &pagecount) < 1) + pagecount = -1; + + return pagecount; +} + +pid_t kid3 = 0; + + +static int start_renderer(const char *cmd) +{ + if (kid3 != 0) + wait_for_renderer(); + + _log("Starting renderer with command: %s\n", cmd); + kid3 = start_process("kid3", exec_kid3, (void *)cmd, NULL, NULL); + if (kid3 < 0) + rip_die(EXIT_STARVED, "Could not start renderer\n"); + + return 1; +} + +static int wait_for_renderer() +{ + int status; + + waitpid(kid3, &status, 0); + + if (!WIFEXITED(status)) { + _log("Kid3 did not finish normally.\n"); + exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS); + } + + _log("Kid3 exit status: %d\n", WEXITSTATUS(status)); + if (WEXITSTATUS(status) != 0) + exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS); + + kid3 = 0; + return 1; +} + +/* + * Extract pages 'first' through 'last' from the pdf and write them into a + * temporary file. + */ +static int pdf_extract_pages(char filename[PATH_MAX], + const char *pdffilename, + int first, + int last) +{ + char gscommand[4095]; + char filename_arg[PATH_MAX], first_arg[50], last_arg[50]; + int fd; + + _log("Extracting pages %d through %d\n", first, last); + + snprintf(filename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir()); + if ((fd = mkstemp(filename)) == -1) + rip_die(EXIT_STARVED, "Unable to create temporary file!\n"); + close (fd); + + snprintf(filename_arg, PATH_MAX, "-sOutputFile=%s", filename); + snprintf(first_arg, 50, "-dFirstPage=%d", first); + if (last > 0) + snprintf(last_arg, 50, "-dLastPage=%d", last); + else + first_arg[0] = '\0'; + + snprintf(gscommand, 4095, "%s -q -dNOPAUSE -dBATCH -dPARANOIDSAFER -dNOINTERPOLATE" + "-sDEVICE=pdfwrite %s %s %s %s", + gspath, filename_arg, first_arg, last_arg, pdffilename); + + FILE *pd = popen(gscommand, "r"); + if (!pd) + rip_die(EXIT_STARVED, "Could not run ghostscript to extract the pages!\n"); + pclose(pd); + + return 1; +} + +static int render_pages_with_generic_command(dstr_t *cmd, + const char *filename, + int firstpage, + int lastpage) +{ + char tmpfile[PATH_MAX]; + int result; + + /* TODO it might be a good idea to give pdf command lines the possibility + * to get the file on the command line rather than piped through stdin + * (maybe introduce a &filename; ??) */ + + if (lastpage < 0) /* i.e. print the whole document */ + dstrcatf(cmd, " < %s", filename); + else + { + if (!pdf_extract_pages(tmpfile, filename, firstpage, lastpage)) + rip_die(EXIT_STARVED, "Could not run ghostscript to extract the pages!\n"); + dstrcatf(cmd, " < %s", tmpfile); + } + + result = start_renderer(cmd->data); + + if (lastpage > 0) + unlink(tmpfile); + + return result; +} + +static int render_pages_with_ghostscript(dstr_t *cmd, + size_t start_gs_cmd, + size_t end_gs_cmd, + const char *filename, + int firstpage, + int lastpage) +{ + char *p; + + /* No need to create a temporary file, just give ghostscript the file and + * first/last page on the command line */ + + /* Some command lines want to read from stdin */ + for (p = &cmd->data[end_gs_cmd -1]; isspace(*p); p--) + ; + if (*p == '-') + *p = ' '; + + dstrinsertf(cmd, end_gs_cmd, " %s ", filename); + + if (lastpage > 0) + dstrinsertf(cmd, start_gs_cmd +2, + " -dFirstPage=%d -dLastPage=%d ", + firstpage, lastpage); + else + dstrinsertf(cmd, start_gs_cmd +2, + " -dFirstPage=%d ", firstpage); + + return start_renderer(cmd->data); +} + +static int render_pages(const char *filename, int firstpage, int lastpage) +{ + dstr_t *cmd = create_dstr(); + size_t start, end; + int result; + + build_commandline(optionset("currentpage"), cmd, 1); + + extract_command(&start, &end, cmd->data, "gs"); + if (start == end) + /* command is not Ghostscript */ + result = render_pages_with_generic_command(cmd, + filename, + firstpage, + lastpage); + else + /* Ghostscript command, tell it which pages we want to render */ + result = render_pages_with_ghostscript(cmd, + start, + end, + filename, + firstpage, + lastpage); + + free_dstr(cmd); + return result; +} + +static int print_pdf_file(const char *filename) +{ + int page_count, i; + int firstpage; + + page_count = pdf_count_pages(filename); + + if (page_count <= 0) + rip_die(EXIT_JOBERR, "Unable to determine number of pages, page count: %d\n", page_count); + _log("File contains %d pages\n", page_count); + + optionset_copy_values(optionset("header"), optionset("currentpage")); + optionset_copy_values(optionset("currentpage"), optionset("previouspage")); + firstpage = 1; + for (i = 1; i <= page_count; i++) + { + set_options_for_page(optionset("currentpage"), i); + if (!optionset_equal(optionset("currentpage"), optionset("previouspage"), 1)) + { + render_pages(filename, firstpage, i); + firstpage = i; + } + optionset_copy_values(optionset("currentpage"), optionset("previouspage")); + } + if (firstpage == 1) + render_pages(filename, 1, -1); /* Render the whole document */ + else + render_pages(filename, firstpage, page_count); + + wait_for_renderer(); + + return 1; +} + +int print_pdf(FILE *s, + const char *alreadyread, + size_t len, + const char *filename, + size_t startpos) +{ + char tmpfilename[PATH_MAX] = ""; + int result; + + /* If reading from stdin, write everything into a temporary file */ + /* TODO don't do this if there aren't any pagerange-limited options */ + if (s == stdin) + { + int fd; + FILE *tmpfile; + + snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir()); + fd = mkstemp(tmpfilename); + if (fd < 0) { + _log("Could not create temporary file: %s\n", strerror(errno)); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; + } + + tmpfile = fdopen(fd, "r+"); + copy_file(tmpfile, stdin, alreadyread, len); + fclose(tmpfile); + + filename = tmpfilename; + } + + result = print_pdf_file(filename); + + if (!isempty(tmpfilename)) + unlink(tmpfilename); + + return result; +} + diff --git a/filter/foomatic-rip/pdf.h b/filter/foomatic-rip/pdf.h new file mode 100644 index 000000000..f2f9b1c91 --- /dev/null +++ b/filter/foomatic-rip/pdf.h @@ -0,0 +1,30 @@ +/* pdf.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef pdf_h +#define pdf_h + +int print_pdf(FILE *s, const char *alreadyread, size_t len, const char *filename, int startpos); + +#endif + diff --git a/filter/foomatic-rip/postscript.c b/filter/foomatic-rip/postscript.c new file mode 100644 index 000000000..59c33197b --- /dev/null +++ b/filter/foomatic-rip/postscript.c @@ -0,0 +1,1223 @@ +/* postscript.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "foomaticrip.h" +#include "util.h" +#include "options.h" +#include "renderer.h" +#include "process.h" + +#include <errno.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> + +void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid); +int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid); + +#define LT_BEGIN_FEATURE 1 +#define LT_FOOMATIC_RIP_OPTION_SETTING 2 +int line_type(const char *line) +{ + const char *p; + if (startswith(line, "%%BeginFeature:")) + return LT_BEGIN_FEATURE; + p = line; + while (*p && isspace(*p)) p++; + if (!startswith(p, "%%")) + return 0; + p += 2; + while (*p && isspace(*p)) p++; + if (startswith(p, "FoomaticRIPOptionSetting:")) + return LT_FOOMATIC_RIP_OPTION_SETTING; + return 0; +} + + +/* Next, examine the PostScript job for traces of command-line and + JCL options. PPD-aware applications and spoolers stuff option + settings directly into the file, they do not necessarily send + PPD options by the command line. Also stuff in PostScript code + to apply option settings given by the command line and to set + the defaults given in the PPD file. + + Examination strategy: read lines from STDIN until the first + %%Page: comment appears and save them as @psheader. This is the + page-independent header part of the PostScript file. The + PostScript interpreter (renderer) must execute this part once + before rendering any assortment of pages. Then pages can be + printed in any arbitrary selection or order. All option + settings we find here will be collected in the default option + set for the RIP command line. + + Now the pages will be read and sent to the renderer, one after + the other. Every page is read into memory until the + %%EndPageSetup comment appears (or a certain amount of lines was + read). So we can get option settings only valid for this + page. If we have such settings we set them in the modified + command set for this page. + + If the renderer is not running yet (first page) we start it with + the command line built from the current modified command set and + send the first page to it, in the end we leave the renderer + running and keep input and output pipes open, so that it can + accept further pages. If the renderer is still running from + the previous page and the current modified command set is the + same as the one for the previous page, we send the page. If + the command set is different, we close the renderer, re-start + it with the command line built from the new modified command + set, send the header again, and then the page. + + After the last page the trailer (%%Trailer) is sent. + + The output pipe of this program stays open all the time so that + the spooler does not assume that the job has finished when the + renderer is re-started. + + Non DSC-conforming documents will be read until a certain line + number is reached. Command line or JCL options inserted later + will be ignored. + + If options are implemented by PostScript code supposed to be + stuffed into the job's PostScript data we stuff the code for all + these options into our job data, So all default settings made in + the PPD file (the user can have edited the PPD file to change + them) are taken care of and command line options get also + applied. To give priority to settings made by applications we + insert the options's code in the beginnings of their respective + sections, so that sommething, which is already inserted, gets + executed after our code. Missing sections are automatically + created. In non-DSC-conforming files we insert the option code + in the beginning of the file. This is the same policy as used by + the "pstops" filter of CUPS. + + If CUPS is the spooler, the option settings were already + inserted by the "pstops" filter, so we don't insert them + again. The only thing we do is correcting settings of numerical + options when they were set to a value not available as choice in + the PPD file, As "pstops" does not support "real" numerical + options, it sees these settings as an invalid choice and stays + with the default setting. In this case we correct the setting in + the first occurence of the option's code, as this one is the one + added by CUPS, later occurences come from applications and + should not be touched. + + If the input is not PostScript (if there is no "%!" after + $maxlinestopsstart lines) we will abort the document with an error. +*/ + +/* PostScript sections */ +#define PS_SECTION_JCLSETUP 1 +#define PS_SECTION_PROLOG 2 +#define PS_SECTION_SETUP 3 +#define PS_SECTION_PAGESETUP 4 + +#define MAX_NON_DSC_LINES_IN_HEADER 1000 +#define MAX_LINES_FOR_PAGE_OPTIONS 200 + +typedef struct { + size_t pos; + + FILE *file; + const char *alreadyread; + size_t len; +} stream_t; + +void _print_ps(stream_t *stream); + +int stream_next_line(dstr_t *line, stream_t *s) +{ + int c; + size_t cnt = 0; + + dstrclear(line); + while (s->pos < s->len) { + c = s->alreadyread[s->pos++]; + dstrputc(line, c); + cnt++; + if (c == '\n') + return cnt; + } + + while ((c = fgetc(s->file)) != EOF) { + dstrputc(line, c); + cnt++; + if (c == '\n') + return cnt; + } + return cnt; +} + +int print_ps(FILE *file, const char *alreadyread, size_t len, const char *filename) +{ + stream_t stream; + + if (file != stdin && (dup2(fileno(file), fileno(stdin)) < 0)) { + _log("Could not dup %s to stdin.\n", filename); + return 0; + } + + stream.pos = 0; + stream.file = stdin; + stream.alreadyread = alreadyread; + stream.len = len; + _print_ps(&stream); + return 1; +} + +void _print_ps(stream_t *stream) +{ + char *p; + + int maxlines = 1000; /* Maximum number of lines to be read when the + documenent is not DSC-conforming. + "$maxlines = 0" means that all will be read and + examined. If it is discovered that the input + file is DSC-conforming, this will be set to 0. */ + + int maxlinestopsstart = 200; /* That many lines are allowed until the + "%!" indicating PS comes. These + additional lines in the + beginning are usually JCL + commands. The lines will be + ignored by our parsing but + passed through. */ + + int printprevpage = 0; /* We set this when encountering "%%Page:" and the + previous page is not printed yet. Then it will + be printed and the new page will be prepared in + the next run of the loop (we don't read a new + line and don't increase the $linect then). */ + + int linect = 0; /* how many lines have we examined */ + int nonpslines = 0; /* lines before "%!" found yet. */ + int more_stuff = 1; /* there is more stuff in stdin */ + int saved = 0; /* DSC line not precessed yet */ + int isdscjob = 0; /* is the job dsc conforming */ + int inheader = 1; /* Are we still in the header, before first + "%%Page:" comment= */ + + int optionsalsointoheader = 0; /* 1: We are in a "%%BeginSetup... + %%EndSetup" section after the first + "%%Page:..." line (OpenOffice.org + does this and intends the options here + apply to the whole document and not + only to the current page). We have to + add all lines also to the end of the + @psheader now and we have to set + non-PostScript options also in the + "header" optionset. 0: otherwise. */ + + int insertoptions = 1; /* If we find out that a file with a DSC magic + string ("%!PS-Adobe-") is not really DSC- + conforming, we insert the options directly + after the line with the magic string. We use + this variable to store the number of the line + with the magic string */ + + int prologfound = 0; /* Did we find the + "%%BeginProlog...%%EndProlog" section? */ + int setupfound = 0; /* Did we find the + %%BeginSetup...%%EndSetup" section? */ + int pagesetupfound = 0; /* special page setup handling needed */ + + int inprolog = 0; /* We are between "%%BeginProlog" and "%%EndProlog" */ + int insetup = 0; /* We are between "%%BeginSetup" and "%%EndSetup" */ + int infeature = 0; /* We are between "%%BeginFeature" and "%%EndFeature" */ + + int optionreplaced = 0; /* Will be set to 1 when we are in an + option ("%%BeginFeature... + %%EndFeature") which we have replaced. */ + + int postscriptsection = PS_SECTION_JCLSETUP; /* In which section of the PostScript file + are we currently ? */ + + int nondsclines = 0; /* Number of subsequent lines found which are at a + non-DSC-conforming place, between the sections + of the header.*/ + + int nestinglevel = 0; /* Are we in the main document (0) or in an + embedded document bracketed by "%%BeginDocument" + and "%%EndDocument" (>0) We do not parse the + PostScript in an embedded document. */ + + int inpageheader = 0; /* Are we in the header of a page, + between "%%BeginPageSetup" and + "%%EndPageSetup" (1) or not (0). */ + + int passthru = 0; /* 0: write data into psfifo, + 1: pass data directly to the renderer */ + + int lastpassthru = 0; /* State of 'passthru' in previous line + (to allow debug output when $passthru + switches. */ + + int ignorepageheader = 0; /* Will be set to 1 as soon as active + code (not between "%%BeginPageSetup" + and "%%EndPageSetup") appears after a + "%%Page:" comment. In this case + "%%BeginPageSetup" and + "%%EndPageSetup" is not allowed any + more on this page and will be ignored. + Will be set to 0 when a new "%%Page:" + comment appears. */ + + int optset = optionset("header"); /* Where do the option settings which + we have found go? */ + + /* current line */ + dstr_t *line = create_dstr(); + + dstr_t *onelinebefore = create_dstr(); + dstr_t *twolinesbefore = create_dstr(); + + /* The header of the PostScript file, to be send after each start of the renderer */ + dstr_t *psheader = create_dstr(); + + /* The input FIFO, data which we have pulled from stdin for examination, + but not send to the renderer yet */ + dstr_t *psfifo = create_dstr(); + + int ignoreline; + + int ooo110 = 0; /* Flag to work around an application bug */ + + int currentpage = 0; /* The page which we are currently printing */ + + option_t *o; + const char *val; + + int linetype; + + dstr_t *linesafterlastbeginfeature = create_dstr(); /* All codelines after the last "%%BeginFeature" */ + + char optionname [128]; + char value [128]; + int fromcomposite = 0; + + dstr_t *pdest; + + double width, height; + + pid_t rendererpid = 0; + FILE *rendererhandle = NULL; + + int retval; + + dstr_t *tmp = create_dstr(); + jobhasjcl = 0; + + /* We do not parse the PostScript to find Foomatic options, we check + only whether we have PostScript. */ + if (dontparse) + maxlines = 1; + + _log("Reading PostScript input ...\n"); + + do { + ignoreline = 0; + + if (printprevpage || saved || stream_next_line(line, stream)) { + saved = 0; + if (linect == nonpslines) { + /* In the beginning should be the postscript leader, + sometimes after some JCL commands */ + if ( !(line->data[0] == '%' && line->data[1] == '!') && + !(line->data[1] == '%' && line->data[2] == '!')) /* There can be a Windows control character before "%!" */ + { + nonpslines++; + if (maxlines == nonpslines) + maxlines ++; + jobhasjcl = 1; + + if (nonpslines > maxlinestopsstart) { + /* This is not a PostScript job, abort it */ + _log("Job does not start with \"%%!\", is it Postscript?\n"); + rip_die(EXIT_JOBERR, "Unknown data format.\n"); + } + } + else { + /* Do we have a DSC-conforming document? */ + if ((line->data[0] == '%' && startswith(line->data, "%!PS-Adobe-")) || + (line->data[1] == '%' && startswith(line->data, "%!PS-Adobe-"))) + { + /* Do not stop parsing the document */ + if (!dontparse) { + maxlines = 0; + isdscjob = 1; + insertoptions = linect + 1; + /* We have written into psfifo before, now we continue in + psheader and move over the data which is already in psfifo */ + dstrcat(psheader, psfifo->data); + dstrclear(psfifo); + } + _log("--> This document is DSC-conforming!\n"); + } + else { + /* Job is not DSC-conforming, stick in all PostScript + option settings in the beginning */ + append_prolog_section(line, optset, 1); + append_setup_section(line, optset, 1); + append_page_setup_section(line, optset, 1); + prologfound = 1; + setupfound = 1; + pagesetupfound = 1; + } + } + } + else { + if (startswith(line->data, "%")) { + if (startswith(line->data, "%%BeginDocument")) { + /* Beginning of an embedded document + Note that Adobe Acrobat has a bug and so uses + "%%BeginDocument " instead of "%%BeginDocument:" */ + nestinglevel++; + _log("Embedded document, nesting level now: %d\n", nestinglevel); + } + else if (nestinglevel > 0 && startswith(line->data, "%%EndDocument")) { + /* End of an embedded document */ + nestinglevel--; + _log("End of embedded document, nesting level now: %d\n", nestinglevel); + } + else if (nestinglevel == 0 && startswith(line->data, "%%Creator")) { + /* Here we set flags to treat particular bugs of the + PostScript produced by certain applications */ + p = strstr(line->data, "%%Creator") + 9; + while (*p && (isspace(*p) || *p == ':')) p++; + if (!strcmp(p, "OpenOffice.org")) { + p += 14; + while (*p && isspace(*p)) p++; + if (sscanf(p, "1.1.%d", &ooo110) == 1) { + _log("Document created with OpenOffice.org 1.1.x\n"); + ooo110 = 1; + } + } else if (!strcmp(p, "StarOffice 8")) { + p += 12; + _log("Document created with StarOffice 8\n"); + ooo110 = 1; + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%BeginProlog")) { + /* Note: Below is another place where a "Prolog" section + start will be considered. There we assume start of the + "Prolog" if the job is DSC-Conformimg, but an arbitrary + comment starting with "%%Begin", but not a comment + explicitly treated here, is found. This is done because + many "dvips" (TeX/LaTeX) files miss the "%%BeginProlog" + comment. + Beginning of Prolog */ + _log("\n-----------\nFound: %%%%BeginProlog\n"); + inprolog = 1; + if (inheader) + postscriptsection = PS_SECTION_PROLOG; + nondsclines = 0; + /* Insert options for "Prolog" */ + if (!prologfound) { + append_prolog_section(line, optset, 0); + prologfound = 1; + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%EndProlog")) { + /* End of Prolog */ + _log("Found: %%%%EndProlog\n"); + inprolog = 0; + insertoptions = linect +1; + } + else if (nestinglevel == 0 && startswith(line->data, "%%BeginSetup")) { + /* Beginning of Setup */ + _log("\n-----------\nFound: %%%%BeginSetup\n"); + insetup = 1; + nondsclines = 0; + /* We need to distinguish with the $inheader variable + here whether we are in the header or on a page, as + OpenOffice.org inserts a "%%BeginSetup...%%EndSetup" + section after the first "%%Page:..." line and assumes + this section to be valid for all pages. */ + if (inheader) { + postscriptsection = PS_SECTION_SETUP; + /* If there was no "Prolog" but there are + options for the "Prolog", push a "Prolog" + with these options onto the psfifo here */ + if (!prologfound) { + dstrclear(tmp); + append_prolog_section(tmp, optset, 1); + dstrprepend(line, tmp->data); + prologfound = 1; + } + /* Insert options for "DocumentSetup" or "AnySetup" */ + if (spooler != SPOOLER_CUPS && !setupfound) { + /* For non-CUPS spoolers or no spooler at all, + we leave everythnig as it is */ + append_setup_section(line, optset, 0); + setupfound = 1; + } + } + else { + /* Found option settings must be stuffed into both + the header and the currrent page now. They will + be written into both the "header" and the + "currentpage" optionsets and the PostScript code + lines of this section will not only go into the + output stream, but also added to the end of the + @psheader, so that they get repeated (to preserve + the embedded PostScript option settings) on a + restart of the renderer due to command line + option changes */ + optionsalsointoheader = 1; + _log("\"%%%%BeginSetup\" in page header\n"); + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%EndSetup")) { + /* End of Setup */ + _log("Found: %%%%EndSetup\n"); + insetup = 0; + if (inheader) { + if (spooler == SPOOLER_CUPS) { + /* In case of CUPS, we must insert the + accounting stuff just before the + %%EndSetup comment in order to leave any + EndPage procedures that have been + defined by either the pstops filter or + the PostScript job itself fully + functional. */ + if (!setupfound) { + dstrclear(tmp); + append_setup_section(tmp, optset, 0); + dstrprepend(line, tmp->data); + setupfound = 1; + } + } + insertoptions = linect +1; + } + else { + /* The "%%BeginSetup...%%EndSetup" which + OpenOffice.org has inserted after the first + "%%Page:..." line ends here, so the following + options go only onto the current page again */ + optionsalsointoheader = 0; + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%Page:")) { + if (!lastpassthru && !inheader) { + /* In the last line we were not in passthru mode, + so the last page is not printed. Prepare to do + it now. */ + printprevpage = 1; + passthru = 1; + _log("New page found but previous not printed, print it now.\n"); + } + else { + /* the previous page is printed, so we can prepare + the current one */ + _log("\n-----------\nNew page: %s", line->data); + printprevpage = 0; + currentpage++; + /* We consider the beginning of the page already as + page setup section, as some apps do not use + "%%PageSetup" tags. */ + postscriptsection = PS_SECTION_PAGESETUP; + + /* TODO can this be removed? + Save PostScript state before beginning the page + $line .= "/foomatic-saved-state save def\n"; */ + + /* Here begins a new page */ + if (inheader) { + build_commandline(optset, NULL, 0); + /* Here we add some stuff which still + belongs into the header */ + dstrclear(tmp); + + /* If there was no "Setup" but there are + options for the "Setup", push a "Setup" + with these options onto the @psfifo here */ + if (!setupfound) { + append_setup_section(tmp, optset, 1); + setupfound = 1; + } + /* If there was no "Prolog" but there are + options for the "Prolog", push a "Prolog" + with these options onto the @psfifo here */ + if (!prologfound) { + append_prolog_section(tmp, optset, 1); + prologfound = 1; + } + /* Now we push this into the header */ + dstrcat(psheader, tmp->data); + + /* The first page starts, so header ends */ + inheader = 0; + nondsclines = 0; + /* Option setting should go into the page + specific option set now */ + optset = optionset("currentpage"); + } + else { + /* Restore PostScript state after completing the + previous page: + + foomatic-saved-state restore + %%Page: ... + /foomatic-saved-state save def + + Print this directly, so that if we need to + restart the renderer for this page due to + a command line change this is done under the + old instance of the renderer + rint $rendererhandle + "foomatic-saved-state restore\n"; */ + + /* Save the option settings of the previous page */ + optionset_copy_values(optionset("currentpage"), optionset("previouspage")); + optionset_delete_values(optionset("currentpage")); + } + /* Initialize the option set */ + optionset_copy_values(optionset("header"), optionset("currentpage")); + + /* Set the command line options which apply only + to given pages */ + set_options_for_page(optionset("currentpage"), currentpage); + pagesetupfound = 0; + if (spooler == SPOOLER_CUPS) { + /* Remove the "notfirst" flag from all options + forseen for the "PageSetup" section, because + when these are numerical options for CUPS. + they have to be set to the correct value + for every page */ + for (o = optionlist; o; o = o->next) { + if (option_get_section(o ) == SECTION_PAGESETUP) + o->notfirst = 0; + } + } + /* Now the page header comes, so buffer the data, + because we must perhaps shut down and restart + the renderer */ + passthru = 0; + ignorepageheader = 0; + optionsalsointoheader = 0; + } + } + else if (nestinglevel == 0 && !ignorepageheader && + startswith(line->data, "%%BeginPageSetup")) { + /* Start of the page header, up to %%EndPageSetup + nothing of the page will be drawn, page-specific + option settngs (as letter-head paper for page 1) + go here*/ + _log("\nFound: %%%%BeginPageSetup\n"); + passthru = 0; + inpageheader = 1; + postscriptsection = PS_SECTION_PAGESETUP; + optionsalsointoheader = (ooo110 && currentpage == 1) ? 1 : 0; + /* Insert PostScript option settings + (options for section "PageSetup") */ + if (isdscjob) { + append_page_setup_section(line, optset, 0); + pagesetupfound = 1; + } + } + else if (nestinglevel == 0 && !ignorepageheader && + startswith(line->data, "%%BeginPageSetup")) { + /* End of the page header, the page is ready to be printed */ + _log("Found: %%%%EndPageSetup\n"); + _log("End of page header\n"); + /* We cannot for sure say that the page header ends here + OpenOffice.org puts (due to a bug) a "%%BeginSetup... + %%EndSetup" section after the first "%%Page:...". It + is possible that CUPS inserts a "%%BeginPageSetup... + %%EndPageSetup" before this section, which means that + the options in the "%%BeginSetup...%%EndSetup" + section are after the "%%EndPageSetup", so we + continue for searching options up to the buffer size + limit $maxlinesforpageoptions. */ + passthru = 0; + inpageheader = 0; + optionsalsointoheader = 0; + } + else if (nestinglevel == 0 && !optionreplaced && (!passthru || !isdscjob) && + ((linetype = line_type(line->data)) && + (linetype == LT_BEGIN_FEATURE || linetype == LT_FOOMATIC_RIP_OPTION_SETTING))) { + + /* parse */ + if (linetype == LT_BEGIN_FEATURE) { + dstrcpy(tmp, line->data); + p = strtok(tmp->data, " \t"); /* %%BeginFeature: */ + p = strtok(NULL, " \t="); /* Option */ + if (*p == '*') p++; + strlcpy(optionname, p, 128); + p = strtok(NULL, " \t\r\n"); /* value */ + fromcomposite = 0; + strlcpy(value, p, 128); + } + else { /* LT_FOOMATIC_RIP_OPTION_SETTING */ + dstrcpy(tmp, line->data); + p = strstr(tmp->data, "FoomaticRIPOptionSetting:"); + p = strtok(p, " \t"); /* FoomaticRIPOptionSetting */ + p = strtok(NULL, " \t="); /* Option */ + strlcpy(optionname, p, 128); + p = strtok(NULL, " \t\r\n"); /* value */ + if (*p == '@') { /* fromcomposite */ + p++; + fromcomposite = 1; + } + else + fromcomposite = 0; + strlcpy(value, p, 128); + } + + /* Mark that we are in a "Feature" section */ + if (linetype == LT_BEGIN_FEATURE) { + infeature = 1; + dstrclear(linesafterlastbeginfeature); + } + + /* OK, we have an option. If it's not a + Postscript-style option (ie, it's command-line or + JCL) then we should note that fact, since the + attribute-to-filter option passing in CUPS is kind of + funky, especially wrt boolean options. */ + _log("Found: %s", line->data); + if ((o = find_option(optionname)) && + (o->type != TYPE_NONE)) { + _log(" Option: %s=%s%s\n", optionname, fromcomposite ? "From" : "", value); + if (spooler == SPOOLER_CUPS && + linetype == LT_BEGIN_FEATURE && + !option_get_value(o, optionset("notfirst")) && + strcmp(option_get_value(o, optset) ?: "", value) != 0 && + (inheader || option_get_section(o) == SECTION_PAGESETUP)) { + + /* We have the first occurence of an option + setting and the spooler is CUPS, so this + setting is inserted by "pstops" or + "imagetops". The value from the command + line was not inserted by "pstops" or + "imagetops" so it seems to be not under + the choices in the PPD. Possible + reasons: + + - "pstops" and "imagetops" ignore settings + of numerical or string options which are + not one of the choices in the PPD file, + and inserts the default value instead. + + - On the command line an option was applied + only to selected pages: + "-o <page ranges>:<option>=<values> + This is not supported by CUPS, so not + taken care of by "pstops". + + We must fix this here by replacing the + setting inserted by "pstops" or "imagetops" + with the exact setting given on the command + line. */ + + /* $arg->{$optionset} is already + range-checked, so do not check again here + Insert DSC comment */ + pdest = (inheader && isdscjob) ? psheader : psfifo; + if (option_is_ps_command(o)) { + /* PostScript option, insert the code */ + + option_get_command(tmp, o, optset, -1); + if (!(val = option_get_value(o, optset))) + val = ""; + + /* Boolean and enumerated choice options can only be set in the + * PageSetup section */ + if ((inheader && option_is_custom_value(o, val)) || !inheader) + { + if (o->type == TYPE_BOOL) + dstrcatf(pdest, "%%%%BeginFeature: *%s %s\n", o->name, + val && !strcmp(val, "1") ? "True" : "False"); + else + dstrcatf(pdest, "%%%%BeginFeature: *%s %s\n", o->name, val); + + dstrcatf(pdest, "%s\n", tmp->data); + + /* We have replaced this option on the FIFO */ + optionreplaced = 1; + } + } + else { /* Command line or JCL option */ + val = option_get_value(o, optset); + + if (!inheader || option_is_custom_value(o, val)) { + dstrcatf(pdest, "%%%% FoomaticRIPOptionSetting: %s=%s\n", + o->name, val ? val : ""); + optionreplaced = 1; + } + } + + if (optionreplaced) { + val = option_get_value(o, optset); + _log(" --> Correcting numerical/string option to %s=%s (Command line argument)\n", + o->name, val ? val : ""); + } + } + + /* Mark that we have already found this option */ + o->notfirst = 1; + if (!optionreplaced) { + if (o->style != 'G') { + /* Controlled by '<Composite>' setting of + a member option of a composite option */ + if (fromcomposite) { + dstrcpyf(tmp, "From%s", value); + strlcpy(value, tmp->data, 128); + } + + /* Non PostScript option + Check whether it is valid */ + if (option_set_value(o, optset, value)) { + _log("Setting option\n"); + strlcpy(value, option_get_value(o, optset), 128); + if (optionsalsointoheader) + option_set_value(o, optionset("header"), value); + if (o->type == TYPE_ENUM && + (!strcmp(o->name, "PageSize") || !strcmp(o->name, "PageRegion")) && + startswith(value, "Custom") && + linetype == LT_FOOMATIC_RIP_OPTION_SETTING) { + /* Custom Page size */ + width = height = 0.0; + p = linesafterlastbeginfeature->data; + while (*p && isspace(*p)) p++; + width = strtod(p, &p); + while (*p && isspace(*p)) p++; + height = strtod(p, &p); + if (width && height) { + dstrcpyf(tmp, "%s.%fx%f", value, width, height); + strlcpy(value, tmp->data, 128); + option_set_value(o, optset, value); + if (optionsalsointoheader) + option_set_value(o, optionset("header"), value); + } + } + /* For a composite option insert the + code from the member options with + current setting "From<composite>" + The code from the member options + is chosen according to the setting + of the composite option. */ + if (option_is_composite(o) && linetype == LT_FOOMATIC_RIP_OPTION_SETTING) { + build_commandline(optset, NULL, 0); /* TODO can this be removed? */ + + /* TODO merge section and ps_section */ + if (postscriptsection == PS_SECTION_JCLSETUP) + option_get_command(tmp, o, optset, SECTION_JCLSETUP); + else if (postscriptsection == PS_SECTION_PROLOG) + option_get_command(tmp, o, optset, SECTION_PROLOG); + else if (postscriptsection == PS_SECTION_SETUP) + option_get_command(tmp, o, optset, SECTION_DOCUMENTSETUP); + else if (postscriptsection == PS_SECTION_PAGESETUP) + option_get_command(tmp, o, optset, SECTION_PAGESETUP); + dstrcat(line, tmp->data); + } + } + else + _log(" --> Invalid option setting found in job\n"); + } + else if (fromcomposite) { + /* PostScript option, but we have to look up + the PostScript code to be inserted from + the setting of a composite option, as + this option is set to "Controlled by + '<Composite>'". */ + /* Set the option */ + dstrcpyf(tmp, "From%s", value); + strlcpy(value, tmp->data, 128); + if (option_set_value(o, optset, value)) { + _log(" --> Looking up setting in composite option %s\n", value); + if (optionsalsointoheader) + option_set_value(o, optionset("header"), value); + /* update composite options */ + build_commandline(optset, NULL, 0); + /* Substitute PostScript comment by the real code */ + /* TODO what exactly is the next line doing? */ + /* dstrcpy(line, o->compositesubst->data); */ + } + else + _log(" --> Invalid option setting found in job\n"); + } + else + /* it is a PostScript style option with + the code readily inserted, no option + for the renderer command line/JCL to set, + no lookup of a composite option needed, + so nothing to do here... */ + _log(" --> Option will be set by PostScript interpreter\n"); + } + } + else + /* This option is unknown to us, WTF? */ + _log("Unknown option %s=%s found in the job\n", optionname, value); + } + else if (nestinglevel == 0 && startswith(line->data, "%%EndFeature")) { + /* End of feature */ + infeature = 0; + /* If the option setting was replaced, it ends here, + too, and the next option is not necessarily also replaced */ + optionreplaced = 0; + dstrclear(linesafterlastbeginfeature); + } + else if (nestinglevel == 0 && isdscjob && !prologfound && + startswith(line->data, "%%Begin")) { + /* In some PostScript files (especially when generated + by "dvips" of TeX/LaTeX) the "%%BeginProlog" is + missing, so assume that it was before the current + line (the first line starting with "%%Begin". */ + _log("Job claims to be DSC-conforming, but \"%%%%BeginProlog\" " + "was missing before first line with another" + "\"%%%%BeginProlog\" comment (is this a TeX/LaTeX/dvips-generated" + " PostScript file?). Assuming start of \"Prolog\" here.\n"); + /* Beginning of Prolog */ + inprolog = 1; + nondsclines = 0; + /* Insert options for "Prolog" before the current line */ + dstrcpyf(tmp, "%%%%BeginProlog\n"); + append_prolog_section(tmp, optset, 0); + dstrprepend(line, tmp->data); + prologfound = 1; + } + else if (nestinglevel == 0 && ( + startswith(line->data, "%RBINumCopies:") || + startswith(line->data, "%%RBINumCopies:"))) { + p = strchr(line->data, ':') +1; + get_current_job()->rbinumcopies = atoi(p); + _log("Found %RBINumCopies: %d\n", get_current_job()->rbinumcopies); + } + else if (startswith(skip_whitespace(line->data), "%") || + startswith(skip_whitespace(line->data), "$")) + /* This is an unknown PostScript comment or a blank + line, no active code */ + ignoreline = 1; + } + else { + /* This line is active PostScript code */ + if (infeature) + /* Collect coe in a "%%BeginFeature: ... %%EndFeature" + section, to get the values for a custom option + setting */ + dstrcat(linesafterlastbeginfeature, line->data); + + if (inheader) { + if (!inprolog && !insetup) { + /* Outside the "Prolog" and "Setup" section + a correct DSC-conforming document has no + active PostScript code, so consider the + file as non-DSC-conforming when there are + too many of such lines. */ + nondsclines++; + if (nondsclines > MAX_NON_DSC_LINES_IN_HEADER) { + /* Consider document as not DSC-conforming */ + _log("This job seems not to be DSC-conforming, " + "DSC-comment for next section not found, " + "stopping to parse the rest, passing it " + "directly to the renderer.\n"); + /* Stop scanning for further option settings */ + maxlines = 1; + isdscjob = 0; + /* Insert defaults and command line settings in + the beginning of the job or after the last valid + section */ + dstrclear(tmp); + if (prologfound) + append_prolog_section(tmp, optset, 1); + if (setupfound) + append_setup_section(tmp, optset, 1); + if (pagesetupfound) + append_page_setup_section(tmp, optset, 1); + dstrinsert(psheader, line_start(psheader->data, insertoptions), tmp->data); + + prologfound = 1; + setupfound = 1; + pagesetupfound = 1; + } + } + } + else if (!inpageheader) { + /* PostScript code inside a page, but not between + "%%BeginPageSetup" and "%%EndPageSetup", so + we are perhaps already drawing onto a page now */ + if (startswith(onelinebefore->data, "%%Page")) + _log("No page header or page header not DSC-conforming\n"); + /* Stop buffering lines to search for options + placed not DSC-conforming */ + if (line_count(psfifo->data) >= MAX_LINES_FOR_PAGE_OPTIONS) { + _log("Stopping search for page header options\n"); + passthru = 1; + /* If there comes a page header now, ignore it */ + ignorepageheader = 1; + optionsalsointoheader = 0; + } + /* Insert PostScript option settings (options for the + * section "PageSetup" */ + if (isdscjob && !pagesetupfound) { + append_page_setup_section(psfifo, optset, 1); + pagesetupfound = 1; + } + } + } + } + + /* Debug Info */ + if (lastpassthru != passthru) { + if (passthru) + _log("Found: %s --> Output goes directly to the renderer now.\n\n", line->data); + else + _log("Found: %s --> Output goes to the FIFO buffer now.\n\n", line->data); + } + + /* We are in an option which was replaced, do not output the current line */ + if (optionreplaced) + dstrclear(line); + + /* If we are in a "%%BeginSetup...%%EndSetup" section after + the first "%%Page:..." and the current line belongs to + an option setting, we have to copy the line also to the + @psheader. */ + if (optionsalsointoheader && (infeature || startswith(line->data, "%%EndFeature"))) + dstrcat(psheader, line->data); + + /* Store or send the current line */ + if (inheader && isdscjob) { + /* We are still in the PostScript header, collect all lines + in @psheader */ + dstrcat(psheader, line->data); + } + else { + if (passthru && isdscjob) { + if (!lastpassthru) { + /* + * We enter passthru mode with this line, so the + * command line can have changed, check it and close + * the renderer if needed + */ + if (rendererpid && !optionset_equal(optionset("currentpage"), optionset("previouspage"), 0)) { + _log("Command line/JCL options changed, restarting renderer\n"); + retval = close_renderer_handle(rendererhandle, rendererpid); + if (retval != EXIT_PRINTED) + rip_die(retval, "Error closing renderer\n"); + rendererpid = 0; + } + } + + /* Flush psfifo and send line directly to the renderer */ + if (!rendererpid) { + /* No renderer running, start it */ + dstrcpy(tmp, psheader->data); + dstrcat(tmp, psfifo->data); + get_renderer_handle(tmp, &rendererhandle, &rendererpid); + /* psfifo is sent out, flush it */ + dstrclear(psfifo); + } + + if (!isempty(psfifo->data)) { + /* Send psfifo to renderer */ + fwrite(psfifo->data, psfifo->len, 1, rendererhandle); + /* flush psfifo */ + dstrclear(psfifo); + } + + /* Send line to renderer */ + if (!printprevpage) { + fwrite(line->data, line->len, 1, rendererhandle); + + while (stream_next_line(line, stream) > 0) { + if (startswith(line->data, "%%")) { + _log("Found: %s", line->data); + _log(" --> Continue DSC parsing now.\n\n"); + saved = 1; + break; + } + else { + fwrite(line->data, line->len, 1, rendererhandle); + linect++; + } + } + } + } + else { + /* Push the line onto the stack to split up later */ + dstrcat(psfifo, line->data); + } + } + + if (!printprevpage) + linect++; + } + else { + /* EOF! */ + more_stuff = 0; + + /* No PostScript header in the whole file? Then it's not + PostScript, convert it. + We open the file converter here when the file has less + lines than the amount which we search for the PostScript + header ($maxlinestopsstart). */ + if (linect <= nonpslines) { + /* This is not a PostScript job, abort it */ + _log("Job does not start with \"%%!\", is it Postscript?\n"); + rip_die(EXIT_JOBERR, "Unknown data format.\n"); + } + } + + lastpassthru = passthru; + + if (!ignoreline && !printprevpage) { + dstrcpy(twolinesbefore, onelinebefore->data); + dstrcpy(onelinebefore, line->data); + } + + } while ((maxlines == 0 || linect < maxlines) && more_stuff != 0); + + /* Some buffer still containing data? Send it out to the renderer */ + if (more_stuff || inheader || !isempty(psfifo->data)) { + /* Flush psfifo and send the remaining data to the renderer, this + only happens with non-DSC-conforming jobs or non-Foomatic PPDs */ + if (more_stuff) + _log("Stopped parsing the PostScript data, " + "sending rest directly to the renderer.\n"); + else + _log("Flushing FIFO.\n"); + + if (inheader) { + build_commandline(optset, NULL, 0); + /* No page initialized yet? Copy the "header" option set into the + "currentpage" option set, so that the renderer will find the + options settings. */ + optionset_copy_values(optionset("header"), optionset("currentpage")); + optset = optionset("currentpage"); + + /* If not done yet, insert defaults and command line settings + in the beginning of the job or after the last valid section */ + dstrclear(tmp); + if (prologfound) + append_prolog_section(tmp, optset, 1); + if (setupfound) + append_setup_section(tmp, optset, 1); + if (pagesetupfound) + append_page_setup_section(tmp, optset, 1); + dstrinsert(psheader, line_start(psheader->data, insertoptions), tmp->data); + + prologfound = 1; + setupfound = 1; + pagesetupfound = 1; + } + + if (rendererpid > 0 && !optionset_equal(optionset("currentpage"), optionset("previouspage"), 0)) { + _log("Command line/JCL options changed, restarting renderer\n"); + retval = close_renderer_handle(rendererhandle, rendererpid); + if (retval != EXIT_PRINTED) + rip_die(retval, "Error closing renderer\n"); + rendererpid = 0; + } + + if (!rendererpid) { + dstrcpy(tmp, psheader->data); + dstrcat(tmp, psfifo->data); + get_renderer_handle(tmp, &rendererhandle, &rendererpid); + /* We have sent psfifo now */ + dstrclear(psfifo); + } + + if (psfifo->len) { + /* Send psfifo to the renderer */ + fwrite(psfifo->data, psfifo->len, 1, rendererhandle); + dstrclear(psfifo); + } + + /* Print the rest of the input data */ + if (more_stuff) { + while (stream_next_line(tmp, stream)) + fwrite(tmp->data, tmp->len, 1, rendererhandle); + } + } + + /* At every "%%Page:..." comment we have saved the PostScript state + and we have increased the page number. So if the page number is + non-zero we had at least one "%%Page:..." comment and so we have + to give a restore the PostScript state. + if ($currentpage > 0) { + print $rendererhandle "foomatic-saved-state restore\n"; + } */ + + /* Close the renderer */ + if (rendererpid) { + retval = close_renderer_handle(rendererhandle, rendererpid); + if (retval != EXIT_PRINTED) + rip_die(retval, "Error closing renderer\n"); + rendererpid = 0; + } + + free_dstr(line); + free_dstr(onelinebefore); + free_dstr(twolinesbefore); + free_dstr(psheader); + free_dstr(psfifo); + free_dstr(tmp); +} + +/* + * Run the renderer command line (and if defined also the postpipe) and returns + * a file handle for stuffing in the PostScript data. + */ +void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid) +{ + pid_t kid3; + FILE *kid3in; + dstr_t *cmdline = create_dstr(); + + /* Build the command line and get the JCL commands */ + build_commandline(optionset("currentpage"), cmdline, 0); + massage_gs_commandline(cmdline); + + _log("\nStarting renderer with command: \"%s\"\n", cmdline->data); + kid3 = start_process("kid3", exec_kid3, (void *)cmdline->data, &kid3in, NULL); + if (kid3 < 0) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Cannot fork for kid3\n"); + + /* Feed the PostScript header and the FIFO contents */ + if (prepend) + fwrite(prepend->data, prepend->len, 1, kid3in); + + /* We are the parent, return glob to the file handle */ + *fd = kid3in; + *pid = kid3; + + free_dstr(cmdline); +} + +/* Close the renderer process and wait until all kid processes finish */ +int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid) +{ + int status; + + _log("\nClosing renderer\n"); + fclose(rendererhandle); + + status = wait_for_process(rendererpid); + if (WIFEXITED(status)) + return WEXITSTATUS(status); + else + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; +} + diff --git a/filter/foomatic-rip/postscript.h b/filter/foomatic-rip/postscript.h new file mode 100644 index 000000000..cb6c52581 --- /dev/null +++ b/filter/foomatic-rip/postscript.h @@ -0,0 +1,30 @@ +/* postscript.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef postscript_h +#define postscript_h + +int print_ps(FILE *s, const char *alreadyread, size_t len, const char *filename); + +#endif + diff --git a/filter/foomatic-rip/process.c b/filter/foomatic-rip/process.c new file mode 100644 index 000000000..d93d2a069 --- /dev/null +++ b/filter/foomatic-rip/process.c @@ -0,0 +1,224 @@ +/* process.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "foomaticrip.h" +#include "process.h" +#include <unistd.h> +#include "util.h" +#include <sys/wait.h> +#include <errno.h> +#include <stdlib.h> + +int kidgeneration = 0; + +struct process { + char name[64]; + pid_t pid; + int isgroup; +}; + +#define MAX_CHILDS 4 +struct process procs[MAX_CHILDS] = { + { "", -1, 0 }, + { "", -1, 0 }, + { "", -1, 0 }, + { "", -1, 0 } +}; + +void add_process(const char *name, int pid, int isgroup) +{ + int i; + for (i = 0; i < MAX_CHILDS; i++) { + if (procs[i].pid == -1) { + strlcpy(procs[i].name, name, 64); + procs[i].pid = pid; + procs[i].isgroup = isgroup; + return; + } + } + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Didn't think there would be that many child processes... Exiting.\n"); +} + +int find_process(int pid) +{ + int i; + for (i = 0; i < MAX_CHILDS; i++) + if (procs[i].pid == pid) + return i; + return -1; +} + +void clear_proc_list() +{ + int i; + for (i = 0; i < MAX_CHILDS; i++) + procs[i].pid = -1; +} + +void kill_all_processes() +{ + int i; + + for (i = 0; i < MAX_CHILDS; i++) { + if (procs[i].pid == -1) + continue; + _log("Killing %s\n", procs[i].name); + kill(procs[i].isgroup ? -procs[i].pid : procs[i].pid, 15); + sleep(1 << (3 - kidgeneration)); + kill(procs[i].isgroup ? -procs[i].pid : procs[i].pid, 9); + } + clear_proc_list(); +} + +static int _start_process(const char *name, + int (*proc_func)(FILE *, FILE *, void *), + void *user_arg, FILE **pipe_in, FILE **pipe_out, + int createprocessgroup) +{ + pid_t pid; + int pfdin[2], pfdout[2]; + int ret; + FILE *in, *out; + + if (pipe_in) + if (pipe(pfdin) < 0) + return -1; + if (pipe_out) + if (pipe(pfdout) < 0) + return -1; + + _log("Starting process \"%s\" (generation %d)\n", name, kidgeneration +1); + + pid = fork(); + if (pid < 0) { + _log("Could not fork for %s\n", name); + if (pipe_in) { + close(pfdin[0]); + close(pfdin[1]); + } + if (pipe_out) { + close(pfdout[0]); + close(pfdout[1]); + } + return -1; + } + + if (pid == 0) { /* Child */ + if (pipe_in) { + close(pfdin[1]); + in = fdopen(pfdin[0], "r"); + } + else + in = NULL; + + if (pipe_out) { + close(pfdout[0]); + out = fdopen(pfdout[1], "w"); + } + else + out = NULL; + + if (createprocessgroup) + setpgid(0, 0); + + kidgeneration++; + + /* The subprocess list is only valid for the parent. Clear it. */ + clear_proc_list(); + + ret = proc_func(in, out, user_arg); + exit(ret); + } + + /* Parent */ + if (pipe_in) { + close(pfdin[0]); + *pipe_in = fdopen(pfdin[1], "w"); + if (!*pipe_in) + _log("fdopen: %s\n", strerror(errno)); + } + if (pipe_out) { + close(pfdout[1]); + *pipe_out = fdopen(pfdout[0], "r"); + if (!*pipe_out) + _log("fdopen: %s\n", strerror(errno)); + } + + /* Add the child process to the list of open processes (to be able to kill + * them in case of a signal. */ + add_process(name, pid, createprocessgroup); + + return pid; +} + +int exec_command(FILE *in, FILE *out, void *cmd) +{ + if (in && dup2(fileno(in), fileno(stdin)) < 0) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "%s: Could not dup stdin\n", (const char *)cmd); + if (out && dup2(fileno(out), fileno(stdout)) < 0) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "%s: Could not dup stdout\n", (const char *)cmd); + + execl(get_modern_shell(), get_modern_shell(), "-c", (const char *)cmd, (char *)NULL); + + _log("Error: Executing \"%s -c %s\" failed (%s).\n", get_modern_shell(), (const char *)cmd, strerror(errno)); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; +} + +int start_system_process(const char *name, const char *command, FILE **fdin, FILE **fdout) +{ + return _start_process(name, exec_command, (void*)command, fdin, fdout, 1); +} + +int start_process(const char *name, int (*proc_func)(FILE *, FILE *, void *), void *user_arg, FILE **fdin, FILE **fdout) +{ + return _start_process(name, proc_func, user_arg, fdin, fdout, 0); +} + +int wait_for_process(int pid) +{ + int i; + int status; + + i = find_process(pid); + if (i < 0) { + _log("No such process \"%d\"", pid); + return -1; + } + + waitpid(procs[i].pid, &status, 0); + if (WIFEXITED(status)) + _log("%s exited with status %d\n", procs[i].name, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + _log("%s received signal %d\n", procs[i].name, WTERMSIG(status)); + + /* remove from process list */ + procs[i].pid = -1; + return status; +} + +int run_system_process(const char *name, const char *command) +{ + int pid = start_system_process(name, command, NULL, NULL); + return wait_for_process(pid); +} + diff --git a/filter/foomatic-rip/process.h b/filter/foomatic-rip/process.h new file mode 100644 index 000000000..b95dcb4da --- /dev/null +++ b/filter/foomatic-rip/process.h @@ -0,0 +1,48 @@ +/* process.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef process_h +#define process_h + +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> + +pid_t start_process(const char *name, int (*proc_func)(), void *user_arg, FILE **fdin, FILE **fdout); +pid_t start_system_process(const char *name, const char *command, FILE **fdin, FILE **fdout); + +/* returns command's return status (see waitpid(2)) */ +int run_system_process(const char *name, const char *command); + +pid_t create_pipe_process(const char *name, + FILE *src, + FILE *dest, + const char *alreadyread, + size_t alreadyread_len); + +int wait_for_process(int pid); + +void kill_all_processes(); + +#endif + diff --git a/filter/foomatic-rip/renderer.c b/filter/foomatic-rip/renderer.c new file mode 100644 index 000000000..0f9c0129d --- /dev/null +++ b/filter/foomatic-rip/renderer.c @@ -0,0 +1,490 @@ +/* renderer.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <signal.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> + +#include "foomaticrip.h" +#include "util.h" +#include "process.h" +#include "options.h" + +/* + * Check whether we have a Ghostscript version with redirection of the standard + * output of the PostScript programs via '-sstdout=%stderr' + */ +int test_gs_output_redirection() +{ + char gstestcommand[PATH_MAX]; + char output[10] = ""; + int bytes; + + snprintf(gstestcommand, PATH_MAX, "%s -dQUIET -dPARANOIDSAFER -dNOPAUSE " + "-dBATCH -dNOMEDIAATTRS -sDEVICE=ps2write -sstdout=%%stderr " + "-sOutputFile=/dev/null -c '(hello\n) print flush' 2>&1", gspath); + + FILE *pd = popen(gstestcommand, "r"); + if (!pd) { + _log("Failed to execute ghostscript!\n"); + return 0; + } + + bytes = fread(output, 1, 10, pd); + pclose(pd); + + if (bytes > 0 && startswith(output, "hello")) + return 1; + + return 0; +} + +/* + * Massage arguments to make ghostscript execute properly as a filter, with + * output on stdout and errors on stderr etc. (This function does what + * foomatic-gswrapper used to do) + */ +void massage_gs_commandline(dstr_t *cmd) +{ + int gswithoutputredirection = test_gs_output_redirection(); + size_t start, end; + dstr_t *gscmd, *cmdcopy; + + extract_command(&start, &end, cmd->data, "gs"); + if (start == end) /* cmd doesn't call ghostscript */ + return; + + gscmd = create_dstr(); + dstrncpy(gscmd, &cmd->data[start], end - start); + + /* If Ghostscript does not support redirecting the standard output + of the PostScript program to standard error with '-sstdout=%stderr', sen + the job output data to fd 3; errors will be on 2(stderr) and job ps + program interpreter output on 1(stdout). */ + if (gswithoutputredirection) + dstrreplace(gscmd, "-sOutputFile=- ", "-sOutputFile=%stdout ", 0); + else + dstrreplace(gscmd, "-sOutputFile=- ", "-sOutputFile=/dev/fd/3 ", 0); + + /* Use always buffered input. This works around a Ghostscript + bug which prevents printing encrypted PDF files with Adobe Reader 8.1.1 + and Ghostscript built as shared library (Ghostscript bug #689577, Ubuntu + bug #172264) */ + if (dstrendswith(gscmd, " -")) + dstrcat(gscmd, "_"); + else + dstrreplace(gscmd, " - ", " -_ ", 0); + dstrreplace(gscmd, " /dev/fd/0", " -_ ", 0); + + /* Turn *off* -q (quiet!); now that stderr is useful! :) */ + dstrreplace(gscmd, " -q ", " ", 0); + + /* Escape any quotes, and then quote everything just to be sure... + Escaping a single quote inside single quotes is a bit complex as the + shell takes everything literal there. So we have to assemble it by + concatinating different quoted strings. Finally we get e.g.: 'x'"'"'y' + or ''"'"'xy' or 'xy'"'"'' or ... */ + /* dstrreplace(cmd, "'", "'\"'\"'"); TODO tbd */ + + dstrremove(gscmd, 0, 2); /* Remove 'gs' */ + if (gswithoutputredirection) + { + dstrprepend(gscmd, " -sstdout=%stderr "); + dstrprepend(gscmd, gspath); + } + else + { + dstrprepend(gscmd, gspath); + dstrcat(gscmd, " 3>&1 1>&2"); + } + + /* put gscmd back into cmd, between 'start' and 'end' */ + cmdcopy = create_dstr(); + dstrcpy(cmdcopy, cmd->data); + + dstrncpy(cmd, cmdcopy->data, start); + dstrcat(cmd, gscmd->data); + dstrcat(cmd, &cmdcopy->data[end]); + + free_dstr(gscmd); + free_dstr(cmdcopy); + + /* If the renderer command line contains the "echo" command, replace the + * "echo" by the user-chosen $myecho (important for non-GNU systems where + * GNU echo is in a special path */ + dstrreplace(cmd, "echo", echopath, 0); /* TODO search for \wecho\w */ +} + +char * read_line(FILE *stream, size_t *readbytes) +{ + char *line; + size_t alloc = 64, len = 0; + int c; + + line = malloc(alloc); + + while ((c = fgetc(stream)) != EOF) { + if (len >= alloc -1) { + alloc *= 2; + line = realloc(line, alloc); + } + line[len] = (char)c; + len++; + if (c == '\n') + break; + } + + line[len] = '\0'; + *readbytes = len; + return line; +} + +void write_binary_data(FILE *stream, const char *data, size_t bytes) +{ + int i; + for (i=0; i < bytes; i++) + { + fputc(data[i], stream); + } +} + +/* + * Read all lines containing 'jclstr' from 'stream' (actually, one more) and + * return them in a zero terminated array. + */ +static char ** read_jcl_lines(FILE *stream, const char *jclstr, + size_t *readbinarybytes) +{ + char *line; + char **result; + size_t alloc = 8, cnt = 0; + + result = malloc(alloc * sizeof(char *)); + + /* read from the renderer output until the first non-JCL line appears */ + while ((line = read_line(stream, readbinarybytes))) + { + if (cnt >= alloc -1) + { + alloc *= 2; + result = realloc(result, alloc * sizeof(char *)); + } + result[cnt] = line; + if (!strstr(line, jclstr)) + break; + /* Remove newline from the end of a line containing JCL */ + result[cnt][*readbinarybytes - 1] = '\0'; + cnt++; + } + + cnt++; + result[cnt] = NULL; + return result; +} + +static int jcl_keywords_equal(const char *jclline1, const char *jclline2, + const char *jclstr) +{ + char *j1, *j2, *p1, *p2; + + j1 = strstr(jclline1, jclstr); + if (!j1) return 0; + if (!(p1 = strchr(skip_whitespace(j1), '='))) + p1 = j1 + strlen(j1); + p1--; + while (p1 > j1 && isspace(*p1)) + p1--; + + j2 = strstr(jclline2, jclstr); + if (!j2) return 0; + if (!(p2 = strchr(skip_whitespace(j2), '='))) + p2 = j2 + strlen(j2); + p2--; + while (p2 > j2 && isspace(*p2)) + p2--; + + if (p1 - j1 != p2 - j2) return 0; + return strncmp(j1, j2, p1 - j1 + 1) == 0; +} + +/* + * Finds the keyword of line in opts + */ +static const char * jcl_options_find_keyword(char **opts, const char *line, + const char *jclstr) +{ + if (!opts) + return NULL; + + while (*opts) + { + if (jcl_keywords_equal(*opts, line, jclstr)) + return *opts; + opts++; + } + return NULL; +} + +static void argv_write(FILE *stream, char **argv, const char *sep) +{ + if (!argv) + return; + + while (*argv) + fprintf(stream, "%s%s", *argv++, sep); +} + +/* + * Merges 'original_opts' and 'pref_opts' and writes them to 'stream'. Header / + * footer is taken from 'original_opts'. If both have the same options, the one + * from 'pref_opts' is preferred + * Returns true, if original_opts was not empty + */ +static int write_merged_jcl_options(FILE *stream, + char **original_opts, + char **opts, + size_t readbinarybytes, + const char *jclstr) +{ + char *p = strstr(original_opts[0], jclstr); + char header[128]; + char **optsp1 = NULL, **optsp2 = NULL; + + /* No JCL options in original_opts, just prepend opts */ + if (argv_count(original_opts) == 1) + { + fprintf(stream, "%s", jclbegin); + argv_write(stream, opts, "\n"); + write_binary_data(stream, original_opts[0], readbinarybytes); + return 0; + } + + if (argv_count(original_opts) == 2) + { + /* If we have only one line of JCL it is probably something like the + * "@PJL ENTER LANGUAGE=..." line which has to be in the end, but it + * also contains the "<esc>%-12345X" which has to be in the beginning + * of the job */ + if (p) + fwrite(original_opts[0], 1, p - original_opts[0], stream); + else + fprintf(stream, "%s\n", original_opts[0]); + + argv_write(stream, opts, "\n"); + + if (p) + fprintf(stream, "%s\n", p); + + write_binary_data(stream, original_opts[1], readbinarybytes); + return 1; + } + + /* Write jcl header */ + strncpy(header, original_opts[0], p - original_opts[0]); + header[p - original_opts[0]] = '\0'; + fprintf(stream, "%s", header); + + /* Insert the JCL commands from the PPD file right before the first + "@PJL SET ..." line from the, if there are no "@PJL SET ..." lines, + directly before "@PJL ENTER LANGUAGE ...", otherwise after the JCL + commands from the driver */ + for (optsp1 = original_opts; *(optsp1 + 1); optsp1++) { + if (optsp2 == NULL && + ((strstr(*optsp1, "ENTER LANGUAGE") != NULL) || + (strncasecmp(*optsp1, "@PJL SET ", 9) == 0))) { + for (optsp2 = opts; *optsp2; optsp2++) + if (!jcl_options_find_keyword(original_opts, *optsp2, jclstr)) + fprintf(stream, "%s\n", *optsp2); + } + if (optsp1 != original_opts) p = *optsp1; + if (!p) + _log("write_merged_jcl_options() dereferences NULL pointer p\n"); + if (jcl_options_find_keyword(opts, p, jclstr)) + fprintf(stream, "%s\n", jcl_options_find_keyword(opts, p, jclstr)); + else + fprintf(stream, "%s\n", p); + } + if (optsp2 == NULL) + for (optsp2 = opts; *optsp2; optsp2++) + if (!jcl_options_find_keyword(original_opts, *optsp2, jclstr)) + fprintf(stream, "%s\n", *optsp2); + + write_binary_data(stream, *optsp1, readbinarybytes); + + return 1; +} + +void log_jcl() +{ + char **opt; + + _log("JCL: %s", jclbegin); + if (jclprepend) + for (opt = jclprepend; *opt; opt++) + _log("%s\n", *opt); + + _log("<job data> %s\n\n", jclappend->data); +} + +int exec_kid4(FILE *in, FILE *out, void *user_arg) +{ + FILE *fileh = open_postpipe(); + int driverjcl = NULL; + size_t readbinarybytes; + + log_jcl(); + + /* wrap the JCL around the job data, if there are any options specified... + * Should the driver already have inserted JCL commands we merge our JCL + * header with the one from the driver */ + if (argv_count(jclprepend) > 0) + { + if (!isspace(jclprepend[0][0])) + { + char *jclstr, **jclheader; + size_t pos; + + pos = strcspn(jclprepend[0], " \t\n\r"); + jclstr = malloc(pos +1); + strncpy(jclstr, jclprepend[0], pos); + jclstr[pos] = '\0'; + + jclheader = read_jcl_lines(in, jclstr, &readbinarybytes); + + driverjcl = write_merged_jcl_options(fileh, + jclheader, + jclprepend, + readbinarybytes, + jclstr); + + free(jclstr); + argv_free(jclheader); + } + else + /* No merging of JCL header possible, simply prepend it */ + argv_write(fileh, jclprepend, "\n"); + } + + /* The job data */ + copy_file(fileh, in, NULL, 0); + + /* A JCL trailer */ + if (argv_count(jclprepend) > 0 && !driverjcl) + fwrite(jclappend->data, jclappend->len, 1, fileh); + + fclose(in); + if (fclose(fileh) != 0) + { + _log("error closing postpipe\n"); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; + } + + return EXIT_PRINTED; +} + +int exec_kid3(FILE *in, FILE *out, void *user_arg) +{ + dstr_t *commandline; + int kid4; + FILE *kid4in; + int status; + + commandline = create_dstr(); + dstrcpy(commandline, (const char *)user_arg); + + kid4 = start_process("kid4", exec_kid4, NULL, &kid4in, NULL); + if (kid4 < 0) { + free_dstr(commandline); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; + } + + if (in && dup2(fileno(in), fileno(stdin)) < 0) { + _log("kid3: Could not dup stdin\n"); + fclose(kid4in); + free_dstr(commandline); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; + } + if (dup2(fileno(kid4in), fileno(stdout)) < 0) { + _log("kid3: Could not dup stdout to kid4\n"); + fclose(kid4in); + free_dstr(commandline); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; + } + if (debug) + { + if (!redirect_log_to_stderr()) { + fclose(kid4in); + free_dstr(commandline); + return EXIT_PRNERR_NORETRY_BAD_SETTINGS; + } + + /* Save the data supposed to be fed into the renderer also into a file*/ + dstrprepend(commandline, "tee $(mktemp " LOG_FILE "-XXXXXX.ps) | ( "); + dstrcat(commandline, ")"); + } + + /* Actually run the thing */ + status = run_system_process("renderer", commandline->data); + + if (in) + fclose(in); + fclose(kid4in); + fclose(stdin); + fclose(stdout); + free_dstr(commandline); + + if (WIFEXITED(status)) { + switch (WEXITSTATUS(status)) { + case 0: /* Success! */ + /* wait for postpipe/output child */ + wait_for_process(kid4); + _log("kid3 finished\n"); + return EXIT_PRINTED; + case 1: + _log("Possible error on renderer command line or PostScript error. Check options."); + return EXIT_JOBERR; + case 139: + _log("The renderer may have dumped core."); + return EXIT_JOBERR; + case 141: + _log("A filter used in addition to the renderer itself may have failed."); + return EXIT_PRNERR; + case 243: + case 255: /* PostScript error? */ + return EXIT_JOBERR; + } + } + else if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGUSR1: + return EXIT_PRNERR; + case SIGUSR2: + return EXIT_PRNERR_NORETRY; + case SIGTTIN: + return EXIT_ENGAGED; + } + } + return EXIT_PRNERR; +} + diff --git a/filter/foomatic-rip/renderer.h b/filter/foomatic-rip/renderer.h new file mode 100644 index 000000000..298f67b5c --- /dev/null +++ b/filter/foomatic-rip/renderer.h @@ -0,0 +1,31 @@ +/* renderer.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef renderer_h +#define renderer_h + +void massage_gs_commandline(dstr_t *cmd); +int exec_kid3(FILE *in, FILE *out, void *user_arg); + +#endif + diff --git a/filter/foomatic-rip/spooler.c b/filter/foomatic-rip/spooler.c new file mode 100644 index 000000000..32d04d3cf --- /dev/null +++ b/filter/foomatic-rip/spooler.c @@ -0,0 +1,340 @@ +/* spooler.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "spooler.h" +#include "foomaticrip.h" +#include "util.h" +#include "options.h" +#include <stdlib.h> +#include <unistd.h> + +const char *spooler_name(int spooler) +{ + switch (spooler) { + case SPOOLER_CUPS: return "cups"; + case SPOOLER_DIRECT: return "direct"; + }; + return "<unknown>"; +} + +/* This piece of PostScript code (initial idea 2001 by Michael + Allerhand (michael.allerhand at ed dot ac dot uk, vastly + improved by Till Kamppeter in 2002) lets Ghostscript output + the page accounting information which CUPS needs on standard + error. + Redesign by Helge Blischke (2004-11-17): + - As the PostScript job itself may define BeginPage and/or EndPage + procedures, or the alternate pstops filter may have inserted + such procedures, we make sure that the accounting routine + will safely coexist with those. To achieve this, we force + - the accountint stuff to be inserted at the very end of the + PostScript job's setup section, + - the accounting stuff just using the return value of the + existing EndPage procedure, if any (and providing a default one + if not). + - As PostScript jobs may contain calls to setpagedevice "between" + pages, e.g. to change media type, do in-job stapling, etc., + we cannot rely on the "showpage count since last pagedevice + activation" but instead count the physical pages by ourselves + (in a global dictionary). +*/ +const char *accounting_prolog_code = + "[{\n" + "%% Code for writing CUPS accounting tags on standard error\n" + "\n" + "/cupsPSLevel2 % Determine whether we can do PostScript level 2 or newer\n" + " systemdict/languagelevel 2 copy\n" + " known{get exec}{pop pop 1}ifelse 2 ge\n" + "def\n" + "\n" + "cupsPSLevel2\n" + "{ % in case of level 2 or higher\n" + " currentglobal true setglobal % define a dictioary foomaticDict\n" + " globaldict begin % in global VM and establish a\n" + " /foomaticDict % pages count key there\n" + " <<\n" + " /PhysPages 0\n" + " >>def\n" + " end\n" + " setglobal\n" + "}if\n" + "\n" + "/cupsGetNumCopies { % Read the number of Copies requested for the current\n" + " % page\n" + " cupsPSLevel2\n" + " {\n" + " % PS Level 2+: Get number of copies from Page Device dictionary\n" + " currentpagedevice /NumCopies get\n" + " }\n" + " {\n" + " % PS Level 1: Number of copies not in Page Device dictionary\n" + " null\n" + " }\n" + " ifelse\n" + " % Check whether the number is defined, if it is \"null\" use #copies \n" + " % instead\n" + " dup null eq {\n" + " pop #copies\n" + " }\n" + " if\n" + " % Check whether the number is defined now, if it is still \"null\" use 1\n" + " % instead\n" + " dup null eq {\n" + " pop 1\n" + " } if\n" + "} bind def\n" + "\n" + "/cupsWrite { % write a string onto standard error\n" + " (%stderr) (w) file\n" + " exch writestring\n" + "} bind def\n" + "\n" + "/cupsFlush % flush standard error to make it sort of unbuffered\n" + "{\n" + " (%stderr)(w)file flushfile\n" + "}bind def\n" + "\n" + "cupsPSLevel2\n" + "{ % In language level 2, we try to do something reasonable\n" + " <<\n" + " /EndPage\n" + " [ % start the array that becomes the procedure\n" + " currentpagedevice/EndPage 2 copy known\n" + " {get} % get the existing EndPage procedure\n" + " {pop pop {exch pop 2 ne}bind}ifelse % there is none, define the default\n" + " /exec load % make sure it will be executed, whatever it is\n" + " /dup load % duplicate the result value\n" + " { % true: a sheet gets printed, do accounting\n" + " currentglobal true setglobal % switch to global VM ...\n" + " foomaticDict begin % ... and access our special dictionary\n" + " PhysPages 1 add % count the sheets printed (including this one)\n" + " dup /PhysPages exch def % and save the value\n" + " end % leave our dict\n" + " exch setglobal % return to previous VM\n" + " (PAGE: )cupsWrite % assemble and print the accounting string ...\n" + " 16 string cvs cupsWrite % ... the sheet count ...\n" + " ( )cupsWrite % ... a space ...\n" + " cupsGetNumCopies % ... the number of copies ...\n" + " 16 string cvs cupsWrite % ...\n" + " (\\n)cupsWrite % ... a newline\n" + " cupsFlush\n" + " }/if load\n" + " % false: current page gets discarded; do nothing \n" + " ]cvx bind % make the array executable and apply bind\n" + " >>setpagedevice\n" + "}\n" + "{\n" + " % In language level 1, we do no accounting currently, as there is no global VM\n" + " % the contents of which are undesturbed by save and restore. \n" + " % If we may be sure that showpage never gets called inside a page related save / restore pair\n" + " % we might implement an hack with showpage similar to the one above.\n" + "}ifelse\n" + "\n" + "} stopped cleartomark\n"; + + +void init_cups(list_t *arglist, dstr_t *filelist, jobparams_t *job) +{ + char path [1024] = ""; + char cups_jobid [128]; + char cups_user [128]; + char cups_jobtitle [128]; + char cups_copies [128]; + int cups_options_len; + char *cups_options; + char cups_filename [256]; + + if (getenv("CUPS_FONTPATH")) + strcpy(path, getenv("CUPS_FONTPATH")); + else if (getenv("CUPS_DATADIR")) { + strcpy(path, getenv("CUPS_DATADIR")); + strcat(path, "/fonts"); + } + if (getenv("GS_LIB")) { + strcat(path, ":"); + strcat(path, getenv("GS_LIB")); + } + setenv("GS_LIB", path, 1); + + /* Get all command line parameters */ + strncpy_omit(cups_jobid, arglist_get(arglist, 0), 128, omit_shellescapes); + strncpy_omit(cups_user, arglist_get(arglist, 1), 128, omit_shellescapes); + strncpy_omit(cups_jobtitle, arglist_get(arglist, 2), 128, omit_shellescapes); + strncpy_omit(cups_copies, arglist_get(arglist, 3), 128, omit_shellescapes); + + cups_options_len = strlen(arglist_get(arglist, 4)); + cups_options = malloc(cups_options_len + 1); + strncpy_omit(cups_options, arglist_get(arglist, 4), cups_options_len + 1, omit_shellescapes); + + /* Common job parameters */ + strcpy(job->id, cups_jobid); + strcpy(job->title, cups_jobtitle); + strcpy(job->user, cups_user); + strcpy(job->copies, cups_copies); + dstrcatf(job->optstr, " %s", cups_options); + + /* Check for and handle inputfile vs stdin */ + if (list_item_count(arglist) > 4) { + strncpy_omit(cups_filename, arglist_get(arglist, 5), 256, omit_shellescapes); + if (cups_filename[0] != '-') { + /* We get input from a file */ + dstrcatf(filelist, "%s ", cups_filename); + _log("Getting input from file %s\n", cups_filename); + } + } + + accounting_prolog = accounting_prolog_code; + + /* On which queue are we printing? + CUPS gives the PPD file the same name as the printer queue, + so we can get the queue name from the name of the PPD file. */ + file_basename(job->printer, job->ppdfile, 256); + + free(cups_options); +} + +/* used by init_direct to find a ppd file */ +int find_ppdfile(const char *user_default_path, jobparams_t *job) +{ + /* Search also common spooler-specific locations, this way a printer + configured under a certain spooler can also be used without spooler */ + + strcpy(job->ppdfile, job->printer); + if (access(job->ppdfile, R_OK) == 0) + return 1; + + snprintf(job->ppdfile, 256, "%s.ppd", job->printer); /* current dir */ + if (access(job->ppdfile, R_OK) == 0) + return 1; + snprintf(job->ppdfile, 256, "%s/%s.ppd", user_default_path, job->printer); /* user dir */ + if (access(job->ppdfile, R_OK) == 0) + return 1; + snprintf(job->ppdfile, 256, "%s/direct/%s.ppd", CONFIG_PATH, job->printer); /* system dir */ + if (access(job->ppdfile, R_OK) == 0) + return 1; + snprintf(job->ppdfile, 256, "%s/%s.ppd", CONFIG_PATH, job->printer); /* system dir */ + if (access(job->ppdfile, R_OK) == 0) + return 1; + snprintf(job->ppdfile, 256, "/etc/cups/ppd/%s.ppd", job->printer); /* CUPS config dir */ + if (access(job->ppdfile, R_OK) == 0) + return 1; + snprintf(job->ppdfile, 256, "/usr/local/etc/cups/ppd/%s.ppd", job->printer); /* CUPS config dir */ + if (access(job->ppdfile, R_OK) == 0) + return 1; + + /* nothing found */ + job->ppdfile[0] = '\0'; + return 0; +} + +/* search 'configfile' for 'key', copy value into dest, return success */ +int configfile_find_option(const char *configfile, const char *key, char *dest, size_t destsize) +{ + FILE *fh; + char line [1024]; + char *p; + + dest[0] = '\0'; + + if (!(fh = fopen(configfile, "r"))) + return 0; + + while (fgets(line, 1024, fh)) { + if (!prefixcmp(line, "default")) { + p = strchr(line, ':'); + if (p) { + strncpy_omit(dest, p + 1, destsize, omit_whitespace_newline); + if (dest[0]) + break; + } + } + } + fclose(fh); + return dest[0] != '\0'; +} + +/* tries to find a default printer name in various config files and copies the + * result into the global var 'printer'. Returns success */ +int find_default_printer(const char *user_default_path, jobparams_t *job) +{ + char configfile [1024]; + char *key = "default"; + + if (configfile_find_option("./.directconfig", key, job->printer, 256)) + return 1; + if (configfile_find_option("./directconfig", key, job->printer, 256)) + return 1; + if (configfile_find_option("./.config", key, job->printer, 256)) + return 1; + strlcpy(configfile, user_default_path, 1024); + strlcat(configfile, "/direct/.config", 1024); + if (configfile_find_option(configfile, key, job->printer, 256)) + return 1; + strlcpy(configfile, user_default_path, 1024); + strlcat(configfile, "/direct.conf", 1024); + if (configfile_find_option(configfile, key, job->printer, 256)) + return 1; + if (configfile_find_option(CONFIG_PATH "/direct/.config", key, job->printer, 256)) + return 1; + if (configfile_find_option(CONFIG_PATH "/direct.conf", key, job->printer, 256)) + return 1; + + return 0; +} + +void init_direct(list_t *arglist, dstr_t *filelist, jobparams_t *job) +{ + char tmp [1024]; + listitem_t *i; + char user_default_path [PATH_MAX]; + + strlcpy(user_default_path, getenv("HOME"), 256); + strlcat(user_default_path, "/.foomatic/", 256); + + /* Which files do we want to print? */ + for (i = arglist->first; i; i = i->next) { + strncpy_omit(tmp, (char*)i->data, 1024, omit_shellescapes); + dstrcatf(filelist, "%s ", tmp); + } + + if (job->ppdfile[0] == '\0') { + if (job->printer[0] == '\0') { + /* No printer definition file selected, check whether we have a + default printer defined */ + find_default_printer(user_default_path, job); + } + + /* Neither in a config file nor on the command line a printer was selected */ + if (!job->printer[0]) { + _log("No printer definition (option \"-P <name>\") specified!\n"); + exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS); + } + + /* Search for the PPD file */ + if (!find_ppdfile(user_default_path, job)) { + _log("There is no readable PPD file for the printer %s, is it configured?\n", job->printer); + exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS); + } + } +} + diff --git a/filter/foomatic-rip/spooler.h b/filter/foomatic-rip/spooler.h new file mode 100644 index 000000000..7025ccaf7 --- /dev/null +++ b/filter/foomatic-rip/spooler.h @@ -0,0 +1,35 @@ +/* spooler.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SPOOLER_H +#define SPOOLER_H + +#include "foomaticrip.h" +#include "util.h" + +const char *spooler_name(int spooler); +void init_cups(list_t *arglist, dstr_t *filelist, jobparams_t *job); +void init_direct(list_t *arglist, dstr_t *filelist, jobparams_t *job); + +#endif + diff --git a/filter/foomatic-rip/util.c b/filter/foomatic-rip/util.c new file mode 100644 index 000000000..4286daadf --- /dev/null +++ b/filter/foomatic-rip/util.c @@ -0,0 +1,1122 @@ +/* util.c + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "util.h" +#include "foomaticrip.h" +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <stdarg.h> +#include <assert.h> + + +const char* shellescapes = "|<>&!$\'\"#*?()[]{}"; + +const char * temp_dir() +{ + static const char *tmpdir = NULL; + + if (!tmpdir) + { + const char *dirs[] = { getenv("TMPDIR"), P_tmpdir, "/tmp" }; + int i; + + for (i = 0; i < (sizeof(dirs) / sizeof(dirs[0])); i++) { + if (access(dirs[i], W_OK) == 0) { + tmpdir = dirs[i]; + break; + } + } + if (tmpdir) + { + _log("Storing temporary files in %s\n", tmpdir); + setenv("TMPDIR", tmpdir, 1); /* for child processes */ + } + else + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, + "Cannot find a writable temp dir."); + } + + return tmpdir; +} + +int prefixcmp(const char *str, const char *prefix) +{ + return strncmp(str, prefix, strlen(prefix)); +} + +int prefixcasecmp(const char *str, const char *prefix) +{ + return strncasecmp(str, prefix, strlen(prefix)); +} + +int startswith(const char *str, const char *prefix) +{ + return str ? (strncmp(str, prefix, strlen(prefix)) == 0) : 0; +} + +int endswith(const char *str, const char *postfix) +{ + int slen = strlen(str); + int plen = strlen(postfix); + const char *pstr; + + if (slen < plen) + return 0; + + pstr = &str[slen - plen]; + return strcmp(pstr, postfix) == 0; +} + +const char * skip_whitespace(const char *str) +{ + while (*str && isspace(*str)) + str++; + return str; +} + +void strlower(char *dest, size_t destlen, const char *src) +{ + char *pdest = dest; + const char *psrc = src; + while (*psrc && --destlen > 0) + { + *pdest = tolower(*psrc); + pdest++; + psrc++; + } + *pdest = '\0'; +} + +int isempty(const char *string) +{ + return !string || string[0] == '\0'; +} + +const char * strncpy_omit(char* dest, const char* src, size_t n, int (*omit_func)(int)) +{ + const char* psrc = src; + char* pdest = dest; + int cnt = n -1; + if (!pdest) + return NULL; + if (psrc) { + while (*psrc != 0 && cnt > 0) { + if (!omit_func(*psrc)) { + *pdest = *psrc; + pdest++; + cnt--; + } + psrc++; + } + } + *pdest = '\0'; + return psrc; +} +int omit_unprintables(int c) { return c>= '\x00' && c <= '\x1f'; } +int omit_shellescapes(int c) { return strchr(shellescapes, c) != NULL; } +int omit_specialchars(int c) { return omit_unprintables(c) || omit_shellescapes(c); } +int omit_whitespace(int c) { return c == ' ' || c == '\t'; } +int omit_whitespace_newline(int c) { return omit_whitespace(c) || c == '\n'; } + +#ifndef HAVE_STRCASESTR +char * +strcasestr (const char *haystack, const char *needle) +{ + char *p, *startn = 0, *np = 0; + + for (p = haystack; *p; p++) { + if (np) { + if (toupper(*p) == toupper(*np)) { + if (!*++np) + return startn; + } else + np = 0; + } else if (toupper(*p) == toupper(*needle)) { + np = needle + 1; + startn = p; + } + } + + return 0; +} +#endif + +#ifndef __OpenBSD__ +size_t strlcpy(char *dest, const char *src, size_t size) +{ + char *pdest = dest; + const char *psrc = src; + + if (!src) { + dest[0] = '\0'; + return 0; + } + + if (size) { + while (--size && (*pdest++ = *psrc++) != '\0'); + *pdest = '\0'; + } + if (!size) + while (*psrc++); + return (psrc - src -1); +} + +size_t strlcat(char *dest, const char *src, size_t size) +{ + char *pdest = dest; + const char *psrc = src; + size_t i = size; + size_t len; + + while (--i && *pdest) + pdest++; + len = pdest - dest; + + if (!i) + return strlen(src) + len; + + while (i-- && *psrc) + *pdest++ = *psrc++; + *pdest = '\0'; + + return len + (psrc - src); +} +#endif /* ! __OpenBSD__ */ + +void strrepl(char *str, const char *chars, char repl) +{ + char *p = str; + + while (*p) { + if (strchr(chars, *p)) + *p = repl; + p++; + } +} + +void strrepl_nodups(char *str, const char *chars, char repl) +{ + char *pstr = str; + char *p = str; + int prev = 0; + + while (*pstr) { + if (strchr(chars, *pstr) || *pstr == repl) { + if (!prev) { + *p = repl; + p++; + prev = 1; + } + } + else { + *p = *pstr; + p++; + prev = 0; + } + pstr++; + } + *p = '\0'; +} + +void strclr(char *str) +{ + while (*str) { + *str = '\0'; + str++; + } +} + +char * strnchr(const char *str, int c, size_t n) +{ + char *p = (char*)str; + + while (*p && --n > 0) { + if (*p == (char)c) + return p; + p++; + } + return p; +} + +void escapechars(char *dest, size_t size, const char *src, const char *esc_chars) +{ + const char *psrc = src; + + while (*psrc && --size > 0) { + if (strchr(esc_chars, *psrc)) + *dest++ = '\\'; + *dest++ = *psrc++; + } +} + +const char * strncpy_tochar(char *dest, const char *src, size_t max, const char *stopchars) +{ + const char *psrc = src; + char *pdest = dest; + if (isempty(psrc)) { + return NULL; + } + while (*psrc && --max > 0 && !strchr(stopchars, *psrc)) { + *pdest = *psrc; + pdest++; + psrc++; + } + *pdest = '\0'; + return psrc +1; +} + +int find_in_path(const char *progname, const char *paths, char *found_in) +{ + char *pathscopy; + char *path; + char filepath[PATH_MAX]; + + if (access(progname, X_OK) == 0) + return 1; + + pathscopy = strdup(paths); + for (path = strtok(pathscopy, ":"); path; path = strtok(NULL, ":")) { + strlcpy(filepath, path, PATH_MAX); + strlcat(filepath, "/", PATH_MAX); + strlcat(filepath, progname, PATH_MAX); + + if (access(filepath, X_OK) == 0) { + if (found_in) + strlcpy(found_in, path, PATH_MAX); + free(pathscopy); + return 1; + } + } + + if (found_in) + found_in[0] = '\0'; + free(pathscopy); + return 0; +} + +void file_basename(char *dest, const char *path, size_t dest_size) +{ + const char *p = strrchr(path, '/'); + char *pdest = dest; + if (!pdest) + return; + if (p) + p += 1; + else + p = path; + while (*p != 0 && *p != '.' && --dest_size > 0) { + *pdest++ = *p++; + } + *pdest = '\0'; +} + +void make_absolute_path(char *path, int len) +{ + char *tmp, *cwd; + + if (path[0] != '/') { + tmp = malloc(len +1); + strlcpy(tmp, path, len); + + cwd = malloc(len); + if (getcwd(cwd, len) != NULL) { + strlcpy(path, cwd, len); + strlcat(path, "/", len); + strlcat(path, tmp, len); + } + + free(tmp); + free(cwd); + } +} + +int is_true_string(const char *str) +{ + return str && (!strcmp(str, "1") || !strcasecmp(str, "Yes") || + !strcasecmp(str, "On") || !strcasecmp(str, "True")); +} + +int is_false_string(const char *str) +{ + return str && (!strcmp(str, "0") || !strcasecmp(str, "No") || + !strcasecmp(str, "Off") || !strcasecmp(str, "False") || + !strcasecmp(str, "None")); +} + +int digit(char c) +{ + if (c >= '0' && c <= '9') + return (int)c - (int)'0'; + return -1; +} + +static const char * next_token(const char *string, const char *separators) +{ + if (!string) + return NULL; + + while (*string && !strchr(separators, *string)) + string++; + + while (*string && strchr(separators, *string)) + string++; + + return string; +} + +static unsigned count_separators(const char *string, const char *separators) +{ + const char *p; + unsigned cnt = 0; + + if (!string) + return 0; + + for (p = string; *p; p = next_token(p, separators)) + cnt++; + + return cnt; +} + +/* + * Returns a zero terminated array of strings + */ +char ** argv_split(const char *string, const char *separators, int *cntp) +{ + unsigned cnt; + int i; + char **argv; + + if (!string) + return NULL; + + if ((cnt = count_separators(string, separators)) == 0) + return NULL; + + argv = malloc((cnt +1) * sizeof(char *)); + argv[cnt] = NULL; + + for (i = 0; i < cnt; i++) + { + size_t len = strcspn(string, separators); + char *s; + s = malloc(len + 1); + strncpy(s, string, len); + s[len] = '\0'; + argv[i] = s; + string = next_token(string, separators); + } + + if (cntp) + *cntp = cnt; + return argv; +} + +size_t argv_count(char **argv) +{ + size_t cnt = 0; + + if (!argv) + return 0; + + while (*argv++) + cnt++; + + return cnt; +} + +void argv_free(char **argv) +{ + char **p; + + if (!argv) + return; + + for (p = argv; *p; p++) + free(*p); + + free(argv); +} + +int line_count(const char *str) +{ + int cnt = 0; + while (*str) { + if (*str == '\n') + cnt++; + str++; + } + return cnt; +} + +int line_start(const char *str, int line_number) +{ + const char *p = str; + while (*p && line_number > 0) { + if (*p == '\n') + line_number--; + p++; + } + return p - str; +} + +void unhexify(char *dest, size_t size, const char *src) +{ + char *pdest = dest; + const char *psrc = src; + char cstr[3]; + + cstr[2] = '\0'; + + while (*psrc && pdest - dest < size -1) { + if (*psrc == '<') { + psrc++; + do { + cstr[0] = *psrc++; + cstr[1] = *psrc++; + if (!isxdigit(cstr[0]) || !isxdigit(cstr[1])) { + printf("Error replacing hex notation in %s!\n", src); + break; + } + *pdest++ = (char)strtol(cstr, NULL, 16); + } while (*psrc != '>'); + psrc++; + } + else + *pdest++ = *psrc++; + } + *pdest = '\0'; +} + +void extract_command(size_t *start, size_t *end, const char *cmdline, const char *cmd) +{ + char *copy = strdup(cmdline); + char *tok = NULL; + const char *delim = "|;"; + + *start = *end = 0; + for (tok = strtok(copy, delim); tok; tok = strtok(NULL, delim)) { + while (*tok && isspace(*tok)) + tok++; + if (startswith(tok, cmd)) { + *start = tok - copy; + *end = tok + strlen(tok) - copy; + break; + } + } + + free(copy); +} + +int contains_command(const char *cmdline, const char *cmd) +{ + size_t start = 0, end = 0; + + extract_command(&start, &end, cmdline, cmd); + if (start == 0 && end == 0) + return 0; + + return 1; +} + +/* + * Dynamic strings + */ +dstr_t * create_dstr() +{ + dstr_t *ds = malloc(sizeof(dstr_t)); + ds->len = 0; + ds->alloc = 32; + ds->data = malloc(ds->alloc); + ds->data[0] = '\0'; + return ds; +} + +void free_dstr(dstr_t *ds) +{ + free(ds->data); + free(ds); +} + +void dstrclear(dstr_t *ds) +{ + ds->len = 0; + ds->data[0] = '\0'; +} + +void dstrassure(dstr_t *ds, size_t alloc) +{ + if (ds->alloc < alloc) { + ds->alloc = alloc; + ds->data = realloc(ds->data, ds->alloc); + } +} + +void dstrcpy(dstr_t *ds, const char *src) +{ + size_t srclen; + + if (!src) { + ds->len = 0; + ds->data[0] = '\0'; + return; + } + + srclen = strlen(src); + + if (srclen >= ds->alloc) { + do { + ds->alloc *= 2; + } while (srclen >= ds->alloc); + ds->data = realloc(ds->data, ds->alloc); + } + + strcpy(ds->data, src); + ds->len = srclen; +} + +void dstrncpy(dstr_t *ds, const char *src, size_t n) +{ + if (n >= ds->alloc) { + do { + ds->alloc *= 2; + } while (n >= ds->alloc); + ds->data = realloc(ds->data, ds->alloc); + } + + strncpy(ds->data, src, n); + ds->len = n; + ds->data[ds->len] = '\0'; +} + +void dstrncat(dstr_t *ds, const char *src, size_t n) +{ + size_t needed = ds->len + n; + + if (needed >= ds->alloc) { + do { + ds->alloc *= 2; + } while (needed >= ds->alloc); + ds->data = realloc(ds->data, ds->alloc); + } + + strncpy(&ds->data[ds->len], src, n); + ds->len = needed; + ds->data[ds->len] = '\0'; +} + +void dstrcpyf(dstr_t *ds, const char *src, ...) +{ + va_list ap; + size_t srclen; + + va_start(ap, src); + srclen = vsnprintf(ds->data, ds->alloc, src, ap); + va_end(ap); + + if (srclen >= ds->alloc) { + do { + ds->alloc *= 2; + } while (srclen >= ds->alloc); + ds->data = realloc(ds->data, ds->alloc); + + va_start(ap, src); + vsnprintf(ds->data, ds->alloc, src, ap); + va_end(ap); + } + + ds->len = srclen; +} + +void dstrputc(dstr_t *ds, int c) +{ + if (ds->len +1 >= ds->alloc) { + ds->alloc *= 2; + ds->data = realloc(ds->data, ds->alloc); + } + ds->data[ds->len++] = c; + ds->data[ds->len] = '\0'; +} + +void dstrcat(dstr_t *ds, const char *src) +{ + size_t srclen = strlen(src); + size_t newlen = ds->len + srclen; + + if (newlen >= ds->alloc) { + do { + ds->alloc *= 2; + } while (newlen >= ds->alloc); + ds->data = realloc(ds->data, ds->alloc); + } + + memcpy(&ds->data[ds->len], src, srclen +1); + ds->len = newlen; +} + +void dstrcatf(dstr_t *ds, const char *src, ...) +{ + va_list ap; + size_t restlen = ds->alloc - ds->len; + size_t srclen; + + va_start(ap, src); + srclen = vsnprintf(&ds->data[ds->len], restlen, src, ap); + va_end(ap); + + if (srclen >= restlen) { + do { + ds->alloc *= 2; + restlen = ds->alloc - ds->len; + } while (srclen >= restlen); + ds->data = realloc(ds->data, ds->alloc); + + va_start(ap, src); + srclen = vsnprintf(&ds->data[ds->len], restlen, src, ap); + va_end(ap); + } + + ds->len += srclen; +} + +size_t fgetdstr(dstr_t *ds, FILE *stream) +{ + int c; + size_t cnt = 0; + + ds->len = 0; + if (ds->alloc == 0) { + ds->alloc = 256; + ds->data = malloc(ds->alloc); + } + + while ((c = fgetc(stream)) != EOF) { + if (ds->len +1 == ds->alloc) { + ds->alloc *= 2; + ds->data = realloc(ds->data, ds->alloc); + } + ds->data[ds->len++] = (char)c; + cnt ++; + if (c == '\n') + break; + } + ds->data[ds->len] = '\0'; + return cnt; +} + +/* + * Replace the first occurrence of 'find' after the index 'start' with 'repl' + * Returns the position right after the replaced string + */ +int dstrreplace(dstr_t *ds, const char *find, const char *repl, int start) +{ + char *p; + dstr_t *copy = create_dstr(); + int end = -1; + + dstrcpy(copy, ds->data); + + if ((p = strstr(©->data[start], find))) + { + dstrncpy(ds, copy->data, p - copy->data); + dstrcatf(ds, "%s", repl); + end = ds->len; + dstrcatf(ds, "%s", p + strlen(find)); + } + + free_dstr(copy); + return end; +} + +void dstrprepend(dstr_t *ds, const char *str) +{ + dstr_t *copy = create_dstr(); + dstrcpy(copy, ds->data); + dstrcpy(ds, str); + dstrcatf(ds, "%s", copy->data); + free_dstr(copy); +} + +void dstrinsert(dstr_t *ds, int idx, const char *str) +{ + char * copy = strdup(ds->data); + size_t len = strlen(str); + + if (idx >= ds->len) + idx = ds->len; + else if (idx < 0) + idx = 0; + + if (ds->len + len >= ds->alloc) { + do { + ds->alloc *= 2; + } while (ds->len + len >= ds->alloc); + free(ds->data); + ds->data = malloc(ds->alloc); + } + + strncpy(ds->data, copy, idx); + ds->data[idx] = '\0'; + strcat(ds->data, str); + strcat(ds->data, ©[idx]); + ds->len += len; + free(copy); +} + +void dstrinsertf(dstr_t *ds, int idx, const char *str, ...) +{ + va_list ap; + char *strf; + size_t len; + + va_start(ap, str); + len = vsnprintf(NULL, 0, str, ap); + va_end(ap); + + strf = malloc(len +1); + va_start(ap, str); + vsnprintf(strf, len +1, str, ap); + va_end(ap); + + dstrinsert(ds, idx, strf); + + free(strf); +} + +void dstrremove(dstr_t *ds, int idx, size_t count) +{ + char *p1, *p2; + + if (idx + count >= ds->len) + return; + + p1 = &ds->data[idx]; + p2 = &ds->data[idx + count]; + + while (*p2) { + *p1 = *p2; + p1++; + p2++; + } + *p1 = '\0'; +} + +static inline int isnewline(int c) +{ + return c == '\n' || c == '\r'; +} + +void dstrcatline(dstr_t *ds, const char *str) +{ + size_t eol = strcspn(str, "\n\r"); + if (isnewline(str[eol])) + eol++; + dstrncat(ds, str, eol); +} + +int dstrendswith(dstr_t *ds, const char *str) +{ + int len = strlen(str); + char *pstr; + + if (ds->len < len) + return 0; + pstr = &ds->data[ds->len - len]; + return strcmp(pstr, str) == 0; + +} + +void dstrfixnewlines(dstr_t *ds) +{ + if (ds->data[ds->len -1] == '\r') { + ds->data[ds->len -1] = '\n'; + } + else if (ds->data[ds->len -2] == '\r') { + ds->data[ds->len -1] = '\n'; + ds->data[ds->len -2] = '\0'; + ds->len -= 1; + } +} + +void dstrremovenewline(dstr_t *ds) +{ + if (!ds->len) + return; + + if (ds->data[ds->len -1] == '\r' || ds->data[ds->len -1] == '\n') { + ds->data[ds->len -1] = '\0'; + ds->len -= 1; + } + + if (ds->len < 2) + return; + + if (ds->data[ds->len -2] == '\r') { + ds->data[ds->len -2] = '\0'; + ds->len -= 2; + } +} + +void dstrtrim(dstr_t *ds) +{ + int pos = 0; + + while (pos < ds->len && isspace(ds->data[pos])) + pos++; + + if (pos > 0) { + ds->len -= pos; + memmove(ds->data, &ds->data[pos], ds->len +1); + } +} + +void dstrtrim_right(dstr_t *ds) +{ + if (!ds->len) + return; + + while (isspace(ds->data[ds->len -1])) + ds->len -= 1; + ds->data[ds->len] = '\0'; +} + + + +/* + * LIST + */ + +list_t * list_create() +{ + list_t *l = malloc(sizeof(list_t)); + l->first = NULL; + l->last = NULL; + return l; +} + +list_t * list_create_from_array(int count, void ** data) +{ + int i; + list_t *l = list_create(); + + for (i = 0; i < count; i++) + list_append(l, data[i]); + + return l; +} + +void list_free(list_t *list) +{ + listitem_t *i = list->first, *tmp; + while (i) { + tmp = i->next; + free(i); + i = tmp; + } +} + +size_t list_item_count(list_t *list) +{ + size_t cnt = 0; + listitem_t *i; + for (i = list->first; i; i = i->next) + cnt++; + return cnt; +} + +list_t * list_copy(list_t *list) +{ + list_t *l = list_create(); + listitem_t *i; + + for (i = list->first; i; i = i->next) + list_append(l, i->data); + return l; +} + +void list_prepend(list_t *list, void *data) +{ + listitem_t *item; + + assert(list); + + item = malloc(sizeof(listitem_t)); + item->data = data; + item->prev = NULL; + + if (list->first) { + item->next = list->first; + list->first->next = item; + list->first = item; + } + else { + item->next = NULL; + list->first = item; + list->last = item; + } +} + +void list_append(list_t *list, void *data) +{ + listitem_t *item; + + assert(list); + + item = malloc(sizeof(listitem_t)); + item->data = data; + item->next = NULL; + + if (list->last) { + item->prev = list->last; + list->last->next = item; + list->last = item; + } + else { + item->prev = NULL; + list->first = item; + list->last = item; + } +} + +void list_remove(list_t *list, listitem_t *item) +{ + assert(item); + + if (item->prev) + item->prev->next = item->next; + if (item->next) + item->next->prev = item->prev; + if (item == list->first) + list->first = item->next; + if (item == list->last) + list->last = item->prev; + + free(item); +} + +listitem_t * list_get(list_t *list, int idx) +{ + listitem_t *i; + for (i = list->first; i && idx; i = i->next) + idx--; + return i; +} + +listitem_t * arglist_find(list_t *list, const char *name) +{ + listitem_t *i; + for (i = list->first; i; i = i->next) { + if (!strcmp((const char*)i->data, name)) + return i; + } + return NULL; +} + +listitem_t * arglist_find_prefix(list_t *list, const char *name) +{ + listitem_t *i; + for (i = list->first; i; i= i->next) { + if (!prefixcmp((const char*)i->data, name)) + return i; + } + return NULL; +} + + +char * arglist_get_value(list_t *list, const char *name) +{ + listitem_t *i; + char *p; + + for (i = list->first; i; i = i->next) { + if (i->next && !strcmp(name, (char*)i->data)) + return (char*)i->next->data; + else if (!prefixcmp((char*)i->data, name)) { + p = &((char*)i->data)[strlen(name)]; + return *p == '=' ? p +1 : p; + } + } + return NULL; +} + +char * arglist_get(list_t *list, int idx) +{ + listitem_t *i = list_get(list, idx); + return i ? (char*)i->data : NULL; +} + +int arglist_remove(list_t *list, const char *name) +{ + listitem_t *i; + char *i_name; + + for (i = list->first; i; i = i->next) { + i_name = (char*)i->data; + if (i->next && !strcmp(name, i_name)) { + list_remove(list, i->next); + list_remove(list, i); + return 1; + } + else if (!prefixcmp(i_name, name)) { + list_remove(list, i); + return 1; + } + } + return 0; +} + +int arglist_remove_flag(list_t *list, const char *name) +{ + listitem_t *i = arglist_find(list, name); + if (i) { + list_remove(list, i); + return 1; + } + return 0; +} + +int copy_file(FILE *dest, + FILE *src, + const char *alreadyread, + size_t alreadyread_len) +{ + char buf[8192]; + size_t bytes; + + if (alreadyread && alreadyread_len) + { + if (fwrite(alreadyread, 1, alreadyread_len, dest) < alreadyread_len) + { + _log("Could not write to temp file\n"); + return 0; + } + } + + while ((bytes = fread(buf, 1, 8192, src))) + fwrite(buf, 1, bytes, dest); + + return !ferror(src) && !ferror(dest); +} + diff --git a/filter/foomatic-rip/util.h b/filter/foomatic-rip/util.h new file mode 100644 index 000000000..c4fb5b6e5 --- /dev/null +++ b/filter/foomatic-rip/util.h @@ -0,0 +1,207 @@ +/* util.h + * + * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com> + * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de> + * + * This file is part of foomatic-rip. + * + * Foomatic-rip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foomatic-rip is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef util_h +#define util_h + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "config.h" +#include <string.h> +#include <stdio.h> + + +extern const char* shellescapes; + +int isempty(const char *string); +const char * temp_dir(); +int prefixcmp(const char *str, const char *prefix); +int prefixcasecmp(const char *str, const char *prefix); + +int startswith(const char *str, const char *prefix); +int endswith(const char *str, const char *postfix); + +const char * skip_whitespace(const char *str); + +void strlower(char *dest, size_t destlen, const char *src); + +/* + * Like strncpy, but omits characters for which omit_func returns true + * It also assures that dest is zero terminated. + * Returns a pointer to the position in 'src' right after the last byte that has been copied. + */ +const char * strncpy_omit(char* dest, const char* src, size_t n, int (*omit_func)(int)); + +int omit_unprintables(int c); +int omit_shellescapes(int c); +int omit_specialchars(int c); +int omit_whitespace(int c); +int omit_whitespace_newline(int c); + +#ifndef HAVE_STRCASESTR +/* strcasestr() is not available under Solaris */ +char * strcasestr (const char *haystack, const char *needle); +#endif + +/* TODO check for platforms which already have strlcpy and strlcat */ + +/* Copy at most size-1 characters from src to dest + dest will always be \0 terminated (unless size == 0) + returns strlen(src) */ +size_t strlcpy(char *dest, const char *src, size_t size); +size_t strlcat(char *dest, const char *src, size_t size); + +/* Replace all occurences of each of the characters in 'chars' by 'repl' */ +void strrepl(char *str, const char *chars, char repl); + +/* Replace all occurences of each of the characters in 'chars' by 'repl', + but do not allow consecutive 'repl' chars */ +void strrepl_nodups(char *str, const char *chars, char repl); + +/* clears 'str' with \0s */ +void strclr(char *str); + +char * strnchr(const char *str, int c, size_t n); + +void escapechars(char *dest, size_t size, const char *src, const char *esc_chars); + +/* copies characters from 'src' to 'dest', until 'src' contains a character from 'stopchars' + will not copy more than 'max' chars + dest will be zero terminated in either case + returns a pointer to the position right after the last byte that has been copied +*/ +const char * strncpy_tochar(char *dest, const char *src, size_t max, const char *stopchars); + +/* 'paths' is a colon seperated list of paths (like $PATH) + * 'found_in' may be NULL if it is not needed */ +int find_in_path(const char *progname, const char *paths, char *found_in); + +/* extracts the base name of 'path', i.e. only the filename, without path or extension */ +void file_basename(char *dest, const char *path, size_t dest_size); + +/* if 'path' is relative, prepend cwd */ +void make_absolute_path(char *path, int len); + +int is_true_string(const char *str); /* "1", "Yes", "On", "True" */ +int is_false_string(const char *str); /* "0", "No", "Off", "False", "None" */ + +int digit(char c); /* returns 0-9 if c is a digit, otherwise -1 */ + +int line_count(const char *str); + +/* returns the index of the beginning of the line_number'th line in str */ +int line_start(const char *str, int line_number); + +/* Replace hex notation for unprintable characters in PPD files + by the actual characters ex: "<0A>" --> chr(hex("0A")) */ +void unhexify(char *dest, size_t size, const char *src); + +void extract_command(size_t *start, size_t *end, const char *cmdline, const char *cmd); + +char ** argv_split(const char *string, const char *separators, int *cntp); +size_t argv_count(char **argv); +void argv_free(char **argv); + +/* + * Returns non-zero if 'cmdline' calls 'cmd' in some way + */ +int contains_command(const char *cmdline, const char *cmd); + +int copy_file(FILE *dest, FILE *src, const char *alreadyread, size_t alreadyread_len); + +/* Dynamic string */ +typedef struct dstr { + char *data; + size_t len; + size_t alloc; +} dstr_t; + +dstr_t * create_dstr(); +void free_dstr(dstr_t *ds); +void dstrclear(dstr_t *ds); +void dstrassure(dstr_t *ds, size_t alloc); +void dstrcpy(dstr_t *ds, const char *src); +void dstrncpy(dstr_t *ds, const char *src, size_t n); +void dstrncat(dstr_t *ds, const char *src, size_t n); +void dstrcpyf(dstr_t *ds, const char *src, ...); +void dstrcat(dstr_t *ds, const char *src); +void dstrcatf(dstr_t *ds, const char *src, ...); +void dstrputc(dstr_t *ds, int c); +size_t fgetdstr(dstr_t *ds, FILE *stream); /* returns number of characters read */ +int dstrreplace(dstr_t *ds, const char *find, const char *repl, int start); +void dstrprepend(dstr_t *ds, const char *str); +void dstrinsert(dstr_t *ds, int idx, const char *str); +void dstrinsertf(dstr_t *ds, int idx, const char *str, ...); +void dstrremove(dstr_t *ds, int idx, size_t count); +void dstrcatline(dstr_t *ds, const char *str); /* appends the first line from str to ds (incl. \n) */ + +int dstrendswith(dstr_t *ds, const char *str); +void dstrfixnewlines(dstr_t *ds); +void dstrremovenewline(dstr_t *ds); +void dstrtrim(dstr_t *ds); +void dstrtrim_right(dstr_t *ds); + + +/* Doubly linked list of void pointers */ +typedef struct listitem_s { + void *data; + struct listitem_s *prev, *next; +} listitem_t; + +typedef struct { + listitem_t *first, *last; +} list_t; + +list_t * list_create(); +list_t * list_create_from_array(int count, void ** data); /* array values are NOT copied */ +void list_free(list_t *list); + +size_t list_item_count(list_t *list); + +list_t * list_copy(list_t *list); + +void list_prepend(list_t *list, void *data); +void list_append(list_t *list, void *data); +void list_remove(list_t *list, listitem_t *item); + +listitem_t * list_get(list_t *list, int idx); + + +/* Argument values may be seperated from their keys in the following ways: + - with whitespace (i.e. it is in the next list entry) + - with a '=' + - not at all +*/ +listitem_t * arglist_find(list_t *list, const char *name); +listitem_t * arglist_find_prefix(list_t *list, const char *name); + +char * arglist_get_value(list_t *list, const char *name); +char * arglist_get(list_t *list, int idx); + +int arglist_remove(list_t *list, const char *name); +int arglist_remove_flag(list_t *list, const char *name); + +#endif + diff --git a/filter/gstoraster.c b/filter/gstoraster.c index c1eb11a1a..44cf95187 100644 --- a/filter/gstoraster.c +++ b/filter/gstoraster.c @@ -35,27 +35,21 @@ MIT Open Source License - http://www.opensource.org/ #define HAVE_CUPS_1_7 1 #endif +#include <config.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <fcntl.h> #include <cups/raster.h> #include <cupsfilters/raster.h> +#include <cupsfilters/colord.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <errno.h> -#include "colord.h" - #define PDF_MAX_CHECK_COMMENT_LINES 20 -#ifndef GS -#define GS "gs" -#endif -#ifndef BINDIR -#define BINDIR "/usr/bin" -#endif #ifndef CUPS_FONTPATH #define CUPS_FONTPATH "/usr/share/cups/fonts" #endif @@ -551,6 +545,9 @@ main (int argc, char **argv, char *envp[]) int status = 1; ppd_file_t *ppd = NULL; struct sigaction sa; +#ifdef HAVE_CUPS_1_7 + int pwgraster; +#endif /* HAVE_CUPS_1_7 */ if (argc < 6 || argc > 7) { fprintf(stderr, "ERROR: %s job-id user title copies options [file]\n", @@ -661,7 +658,7 @@ main (int argc, char **argv, char *envp[]) /* Part of Ghostscript command line which is not dependent on the job and/or the driver */ - snprintf(tmpstr, sizeof(tmpstr), "%s/%s", BINDIR, GS); + snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_GHOSTSCRIPT); cupsArrayAdd(gs_args, strdup(tmpstr)); cupsArrayAdd(gs_args, strdup("-dQUIET")); /*cupsArrayAdd(gs_args, strdup("-dDEBUG"));*/ @@ -680,14 +677,25 @@ main (int argc, char **argv, char *envp[]) if (ppd) cupsRasterInterpretPPD(&h,ppd,num_options,options,0); else + { #ifdef HAVE_CUPS_1_7 - cupsRasterParseIPPOptions(&h,num_options,options,1,1); + pwgraster = 1; + t = cupsGetOption("media-class", num_options, options); + if (t == NULL) + t = cupsGetOption("MediaClass", num_options, options); + if (t != NULL) + { + if (strcasestr(t, "pwg")) + pwgraster = 1; + else + pwgraster = 0; + } + cupsRasterParseIPPOptions(&h, num_options, options, pwgraster, 1); #else - { fprintf(stderr, "ERROR: No PPD file specified.\n"); exit(1); - } #endif /* HAVE_CUPS_1_7 */ + } /* setPDF specific options */ if (doc_type == GS_DOC_TYPE_PDF) { @@ -716,6 +724,16 @@ main (int argc, char **argv, char *envp[]) /* Switch to taking PostScript commands on the Ghostscript command line */ cupsArrayAdd(gs_args, strdup("-c")); + /* Set margins if we have a bounding box defined */ + if (h.cupsImagingBBox[3] > 0.0) { + snprintf(tmpstr, sizeof(tmpstr), + "<</.HWMargins[%f %f %f %f] /Margins[0 0]>>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 ((t = cupsGetOption("profile", num_options, options)) != NULL) { snprintf(tmpstr, sizeof(tmpstr), "<</cupsProfile(%s)>>setpagedevice", t); cupsArrayAdd(gs_args, strdup(tmpstr)); @@ -729,7 +747,7 @@ main (int argc, char **argv, char *envp[]) cupsArrayAdd(gs_args, strdup("-_")); /* Execute Ghostscript command line ... */ - snprintf(tmpstr, sizeof(tmpstr), "%s/%s", BINDIR, GS); + snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_GHOSTSCRIPT); /* call Ghostscript */ rewind(fp); diff --git a/filter/pcl-common.c b/filter/pcl-common.c index a62dc98c1..7b1811196 100644 --- a/filter/pcl-common.c +++ b/filter/pcl-common.c @@ -37,12 +37,66 @@ pcl_set_media_size(ppd_file_t *ppd, /* I - PPD file */ float width, /* I - Width of page */ float length) /* I - Length of page */ { - (void)width; + float l; + int l_int; + + if (width < length) + l = length; + else + l = width; + l_int = (int)(l + 0.5f); + fprintf (stderr, "DEBUG: Width: %f Length: %f Long Edge: %f\n", + width, length, l); printf("\033&l0O"); /* Set portrait orientation */ if (!ppd || ppd->model_number & PCL_PAPER_SIZE) - switch ((int)(length + 0.5f)) + { + if (l_int >= 418 && l_int <= 420) /* Postcard */ + printf("\033&l71A"); /* Set page size */ + else if (l_int >= 539 && l_int <= 541) /* Monarch Envelope */ + printf("\033&l80A"); /* Set page size */ + else if (l_int >= 566 && l_int <= 568) /* Double Postcard */ + printf("\033&l72A"); /* Set page size */ + else if (l_int >= 594 && l_int <= 596) /* A5 */ + printf("\033&l25A"); /* Set page size */ + else if (l_int >= 611 && l_int <= 613) /* Statement */ + printf("\033&l5A"); /* Set page size */ + else if (l_int >= 623 && l_int <= 625) /* DL Envelope */ + printf("\033&l90A"); /* Set page size */ + else if (l_int >= 648 && l_int <= 650) /* C5 Envelope */ + printf("\033&l91A"); /* Set page size */ + else if (l_int >= 683 && l_int <= 685) /* COM-10 Envelope */ + printf("\033&l81A"); /* Set page size */ + else if (l_int >= 708 && l_int <= 710) /* B5 Envelope */ + printf("\033&l100A"); /* Set page size */ + else if (l_int >= 728 && l_int <= 730) /* B5 */ + printf("\033&l45A"); /* Set page size */ + else if (l_int >= 755 && l_int <= 757) /* Executive */ + printf("\033&l1A"); /* Set page size */ + else if (l_int >= 791 && l_int <= 793) /* Letter */ + printf("\033&l2A"); /* Set page size */ + else if (l_int >= 841 && l_int <= 843) /* A4 */ + printf("\033&l26A"); /* Set page size */ + else if (l_int >= 935 && l_int <= 937) /* Foolscap */ + printf("\033&l23A"); /* Set page size */ + else if (l_int >= 1007 && l_int <= 1009) /* Legal */ + printf("\033&l3A"); /* Set page size */ + else if (l_int >= 1031 && l_int <= 1033) /* B4 */ + printf("\033&l46A"); /* Set page size */ + else if (l_int >= 1190 && l_int <= 1192) /* A3 */ + printf("\033&l27A"); /* Set page size */ + else if (l_int >= 1223 && l_int <= 1225) /* Tabloid */ + printf("\033&l6A"); /* Set page size */ + else + { + printf("\033&l101A"); /* Set page size */ + printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */ + printf("\033&l%.2fP", l / 12.0); /* Set page length */ + printf("\033&l%.0fF", l / 12.0); /* Set text length to page */ + } +#if 0 + switch ((int)(l + 0.5f)) { case 419 : /* Postcard */ printf("\033&l71A"); /* Set page size */ @@ -119,18 +173,20 @@ pcl_set_media_size(ppd_file_t *ppd, /* I - PPD file */ default : printf("\033&l101A"); /* Set page size */ printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */ - printf("\033&l%.2fP", length / 12.0); + printf("\033&l%.2fP", l / 12.0); /* Set page length */ - printf("\033&l%.0fF", length / 12.0); + printf("\033&l%.0fF", l / 12.0); /* Set text length to page */ break; } +#endif + } else { printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */ - printf("\033&l%.2fP", length / 12.0); + printf("\033&l%.2fP", l / 12.0); /* Set page length */ - printf("\033&l%.0fF", length / 12.0); + printf("\033&l%.0fF", l / 12.0); /* Set text length to page */ } diff --git a/filter/pdftoippprinter.c b/filter/pdftoippprinter.c index a006c1aea..67d4cb7f9 100644 --- a/filter/pdftoippprinter.c +++ b/filter/pdftoippprinter.c @@ -17,6 +17,8 @@ * exec_filter() - Execute a filter process * exec_filters() - Execute a filter chain * open_pipe() - Create a pipe to transfer data from filter to filter + * get_option_in_str() - Get an option value from a string like argv[5] + * set_option_in_str() - Set an option value in a string like argv[5] */ /* @@ -62,6 +64,11 @@ static int exec_filter(const char *filter, char **argv, int infd, int outfd); static int exec_filters(cups_array_t *filters, char **argv); static int open_pipe(int *fds); +static char* get_option_in_str(char *buf, const char *option, + int return_value); +static void set_option_in_str(char *buf, int buflen, + const char *option, + const char *value); /* * Local globals... @@ -69,6 +76,7 @@ static int open_pipe(int *fds); static int job_canceled = 0; + /* * 'main()' - Main entry for filter... */ @@ -87,14 +95,28 @@ main(int argc, /* I - Number of command-line args */ int num_options; /* Number of options */ cups_option_t *options; /* Options */ const char *val; /* Option value */ - const char *argv_nt[8]; /* NULL-terminated array of the command + char *argv_nt[8]; /* NULL-terminated array of the command line arguments */ + int optbuflen; cups_array_t *filter_chain; /* Filter chain to execute */ int exit_status = 0; /* Exit status */ - char *filter; + int color_printing; /* Do we print in color? */ + char *filter, *p; #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Actions for POSIX signals */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ + static const char * const color_mode_option_names[] = + { /* Possible names for a color mode option */ + "pwg-raster-document-type", + "PwgRasterDocumentType", + "print-color-mode", + "PrintColorMode", + "color-space", + "ColorSpace", + "color-model", + "ColorModel", + NULL + }; /* @@ -182,8 +204,13 @@ main(int argc, /* I - Number of command-line args */ * Copy the command line arguments into a NULL-terminated array */ - for (i = 0; i < 6; i++) + for (i = 0; i < 5; i++) argv_nt[i] = argv[i]; + /* We copy the contents of argv[5] into a somewhat larger buffer so that + we can manipulate it */ + optbuflen = strlen(argv[5]) + 256; + argv_nt[5] = calloc(optbuflen, sizeof(char)); + strcpy(argv_nt[5], (const char*)argv[5]); argv_nt[6] = filename; argv_nt[7] = NULL; @@ -219,12 +246,15 @@ main(int argc, /* I - Number of command-line args */ if (strcasestr(val, "raster")) { output_format = PWGRASTER; - if (filter_present("gstoraster")) + /* PWG Raster output */ + set_option_in_str(argv_nt[5], optbuflen, "MediaClass", NULL); + set_option_in_str(argv_nt[5], optbuflen, "media-class", "PwgRaster"); + if (filter_present("gstoraster") && access(CUPS_GHOSTSCRIPT, X_OK) == 0) cupsArrayAdd(filter_chain, "gstoraster"); else { fprintf(stderr, - "DEBUG: Filter gstoraster missing for \"output-format=%s\", using pdftoraster.\n", val); + "DEBUG: Filter gstoraster or Ghostscript (%s) missing for \"output-format=%s\", using pdftoraster.\n", CUPS_GHOSTSCRIPT, val); if (filter_present("pdftoraster")) cupsArrayAdd(filter_chain, "pdftoraster"); else @@ -235,15 +265,6 @@ main(int argc, /* I - Number of command-line args */ goto error; } } - /*if (filter_present("rastertopwg")) - cupsArrayAdd(filter_chain, "rastertopwg"); - else - { - fprintf(stderr, - "ERROR: Filter rastertopwg missing for \"output-format=%s\"\n", val); - exit_status = 1; - goto error; - }*/ } else if (strcasestr(val, "pdf")) output_format = PDF; @@ -296,12 +317,53 @@ main(int argc, /* I - Number of command-line args */ } if (output_format == PCL) { - if (filter_present("gstoraster")) + /* We need CUPS Raster as we want to use rastertopclx with unprintable + margins */ + set_option_in_str(argv_nt[5], optbuflen, "MediaClass", NULL); + set_option_in_str(argv_nt[5], optbuflen, "media-class", ""); + /* Does the client send info about margins? */ + if (!get_option_in_str(argv_nt[5], "media-left-margin", 0) && + !get_option_in_str(argv_nt[5], "media-right-margin", 0) && + !get_option_in_str(argv_nt[5], "media-top-margin", 0) && + !get_option_in_str(argv_nt[5], "media-bottom-margin", 0)) + { + /* Set default 12pt margins if there is no info about printer's + unprintable margins (100th of mm units, 12.0 * 2540.0 / 72.0 = 423.33) + */ + set_option_in_str(argv_nt[5], optbuflen, "media-left-margin", "423.33"); + set_option_in_str(argv_nt[5], optbuflen, "media-right-margin", "423.33"); + set_option_in_str(argv_nt[5], optbuflen, "media-top-margin", "423.33"); + set_option_in_str(argv_nt[5], optbuflen, "media-bottom-margin", "423.33"); + } + /* Check whether the job is requested to be printed in color and if so, + set the color space to RGB as this is the best color printing support + in PCL 5c */ + color_printing = 0; + for (i = 0; color_mode_option_names[i]; i ++) + { + p = get_option_in_str(argv_nt[5], color_mode_option_names[i], 1); + if (p && (strcasestr(p, "RGB") || strcasestr(p, "CMY") || + strcasestr(p, "color"))) + { + color_printing = 1; + break; + } + } + if (color_printing == 1) + { + /* Remove unneeded color mode options */ + for (i = 0; color_mode_option_names[i]; i ++) + set_option_in_str(argv_nt[5], optbuflen, color_mode_option_names[i], + NULL); + /* Set RGB as color mode */ + set_option_in_str(argv_nt[5], optbuflen, "print-color-mode", "RGB"); + } + if (filter_present("gstoraster") && access(CUPS_GHOSTSCRIPT, X_OK) == 0) cupsArrayAdd(filter_chain, "gstoraster"); else { fprintf(stderr, - "DEBUG: Filter gstoraster missing for \"output-format=%s\", using pdftoraster.\n", val); + "DEBUG: Filter gstoraster or Ghostscript (%s) missing for \"output-format=%s\", using pdftoraster.\n", CUPS_GHOSTSCRIPT, val); if (filter_present("pdftoraster")) cupsArrayAdd(filter_chain, "pdftoraster"); else @@ -689,6 +751,86 @@ open_pipe(int *fds) /* O - Pipe file descriptors (2) */ return (0); } + +/* + * Get option value in a string of options + */ + +static char* /* O - Value, NULL if option not set */ +get_option_in_str(char *buf, /* I - Buffer with option list string */ + const char *option, /* I - Option of which to get value */ + int return_value) /* I - Return value or only check + presence of option? */ +{ + char *p1, *p2; + char *result; + + if (!buf || !option) + return NULL; + if ((p1 = strcasestr(buf, option)) == NULL) + return NULL; + if (p1 > buf && *(p1 - 1) != ' ' && *(p1 - 1) != '\t') + return NULL; + p2 = p1 + strlen(option); + if (*p2 == ' ' || *p2 == '\t' || *p2 == '\0') + return ""; + if (*p2 != '=') + return NULL; + if (!return_value) + return ""; + p1 = p2 + 1; + for (p2 = p1; *p2 != ' ' && *p2 != '\t' && *p2 != '\0'; p2 ++); + if (p2 == p1) + return ""; + result = calloc(p2 - p1 + 1, sizeof(char)); + memcpy(result, p1, p2 - p1); + result[p2 - p1] = '\0'; + return result; +} + + +/* + * Set an option in a string of options + */ + +void /* O - 0 on success, 1 on error */ +set_option_in_str(char *buf, /* I - Buffer with option list string */ + int buflen, /* I - Length of buffer */ + const char *option, /* I - Option to change/add */ + const char *value) /* I - New value for option, NULL + removes option */ +{ + char *p1, *p2; + + if (!buf || buflen == 0 || !option) + return; + /* Remove any occurrence of option in the string */ + p1 = buf; + while (*p1 != '\0' && (p2 = strcasestr(p1, option)) != NULL) + { + if (p2 > buf && *(p2 - 1) != ' ' && *(p2 - 1) != '\t') + { + p1 = p2 + 1; + continue; + } + p1 = p2 + strlen(option); + if (*p1 != '=' && *p1 != ' ' && *p1 != '\t' && *p1 != '\0') + continue; + while (*p1 != ' ' && *p1 != '\t' && *p1 != '\0') p1 ++; + while ((*p1 == ' ' || *p1 == '\t') && *p1 != '\0') p1 ++; + memmove(p2, p1, strlen(buf) - (buf - p1) + 1); + p1 = p2; + } + /* Add option=value to the end of the string */ + if (!value) + return; + p1 = buf + strlen(buf); + *p1 = ' '; + p1 ++; + snprintf(p1, buflen - (buf - p1), "%s=%s", option, value); + buf[buflen - 1] = '\0'; +} + /* * End */ diff --git a/filter/pdftopdf/pdftopdf.cc b/filter/pdftopdf/pdftopdf.cc index 6cb012f62..d60fd7f7f 100644 --- a/filter/pdftopdf/pdftopdf.cc +++ b/filter/pdftopdf/pdftopdf.cc @@ -119,14 +119,28 @@ static bool is_true(const char *value) // {{{ static bool ppdGetDuplex(ppd_file_t *ppd) // {{{ { - return 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,"KD03Duplex","DuplexNoTumble")|| - ppdIsMarked(ppd,"KD03Duplex","DuplexTumble"); + const char **option, **choice; + const char *option_names[] = { + "Duplex", + "JCLDuplex", + "EFDuplex", + "KD03Duplex", + NULL + }; + const char *choice_names[] = { + "DuplexNoTumble", + "DuplexTumble", + "LongEdge", + "ShortEdge", + "Top", + "Bottom", + NULL + }; + for (option = option_names; *option; option ++) + for (choice = choice_names; *choice; choice ++) + if (ppdIsMarked(ppd, *option, *choice)) + return 1; + return 0; } // }}} diff --git a/filter/pdftoraster.cxx b/filter/pdftoraster.cxx index b0f3bfe1b..8da042a7d 100644 --- a/filter/pdftoraster.cxx +++ b/filter/pdftoraster.cxx @@ -445,15 +445,25 @@ static void parseOpts(int argc, char **argv) /* ICCProfile is specified */ colorProfile = cmsOpenProfileFromFile(profilePath.getCString(),"r"); } - } else + } else { #ifdef HAVE_CUPS_1_7 - cupsRasterParseIPPOptions(&header,num_options,options,1,1); + int pwgraster = 1; + const char *t = cupsGetOption("media-class", num_options, options); + if (t == NULL) + t = cupsGetOption("MediaClass", num_options, options); + if (t != NULL) + { + if (strcasestr(t, "pwg")) + pwgraster = 1; + else + pwgraster = 0; + } + cupsRasterParseIPPOptions(&header,num_options,options,pwgraster,1); #else - { fprintf(stderr, "ERROR: No PPD file specified.\n"); exit(1); - } #endif /* HAVE_CUPS_1_7 */ + } } static void parsePDFTOPDFComment(FILE *fp) @@ -1553,6 +1563,7 @@ static void outPage(PDFDoc *doc, Catalog *catalog, int pageNo, SplashBitmap *bitmap; Page *page = catalog->getPage(pageNo); PDFRectangle *mediaBox = page->getMediaBox(); + int rotate = page->getRotate(); double paperdimensions[2], /* Physical size of the paper */ margins[4]; /* Physical margins of print */ ppd_size_t *size; /* Page size */ @@ -1560,14 +1571,20 @@ static void outPage(PDFDoc *doc, Catalog *catalog, int pageNo, int i; bool landscape = 0; - fprintf(stderr, "DEBUG: mediaBox = [ %f %f %f %f ]\n", - mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2); + fprintf(stderr, "DEBUG: mediaBox = [ %f %f %f %f ]; rotate = %d\n", + mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2, rotate); l = mediaBox->x2 - mediaBox->x1; if (l < 0) l = -l; - header.PageSize[0] = (unsigned)l; + if (rotate == 90 || rotate == 270) + header.PageSize[1] = (unsigned)l; + else + header.PageSize[0] = (unsigned)l; l = mediaBox->y2 - mediaBox->y1; if (l < 0) l = -l; - header.PageSize[1] = (unsigned)l; + if (rotate == 90 || rotate == 270) + header.PageSize[0] = (unsigned)l; + else + header.PageSize[1] = (unsigned)l; memset(paperdimensions, 0, sizeof(paperdimensions)); memset(margins, 0, sizeof(margins)); @@ -1638,8 +1655,16 @@ static void outPage(PDFDoc *doc, Catalog *catalog, int pageNo, } else { for (i = 0; i < 2; i ++) paperdimensions[i] = header.PageSize[i]; - for (i = 0; i < 4; i ++) - margins[i] = 0.0; + if (header.cupsImagingBBox[3] > 0.0) { + /* Set margins if we have a bounding box defined ... */ + margins[0] = header.cupsImagingBBox[0]; + margins[1] = header.cupsImagingBBox[1]; + margins[2] = paperdimensions[0] - header.cupsImagingBBox[2]; + margins[3] = paperdimensions[1] - 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]; @@ -1664,21 +1689,31 @@ static void outPage(PDFDoc *doc, Catalog *catalog, int pageNo, bitmapoffset[1] = margins[3] / 72.0 * header.HWResolution[1]; /* write page header */ - header.cupsWidth = (paperdimensions[0] - margins[0] - margins[2]) / 72.0 * - header.HWResolution[0]; - header.cupsHeight = (paperdimensions[1] - margins[1] - margins[3]) / 72.0 * - header.HWResolution[1]; + header.cupsWidth = ((paperdimensions[0] - margins[0] - margins[2]) / + 72.0 * header.HWResolution[0]) + 0.5; + header.cupsHeight = ((paperdimensions[1] - margins[1] - margins[3]) / + 72.0 * header.HWResolution[1] + 0.5); for (i = 0; i < 2; i ++) { header.cupsPageSize[i] = paperdimensions[i]; - header.PageSize[i] = (unsigned int)header.cupsPageSize[i]; - header.Margins[i] = margins[i]; - } - header.cupsImagingBBox[0] = margins[0]; - header.cupsImagingBBox[1] = margins[1]; - header.cupsImagingBBox[2] = paperdimensions[0]-margins[2]; - header.cupsImagingBBox[3] = paperdimensions[1]-margins[3]; - for (i = 0; i < 4; i ++) - header.ImagingBoundingBox[i] = (unsigned int)header.cupsImagingBBox[i]; + header.PageSize[i] = (unsigned int)(header.cupsPageSize[i] + 0.5); + if (strcasecmp(header.MediaClass, "PwgRaster") != 0) + header.Margins[i] = margins[i] + 0.5; + else + header.Margins[i] = 0; + } + if (strcasecmp(header.MediaClass, "PwgRaster") != 0) { + header.cupsImagingBBox[0] = margins[0]; + header.cupsImagingBBox[1] = margins[1]; + header.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; + header.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; + for (i = 0; i < 4; i ++) + header.ImagingBoundingBox[i] = + (unsigned int)(header.cupsImagingBBox[i] + 0.5); + } else + for (i = 0; i < 4; i ++) { + header.cupsImagingBBox[i] = 0.0; + header.ImagingBoundingBox[i] = 0; + } bytesPerLine = header.cupsBytesPerLine = (header.cupsBitsPerPixel * header.cupsWidth + 7) / 8; diff --git a/filter/rastertopclx.c b/filter/rastertopclx.c index fbca783a9..ca73bad45 100644 --- a/filter/rastertopclx.c +++ b/filter/rastertopclx.c @@ -287,7 +287,7 @@ StartPage(ppd_file_t *ppd, /* I - PPD file */ memset(DitherStates, 0, sizeof(DitherStates)); } else if (header->cupsColorSpace == CUPS_CSPACE_RGB && - (ppd && (ppd->model_number & PCL_RASTER_RGB24))) + (!ppd || (ppd->model_number & PCL_RASTER_RGB24))) { /* * Use 24-bit RGB output mode... @@ -358,15 +358,15 @@ StartPage(ppd_file_t *ppd, /* I - PPD file */ fputs("DEBUG: Loaded CMYK separation from PPD.\n", stderr); else { - /*if (header->cupsColorSpace == CUPS_CSPACE_KCMY || + if (header->cupsColorSpace == CUPS_CSPACE_KCMY || header->cupsColorSpace == CUPS_CSPACE_CMYK) PrinterPlanes = 4; else if (header->cupsColorSpace == CUPS_CSPACE_CMY) PrinterPlanes = 3; - else*/ - PrinterPlanes = 1; - fputs("DEBUG: Loading default K separation.\n", stderr); - /*fprintf(stderr, "DEBUG: Color Space: %d; Color Planes %d\n", header->cupsColorSpace, PrinterPlanes);*/ + else + PrinterPlanes = 1; + /*fputs("DEBUG: Loading default K separation.\n", stderr);*/ + fprintf(stderr, "DEBUG: Color Space: %d; Color Planes %d\n", header->cupsColorSpace, PrinterPlanes); CMYK = cupsCMYKNew(PrinterPlanes); } @@ -467,6 +467,8 @@ StartPage(ppd_file_t *ppd, /* I - PPD file */ snprintf(spec, sizeof(spec), "COLORSPACE.%s", colormodel); if (ppd && ((attr = ppdFindAttr(ppd, "cupsPJL", spec)) != NULL)) printf("@PJL SET COLORSPACE=%s\r\n", attr->value); + if (!ppd) + printf("@PJL SET COLORSPACE=%s\r\n", colormodel); snprintf(spec, sizeof(spec), "RENDERINTENT.%s", colormodel); if (ppd && ((attr = ppdFindAttr(ppd, "cupsPJL", spec)) != NULL)) @@ -730,7 +732,7 @@ StartPage(ppd_file_t *ppd, /* I - PPD file */ } } } - else if (ppd && (ppd->model_number & PCL_RASTER_CID) && + else if ((!ppd || (ppd->model_number & PCL_RASTER_CID)) && OutputMode == OUTPUT_RGB) { /* @@ -740,8 +742,8 @@ StartPage(ppd_file_t *ppd, /* I - PPD file */ pcl_set_simple_resolution(header->HWResolution[0]); /* Set output resolution */ - cupsWritePrintData("\033*v6W\0\3\0\10\10\10", 11); - /* 24-bit RGB */ + cupsWritePrintData("\033*v6W\2\3\0\10\10\10", 11); + /* 24-bit sRGB */ } else { diff --git a/mime/cupsfilters.convs b/mime/cupsfilters.convs index 5ce3df556..11313152b 100644 --- a/mime/cupsfilters.convs +++ b/mime/cupsfilters.convs @@ -85,7 +85,7 @@ application/vnd.cups-pdf application/vnd.cups-postscript 100 pdftops # application/vnd.cups-pdf application/vnd.cups-raster 99 gstoraster -application/vnd.cups-postscript application/vnd.cups-raster 200 gstoraster +application/vnd.cups-postscript application/vnd.cups-raster 175 gstoraster application/vnd.cups-pdf application/vnd.cups-raster 100 pdftoraster image/gif application/vnd.cups-raster 100 imagetoraster image/png application/vnd.cups-raster 100 imagetoraster diff --git a/utils/cups-browsed.c b/utils/cups-browsed.c index c312804d4..155b581c7 100644 --- a/utils/cups-browsed.c +++ b/utils/cups-browsed.c @@ -993,6 +993,10 @@ void generate_local_queue(const char *host, would get, so ignore this remote printer */ debug_printf("cups-browsed: Printer with URI %s already exists, printer ignored.\n", uri); + free (uri); + free (remote_host); + free (backup_queue_name); + cupsFreeDests(num_dests, dests); return; } /* Is there a local queue with the name of the remote queue? */ @@ -1027,6 +1031,7 @@ void generate_local_queue(const char *host, ignore this remote printer */ debug_printf("cups-browsed: %s also taken, printer ignored.\n", local_queue_name); + free (uri); free (backup_queue_name); free (remote_host); cupsFreeDests(num_dests, dests); @@ -2426,9 +2431,9 @@ int main(int argc, char*argv[]) { strcasecmp(val, "false") != 0) { /* Queue found, add to our list */ p = create_local_queue (dest->name, - strdup(cupsGetOption("device-uri", - dest->num_options, - dest->options)), + cupsGetOption("device-uri", + dest->num_options, + dest->options), "", "", "", "", NULL, 1); if (p) { /* Mark as unconfirmed, if no Avahi report of this queue appears |