"checkit_tiff" is an incredibly fast conformance checker for baseline TIFFs (with various extensions), see http://andreas-romeyke.de
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1060 lines
35 KiB

/* rule based checks if given TIFF is a specific baseline TIFF
*
* author: Andreas Romeyke, 2015-2017
* licensed under conditions of libtiff
* (see file LICENSE)
*
*/
#include <math.h>
#include "check.h"
#include "check_helper.h"
#include <assert.h>
#include <fcntl.h>
#ifdef _HAVE_MMAP
#include <sys/mman.h>
#include <errno.h>
#endif
off_t ct_seek(ctiff_t * ctif, off_t pos, int whence) {
#ifdef _HAVE_MMAP
switch (ctif->ioflag) {
case is_filep:
assert(ctif->fd >= 0);
// TODO: add checks if seek will be outside of file!!!
return lseek(ctif->fd, pos, whence);
break;
case is_memmap:
assert( ctif->streamp != NULL);
assert( ctif->actual_streamp != NULL);
switch (whence) {
case SEEK_SET:
ctif->actual_streamp = ctif->streamp + pos;
break;
case SEEK_CUR:
ctif->actual_streamp+=pos;
break;
case SEEK_END:
ctif->actual_streamp = ctif->streamp + ctif->streamlen + pos;
break;
}
uint32 testpos = ctif->actual_streamp - ctif->streamp;
if ( testpos > ctif->streamlen) {
/*
* fprintf(stderr, "seek offset outside of file on new pos=%i (filesize=%i)\n", testpos, ctif->streamlen);
* exit(EXIT_FAILURE);
*/
return -1;
}
if ( testpos < 0 ) {
/*
* fprintf(stderr, "seek offset outside of file on new pos=%i (filesize=%i)\n", testpos, ctif->streamlen);
* exit(EXIT_FAILURE);
*/
return -1;
}
return ctif->actual_streamp - ctif->streamp;
break;
}
#else
assert(ctif->fd >= 0);
// TODO: add checks if seek will be outside of file!!!
return lseek(ctif->fd, pos, whence);
#endif
}
ssize_t ct_read(ctiff_t * ctif, void *buf, size_t count) {
if (NULL == buf) {
perror("Buffer buf is a NULL-pointer");
exit( EXIT_FAILURE );
}
#ifdef _HAVE_MMAP
switch (ctif->ioflag) {
case is_filep:
// TODO: add checks if seek will be outside of file!!!
assert(ctif->fd >= 0);
return read(ctif->fd, buf, count);
break;
case is_memmap: {
assert( ctif->streamp != NULL);
assert( ctif->actual_streamp != NULL);
uint32 testpos = (ctif->actual_streamp+count) - (ctif->streamp);
if ( testpos > ctif->streamlen) {
/*
* fprintf(stderr, "read offset outside of file on new pos=%i (filesize=%i)\n", testpos, ctif->streamlen);
* exit(EXIT_FAILURE);
*/
return -1;
}
if ( testpos < 0 ) {
/*
* fprintf(stderr, "read offset outside of file on new pos=%i (filesize=%i)\n", testpos, ctif->streamlen);
* exit(EXIT_FAILURE);
*/
return -1;
}
memcpy(buf, ctif->actual_streamp, count);
ctif->actual_streamp+=count;
return count;
/* break; */
}
}
#else
// TODO: add checks if seek will be outside of file!!!
assert(ctif->fd >= 0);
return read(ctif->fd, buf, count);
#endif
}
int TIFFGetRawTagListIndex(ctiff_t * ctif, tag_t tag) { /* find n-th entry in IFD for given tag, return -1 if not found */
switch (ctif->tagorder) {
case has_unsorted_tags:
case has_sorted_tags:
break;
case unknown_tag_order:
{
ctif->tagorder=has_sorted_tags;
uint16 count = get_ifd0_count( ctif);
if (0 == count) { return -1; }
tag_t last = TIFFGetRawTagListEntry(ctif, 0);
ctif->tag_cache[last] = 0;
for (uint16 i= 1; i < count; i++) {
tag_t current = TIFFGetRawTagListEntry( ctif, i );
if (last >= current) {
ctif->tagorder=has_unsorted_tags;
}
ctif->tag_cache[current] = i;
last = current;
};
}
}
return ctif->tag_cache[tag];
}
//------------------------------------------------------------------------------
ret_t check_tag_has_fvalue(ctiff_t * ctif, tag_t tag, float value) {
GET_EMPTY_RET(ret);
TIFP_CHECK( ctif, ret);
float * valp = NULL;
uint32 found;
ret =TIFFGetFieldRATIONAL(ctif, tag, &valp, &found);
if (1 == found) {
float val = * valp;
if ( fabsf(val - value) < 0.01 ) {
ret.returncode=is_valid;
return ret;
} else {
char * msg = float2str(val);
ret = set_value_found_ret(&ret, msg ) ;
free ( msg );
ret.returncode = tagerror_value_differs;
}
}
return ret;
}
//------------------------------------------------------------------------------
ret_t check_tag_has_u16value(ctiff_t * ctif, tag_t tag, uint16 value) {
GET_EMPTY_RET(ret);
TIFP_CHECK( ctif, ret);
uint16 * valp = NULL;
uint32 found;
ret=TIFFGetFieldSHORT(ctif, tag, &valp, &found);
if (1 == found) {
uint16 val = *valp;
if ( val == value ) {
ret.returncode=is_valid;
return ret;
} else {
char * msg = int2str(val);
ret = set_value_found_ret(&ret, msg );
free ( msg );
ret.returncode = tagerror_value_differs;
return ret;
}
}
return ret;
}
//------------------------------------------------------------------------------
ret_t check_tag_has_u32value(ctiff_t * ctif, tag_t tag, uint32 value) {
GET_EMPTY_RET(ret);
TIFP_CHECK( ctif, ret);
uint32 * valp = NULL;
uint32 found;
ret=TIFFGetFieldLONG(ctif, tag, &valp, &found);
if (1 == found) {
uint32 val = *valp;
if ( val == value ) {
ret.returncode=is_valid;
return ret;
} else {
char * msg = int2str(val);
ret = set_value_found_ret(&ret, msg );
free ( msg );
ret.returncode = tagerror_value_differs;
return ret;
}
}
return ret;
}
ret_t parse_header_and_endianess(ctiff_t * ctif) {
GET_EMPTY_RET(ret);
TIFP_CHECK( ctif, ret);
/* seek the image file directory (bytes 4-7) */
//ct_seek(fd, (off_t) 0, SEEK_SET);
if ( ct_seek(ctif, 0, SEEK_SET) != 0) {
ret = set_value_found_ret(&ret, "TIFF header ct_seek error to 0");
ret.returncode=tiff_seek_error_header;
return ret;
}
uint16 header;
uint16 magic;
int endianness;
if ( ct_read( ctif, &header, 2) != 2 ) {
ret = set_value_found_ret(&ret, "TIFF Header ct_read error to magic byte header (first 2 bytes)");
ret.returncode=tiff_read_error_header;
return ret;
}
if (header == 0x4949) { endianness = 0; /* little endian */
} else if (header == 0x4d4d) { endianness = 1; /* big endian */
} else {
char errmsg[VALUESTRLEN]="";
snprintf (errmsg, VALUESTRLEN, "TIFF Header error, not Byte Order Bytes for TIFF: 0x%04x", header);
if (header == 0x4550) {
strcat(errmsg, ", could be a Microsoft Document Image file (little endian), if header begins with by 0x45 0x50 0x2a 0x00");
}
ret=set_value_found_ret(&ret, errmsg);
ret.returncode = tiff_byteorder_error;
return ret;
}
ctif->isbyteswapped = endianness;
if ( ct_read( ctif, &magic, 2) != 2 ) {
ret = set_value_found_ret(&ret, "TIFF Header ct_read error to magic byte header (second 2 bytes == 42)");
ret.returncode=tiff_read_error_header;
return ret;
}
uint16 magic2 = magic;
if (endianness) {
TIFFSwabShort( &magic2 ); /* big endian */
}
ret.returncode = should_not_occur;
if (magic2 == 42) {
ret.returncode=is_valid;
} else {
char errmsg[VALUESTRLEN]="";
snprintf (errmsg, VALUESTRLEN, "TIFF Header error, not a MAGIC BYTE for TIFF: 0x%04x\n", magic);
if (magic2==0x002b) { strcat(errmsg, ", but could be a BigTIFF, see http://www.awaresystems.be/imaging/tiff/bigtiff.html");
}
if (magic2==0x0055) { strcat(errmsg, ", but could be a Panasonic Raw/RW2, see http://libopenraw.freedesktop.org/wiki/Panasonic_RAW/");
}
if (magic2==0x01bc) { strcat(errmsg, ", but could be a JPEG XR, see http://www.itu.int/rec/T-REC-T.832");
}
if (magic2==0x314e) { strcat(errmsg, ", but could be a Navy Image FileFormat, see http://www.navsea.navy.mil/nswc/carderock/tecinfsys/cal-std/doc/28002c.pdf");
}
if (magic2==0x4352) { strcat(errmsg, ", but could be a DNG camera profile, see http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf");
}
if (magic2==0x4f52) { strcat(errmsg, ", but could be an Olympus ORF, see http://libopenraw.freedesktop.org/wiki/Olympus_ORF/");
}
if (magic2==0x5352) { strcat(errmsg, ", but could be an Olympus ORF, see http://libopenraw.freedesktop.org/wiki/Olympus_ORF/");
}
ret = set_value_found_ret(&ret, errmsg);
ret.returncode = tiff_byteorder_error;
}
assert( ret.returncode != should_not_occur);
return ret;
}
ret_t get_first_IFD(ctiff_t * ctif, uint32 * ifd) {
GET_EMPTY_RET(ret);
TIFP_CHECK( ctif, ret);
int isByteSwapped = ctif->isbyteswapped;
/* seek the image file directory (bytes 4-7) */
if (ct_seek(ctif, 4, SEEK_SET) != 4 ) {
ret = set_value_found_ret(&ret, "TIFF Header seak error, seek set to byte 4");
ret.returncode=tiff_seek_error_header;
return ret;
}
uint32 offset;
if ( ct_read( ctif, &offset, 4) != 4 ) {
ret = set_value_found_ret(&ret, "TIFF Header ct_read error, reading 4 bytes from 4");
ret.returncode=tiff_read_error_header;
return ret;
}
if (isByteSwapped) {
TIFFSwabLong (&offset);
}
if (offset <= 7) {
char msg[VALUESTRLEN];
snprintf(msg, VALUESTRLEN, "pointer to IFD0 is %u", offset);
ret=set_value_found_ret(&ret, msg);
ret.returncode=tiff_ifd0_offset_must_be_greater_than_eight;
return ret;
}
ctif->ifd0pos=offset;
if (ct_seek(ctif, offset, SEEK_SET) != offset ) {
char msg[VALUESTRLEN];
snprintf(msg, VALUESTRLEN, "TIFF Header seek error, seek set to byte %u", offset);
ret=set_value_found_ret(&ret, msg);
ret.returncode=tiff_seek_error_header;
return ret;
}
uint16 count;
if ( ct_read( ctif, &count, 2) != 2 ) {
ret=set_value_found_ret(&ret, "TIFF Header ct_read error2, reading ifd0 count (2 bytes)");
ret.returncode=tiff_read_error_header;
return ret;
}
if (is_byteswapped(ctif)) {
TIFFSwabShort(&count);
}
ctif->ifd0c = count;
*ifd = offset;
ret.returncode=is_valid;
return ret;
}
/* scans first IDF and returns the n-th tag, if errorneous it returns a value 0 */
tag_t TIFFGetRawTagListEntry( ctiff_t * ctif, int tagidx ) {
get_ifd0_count( ctif); /* return code ignored, used to read TIFF header */
/* ct_read count of tags (2 Bytes) */
uint32 adress = ctif->ifd0pos+2+tagidx*12;
if (ct_seek(ctif, adress , SEEK_SET) != adress) { /* IFD0 plus 2byte to get IFD-entries, then nth tag */
perror ("TIFF Header ct_seek error, seeking adress from ifd0 for tagid");
//exit( EXIT_FAILURE );
return 0;
}
uint16 tagid;
if ( ct_read( ctif, &tagid, 2) != 2) {
perror ("TIFF Header ct_read error2, reading tagid from ifd0 (2bytes)");
// exit( EXIT_FAILURE );
return 0;
}
if (is_byteswapped(ctif)) {
TIFFSwabShort(&tagid);
}
//printf("tag idx=%i, tag=%u (0x%04x) (0x%02x) (0x%02x)\n", i, tagid, tagid, hi, lo);
return tagid;
}
#define OFFSET_MALLOC(ctif_p, offsetdata, offset_type, count ) {\
size_t size = (size_t) sizeof(offset_type) * (count);\
if((size_t) (ctif_p)->streamlen < size) {\
char msg[VALUESTRLEN]; \
snprintf(msg, VALUESTRLEN, "TIFF Offset ct_read error, try to read from offset count=%zu bytes, but file has size=%u\n", size, (ctif_p)->streamlen); \
*ret_p = set_value_found_ret( ret_p, msg); \
ret_p->returncode = tiff_seek_error_offset;\
return * ret_p;\
}\
(offsetdata) = NULL; (offsetdata) = malloc (size);\
if (NULL == (offsetdata)) {\
fprintf( stderr, "could not allocate memory for offset_t\n");\
exit (EXIT_FAILURE);\
}\
ssize_t result = ct_read( ctif_p, offsetdata, size);\
if (result == 0) {\
char msg[VALUESTRLEN]; \
snprintf(msg, VALUESTRLEN, "TIFF Offset ct_read error, try to read from offset count=%zu bytes, but EOF detected\n", size); \
*ret_p = set_value_found_ret( ret_p, msg); \
ret_p->returncode = tiff_read_error_offset; \
return *ret_p; \
}\
if (\
(result == -1) ||\
(result != (ssize_t) size)\
) {\
/* fprintf(stderr, "TIFF Offset ct_read error, try to read from offset count=%lu bytes\n", sizeof(offset_type) * count); */\
/* exit( EXIT_FAILURE ); */\
char msg[VALUESTRLEN]; \
snprintf(msg, VALUESTRLEN, "TIFF Offset ct_read error, try to read from offset count=%zu bytes\n", size); \
*ret_p = set_value_found_ret( ret_p, msg); \
ret_p->returncode = tiff_read_error_offset; \
return *ret_p; \
}\
}
void offset_swabshort(ctiff_t * ctif, uint16 * address, uint16 count) {
if (is_byteswapped(ctif)) {
for (int i=0; i<count; i++, address++) {
TIFFSwabShort( address );
}
}
}
void offset_swablong(ctiff_t * ctif, uint32 * address, uint16 count) {
if (is_byteswapped(ctif)) {
for (int i=0; i<count; i++, address++) {
TIFFSwabLong( address );
}
}
}
/* get count-data datastream from offset-address */
ret_t read_offsetdata(ctiff_t * ctif, const uint32 address, const uint32 count, const uint16 datatype, offset_t * offset_p, ret_t * ret_p) {
assert(NULL != offset_p);
assert(NULL != ret_p);
offset_p->count = count;
offset_p->datatype = datatype;
ret_p->returncode = is_valid;
/* ct_read and seek to IFD address */
if (ct_seek(ctif, address, SEEK_SET) != (off_t) address) {
offset_p->count = 0;
ret_p->returncode = tiff_seek_error_offset;
return * ret_p;
}
#ifdef DEBUG
printf("read_offsetdata(tif, address=%u, count=%u, datatype=%u)\n", address, count, datatype);
#endif
switch (datatype) {
case 1: /* 8-bit unsigned integer */
case 7: /* !8-bit untyped data */
/*
offset_p->data8p = NULL;
offset_p->data8p = malloc ( sizeof(uint8) * count);
if (ct_read(fd, offset_p->data8p, sizeof(uint8) * count) != sizeof(uint8) *count)
perror ("TIFF Offset ct_read error");
*/
OFFSET_MALLOC(ctif, offset_p->data8p, uint8, count)
break;
case 2: /* 8-bit bytes w/ last byte null */
assert( sizeof(char) == sizeof(uint8));
OFFSET_MALLOC(ctif, offset_p->datacharp, char, count)
break;
case 6: /* !8-bit signed integer */
OFFSET_MALLOC(ctif, offset_p->datas8p, int8, count)
break;
case 3: /* 16-bit unsigned integer */
OFFSET_MALLOC(ctif, offset_p->data16p, uint16, count)
offset_swabshort(ctif, offset_p->data16p, count);
break;
case 8: /* !16-bit signed integer */
OFFSET_MALLOC(ctif, offset_p->datas16p, int16, count)
offset_swabshort(ctif, (uint16 *) offset_p->datas16p, count);
break;
case 4: /* 32-bit unsigned integer */
case 13: /* %32-bit unsigned integer (offset) */
OFFSET_MALLOC(ctif, offset_p->data32p, uint32, count)
offset_swablong(ctif, offset_p->data32p, count);
break;
case 9: /* !32-bit signed integer */
OFFSET_MALLOC(ctif, offset_p->datas32p, uint32, count)
offset_swablong(ctif, (uint32 *) offset_p->data32p, count);
break;
case 5: /* 64-bit unsigned fraction */
OFFSET_MALLOC(ctif, offset_p->data32p, uint32, 2*count) /* because numerator + denominator */
offset_swablong(ctif, offset_p->data32p, 2*count);
break;
case 10: /* !64-bit signed fraction */
fprintf(stderr, "offsetdata datatype=%i not supported yet", datatype);
exit(EXIT_FAILURE);
case 11: /* !32-bit IEEE floating point */
assert( sizeof(float) == 4);
OFFSET_MALLOC(ctif, offset_p->datafloatp, float, count)
break;
case 12: /* !64-bit IEEE floating point */
assert( sizeof(double) == 8);
OFFSET_MALLOC(ctif, offset_p->datadoublep, double, count)
break;
case 16: /* BigTIFF 64-bit unsigned integer */
case 18: /* BigTIFF 64-bit unsigned integer (offset) */
assert( sizeof(double) == 8);
OFFSET_MALLOC(ctif, offset_p->data64p, uint64, count)
break;
case 17: /* BigTIFF 64-bit signed integer */
assert( sizeof(double) == 8);
OFFSET_MALLOC(ctif, offset_p->datas64p, int64, count)
break;
default: /* should not occur */
{
char msg[VALUESTRLEN];
snprintf(msg, VALUESTRLEN, "offsetdata datatype=%i not supported yet", datatype);
*ret_p = set_value_found_ret(ret_p, msg);
ret_p->returncode = should_not_occur;
assert( ret_p->returncode != should_not_occur);
return *ret_p;
}
};
#ifdef DEBUG
printf ("is valid offset\n");
printf ("RET=%s\n", get_parser_error_description(ret_p->returncode));
#endif
return *ret_p;
}
/* scans first IDF and returns the type of the n-th tag */
ifd_entry_t TIFFGetRawTagIFDListEntry( ctiff_t * ctif, int tagidx ) {
uint16 tagcount = get_ifd0_count( ctif);
assert( tagcount > 0);
int byteswapped = is_byteswapped(ctif);
#ifdef DEBUG
printf(" count of tags = %i\n", tagcount);
#endif
ifd_entry_t ifd_entry;
ifd_entry.value_or_offset = is_error;
uint32 adress=ctif->ifd0pos+2+tagidx*12;
if (ct_seek(ctif, adress, SEEK_SET) !=adress) { /* IFD0 plus 2byte to get IFD-entries, then nth tag */
perror ("TIFF Header ct_seek error, seeking adress from ifd0 for tagid");
return ifd_entry;
}
uint16 tagid;
if ( ct_read( ctif, &tagid, 2) != 2) {
perror ("TIFF Header ct_read error2, reading tagid from ifd0 (2bytes)");
return ifd_entry;
}
if (byteswapped) {
TIFFSwabShort(&tagid);
}
// tag type check
uint16 tagtype;
if ( ct_read( ctif, &tagtype, 2) != 2) {
perror ("TIFF Header ct_read error2, reading tagtype from ifd0 (2bytes)");
return ifd_entry;
}
if (byteswapped) {
TIFFSwabShort(&tagtype);
}
uint32 count;
if ( ct_read( ctif, &count, 4) != 4) {
perror ("TIFF Header ct_read error4, reading count from ifd0 (4bytes)");
return ifd_entry;
}
if (byteswapped) {
TIFFSwabLong( &count);
}
#ifdef DEBUG
printf("\ncount=%0x\n\n", count);
#endif
/* is value or offset? */
ifd_entry.count=count;
ifd_entry.datatype=tagtype;
uint32 value_or_offset;
if ( ct_read( ctif, &value_or_offset, 4) != 4) {
perror ("TIFF Header ct_read error4, reading value/offset from ifd0 (4bytes)");
return ifd_entry;
}
if (byteswapped) {
TIFFSwabLong( &value_or_offset);
}
switch( tagtype) {
case 1: /* 8-bit unsigned integer */
case 2: /* 8-bit bytes w/ last byte null */
case 6: /* !8-bit signed integer */
case 7: /* !8-bit untyped data */
if (count > 4) { /* offset */
ifd_entry.value_or_offset=is_offset;
ifd_entry.data32offset=value_or_offset;
} else { /* values */
ifd_entry.value_or_offset=is_value;
ifd_entry.data8[0] = (uint8) (value_or_offset & 0x000000ff);
ifd_entry.data8[1] = (uint8) ((value_or_offset >> 8) & 0x000000ff);
ifd_entry.data8[2] = (uint8) ((value_or_offset >> 16) & 0x000000ff);
ifd_entry.data8[3] = (uint8) ((value_or_offset >> 24) & 0x000000ff);
#ifdef DEBUG
printf("data8[0]=%u\n", ifd_entry.data8[0] );
printf("data8[1]=%u\n", ifd_entry.data8[1] );
printf("data8[2]=%u\n", ifd_entry.data8[2] );
printf("data8[3]=%u\n", ifd_entry.data8[3] );
#endif
}; break;
case 3: /* 16-bit unsigned integer */
case 8: /* !16-bit signed integer */
if (count > 2) { /* offset */
ifd_entry.value_or_offset=is_offset;
ifd_entry.data32offset=value_or_offset;
} else { /* values */
ifd_entry.value_or_offset=is_value;
// swap back if byteswapped
if (byteswapped) {
TIFFSwabLong( &value_or_offset);
}
uint16 w0 = (uint16) (value_or_offset & 0x0000ffff);
uint16 w1 = (uint16) ((value_or_offset >> 16) & 0x0000ffff);;
if (byteswapped) { /* swap correctly 16bit */
TIFFSwabShort( &w0 );
TIFFSwabShort( &w1 );
}
ifd_entry.data16[0] = w0;
ifd_entry.data16[1] = w1;
#ifdef DEBUG
printf("data16[0]=%u\n", w0);
printf("data16[1]=%u\n", w1);
#endif
}; break;
case 4: /* 32-bit unsigned integer */
case 9: /* !32-bit signed integer */
if (count > 1) { /* offset */
ifd_entry.value_or_offset=is_offset;
ifd_entry.data32offset=value_or_offset;
} else { /* values */
ifd_entry.value_or_offset=is_value;
ifd_entry.data32=value_or_offset;
#ifdef DEBUG
printf("data32[0]=%u\n", value_or_offset);
#endif
}; break;
case 5: /* 64-bit unsigned fraction */
case 10: /* !64-bit signed fraction */
case 11: /* !32-bit IEEE floating point */
case 12: /* !64-bit IEEE floating point */
case 13: /* %32-bit unsigned integer (offset) */
case 16: /* BigTIFF 64-bit unsigned integer */
case 17: /* BigTIFF 64-bit signed integer */
case 18: /* BigTIFF 64-bit unsigned integer (offset) */
ifd_entry.value_or_offset=is_offset;
ifd_entry.data32offset=value_or_offset;
}
#ifdef DEBUG
printf(" tag=%u (0x%04x) tagtype=0x%04x is_offset=%s count=%u value_or_offset=0x%08x\n", tagid, tagid, tagtype, (ifd_entry.value_or_offset==is_offset ? "true" : "false"), count, value_or_offset);
#endif
return ifd_entry;
}
/* TODO */
ifd_entry_t TIFFGetRawIFDEntry( ctiff_t * ctif, tag_t tag) {
int tagidx = -1;
for (uint16 i= 0; i < get_ifd0_count( ctif ); i++) {
if ((tag >= MINTAGS) && (tag == TIFFGetRawTagListEntry( ctif, i ))) {
tagidx= i;
break;
};
};
ifd_entry_t ifd_entry;
if (tagidx >= 0) {
ifd_entry = TIFFGetRawTagIFDListEntry( ctif, tagidx );
} else { /* tag not defined */
printf("\ttag %u (%s) was not found, but requested because defined\n", tag, TIFFTagName(tag));
ifd_entry.value_or_offset = is_error;
ifd_entry.count = 0;
}
return ifd_entry;
}
/* scans first IDF and returns the type of the n-th tag */
uint32 TIFFGetRawTagTypeListEntry( ctiff_t * ctif, int tagidx ) {
if (tagidx >= 0) {
ifd_entry_t ifd_entry;
ifd_entry = TIFFGetRawTagIFDListEntry( ctif, tagidx );
return ifd_entry.datatype;
} else { /* tag not defined */
fprintf(stderr, "tagidx should be greater equal 0");
exit(EXIT_FAILURE);
}
}
/* ct_reads the datatype of given tag on specified TIFF,
* because FieldType of libtiff does not return the true value (because it maps
* its own datastructure), we need to use this function instead
* @param tif pointer to TIFF structure
* @param tag tag
* @return datatype of given tag
* if tag does not exists the function aborts with an error
*/
TIFFDataType TIFFGetRawTagType(ctiff_t * ctif, tag_t tag) {
int tagidx = -1;
for (uint16 i= 0; i < get_ifd0_count( ctif ); i++) {
if ((tag >= MINTAGS) && (tag == TIFFGetRawTagListEntry( ctif, i ))) {
tagidx= i;
break;
};
};
if (tagidx >= 0) {
TIFFDataType datatype = TIFFGetRawTagTypeListEntry( ctif, tagidx );
#ifdef DEBUG
printf("### datatype=%i \n", datatype);
#endif
return datatype;
} else { /* tag not defined */
printf("\ttag %u (%s) was not found, but requested because defined\n", tag, TIFFTagName(tag));
return TIFF_ERROR;
}
}
ctiff_t * initialize_ctif(const char * tiff_file, ct_ioflag_t ioflag) {
ctiff_t * ctif = malloc ( sizeof( ctiff_t) );
if (NULL == ctif) {
fprintf( stderr, "could not allocate memory for ctiff_t\n");
exit (EXIT_FAILURE);
}
/* load tiff file */
#ifdef __WIN32__
int tif = open(tiff_file, O_RDONLY | O_BINARY);
#else
int tif = open(tiff_file, O_RDONLY);
#endif
if (-1 == tif) {
fprintf( stderr, "file '%s' could not be opened\n", tiff_file);
exit (EXIT_FAILURE);
};
ctif->fd = tif;
ctif->streamlen = (uint32) fsize(tif);
ctif->streamp = NULL;
ctif->actual_streamp = NULL;
ctif->tagorder=unknown_tag_order;
for (unsigned int i= 0; i<MAXTAGS; i++) { /* init with -1 indicating 'not found' */
ctif->tag_cache[i]= -1;
}
#ifdef _HAVE_MMAP
switch (ioflag) {
case is_filep: {
/* streamlen */
break;
}
case is_memmap: {
void * tifmap = mmap( NULL, ctif->streamlen, PROT_READ, MAP_PRIVATE, tif, 0 );
if (MAP_FAILED == tifmap) {
fprintf( stderr, "file '%s' could not be mem-mapped, %s\n", tiff_file, strerror(errno));
exit (EXIT_FAILURE);
};
ctif->streamp=tifmap;
ctif->actual_streamp=tifmap;
break;
}
}
#endif
ctif->ioflag = ioflag;
ctif->filename = strdup(tiff_file);
ctif->ifd0pos= 0;
ctif->ifd0c= 0;
return ctif;
}
void free_ctif( ctiff_t * ctif) {
assert( NULL != ctif);
if (NULL != ctif->filename) { free(ctif->filename);
}
ctif->filename=NULL;
switch (ctif->ioflag) {
case is_filep: {
break;
}
case is_memmap: {
/* TODO */
break;
}
}
close(ctif->fd);
ctif->fd = -1;
free (ctif);
/* ctif = NULL; */
}
uint32 get_ifd0_pos( ctiff_t * ctif ) {
assert( NULL != ctif);
return ctif->ifd0pos;
}
uint16 get_ifd0_count( ctiff_t * ctif ) {
assert( NULL != ctif);
return ctif->ifd0c;
}
int is_byteswapped( ctiff_t * ctif ) {
assert( NULL != ctif);
return ctif->isbyteswapped;
}
uint32 get_next_ifd_pos(ctiff_t * ctif, uint32 actual_pos) {
assert( NULL != ctif);
ct_seek(ctif, actual_pos, SEEK_SET);
uint16 count;
if ( ct_read( ctif, &count, 2) != 2 ) {
/* FIXME: replace perror/exit with own error handling routine */
perror ("TIFF Header ct_read error2, reading count from ifd0 (2bytes)");
//exit( EXIT_FAILURE );
return 0;
}
if (ctif->isbyteswapped) {
TIFFSwabShort(&count);
}
ct_seek(ctif, 12 * count, SEEK_CUR);
uint32 offset;
if ( ct_read( ctif, &offset, 4) != 4 ) {
/* FIXME: replace perror/exit with own error handling routine */
perror ("TIFF Header ct_read error3, , reading next ifd offset from ifd0 (4bytes)");
//exit( EXIT_FAILURE );
return 0;
}
if (ctif->isbyteswapped) {
TIFFSwabLong(&offset);
}
return offset;
}
ret_t TIFFGetFieldASCII(ctiff_t * ctif, const tag_t tag, char** string_pp, uint32 * countp ) {
assert( *string_pp == NULL);
assert( countp != NULL);
GET_EMPTY_RET(ret)
int tagidx = TIFFGetRawTagListIndex(ctif, tag);
if (tagidx >= 0) { /* there exists the tag */
ifd_entry_t entry = TIFFGetRawTagIFDListEntry( ctif, tagidx );
/* assert (entry.datatype == TIFF_ASCII); */
if (entry.datatype != TIFF_ASCII) {
char msg[200];
snprintf(msg, 200, "data of type ASCII expected, but got type %s", TIFFTypeName(entry.datatype));
ret = set_value_found_ret(&ret, msg);
ret.returncode = tagerror_unexpected_type_found;
return ret;
}
*countp = entry.count;
/*
*(string_pp) = malloc( sizeof(char) * entry.count +1);
if (NULL == (* string_pp)) {
ret.returncode=could_not_allocate_memory;
return ret;
}
memset(*string_pp, '\0', entry.count+1);
*/
*(string_pp) = calloc(entry.count+1, sizeof(char) );
if (NULL == (* string_pp)) {
ret.returncode=could_not_allocate_memory;
return ret;
}
if (entry.value_or_offset == is_value) {
assert (entry.count <= 4);
for (uint32 i=0; i<entry.count; i++) {
(*string_pp)[i]=entry.data8[i];
}
ret.returncode=is_valid;
return ret;
} else if (entry.value_or_offset == is_offset) {
uint32 data32offset = entry.data32offset;
offset_t offset;
ret = read_offsetdata( ctif, data32offset, entry.count, entry.datatype, &offset, &ret);
if (ret.returncode != is_valid) {
/* FIXME: free(offset.datacharp); */
return ret;
}
char * p = offset.datacharp;
char * s = *(string_pp);
#ifdef DEBUG
/* DEBUG: */
printf("offset.count=%u, offset.datacharp=%p\n", offset.count, offset.datacharp);
printf("tag=%i entry.count=%u offset.count=%u\n", tag, entry.count, offset.count);
#endif
for (uint32 i=0; i<entry.count; i++) {
#ifdef DEBUG
printf("P[%u]=%c\n", i, *p);
#endif
*(s++) = *(p++);
}
//printf("ASCII='%s'\n", *(string_pp));
free(offset.datacharp);
ret.returncode=is_valid;
return ret;
}
}
ret.returncode = tag_does_not_exist;
return ret;
}
ret_t TIFFGetFieldLONG(ctiff_t * ctif, const tag_t tag, uint32 ** long_pp, uint32 * countp) {
assert( *long_pp == NULL);
assert( countp != NULL);
*countp = 0; /* init */
GET_EMPTY_RET(ret)
int tagidx = TIFFGetRawTagListIndex(ctif, tag);
if (tagidx >= 0) { /* there exists the tag */
ifd_entry_t entry = TIFFGetRawTagIFDListEntry( ctif, tagidx );
uint32 overflow = (0xffffffff / (sizeof(uint32)));
if (entry.count > overflow) {
char msg[200];
snprintf(msg, 200, "count=%u overflow boundary=%u", entry.count, overflow);
ret = set_value_found_ret(&ret, msg);
ret.returncode = tagerror_count_results_in_offsets_overflow;
return ret;
}
*countp = entry.count;
*(long_pp) = malloc( sizeof(uint32) * entry.count);
if (NULL == (*long_pp)) {
ret.returncode=could_not_allocate_memory;
return ret;
}
/* assert (entry.datatype == TIFF_LONG); */
if (entry.datatype != TIFF_LONG) {
char msg[200];
snprintf(msg, 200, "data of type LONG expected, but got type %s", TIFFTypeName(entry.datatype));
ret = set_value_found_ret(&ret, msg);
ret.returncode = tagerror_unexpected_type_found;
return ret;
}
if (entry.value_or_offset == is_value) {
assert (entry.count <= 1);
//printf("LONG (direct)=%lu\n", entry.data32);
memcpy((void *) (*long_pp), (void *) &entry.data32, (sizeof(uint32)*entry.count));
ret.returncode=is_valid;
return ret;
} else if (entry.value_or_offset == is_offset) {
uint32 data32offset = entry.data32offset;
offset_t offset;
ret = read_offsetdata( ctif, data32offset, entry.count, entry.datatype, &offset, &ret);
if (ret.returncode != is_valid) {
/* FIXME: free(offset.datacharp); */
return ret;
}
//printf("LONG (offset)=%lu\n", *offset.datacharp);
memcpy((void *) (*long_pp), (void *) offset.datacharp, (sizeof(uint32)*offset.count));
free(offset.datacharp);
ret.returncode=is_valid;
return ret;
}
}
ret.returncode = tag_does_not_exist;
return ret;
}
ret_t TIFFGetFieldSHORT(ctiff_t * ctif, const tag_t tag, uint16 ** short_pp, uint32 * countp) {
assert( *short_pp == NULL);
assert( countp != NULL);
*countp = 0; /* init */
GET_EMPTY_RET(ret)
int tagidx = TIFFGetRawTagListIndex(ctif, tag);
if (tagidx >= 0) { /* there exists the tag */
ifd_entry_t entry = TIFFGetRawTagIFDListEntry( ctif, tagidx );
uint32 overflow = (0xffffffff / (sizeof(uint16)));
if (entry.count > overflow) {
char msg[200];
snprintf(msg, 200, "count=%u overflow boundary=%u", entry.count, overflow);
ret = set_value_found_ret(&ret, msg);
ret.returncode = tagerror_count_results_in_offsets_overflow;
return ret;
}
*countp = entry.count;
*(short_pp) = malloc( (size_t) sizeof(uint16) * entry.count);
if (NULL == *(short_pp)) {
ret.returncode=could_not_allocate_memory;
return ret;
}
/* assert (entry.datatype == TIFF_SHORT); */
if (entry.datatype != TIFF_SHORT) {
char msg[200];
snprintf(msg, 200, "data of type SHORT expected, but got type %s", TIFFTypeName(entry.datatype));
ret = set_value_found_ret(&ret, msg);
ret.returncode = tagerror_unexpected_type_found;
return ret;
}
if (entry.value_or_offset == is_value) {
assert (entry.count <= 2);
memcpy((void *) (*short_pp), (void *) &entry.data16, (sizeof(uint16)*entry.count));
//printf("SHORT (direct)=%u %u\n", entry.data32, **short_pp);
ret.returncode=is_valid;
return ret;
} else if (entry.value_or_offset == is_offset) {
uint32 data32offset = entry.data32offset;
offset_t offset;
ret = read_offsetdata( ctif, data32offset, entry.count, entry.datatype, &offset, &ret);
if (ret.returncode != is_valid) {
/* FIXME: free(offset.datacharp); */
return ret;
}
//printf("SHORT (offset)=%u\n", *offset.datacharp);
memcpy((void *) (*short_pp), (void *)offset.datacharp, (sizeof(uint16)*offset.count));
free( offset.datacharp );
offset.datacharp=NULL;
ret.returncode=is_valid;
return ret;
}
}
ret.returncode = tag_does_not_exist;
return ret;
}
ret_t TIFFGetFieldRATIONAL(ctiff_t * ctif, const tag_t tag, float ** float_pp, uint32 * countp) {
assert( *float_pp == NULL);
assert( countp != NULL);
*countp = 0; /* init */
GET_EMPTY_RET(ret)
int tagidx = TIFFGetRawTagListIndex(ctif, tag);
if (tagidx >= 0) { /* there exists the tag */
ifd_entry_t entry = TIFFGetRawTagIFDListEntry( ctif, tagidx );
uint32 overflow = (0xffffffff / (2*sizeof(uint32)));
if (entry.count > overflow) {
char msg[200];
snprintf(msg, 200, "count=%u overflow boundary=%u", entry.count, overflow);
ret = set_value_found_ret(&ret, msg);
ret.returncode = tagerror_count_results_in_offsets_overflow;
return ret;
}
//printf("entry.count=%i\n", entry.count);
*countp = entry.count;
*(float_pp) = malloc( sizeof(float) * (entry.count));
if (NULL == *(float_pp)) {
ret.returncode=could_not_allocate_memory;
return ret;
}
/* assert (entry.datatype == TIFF_RATIONAL); */
if (entry.datatype != TIFF_RATIONAL) {
char msg[200];
snprintf(msg, 200, "data of type RATIONAL expected, but got type %s", TIFFTypeName(entry.datatype));
ret = set_value_found_ret(&ret, msg);
ret.returncode = tagerror_unexpected_type_found;
return ret;
}
/* rational is defined as 2x32bits */
if (entry.value_or_offset == is_value) {
ret.returncode = tagerror_expected_offsetdata;
return ret;
} else if (entry.value_or_offset == is_offset) {
uint32 data32offset = entry.data32offset;
offset_t offset;
//printf("data32offset=%u count=%i\n", data32offset, entry.count);
ret = read_offsetdata( ctif, data32offset, entry.count, entry.datatype, &offset, &ret);
if (ret.returncode != is_valid) {
/* FIXME: free(offset.datacharp); */
return ret;
}
/* copy to a float */
uint32 * orig_data32p = offset.data32p;
for (uint32 i = 0; i< entry.count; i++, orig_data32p+=2) {
uint32 numerator = *(orig_data32p);
uint32 denominator = *(orig_data32p+1);
//printf("DEBUG: numerator=%u denumeator=%u\n", numerator, denominator);
float v;
if (denominator == 0) {
v=NAN;
} else {
v = (float) numerator / (float) denominator;
}
//printf("DEBUG2: *float_pp[%i]=%f (%u / %u)\n", i, v, numerator, denominator);
(*(float_pp))[i]=v;
}
free( offset.data32p );
offset.data32p=NULL;
ret.returncode=is_valid;
return ret;
}
}
ret.returncode = tag_does_not_exist;
return ret;
}
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 smarttab expandtab :*/