long GalleryUtil::GetNaturalRotation(const unsigned char *buffer, int size) { long rotateAngle = 0; #ifdef EXIF_SUPPORT try { ExifData *data = exif_data_new_from_data(buffer, size); if (data) { rotateAngle = GetNaturalRotation(data); exif_data_free(data); } else { LOG(VB_FILE, LOG_ERR, LOC + "Could not load exif data from buffer"); } } catch (...) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to extract EXIF headers from buffer"); } #else // Shut the compiler up about the unused argument (void)buffer; (void)size; #endif return rotateAngle; }
void JP4::readMakerNote() { if (_raw_app1) _ed = exif_data_new_from_data(_raw_app1, _raw_app1_length); if (!_ed) return; ExifEntry* makerNoteEntry = exif_data_get_entry(_ed, EXIF_TAG_MAKER_NOTE); if (!makerNoteEntry) { _makerNote.gain[0] = _makerNote.gain[1] = _makerNote.gain[2] = _makerNote.gain[3] = 2.0; _makerNote.gamma[0] = _makerNote.gamma[1] = _makerNote.gamma[2] = _makerNote.gamma[3] = 0.57; _makerNote.gamma_scale[0] = _makerNote.gamma_scale[1] = _makerNote.gamma_scale[2] = _makerNote.gamma_scale[3] = 0xffff; _makerNote.black[0] = _makerNote.black[1] = _makerNote.black[2] = _makerNote.black[3] = 10/256.0; _makerNote.decim_hor = 1; _makerNote.decim_ver = 1; _makerNote.bin_hor = 1; _makerNote.bin_ver = 1; return; } int makerNoteLength = makerNoteEntry->size/4; long makerNote[makerNoteLength]; for (int i = 0; i < makerNoteLength; i++) { makerNote[i] = get_long(makerNoteEntry->data + i*4); } for (int i = 0; i < 4; i++) { // channel gain 8.16 (0x10000 - 1.0). Combines both analog gain and digital scaling _makerNote.gain[i] = makerNote[i] / 65536.0; // (P_PIXEL_LOW<<24) | (P_GAMMA <<16) and 16-bit (6.10) scale for gamma tables, _makerNote.gamma_scale[i] = (makerNote[i+4] & 0xffff); _makerNote.gamma[i] = ((makerNote[i+4]>>16) & 0xff) / 100.0; _makerNote.black[i] = (makerNote[i+4]>>24) / 256.0; } if (makerNoteLength >= 12) { _makerNote.woi_left = (makerNote[8] & 0xffff); _makerNote.woi_width = (makerNote[8]>>16) & 0xffff; _makerNote.woi_top = (makerNote[9] & 0xffff); _makerNote.woi_height = (makerNote[9]>>16); _makerNote.flip_hor = (makerNote[10] & 0x1); _makerNote.flip_ver = (makerNote[10]>>1) & 0x1; _makerNote.bayer_mode = (makerNote[10]>>2) & 0x3; _makerNote.color_mode = (makerNote[10]>>4) & 0x0f; _makerNote.decim_hor = (makerNote[10]>> 8) & 0x0f; _makerNote.decim_ver = (makerNote[10]>>12) & 0x0f; _makerNote.bin_hor = (makerNote[10]>>16) & 0x0f; _makerNote.bin_ver = (makerNote[10]>>20) & 0x0f; }
void metadatamux_exif_create_chunk_from_tag_list (guint8 ** buf, guint32 * size, const GstTagList * taglist, const MetaExifWriteOptions * opts) { ExifData *ed = NULL; GstBuffer *exif_chunk = NULL; const GValue *val = NULL; if (!(buf && size)) goto done; g_free (*buf); *buf = NULL; *size = 0; val = gst_tag_list_get_value_index (taglist, GST_TAG_EXIF, 0); if (val) { exif_chunk = gst_value_get_buffer (val); if (exif_chunk) { ed = exif_data_new_from_data (GST_BUFFER_DATA (exif_chunk), GST_BUFFER_SIZE (exif_chunk)); } } if (!ed) { ed = exif_data_new (); GST_DEBUG ("setting byteorder %d", opts->byteorder); switch (opts->byteorder) { case GST_META_EXIF_BYTE_ORDER_MOTOROLA: exif_data_set_byte_order (ed, EXIF_BYTE_ORDER_MOTOROLA); break; case GST_META_EXIF_BYTE_ORDER_INTEL: exif_data_set_byte_order (ed, EXIF_BYTE_ORDER_INTEL); break; default: break; } exif_data_set_data_type (ed, EXIF_DATA_TYPE_COMPRESSED); exif_data_fix (ed); } gst_tag_list_foreach (taglist, metadatamux_exif_for_each_tag_in_list, ed); exif_data_save_data (ed, buf, size); done: if (ed) exif_data_unref (ed); return; }
void Image::load_tags( gpointer data , gsize size ) { g_assert( data ); g_assert( size ); if ( ExifData * ed = exif_data_new_from_data( ( const unsigned char * ) data , size ) ) { load_exif_tags( ed , tags ); exif_data_unref( ed ); } else { tplog2( " FAILED TO LOAD TAGS" ); } }
static int parse_exif( VipsImage *im, void *data, int data_length ) { #ifdef HAVE_EXIF { ExifData *ed; VipsExif ve; if( !(ed = exif_data_new_from_data( data, data_length )) ) return( -1 ); #ifdef DEBUG_VERBOSE show_tags( ed ); show_values( ed ); #endif /*DEBUG_VERBOSE*/ /* Attach informational fields for what we find. FIXME ... better to have this in the UI layer? Or we could attach non-human-readable tags here (int, double etc) and then move the human stuff to the UI layer? */ ve.image = im; ve.ed = ed; exif_data_foreach_content( ed, (ExifDataForeachContentFunc) attach_exif_content, &ve ); /* Look for resolution fields and use them to set the VIPS * xres/yres fields. */ res_from_exif( im, ed ); attach_thumbnail( im, ed ); exif_data_free( ed ); } #endif /*HAVE_EXIF*/ return( 0 ); }
/* Replacement for exif_data_new_from_file() to work around * filename encoding problems (see bug #335391). */ ExifData * jpeg_exif_data_new_from_file (const gchar *filename, GError **error) { ExifData *data; GMappedFile *file; g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); file = g_mapped_file_new (filename, FALSE, error); if (! file) return NULL; data = exif_data_new_from_data ((guchar *) g_mapped_file_get_contents (file), g_mapped_file_get_length (file)); g_mapped_file_free (file); return data; }
void metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, GstAdapter * adapter, MetadataTagMapping mapping) { const guint8 *buf; guint32 size; ExifData *exif = NULL; MEUserData user_data = { taglist, mode, 2, -1, 'k', 'k' }; if (adapter == NULL || (size = gst_adapter_available (adapter)) == 0) { goto done; } /* add chunk tag */ if (mapping & METADATA_TAG_MAP_WHOLECHUNK) metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_EXIF, adapter); if (!(mapping & METADATA_TAG_MAP_INDIVIDUALS)) goto done; buf = gst_adapter_peek (adapter, size); exif = exif_data_new_from_data (buf, size); if (exif == NULL) { goto done; } exif_data_foreach_content (exif, metadataparse_exif_data_foreach_content_func, (void *) &user_data); done: if (exif) exif_data_unref (exif); return; }
int main (int argc, char **argv) { ExifData *d; unsigned int buf_size; unsigned char *buf; int r; if (argc <= 1) { fprintf (stderr, "You need to supply a filename!\n"); return 1; } fprintf (stdout, "Loading '%s'...\n", argv[1]); d = exif_data_new_from_file (argv[1]); if (!d) { fprintf (stderr, "Could not load data from '%s'!\n", argv[1]); return 1; } fprintf (stdout, "Loaded '%s'.\n", argv[1]); fprintf (stdout, "######### Test 1 #########\n"); r = test_exif_data (d); if (r) return r; exif_data_save_data (d, &buf, &buf_size); exif_data_unref (d); d = exif_data_new_from_data (buf, buf_size); free (buf); fprintf (stdout, "######### Test 2 #########\n"); r = test_exif_data (d); if (r) return r; fprintf (stdout, "Test successful!\n"); return 1; }
void jpeg_data_load_data (JPEGData *data, const unsigned char *d, unsigned int size) { unsigned int i, o, len; JPEGSection *s; JPEGMarker marker; if (!data) return; if (!d) return; for (o = 0; o < size;) { /* * JPEG sections start with 0xff. The first byte that is * not 0xff is a marker (hopefully). */ for (i = 0; i < MIN(7, size - o); i++) if (d[o + i] != 0xff) break; if (!JPEG_IS_MARKER (d[o + i])) { exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data", _("Data does not follow JPEG specification.")); return; } marker = (JPEGMarker)d[o + i]; /* Append this section */ jpeg_data_append_section (data); if (!data->count) return; s = &data->sections[data->count - 1]; s->marker = marker; o += i + 1; switch (s->marker) { case JPEG_MARKER_SOI: case JPEG_MARKER_EOI: break; default: /* Read the length of the section */ len = ((d[o] << 8) | d[o + 1]) - 2; if (len > size) { o = size; break; } o += 2; if (o + len > size) { o = size; break; } switch (s->marker) { case JPEG_MARKER_APP1: s->content.app1 = exif_data_new_from_data ( d + o - 4, len + 4); break; default: s->content._generic.data = (unsigned char*)malloc (sizeof (char) * len); if (!s->content._generic.data) return; s->content._generic.size = len; memcpy (s->content._generic.data, &d[o], len); /* In case of SOS, image data will follow. */ if (s->marker == JPEG_MARKER_SOS) { /* -2 means 'take all but the last 2 bytes which are hoped to be JPEG_MARKER_EOI */ data->size = size - 2 - o - len; if (d[o + len + data->size] != 0xFF) { /* A truncated file (i.e. w/o JPEG_MARKER_EOI at the end). Instead of trying to use the last two bytes as marker, touching memory beyond allocated memory and posssibly saving back screwed file, we rather take the rest of the file. */ data->size += 2; } data->data = (unsigned char*)malloc ( sizeof (char) * data->size); if (!data->data) return; memcpy (data->data, d + o + len, data->size); o += data->size; } break; } o += len; break; } } }
void jpeg_data_load_data (JPEGData *data, const unsigned char *d, unsigned int size) { unsigned int i, o, len; JPEGSection *s; JPEGMarker marker; if (!data) return; if (!d) return; #ifdef DEBUG printf ("Parsing %i bytes...\n", size); #endif for (o = 0; o < size;) { /* * JPEG sections start with 0xff. The first byte that is * not 0xff is a marker (hopefully). */ for (i = 0; i < 7; i++) if (d[o + i] != 0xff) break; if (!JPEG_IS_MARKER (d[o + i])) return; marker = d[o + i]; #ifdef DEBUG printf ("Found marker 0x%x ('%s') at %i.\n", marker, jpeg_marker_get_name (marker), o + i); #endif /* Append this section */ jpeg_data_append_section (data); s = &data->sections[data->count - 1]; s->marker = marker; s->content.generic.data = NULL; o += i + 1; switch (s->marker) { case JPEG_MARKER_SOI: case JPEG_MARKER_EOI: break; default: /* Read the length of the section */ len = ((d[o] << 8) | d[o + 1]) - 2; if (len > size) { o = size; break; } o += 2; if (o + len > size) { o = size; break; } switch (s->marker) { case JPEG_MARKER_APP1: s->content.app1 = exif_data_new_from_data ( d + o - 4, len + 4); break; default: s->content.generic.size = len; s->content.generic.data = malloc (sizeof (char) * len); memcpy (s->content.generic.data, &d[o], len); /* In case of SOS, image data will follow. */ if (s->marker == JPEG_MARKER_SOS) { data->size = size - 2 - o - len; data->data = malloc ( sizeof (char) * data->size); memcpy (data->data, d + o + len, data->size); o += data->size; } break; } o += len; break; } } }
static int write_exif( Write *write ) { #ifdef HAVE_EXIF unsigned char *data; size_t data_length; unsigned int idl; ExifData *ed; /* Either parse from the embedded EXIF, or if there's none, make * some fresh EXIF we can write the resolution to. */ if( vips_image_get_typeof( write->in, VIPS_META_EXIF_NAME ) ) { if( vips_image_get_blob( write->in, VIPS_META_EXIF_NAME, (void *) &data, &data_length ) ) return( -1 ); if( !(ed = exif_data_new_from_data( data, data_length )) ) return( -1 ); } else { ed = exif_data_new(); exif_data_set_option( ed, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION ); exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED ); exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL ); /* Create the mandatory EXIF fields with default data. */ exif_data_fix( ed ); } /* Update EXIF tags from the image metadata. */ vips_exif_update( ed, write->in ); /* Update EXIF resolution from the vips image header. */ if( vips__set_exif_resolution( ed, write->in ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF image dimensions from the vips image header. */ if( set_exif_dimensions( ed, write->in ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF orientation from the vips image header. */ if( set_exif_orientation( ed, write->in ) ) { exif_data_free( ed ); return( -1 ); } /* Reserialise and write. exif_data_save_data() returns an int for some * reason. */ exif_data_save_data( ed, &data, &idl ); if( !idl ) { vips_error( "VipsJpeg", "%s", _( "error saving EXIF" ) ); exif_data_free( ed ); return( -1 ); } data_length = idl; #ifdef DEBUG printf( "write_exif: attaching %zd bytes of EXIF\n", data_length ); #endif /*DEBUG*/ exif_data_free( ed ); jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length ); free( data ); #else /*!HAVE_EXIF*/ /* No libexif ... just copy the embedded EXIF over. */ if( write_blob( write, VIPS_META_EXIF_NAME, JPEG_APP0 + 1 ) ) return( -1 ); #endif /*!HAVE_EXIF*/ return( 0 ); }
static int read_exif( IMAGE *im, void *data, int data_length ) { char *data_copy; /* Horrifyingly, some JPEGs have several APP1 sections. We must only * use the first one that starts "Exif.." */ if( ((char *) data)[0] != 'E' || ((char *) data)[1] != 'x' || ((char *) data)[2] != 'i' || ((char *) data)[3] != 'f' ) return( 0 ); if( im_header_get_typeof( im, IM_META_EXIF_NAME ) ) return( 0 ); /* Always attach a copy of the unparsed exif data. */ if( !(data_copy = im_malloc( NULL, data_length )) ) return( -1 ); memcpy( data_copy, data, data_length ); if( im_meta_set_blob( im, IM_META_EXIF_NAME, (im_callback_fn) im_free, data_copy, data_length ) ) { im_free( data_copy ); return( -1 ); } #ifdef HAVE_EXIF { ExifData *ed; if( !(ed = exif_data_new_from_data( data, data_length )) ) return( -1 ); if( ed->size > 0 ) { #ifdef DEBUG_VERBOSE show_tags( ed ); show_values( ed ); #endif /*DEBUG_VERBOSE*/ /* Attach informational fields for what we find. FIXME ... better to have this in the UI layer? Or we could attach non-human-readable tags here (int, double etc) and then move the human stuff to the UI layer? */ exif_data_foreach_content( ed, (ExifDataForeachContentFunc) attach_exif_content, im ); /* Look for resolution fields and use them to set the VIPS * xres/yres fields. */ set_vips_resolution( im, ed ); attach_thumbnail( im, ed ); } exif_data_free( ed ); } #endif /*HAVE_EXIF*/ return( 0 ); }
static int Lloadbuffer (lua_State *L) { /** loadbuffer(buf) */ size_t length; const char *buffer = luaL_checklstring(L, 1, &length); pushdata(L, exif_data_new_from_data((const unsigned char *)buffer, length)); return 1; }
int image_jpeg_read_header(MediaScanImage *i, MediaScanResult *r) { int ret = 1; int x; JPEGData *j = malloc(sizeof(JPEGData)); i->_jpeg = (void *)j; LOG_MEM("new JPEGData @ %p\n", i->_jpeg); j->cinfo = malloc(sizeof(struct jpeg_decompress_struct)); j->jpeg_error_pub = malloc(sizeof(struct jpeg_error_mgr)); LOG_MEM("new JPEG cinfo @ %p\n", j->cinfo); LOG_MEM("new JPEG error_pub @ %p\n", j->jpeg_error_pub); j->cinfo->err = jpeg_std_error(j->jpeg_error_pub); j->jpeg_error_pub->error_exit = libjpeg_error_handler; j->jpeg_error_pub->output_message = libjpeg_output_message; if (setjmp(setjmp_buffer)) { image_jpeg_destroy(i); return 0; } // Save filename in case any warnings/errors occur strncpy(Filename, r->path, FILENAME_LEN); if (strlen(r->path) > FILENAME_LEN) Filename[FILENAME_LEN] = 0; jpeg_create_decompress(j->cinfo); // Init custom source manager to read from existing buffer image_jpeg_buf_src(i, r); // Save APP1 marker for EXIF jpeg_save_markers(j->cinfo, 0xE1, 1024 * 64); jpeg_read_header(j->cinfo, TRUE); i->width = j->cinfo->image_width; i->height = j->cinfo->image_height; i->channels = j->cinfo->num_components; r->mime_type = MIME_IMAGE_JPEG; // Match with DLNA profile for (x = 0; jpeg_profiles_mapping[x].profile; x++) { if (i->width <= jpeg_profiles_mapping[x].max_width && i->height <= jpeg_profiles_mapping[x].max_height) { r->dlna_profile = jpeg_profiles_mapping[x].profile->id; break; } } // Process Exif tag if (j->cinfo->marker_list != NULL) { jpeg_saved_marker_ptr marker = j->cinfo->marker_list; while (marker != NULL) { if (marker->marker == 0xE1 && marker->data[0] == 'E' && marker->data[1] == 'x' && marker->data[2] == 'i' && marker->data[3] == 'f') { ExifData *exif; result_create_tag(r, "Exif"); LOG_DEBUG("Parsing EXIF tag of size %d\n", marker->data_length); exif = exif_data_new_from_data(marker->data, marker->data_length); LOG_MEM("new EXIF data @ %p\n", exif); if (exif != NULL) { exif_data_foreach_content(exif, parse_exif_ifd, (void *)r); LOG_MEM("destroy EXIF data @ %p\n", exif); exif_data_free(exif); } break; } marker = marker->next; } } return ret; }
psd_status psd_get_image_resource(psd_context * context) { psd_int length, i, size; psd_ushort ID; psd_uint tag; psd_uchar sizeofname; psd_int sizeofdata, prev_stream_pos; psd_uchar * buffer; //psd_status status; // Length of image resource section length = psd_stream_get_int(context); if(length <= 0) return psd_status_done; // default context->global_angle = 30; context->global_altitude = 30; while(length > 0) { // Signature: '8BIM' tag = psd_stream_get_int(context); if(tag == '8BIM') { length -= 4; // Unique identifier for the resource ID = psd_stream_get_short(context); length -= 2; // Name: Pascal string, padded to make the size even (a null name consists of two bytes of 0) sizeofname = psd_stream_get_char(context); if((sizeofname & 0x01) == 0) sizeofname ++; psd_stream_get_null(context, sizeofname); length -= sizeofname + 1; // Actual size of resource data that follows sizeofdata = psd_stream_get_int(context); length -= 4; // resource data must be even if(sizeofdata & 0x01) sizeofdata ++; length -= sizeofdata; switch(context->load_tag) { case psd_load_tag_thumbnail: if(ID != 1033 && ID != 1036) { psd_stream_get_null(context, sizeofdata); continue; } break; case psd_load_tag_merged: // alpha channels information if(ID != 1006 && ID != 1045 && ID != 1053) { psd_stream_get_null(context, sizeofdata); continue; } break; case psd_load_tag_exif: if(ID != 1058 && ID != 1059) { psd_stream_get_null(context, sizeofdata); continue; } break; } prev_stream_pos = context->stream.current_pos; if(sizeofdata > 0) { switch(ID) { // ResolutionInfo structure case 1005: // Horizontal resolution in pixels per inch. context->resolution_info.hres = psd_stream_get_int(context) / 65536.0f; // 1=display horitzontal resolution in pixels per inch; 2=display horitzontal resolution in pixels per cm. context->resolution_info.hres_unit = psd_stream_get_short(context); // Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. context->resolution_info.width_unit = psd_stream_get_short(context); // Vertial resolution in pixels per inch. context->resolution_info.vres = psd_stream_get_int(context) / 65536.0f; // 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm. context->resolution_info.vres_unit = psd_stream_get_short(context); // Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. context->resolution_info.height_unit = psd_stream_get_short(context); context->fill_resolution_info = psd_true; break; // Names of the alpha channels as a series of Pascal strings. case 1006: buffer = (psd_uchar *)psd_malloc(sizeofdata); if(buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer, sizeofdata); if(context->alpha_channels == 0) { size = 0; // maybe odd while(size + 1 < sizeofdata) { size += *(buffer + size) + 1; context->alpha_channels ++; } context->color_channels = context->channels - context->alpha_channels; context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info)); if(context->alpha_channel_info == NULL) { psd_free(buffer); return psd_status_malloc_failed; } memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info)); } size = 0; for(i = 0; i < context->alpha_channels; i ++) { memcpy(context->alpha_channel_info[i].name, buffer + size + 1, *(buffer + size)); size += *(buffer + size) + 1; } psd_free(buffer); context->fill_alpha_channel_info = psd_true; break; // DisplayInfo structure case 1007: context->display_info.color = psd_stream_get_space_color(context); // 0..100 context->display_info.opacity = psd_stream_get_short(context); psd_assert(context->display_info.opacity >= 0 && context->display_info.opacity <= 100); // selected = 0, protected = 1 context->display_info.kind = psd_stream_get_char(context); // maybe be 2 when color mode is multichannel //psd_assert(context->display_info.kind == 0 || context->display_info.kind == 1); // padding psd_stream_get_char(context); context->fill_display_info = psd_true; break; // The caption as a Pascal string. case 1008: size = psd_stream_get_char(context); psd_stream_get(context, context->caption, size); break; // Layer state information // 2 bytes containing the index of target layer (0 = bottom layer). case 1024: context->target_layer_index = psd_stream_get_short(context); break; // Layers group information // 2 bytes per layer containing a group ID for the dragging groups. Layers in // a group have the same group ID. case 1026: context->layer_group_count = sizeofdata / 2; context->layer_group_id = (psd_ushort *)psd_malloc(context->layer_group_count * 2); if(context->layer_group_id == NULL) return psd_status_malloc_failed; for(i = 0; i < context->layer_group_count; i ++) context->layer_group_id[i] = psd_stream_get_short(context); context->fill_layer_group = psd_true; break; // (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only case 1033: // (Photoshop 5.0) Thumbnail resource (supersedes resource 1033) case 1036: if(context->load_tag == psd_load_tag_layer) { psd_stream_get_null(context, sizeofdata); continue; } // 1 = kJpegRGB . Also supports kRawRGB (0). context->thumbnail_resource.format = psd_stream_get_int(context); psd_assert(context->thumbnail_resource.format == 0 || context->thumbnail_resource.format == 1); // Width of thumbnail in pixels. context->thumbnail_resource.width = psd_stream_get_int(context); // Height of thumbnail in pixels. context->thumbnail_resource.height = psd_stream_get_int(context); // Padded row bytes = (width * bits per pixel + 31) / 32 * 4. context->thumbnail_resource.width_bytes = psd_stream_get_int(context); // Total size = widthbytes * height * planes context->thumbnail_resource.total_size = psd_stream_get_int(context); // Used for consistency check. context->thumbnail_resource.size_after_compression = psd_stream_get_int(context); context->thumbnail_resource.bits_per_pixel = psd_stream_get_short(context); // Bits per pixel. = 24 psd_assert(context->thumbnail_resource.bits_per_pixel == 24); context->thumbnail_resource.number_of_planes = psd_stream_get_short(context); // Number of planes. = 1 psd_assert(context->thumbnail_resource.number_of_planes == 1); #ifdef PSD_INCLUDE_LIBJPEG if(context->thumbnail_resource.format == 0) { status = psd_thumbnail_decode_raw(&context->thumbnail_resource.thumbnail_data, context->thumbnail_resource.size_after_compression, context); } else { status = psd_thumbnail_decode_jpeg(&context->thumbnail_resource.thumbnail_data, context->thumbnail_resource.size_after_compression, context); } if(status != psd_status_done) return status; #else context->thumbnail_resource.jfif_data = (psd_uchar *)psd_malloc(sizeofdata - 28); if(context->thumbnail_resource.jfif_data == NULL) return psd_status_malloc_failed; psd_stream_get(context, context->thumbnail_resource.jfif_data, sizeofdata - 28); #endif context->fill_thumbnail_resource = psd_true; break; // (Photoshop 4.0) Copyright flag // Boolean indicating whether image is copyrighted. Can be set via // Property suite or by user in File Info... case 1034: context->copyright_flag = (psd_bool)psd_stream_get_short(context); psd_assert(context->copyright_flag == 0 || context->copyright_flag == 1); break; // (Photoshop 5.0) Global Angle // 4 bytes that contain an integer between 0 and 359, which is the global // lighting angle for effects layer. If not present, assumed to be 30. case 1037: context->global_angle = psd_stream_get_int(context); break; // (Photoshop 5.0) Effects visible // 1-byte global flag to show/hide all the effects layer. Only present when // they are hidden. case 1042: context->effects_visible = (psd_bool)psd_stream_get_short(context); psd_assert(context->effects_visible == 0 || context->effects_visible == 1); break; // (Photoshop 5.0) Unicode Alpha Names // Unicode string (4 bytes length followed by string). case 1045: buffer = (psd_uchar *)psd_malloc(sizeofdata); if(buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer, sizeofdata); if(context->alpha_channels == 0) { size = 0; while(size < sizeofdata) { size += PSD_CHAR_TO_INT(buffer + size) * 2 + 4; context->alpha_channels ++; } context->color_channels = context->channels - context->alpha_channels; context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info)); if(context->alpha_channel_info == NULL) { psd_free(buffer); return psd_status_malloc_failed; } memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info)); } size = 0; for(i = 0; i < context->alpha_channels; i ++) { context->alpha_channel_info[i].unicode_name_length = PSD_CHAR_TO_INT(buffer + size); context->alpha_channel_info[i].unicode_name = (psd_ushort *)psd_malloc(2 * context->alpha_channel_info[i].unicode_name_length); if(context->alpha_channel_info[i].unicode_name == NULL) { psd_free(buffer); return psd_status_malloc_failed; } memcpy(context->alpha_channel_info[i].unicode_name, buffer + size + 4, 2 * context->alpha_channel_info[i].unicode_name_length); size += 2 * context->alpha_channel_info[i].unicode_name_length + 4; } psd_free(buffer); context->fill_alpha_channel_info = psd_true; break; // (Photoshop 6.0) Indexed Color Table Count // 2 bytes for the number of colors in table that are actually defined case 1046: context->indexed_color_table_count = psd_stream_get_short(context); break; // (Photoshop 6.0) Transparency Index. // 2 bytes for the index of transparent color, if any. case 1047: context->transparency_index = psd_stream_get_short(context); break; // (Photoshop 6.0) Global Altitude // 4 byte entry for altitude case 1049: context->global_altitude = psd_stream_get_int(context); break; // (Photoshop 6.0) Alpha Identifiers // 4 bytes of length, followed by 4 bytes each for every alpha identifier. case 1053: if(context->alpha_channels == 0) { context->alpha_channels = sizeofdata / 4; context->color_channels = context->channels - context->alpha_channels; context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info)); if(context->alpha_channel_info == NULL) return psd_status_malloc_failed; memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info)); } for(i = 0; i < context->alpha_channels; i ++) { context->alpha_channel_info[i].identifier = psd_stream_get_int(context); } context->fill_alpha_channel_info = psd_true; break; // (Photoshop 6.0) Version Info // 4 bytes version, 1 byte hasRealMergedData, Unicode string: writer // name, Unicode string: reader name, 4 bytes file version. case 1057: context->version_info.version = psd_stream_get_int(context); context->version_info.has_real_merged_data = psd_stream_get_bool(context); context->version_info.writer_name_length = psd_stream_get_int(context); context->version_info.writer_name = (psd_ushort *)psd_malloc(2 * context->version_info.writer_name_length); if(context->version_info.writer_name == NULL) return psd_status_malloc_failed; psd_stream_get(context, (psd_uchar *)context->version_info.writer_name, 2 * context->version_info.writer_name_length); context->version_info.reader_name_length = psd_stream_get_int(context); context->version_info.reader_name = (psd_ushort *)psd_malloc(2 * context->version_info.reader_name_length); if(context->version_info.reader_name == NULL) return psd_status_malloc_failed; psd_stream_get(context, (psd_uchar *)context->version_info.reader_name, 2 * context->version_info.reader_name_length); context->version_info.file_version = psd_stream_get_int(context); context->fill_version_info = psd_true; break; // if you don't need the following image resource, // you can undef this macro in psd_config.h to reduce the parsing time ///////////////////////////////////////////////////////////////////////////////////////////// #ifdef PSD_GET_ALL_IMAGE_RESOURCE // Border information case 1009: // a fixed number (2 bytes real, 2 bytes fraction) for the border width context->border_info.border_width = psd_fixed_16_16_tofloat((psd_fixed_16_16)psd_stream_get_int(context)); // 2 bytes for border units (1 = inches, 2 = cm, 3 = points, 4 = picas, 5 = columns). context->border_info.border_units = (psd_units)psd_stream_get_short(context); psd_assert(context->border_info.border_units >= psd_units_inches && context->border_info.border_units <= psd_units_columns); context->fill_border_info = psd_true; break; // Print flags // A series of one-byte boolean values (see Page Setup dialog): labels, crop // marks, color bars, registration marks, negative, flip, interpolate, caption, // print flags. case 1011: context->print_flags.labels = psd_stream_get_bool(context); context->print_flags.crop_marks = psd_stream_get_bool(context); context->print_flags.color_bars = psd_stream_get_bool(context); context->print_flags.registration_marks = psd_stream_get_bool(context); context->print_flags.negative = psd_stream_get_bool(context); context->print_flags.flip = psd_stream_get_bool(context); context->print_flags.interpolate = psd_stream_get_bool(context); context->print_flags.caption = psd_stream_get_bool(context); context->print_flags.print_flags = psd_stream_get_bool(context); context->fill_print_flags = psd_true; break; // (Photoshop 4.0) Grid and guides information case 1032: // Version ( = 1) psd_assert(psd_stream_get_int(context) == 1); // Future implementation of document-specific grids (4 bytes horizontal, 4 bytes vertical). context->grid_guides.horz_grid = psd_stream_get_int(context); context->grid_guides.vert_grid = psd_stream_get_int(context); // Number of guide resource blocks (can be 0). context->grid_guides.guide_count = psd_stream_get_int(context); if(context->grid_guides.guide_count > 0) { //Location of guide in document coordinates. Since the guide is either vertical or horizontal, this only has to be one component of the coordinate. context->grid_guides.guide_coordinate = (psd_int *)psd_malloc(context->grid_guides.guide_count * 4); // Direction of guide. VHSelect is a system type of psd_uchar where 0 = vertical, 1 = horizontal. context->grid_guides.guide_direction = (psd_uchar *)psd_malloc(context->grid_guides.guide_count); if(context->grid_guides.guide_coordinate == NULL || context->grid_guides.guide_direction == NULL) { psd_free(context->grid_guides.guide_coordinate); context->grid_guides.guide_coordinate = NULL; psd_free(context->grid_guides.guide_direction); context->grid_guides.guide_direction = NULL; return psd_status_malloc_failed; } for(i = 0; i < context->grid_guides.guide_count; i ++) { context->grid_guides.guide_coordinate[i] = psd_stream_get_int(context); context->grid_guides.guide_direction[i] = psd_stream_get_char(context); } } context->fill_grid_and_guides_info = psd_true; break; // (Photoshop 5.0) Color samplers resource case 1038: // Version ( = 1) psd_assert(psd_stream_get_int(context) == 1); // Number of color samplers to follow. context->color_samplers.number_of_color_samplers = psd_stream_get_int(context); if (context->color_samplers.number_of_color_samplers > 0) { context->color_samplers.resource = (psd_color_samplers_resource *) psd_malloc(sizeof(psd_color_samplers_resource) * context->color_samplers.number_of_color_samplers); if (context->color_samplers.resource == NULL) return psd_status_malloc_failed; for (i = 0; i < context->color_samplers.number_of_color_samplers; i ++) { // The vertical and horizontal position of the point (4 bytes each). context->color_samplers.resource[i].vertical_position = psd_stream_get_int(context); context->color_samplers.resource[i].horizontal_position = psd_stream_get_int(context); // Color Space context->color_samplers.resource[i].color_space = psd_stream_get_short(context); } } context->fill_color_samplers = psd_true; break; // (Photoshop 6.0) Slices case 1050: // Version ( = 6) psd_assert(psd_stream_get_int(context) == 6); // Bounding rectangle for all of the slices: top, left, bottom, right of all the slices context->slices_resource.bounding_top = psd_stream_get_int(context); context->slices_resource.bounding_left = psd_stream_get_int(context); context->slices_resource.bounding_bottom = psd_stream_get_int(context); context->slices_resource.bounding_right = psd_stream_get_int(context); // Name of group of slices: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Number of slices to follow context->slices_resource.number_of_slices = psd_stream_get_int(context); context->slices_resource.slices_resource_block = (psd_slices_resource_block *)psd_malloc(context->slices_resource.number_of_slices * sizeof(psd_slices_resource_block)); if(context->slices_resource.slices_resource_block == NULL) return psd_status_malloc_failed; memset(context->slices_resource.slices_resource_block, 0, context->slices_resource.number_of_slices * sizeof(psd_slices_resource_block)); for(i = 0; i < context->slices_resource.number_of_slices; i ++) { context->slices_resource.slices_resource_block[i].id = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].group_id = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].origin = psd_stream_get_int(context); // NOTE: Only present if Origin = 1 if(context->slices_resource.slices_resource_block[i].origin == 1) context->slices_resource.slices_resource_block[i].associated_layer_id = psd_stream_get_int(context); // Name: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); context->slices_resource.slices_resource_block[i].type = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].left = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].top = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].right = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].bottom = psd_stream_get_int(context); // URL: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Target: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Message: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Alt Tag: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); context->slices_resource.slices_resource_block[i].cell_text_is_html = psd_stream_get_char(context); // Cell text: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); context->slices_resource.slices_resource_block[i].horizontal_alignment = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].veritcal_alignment = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].color = psd_argb_to_color(psd_stream_get_char(context), psd_stream_get_char(context), psd_stream_get_char(context), psd_stream_get_char(context)); } context->fill_slices_resource = psd_true; break; // (Photoshop 6.0) URL List case 1054: // 4 byte count of URLs context->url_list.number_of_urls = psd_stream_get_int(context); if (context->url_list.number_of_urls > 0) { context->url_list.items = (psd_url_list_item *)psd_malloc(sizeof(psd_url_list_item) * context->url_list.number_of_urls); if (context->url_list.items == NULL) return psd_status_malloc_failed; memset(context->url_list.items, 0, sizeof(psd_url_list_item) * context->url_list.number_of_urls); } for (i = 0; i < context->url_list.number_of_urls; i ++) { // followed by 4 byte long context->url_list.items[i].tag = psd_stream_get_int(context); // 4 byte ID context->url_list.items[i].ID = psd_stream_get_int(context); // Unicode string for each count. context->url_list.items[i].name_length = psd_stream_get_int(context); context->url_list.items[i].name = (psd_ushort *)psd_malloc(2 * context->url_list.items[i].name_length); if (context->url_list.items[i].name == NULL) return psd_status_malloc_failed; psd_stream_get(context, (psd_uchar *)context->url_list.items[i].name, 2 * context->url_list.items[i].name_length); } context->fill_url_list = psd_true; break; // (Photoshop 7.0) EXIF data 1 case 1058: // (Photoshop 7.0) EXIF data 3 // http://www.pima.net/standards/it10/PIMA15740/exif.htm case 1059: // avoid to get the exif data for twice psd_assert(context->fill_exif_data == psd_false); # ifdef PSD_INCLUDDE_LIBEXIF buffer = (psd_uchar *)psd_malloc(sizeofdata + sizeof(ExifHeader)); if (buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer + sizeof(ExifHeader), sizeofdata); memcpy(buffer, ExifHeader, sizeof(ExifHeader)); context->exif_data = (psd_uchar *)exif_data_new_from_data(buffer, sizeofdata + sizeof(ExifHeader)); psd_free(buffer); context->fill_exif_data = psd_true; # else // ifdef PSD_INCLUDDE_LIBEXIF context->exif_data = (psd_uchar *)psd_malloc(sizeofdata); if (context->exif_data == NULL) return psd_status_malloc_failed; psd_stream_get(context, context->exif_data, sizeofdata); context->exif_data_length = sizeofdata; context->fill_exif_data = psd_true; # endif // ifdef PSD_INCLUDDE_LIBEXIF break; // (Photoshop 7.0) XMP metadata // File info as XML description // http://Partners.adobe.com/asn/developer/xmp/main.html case 1060: # ifdef PSD_INCLUDE_LIBXML buffer = (psd_uchar *)psd_malloc(sizeofdata); if (buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer, sizeofdata); context->XMP_metadata = (psd_uchar *)xmlParseMemory(buffer, sizeofdata); psd_free(buffer); context->fill_XMP_metadata = psd_true; # else // ifdef PSD_INCLUDE_LIBXML context->XMP_metadata = (psd_uchar *)psd_malloc(sizeofdata); if (context->XMP_metadata == NULL) return psd_status_malloc_failed; psd_stream_get(context, context->XMP_metadata, sizeofdata); context->XMP_metadata_length = sizeofdata; context->fill_XMP_metadata = psd_true; # endif // ifdef PSD_INCLUDE_LIBXML break; // (Photoshop 7.0) Print scale case 1062: // 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined). context->print_scale.style = psd_stream_get_short(context); psd_assert(context->print_scale.style >= psd_print_centered && context->print_scale.style <= psd_print_user_defined); // 4 bytes x location (floating point). context->print_scale.x_location = psd_stream_get_float(context); // 4 bytes y location (floating point). context->print_scale.y_location = psd_stream_get_float(context); // 4 bytes scale (floating point) context->print_scale.scale = psd_stream_get_float(context); context->fill_print_scale = psd_true; break; // (Photoshop CS) Pixel Aspect Ratio case 1064: // 4 bytes (version = 1) psd_assert(psd_stream_get_int(context) == 1); // 8 bytes double, x / y of a pixel context->pixel_aspect_ratio = psd_stream_get_double(context); break; // Print flags information case 10000: // 2 bytes version ( = 1) psd_assert(psd_stream_get_short(context) == 1); // 1 byte center crop marks context->print_flags_info.center_crop = psd_stream_get_bool(context); // 1 byte ( = 0) psd_assert(psd_stream_get_char(context) == 0); // 4 bytes bleed width value context->print_flags_info.value = psd_stream_get_int(context); // 2 bytes bleed width scale context->print_flags_info.scale = psd_stream_get_short(context); context->fill_print_flags_info = psd_true; break; #endif // ifdef PSD_GET_ALL_IMAGE_RESOURCE ///////////////////////////////////////////////////////////////////////////////////////////// default: #ifdef PSD_GET_PATH_RESOURCE // Photoshop stores its paths as resources of type 8BIM, with IDs in the range 2000 // through 2998. if(ID >= 2000 && ID <= 2998) { psd_get_path(context, sizeofdata); } // If the file contains a resource of type 8BIM with an ID of 2999, then this resource // contains a Pascal¨Cstyle string containing the name of the clipping path to use with this // image when saving it as an EPS file??? else if(ID == 2999) { // we don't find any files includes the name of the clipping path. psd_assert(0); } else #endif // ifdef PSD_GET_PATH_RESOURCE { psd_stream_get_null(context, sizeofdata); } break; } // Filler psd_stream_get_null(context, prev_stream_pos + sizeofdata - context->stream.current_pos); } } else { return psd_status_resource_signature_error; } } return psd_status_done; }
void imFileFormatJPEG::iReadExifAttrib(unsigned char* data, int data_length, imAttribTable* attrib_table) { ExifData* exif = exif_data_new_from_data(data, data_length); if (!exif) return; void* value = NULL; int c, value_size = 0; ExifByteOrder byte_order = exif_data_get_byte_order(exif); for (int ifd = 0; ifd < EXIF_IFD_COUNT; ifd++) { if (ifd == EXIF_IFD_1 || ifd == EXIF_IFD_INTEROPERABILITY) // Skip thumbnail and interoperability continue; ExifContent *content = exif->ifd[ifd]; if (content && content->count) { for (int j = 0; j < (int)content->count; j++) { ExifEntry *entry = content->entries[j]; int type = 0; const char* name = exif_tag_get_name_in_ifd(entry->tag, (ExifIfd)ifd); if (!name) continue; if (value_size < (int)entry->size) { value = realloc(value, entry->size); value_size = entry->size; } int format_size = exif_format_get_size(entry->format); if (entry->tag == EXIF_TAG_RESOLUTION_UNIT) { int res_unit = (int)exif_get_short (entry->data, byte_order); if (res_unit == 2) attrib_table->Set("ResolutionUnit", IM_BYTE, -1, "DPI"); else if (res_unit == 3) attrib_table->Set("ResolutionUnit", IM_BYTE, -1, "DPC"); continue; } switch (entry->format) { case EXIF_FORMAT_UNDEFINED: case EXIF_FORMAT_ASCII: case EXIF_FORMAT_SBYTE: case EXIF_FORMAT_BYTE: { type = IM_BYTE; imbyte *bvalue = (imbyte*)value; for (c = 0; c < (int)entry->components; c++) bvalue[c] = entry->data[c]; } break; case EXIF_FORMAT_SSHORT: { type = IM_SHORT; short *svalue = (short*)value; for (c = 0; c < (int)entry->components; c++) svalue[c] = exif_get_short(entry->data + format_size * c, byte_order); } break; case EXIF_FORMAT_SHORT: { type = IM_USHORT; imushort *usvalue = (imushort*)value; for (c = 0; c < (int)entry->components; c++) usvalue[c] = exif_get_short(entry->data + format_size * c, byte_order); } break; case EXIF_FORMAT_LONG: { type = IM_INT; int *ivalue = (int*)value; for (c = 0; c < (int)entry->components; c++) ivalue[c] = (int)exif_get_long(entry->data + format_size * c, byte_order); } break; case EXIF_FORMAT_SLONG: { type = IM_INT; int *ivalue = (int*)value; for (c = 0; c < (int)entry->components; c++) ivalue[c] = (int)exif_get_slong(entry->data + format_size * c, byte_order); } break; case EXIF_FORMAT_RATIONAL: { ExifRational v_rat; type = IM_FLOAT; float *fvalue = (float*)value; for (c = 0; c < (int)entry->components; c++) { v_rat = exif_get_rational(entry->data + format_size * c, byte_order); fvalue[c] = (float)v_rat.numerator / (float)v_rat.denominator; } } break; case EXIF_FORMAT_SRATIONAL: { ExifSRational v_srat; type = IM_FLOAT; float *fvalue = (float*)value; for (c = 0; c < (int)entry->components; c++) { v_srat = exif_get_srational(entry->data + format_size * c, byte_order); fvalue[c] = (float)v_srat.numerator / (float)v_srat.denominator; } } break; case EXIF_FORMAT_FLOAT: // defined but unsupported in libEXIF case EXIF_FORMAT_DOUBLE: // defined but unsupported in libEXIF break; } attrib_table->Set(name, type, entry->components, value); } } } if (value) free(value); exif_data_free(exif); }
static int write_exif( Write *write ) { unsigned char *data; size_t data_length; unsigned int idl; #ifdef HAVE_EXIF ExifData *ed; /* Either parse from the embedded EXIF, or if there's none, make * some fresh EXIF we can write the resolution to. */ if( im_header_get_typeof( write->in, IM_META_EXIF_NAME ) ) { if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, (void *) &data, &data_length ) ) return( -1 ); if( !(ed = exif_data_new_from_data( data, data_length )) ) return( -1 ); } else ed = exif_data_new(); /* Update EXIF resolution from VIPS. */ if( set_exif_resolution( ed, write->in ) ) { exif_data_free( ed ); return( -1 ); } /* Reserialise and write. exif_data_save_data() returns an int for some * reason. */ exif_data_save_data( ed, &data, &idl ); if( !idl ) { im_error( "im_jpeg2vips", "%s", _( "error saving EXIF" ) ); exif_data_free( ed ); return( -1 ); } data_length = idl; #ifdef DEBUG printf( "im_vips2jpeg: attaching %zd bytes of EXIF\n", data_length ); #endif /*DEBUG*/ exif_data_free( ed ); jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length ); free( data ); #else /*!HAVE_EXIF*/ /* No libexif ... just copy the embedded EXIF over. */ if( im_header_get_typeof( write->in, IM_META_EXIF_NAME ) ) { if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, (void *) &data, &data_length ) ) return( -1 ); #ifdef DEBUG printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", data_length ); #endif /*DEBUG*/ jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length ); } #endif /*!HAVE_EXIF*/ return( 0 ); }
/* Examine the metadata tags on the image and update the EXIF block. */ int vips__exif_update( VipsImage *image ) { unsigned char *data; size_t length; unsigned int idl; ExifData *ed; /* Either parse from the embedded EXIF, or if there's none, make * some fresh EXIF we can write the resolution to. */ if( vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) ) { if( vips_image_get_blob( image, VIPS_META_EXIF_NAME, (void *) &data, &length ) ) return( -1 ); if( !(ed = exif_data_new_from_data( data, length )) ) return( -1 ); } else { ed = exif_data_new(); exif_data_set_option( ed, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION ); exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED ); exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL ); /* Create the mandatory EXIF fields with default data. */ exif_data_fix( ed ); } /* Update EXIF tags from the image metadata. */ vips_exif_update( ed, image ); /* Update EXIF resolution from the vips image header. */ if( vips_exif_resolution_from_image( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF image dimensions from the vips image header. */ if( vips_exif_set_dimensions( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF orientation from the vips image header. */ if( vips_exif_set_orientation( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update the thumbnail. */ if( vips_exif_set_thumbnail( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Reserialise and write. exif_data_save_data() returns an int for some * reason. */ exif_data_save_data( ed, &data, &idl ); if( !idl ) { vips_error( "exif", "%s", _( "error saving EXIF" ) ); exif_data_free( ed ); return( -1 ); } length = idl; #ifdef DEBUG printf( "vips__exif_update: generated %zd bytes of EXIF\n", length ); #endif /*DEBUG*/ vips_image_set_blob( image, VIPS_META_EXIF_NAME, (VipsCallbackFn) vips_free, data, length ); exif_data_free( ed ); return( 0 ); }
static int analyzer_jpeg_pload_analyze(struct analyzer *analyzer, struct analyzer_pload_buffer *pload, void *buffer, size_t buff_len) { struct analyzer_jpeg_pload_priv *priv = analyzer_pload_buffer_get_priv(pload); if (!priv) { priv = malloc(sizeof(struct analyzer_jpeg_pload_priv)); if (!priv) { pom_oom(sizeof(struct analyzer_jpeg_pload_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct analyzer_jpeg_pload_priv)); priv->pload_buff = buffer; priv->pload_buff_len = buff_len; // Setup error handler struct jpeg_error_mgr *jerr = malloc(sizeof(struct jpeg_error_mgr)); if (!jerr) { free(priv); pom_oom(sizeof(struct jpeg_error_mgr)); return POM_ERR; } memset(jerr, 0, sizeof(struct jpeg_error_mgr)); priv->cinfo.err = jpeg_std_error(jerr); priv->cinfo.err->error_exit = analyzer_jpeg_lib_error_exit; // Allocate the decompressor jpeg_create_decompress(&priv->cinfo); priv->cinfo.client_data = priv; #ifdef HAVE_LIBEXIF // Save APP1 jpeg_save_markers(&priv->cinfo, JPEG_APP0 + 1, 0xFFFF); #endif // Allocate the source struct jpeg_source_mgr *src = malloc(sizeof(struct jpeg_source_mgr)); if (!src) { free(priv->cinfo.err); pom_oom(sizeof(struct jpeg_source_mgr)); jpeg_destroy_decompress(&priv->cinfo); free(priv); return POM_ERR; } memset(src, 0, sizeof(struct jpeg_source_mgr)); src->init_source = analyzer_jpeg_lib_init_source; src->fill_input_buffer = analyzer_jpeg_lib_fill_input_buffer; src->skip_input_data = analyzer_jpeg_lib_skip_input_data; src->resync_to_restart = jpeg_resync_to_restart; src->term_source = analyzer_jpeg_lib_term_source; priv->cinfo.src = src; analyzer_pload_buffer_set_priv(pload, priv); } else { priv->pload_buff = buffer; priv->pload_buff_len = buff_len; } if (priv->jpeg_lib_pos >= buff_len) // Nothing more to process return POM_OK; int res = POM_OK; if (!setjmp(priv->jmp_buff)) { if (priv->jpeg_lib_pos) { // It's not garanteed that buffer points to the // same memory area after each call, so we reset it here priv->cinfo.src->next_input_byte = buffer + priv->jpeg_lib_pos; } if (jpeg_read_header(&priv->cinfo, TRUE) == JPEG_SUSPENDED) return POM_OK; // Headers are incomplete struct data *data = analyzer_pload_buffer_get_data(pload); PTYPE_UINT16_SETVAL(data[analyzer_jpeg_pload_width].value, priv->cinfo.image_width); data_set(data[analyzer_jpeg_pload_width]); PTYPE_UINT16_SETVAL(data[analyzer_jpeg_pload_height].value, priv->cinfo.image_height); data_set(data[analyzer_jpeg_pload_height]); debug_jpeg("JPEG read header returned %u, image is %ux%u", res, priv->cinfo.image_width, priv->cinfo.image_height); #ifdef HAVE_LIBEXIF // Parse the exif data jpeg_saved_marker_ptr marker; for (marker = priv->cinfo.marker_list; marker && marker->marker != JPEG_APP0 + 1; marker = marker->next); if (marker) { ExifData *exif_data = exif_data_new_from_data(marker->data, marker->data_length); if (!exif_data) { pomlog(POMLOG_DEBUG "Unable to parse EXIF data"); } exif_data_foreach_content(exif_data, analyzer_jpeg_exif_content_process, pload); exif_data_free(exif_data); } #endif analyzer_pload_buffer_set_state(pload, analyzer_pload_buffer_state_analyzed); } else { pomlog(POMLOG_DEBUG "Error while parsing JPEG headers"); res = POM_ERR; } free(priv->cinfo.err); free(priv->cinfo.src); jpeg_destroy_decompress(&priv->cinfo); free(priv); analyzer_pload_buffer_set_priv(pload, NULL); return res; }