diff --git a/app/core/gimppalette-import.c b/app/core/gimppalette-import.c index 93ab237a5d..5dd8f7d88c 100644 --- a/app/core/gimppalette-import.c +++ b/app/core/gimppalette-import.c @@ -534,6 +534,10 @@ gimp_palette_import_from_file (GimpContext *context, palette_list = gimp_palette_load_css (context, file, input, error); break; + case GIMP_PALETTE_FILE_FORMAT_SBZ: + palette_list = gimp_palette_load_sbz (context, file, input, error); + break; + default: g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, diff --git a/app/core/gimppalette-load.c b/app/core/gimppalette-load.c index 7c770a8551..3f26004d95 100644 --- a/app/core/gimppalette-load.c +++ b/app/core/gimppalette-load.c @@ -19,21 +19,63 @@ #include +#include +#include #include -#include #include +#include #include "libgimpbase/gimpbase.h" #include "libgimpcolor/gimpcolor.h" #include "core-types.h" +#include "config/gimpxmlparser.h" + #include "gimp-utils.h" #include "gimppalette.h" #include "gimppalette-load.h" #include "gimp-intl.h" +typedef struct +{ + GimpColorProfile *profile; + gchar *id; +} SwatchBookerColorProfile; + +typedef struct +{ + GimpPalette *palette; + gint position; + gchar *palette_name; + gchar *color_model; + gchar *color_space; + GList *embedded_profiles; + gboolean in_color_tag; + gboolean in_book_tag; + gboolean copy_name; + gboolean copy_values; + gint sorted_position; +} SwatchBookerData; + +/* SwatchBooker XML parser functions */ +static void swatchbooker_load_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void swatchbooker_load_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); +static void swatchbooker_load_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); + static gchar * gimp_palette_load_ase_block_name (GInputStream *input, goffset file_size, @@ -924,6 +966,358 @@ gimp_palette_load_css (GimpContext *context, return g_list_prepend (NULL, palette); } +GList * +gimp_palette_load_sbz (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + SwatchBookerData sbz_data; + gchar *palette_name; + gchar *xml_data = NULL; + struct archive *a; + struct archive_entry *entry; + size_t entry_size; + int r; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + palette_name = g_path_get_basename (gimp_file_get_utf8_name (file)); + sbz_data.palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + g_free (palette_name); + + sbz_data.position = 0; + sbz_data.sorted_position = 0; + sbz_data.in_color_tag = FALSE; + sbz_data.in_book_tag = FALSE; + sbz_data.copy_name = FALSE; + sbz_data.copy_values = FALSE; + sbz_data.embedded_profiles = NULL; + + if ((a = archive_read_new ())) + { + const gchar *name = gimp_file_get_utf8_name (file); + + archive_read_support_format_all (a); + r = archive_read_open_filename (a, name, 10240); + if (r != ARCHIVE_OK) + { + archive_read_free (a); + + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unable to read SBZ file")); + return NULL; + } + + while (archive_read_next_header (a, &entry) == ARCHIVE_OK) + { + const gchar *lower = g_ascii_strdown (archive_entry_pathname (entry), -1); + + if (g_str_has_suffix (lower, ".xml")) + { + entry_size = archive_entry_size (entry); + xml_data = (gchar *) g_malloc (entry_size); + + r = archive_read_data (a, xml_data, entry_size); + } + else if (g_str_has_suffix (lower, ".icc") || g_str_has_suffix (lower, ".icm")) + { + GimpColorProfile *profile = NULL; + size_t icc_size = archive_entry_size (entry); + uint8_t *icc_data = g_malloc (icc_size); + + r = archive_read_data (a, icc_data, icc_size); + + if (icc_data) + profile = gimp_color_profile_new_from_icc_profile (icc_data, icc_size, + NULL); + + if (profile) + { + SwatchBookerColorProfile sbz_profile; + + sbz_profile.profile = profile; + sbz_profile.id = g_strdup (archive_entry_pathname (entry)); + + sbz_data.embedded_profiles = + g_list_append (sbz_data.embedded_profiles, &sbz_profile); + } + } + } + + if (xml_data) + { + GimpXmlParser *xml_parser; + GMarkupParser markup_parser; + + markup_parser.start_element = swatchbooker_load_start_element; + markup_parser.end_element = swatchbooker_load_end_element; + markup_parser.text = swatchbooker_load_text; + markup_parser.passthrough = NULL; + markup_parser.error = NULL; + + xml_parser = gimp_xml_parser_new (&markup_parser, &sbz_data); + + gimp_xml_parser_parse_buffer (xml_parser, xml_data, entry_size, NULL); + gimp_xml_parser_free (xml_parser); + + g_free (xml_data); + } + + r = archive_read_free (a); + } + else + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unable to open SBZ file")); + return NULL; + } + + return g_list_prepend (NULL, sbz_data.palette); +} + +static void +swatchbooker_load_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + SwatchBookerData *sbz_data = user_data; + + sbz_data->copy_values = FALSE; + sbz_data->color_model = NULL; + sbz_data->color_space = NULL; + + if (! strcmp (g_ascii_strdown (element_name, -1), "color")) + { + sbz_data->in_color_tag = TRUE; + } + else if (! strcmp (g_ascii_strdown (element_name, -1), "dc:identifier")) + { + if (sbz_data->in_color_tag) + sbz_data->copy_name = TRUE; + } + else if (! strcmp (g_ascii_strdown (element_name, -1), "values")) + { + while (*attribute_names) + { + if (! strcmp (g_ascii_strdown (*attribute_names, -1), "model")) + { + sbz_data->color_model = + g_strdup (g_ascii_strdown (*attribute_values, -1)); + } + else if (! strcmp (g_ascii_strdown (*attribute_names, -1), "space")) + { + sbz_data->color_space = g_strdup (*attribute_values); + } + + attribute_names++; + attribute_values++; + } + + sbz_data->copy_values = TRUE; + } + else if (! strcmp (g_ascii_strdown (element_name, -1), "book")) + { + sbz_data->in_book_tag = TRUE; + + while (*attribute_names) + { + if (! strcmp (g_ascii_strdown (*attribute_names, -1), "columns")) + { + gint columns = atoi (*attribute_values); + + if (columns > 0) + gimp_palette_set_columns (sbz_data->palette, columns); + + break; + } + + attribute_names++; + attribute_values++; + } + } + else if (! strcmp (g_ascii_strdown (element_name, -1), "swatch") && + sbz_data->in_book_tag) + { + while (*attribute_names) + { + if (! strcmp (g_ascii_strdown (*attribute_names, -1), "material")) + { + GList *cols; + gint original_id = 0; + + for (cols = gimp_palette_get_colors (sbz_data->palette); + cols; + cols = g_list_next (cols)) + { + GimpPaletteEntry *entry = cols->data; + + if (! strcmp (*attribute_values, entry->name)) + { + gimp_palette_move_entry (sbz_data->palette, entry, + sbz_data->sorted_position); + + sbz_data->sorted_position++; + break; + } + original_id++; + } + break; + } + + attribute_names++; + attribute_values++; + } + } +} + +static void +swatchbooker_load_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + SwatchBookerData *sbz_data = user_data; + + if (! strcmp (g_ascii_strdown (element_name, -1), "color")) + sbz_data->in_color_tag = FALSE; + else if (! strcmp (g_ascii_strdown (element_name, -1), "book")) + sbz_data->in_book_tag = FALSE; +} + +static void +swatchbooker_load_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + SwatchBookerData *sbz_data = user_data; + gchar **values; + gint i; + gint total = 0; + + /* Save palette name */ + if (sbz_data->copy_name) + { + if (! sbz_data->palette_name) + sbz_data->palette_name = g_strdup (text); + + sbz_data->copy_name = FALSE; + } + + /* Load palette entry */ + if (sbz_data->copy_values) + { + values = g_strsplit (text, " ", 0); + + for (i = 0; values[i]; i++) + total++; + + if (total > 0) + { + gfloat true_values[total]; + GimpRGB color; + gboolean load_pal = FALSE; + const Babl *src_format = NULL; + const Babl *dst_format = babl_format ("R'G'B' float"); + const Babl *space = NULL; + + /* Load profile if applicable */ + if (sbz_data->embedded_profiles && + sbz_data->color_space) + { + GList *profile_list; + + for (profile_list = g_list_copy (sbz_data->embedded_profiles); + profile_list; + profile_list = g_list_next (profile_list)) + { + SwatchBookerColorProfile *icc = profile_list->data; + + if (! strcmp (sbz_data->color_space, icc->id)) + { + space = gimp_color_profile_get_space (icc->profile, + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + NULL); + break; + } + } + } + + for (i = 0; values[i]; i++) + true_values[i] = atof (values[i]); + + /* No need for babl conversion for sRGB colors */ + if (! strcmp (sbz_data->color_model, "srgb")) + { + gimp_rgb_set (&color, true_values[0], true_values[1], + true_values[2]); + load_pal = TRUE; + } + else if (! strcmp (sbz_data->color_model, "rgb")) + { + src_format = babl_format_with_space ("R'G'B' float", space); + } + else if (! strcmp (sbz_data->color_model, "gray")) + { + src_format = babl_format_with_space ("Y' float", space); + } + else if (! strcmp (sbz_data->color_model, "cmyk")) + { + src_format = babl_format_with_space ("CMYK float", space); + } + else if (! strcmp (sbz_data->color_model, "hsl")) + { + src_format = babl_format_with_space ("HSL float", space); + } + else if (! strcmp (sbz_data->color_model, "hsv")) + { + src_format = babl_format_with_space ("HSV float", space); + } + else if (! strcmp (sbz_data->color_model, "lab")) + { + src_format = babl_format_with_space ("CIE Lab float", space); + } + else if (! strcmp (sbz_data->color_model, "xyz")) + { + src_format = babl_format_with_space ("CIE XYZ float", space); + } + + if (src_format != NULL) + { + gfloat rgb[3]; + + babl_process (babl_fish (src_format, dst_format), + true_values, rgb, 1); + gimp_rgb_set (&color, rgb[0], rgb[1], rgb[2]); + load_pal = TRUE; + } + + if (load_pal) + { + gimp_palette_add_entry (sbz_data->palette, sbz_data->position, + NULL, &color); + if (sbz_data->palette_name) + gimp_palette_set_entry_name (sbz_data->palette, + sbz_data->position, + sbz_data->palette_name); + sbz_data->position++; + } + } + + sbz_data->palette_name = NULL; + sbz_data->copy_values = FALSE; + if (sbz_data->color_model) + g_free (sbz_data->color_model); + } +} + GimpPaletteFileFormat gimp_palette_load_detect_format (GFile *file, GInputStream *input) @@ -967,6 +1361,10 @@ gimp_palette_load_detect_format (GFile *file, { format = GIMP_PALETTE_FILE_FORMAT_CSS; } + else if (g_str_has_suffix (lower, ".sbz")) + { + format = GIMP_PALETTE_FILE_FORMAT_SBZ; + } g_free (lower); } diff --git a/app/core/gimppalette-load.h b/app/core/gimppalette-load.h index 4018223793..c0aef9ea88 100644 --- a/app/core/gimppalette-load.h +++ b/app/core/gimppalette-load.h @@ -31,7 +31,8 @@ typedef enum GIMP_PALETTE_FILE_FORMAT_PSP_PAL, /* JASC's Paint Shop Pro color palette */ GIMP_PALETTE_FILE_FORMAT_ACO, /* Photoshop ACO color file */ GIMP_PALETTE_FILE_FORMAT_ASE, /* Photoshop ASE color palette */ - GIMP_PALETTE_FILE_FORMAT_CSS /* Cascaded Stylesheet file (CSS) */ + GIMP_PALETTE_FILE_FORMAT_CSS, /* Cascaded Stylesheet file (CSS) */ + GIMP_PALETTE_FILE_FORMAT_SBZ /* Swatchbooker SBZ file */ } GimpPaletteFileFormat; @@ -63,6 +64,10 @@ GList * gimp_palette_load_css (GimpContext *context, GFile *file, GInputStream *input, GError **error); +GList * gimp_palette_load_sbz (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); GimpPaletteFileFormat gimp_palette_load_detect_format (GFile *file, GInputStream *input);