From 697b41dc45dcfb9eb366f918e88227525ad76766 Mon Sep 17 00:00:00 2001 From: Alx Sa Date: Fri, 30 Sep 2022 18:27:17 +0000 Subject: [PATCH] plug-ins: Export PSD with paths Ports PSD path export from file-tiff-save.c so that paths are carried over in PSD project files as well. --- plug-ins/file-psd/psd-save.c | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/plug-ins/file-psd/psd-save.c b/plug-ins/file-psd/psd-save.c index 2b942e0b4d..0392b233f3 100644 --- a/plug-ins/file-psd/psd-save.c +++ b/plug-ins/file-psd/psd-save.c @@ -145,6 +145,9 @@ static void save_resources (GOutputStream *output, gboolean export_cmyk, gboolean export_duotone); +static void save_paths (GOutputStream *output, + GimpImage *image); + static void save_layer_and_mask (GOutputStream *output, GimpImage *image, gboolean export_cmyk); @@ -153,6 +156,9 @@ static void save_data (GOutputStream *output, GimpImage *image, gboolean export_cmyk); +static void double_to_psd_fixed (gdouble value, + gchar *target); + static void xfwrite (GOutputStream *output, gconstpointer buf, gsize len, @@ -844,6 +850,9 @@ save_resources (GOutputStream *output, (int) sizeof (gint16)); } + /* --------------- Write paths ------------------- */ + save_paths (output, image); + /* --------------- Write resolution data ------------------- */ { gdouble xres = 0, yres = 0; @@ -1066,6 +1075,169 @@ get_compress_channel_data (guchar *channel_data, return len; } +/* Ported /from plug-ins/file-tiff/file-tiff-save.c */ +static void +double_to_psd_fixed (gdouble value, + gchar *target) +{ + gdouble in, frac; + gint i, f; + + frac = modf (value, &in); + if (frac < 0) + { + in -= 1; + frac += 1; + } + + i = (gint) CLAMP (in, -16, 15); + f = CLAMP ((gint) (frac * 0xFFFFFF), 0, 0xFFFFFF); + + target[0] = i & 0xFF; + target[1] = (f >> 16) & 0xFF; + target[2] = (f >> 8) & 0xFF; + target[3] = f & 0xFF; +} + +/* Ported from /plug-ins/file-tiff/file-tiff-save.c */ +static void +save_paths (GOutputStream *output, + GimpImage *image) +{ + gshort id = 0x07D0; /* Photoshop paths have IDs >= 2000 */ + gdouble width = gimp_image_get_width (image); + gdouble height = gimp_image_get_height (image); + GList *vectors; + GList *iter; + gint v; + gint num_strokes; + gint *strokes; + gint s; + + vectors = gimp_image_list_vectors (image); + + if (! vectors) + return; + + /* Only up to 997 paths supported */ + for (iter = vectors, v = 0; + iter && v <= 997; + iter = g_list_next (iter), v++) + { + GString *data; + gchar *name, *nameend; + gsize len; + gint lenpos; + gchar pointrecord[26] = { 0, }; + gchar *tmpname; + GError *err = NULL; + + data = g_string_new ("8BIM"); + g_string_append_c (data, id / 256); + g_string_append_c (data, id % 256); + + /* + * - use iso8859-1 if possible + * - otherwise use UTF-8, prepended with \xef\xbb\xbf (Byte-Order-Mark) + */ + name = gimp_item_get_name (iter->data); + tmpname = g_convert (name, -1, "iso8859-1", "utf-8", NULL, &len, &err); + + if (tmpname && err == NULL) + { + g_string_append_c (data, MIN (len, 255)); + g_string_append_len (data, tmpname, MIN (len, 255)); + g_free (tmpname); + } + else + { + /* conversion failed, we fall back to UTF-8 */ + len = g_utf8_strlen (name, 255 - 3); /* need three marker-bytes */ + + nameend = g_utf8_offset_to_pointer (name, len); + len = nameend - name; /* in bytes */ + g_assert (len + 3 <= 255); + + g_string_append_c (data, len + 3); + g_string_append_len (data, "\xEF\xBB\xBF", 3); /* Unicode 0xfeff */ + g_string_append_len (data, name, len); + + if (tmpname) + g_free (tmpname); + } + + if (data->len % 2) /* padding to even size */ + g_string_append_c (data, 0); + g_free (name); + + lenpos = data->len; + g_string_append_len (data, "\0\0\0\0", 4); /* will be filled in later */ + len = data->len; /* to calculate the data size later */ + + pointrecord[1] = 6; /* fill rule record */ + g_string_append_len (data, pointrecord, 26); + + strokes = gimp_vectors_get_strokes (iter->data, &num_strokes); + + for (s = 0; s < num_strokes; s++) + { + GimpVectorsStrokeType type; + gdouble *points; + gint num_points; + gboolean closed; + gint p = 0; + + type = gimp_vectors_stroke_get_points (iter->data, strokes[s], + &num_points, &points, &closed); + + if (type != GIMP_VECTORS_STROKE_TYPE_BEZIER || + num_points > 65535 || + num_points % 6) + { + g_printerr ("psd-save: unsupported stroke type: " + "%d (%d points)\n", type, num_points); + continue; + } + + memset (pointrecord, 0, 26); + pointrecord[1] = closed ? 0 : 3; + pointrecord[2] = (num_points / 6) / 256; + pointrecord[3] = (num_points / 6) % 256; + g_string_append_len (data, pointrecord, 26); + + for (p = 0; p < num_points; p += 6) + { + pointrecord[1] = closed ? 2 : 5; + + double_to_psd_fixed (points[p+1] / height, pointrecord + 2); + double_to_psd_fixed (points[p+0] / width, pointrecord + 6); + double_to_psd_fixed (points[p+3] / height, pointrecord + 10); + double_to_psd_fixed (points[p+2] / width, pointrecord + 14); + double_to_psd_fixed (points[p+5] / height, pointrecord + 18); + double_to_psd_fixed (points[p+4] / width, pointrecord + 22); + + g_string_append_len (data, pointrecord, 26); + } + } + + g_free (strokes); + + /* fix up the length */ + + len = data->len - len; + data->str[lenpos + 0] = (len & 0xFF000000) >> 24; + data->str[lenpos + 1] = (len & 0x00FF0000) >> 16; + data->str[lenpos + 2] = (len & 0x0000FF00) >> 8; + data->str[lenpos + 3] = (len & 0x000000FF) >> 0; + + xfwrite (output, data->str, data->len, "path resources data"); + g_string_free (data, TRUE); + id += 0x01; + } + + g_list_free (vectors); +} + static void save_layer_and_mask (GOutputStream *output, GimpImage *image,