"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
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 :*/
|