static gboolean lcms_image_set_profile (gint32 image, cmsHPROFILE profile, const gchar *filename, gboolean undo_group) { g_return_val_if_fail (image != -1, FALSE); if (filename) { GimpParasite *parasite; GMappedFile *file; GError *error = NULL; file = g_mapped_file_new (filename, FALSE, &error); if (! file) { g_message ("%s", error->message); g_error_free (error); return FALSE; } /* check that this file is actually an ICC profile */ if (! profile) { profile = cmsOpenProfileFromMem (g_mapped_file_get_contents (file), g_mapped_file_get_length (file)); if (profile) { cmsCloseProfile (profile); } else { g_message (_("'%s' does not appear to be an ICC color profile"), gimp_filename_to_utf8 (filename)); return FALSE; } } if (undo_group) gimp_image_undo_group_start (image); parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, g_mapped_file_get_length (file), g_mapped_file_get_contents (file)); g_mapped_file_unref (file); gimp_image_attach_parasite (image, parasite); gimp_parasite_free (parasite); } else { if (undo_group) gimp_image_undo_group_start (image); gimp_image_detach_parasite (image, "icc-profile"); } gimp_image_detach_parasite (image, "icc-profile-name"); if (undo_group) gimp_image_undo_group_end (image); return TRUE; }
/** * gimp_image_parasite_attach: * @image_ID: The image. * @parasite: The parasite to attach to an image. * * Deprecated: Use gimp_image_attach_parasite() instead. * * Returns: TRUE on success. **/ gboolean gimp_image_parasite_attach (gint32 image_ID, const GimpParasite *parasite) { return gimp_image_attach_parasite (image_ID, parasite); }
static gboolean lcms_image_set_profile (gint32 image, cmsHPROFILE profile, GFile *file) { g_return_val_if_fail (image != -1, FALSE); if (file) { cmsHPROFILE file_profile; GimpParasite *parasite; guint8 *profile_data; gsize profile_length; GError *error = NULL; /* check that this file is actually an ICC profile */ file_profile = gimp_lcms_profile_open_from_file (file, &error); if (! file_profile) { g_message ("%s", error->message); g_clear_error (&error); return FALSE; } profile_data = gimp_lcms_profile_save_to_data (file_profile, &profile_length, &error); cmsCloseProfile (file_profile); if (! profile_data) { g_message ("%s", error->message); g_clear_error (&error); return FALSE; } gimp_image_undo_group_start (image); parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, profile_length, profile_data); g_free (profile_data); gimp_image_attach_parasite (image, parasite); gimp_parasite_free (parasite); } else { gimp_image_undo_group_start (image); gimp_image_detach_parasite (image, "icc-profile"); } gimp_image_detach_parasite (image, "icc-profile-name"); gimp_image_undo_group_end (image); return TRUE; }
gint32 load_image (const gchar *filename, GimpRunMode runmode, gboolean preview, gboolean *resolution_loaded, GError **error) { gint32 volatile image_ID; gint32 layer_ID; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; jpeg_saved_marker_ptr marker; FILE *infile; guchar *buf; guchar **rowbuf; GimpImageBaseType image_type; GimpImageType layer_type; GeglBuffer *buffer = NULL; const Babl *format; gint tile_height; gint scanlines; gint i, start, end; cmsHTRANSFORM cmyk_transform = NULL; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; if (!preview) { jerr.pub.output_message = my_output_message; gimp_progress_init_printf (_("Opening '%s'"), gimp_filename_to_utf8 (filename)); } if ((infile = g_fopen (filename, "rb")) == NULL) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } image_ID = -1; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); if (image_ID != -1 && !preview) gimp_image_delete (image_ID); if (preview) destroy_preview (); if (buffer) g_object_unref (buffer); return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); if (! preview) { /* - step 2.1: tell the lib to save the comments */ jpeg_save_markers (&cinfo, JPEG_COM, 0xffff); /* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff); /* - step 2.3: tell the lib to save APP2 data (ICC profiles) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff); } /* Step 3: read file parameters with jpeg_read_header() */ jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. */ /* temporary buffer */ tile_height = gimp_tile_height (); buf = g_new (guchar, tile_height * cinfo.output_width * cinfo.output_components); rowbuf = g_new (guchar *, tile_height); for (i = 0; i < tile_height; i++) rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; switch (cinfo.output_components) { case 1: image_type = GIMP_GRAY; layer_type = GIMP_GRAY_IMAGE; break; case 3: image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; case 4: if (cinfo.out_color_space == JCS_CMYK) { image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; } /*fallthrough*/ default: g_message ("Don't know how to load JPEG images " "with %d color channels, using colorspace %d (%d).", cinfo.output_components, cinfo.out_color_space, cinfo.jpeg_color_space); return -1; break; } if (preview) { image_ID = preview_image_ID; } else { image_ID = gimp_image_new_with_precision (cinfo.output_width, cinfo.output_height, image_type, GIMP_PRECISION_U8_GAMMA); gimp_image_undo_disable (image_ID); gimp_image_set_filename (image_ID, filename); } if (preview) { preview_layer_ID = gimp_layer_new (preview_image_ID, _("JPEG preview"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); layer_ID = preview_layer_ID; } else { layer_ID = gimp_layer_new (image_ID, _("Background"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); } if (! preview) { GString *comment_buffer = NULL; guint8 *profile = NULL; guint profile_size = 0; /* Step 5.0: save the original JPEG settings in a parasite */ jpeg_detect_original_settings (&cinfo, image_ID); /* Step 5.1: check for comments, or Exif metadata in APP1 markers */ for (marker = cinfo.marker_list; marker; marker = marker->next) { const gchar *data = (const gchar *) marker->data; gsize len = marker->data_length; if (marker->marker == JPEG_COM) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found image comment (%d bytes)\n", marker->data_length); #endif if (! comment_buffer) { comment_buffer = g_string_new_len (data, len); } else { /* concatenate multiple comments, separate them with LF */ g_string_append_c (comment_buffer, '\n'); g_string_append_len (comment_buffer, data, len); } } else if ((marker->marker == JPEG_APP0 + 1) && (len > sizeof (JPEG_APP_HEADER_EXIF) + 8) && ! strcmp (JPEG_APP_HEADER_EXIF, data)) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found Exif block (%d bytes)\n", (gint) (len - sizeof (JPEG_APP_HEADER_EXIF))); #endif } } if (jpeg_load_resolution (image_ID, &cinfo)) { if (resolution_loaded) *resolution_loaded = TRUE; } /* if we found any comments, then make a parasite for them */ if (comment_buffer && comment_buffer->len) { GimpParasite *parasite; jpeg_load_sanitize_comment (comment_buffer->str); parasite = gimp_parasite_new ("gimp-comment", GIMP_PARASITE_PERSISTENT, strlen (comment_buffer->str) + 1, comment_buffer->str); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); g_string_free (comment_buffer, TRUE); } /* Step 5.3: check for an embedded ICC profile in APP2 markers */ jpeg_icc_read_profile (&cinfo, &profile, &profile_size); if (cinfo.out_color_space == JCS_CMYK) { cmyk_transform = jpeg_load_cmyk_transform (profile, profile_size); } else if (profile) /* don't attach the profile if we are transforming */ { GimpParasite *parasite; parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, profile_size, profile); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); } g_free (profile); /* Do not attach the "jpeg-save-options" parasite to the image * because this conflicts with the global defaults (bug #75398). */ } /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ buffer = gimp_drawable_get_buffer (layer_ID); format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8"); while (cinfo.output_scanline < cinfo.output_height) { start = cinfo.output_scanline; end = cinfo.output_scanline + tile_height; end = MIN (end, cinfo.output_height); scanlines = end - start; for (i = 0; i < scanlines; i++) jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1); if (cinfo.out_color_space == JCS_CMYK) jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines, cmyk_transform); gegl_buffer_set (buffer, GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines), 0, format, buf, GEGL_AUTO_ROWSTRIDE); if (! preview && (cinfo.output_scanline % 32) == 0) gimp_progress_update ((gdouble) cinfo.output_scanline / (gdouble) cinfo.output_height); } /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ if (cmyk_transform) cmsDeleteTransform (cmyk_transform); /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); g_object_unref (buffer); /* free up the temporary buffers */ g_free (rowbuf); g_free (buf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ /* Detach from the drawable and add it to the image. */ if (! preview) { gimp_progress_update (1.0); } gimp_image_insert_layer (image_ID, layer_ID, -1, 0); return image_ID; }
static gint32 load_image (const gchar *filename, gboolean thumbnail, GError **error) { FILE *fd; guchar buf[16]; guchar c; CMap localColorMap; gint grayScale; gboolean useGlobalColormap; gint bitPixel; gint imageCount = 0; gchar version[4]; gint32 image_ID = -1; fd = g_fopen (filename, "rb"); if (! fd) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } gimp_progress_init_printf (_("Opening '%s'"), gimp_filename_to_utf8 (filename)); if (! ReadOK (fd, buf, 6)) { g_message ("Error reading magic number"); return -1; } if (strncmp ((gchar *) buf, "GIF", 3) != 0) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s", _("This is not a GIF file")); return -1; } strncpy (version, (gchar *) buf + 3, 3); version[3] = '\0'; if ((strcmp (version, "87a") != 0) && (strcmp (version, "89a") != 0)) { g_message ("Bad version number, not '87a' or '89a'"); return -1; } if (! ReadOK (fd, buf, 7)) { g_message ("Failed to read screen descriptor"); return -1; } GifScreen.Width = LM_to_uint (buf[0], buf[1]); GifScreen.Height = LM_to_uint (buf[2], buf[3]); GifScreen.BitPixel = 2 << (buf[4] & 0x07); GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1); GifScreen.Background = buf[5]; GifScreen.AspectRatio = buf[6]; if (BitSet (buf[4], LOCALCOLORMAP)) { /* Global Colormap */ if (! ReadColorMap (fd, GifScreen.BitPixel, GifScreen.ColorMap, &GifScreen.GrayScale)) { g_message ("Error reading global colormap"); return -1; } } if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) { g_message (_("Non-square pixels. Image might look squashed.")); } highest_used_index = 0; while (TRUE) { if (! ReadOK (fd, &c, 1)) { g_message ("EOF / read error on image data"); return image_ID; /* will be -1 if failed on first image! */ } if (c == ';') { /* GIF terminator */ return image_ID; } if (c == '!') { /* Extension */ if (! ReadOK (fd, &c, 1)) { g_message ("EOF / read error on extension function code"); return image_ID; /* will be -1 if failed on first image! */ } DoExtension (fd, c); continue; } if (c != ',') { /* Not a valid start character */ g_printerr ("GIF: bogus character 0x%02x, ignoring.\n", (int) c); continue; } ++imageCount; if (! ReadOK (fd, buf, 9)) { g_message ("Couldn't read left/top/width/height"); return image_ID; /* will be -1 if failed on first image! */ } useGlobalColormap = !BitSet (buf[8], LOCALCOLORMAP); bitPixel = 1 << ((buf[8] & 0x07) + 1); if (! useGlobalColormap) { if (! ReadColorMap (fd, bitPixel, localColorMap, &grayScale)) { g_message ("Error reading local colormap"); return image_ID; /* will be -1 if failed on first image! */ } image_ID = ReadImage (fd, filename, LM_to_uint (buf[4], buf[5]), LM_to_uint (buf[6], buf[7]), localColorMap, bitPixel, grayScale, BitSet (buf[8], INTERLACE), imageCount, (guint) LM_to_uint (buf[0], buf[1]), (guint) LM_to_uint (buf[2], buf[3]), GifScreen.Width, GifScreen.Height); } else { image_ID = ReadImage (fd, filename, LM_to_uint (buf[4], buf[5]), LM_to_uint (buf[6], buf[7]), GifScreen.ColorMap, GifScreen.BitPixel, GifScreen.GrayScale, BitSet (buf[8], INTERLACE), imageCount, (guint) LM_to_uint (buf[0], buf[1]), (guint) LM_to_uint (buf[2], buf[3]), GifScreen.Width, GifScreen.Height); } if (comment_parasite != NULL) { if (! thumbnail) gimp_image_attach_parasite (image_ID, comment_parasite); gimp_parasite_free (comment_parasite); comment_parasite = NULL; } /* If we are loading a thumbnail, we stop after the first frame. */ if (thumbnail) break; } return image_ID; }
static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[MAX_EXTRACT_IMAGES + 1]; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint32 num_images; gint32 image_ID_extract[MAX_EXTRACT_IMAGES]; gint32 layer_ID_extract[MAX_EXTRACT_IMAGES]; gint j; gint32 layer; gint32 num_layers; gint32 image_ID; INIT_I18N (); gegl_init (NULL, NULL); run_mode = param[0].data.d_int32; image_ID = param[1].data.d_image; layer = param[2].data.d_drawable; *nreturn_vals = MAX_EXTRACT_IMAGES + 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; for (j = 0; j < MAX_EXTRACT_IMAGES; j++) { values[j+1].type = GIMP_PDB_IMAGE; values[j+1].data.d_int32 = -1; } switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data (PLUG_IN_PROC, &decovals); /* First acquire information with a dialog */ if (! decompose_dialog ()) return; break; case GIMP_RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ if (nparams != 4 && nparams != 5 && nparams != 6) { status = GIMP_PDB_CALLING_ERROR; } else { strncpy (decovals.extract_type, param[3].data.d_string, sizeof (decovals.extract_type)); decovals.extract_type[sizeof (decovals.extract_type) - 1] = '\0'; decovals.as_layers = nparams > 4 ? param[4].data.d_int32 : FALSE; decovals.use_registration = (strcmp (name, PLUG_IN_PROC_REG) == 0); } break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data (PLUG_IN_PROC, &decovals); break; default: break; } if (status == GIMP_PDB_SUCCESS) { gimp_progress_init (_("Decomposing")); num_images = decompose (image_ID, layer, decovals.extract_type, image_ID_extract, &num_layers, layer_ID_extract); if (num_images <= 0) { status = GIMP_PDB_EXECUTION_ERROR; } else { /* create decompose-data parasite */ GString *data = g_string_new (""); g_string_printf (data, "source=%d type=%s ", layer, decovals.extract_type); for (j = 0; j < num_layers; j++) g_string_append_printf (data, "%d ", layer_ID_extract[j]); for (j = 0; j < num_images; j++) { GimpParasite *parasite; values[j+1].data.d_int32 = image_ID_extract[j]; gimp_image_undo_enable (image_ID_extract[j]); gimp_image_clean_all (image_ID_extract[j]); parasite = gimp_parasite_new ("decompose-data", 0, data->len + 1, data->str); gimp_image_attach_parasite (image_ID_extract[j], parasite); gimp_parasite_free (parasite); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_display_new (image_ID_extract[j]); } /* Store data */ if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data (PLUG_IN_PROC, &decovals, sizeof (DecoVals)); } gimp_progress_end (); } values[0].data.d_status = status; }
static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[4]; gint32 image_ID; XMPModel *xmp_model; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpParasite *parasite = NULL; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; INIT_I18N(); if (! strcmp (name, EDITOR_PROC)) image_ID = param[1].data.d_image; else image_ID = param[0].data.d_image; xmp_model = xmp_model_new (); /* if there is already a metadata parasite, load it */ parasite = gimp_image_get_parasite (image_ID, METADATA_PARASITE); if (parasite) { GError *error = NULL; if (!! strncmp (gimp_parasite_data (parasite), METADATA_MARKER, METADATA_MARKER_LEN) || ! xmp_model_parse_buffer (xmp_model, (const gchar *) gimp_parasite_data (parasite) + METADATA_MARKER_LEN, gimp_parasite_data_size (parasite) - METADATA_MARKER_LEN, TRUE, &error)) { g_printerr ("\nMetadata parasite seems to be corrupt\n"); /* continue anyway, we will attach a clean parasite later */ } gimp_parasite_free (parasite); } /* If we have no metadata yet, try to find an XMP packet in the file * (but ignore errors if nothing is found). * * FIXME: This is a workaround until all file plug-ins do the right * thing when loading their files. */ if (xmp_model_is_empty (xmp_model) && !! strcmp (name, DECODE_XMP_PROC)) { const gchar *filename; GError *error = NULL; filename = gimp_image_get_filename (image_ID); if (filename != NULL) if (xmp_model_parse_file (xmp_model, filename, &error)) /* g_message ("XMP loaded from file '%s'\n", filename) */; } /* Now check what we are supposed to do */ if (! strcmp (name, DECODE_XMP_PROC)) { const gchar *buffer; GError *error = NULL; buffer = param[1].data.d_string; if (! xmp_model_parse_buffer (xmp_model, buffer, strlen (buffer), FALSE, &error)) status = GIMP_PDB_EXECUTION_ERROR; } else if (! strcmp (name, ENCODE_XMP_PROC)) { /* done below together with the parasite */ } else if (! strcmp (name, DECODE_EXIF_PROC)) { GError *error = NULL; if (! xmp_merge_from_exifbuffer (xmp_model, image_ID, &error)) { status = GIMP_PDB_EXECUTION_ERROR; g_printerr ("\nExif to XMP merge failed.\n"); } } else if (! strcmp (name, GET_PROC)) { g_printerr ("Not implemented yet (GET_PROC)\n"); /* FIXME */ status = GIMP_PDB_EXECUTION_ERROR; } else if (! strcmp (name, SET_PROC)) { g_printerr ("Not implemented yet (SET_PROC)\n"); /* FIXME */ status = GIMP_PDB_EXECUTION_ERROR; } else if (! strcmp (name, GET_SIMPLE_PROC)) { const gchar *schema_name; const gchar *property_name; const gchar *value; schema_name = param[1].data.d_string; property_name = param[2].data.d_string; value = xmp_model_get_scalar_property (xmp_model, schema_name, property_name); if (value) { *nreturn_vals = 2; values[1].type = GIMP_PDB_STRING; values[1].data.d_string = g_strdup (value); } else status = GIMP_PDB_EXECUTION_ERROR; } else if (! strcmp (name, SET_SIMPLE_PROC)) { const gchar *schema_name; const gchar *property_name; const gchar *property_value; schema_name = param[1].data.d_string; property_name = param[2].data.d_string; property_value = param[3].data.d_string; if (! xmp_model_set_scalar_property (xmp_model, schema_name, property_name, property_value)) status = GIMP_PDB_EXECUTION_ERROR; } else if (! strcmp (name, IMPORT_PROC)) { const gchar *filename; gchar *buffer; gsize buffer_length; GError *error = NULL; filename = param[1].data.d_string; if (! g_file_get_contents (filename, &buffer, &buffer_length, &error)) { g_error_free (error); status = GIMP_PDB_EXECUTION_ERROR; } else if (! xmp_model_parse_buffer (xmp_model, buffer, buffer_length, TRUE, &error)) { g_error_free (error); status = GIMP_PDB_EXECUTION_ERROR; } g_free (buffer); } else if (! strcmp (name, EXPORT_PROC)) { /* FIXME: this is easy to implement, but the first thing to do is */ /* to improve the code of export_dialog_response() in interface.c */ g_printerr ("Not implemented yet (EXPORT_PROC)\n"); status = GIMP_PDB_EXECUTION_ERROR; } else if (! strcmp (name, EDITOR_PROC)) { GimpRunMode run_mode; run_mode = param[0].data.d_int32; if (run_mode == GIMP_RUN_INTERACTIVE) { if (! metadata_dialog (image_ID, xmp_model)) status = GIMP_PDB_CANCEL; } g_printerr ("Not implemented yet (EDITOR_PROC)\n"); status = GIMP_PDB_EXECUTION_ERROR; } else { status = GIMP_PDB_CALLING_ERROR; } if (status == GIMP_PDB_SUCCESS) { GString *buffer; /* Generate the updated parasite and attach it to the image */ buffer = g_string_new (METADATA_MARKER); xmp_generate_packet (xmp_model, buffer); parasite = gimp_parasite_new (METADATA_PARASITE, GIMP_PARASITE_PERSISTENT, buffer->len, (gpointer) buffer->str); gimp_image_attach_parasite (image_ID, parasite); if (! strcmp (name, ENCODE_XMP_PROC)) { *nreturn_vals = 2; values[1].type = GIMP_PDB_STRING; values[1].data.d_string = g_strdup (buffer->str + METADATA_MARKER_LEN); } g_string_free (buffer, TRUE); } g_object_unref (xmp_model); values[0].data.d_status = status; }