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