ghostscript/ghostscript-colord.patch

1268 lines
40 KiB
Diff

diff -urNp ghostscript-9.01.old/configure.ac ghostscript-9.01/configure.ac
--- ghostscript-9.01.old/configure.ac 2011-03-08 10:47:24.851721587 +0000
+++ ghostscript-9.01/configure.ac 2011-03-08 10:48:11.022721481 +0000
@@ -423,6 +423,37 @@ AC_SUBST(HAVE_FONTCONFIG)
AC_SUBST(FONTCONFIG_CFLAGS)
AC_SUBST(FONTCONFIG_LIBS)
+dnl DBus support
+HAVE_DBUS=""
+DBUS_CFLAGS=""
+DBUS_LIBS=""
+AC_ARG_ENABLE([dbus], AC_HELP_STRING([--disable-dbus],
+ [Do not use dbus to communicate with external services]))
+if test "$enable_dbus" != "no"; then
+ if test "x$PKGCONFIG" != x; then
+ AC_MSG_CHECKING(for dbus with pkg-config)
+ if $PKGCONFIG --exists dbus-1; then
+ AC_MSG_RESULT(yes)
+ DBUS_CFLAGS="$CFLAGS `$PKGCONFIG --cflags dbus-1`"
+ DBUS_LIBS="`$PKGCONFIG --libs dbus-1`"
+ HAVE_DBUS=-DHAVE_DBUS
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+ if test -z "$HAVE_DBUS"; then
+ AC_CHECK_LIB([dbus], [dbus_message_iter_get_basic], [
+ AC_CHECK_HEADER([dbus-1.0/dbus/dbus.h], [
+ DBUS_LIBS="-ldbus-1 -lpthread -lrt"
+ HAVE_DBUS="-DHAVE_DBUS"
+ ])
+ ])
+ fi
+fi
+AC_SUBST(HAVE_DBUS)
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
AC_CHECK_LIB(dl, dlopen)
AC_ARG_ENABLE([freetype], AC_HELP_STRING([--disable-freetype],
@@ -1439,6 +1470,6 @@ dnl ------------------------------------
AC_SUBST(OPT_CFLAGS)
AC_SUBST(DBG_CFLAGS)
AC_SUBST(GCFLAGS)
-AC_OUTPUT(Makefile cups/pstopxl cups/pstoraster)
+AC_OUTPUT(Makefile cups/pstopxl)
-chmod +x cups/pstopxl cups/pstoraster
+chmod +x cups/pstopxl
diff -urNp ghostscript-9.01.old/cups/colord.c ghostscript-9.01/cups/colord.c
--- ghostscript-9.01.old/cups/colord.c 1970-01-01 01:00:00.000000000 +0100
+++ ghostscript-9.01/cups/colord.c 2011-03-08 10:48:11.023721494 +0000
@@ -0,0 +1,367 @@
+/*
+Copyright (c) 2011, Tim Waugh
+Copyright (c) 2011, Richard Hughes
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+MIT Open Source License - http://www.opensource.org/
+
+*/
+
+/* $Id$ */
+
+/* Common routines for accessing the colord CMS framework */
+
+#include <cups/raster.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifdef HAVE_DBUS
+ #include <dbus/dbus.h>
+#endif
+
+#include "colord.h"
+
+#define QUAL_COLORSPACE 0
+#define QUAL_MEDIA 1
+#define QUAL_RESOLUTION 2
+#define QUAL_SIZE 3
+
+char **
+colord_get_qualifier_for_ppd (ppd_file_t *ppd)
+{
+ char q_keyword[PPD_MAX_NAME];
+ char **tuple = NULL;
+ const char *q1_choice;
+ const char *q2_choice;
+ const char *q3_choice;
+ ppd_attr_t *attr;
+ ppd_attr_t *q1_attr;
+ ppd_attr_t *q2_attr;
+ ppd_attr_t *q3_attr;
+
+ /* get colorspace */
+ if ((attr = ppdFindAttr (ppd, "cupsICCQualifier1", NULL)) != NULL &&
+ attr->value && attr->value[0])
+ {
+ snprintf (q_keyword, sizeof (q_keyword), "Default%s", attr->value);
+ q1_attr = ppdFindAttr (ppd, q_keyword, NULL);
+ }
+ else if ((q1_attr = ppdFindAttr (ppd, "DefaultColorModel", NULL)) == NULL)
+ q1_attr = ppdFindAttr (ppd, "DefaultColorSpace", NULL);
+
+ if (q1_attr && q1_attr->value && q1_attr->value[0])
+ q1_choice = q1_attr->value;
+ else
+ q1_choice = "";
+
+ /* get colorspace */
+ if ((attr = ppdFindAttr (ppd, "cupsICCQualifier2", NULL)) != NULL &&
+ attr->value && attr->value[0])
+ {
+ snprintf (q_keyword, sizeof (q_keyword), "Default%s", attr->value);
+ q1_attr = ppdFindAttr (ppd, q_keyword, NULL);
+ }
+ else if ((q1_attr = ppdFindAttr (ppd, "DefaultColorModel", NULL)) == NULL)
+ q1_attr = ppdFindAttr (ppd, "DefaultColorSpace", NULL);
+
+ if (q1_attr && q1_attr->value && q1_attr->value[0])
+ q1_choice = q1_attr->value;
+ else
+ q1_choice = "";
+
+ /* get media */
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
+ attr->value && attr->value[0])
+ {
+ snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
+ q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
+ }
+ else
+ q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
+
+ if (q2_attr && q2_attr->value && q2_attr->value[0])
+ q2_choice = q2_attr->value;
+ else
+ q2_choice = "";
+
+ /* get resolution */
+ if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
+ attr->value && attr->value[0])
+ {
+ snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
+ q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
+ }
+ else
+ q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
+
+ if (q3_attr && q3_attr->value && q3_attr->value[0])
+ q3_choice = q3_attr->value;
+ else
+ q3_choice = "";
+
+ /* return a NULL terminated array so we don't have to break it up later */
+ tuple = calloc(QUAL_SIZE + 1, sizeof(char*));
+ tuple[QUAL_COLORSPACE] = strdup(q1_choice);
+ tuple[QUAL_MEDIA] = strdup(q2_choice);
+ tuple[QUAL_RESOLUTION] = strdup(q3_choice);
+ return tuple;
+}
+
+#ifdef HAVE_DBUS
+
+static char *
+get_filename_for_profile_path (DBusConnection *con,
+ const char *object_path)
+{
+ char *filename = NULL;
+ const char *interface = "org.freedesktop.ColorManager.Profile";
+ const char *property = "Filename";
+ const char *tmp;
+ DBusError error;
+ DBusMessageIter args;
+ DBusMessage *message = NULL;
+ DBusMessage *reply = NULL;
+ DBusMessageIter sub;
+
+ message = dbus_message_new_method_call("org.freedesktop.ColorManager",
+ object_path,
+ "org.freedesktop.DBus.Properties",
+ "Get");
+
+ dbus_message_iter_init_append(message, &args);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &interface);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &property);
+
+ /* send syncronous */
+ dbus_error_init(&error);
+ fprintf(stderr, "DEBUG: Calling %s.Get(%s)\n", interface, property);
+ reply = dbus_connection_send_with_reply_and_block(con,
+ message,
+ -1,
+ &error);
+ if (reply == NULL) {
+ fprintf(stderr, "DEBUG: Failed to send: %s:%s\n",
+ error.name, error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+ /* get reply data */
+ dbus_message_iter_init(reply, &args);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) {
+ fprintf(stderr, "DEBUG: Incorrect reply type\n");
+ goto out;
+ }
+
+ dbus_message_iter_recurse(&args, &sub);
+ dbus_message_iter_get_basic(&sub, &tmp);
+ filename = strdup(tmp);
+out:
+ if (message != NULL)
+ dbus_message_unref(message);
+ if (reply != NULL)
+ dbus_message_unref(reply);
+ return filename;
+}
+
+static char *
+get_profile_for_device_path (DBusConnection *con,
+ const char *object_path,
+ const char **split)
+{
+ char **key = NULL;
+ char *profile = NULL;
+ char str[256];
+ const char *tmp;
+ DBusError error;
+ DBusMessageIter args;
+ DBusMessageIter entry;
+ DBusMessage *message = NULL;
+ DBusMessage *reply = NULL;
+ int i = 0;
+ const int max_keys = 7;
+
+ message = dbus_message_new_method_call("org.freedesktop.ColorManager",
+ object_path,
+ "org.freedesktop.ColorManager.Device",
+ "GetProfileForQualifiers");
+ dbus_message_iter_init_append(message, &args);
+
+ /* create the fallbacks */
+ key = calloc(max_keys + 1, sizeof(char*));
+
+ /* exact match */
+ i = 0;
+ snprintf(str, sizeof(str), "%s.%s.%s",
+ split[QUAL_COLORSPACE],
+ split[QUAL_MEDIA],
+ split[QUAL_RESOLUTION]);
+ key[i++] = strdup(str);
+ snprintf(str, sizeof(str), "%s.%s.*",
+ split[QUAL_COLORSPACE],
+ split[QUAL_MEDIA]);
+ key[i++] = strdup(str);
+ snprintf(str, sizeof(str), "%s.*.%s",
+ split[QUAL_COLORSPACE],
+ split[QUAL_RESOLUTION]);
+ key[i++] = strdup(str);
+ snprintf(str, sizeof(str), "%s.*.*",
+ split[QUAL_COLORSPACE]);
+ key[i++] = strdup(str);
+ key[i++] = strdup("*");
+ dbus_message_iter_open_container(&args,
+ DBUS_TYPE_ARRAY,
+ "s",
+ &entry);
+ for (i=0; key[i] != NULL; i++) {
+ dbus_message_iter_append_basic(&entry,
+ DBUS_TYPE_STRING,
+ &key[i]);
+ }
+ dbus_message_iter_close_container(&args, &entry);
+
+ /* send syncronous */
+ dbus_error_init(&error);
+ fprintf(stderr, "DEBUG: Calling GetProfileForQualifiers(%s...)\n", key[0]);
+ reply = dbus_connection_send_with_reply_and_block(con,
+ message,
+ -1,
+ &error);
+ if (reply == NULL) {
+ fprintf(stderr, "DEBUG: Failed to send: %s:%s\n",
+ error.name, error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+ /* get reply data */
+ dbus_message_iter_init(reply, &args);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) {
+ fprintf(stderr, "DEBUG: Incorrect reply type\n");
+ goto out;
+ }
+ dbus_message_iter_get_basic(&args, &tmp);
+ fprintf(stderr, "DEBUG: Found profile %s\n", tmp);
+
+ /* get filename */
+ profile = get_filename_for_profile_path(con, tmp);
+
+out:
+ if (message != NULL)
+ dbus_message_unref(message);
+ if (reply != NULL)
+ dbus_message_unref(reply);
+ if (key != NULL) {
+ for (i=0; i < max_keys; i++)
+ free(key[i]);
+ free(key);
+ }
+ return profile;
+}
+
+static char *
+get_profile_for_device_id (DBusConnection *con,
+ const char *device_id,
+ const char **qualifier_tuple)
+{
+ char *profile = NULL;
+ const char *device_path_tmp;
+ DBusError error;
+ DBusMessageIter args;
+ DBusMessage *message = NULL;
+ DBusMessage *reply = NULL;
+
+ message = dbus_message_new_method_call("org.freedesktop.ColorManager",
+ "/org/freedesktop/ColorManager",
+ "org.freedesktop.ColorManager",
+ "FindDeviceById");
+ dbus_message_iter_init_append(message, &args);
+ dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id);
+
+ /* send syncronous */
+ dbus_error_init(&error);
+ fprintf(stderr, "DEBUG: Calling FindDeviceById(%s)\n", device_id);
+ reply = dbus_connection_send_with_reply_and_block(con,
+ message,
+ -1,
+ &error);
+ if (reply == NULL) {
+ fprintf(stderr, "DEBUG: Failed to send: %s:%s\n",
+ error.name, error.message);
+ dbus_error_free(&error);
+ goto out;
+ }
+
+ /* get reply data */
+ dbus_message_iter_init(reply, &args);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) {
+ fprintf(stderr, "DEBUG: Incorrect reply type\n");
+ goto out;
+ }
+ dbus_message_iter_get_basic(&args, &device_path_tmp);
+ fprintf(stderr, "DEBUG: Found device %s\n", device_path_tmp);
+ profile = get_profile_for_device_path(con, device_path_tmp, qualifier_tuple);
+out:
+ if (message != NULL)
+ dbus_message_unref(message);
+ if (reply != NULL)
+ dbus_message_unref(reply);
+ return profile;
+}
+
+char *
+colord_get_profile_for_device_id (const char *device_id,
+ const char **qualifier_tuple)
+{
+ DBusConnection *con;
+ char *filename = NULL;
+
+ /* connect to system bus */
+ con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (con == NULL) {
+ fprintf(stderr, "ERROR: Failed to connect to system bus\n");
+ goto out;
+ }
+
+ /* get the best profile for the device */
+ filename = get_profile_for_device_id (con, device_id, qualifier_tuple);
+ if (filename == NULL) {
+ fprintf(stderr, "DEBUG: Failed to get profile filename!\n");
+ goto out;
+ }
+ fprintf(stderr, "DEBUG: Use profile filename: '%s'\n", filename);
+out:
+ if (con != NULL)
+ dbus_connection_unref(con);
+ return filename;
+}
+
+#else
+
+char *
+colord_get_profile_for_device_id (const char *device_id,
+ const char **qualifier_tuple)
+{
+ fprintf(stderr, "WARN: not compiled with DBus support\n");
+ return NULL;
+}
+
+#endif
diff -urNp ghostscript-9.01.old/cups/colord.h ghostscript-9.01/cups/colord.h
--- ghostscript-9.01.old/cups/colord.h 1970-01-01 01:00:00.000000000 +0100
+++ ghostscript-9.01/cups/colord.h 2011-03-08 10:48:11.024721504 +0000
@@ -0,0 +1,35 @@
+/*
+Copyright (c) 2011, Richard Hughes
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+MIT Open Source License - http://www.opensource.org/
+
+*/
+
+/* $Id$ */
+
+/* Common routines for accessing the colord CMS framework */
+
+#include <cups/raster.h>
+
+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);
diff -urNp ghostscript-9.01.old/cups/cups.mak ghostscript-9.01/cups/cups.mak
--- ghostscript-9.01.old/cups/cups.mak 2011-03-08 10:47:23.796721588 +0000
+++ ghostscript-9.01/cups/cups.mak 2011-03-08 10:50:01.360721549 +0000
@@ -35,30 +35,30 @@ cups_= $(GLOBJ)gdevcups.$(OBJ)
# CUPSDATA=`cups-config --datadir`
# CUPSPDFTORASTER= 1 if CUPS is new enough (cups-config --version)
-PDFTORASTER_XE=$(BINDIR)$(D)pdftoraster$(XE)
+GSTORASTER_XE=$(BINDIR)$(D)gstoraster$(XE)
-cups: pdftoraster
-pdftoraster: $(PDFTORASTER_XE)
-pdftoraster_=cups/pdftoraster.c
+cups: gstoraster
-$(PDFTORASTER_XE): $(pdftoraster_)
+gstoraster: $(GSTORASTER_XE)
+gstoraster_=cups/gstoraster.c cups/colord.c
+
+$(GSTORASTER_XE): $(gstoraster_)
if [ "$(CUPSPDFTORASTER)" = "1" ]; then \
- $(GLCC) $(LDFLAGS) -DBINDIR='"$(bindir)"' -DGS='"$(GS)"' -o $@ $(pdftoraster_) `cups-config --image --ldflags --libs`; \
+ $(GLCC) $(LDFLAGS) $(DBUS_CFLAGS) -DBINDIR='"$(bindir)"' -DCUPSDATA='"$(CUPSDATA)"' -DGS='"$(GS)"' -o $@ $(gstoraster_) `cups-config --image --ldflags --libs` $(DBUS_LIBS); \
fi
+
install: install-cups
install-cups: cups
-mkdir -p $(DESTDIR)$(CUPSSERVERBIN)/filter
- $(INSTALL_PROGRAM) cups/pstoraster $(DESTDIR)$(CUPSSERVERBIN)/filter
if [ "$(CUPSPDFTORASTER)" = "1" ]; then \
- $(INSTALL_PROGRAM) $(PDFTORASTER_XE) $(DESTDIR)$(CUPSSERVERBIN)/filter; \
+ $(INSTALL_PROGRAM) $(GSTORASTER_XE) $(DESTDIR)$(CUPSSERVERBIN)/filter; \
fi
$(INSTALL_PROGRAM) cups/pstopxl $(DESTDIR)$(CUPSSERVERBIN)/filter
-mkdir -p $(DESTDIR)$(CUPSDATA)/mime
- $(INSTALL_DATA) cups/pstoraster.convs $(DESTDIR)$(CUPSDATA)/mime
if [ "$(CUPSPDFTORASTER)" = "1" ]; then \
- $(INSTALL_DATA) cups/pdftoraster.convs $(DESTDIR)$(CUPSDATA)/mime; \
+ $(INSTALL_DATA) cups/gstoraster.convs $(DESTDIR)$(CUPSDATA)/mime; \
fi
-mkdir -p $(DESTDIR)$(CUPSDATA)/model
$(INSTALL_DATA) cups/pxlcolor.ppd $(DESTDIR)$(CUPSDATA)/model
diff -urNp ghostscript-9.01.old/cups/gstoraster.c ghostscript-9.01/cups/gstoraster.c
--- ghostscript-9.01.old/cups/gstoraster.c 1970-01-01 01:00:00.000000000 +0100
+++ ghostscript-9.01/cups/gstoraster.c 2011-03-08 10:48:11.026721536 +0000
@@ -0,0 +1,702 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: s; c-basic-offset: 8 -*-
+
+Copyright (c) 2008, Till Kamppeter
+Copyright (c) 2011, Tim Waugh
+Copyright (c) 2011, Richard Hughes
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+MIT Open Source License - http://www.opensource.org/
+
+*/
+
+/* $Id$ */
+
+/* PS/PDF to CUPS Raster filter based on Ghostscript */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <cups/raster.h>
+#include <sys/types.h>
+#include <sys/wait.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
+#ifndef CUPSDATA
+#define CUPSDATA "/usr/share/cups"
+#endif
+
+typedef enum {
+ GS_DOC_TYPE_PDF,
+ GS_DOC_TYPE_PS,
+ GS_DOC_TYPE_UNKNOWN
+} GsDocType;
+
+#ifdef CUPS_RASTER_SYNCv1
+typedef cups_page_header2_t gs_page_header;
+#else
+typedef cups_page_header_t gs_page_header;
+#endif /* CUPS_RASTER_SYNCv1 */
+
+static GsDocType
+parse_doc_type(FILE *fp)
+{
+ char buf[5];
+ GsDocType doc_type;
+ char *rc;
+
+ /* get the first few bytes of the file */
+ doc_type = GS_DOC_TYPE_UNKNOWN;
+ rewind(fp);
+ rc = fgets(buf,sizeof(buf),fp);
+ if (rc == NULL)
+ goto out;
+
+ /* is PDF */
+ if (strncmp(buf,"%PDF",4) == 0) {
+ doc_type = GS_DOC_TYPE_PDF;
+ goto out;
+ }
+
+ /* is PS */
+ if (strncmp(buf,"%!",2) == 0) {
+ doc_type = GS_DOC_TYPE_PS;
+ goto out;
+ }
+out:
+ return doc_type;
+}
+
+static void
+parse_pdf_header_options(FILE *fp, gs_page_header *h)
+{
+ char buf[4096];
+ int i;
+
+ rewind(fp);
+ /* skip until PDF start header */
+ while (fgets(buf,sizeof(buf),fp) != 0) {
+ if (strncmp(buf,"%PDF",4) == 0) {
+ break;
+ }
+ }
+ for (i = 0;i < PDF_MAX_CHECK_COMMENT_LINES;i++) {
+ if (fgets(buf,sizeof(buf),fp) == 0) break;
+ if (strncmp(buf,"%%PDFTOPDFNumCopies",19) == 0) {
+ char *p;
+
+ p = strchr(buf+19,':');
+ h->NumCopies = atoi(p+1);
+ } else if (strncmp(buf,"%%PDFTOPDFCollate",17) == 0) {
+ char *p;
+
+ p = strchr(buf+17,':');
+ while (*p == ' ' || *p == '\t') p++;
+ if (strncasecmp(p,"true",4) == 0) {
+ h->Collate = CUPS_TRUE;
+ } else {
+ h->Collate = CUPS_FALSE;
+ }
+ }
+ }
+}
+
+static void
+add_pdf_header_options(gs_page_header *h, cups_array_t *gs_args)
+{
+ int i;
+ char tmpstr[1024];
+
+ /* Simple boolean, enumerated choice, numerical, and string parameters */
+ if (h->MediaClass[0] |= '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-sMediaClass=%s", h->MediaClass);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->MediaColor[0] |= '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-sMediaColor=%s", h->MediaColor);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->MediaType[0] |= '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-sMediaType=%s", h->MediaType);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->OutputType[0] |= '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-sOutputType=%s", h->OutputType);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->AdvanceDistance) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dAdvanceDistance=%d",
+ (unsigned)(h->AdvanceDistance));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->AdvanceMedia) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dAdvanceMedia=%d",
+ (unsigned)(h->AdvanceMedia));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->Collate) {
+ cupsArrayAdd(gs_args, strdup("-dCollate"));
+ }
+ if (h->CutMedia) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dCutMedia=%d",
+ (unsigned)(h->CutMedia));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->Duplex) {
+ cupsArrayAdd(gs_args, strdup("-dDuplex"));
+ }
+ if ((h->HWResolution[0] != 100) || (h->HWResolution[1] != 100))
+ snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d",
+ (unsigned)(h->HWResolution[0]), (unsigned)(h->HWResolution[1]));
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "-r100x100");
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ if (h->InsertSheet) {
+ cupsArrayAdd(gs_args, strdup("-dInsertSheet"));
+ }
+ if (h->Jog) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dJog=%d",
+ (unsigned)(h->Jog));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->LeadingEdge) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dLeadingEdge=%d",
+ (unsigned)(h->LeadingEdge));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->ManualFeed) {
+ cupsArrayAdd(gs_args, strdup("-dManualFeed"));
+ }
+ if (h->MediaPosition) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dMediaPosition=%d",
+ (unsigned)(h->MediaPosition));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->MediaWeight) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dMediaWeight=%d",
+ (unsigned)(h->MediaWeight));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->MirrorPrint) {
+ cupsArrayAdd(gs_args, strdup("-dMirrorPrint"));
+ }
+ if (h->NegativePrint) {
+ cupsArrayAdd(gs_args, strdup("-dNegativePrint"));
+ }
+ if (h->NumCopies != 1) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dNumCopies=%d",
+ (unsigned)(h->NumCopies));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->Orientation) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dOrientation=%d",
+ (unsigned)(h->Orientation));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->OutputFaceUp) {
+ cupsArrayAdd(gs_args, strdup("-dOutputFaceUp"));
+ }
+ if (h->PageSize[0] != 612)
+ snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEWIDTHPOINTS=%d",
+ (unsigned)(h->PageSize[0]));
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEWIDTHPOINTS=612");
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ if (h->PageSize[1] != 792)
+ snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEHEIGHTPOINTS=%d",
+ (unsigned)(h->PageSize[1]));
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEHEIGHTPOINTS=792");
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ if (h->Separations) {
+ cupsArrayAdd(gs_args, strdup("-dSeparations"));
+ }
+ if (h->TraySwitch) {
+ cupsArrayAdd(gs_args, strdup("-dTraySwitch"));
+ }
+ if (h->Tumble) {
+ cupsArrayAdd(gs_args, strdup("-dTumble"));
+ }
+ if (h->cupsMediaType) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsMediaType=%d",
+ (unsigned)(h->cupsMediaType));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->cupsBitsPerColor != 1)
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsBitsPerColor=%d",
+ (unsigned)(h->cupsBitsPerColor));
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsBitsPerColor=1");
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ if (h->cupsColorOrder != CUPS_ORDER_CHUNKED)
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorOrder=%d",
+ (unsigned)(h->cupsColorOrder));
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorOrder=%d",
+ CUPS_ORDER_CHUNKED);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ if (h->cupsColorSpace != CUPS_CSPACE_K)
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorSpace=%d",
+ (unsigned)(h->cupsColorSpace));
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorSpace=%d",
+ CUPS_CSPACE_K);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ if (h->cupsCompression) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsCompression=%d",
+ (unsigned)(h->cupsCompression));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->cupsRowCount) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowCount=%d",
+ (unsigned)(h->cupsRowCount));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->cupsRowFeed) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowFeed=%d",
+ (unsigned)(h->cupsRowFeed));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->cupsRowStep) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowStep=%d",
+ (unsigned)(h->cupsRowStep));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+#ifdef CUPS_RASTER_SYNCv1
+ if (h->cupsBorderlessScalingFactor != 1.0f) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsBorderlessScalingFactor=%.4f",
+ h->cupsBorderlessScalingFactor);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ for (i=0; i <= 15; i ++)
+ if (h->cupsInteger[i]) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsInteger%d=%d",
+ i, (unsigned)(h->cupsInteger[i]));
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ for (i=0; i <= 15; i ++)
+ if (h->cupsReal[i]) {
+ snprintf(tmpstr, sizeof(tmpstr), "-dcupsReal%d=%.4f",
+ i, h->cupsReal[i]);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ for (i=0; i <= 15; i ++)
+ if (h->cupsString[i][0] != '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-scupsString%d=%s",
+ i, h->cupsString[i]);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->cupsMarkerType[0] != '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-scupsMarkerType=%s",
+ h->cupsMarkerType);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->cupsRenderingIntent[0] != '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-scupsRenderingIntent=%s",
+ h->cupsRenderingIntent);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+ if (h->cupsPageSizeName[0] != '\0') {
+ snprintf(tmpstr, sizeof(tmpstr), "-scupsPageSizeName=%s",
+ h->cupsPageSizeName);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+#endif /* CUPS_RASTER_SYNCv1 */
+}
+
+static int
+gs_spawn (const char *filename,
+ cups_array_t *gs_args,
+ char **envp,
+ FILE *fp)
+{
+ char *argument;
+ char buf[BUFSIZ];
+ char **gsargv;
+ const char* apos;
+ int fds[2];
+ int i;
+ int n;
+ int numargs;
+ int pid;
+ int status = 1;
+
+ /* Put Ghostscript command line argument into an array for the "exec()"
+ call */
+ numargs = cupsArrayCount(gs_args);
+ gsargv = calloc(numargs + 1, sizeof(char *));
+ for (argument = (char *)cupsArrayFirst(gs_args), i = 0; argument;
+ argument = (char *)cupsArrayNext(gs_args), i++) {
+ gsargv[i] = argument;
+ }
+ gsargv[i] = NULL;
+
+ /* Debug output: Full Ghostscript command line and environment variables */
+ fprintf(stderr, "DEBUG: Ghostscript command line:");
+ for (i = 0; gsargv[i]; i ++) {
+ if ((strchr(gsargv[i],' ')) || (strchr(gsargv[i],'\t')))
+ apos = "'";
+ else
+ apos = "";
+ fprintf(stderr, " %s%s%s", apos, gsargv[i], apos);
+ }
+ fprintf(stderr, "\n");
+
+ for (i = 0; envp[i]; i ++)
+ fprintf(stderr, "DEBUG: envp[%d]=\"%s\"\n", i, envp[i]);
+
+ /* Create a pipe for feeding the job into Ghostscript */
+ if (pipe(fds))
+ {
+ fds[0] = -1;
+ fds[1] = -1;
+ fprintf(stderr, "ERROR: Unable to establish pipe for Ghostscript call");
+ goto out;
+ }
+
+ /* Set the "close on exec" flag on each end of the pipe... */
+ if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
+ {
+ close(fds[0]);
+ close(fds[1]);
+ fds[0] = -1;
+ fds[1] = -1;
+ fprintf(stderr, "ERROR: Unable to set \"close on exec\" flag on read end of the pipe for Ghostscript call");
+ goto out;
+ }
+ if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
+ {
+ close(fds[0]);
+ close(fds[1]);
+ fprintf(stderr, "ERROR: Unable to set \"close on exec\" flag on write end of the pipe for Ghostscript call");
+ goto out;
+ }
+
+ if ((pid = fork()) == 0)
+ {
+ /* Couple pipe with STDIN of Ghostscript process */
+ if (fds[0] != 0) {
+ close(0);
+ if (fds[0] > 0)
+ dup(fds[0]);
+ else {
+ fprintf(stderr, "ERROR: Unable to couple pipe with STDIN of Ghostscript process");
+ goto out;
+ }
+ }
+
+ /* Execute Ghostscript command line ... */
+ execve(filename, gsargv, envp);
+ perror(filename);
+ goto out;
+ }
+
+ /* Feed job data into Ghostscript */
+ while ((n = fread(buf, 1, BUFSIZ, fp)) > 0) {
+ if (write(fds[1], buf, n) != n) {
+ fprintf(stderr, "ERROR: Can't feed job data into Ghostscript");
+ goto out;
+ }
+ }
+ close (fds[1]);
+
+ if (waitpid (pid, &status, 0) == -1) {
+ perror ("gs");
+ goto out;
+ }
+out:
+ free(gsargv);
+ return status;
+}
+
+static char *
+get_ppd_icc_fallback (ppd_file_t *ppd, char **qualifier)
+{
+ char full_path[1024];
+ char *icc_profile = NULL;
+ char qualifer_tmp[1024];
+ const char *profile_key;
+ ppd_attr_t *attr;
+
+ /* get profile attr, falling back to CUPS */
+ profile_key = "APTiogaProfile";
+ attr = ppdFindAttr(ppd, profile_key, NULL);
+ if (attr == NULL) {
+ profile_key = "cupsICCProfile";
+ attr = ppdFindAttr(ppd, profile_key, NULL);
+ }
+
+ /* create a string for a quick comparion */
+ snprintf(qualifer_tmp, sizeof(qualifer_tmp),
+ "%s.%s.%s",
+ qualifier[0],
+ qualifier[1],
+ qualifier[2]);
+
+ /* neither */
+ if (attr == NULL) {
+ fprintf(stderr, "INFO: no profiles specified in PPD\n");
+ goto out;
+ }
+
+ /* try to find a profile that matches the qualifier exactly */
+ for (;attr != NULL; attr = ppdFindNextAttr(ppd, profile_key, NULL)) {
+ fprintf(stderr, "INFO: found profile %s in PPD with qualifier '%s'\n",
+ attr->value, attr->spec);
+
+ /* invalid entry */
+ if (attr->spec == NULL || attr->value == NULL)
+ continue;
+
+ /* expand to a full path if not already specified */
+ if (attr->value[0] != '/')
+ snprintf(full_path, sizeof(full_path),
+ "%s/profiles/%s", CUPSDATA, attr->value);
+ else
+ strncpy(full_path, attr->value, sizeof(full_path));
+
+ /* check the file exists */
+ if (access(full_path, 0)) {
+ fprintf(stderr, "INFO: found profile %s in PPD that does not exist\n",
+ full_path);
+ continue;
+ }
+
+ /* matches the qualifier */
+ if (strcmp(qualifer_tmp, attr->spec) == 0) {
+ icc_profile = strdup(full_path);
+ goto out;
+ }
+ }
+
+ /* no match */
+ if (attr == NULL) {
+ fprintf(stderr, "INFO: no profiles in PPD for qualifier '%s'\n",
+ qualifer_tmp);
+ goto out;
+ }
+
+out:
+ return icc_profile;
+}
+
+int
+main (int argc, char **argv, char *envp[])
+{
+ char buf[BUFSIZ];
+ char *icc_profile = NULL;
+ char **qualifier = NULL;
+ char *tmp;
+ char tmpstr[1024];
+ const char *t = NULL;
+ cups_array_t *gs_args = NULL;
+ cups_option_t *options = NULL;
+ FILE *fp = NULL;
+ GsDocType doc_type;
+ gs_page_header h;
+ int fd;
+ int i;
+ int n;
+ int num_options;
+ int status = 1;
+ ppd_file_t *ppd = NULL;
+
+ if (argc < 6 || argc > 7) {
+ fprintf(stderr, "ERROR: %s job-id user title copies options [file]\n",
+ argv[0]);
+ goto out;
+ }
+
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ t = getenv("PPD");
+ if ((ppd = ppdOpenFile(t)) == NULL) {
+ fprintf(stderr, "ERROR: Failed to open PPD: %s", t);
+ goto out;
+ }
+
+ ppdMarkDefaults (ppd);
+ cupsMarkOptions (ppd, num_options, options);
+
+ if (argc == 6) {
+ /* stdin */
+
+ fd = cupsTempFd(buf,BUFSIZ);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: Can't create temporary file");
+ goto out;
+ }
+ /* remove name */
+ unlink(buf);
+
+ /* copy stdin to the tmp file */
+ while ((n = read(0,buf,BUFSIZ)) > 0) {
+ if (write(fd,buf,n) != n) {
+ fprintf(stderr, "ERROR: Can't copy stdin to temporary file");
+ close(fd);
+ goto out;
+ }
+ }
+ if (lseek(fd,0,SEEK_SET) < 0) {
+ fprintf(stderr, "ERROR: Can't rewind temporary file");
+ close(fd);
+ goto out;
+ }
+
+ if ((fp = fdopen(fd,"rb")) == 0) {
+ fprintf(stderr, "ERROR: Can't fdopen temporary file");
+ close(fd);
+ goto out;
+ }
+ } else {
+ /* argc == 7 filename is specified */
+
+ if ((fp = fopen(argv[6],"rb")) == 0) {
+ fprintf(stderr, "ERROR: Can't open input file %s",argv[6]);
+ goto out;
+ }
+ }
+
+ /* find out file type */
+ doc_type = parse_doc_type(fp);
+ if (doc_type == GS_DOC_TYPE_UNKNOWN) {
+ fprintf(stderr, "ERROR: Can't detect file type");
+ goto out;
+ }
+
+ qualifier = colord_get_qualifier_for_ppd (ppd);
+ if (qualifier != NULL) {
+
+ fprintf(stderr, "DEBUG: PPD uses qualifier '%s.%s.%s'\n",
+ qualifier[0], qualifier[1], qualifier[2]);
+ icc_profile = colord_get_profile_for_device_id (getenv("PRINTER"),
+ (const char**) qualifier);
+
+ /* fall back to the PPD */
+ if (icc_profile == NULL)
+ icc_profile = get_ppd_icc_fallback (ppd, qualifier);
+
+ if(icc_profile != NULL)
+ fprintf(stderr, "DEBUG: Using ICC Profile '%s'\n", icc_profile);
+ }
+
+ /* Ghostscript parameters */
+ gs_args = cupsArrayNew(NULL, NULL);
+ if (!gs_args)
+ {
+ fprintf(stderr, "ERROR: Unable to allocate memory for Ghostscript arguments array\n");
+ exit(1);
+ }
+
+ /* Part of Ghostscript command line which is not dependent on the job and/or
+ the driver */
+ snprintf(tmpstr, sizeof(tmpstr), "%s/%s", BINDIR, GS);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ cupsArrayAdd(gs_args, strdup("-dQUIET"));
+ /*cupsArrayAdd(gs_args, strdup("-dDEBUG"));*/
+ cupsArrayAdd(gs_args, strdup("-dPARANOIDSAFER"));
+ cupsArrayAdd(gs_args, strdup("-dNOPAUSE"));
+ cupsArrayAdd(gs_args, strdup("-dBATCH"));
+ if (doc_type == GS_DOC_TYPE_PS)
+ cupsArrayAdd(gs_args, strdup("-dNOMEDIAATTRS"));
+ cupsArrayAdd(gs_args, strdup("-sDEVICE=cups"));
+ cupsArrayAdd(gs_args, strdup("-sstdout=%stderr"));
+ cupsArrayAdd(gs_args, strdup("-sOutputFile=%stdout"));
+
+ /* setPDF specific options */
+ if (doc_type == GS_DOC_TYPE_PDF) {
+ cupsRasterInterpretPPD(&h,ppd,num_options,options,0);
+ parse_pdf_header_options(fp, &h);
+
+ /* fixed other values that pdftopdf handles */
+ h.MirrorPrint = CUPS_FALSE;
+ h.Orientation = CUPS_ORIENT_0;
+
+ /* get all the data from the header and pass it to ghostscript */
+ add_pdf_header_options (&h, gs_args);
+ }
+
+ /* CUPS font path */
+ if ((t = getenv("CUPS_FONTPATH")) == NULL)
+ t = CUPS_FONTPATH;
+ snprintf(tmpstr, sizeof(tmpstr), "-I%s", t);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+
+ /* set the device output ICC profile */
+ if(icc_profile != NULL) {
+ snprintf(tmpstr, sizeof(tmpstr), "-sOutputICCProfile=%s", icc_profile);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+
+ /* Switch to taking PostScript commands on the Ghostscript command line */
+ cupsArrayAdd(gs_args, strdup("-c"));
+
+ if ((t = cupsGetOption("profile", num_options, options)) != NULL) {
+ snprintf(tmpstr, sizeof(tmpstr), "<</cupsProfile(%s)>>setpagedevice", t);
+ cupsArrayAdd(gs_args, strdup(tmpstr));
+ }
+
+ /* Mark the end of PostScript commands supplied on the Ghostscript command
+ line (with the "-c" option), so that we can supply the input file name */
+ cupsArrayAdd(gs_args, strdup("-f"));
+
+ /* Let Ghostscript read from STDIN */
+ cupsArrayAdd(gs_args, strdup("-_"));
+
+ /* Execute Ghostscript command line ... */
+ snprintf(tmpstr, sizeof(tmpstr), "%s/%s", BINDIR, GS);
+
+ /* call Ghostscript */
+ rewind(fp);
+ status = gs_spawn (tmpstr, gs_args, envp, fp);
+out:
+ if (fp)
+ fclose(fp);
+ if (qualifier != NULL) {
+ for (i=0; qualifier[i] != NULL; i++)
+ free(qualifier[i]);
+ free(qualifier);
+ }
+ if (gs_args) {
+ while ((tmp = cupsArrayFirst(gs_args)) != NULL) {
+ cupsArrayRemove(gs_args,tmp);
+ free(tmp);
+ }
+ cupsArrayDelete(gs_args);
+ }
+ free(icc_profile);
+ if (ppd)
+ ppdClose(ppd);
+ return status;
+}
diff -urNp ghostscript-9.01.old/cups/gstoraster.convs ghostscript-9.01/cups/gstoraster.convs
--- ghostscript-9.01.old/cups/gstoraster.convs 1970-01-01 01:00:00.000000000 +0100
+++ ghostscript-9.01/cups/gstoraster.convs 2011-03-08 10:48:11.026721536 +0000
@@ -0,0 +1,30 @@
+# Copyright (c) 2008, Till Kamppeter
+# Copyright (c) 2011, Richard Hughes
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# MIT Open Source License - http://www.opensource.org/
+#
+# $Id: pdftoraster.convs 8803 2008-06-24 14:16:29Z till $
+#
+# CUPS file conversion rules for gstoraster filter
+
+application/vnd.cups-pdf application/vnd.cups-raster 66 gstoraster
+application/vnd.cups-postscript application/vnd.cups-raster 100 gstoraster
diff -urNp ghostscript-9.01.old/Makefile.in ghostscript-9.01/Makefile.in
--- ghostscript-9.01.old/Makefile.in 2011-03-08 10:47:24.951721587 +0000
+++ ghostscript-9.01/Makefile.in 2011-03-08 10:48:11.020721446 +0000
@@ -140,7 +140,7 @@ GENOPT=
# -DHAVE_SETLOCALE
# call setlocale(LC_CTYPE) when running as a standalone app
-CAPOPT= @HAVE_MKSTEMP@ @HAVE_HYPOT@ @HAVE_FILE64@ @HAVE_MKSTEMP64@ @HAVE_FONTCONFIG@ @HAVE_LIBIDN@ @HAVE_SETLOCALE@
+CAPOPT= @HAVE_MKSTEMP@ @HAVE_HYPOT@ @HAVE_FILE64@ @HAVE_MKSTEMP64@ @HAVE_FONTCONFIG@ @HAVE_LIBIDN@ @HAVE_SETLOCALE@ @HAVE_DBUS@
# Define the name of the executable file.
@@ -339,6 +339,10 @@ XCFLAGS=@DYNAMIC_FLAGS@
FONTCONFIG_CFLAGS=@FONTCONFIG_CFLAGS@
FONTCONFIG_LIBS=@FONTCONFIG_LIBS@
+# DBus flags, used by cups.mak
+DBUS_CFLAGS=@DBUS_CFLAGS@
+DBUS_LIBS=@DBUS_LIBS@
+
# defines from autoconf; note that we don't use these at present.
ACDEFS=@DEFS@