diff --git a/foomatic-colord.patch b/foomatic-colord.patch new file mode 100644 index 0000000..6841351 --- /dev/null +++ b/foomatic-colord.patch @@ -0,0 +1,616 @@ +diff --git a/Makefile.am b/Makefile.am +index 03e437e..1f12fbb 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -53,6 +53,15 @@ foomatic_rip_SOURCES = \ + fileconverter.c \ + fileconverter.h + ++if BUILD_DBUS ++foomatic_rip_SOURCES += \ ++ colord.c \ ++ colord.h ++ ++foomatic_rip_CFLAGS = $(DBUS_CFLAGS) -DHAVE_DBUS ++foomatic_rip_LDADD = $(DBUS_LIBS) ++endif ++ + AM_CPPFLAGS = -DCONFIG_PATH='"$(sysconfdir)/foomatic"' + + # Masks for trash files which have to be removed before packaging Foomatic +diff --git a/colord.c b/colord.c +new file mode 100644 +index 0000000..260b8c7 +--- /dev/null ++++ b/colord.c +@@ -0,0 +1,263 @@ ++/* colord.c ++ * ++ * Copyright (C) 2011 Richard Hughes ++ * ++ * 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. ++ */ ++ ++/* Common routines for accessing the colord CMS framework */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "colord.h" ++ ++#define QUAL_COLORSPACE 0 ++#define QUAL_MEDIA 1 ++#define QUAL_RESOLUTION 2 ++#define QUAL_SIZE 3 ++ ++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; ++} +diff --git a/colord.h b/colord.h +new file mode 100644 +index 0000000..2ea98bc +--- /dev/null ++++ b/colord.h +@@ -0,0 +1,24 @@ ++/* colord.h ++ * ++ * Copyright (C) 2011 Richard Hughes ++ * ++ * 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. ++ */ ++ ++char *colord_get_profile_for_device_id (const char *device_id, ++ const char **qualifier_tuple); +diff --git a/configure.ac b/configure.ac +index 96a9e21..d33726d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -16,6 +16,9 @@ AC_PROG_INSTALL + AC_PROG_LN_S + AC_PROG_MAKE_SET + ++# Allows per-target compilation flags ++AM_PROG_CC_C_O ++ + # Checks for libraries. + AC_CHECK_LIB(m, roundf) + +@@ -119,6 +122,14 @@ AC_ARG_WITH(file-converter,[ --with-file-converter=[a2ps|enscript|mpage] + AC_MSG_RESULT([file-converter: $FILECONVERTER]) + AC_SUBST(FILECONVERTER) + ++# Use DBUS ++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 ++ + if test "${NOCONVERTERCHECK}" = "" -a "${A2PS}" = "" -a "${ENSCRIPT}" = "" -a "${MPAGE}" = "" -a "${TEXTTOPS}" = "" ; then + AC_MSG_ERROR([cannot find a2ps, enscript, mpage, or CUPS' texttops. You need to have at least one installed]); + fi +diff --git a/foomaticrip.c b/foomaticrip.c +index 41a7577..6065768 100644 +--- a/foomaticrip.c ++++ b/foomaticrip.c +@@ -45,6 +45,9 @@ + #include + #include + ++#ifdef HAVE_DBUS ++ #include "colord.h" ++#endif + + /* Logging */ + FILE* logh = NULL; +@@ -1477,13 +1480,45 @@ int main(int argc, char** argv) + strlcat(pstoraster, " 0 '' '' 0 '%X'", 256); + break; + } ++ /* gstoraster is the new name for pstoraster */ ++ strlcat(tmp, "/gstoraster", 1024); ++ if (access(tmp, X_OK) == 0) { ++ havepstoraster = 1; ++ strlcpy(pstoraster, tmp, 256); ++ strlcat(pstoraster, " 0 '' '' 0 '%X'", 256); ++ break; ++ } + } + if (!havepstoraster) { +- strcpy(pstoraster, "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOMEDIAATTRS -sDEVICE=cups -sOutputFile=-%W -"); ++ 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(pstoraster, sizeof(pstoraster), "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOMEDIAATTRS -sDEVICE=cups %s -sOutputFile=-%W -", cmd); + } + + /* build Ghostscript/CUPS driver command line */ + snprintf(cmd, 1024, "%s | %s", pstoraster, cupsfilter); ++ _log("INFO: Using command line: %s\n", cmd); + + /* Set environment variables */ + setenv("PPD", job->ppdfile, 1); +diff --git a/options.c b/options.c +index 9a4b116..662cedd 100644 +--- a/options.c ++++ b/options.c +@@ -31,6 +31,11 @@ + #include + #include + ++/* 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]; +@@ -70,6 +75,8 @@ 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; +@@ -78,7 +85,42 @@ 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) + { +@@ -239,6 +281,8 @@ 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]); +@@ -247,6 +291,20 @@ void options_free() + 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; +@@ -1493,6 +1551,9 @@ int optionset_equal(int optset1, int optset2, int exceptPS) + 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]; +@@ -1501,6 +1562,7 @@ void read_ppd_file(const char *filename) + value_t *val; + option_t *opt, *current_opt = NULL; + param_t *param; ++ icc_mapping_entry_t *entry; + + fh = fopen(filename, "r"); + if (!fh) { +@@ -1511,6 +1573,7 @@ void read_ppd_file(const char *filename) + + dstrassure(value, 256); + ++ qualifier_data = list_create(); + while (!feof(fh)) { + fgets(line, 256, fh); + +@@ -1691,10 +1754,6 @@ void read_ppd_file(const char *filename) + else if (!prefixcmp(key, "Default")) { + /* Default