summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDidier Raboud <odyx@debian.org>2013-12-05 17:27:40 +0100
committerDidier Raboud <odyx@debian.org>2013-12-05 17:27:40 +0100
commit372533b24d77236e15db57aeeb858a7f82435b41 (patch)
tree9655eb4b714f98a13240fbc9aa4c7fcd76eacf2b
parente16e72cafa4ed8c150fdd5a597b5175fb5a78c65 (diff)
Imported Upstream version 1.0.42
-rw-r--r--INSTALL2
-rw-r--r--Makefile.am39
-rw-r--r--Makefile.in349
-rw-r--r--NEWS76
-rw-r--r--README2
-rw-r--r--backend/parallel.c4
-rw-r--r--backend/serial.c4
-rwxr-xr-xconfigure153
-rw-r--r--configure.ac14
-rw-r--r--cupsfilters/colord.c (renamed from filter/colord.c)2
-rw-r--r--cupsfilters/colord.h (renamed from filter/colord.h)13
-rw-r--r--cupsfilters/driver.h6
-rw-r--r--cupsfilters/image-jpeg.c2
-rw-r--r--cupsfilters/image.h6
-rw-r--r--cupsfilters/raster.c73
-rw-r--r--cupsfilters/raster.h6
-rw-r--r--filter/foomatic-rip/foomatic-rip.1229
-rw-r--r--filter/foomatic-rip/foomatic-rip.1.in229
-rw-r--r--filter/foomatic-rip/foomaticrip.c1065
-rw-r--r--filter/foomatic-rip/foomaticrip.h118
-rw-r--r--filter/foomatic-rip/options.c2279
-rw-r--r--filter/foomatic-rip/options.h184
-rw-r--r--filter/foomatic-rip/pdf.c301
-rw-r--r--filter/foomatic-rip/pdf.h30
-rw-r--r--filter/foomatic-rip/postscript.c1223
-rw-r--r--filter/foomatic-rip/postscript.h30
-rw-r--r--filter/foomatic-rip/process.c224
-rw-r--r--filter/foomatic-rip/process.h48
-rw-r--r--filter/foomatic-rip/renderer.c490
-rw-r--r--filter/foomatic-rip/renderer.h31
-rw-r--r--filter/foomatic-rip/spooler.c340
-rw-r--r--filter/foomatic-rip/spooler.h35
-rw-r--r--filter/foomatic-rip/util.c1122
-rw-r--r--filter/foomatic-rip/util.h207
-rw-r--r--filter/gstoraster.c44
-rw-r--r--filter/pcl-common.c68
-rw-r--r--filter/pdftoippprinter.c174
-rw-r--r--filter/pdftopdf/pdftopdf.cc30
-rw-r--r--filter/pdftoraster.cxx81
-rw-r--r--filter/rastertopclx.c20
-rw-r--r--mime/cupsfilters.convs2
-rw-r--r--utils/cups-browsed.c11
42 files changed, 9161 insertions, 205 deletions
diff --git a/INSTALL b/INSTALL
index 3e39c4310..7216cea10 100644
--- a/INSTALL
+++ b/INSTALL
@@ -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 \
diff --git a/NEWS b/NEWS
index 5cfe48514..779f07bcb 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/README b/README
index f05003d19..427ae1583 100644
--- a/README
+++ b/README
@@ -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);
diff --git a/configure b/configure
index ac7dcea62..20adbbcba 100755
--- a/configure
+++ b/configure
@@ -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 < &lt;
+.B > &gt;
+.B " &quot;
+.B ' &apos;
+.B & &amp;
+.fi
+
+This way you can print directly to your printer, use
+
+\fB*FoomaticRIPPostPipe: "| cat &gt; /dev/lp0"\fR
+
+or
+
+\fB*FoomaticRIPPostPipe: "| cat &gt; /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 < &lt;
+.B > &gt;
+.B " &quot;
+.B ' &apos;
+.B & &amp;
+.fi
+
+This way you can print directly to your printer, use
+
+\fB*FoomaticRIPPostPipe: "| cat &gt; /dev/lp0"\fR
+
+or
+
+\fB*FoomaticRIPPostPipe: "| cat &gt; /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",
+ &param->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(&copy->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, &copy[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