diff options
author | Scott Fritzinger <scottf@unr.edu> | 2000-06-21 16:34:31 +0000 |
---|---|---|
committer | Scott Fritzinger <scottf@unr.edu> | 2000-06-21 16:34:31 +0000 |
commit | c9b2d8cb1ef55781b2ee1582f40b4ce54d5e95ff (patch) | |
tree | 74887108088a7163831156e507ec16288a6003c0 /libgphoto2/exif.c | |
parent | 77e0238b746f7615c67151f387572c8fe19cbaa8 (diff) | |
download | libgphoto2-c9b2d8cb1ef55781b2ee1582f40b4ce54d5e95ff.tar.gz |
added exif lib
git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@567 67ed7778-7388-44ab-90cf-0a291f65f57c
Diffstat (limited to 'libgphoto2/exif.c')
-rw-r--r-- | libgphoto2/exif.c | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/libgphoto2/exif.c b/libgphoto2/exif.c new file mode 100644 index 000000000..410fa8b40 --- /dev/null +++ b/libgphoto2/exif.c @@ -0,0 +1,692 @@ +#include "exif.h" +/* + EXIF file library for GPHOTO package + Copyright (C) 1999 Matthew G. Martin + + Generic gphoto implementation and extension by Edouard Lafargue. + + Tag descriptions and an ever-increasing number of structural details + Taken from "exifdump.py" by Thierry Bousch <bousch@topo.math.u-psud.fr> + + Thanks to Paul Wood <pwood@cs.bris.ac.uk> for sub-ifd parsing. + + This program 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. + + This program 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 General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/*********** + * Conventions: + * + * "tag number" (tagnum) : ID number of a tag (see list just below). + * "tag index" (tagind) : index number of a tag inside an EXIF structure + * (for example, tag number one if the first tag, etc..) + * "tag name" (tagname) : name of the tag as defined in the EXIF standard. + */ + +int exif_debug=0; + +/* Size of various tags */ +static int exif_sizetab[13]={ + 0,1,1,2,4,8,1,1,2,4,8,4,8 +}; + + +/**************************************************************/ +/* Utility functions: get fields in little-endian format, */ +/* initialize data structures, etc. */ +/**************************************************************/ + +/** + * Convert to Intel-Endian number. + * + * data : pointer to the data to convert + * size : size of the data to convert + * + * Returns : the value + */ +long exif_get_lilend(unsigned char *data, int size){ + long total; + + total=0; + for (--size;size>=0;size--){ + total<<=8; + total+=data[size]; + }; + + return(total); +}; + +/** + * Convert to signed Intel-Endian number + */ +long exif_get_slilend(unsigned char *data, int size){ + long total; + long unsigned mask=1<<(size*8-1); + + total=0; + for (--size;size>=0;size--){ + total<<=8; + total+=data[size]; + }; + + if (total&mask) total-=mask; + + return(total); +}; + + +/** + * Return "value" of directory entry at tagind + */ +int gpe_theval(unsigned char *data,int tagind){ + return(exif_get_lilend(data+tagind*12+10,4)); +}; + + +/** + * Set the "value" of directory entry at tagind. + */ +void gpe_setval(unsigned char *data,int tagind,long newval){ + int i; + for (i=0;i<4;i++) data[tagind*12+10+i]=0xff&(newval>>(i*8)); + if (gpe_getvalue(data,tagind)!=newval) + printf("Setptr: error %d inst %ld\n",gpe_theval(data,tagind),newval); +}; + + +/** + * Returns the offset of the next IFD entry after + * offset "num". + * + * exif : exif data + * num : start offset + * + * returns: offset of next IFD. + */ +long exif_next_ifd(unsigned char *exif,int num){ + + int offset=(exif[num]+(exif[num+1]<<8))*12+num+2; + if (exif_debug) printf("next_ifd,offset=%d\n",offset); + return(exif_get_lilend(exif+offset,4)); + +} + + +/** + * Parse an exif data structure and initialise the "exifparser". + * + * exifdat : the exif data + * + * returns : size of the rest of the file. + * -1 if failed. + */ +int exif_parse_data(exifparser *exifdat) { + long offset=0; + ExifData tagdat; + + exif_debug=1; + /* Check that it's not already preparsed */ + // Doesn't work if we don't initialize this first... + // if (exifdat->preparsed) return 0; + if (exif_debug) printf("Parsing exif structure\n"); + + /* First, verify that we really have an EXIF file */ + /* Note: maybe we could also generalize to TIFF... */ + if ((strncmp("Exif",exifdat->header+6,4)) || + strncmp("\377\330\377\341",exifdat->header,4)){ + fprintf(stderr,"Not exif data\n"); + return(-1); + } + + exifdat->exiflen = exifdat->header[5] + (exifdat->header[4]<<8) - 8; + if (exif_debug) printf("Exif length is %ld\n",exifdat->exiflen); + + /* Count the number of IFD's and register their pointers. */ + exifdat->endian=0; + if (exifdat->data[0]!='I') { + exifdat->endian=1; + printf("%c,Intel-Endian format only supported right now!\n", + exifdat->data[0]); + return(-1); + }; + + offset=exif_get_lilend(exifdat->data+4,4); /*Get offset of first IFD */ + exifdat->ifdcnt=-1; + + /*Step through each IFD */ + do { + exifdat->ifdcnt++; + exifdat->ifds[exifdat->ifdcnt] = exifdat->data+offset; + exifdat->ifdtags[exifdat->ifdcnt] = exif_get_lilend(exifdat->data+offset,2); + } while (offset=exif_next_ifd(exifdat->data,offset)); + + exifdat->ifdcnt++; + + /* Look for the EXIF IFD */ + exifdat->preparsed=1; /* Indicate that our exifparser is initialized */ + /* Has to be done before calling exif_get_field */ + if ( ! exif_get_field( EXIF_ExifOffset, 0, exifdat, &tagdat)) { + printf("No EXIF information stored in this image\n"); + } else { + if (exif_debug) printf("Offset to the Exif subIFD is %d\n",tagdat.intval); + exifdat->ifds[exifdat->ifdcnt] = exifdat->data + tagdat.intval; + exifdat->ifdtags[exifdat->ifdcnt] = exif_get_lilend(exifdat->data+tagdat.intval,2); + exifdat->ifdcnt++; + } + + if (exif_debug) printf("Finished parsing exif structure\n"); + return exifdat->exiflen; +} + +/**************************************************************/ +/* tag-level functions: get/set tags in the EXIF structure */ +/**************************************************************/ + +/** + * Returns the value of a field, identified by its tag and the IFD. + * + * Specifying an IFD of '-1' will return the first occurence of + * the tag in the first IFD where it finds it. + * + * tag_number: tag that identifies the field + * ifd : image file directory where to look for that tag + * exifdata : pointer to the EXIF data + * tag_data : filled by this function, contains the actual data + * + * returns : 1 on success, 0 on failure + */ +int exif_get_field( int tag_number, int ifd, exifparser *exifdata, ExifData *tag_data) { + int numtags,i,j,tag, sign; + char *ifdp, *offset, *data; + + // Sanity check first: + exif_debug=1; + if ( !exifdata->preparsed ) { + if(exif_parse_data(exifdata) < 0) + return 0; + } + + if (ifd == -1) { + for (i = 0; i < exifdata->ifdcnt+1; i++){ + if (exif_debug) printf("Searching in IFD %d\n",i); + if ( exif_get_field(tag_number, i, exifdata, tag_data) == 1) + return 1; + } + return 0; // We did not find it. + } else { + + // Find tag in a specific IFD + ifdp = exifdata->ifds[ifd]; + numtags=exif_get_lilend(ifdp,2); // How many tags in the IFD + if (exif_debug) printf("exif_get_field: %d tags in ifd %d\n",numtags, ifd); + i=-1; + do { + i++; + tag = exif_get_lilend(ifdp+i*12+2, 2); // Get the tag ID + // if (exif_debug) fprintf(stderr,"Found tag %d \n",tag); + } while ((i < numtags) && (tag != tag_number)); + + if(tag != tag_number) { + if (exif_debug) fprintf(stderr,"Tag %d not found\n",tag_number); + return 0; + } + + ifdp = ifdp+i*12+2; // Place pointer at TAG type position + tag_data->tag = tag; + tag_data->type = exif_get_lilend(ifdp+2,2); /* tag type */ + tag_data->size = exif_sizetab[tag_data->type]*exif_get_lilend(ifdp+4,4); + if (exif_debug) printf("(%d bytes) ",tag_data->size); + + ifdp += 8; // place pointer at beginning of the contents of the field + + /* Data types smaller than 4 bytes are stored directly in + the IFD field value. Otherwise, that value is an offset + to the real data */ + if (tag_data->size > 4) { + ifdp = exifdata->data + exif_get_lilend(ifdp,4); + } + + /* Last operation, get the data itself: */ + /* Allocate memory, and copy the data */ + data=malloc(tag_data->size); + if (data==NULL){ + fprintf(stderr,"exif_get_field: could not malloc\n"); + return 0; + }; + + if (tag_data->type == EXIF_ASCII) { + memcpy(data,ifdp,tag_data->size); // Normally, the exif data includes a terminating 0 + tag_data->data = data; + if(exif_debug) printf("\"%s\"",data); + } else { + for (i = 0; i < tag_data->size; i += exif_sizetab[tag_data->type]) { + //if (exif_debug) fprintf(stderr,"."); + if ( tag_data->type % 5 ) { + memcpy(data+i,ifdp+i,exif_sizetab[tag_data->type]); + } else { // Fraction + tag_data->num =exif_get_lilend(ifdp+i,4); + tag_data->den =exif_get_lilend(ifdp+i+4,4); + + if(exif_debug) printf("%d/%d=%.3g ",tag_data->num, + tag_data->den, + (tag_data->den==0)?0:(1.0*tag_data->num/tag_data->den)); + } + } + // If the value can be put into an int, save the trouble and store + // it into "intval" right away... + if(tag_data->type != EXIF_ASCII && tag_data->type != EXIF_RATIONAL && + tag_data->type != EXIF_UNDEFINED && tag_data->type != EXIF_SRATIONAL) { + tag_data->intval = exif_get_lilend(data,exif_sizetab[tag_data->type]); + if(exif_debug) printf("'%d'",tag_data->intval); + } + tag_data->data = data; // Save the data field + } + if (exif_debug) printf("\n"); /*end of this tag */ + return 1; + } + return 0; +} + +/** + * Gets an ASCII tag. + * + * Specifying an IFD of '-1' will return the first occurence of + * the tag in the first IFD where it finds it. + * + * tag_number: tag that identifies the field + * ifd : image file directory where to look for that tag + * exifdata : pointer to the EXIF data + * + * returns : pointer to the tag value. + */ +char * exif_get_ascii_field( int tag_number, int ifd, exifparser *exifdat) { + ExifData tagdat; + + if (exif_parse_data(exifdat)<0) return NULL; + + if ( ! exif_get_field( tag_number, ifd, exifdat, &tagdat)) { + if (exif_debug) printf("No comment field in this image\n"); + return NULL; + } else { + return tagdat.data; + } +} + +/** + * Gets a numeric tag. + * + * Specifying an IFD of '-1' will return the first occurence of + * the tag in the first IFD where it finds it. + * + * tag_number: tag that identifies the field + * ifd : image file directory where to look for that tag + * exifdata : pointer to the EXIF data + * + * returns : tag value. + */ +int exif_get_int_field( int tag_number, int ifd, exifparser *exifdat) { + ExifData tagdat; + int tmp; + + if (exif_parse_data(exifdat)<0) return 0; + + if ( ! exif_get_field( tag_number, ifd, exifdat, &tagdat)) { + if (exif_debug) printf("Field not found in this image.\n"); + return 0; + } else { + tmp = tagdat.intval; + free(tagdat.data); // ??? + return tmp; + } + return 0; + +} + + +/** + * Returns the name of a given tag number + */ +char *exif_get_tagname(int tag_number) { + int i=-1; + do { + i++; + if (tagnames[i].num == tag_number) return (tagnames[i].desc); + } while (tagnames[i].num); + return("Unknown"); +} + +/**************************************************************/ +/* Higher-level functions: get/set logical entities, such as */ +/* comments, thumbnail, etc. */ +/**************************************************************/ + +/** + * Gets the comment field if it exists. + */ +int exif_get_comment(exifparser *exifdat, char *comment) { + ExifData tagdat; + + if (exif_parse_data(exifdat)<0) return 0; + + /* User Comment is in IFD number 2 */ + if ( ! exif_get_field( EXIF_UserComment, 2, exifdat, &tagdat)) { + if (exif_debug) printf("No comment field in this image\n"); + return 0; + } else { + comment = tagdat.data; + return tagdat.size; + } + return 0; +} + +/** + * Sets the comment field if it exists. + */ +int exif_set_comment(exifparser *exifdat, char *comment) { + return 0; +} + +/** + * Gets the thumbnail of an EXIF image. + * + * The returned thumbnail might be in JPEG or TIFF format. + */ +unsigned char *exif_get_thumbnail(exifparser *exifdat) { + char *tmpstr[32]; + unsigned char *imagedata,*exifimg,*newimg,*curptr; + unsigned int exiflen,entry; + long dataptr,dsize,tag,datvec,size,tmp; + int i,j; + + exif_debug=1; + if (exif_parse_data(exifdat)<0) return(NULL); + + newimg=malloc(exifdat->exiflen); + if (newimg==NULL){ + fprintf(stderr,"exif_get_thumbnail: could not malloc\n"); + return(NULL); + }; + + /* Copy header*/ + memcpy(newimg,exifdat->data,8); + curptr=newimg+8; + + if (exif_debug) { + ExifData owner; + char *comment; + printf("Decoding EXIF fields in thumbnail\n"); + exif_get_field( EXIF_Model, -1, exifdat, &owner); + printf("Camera model: %s\n",owner.data); + printf("Comment for this picture (%d chars)",exif_get_comment( exifdat, comment)); + //printf(" -> %s\n",comment); Not OK on Linux + exif_get_field( EXIF_SubjectDistance, 2, exifdat, &owner); + // dump_exif(exifdat); + }; + + /* Skip to Thumbnail image data */ + if(exifdat->ifdcnt<2) { + if (exif_debug) { + fprintf(stderr,"Too few ifds, doesn't look right. Giving up\n"); + }; + return(NULL); /* Things don't look right...*/ + }; + + /* Jump to thumbnail image data */ + exifimg=exifdat->ifds[1]; + + /* Copy number of entries */ + memcpy(curptr,exifimg,2); + curptr+=2; + + entry=exif_get_lilend(exifimg,2); + + if (exif_debug) printf("Entry is %d \n",entry); + + /* See if thumb is a JPEG */ + tmp=gpe_getintval(exifimg,EXIF_JPEGInterchangeFormat); /*imagedata start*/ + if (tmp>0) { /* jpeg image */ + if (exif_debug) fprintf(stderr,"Found jpeg thumb data\n"); + dsize=gpe_getintval(exifimg,EXIF_JPEGInterchangeFormatLength); + if (dsize==-1){ + fprintf(stderr,"No Jpeg size tag for thumbnail, skipping\n"); + return(NULL); + }; + imagedata=exifdat->data+tmp; + memcpy(newimg,imagedata,dsize); + return(newimg); + }; + + /* Try a TIFF */ + tmp=gpe_getintval(exifimg,EXIF_StripOffsets); /*imagedata start*/ + if (tmp==-1) { + fprintf(stderr,"gpe_get_thumbnail: Tiff or jpeg data not found, skipping\n"); + return(NULL); + }; + imagedata=exifdat->data+tmp; + + dataptr=gpe_getintval(exifimg,EXIF_StripByteCounts); /* imagedata size */ + if (dataptr==-1) { + printf("Split two\n"); + return(NULL); + }; + + if (exif_debug) printf("Imagedata size is %ld bytes\n",dataptr); + + for (i=0;i<entry;i++){ + dsize=gpe_datsize(exifimg,i); + tag=gpe_tagnum(exifimg,i); + + /* + if (exif_debug) printf("Datsize %d (tag=%ld) is %ld\n",i,tag,dsize); + */ + + if (tag==EXIF_StripOffsets) { + gpe_setval(exifimg,i,12*entry+14); /* set to end of directory */ + memcpy(curptr,exifimg+12*i+2,12); + curptr+=12; + } + else { + if (dsize<5){ + /* Just copy the field if small */ + memcpy(curptr,exifimg+12*i+2,12); + curptr+=12; + } + else { + datvec=gpe_getvalue(exifimg,i); + gpe_setval(exifimg,i,dataptr+12*entry+14); + for (j=0;j<dsize;j++) imagedata[dataptr++]=exifdat->data[datvec+j]; + memcpy(curptr,exifimg+12*i+2,12); + curptr+=12; + }; + }; + }; + memcpy(curptr,exifimg+12*entry+10,4); /* Write 4 zero bytes */ + curptr+=4; + memcpy(curptr,imagedata,dataptr);/* ? */ + curptr+=dataptr; + return newimg; + + return 0; +} + +/** + * Return tag number of directory entry at tag index + * + * data : pointer to EXIF data, aligned to the beginning of an IFD + * tagind : index of the tag within the IFD + * + * returns: tag ID number + */ +int gpe_tagnum( char *data,int tagind){ + return(exif_get_lilend(data+tagind*12+2,2)); +}; + +/** + * Get the value of a tag as an integer. + * only works for 4 byte values. + * + * data : exif data, aligned to the beginning of an IFD. + * tagnum : tag number. + * + * returns: -1 if the tag is not found. + */ +int gpe_getintval(unsigned char *data, int tagnum) { + int numtags,i,tag,tagtype; + + numtags=exif_get_lilend(data,2); + + if (exif_debug) printf("getval:%d tags\n",numtags); + + i=-1; + do { + i++; + tag=exif_get_lilend(data+i*12+2,2); + } while ((i<numtags) && (tag!=tagnum)); + + if(tag!=tagnum) { + if (exif_debug) fprintf(stderr,"Tag %d not found\n",tagnum); + return(-1); + }; + + tagtype=exif_get_lilend(data+i*12+4,2); /* tag type */ + + return(exif_get_lilend(data+i*12+10,exif_sizetab[tagtype-1])); +}; + +/** + * Return "value" of directory entry at tagind + * + * data : exif data + * tagind: tag index + */ +int gpe_getvalue(unsigned char *data,int tagind){ + return(exif_get_lilend(data+tagind*12+10,4)); +}; + + +/** + * Add all tag names and values in an ifd to an array of strings for gphoto + */ +//int gpe_exif_add_all(exifparser *exifdata,int ifdnum,char **datastrings){ +// int i; +// for (i=0;i<exifdata->ifdtags[ifdnum];i++){ +// togphotostr(exifdata,ifdnum,i,datastrings+i*2,datastrings+i*2+1); +// /* printf("%s = %s\n",datastrings[i*2],datastrings[i*2+1]);*/ +// }; +//}; + + + +int gpe_dump_ifd(int ifdnum,exifparser *exifdata,char **allpars){ + int i,j,tag,numtags,tagtype,count,typelen, value,tmp1,tmp2; + char tmpstr[256]; + unsigned char* thistag; + unsigned char* thedata; + unsigned char* thisisd; + char tmpdat[200]; + char** name; + + + thisisd=exifdata->ifds[ifdnum]; + numtags=exif_get_lilend(thisisd,2); + printf("has %d tags ----------------------\n",numtags); + for (i=0;i<numtags;i++){ + + thistag=thisisd+i*12+2; /* pointer to data for this tag */ + + tag=exif_get_lilend(thistag,2); /* tag identifier */ + tagtype=exif_get_lilend(thistag+2,2); /* tag type */ + count = exif_get_lilend(thistag+4, 4); /* how many */ + typelen=exif_sizetab[tagtype];/* length of this type */ + + if (exif_debug) printf("(%dX) ",count); + + thedata=thistag+8; + if (count*typelen > 4) /* find it in a data block elsewhere */ + thedata = exifdata->data+exif_get_lilend(thistag+8, 4); + + printf("Tag 0x%X %s = ",tag,exif_get_tagname(tag)); + + if (tagtype==2) { + /* Do an ASCII tag */ + strncpy(tmpstr,thedata,count+1); + tmpstr[count+1]='\0'; + printf("'%s'",tmpstr); + } + else for (j=0;j<count;j++) { + + if ((tagtype==5)||(tagtype==10)) {/* Fractional */ + tmp1=exif_get_slilend(thedata+j*typelen,4); + tmp2=exif_get_slilend(thedata+4+j*typelen,4); + printf("%d/%d=%.3g ",tmp1,tmp2,(tmp2==0)?0:(1.0*tmp1/tmp2)); + } + else { + value =exif_get_lilend(thedata+j*typelen,typelen); + printf("%d ",value); + }; + + }; + + printf("\n"); /*end of this tag */ + +/* Print SubIfd tags */ + if ( tag == 0x8769 ) { + printf("Exif SubIFD at offset %d\n", value ); + exifdata->ifds[exifdata->ifdcnt] = exifdata->data+value; + exifdata->ifds[exifdata->ifdcnt]; + exifdata->ifdtags[exifdata->ifdcnt]=exif_get_lilend(exifdata->data+value,2); + exifdata->ifdcnt++; + +} +/***/ + + }; +}; + + + +/* Walk through EXIF structure, printing values */ +int gpe_dump_exif(exifparser *exifdata){ + int i,tag; + unsigned char* thisifd; + + if (!exifdata->preparsed) + if (stat_exif(exifdata)) return(-1); + + for (i=0;i<exifdata->ifdcnt;i++){ + switch (i) { + case 0: + printf("IFD %d, %s ",i,"Main Image"); + break; + case 1: + printf("IFD %d, %s ",i,"Thumbnail"); + break; + case 2: + printf("IFD %d, %s ",i,"Sub IFD"); + break; + + } + + + gpe_dump_ifd(i,exifdata,NULL); + }; +}; + +/** + * Return data size of directory entry at tagind + */ +int gpe_datsize(unsigned char *data,int tagind){ + return(exif_sizetab[exif_get_lilend(data+tagind*12+4,2)]*exif_get_lilend(data+tagind*12+6,4)); +}; |