/* * Copyright (c) 1988-1997 Sam Leffler * Copyright (c) 1991-1997 Silicon Graphics, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that (i) the above copyright notices and this permission notice appear in * all copies of the software and related documentation, and (ii) the names of * Sam Leffler and Silicon Graphics may not be used in any advertising or * publicity relating to the software without the specific, prior written * permission of Sam Leffler and Silicon Graphics. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* * TIFF Library. * * Directory Write Support Routines. */ #include "tiffiop.h" #include /*--: for Rational2Double */ #include /*--: for Rational2Double */ #ifdef HAVE_IEEEFP #define TIFFCvtNativeToIEEEFloat(tif, n, fp) #define TIFFCvtNativeToIEEEDouble(tif, n, dp) #else extern void TIFFCvtNativeToIEEEFloat(TIFF *tif, uint32_t n, float *fp); extern void TIFFCvtNativeToIEEEDouble(TIFF *tif, uint32_t n, double *dp); #endif static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone, uint64_t *pdiroff); static int TIFFWriteDirectoryTagSampleformatArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value); static int TIFFWriteDirectoryTagAscii(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, char *value); static int TIFFWriteDirectoryTagUndefinedArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value); static int TIFFWriteDirectoryTagByteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value); static int TIFFWriteDirectoryTagSbyteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int8_t *value); static int TIFFWriteDirectoryTagShort(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t value); static int TIFFWriteDirectoryTagShortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint16_t *value); static int TIFFWriteDirectoryTagShortPerSample(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t value); static int TIFFWriteDirectoryTagSshortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int16_t *value); static int TIFFWriteDirectoryTagLong(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t value); static int TIFFWriteDirectoryTagLongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value); static int TIFFWriteDirectoryTagSlongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int32_t *value); static int TIFFWriteDirectoryTagLong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value); static int TIFFWriteDirectoryTagSlong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int64_t *value); static int TIFFWriteDirectoryTagRational(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, double value); static int TIFFWriteDirectoryTagRationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value); static int TIFFWriteDirectoryTagSrationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value); static int TIFFWriteDirectoryTagFloatArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value); static int TIFFWriteDirectoryTagDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value); static int TIFFWriteDirectoryTagIfdArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value); static int TIFFWriteDirectoryTagShortLong(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t value); static int TIFFWriteDirectoryTagLongLong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value); static int TIFFWriteDirectoryTagIfdIfd8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value); static int TIFFWriteDirectoryTagColormap(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir); static int TIFFWriteDirectoryTagTransferfunction(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir); static int TIFFWriteDirectoryTagSubifd(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir); static int TIFFWriteDirectoryTagCheckedAscii(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, char *value); static int TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value); static int TIFFWriteDirectoryTagCheckedByteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value); static int TIFFWriteDirectoryTagCheckedSbyteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int8_t *value); static int TIFFWriteDirectoryTagCheckedShort(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t value); static int TIFFWriteDirectoryTagCheckedShortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint16_t *value); static int TIFFWriteDirectoryTagCheckedSshortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int16_t *value); static int TIFFWriteDirectoryTagCheckedLong(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t value); static int TIFFWriteDirectoryTagCheckedLongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value); static int TIFFWriteDirectoryTagCheckedSlongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int32_t *value); static int TIFFWriteDirectoryTagCheckedLong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value); static int TIFFWriteDirectoryTagCheckedSlong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int64_t *value); static int TIFFWriteDirectoryTagCheckedRational(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, double value); static int TIFFWriteDirectoryTagCheckedRationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value); static int TIFFWriteDirectoryTagCheckedSrationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value); /*--: Rational2Double: New functions to support true double-precision for custom * rational tag types. */ static int TIFFWriteDirectoryTagRationalDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value); static int TIFFWriteDirectoryTagSrationalDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value); static int TIFFWriteDirectoryTagCheckedRationalDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value); static int TIFFWriteDirectoryTagCheckedSrationalDoubleArray( TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value); static void DoubleToRational(double value, uint32_t *num, uint32_t *denom); static void DoubleToSrational(double value, int32_t *num, int32_t *denom); static int TIFFWriteDirectoryTagCheckedFloatArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value); static int TIFFWriteDirectoryTagCheckedDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value); static int TIFFWriteDirectoryTagCheckedIfdArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value); static int TIFFWriteDirectoryTagCheckedIfd8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value); static int TIFFWriteDirectoryTagData(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t datatype, uint32_t count, uint32_t datalength, void *data); static int TIFFLinkDirectory(TIFF *); /* * Write the contents of the current directory * to the specified file. This routine doesn't * handle overwriting a directory with auxiliary * storage that's been changed. */ int TIFFWriteDirectory(TIFF *tif) { return TIFFWriteDirectorySec(tif, TRUE, TRUE, NULL); } /* * This is an advanced writing function that must be used in a particular * sequence, and generally together with TIFFForceStrileArrayWriting(), * to make its intended effect. Its aim is to modify the location * where the [Strip/Tile][Offsets/ByteCounts] arrays are located in the file. * More precisely, when TIFFWriteCheck() will be called, the tag entries for * those arrays will be written with type = count = offset = 0 as a temporary * value. * * Its effect is only valid for the current directory, and before * TIFFWriteDirectory() is first called, and will be reset when * changing directory. * * The typical sequence of calls is: * TIFFOpen() * [ TIFFCreateDirectory(tif) ] * Set fields with calls to TIFFSetField(tif, ...) * TIFFDeferStrileArrayWriting(tif) * TIFFWriteCheck(tif, ...) * TIFFWriteDirectory(tif) * ... potentially create other directories and come back to the above directory * TIFFForceStrileArrayWriting(tif): emit the arrays at the end of file * * Returns 1 in case of success, 0 otherwise. */ int TIFFDeferStrileArrayWriting(TIFF *tif) { static const char module[] = "TIFFDeferStrileArrayWriting"; if (tif->tif_mode == O_RDONLY) { TIFFErrorExtR(tif, tif->tif_name, "File opened in read-only mode"); return 0; } if (tif->tif_diroff != 0) { TIFFErrorExtR(tif, module, "Directory has already been written"); return 0; } tif->tif_dir.td_deferstrilearraywriting = TRUE; return 1; } /* * Similar to TIFFWriteDirectory(), writes the directory out * but leaves all data structures in memory so that it can be * written again. This will make a partially written TIFF file * readable before it is successfully completed/closed. */ int TIFFCheckpointDirectory(TIFF *tif) { int rc; /* Setup the strips arrays, if they haven't already been. */ if (tif->tif_dir.td_stripoffset_p == NULL) (void)TIFFSetupStrips(tif); rc = TIFFWriteDirectorySec(tif, TRUE, FALSE, NULL); (void)TIFFSetWriteOffset(tif, TIFFSeekFile(tif, 0, SEEK_END)); return rc; } int TIFFWriteCustomDirectory(TIFF *tif, uint64_t *pdiroff) { return TIFFWriteDirectorySec(tif, FALSE, FALSE, pdiroff); } /* * Similar to TIFFWriteDirectory(), but if the directory has already * been written once, it is relocated to the end of the file, in case it * has changed in size. Note that this will result in the loss of the * previously used directory space. */ int TIFFRewriteDirectory(TIFF *tif) { static const char module[] = "TIFFRewriteDirectory"; /* We don't need to do anything special if it hasn't been written. */ if (tif->tif_diroff == 0) return TIFFWriteDirectory(tif); /* * Find and zero the pointer to this directory, so that TIFFLinkDirectory * will cause it to be added after this directories current pre-link. */ uint64_t torewritediroff = tif->tif_diroff; if (!(tif->tif_flags & TIFF_BIGTIFF)) { if (tif->tif_header.classic.tiff_diroff == tif->tif_diroff) { tif->tif_header.classic.tiff_diroff = 0; tif->tif_diroff = 0; TIFFSeekFile(tif, 4, SEEK_SET); if (!WriteOK(tif, &(tif->tif_header.classic.tiff_diroff), 4)) { TIFFErrorExtR(tif, tif->tif_name, "Error updating TIFF header"); return (0); } } else if (tif->tif_diroff > 0xFFFFFFFFU) { TIFFErrorExtR(tif, module, "tif->tif_diroff exceeds 32 bit range allowed for " "Classic TIFF"); return (0); } else { uint32_t nextdir; nextdir = tif->tif_header.classic.tiff_diroff; while (1) { uint16_t dircount; uint32_t nextnextdir; if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount, 2)) { TIFFErrorExtR(tif, module, "Error fetching directory count"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&dircount); (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12, SEEK_SET); if (!ReadOK(tif, &nextnextdir, 4)) { TIFFErrorExtR(tif, module, "Error fetching directory link"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&nextnextdir); if (nextnextdir == tif->tif_diroff) { uint32_t m; m = 0; (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12, SEEK_SET); if (!WriteOK(tif, &m, 4)) { TIFFErrorExtR(tif, module, "Error writing directory link"); return (0); } tif->tif_diroff = 0; /* Force a full-traversal to reach the zeroed pointer */ tif->tif_lastdiroff = 0; break; } nextdir = nextnextdir; } } /* Remove skipped offset from IFD loop directory list. */ _TIFFRemoveEntryFromDirectoryListByOffset(tif, torewritediroff); } else { if (tif->tif_header.big.tiff_diroff == tif->tif_diroff) { tif->tif_header.big.tiff_diroff = 0; tif->tif_diroff = 0; TIFFSeekFile(tif, 8, SEEK_SET); if (!WriteOK(tif, &(tif->tif_header.big.tiff_diroff), 8)) { TIFFErrorExtR(tif, tif->tif_name, "Error updating TIFF header"); return (0); } } else { uint64_t nextdir; nextdir = tif->tif_header.big.tiff_diroff; while (1) { uint64_t dircount64; uint16_t dircount; uint64_t nextnextdir; if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount64, 8)) { TIFFErrorExtR(tif, module, "Error fetching directory count"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&dircount64); if (dircount64 > 0xFFFF) { TIFFErrorExtR(tif, module, "Sanity check on tag count failed, likely " "corrupt TIFF"); return (0); } dircount = (uint16_t)dircount64; (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20, SEEK_SET); if (!ReadOK(tif, &nextnextdir, 8)) { TIFFErrorExtR(tif, module, "Error fetching directory link"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&nextnextdir); if (nextnextdir == tif->tif_diroff) { uint64_t m; m = 0; (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20, SEEK_SET); if (!WriteOK(tif, &m, 8)) { TIFFErrorExtR(tif, module, "Error writing directory link"); return (0); } tif->tif_diroff = 0; /* Force a full-traversal to reach the zeroed pointer */ tif->tif_lastdiroff = 0; break; } nextdir = nextnextdir; } } /* Remove skipped offset from IFD loop directory list. */ _TIFFRemoveEntryFromDirectoryListByOffset(tif, torewritediroff); } /* * Now use TIFFWriteDirectory() normally. */ return TIFFWriteDirectory(tif); } static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone, uint64_t *pdiroff) { static const char module[] = "TIFFWriteDirectorySec"; uint32_t ndir; TIFFDirEntry *dir; uint32_t dirsize; void *dirmem; uint32_t m; if (tif->tif_mode == O_RDONLY) return (1); _TIFFFillStriles(tif); /* * Clear write state so that subsequent images with * different characteristics get the right buffers * setup for them. */ if (imagedone) { if (tif->tif_flags & TIFF_POSTENCODE) { tif->tif_flags &= ~TIFF_POSTENCODE; if (!(*tif->tif_postencode)(tif)) { TIFFErrorExtR(tif, module, "Error post-encoding before directory write"); return (0); } } (*tif->tif_close)(tif); /* shutdown encoder */ /* * Flush any data that might have been written * by the compression close+cleanup routines. But * be careful not to write stuff if we didn't add data * in the previous steps as the "rawcc" data may well be * a previously read tile/strip in mixed read/write mode. */ if (tif->tif_rawcc > 0 && (tif->tif_flags & TIFF_BEENWRITING) != 0) { if (!TIFFFlushData1(tif)) { TIFFErrorExtR(tif, module, "Error flushing data before directory write"); return (0); } } if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) { _TIFFfreeExt(tif, tif->tif_rawdata); tif->tif_rawdata = NULL; tif->tif_rawcc = 0; tif->tif_rawdatasize = 0; tif->tif_rawdataoff = 0; tif->tif_rawdataloaded = 0; } tif->tif_flags &= ~(TIFF_BEENWRITING | TIFF_BUFFERSETUP); } if (TIFFFieldSet(tif, FIELD_COMPRESSION) && (tif->tif_dir.td_compression == COMPRESSION_DEFLATE)) { TIFFWarningExtR(tif, module, "Creating TIFF with legacy Deflate codec identifier, " "COMPRESSION_ADOBE_DEFLATE is more widely supported"); } dir = NULL; dirmem = NULL; dirsize = 0; while (1) { ndir = 0; if (isimage) { if (TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { if (!TIFFWriteDirectoryTagShortLong(tif, &ndir, dir, TIFFTAG_IMAGEWIDTH, tif->tif_dir.td_imagewidth)) goto bad; if (!TIFFWriteDirectoryTagShortLong( tif, &ndir, dir, TIFFTAG_IMAGELENGTH, tif->tif_dir.td_imagelength)) goto bad; } if (TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { if (!TIFFWriteDirectoryTagShortLong(tif, &ndir, dir, TIFFTAG_TILEWIDTH, tif->tif_dir.td_tilewidth)) goto bad; if (!TIFFWriteDirectoryTagShortLong(tif, &ndir, dir, TIFFTAG_TILELENGTH, tif->tif_dir.td_tilelength)) goto bad; } if (TIFFFieldSet(tif, FIELD_RESOLUTION)) { if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir, TIFFTAG_XRESOLUTION, tif->tif_dir.td_xresolution)) goto bad; if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir, TIFFTAG_YRESOLUTION, tif->tif_dir.td_yresolution)) goto bad; } if (TIFFFieldSet(tif, FIELD_POSITION)) { if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir, TIFFTAG_XPOSITION, tif->tif_dir.td_xposition)) goto bad; if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir, TIFFTAG_YPOSITION, tif->tif_dir.td_yposition)) goto bad; } if (TIFFFieldSet(tif, FIELD_SUBFILETYPE)) { if (!TIFFWriteDirectoryTagLong(tif, &ndir, dir, TIFFTAG_SUBFILETYPE, tif->tif_dir.td_subfiletype)) goto bad; } if (TIFFFieldSet(tif, FIELD_BITSPERSAMPLE)) { if (!TIFFWriteDirectoryTagShortPerSample( tif, &ndir, dir, TIFFTAG_BITSPERSAMPLE, tif->tif_dir.td_bitspersample)) goto bad; } if (TIFFFieldSet(tif, FIELD_COMPRESSION)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_COMPRESSION, tif->tif_dir.td_compression)) goto bad; } if (TIFFFieldSet(tif, FIELD_PHOTOMETRIC)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_PHOTOMETRIC, tif->tif_dir.td_photometric)) goto bad; } if (TIFFFieldSet(tif, FIELD_THRESHHOLDING)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_THRESHHOLDING, tif->tif_dir.td_threshholding)) goto bad; } if (TIFFFieldSet(tif, FIELD_FILLORDER)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_FILLORDER, tif->tif_dir.td_fillorder)) goto bad; } if (TIFFFieldSet(tif, FIELD_ORIENTATION)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_ORIENTATION, tif->tif_dir.td_orientation)) goto bad; } if (TIFFFieldSet(tif, FIELD_SAMPLESPERPIXEL)) { if (!TIFFWriteDirectoryTagShort( tif, &ndir, dir, TIFFTAG_SAMPLESPERPIXEL, tif->tif_dir.td_samplesperpixel)) goto bad; } if (TIFFFieldSet(tif, FIELD_ROWSPERSTRIP)) { if (!TIFFWriteDirectoryTagShortLong( tif, &ndir, dir, TIFFTAG_ROWSPERSTRIP, tif->tif_dir.td_rowsperstrip)) goto bad; } if (TIFFFieldSet(tif, FIELD_MINSAMPLEVALUE)) { if (!TIFFWriteDirectoryTagShortPerSample( tif, &ndir, dir, TIFFTAG_MINSAMPLEVALUE, tif->tif_dir.td_minsamplevalue)) goto bad; } if (TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE)) { if (!TIFFWriteDirectoryTagShortPerSample( tif, &ndir, dir, TIFFTAG_MAXSAMPLEVALUE, tif->tif_dir.td_maxsamplevalue)) goto bad; } if (TIFFFieldSet(tif, FIELD_PLANARCONFIG)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_PLANARCONFIG, tif->tif_dir.td_planarconfig)) goto bad; } if (TIFFFieldSet(tif, FIELD_RESOLUTIONUNIT)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_RESOLUTIONUNIT, tif->tif_dir.td_resolutionunit)) goto bad; } if (TIFFFieldSet(tif, FIELD_PAGENUMBER)) { if (!TIFFWriteDirectoryTagShortArray( tif, &ndir, dir, TIFFTAG_PAGENUMBER, 2, &tif->tif_dir.td_pagenumber[0])) goto bad; } if (TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) { if (!isTiled(tif)) { if (!TIFFWriteDirectoryTagLongLong8Array( tif, &ndir, dir, TIFFTAG_STRIPBYTECOUNTS, tif->tif_dir.td_nstrips, tif->tif_dir.td_stripbytecount_p)) goto bad; } else { if (!TIFFWriteDirectoryTagLongLong8Array( tif, &ndir, dir, TIFFTAG_TILEBYTECOUNTS, tif->tif_dir.td_nstrips, tif->tif_dir.td_stripbytecount_p)) goto bad; } } if (TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { if (!isTiled(tif)) { /* td_stripoffset_p might be NULL in an odd OJPEG case. See * tif_dirread.c around line 3634. * XXX: OJPEG hack. * If a) compression is OJPEG, b) it's not a tiled TIFF, * and c) the number of strips is 1, * then we tolerate the absence of stripoffsets tag, * because, presumably, all required data is in the * JpegInterchangeFormat stream. * We can get here when using tiffset on such a file. * See http://bugzilla.maptools.org/show_bug.cgi?id=2500 */ if (tif->tif_dir.td_stripoffset_p != NULL && !TIFFWriteDirectoryTagLongLong8Array( tif, &ndir, dir, TIFFTAG_STRIPOFFSETS, tif->tif_dir.td_nstrips, tif->tif_dir.td_stripoffset_p)) goto bad; } else { if (!TIFFWriteDirectoryTagLongLong8Array( tif, &ndir, dir, TIFFTAG_TILEOFFSETS, tif->tif_dir.td_nstrips, tif->tif_dir.td_stripoffset_p)) goto bad; } } if (TIFFFieldSet(tif, FIELD_COLORMAP)) { if (!TIFFWriteDirectoryTagColormap(tif, &ndir, dir)) goto bad; } if (TIFFFieldSet(tif, FIELD_EXTRASAMPLES)) { if (tif->tif_dir.td_extrasamples) { uint16_t na; uint16_t *nb; TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, &na, &nb); if (!TIFFWriteDirectoryTagShortArray( tif, &ndir, dir, TIFFTAG_EXTRASAMPLES, na, nb)) goto bad; } } if (TIFFFieldSet(tif, FIELD_SAMPLEFORMAT)) { if (!TIFFWriteDirectoryTagShortPerSample( tif, &ndir, dir, TIFFTAG_SAMPLEFORMAT, tif->tif_dir.td_sampleformat)) goto bad; } if (TIFFFieldSet(tif, FIELD_SMINSAMPLEVALUE)) { if (!TIFFWriteDirectoryTagSampleformatArray( tif, &ndir, dir, TIFFTAG_SMINSAMPLEVALUE, tif->tif_dir.td_samplesperpixel, tif->tif_dir.td_sminsamplevalue)) goto bad; } if (TIFFFieldSet(tif, FIELD_SMAXSAMPLEVALUE)) { if (!TIFFWriteDirectoryTagSampleformatArray( tif, &ndir, dir, TIFFTAG_SMAXSAMPLEVALUE, tif->tif_dir.td_samplesperpixel, tif->tif_dir.td_smaxsamplevalue)) goto bad; } if (TIFFFieldSet(tif, FIELD_IMAGEDEPTH)) { if (!TIFFWriteDirectoryTagLong(tif, &ndir, dir, TIFFTAG_IMAGEDEPTH, tif->tif_dir.td_imagedepth)) goto bad; } if (TIFFFieldSet(tif, FIELD_TILEDEPTH)) { if (!TIFFWriteDirectoryTagLong(tif, &ndir, dir, TIFFTAG_TILEDEPTH, tif->tif_dir.td_tiledepth)) goto bad; } if (TIFFFieldSet(tif, FIELD_HALFTONEHINTS)) { if (!TIFFWriteDirectoryTagShortArray( tif, &ndir, dir, TIFFTAG_HALFTONEHINTS, 2, &tif->tif_dir.td_halftonehints[0])) goto bad; } if (TIFFFieldSet(tif, FIELD_YCBCRSUBSAMPLING)) { if (!TIFFWriteDirectoryTagShortArray( tif, &ndir, dir, TIFFTAG_YCBCRSUBSAMPLING, 2, &tif->tif_dir.td_ycbcrsubsampling[0])) goto bad; } if (TIFFFieldSet(tif, FIELD_YCBCRPOSITIONING)) { if (!TIFFWriteDirectoryTagShort( tif, &ndir, dir, TIFFTAG_YCBCRPOSITIONING, tif->tif_dir.td_ycbcrpositioning)) goto bad; } if (TIFFFieldSet(tif, FIELD_REFBLACKWHITE)) { if (!TIFFWriteDirectoryTagRationalArray( tif, &ndir, dir, TIFFTAG_REFERENCEBLACKWHITE, 6, tif->tif_dir.td_refblackwhite)) goto bad; } if (TIFFFieldSet(tif, FIELD_TRANSFERFUNCTION)) { if (!TIFFWriteDirectoryTagTransferfunction(tif, &ndir, dir)) goto bad; } if (TIFFFieldSet(tif, FIELD_INKNAMES)) { if (!TIFFWriteDirectoryTagAscii( tif, &ndir, dir, TIFFTAG_INKNAMES, tif->tif_dir.td_inknameslen, tif->tif_dir.td_inknames)) goto bad; } if (TIFFFieldSet(tif, FIELD_NUMBEROFINKS)) { if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir, TIFFTAG_NUMBEROFINKS, tif->tif_dir.td_numberofinks)) goto bad; } if (TIFFFieldSet(tif, FIELD_SUBIFD)) { if (!TIFFWriteDirectoryTagSubifd(tif, &ndir, dir)) goto bad; } { uint32_t n; for (n = 0; n < tif->tif_nfields; n++) { const TIFFField *o; o = tif->tif_fields[n]; if ((o->field_bit >= FIELD_CODEC) && (TIFFFieldSet(tif, o->field_bit))) { switch (o->get_field_type) { case TIFF_SETGET_ASCII: { uint32_t pa; char *pb; assert(o->field_type == TIFF_ASCII); assert(o->field_readcount == TIFF_VARIABLE); assert(o->field_passcount == 0); TIFFGetField(tif, o->field_tag, &pb); pa = (uint32_t)(strlen(pb)); if (!TIFFWriteDirectoryTagAscii( tif, &ndir, dir, (uint16_t)o->field_tag, pa, pb)) goto bad; } break; case TIFF_SETGET_UINT16: { uint16_t p; assert(o->field_type == TIFF_SHORT); assert(o->field_readcount == 1); assert(o->field_passcount == 0); TIFFGetField(tif, o->field_tag, &p); if (!TIFFWriteDirectoryTagShort( tif, &ndir, dir, (uint16_t)o->field_tag, p)) goto bad; } break; case TIFF_SETGET_UINT32: { uint32_t p; assert(o->field_type == TIFF_LONG); assert(o->field_readcount == 1); assert(o->field_passcount == 0); TIFFGetField(tif, o->field_tag, &p); if (!TIFFWriteDirectoryTagLong( tif, &ndir, dir, (uint16_t)o->field_tag, p)) goto bad; } break; case TIFF_SETGET_C32_UINT8: { uint32_t pa; void *pb; assert(o->field_type == TIFF_UNDEFINED); assert(o->field_readcount == TIFF_VARIABLE2); assert(o->field_passcount == 1); TIFFGetField(tif, o->field_tag, &pa, &pb); if (!TIFFWriteDirectoryTagUndefinedArray( tif, &ndir, dir, (uint16_t)o->field_tag, pa, pb)) goto bad; } break; default: TIFFErrorExtR( tif, module, "Cannot write tag %" PRIu32 " (%s)", TIFFFieldTag(o), o->field_name ? o->field_name : "unknown"); goto bad; } } } } } for (m = 0; m < (uint32_t)(tif->tif_dir.td_customValueCount); m++) { uint16_t tag = (uint16_t)tif->tif_dir.td_customValues[m].info->field_tag; uint32_t count = tif->tif_dir.td_customValues[m].count; switch (tif->tif_dir.td_customValues[m].info->field_type) { case TIFF_ASCII: if (!TIFFWriteDirectoryTagAscii( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_UNDEFINED: if (!TIFFWriteDirectoryTagUndefinedArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_BYTE: if (!TIFFWriteDirectoryTagByteArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_SBYTE: if (!TIFFWriteDirectoryTagSbyteArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_SHORT: if (!TIFFWriteDirectoryTagShortArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_SSHORT: if (!TIFFWriteDirectoryTagSshortArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_LONG: if (!TIFFWriteDirectoryTagLongArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_SLONG: if (!TIFFWriteDirectoryTagSlongArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_LONG8: if (!TIFFWriteDirectoryTagLong8Array( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_SLONG8: if (!TIFFWriteDirectoryTagSlong8Array( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_RATIONAL: { /*-- Rational2Double: For Rationals evaluate * "set_field_type" to determine internal storage size. */ int tv_size; tv_size = TIFFFieldSetGetSize( tif->tif_dir.td_customValues[m].info); if (tv_size == 8) { if (!TIFFWriteDirectoryTagRationalDoubleArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; } else { /*-- default should be tv_size == 4 */ if (!TIFFWriteDirectoryTagRationalArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; /*-- ToDo: After Testing, this should be removed and * tv_size==4 should be set as default. */ if (tv_size != 4) { TIFFErrorExtR(tif, "TIFFLib: _TIFFWriteDirectorySec()", "Rational2Double: .set_field_type is " "not 4 but %d", tv_size); } } } break; case TIFF_SRATIONAL: { /*-- Rational2Double: For Rationals evaluate * "set_field_type" to determine internal storage size. */ int tv_size; tv_size = TIFFFieldSetGetSize( tif->tif_dir.td_customValues[m].info); if (tv_size == 8) { if (!TIFFWriteDirectoryTagSrationalDoubleArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; } else { /*-- default should be tv_size == 4 */ if (!TIFFWriteDirectoryTagSrationalArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; /*-- ToDo: After Testing, this should be removed and * tv_size==4 should be set as default. */ if (tv_size != 4) { TIFFErrorExtR(tif, "TIFFLib: _TIFFWriteDirectorySec()", "Rational2Double: .set_field_type is " "not 4 but %d", tv_size); } } } break; case TIFF_FLOAT: if (!TIFFWriteDirectoryTagFloatArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_DOUBLE: if (!TIFFWriteDirectoryTagDoubleArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_IFD: if (!TIFFWriteDirectoryTagIfdArray( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; case TIFF_IFD8: if (!TIFFWriteDirectoryTagIfdIfd8Array( tif, &ndir, dir, tag, count, tif->tif_dir.td_customValues[m].value)) goto bad; break; default: assert(0); /* we should never get here */ break; } } if (dir != NULL) break; dir = _TIFFmallocExt(tif, ndir * sizeof(TIFFDirEntry)); if (dir == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); goto bad; } if (isimage) { if ((tif->tif_diroff == 0) && (!TIFFLinkDirectory(tif))) goto bad; } else tif->tif_diroff = (TIFFSeekFile(tif, 0, SEEK_END) + 1) & (~((toff_t)1)); if (pdiroff != NULL) *pdiroff = tif->tif_diroff; if (!(tif->tif_flags & TIFF_BIGTIFF)) dirsize = 2 + ndir * 12 + 4; else dirsize = 8 + ndir * 20 + 8; tif->tif_dataoff = tif->tif_diroff + dirsize; if (!(tif->tif_flags & TIFF_BIGTIFF)) tif->tif_dataoff = (uint32_t)tif->tif_dataoff; if ((tif->tif_dataoff < tif->tif_diroff) || (tif->tif_dataoff < (uint64_t)dirsize)) { TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded"); goto bad; } if (tif->tif_dataoff & 1) tif->tif_dataoff++; if (isimage) { if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER) tif->tif_curdir = 0; else tif->tif_curdir++; } } if (isimage) { if (TIFFFieldSet(tif, FIELD_SUBIFD) && (tif->tif_subifdoff == 0)) { uint32_t na; TIFFDirEntry *nb; for (na = 0, nb = dir;; na++, nb++) { if (na == ndir) { TIFFErrorExtR(tif, module, "Cannot find SubIFD tag"); goto bad; } if (nb->tdir_tag == TIFFTAG_SUBIFD) break; } if (!(tif->tif_flags & TIFF_BIGTIFF)) tif->tif_subifdoff = tif->tif_diroff + 2 + na * 12 + 8; else tif->tif_subifdoff = tif->tif_diroff + 8 + na * 20 + 12; } } dirmem = _TIFFmallocExt(tif, dirsize); if (dirmem == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); goto bad; } if (!(tif->tif_flags & TIFF_BIGTIFF)) { uint8_t *n; uint32_t nTmp; TIFFDirEntry *o; n = dirmem; *(uint16_t *)n = (uint16_t)ndir; if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort((uint16_t *)n); n += 2; o = dir; for (m = 0; m < ndir; m++) { *(uint16_t *)n = o->tdir_tag; if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort((uint16_t *)n); n += 2; *(uint16_t *)n = o->tdir_type; if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort((uint16_t *)n); n += 2; nTmp = (uint32_t)o->tdir_count; _TIFFmemcpy(n, &nTmp, 4); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong((uint32_t *)n); n += 4; /* This is correct. The data has been */ /* swabbed previously in TIFFWriteDirectoryTagData */ _TIFFmemcpy(n, &o->tdir_offset, 4); n += 4; o++; } nTmp = (uint32_t)tif->tif_nextdiroff; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&nTmp); _TIFFmemcpy(n, &nTmp, 4); } else { uint8_t *n; TIFFDirEntry *o; n = dirmem; *(uint64_t *)n = ndir; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8((uint64_t *)n); n += 8; o = dir; for (m = 0; m < ndir; m++) { *(uint16_t *)n = o->tdir_tag; if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort((uint16_t *)n); n += 2; *(uint16_t *)n = o->tdir_type; if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort((uint16_t *)n); n += 2; _TIFFmemcpy(n, &o->tdir_count, 8); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8((uint64_t *)n); n += 8; _TIFFmemcpy(n, &o->tdir_offset, 8); n += 8; o++; } _TIFFmemcpy(n, &tif->tif_nextdiroff, 8); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8((uint64_t *)n); } _TIFFfreeExt(tif, dir); dir = NULL; if (!SeekOK(tif, tif->tif_diroff)) { TIFFErrorExtR(tif, module, "IO error writing directory"); goto bad; } if (!WriteOK(tif, dirmem, (tmsize_t)dirsize)) { TIFFErrorExtR(tif, module, "IO error writing directory"); goto bad; } _TIFFfreeExt(tif, dirmem); if (imagedone) { TIFFFreeDirectory(tif); tif->tif_flags &= ~TIFF_DIRTYDIRECT; tif->tif_flags &= ~TIFF_DIRTYSTRIP; (*tif->tif_cleanup)(tif); /* * Reset directory-related state for subsequent * directories. */ TIFFCreateDirectory(tif); } return (1); bad: if (dir != NULL) _TIFFfreeExt(tif, dir); if (dirmem != NULL) _TIFFfreeExt(tif, dirmem); return (0); } static int8_t TIFFClampDoubleToInt8(double val) { if (val > 127) return 127; if (val < -128 || val != val) return -128; return (int8_t)val; } static int16_t TIFFClampDoubleToInt16(double val) { if (val > 32767) return 32767; if (val < -32768 || val != val) return -32768; return (int16_t)val; } static int32_t TIFFClampDoubleToInt32(double val) { if (val > 0x7FFFFFFF) return 0x7FFFFFFF; if (val < -0x7FFFFFFF - 1 || val != val) return -0x7FFFFFFF - 1; return (int32_t)val; } static uint8_t TIFFClampDoubleToUInt8(double val) { if (val < 0) return 0; if (val > 255 || val != val) return 255; return (uint8_t)val; } static uint16_t TIFFClampDoubleToUInt16(double val) { if (val < 0) return 0; if (val > 65535 || val != val) return 65535; return (uint16_t)val; } static uint32_t TIFFClampDoubleToUInt32(double val) { if (val < 0) return 0; if (val > 0xFFFFFFFFU || val != val) return 0xFFFFFFFFU; return (uint32_t)val; } static int TIFFWriteDirectoryTagSampleformatArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value) { static const char module[] = "TIFFWriteDirectoryTagSampleformatArray"; void *conv; uint32_t i; int ok; conv = _TIFFmallocExt(tif, count * sizeof(double)); if (conv == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } switch (tif->tif_dir.td_sampleformat) { case SAMPLEFORMAT_IEEEFP: if (tif->tif_dir.td_bitspersample <= 32) { for (i = 0; i < count; ++i) ((float *)conv)[i] = _TIFFClampDoubleToFloat(value[i]); ok = TIFFWriteDirectoryTagFloatArray(tif, ndir, dir, tag, count, (float *)conv); } else { ok = TIFFWriteDirectoryTagDoubleArray(tif, ndir, dir, tag, count, value); } break; case SAMPLEFORMAT_INT: if (tif->tif_dir.td_bitspersample <= 8) { for (i = 0; i < count; ++i) ((int8_t *)conv)[i] = TIFFClampDoubleToInt8(value[i]); ok = TIFFWriteDirectoryTagSbyteArray(tif, ndir, dir, tag, count, (int8_t *)conv); } else if (tif->tif_dir.td_bitspersample <= 16) { for (i = 0; i < count; ++i) ((int16_t *)conv)[i] = TIFFClampDoubleToInt16(value[i]); ok = TIFFWriteDirectoryTagSshortArray(tif, ndir, dir, tag, count, (int16_t *)conv); } else { for (i = 0; i < count; ++i) ((int32_t *)conv)[i] = TIFFClampDoubleToInt32(value[i]); ok = TIFFWriteDirectoryTagSlongArray(tif, ndir, dir, tag, count, (int32_t *)conv); } break; case SAMPLEFORMAT_UINT: if (tif->tif_dir.td_bitspersample <= 8) { for (i = 0; i < count; ++i) ((uint8_t *)conv)[i] = TIFFClampDoubleToUInt8(value[i]); ok = TIFFWriteDirectoryTagByteArray(tif, ndir, dir, tag, count, (uint8_t *)conv); } else if (tif->tif_dir.td_bitspersample <= 16) { for (i = 0; i < count; ++i) ((uint16_t *)conv)[i] = TIFFClampDoubleToUInt16(value[i]); ok = TIFFWriteDirectoryTagShortArray(tif, ndir, dir, tag, count, (uint16_t *)conv); } else { for (i = 0; i < count; ++i) ((uint32_t *)conv)[i] = TIFFClampDoubleToUInt32(value[i]); ok = TIFFWriteDirectoryTagLongArray(tif, ndir, dir, tag, count, (uint32_t *)conv); } break; default: ok = 0; } _TIFFfreeExt(tif, conv); return (ok); } static int TIFFWriteDirectoryTagAscii(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, char *value) { if (dir == NULL) { (*ndir)++; return (1); } return ( TIFFWriteDirectoryTagCheckedAscii(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagUndefinedArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedUndefinedArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagByteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedByteArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagSbyteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int8_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedSbyteArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagShort(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedShort(tif, ndir, dir, tag, value)); } static int TIFFWriteDirectoryTagShortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint16_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedShortArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagShortPerSample(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t value) { static const char module[] = "TIFFWriteDirectoryTagShortPerSample"; uint16_t *m; uint16_t *na; uint16_t nb; int o; if (dir == NULL) { (*ndir)++; return (1); } m = _TIFFmallocExt(tif, tif->tif_dir.td_samplesperpixel * sizeof(uint16_t)); if (m == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (na = m, nb = 0; nb < tif->tif_dir.td_samplesperpixel; na++, nb++) *na = value; o = TIFFWriteDirectoryTagCheckedShortArray( tif, ndir, dir, tag, tif->tif_dir.td_samplesperpixel, m); _TIFFfreeExt(tif, m); return (o); } static int TIFFWriteDirectoryTagSshortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int16_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedSshortArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagLong(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedLong(tif, ndir, dir, tag, value)); } static int TIFFWriteDirectoryTagLongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedLongArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagSlongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int32_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedSlongArray(tif, ndir, dir, tag, count, value)); } /************************************************************************/ /* TIFFWriteDirectoryTagLong8Array() */ /* */ /* Write either Long8 or Long array depending on file type. */ /************************************************************************/ static int TIFFWriteDirectoryTagLong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value) { static const char module[] = "TIFFWriteDirectoryTagLong8Array"; uint64_t *ma; uint32_t mb; uint32_t *p; uint32_t *q; int o; /* is this just a counting pass? */ if (dir == NULL) { (*ndir)++; return (1); } /* We always write Long8 for BigTIFF, no checking needed. */ if (tif->tif_flags & TIFF_BIGTIFF) return (TIFFWriteDirectoryTagCheckedLong8Array(tif, ndir, dir, tag, count, value)); /* ** For classic tiff we want to verify everything is in range for long ** and convert to long format. */ p = _TIFFmallocExt(tif, count * sizeof(uint32_t)); if (p == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++) { if (*ma > 0xFFFFFFFF) { TIFFErrorExtR(tif, module, "Attempt to write unsigned long value %" PRIu64 " larger than 0xFFFFFFFF for tag %d in Classic TIFF " "file. TIFF file writing aborted", *ma, tag); _TIFFfreeExt(tif, p); return (0); } *q = (uint32_t)(*ma); } o = TIFFWriteDirectoryTagCheckedLongArray(tif, ndir, dir, tag, count, p); _TIFFfreeExt(tif, p); return (o); } /************************************************************************/ /* TIFFWriteDirectoryTagSlong8Array() */ /* */ /* Write either SLong8 or SLong array depending on file type. */ /************************************************************************/ static int TIFFWriteDirectoryTagSlong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int64_t *value) { static const char module[] = "TIFFWriteDirectoryTagSlong8Array"; int64_t *ma; uint32_t mb; int32_t *p; int32_t *q; int o; /* is this just a counting pass? */ if (dir == NULL) { (*ndir)++; return (1); } /* We always write SLong8 for BigTIFF, no checking needed. */ if (tif->tif_flags & TIFF_BIGTIFF) return (TIFFWriteDirectoryTagCheckedSlong8Array(tif, ndir, dir, tag, count, value)); /* ** For classic tiff we want to verify everything is in range for signed-long ** and convert to signed-long format. */ p = _TIFFmallocExt(tif, count * sizeof(uint32_t)); if (p == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++) { if (*ma > (2147483647)) { TIFFErrorExtR(tif, module, "Attempt to write signed long value %" PRIi64 " larger than 0x7FFFFFFF (2147483647) for tag %d in " "Classic TIFF file. TIFF writing to file aborted", *ma, tag); _TIFFfreeExt(tif, p); return (0); } else if (*ma < (-2147483647 - 1)) { TIFFErrorExtR(tif, module, "Attempt to write signed long value %" PRIi64 " smaller than 0x80000000 (-2147483648) for tag %d " "in Classic TIFF file. TIFF writing to file aborted", *ma, tag); _TIFFfreeExt(tif, p); return (0); } *q = (int32_t)(*ma); } o = TIFFWriteDirectoryTagCheckedSlongArray(tif, ndir, dir, tag, count, p); _TIFFfreeExt(tif, p); return (o); } static int TIFFWriteDirectoryTagRational(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, double value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedRational(tif, ndir, dir, tag, value)); } static int TIFFWriteDirectoryTagRationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedRationalArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagSrationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedSrationalArray(tif, ndir, dir, tag, count, value)); } /*-- Rational2Double: additional write functions */ static int TIFFWriteDirectoryTagRationalDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedRationalDoubleArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagSrationalDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedSrationalDoubleArray( tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagFloatArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedFloatArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedDoubleArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagIfdArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value) { if (dir == NULL) { (*ndir)++; return (1); } return (TIFFWriteDirectoryTagCheckedIfdArray(tif, ndir, dir, tag, count, value)); } static int TIFFWriteDirectoryTagShortLong(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t value) { if (dir == NULL) { (*ndir)++; return (1); } if (value <= 0xFFFF) return (TIFFWriteDirectoryTagCheckedShort(tif, ndir, dir, tag, (uint16_t)value)); else return (TIFFWriteDirectoryTagCheckedLong(tif, ndir, dir, tag, value)); } static int _WriteAsType(TIFF *tif, uint64_t strile_size, uint64_t uncompressed_threshold) { const uint16_t compression = tif->tif_dir.td_compression; if (compression == COMPRESSION_NONE) { return strile_size > uncompressed_threshold; } else if (compression == COMPRESSION_JPEG || compression == COMPRESSION_LZW || compression == COMPRESSION_ADOBE_DEFLATE || compression == COMPRESSION_DEFLATE || compression == COMPRESSION_LZMA || compression == COMPRESSION_LERC || compression == COMPRESSION_ZSTD || compression == COMPRESSION_WEBP || compression == COMPRESSION_JXL) { /* For a few select compression types, we assume that in the worst */ /* case the compressed size will be 10 times the uncompressed size */ /* This is overly pessismistic ! */ return strile_size >= uncompressed_threshold / 10; } return 1; } static int WriteAsLong8(TIFF *tif, uint64_t strile_size) { return _WriteAsType(tif, strile_size, 0xFFFFFFFFU); } static int WriteAsLong4(TIFF *tif, uint64_t strile_size) { return _WriteAsType(tif, strile_size, 0xFFFFU); } /************************************************************************/ /* TIFFWriteDirectoryTagLongLong8Array() */ /* */ /* Write out LONG8 array and write a SHORT/LONG/LONG8 depending */ /* on strile size and Classic/BigTIFF mode. */ /************************************************************************/ static int TIFFWriteDirectoryTagLongLong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value) { static const char module[] = "TIFFWriteDirectoryTagLongLong8Array"; int o; int write_aslong4; /* is this just a counting pass? */ if (dir == NULL) { (*ndir)++; return (1); } if (tif->tif_dir.td_deferstrilearraywriting) { return TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_NOTYPE, 0, 0, NULL); } if (tif->tif_flags & TIFF_BIGTIFF) { int write_aslong8 = 1; /* In the case of ByteCounts array, we may be able to write them on */ /* LONG if the strip/tilesize is not too big. */ /* Also do that for count > 1 in the case someone would want to create */ /* a single-strip file with a growing height, in which case using */ /* LONG8 will be safer. */ if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS) { write_aslong8 = WriteAsLong8(tif, TIFFStripSize64(tif)); } else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS) { write_aslong8 = WriteAsLong8(tif, TIFFTileSize64(tif)); } if (write_aslong8) { return TIFFWriteDirectoryTagCheckedLong8Array(tif, ndir, dir, tag, count, value); } } write_aslong4 = 1; if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS) { write_aslong4 = WriteAsLong4(tif, TIFFStripSize64(tif)); } else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS) { write_aslong4 = WriteAsLong4(tif, TIFFTileSize64(tif)); } if (write_aslong4) { /* ** For classic tiff we want to verify everything is in range for LONG ** and convert to long format. */ uint32_t *p = _TIFFmallocExt(tif, count * sizeof(uint32_t)); uint32_t *q; uint64_t *ma; uint32_t mb; if (p == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++) { if (*ma > 0xFFFFFFFF) { TIFFErrorExtR(tif, module, "Attempt to write value larger than 0xFFFFFFFF " "in LONG array."); _TIFFfreeExt(tif, p); return (0); } *q = (uint32_t)(*ma); } o = TIFFWriteDirectoryTagCheckedLongArray(tif, ndir, dir, tag, count, p); _TIFFfreeExt(tif, p); } else { uint16_t *p = _TIFFmallocExt(tif, count * sizeof(uint16_t)); uint16_t *q; uint64_t *ma; uint32_t mb; if (p == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++) { if (*ma > 0xFFFF) { /* Should not happen normally given the check we did before */ TIFFErrorExtR(tif, module, "Attempt to write value larger than 0xFFFF in " "SHORT array."); _TIFFfreeExt(tif, p); return (0); } *q = (uint16_t)(*ma); } o = TIFFWriteDirectoryTagCheckedShortArray(tif, ndir, dir, tag, count, p); _TIFFfreeExt(tif, p); } return (o); } /************************************************************************/ /* TIFFWriteDirectoryTagIfdIfd8Array() */ /* */ /* Write either IFD8 or IFD array depending on file type. */ /************************************************************************/ static int TIFFWriteDirectoryTagIfdIfd8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value) { static const char module[] = "TIFFWriteDirectoryTagIfdIfd8Array"; uint64_t *ma; uint32_t mb; uint32_t *p; uint32_t *q; int o; /* is this just a counting pass? */ if (dir == NULL) { (*ndir)++; return (1); } /* We always write IFD8 for BigTIFF, no checking needed. */ if (tif->tif_flags & TIFF_BIGTIFF) return TIFFWriteDirectoryTagCheckedIfd8Array(tif, ndir, dir, tag, count, value); /* ** For classic tiff we want to verify everything is in range for IFD ** and convert to long format. */ p = _TIFFmallocExt(tif, count * sizeof(uint32_t)); if (p == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++) { if (*ma > 0xFFFFFFFF) { TIFFErrorExtR(tif, module, "Attempt to write value larger than 0xFFFFFFFF in " "Classic TIFF file."); _TIFFfreeExt(tif, p); return (0); } *q = (uint32_t)(*ma); } o = TIFFWriteDirectoryTagCheckedIfdArray(tif, ndir, dir, tag, count, p); _TIFFfreeExt(tif, p); return (o); } static int TIFFWriteDirectoryTagColormap(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir) { static const char module[] = "TIFFWriteDirectoryTagColormap"; uint32_t m; uint16_t *n; int o; if (dir == NULL) { (*ndir)++; return (1); } m = (1 << tif->tif_dir.td_bitspersample); n = _TIFFmallocExt(tif, 3 * m * sizeof(uint16_t)); if (n == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } _TIFFmemcpy(&n[0], tif->tif_dir.td_colormap[0], m * sizeof(uint16_t)); _TIFFmemcpy(&n[m], tif->tif_dir.td_colormap[1], m * sizeof(uint16_t)); _TIFFmemcpy(&n[2 * m], tif->tif_dir.td_colormap[2], m * sizeof(uint16_t)); o = TIFFWriteDirectoryTagCheckedShortArray(tif, ndir, dir, TIFFTAG_COLORMAP, 3 * m, n); _TIFFfreeExt(tif, n); return (o); } static int TIFFWriteDirectoryTagTransferfunction(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir) { static const char module[] = "TIFFWriteDirectoryTagTransferfunction"; uint32_t m; uint16_t n; uint16_t *o; int p; if (dir == NULL) { (*ndir)++; return (1); } m = (1 << tif->tif_dir.td_bitspersample); n = tif->tif_dir.td_samplesperpixel - tif->tif_dir.td_extrasamples; /* * Check if the table can be written as a single column, * or if it must be written as 3 columns. Note that we * write a 3-column tag if there are 2 samples/pixel and * a single column of data won't suffice--hmm. */ if (n > 3) n = 3; if (n == 3) { if (tif->tif_dir.td_transferfunction[2] == NULL || !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0], tif->tif_dir.td_transferfunction[2], m * sizeof(uint16_t))) n = 2; } if (n == 2) { if (tif->tif_dir.td_transferfunction[1] == NULL || !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0], tif->tif_dir.td_transferfunction[1], m * sizeof(uint16_t))) n = 1; } if (n == 0) n = 1; o = _TIFFmallocExt(tif, n * m * sizeof(uint16_t)); if (o == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } _TIFFmemcpy(&o[0], tif->tif_dir.td_transferfunction[0], m * sizeof(uint16_t)); if (n > 1) _TIFFmemcpy(&o[m], tif->tif_dir.td_transferfunction[1], m * sizeof(uint16_t)); if (n > 2) _TIFFmemcpy(&o[2 * m], tif->tif_dir.td_transferfunction[2], m * sizeof(uint16_t)); p = TIFFWriteDirectoryTagCheckedShortArray( tif, ndir, dir, TIFFTAG_TRANSFERFUNCTION, n * m, o); _TIFFfreeExt(tif, o); return (p); } static int TIFFWriteDirectoryTagSubifd(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir) { static const char module[] = "TIFFWriteDirectoryTagSubifd"; uint64_t m; int n; if (tif->tif_dir.td_nsubifd == 0) return (1); if (dir == NULL) { (*ndir)++; return (1); } m = tif->tif_dataoff; if (!(tif->tif_flags & TIFF_BIGTIFF)) { uint32_t *o; uint64_t *pa; uint32_t *pb; uint16_t p; o = _TIFFmallocExt(tif, tif->tif_dir.td_nsubifd * sizeof(uint32_t)); if (o == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } pa = tif->tif_dir.td_subifd; pb = o; for (p = 0; p < tif->tif_dir.td_nsubifd; p++) { assert(pa != 0); /* Could happen if an classicTIFF has a SubIFD of type LONG8 (which * is illegal) */ if (*pa > 0xFFFFFFFFUL) { TIFFErrorExtR(tif, module, "Illegal value for SubIFD tag"); _TIFFfreeExt(tif, o); return (0); } *pb++ = (uint32_t)(*pa++); } n = TIFFWriteDirectoryTagCheckedIfdArray(tif, ndir, dir, TIFFTAG_SUBIFD, tif->tif_dir.td_nsubifd, o); _TIFFfreeExt(tif, o); } else n = TIFFWriteDirectoryTagCheckedIfd8Array( tif, ndir, dir, TIFFTAG_SUBIFD, tif->tif_dir.td_nsubifd, tif->tif_dir.td_subifd); if (!n) return (0); /* * Total hack: if this directory includes a SubIFD * tag then force the next directories to be * written as ``sub directories'' of this one. This * is used to write things like thumbnails and * image masks that one wants to keep out of the * normal directory linkage access mechanism. */ tif->tif_flags |= TIFF_INSUBIFD; tif->tif_nsubifd = tif->tif_dir.td_nsubifd; if (tif->tif_dir.td_nsubifd == 1) tif->tif_subifdoff = 0; else tif->tif_subifdoff = m; return (1); } static int TIFFWriteDirectoryTagCheckedAscii(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, char *value) { assert(sizeof(char) == 1); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_ASCII, count, count, value)); } static int TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value) { assert(sizeof(uint8_t) == 1); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_UNDEFINED, count, count, value)); } static int TIFFWriteDirectoryTagCheckedByteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint8_t *value) { assert(sizeof(uint8_t) == 1); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_BYTE, count, count, value)); } static int TIFFWriteDirectoryTagCheckedSbyteArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int8_t *value) { assert(sizeof(int8_t) == 1); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SBYTE, count, count, value)); } static int TIFFWriteDirectoryTagCheckedShort(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t value) { uint16_t m; assert(sizeof(uint16_t) == 2); m = value; if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&m); return ( TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SHORT, 1, 2, &m)); } static int TIFFWriteDirectoryTagCheckedShortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint16_t *value) { assert(count < 0x80000000); assert(sizeof(uint16_t) == 2); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfShort(value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SHORT, count, count * 2, value)); } static int TIFFWriteDirectoryTagCheckedSshortArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int16_t *value) { assert(count < 0x80000000); assert(sizeof(int16_t) == 2); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfShort((uint16_t *)value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SSHORT, count, count * 2, value)); } static int TIFFWriteDirectoryTagCheckedLong(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t value) { uint32_t m; assert(sizeof(uint32_t) == 4); m = value; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&m); return ( TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_LONG, 1, 4, &m)); } static int TIFFWriteDirectoryTagCheckedLongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value) { assert(count < 0x40000000); assert(sizeof(uint32_t) == 4); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong(value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_LONG, count, count * 4, value)); } static int TIFFWriteDirectoryTagCheckedSlongArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int32_t *value) { assert(count < 0x40000000); assert(sizeof(int32_t) == 4); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong((uint32_t *)value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SLONG, count, count * 4, value)); } static int TIFFWriteDirectoryTagCheckedLong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value) { assert(count < 0x20000000); assert(sizeof(uint64_t) == 8); if (!(tif->tif_flags & TIFF_BIGTIFF)) { TIFFErrorExtR(tif, "TIFFWriteDirectoryTagCheckedLong8Array", "LONG8 not allowed for ClassicTIFF"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong8(value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_LONG8, count, count * 8, value)); } static int TIFFWriteDirectoryTagCheckedSlong8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, int64_t *value) { assert(count < 0x20000000); assert(sizeof(int64_t) == 8); if (!(tif->tif_flags & TIFF_BIGTIFF)) { TIFFErrorExtR(tif, "TIFFWriteDirectoryTagCheckedSlong8Array", "SLONG8 not allowed for ClassicTIFF"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong8((uint64_t *)value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SLONG8, count, count * 8, value)); } static int TIFFWriteDirectoryTagCheckedRational(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, double value) { static const char module[] = "TIFFWriteDirectoryTagCheckedRational"; uint32_t m[2]; assert(sizeof(uint32_t) == 4); if (value < 0) { TIFFErrorExtR(tif, module, "Negative value is illegal"); return 0; } else if (value != value) { TIFFErrorExtR(tif, module, "Not-a-number value is illegal"); return 0; } /*--Rational2Double: New function also used for non-custom rational tags. * However, could be omitted here, because * TIFFWriteDirectoryTagCheckedRational() is not used by code for custom * tags, only by code for named-tiff-tags like FIELD_RESOLUTION and * FIELD_POSITION */ else { DoubleToRational(value, &m[0], &m[1]); } if (tif->tif_flags & TIFF_SWAB) { TIFFSwabLong(&m[0]); TIFFSwabLong(&m[1]); } return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_RATIONAL, 1, 8, &m[0])); } static int TIFFWriteDirectoryTagCheckedRationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value) { static const char module[] = "TIFFWriteDirectoryTagCheckedRationalArray"; uint32_t *m; float *na; uint32_t *nb; uint32_t nc; int o; assert(sizeof(uint32_t) == 4); m = _TIFFmallocExt(tif, count * 2 * sizeof(uint32_t)); if (m == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++) { DoubleToRational(*na, &nb[0], &nb[1]); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong(m, count * 2); o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_RATIONAL, count, count * 8, &m[0]); _TIFFfreeExt(tif, m); return (o); } static int TIFFWriteDirectoryTagCheckedSrationalArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value) { static const char module[] = "TIFFWriteDirectoryTagCheckedSrationalArray"; int32_t *m; float *na; int32_t *nb; uint32_t nc; int o; assert(sizeof(int32_t) == 4); m = _TIFFmallocExt(tif, count * 2 * sizeof(int32_t)); if (m == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++) { DoubleToSrational(*na, &nb[0], &nb[1]); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong((uint32_t *)m, count * 2); o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SRATIONAL, count, count * 8, &m[0]); _TIFFfreeExt(tif, m); return (o); } /*-- Rational2Double: additional write functions for double arrays */ static int TIFFWriteDirectoryTagCheckedRationalDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value) { static const char module[] = "TIFFWriteDirectoryTagCheckedRationalDoubleArray"; uint32_t *m; double *na; uint32_t *nb; uint32_t nc; int o; assert(sizeof(uint32_t) == 4); m = _TIFFmallocExt(tif, count * 2 * sizeof(uint32_t)); if (m == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++) { DoubleToRational(*na, &nb[0], &nb[1]); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong(m, count * 2); o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_RATIONAL, count, count * 8, &m[0]); _TIFFfreeExt(tif, m); return (o); } /*-- TIFFWriteDirectoryTagCheckedRationalDoubleArray() ------- */ static int TIFFWriteDirectoryTagCheckedSrationalDoubleArray( TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value) { static const char module[] = "TIFFWriteDirectoryTagCheckedSrationalDoubleArray"; int32_t *m; double *na; int32_t *nb; uint32_t nc; int o; assert(sizeof(int32_t) == 4); m = _TIFFmallocExt(tif, count * 2 * sizeof(int32_t)); if (m == NULL) { TIFFErrorExtR(tif, module, "Out of memory"); return (0); } for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++) { DoubleToSrational(*na, &nb[0], &nb[1]); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong((uint32_t *)m, count * 2); o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SRATIONAL, count, count * 8, &m[0]); _TIFFfreeExt(tif, m); return (o); } /*--- TIFFWriteDirectoryTagCheckedSrationalDoubleArray() -------- */ /** ----- Rational2Double: Double To Rational Conversion ---------------------------------------------------------- * There is a mathematical theorem to convert real numbers into a rational (integer fraction) number. * This is called "continuous fraction" which uses the Euclidean algorithm to find the greatest common divisor (GCD). * (ref. e.g. https://de.wikipedia.org/wiki/Kettenbruch or https://en.wikipedia.org/wiki/Continued_fraction * https://en.wikipedia.org/wiki/Euclidean_algorithm) * The following functions implement the * - ToRationalEuclideanGCD() auxiliary function which mainly implements euclidean GCD * - DoubleToRational() conversion function for un-signed rationals * - DoubleToSrational() conversion function for signed rationals ------------------------------------------------------------------------------------------------------------------*/ /**---- ToRationalEuclideanGCD() ----------------------------------------- * Calculates the rational fractional of a double input value * using the Euclidean algorithm to find the greatest common divisor (GCD) ------------------------------------------------------------------------*/ static void ToRationalEuclideanGCD(double value, int blnUseSignedRange, int blnUseSmallRange, uint64_t *ullNum, uint64_t *ullDenom) { /* Internally, the integer variables can be bigger than the external ones, * as long as the result will fit into the external variable size. */ uint64_t numSum[3] = {0, 1, 0}, denomSum[3] = {1, 0, 0}; uint64_t aux, bigNum, bigDenom; uint64_t returnLimit; int i; uint64_t nMax; double fMax; unsigned long maxDenom; /*-- nMax and fMax defines the initial accuracy of the starting fractional, * or better, the highest used integer numbers used within the starting * fractional (bigNum/bigDenom). There are two approaches, which can * accidentally lead to different accuracies just depending on the value. * Therefore, blnUseSmallRange steers this behavior. * For long long nMax = ((9223372036854775807-1)/2); for long nMax = * ((2147483647-1)/2); */ if (blnUseSmallRange) { nMax = (uint64_t)((2147483647 - 1) / 2); /* for ULONG range */ } else { nMax = ((9223372036854775807 - 1) / 2); /* for ULLONG range */ } fMax = (double)nMax; /*-- For the Euclidean GCD define the denominator range, so that it stays * within size of unsigned long variables. maxDenom should be LONG_MAX for * negative values and ULONG_MAX for positive ones. Also the final returned * value of ullNum and ullDenom is limited according to signed- or * unsigned-range. */ if (blnUseSignedRange) { maxDenom = 2147483647UL; /*LONG_MAX = 0x7FFFFFFFUL*/ returnLimit = maxDenom; } else { maxDenom = 0xFFFFFFFFUL; /*ULONG_MAX = 0xFFFFFFFFUL*/ returnLimit = maxDenom; } /*-- First generate a rational fraction (bigNum/bigDenom) which represents *the value as a rational number with the highest accuracy. Therefore, *uint64_t (uint64_t) is needed. This rational fraction is then reduced *using the Euclidean algorithm to find the greatest common divisor (GCD). * bigNum = big numinator of value without fraction (or cut residual *fraction) bigDenom = big denominator of value *-- Break-criteria so that uint64_t cast to "bigNum" introduces no error *and bigDenom has no overflow, and stop with enlargement of fraction when *the double-value of it reaches an integer number without fractional part. */ bigDenom = 1; while ((value != floor(value)) && (value < fMax) && (bigDenom < nMax)) { bigDenom <<= 1; value *= 2; } bigNum = (uint64_t)value; /*-- Start Euclidean algorithm to find the greatest common divisor (GCD) -- */ #define MAX_ITERATIONS 64 for (i = 0; i < MAX_ITERATIONS; i++) { uint64_t val; /* if bigDenom is not zero, calculate integer part of fraction. */ if (bigDenom == 0) { break; } val = bigNum / bigDenom; /* Set bigDenom to reminder of bigNum/bigDenom and bigNum to previous * denominator bigDenom. */ aux = bigNum; bigNum = bigDenom; bigDenom = aux % bigDenom; /* calculate next denominator and check for its given maximum */ aux = val; if (denomSum[1] * val + denomSum[0] >= maxDenom) { aux = (maxDenom - denomSum[0]) / denomSum[1]; if (aux * 2 >= val || denomSum[1] >= maxDenom) i = (MAX_ITERATIONS + 1); /* exit but execute rest of for-loop */ else break; } /* calculate next numerator to numSum2 and save previous one to numSum0; * numSum1 just copy of numSum2. */ numSum[2] = aux * numSum[1] + numSum[0]; numSum[0] = numSum[1]; numSum[1] = numSum[2]; /* calculate next denominator to denomSum2 and save previous one to * denomSum0; denomSum1 just copy of denomSum2. */ denomSum[2] = aux * denomSum[1] + denomSum[0]; denomSum[0] = denomSum[1]; denomSum[1] = denomSum[2]; } /*-- Check and adapt for final variable size and return values; reduces * internal accuracy; denominator is kept in ULONG-range with maxDenom -- */ while (numSum[1] > returnLimit || denomSum[1] > returnLimit) { numSum[1] = numSum[1] / 2; denomSum[1] = denomSum[1] / 2; } /* return values */ *ullNum = numSum[1]; *ullDenom = denomSum[1]; } /*-- ToRationalEuclideanGCD() -------------- */ /**---- DoubleToRational() ----------------------------------------------- * Calculates the rational fractional of a double input value * for UN-SIGNED rationals, * using the Euclidean algorithm to find the greatest common divisor (GCD) ------------------------------------------------------------------------*/ static void DoubleToRational(double value, uint32_t *num, uint32_t *denom) { /*---- UN-SIGNED RATIONAL ---- */ double dblDiff, dblDiff2; uint64_t ullNum, ullDenom, ullNum2, ullDenom2; /*-- Check for negative values. If so it is an error. */ /* Test written that way to catch NaN */ if (!(value >= 0)) { *num = *denom = 0; TIFFErrorExt(0, "TIFFLib: DoubleToRational()", " Negative Value for Unsigned Rational given."); return; } /*-- Check for too big numbers (> ULONG_MAX) -- */ if (value > 0xFFFFFFFFUL) { *num = 0xFFFFFFFFU; *denom = 0; return; } /*-- Check for easy integer numbers -- */ if (value == (uint32_t)(value)) { *num = (uint32_t)value; *denom = 1; return; } /*-- Check for too small numbers for "unsigned long" type rationals -- */ if (value < 1.0 / (double)0xFFFFFFFFUL) { *num = 0; *denom = 0xFFFFFFFFU; return; } /*-- There are two approaches using the Euclidean algorithm, * which can accidentally lead to different accuracies just depending on * the value. Try both and define which one was better. */ ToRationalEuclideanGCD(value, FALSE, FALSE, &ullNum, &ullDenom); ToRationalEuclideanGCD(value, FALSE, TRUE, &ullNum2, &ullDenom2); /*-- Double-Check, that returned values fit into ULONG :*/ if (ullNum > 0xFFFFFFFFUL || ullDenom > 0xFFFFFFFFUL || ullNum2 > 0xFFFFFFFFUL || ullDenom2 > 0xFFFFFFFFUL) { TIFFErrorExt(0, "TIFFLib: DoubleToRational()", " Num or Denom exceeds ULONG: val=%14.6f, num=%12" PRIu64 ", denom=%12" PRIu64 " | num2=%12" PRIu64 ", denom2=%12" PRIu64 "", value, ullNum, ullDenom, ullNum2, ullDenom2); assert(0); } /* Check, which one has higher accuracy and take that. */ dblDiff = fabs(value - ((double)ullNum / (double)ullDenom)); dblDiff2 = fabs(value - ((double)ullNum2 / (double)ullDenom2)); if (dblDiff < dblDiff2) { *num = (uint32_t)ullNum; *denom = (uint32_t)ullDenom; } else { *num = (uint32_t)ullNum2; *denom = (uint32_t)ullDenom2; } } /*-- DoubleToRational() -------------- */ /**---- DoubleToSrational() ----------------------------------------------- * Calculates the rational fractional of a double input value * for SIGNED rationals, * using the Euclidean algorithm to find the greatest common divisor (GCD) ------------------------------------------------------------------------*/ static void DoubleToSrational(double value, int32_t *num, int32_t *denom) { /*---- SIGNED RATIONAL ----*/ int neg = 1; double dblDiff, dblDiff2; uint64_t ullNum, ullDenom, ullNum2, ullDenom2; /*-- Check for negative values and use then the positive one for internal * calculations, but take the sign into account before returning. */ if (value < 0) { neg = -1; value = -value; } /*-- Check for too big numbers (> LONG_MAX) -- */ if (value > 0x7FFFFFFFL) { *num = 0x7FFFFFFFL; *denom = 0; return; } /*-- Check for easy numbers -- */ if (value == (int32_t)(value)) { *num = (int32_t)(neg * value); *denom = 1; return; } /*-- Check for too small numbers for "long" type rationals -- */ if (value < 1.0 / (double)0x7FFFFFFFL) { *num = 0; *denom = 0x7FFFFFFFL; return; } /*-- There are two approaches using the Euclidean algorithm, * which can accidentally lead to different accuracies just depending on * the value. Try both and define which one was better. Furthermore, set * behavior of ToRationalEuclideanGCD() to the range of signed-long. */ ToRationalEuclideanGCD(value, TRUE, FALSE, &ullNum, &ullDenom); ToRationalEuclideanGCD(value, TRUE, TRUE, &ullNum2, &ullDenom2); /*-- Double-Check, that returned values fit into LONG :*/ if (ullNum > 0x7FFFFFFFL || ullDenom > 0x7FFFFFFFL || ullNum2 > 0x7FFFFFFFL || ullDenom2 > 0x7FFFFFFFL) { TIFFErrorExt(0, "TIFFLib: DoubleToSrational()", " Num or Denom exceeds LONG: val=%14.6f, num=%12" PRIu64 ", denom=%12" PRIu64 " | num2=%12" PRIu64 ", denom2=%12" PRIu64 "", neg * value, ullNum, ullDenom, ullNum2, ullDenom2); assert(0); } /* Check, which one has higher accuracy and take that. */ dblDiff = fabs(value - ((double)ullNum / (double)ullDenom)); dblDiff2 = fabs(value - ((double)ullNum2 / (double)ullDenom2)); if (dblDiff < dblDiff2) { *num = (int32_t)(neg * (long)ullNum); *denom = (int32_t)ullDenom; } else { *num = (int32_t)(neg * (long)ullNum2); *denom = (int32_t)ullDenom2; } } /*-- DoubleToSrational() --------------*/ static int TIFFWriteDirectoryTagCheckedFloatArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, float *value) { assert(count < 0x40000000); assert(sizeof(float) == 4); TIFFCvtNativeToIEEEFloat(tif, count, &value); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfFloat(value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_FLOAT, count, count * 4, value)); } static int TIFFWriteDirectoryTagCheckedDoubleArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, double *value) { assert(count < 0x20000000); assert(sizeof(double) == 8); TIFFCvtNativeToIEEEDouble(tif, count, &value); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfDouble(value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_DOUBLE, count, count * 8, value)); } static int TIFFWriteDirectoryTagCheckedIfdArray(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint32_t *value) { assert(count < 0x40000000); assert(sizeof(uint32_t) == 4); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong(value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_IFD, count, count * 4, value)); } static int TIFFWriteDirectoryTagCheckedIfd8Array(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count, uint64_t *value) { assert(count < 0x20000000); assert(sizeof(uint64_t) == 8); assert(tif->tif_flags & TIFF_BIGTIFF); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong8(value, count); return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_IFD8, count, count * 8, value)); } static int TIFFWriteDirectoryTagData(TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint16_t datatype, uint32_t count, uint32_t datalength, void *data) { static const char module[] = "TIFFWriteDirectoryTagData"; uint32_t m; m = 0; while (m < (*ndir)) { assert(dir[m].tdir_tag != tag); if (dir[m].tdir_tag > tag) break; m++; } if (m < (*ndir)) { uint32_t n; for (n = *ndir; n > m; n--) dir[n] = dir[n - 1]; } dir[m].tdir_tag = tag; dir[m].tdir_type = datatype; dir[m].tdir_count = count; dir[m].tdir_offset.toff_long8 = 0; if (datalength <= ((tif->tif_flags & TIFF_BIGTIFF) ? 0x8U : 0x4U)) { if (data && datalength) { _TIFFmemcpy(&dir[m].tdir_offset, data, datalength); } } else { uint64_t na, nb; na = tif->tif_dataoff; nb = na + datalength; if (!(tif->tif_flags & TIFF_BIGTIFF)) nb = (uint32_t)nb; if ((nb < na) || (nb < datalength)) { TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded"); return (0); } if (!SeekOK(tif, na)) { TIFFErrorExtR(tif, module, "IO error writing tag data"); return (0); } if (datalength >= 0x80000000UL) { TIFFErrorExtR(tif, module, "libtiff does not allow writing more than 2147483647 " "bytes in a tag"); return (0); } if (!WriteOK(tif, data, (tmsize_t)datalength)) { TIFFErrorExtR(tif, module, "IO error writing tag data"); return (0); } tif->tif_dataoff = nb; if (tif->tif_dataoff & 1) tif->tif_dataoff++; if (!(tif->tif_flags & TIFF_BIGTIFF)) { uint32_t o; o = (uint32_t)na; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&o); _TIFFmemcpy(&dir[m].tdir_offset, &o, 4); } else { dir[m].tdir_offset.toff_long8 = na; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&dir[m].tdir_offset.toff_long8); } } (*ndir)++; return (1); } /* * Link the current directory into the directory chain for the file. */ static int TIFFLinkDirectory(TIFF *tif) { static const char module[] = "TIFFLinkDirectory"; tif->tif_diroff = (TIFFSeekFile(tif, 0, SEEK_END) + 1) & (~((toff_t)1)); /* * Handle SubIFDs */ if (tif->tif_flags & TIFF_INSUBIFD) { if (!(tif->tif_flags & TIFF_BIGTIFF)) { uint32_t m; m = (uint32_t)tif->tif_diroff; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&m); (void)TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET); if (!WriteOK(tif, &m, 4)) { TIFFErrorExtR(tif, module, "Error writing SubIFD directory link"); return (0); } /* * Advance to the next SubIFD or, if this is * the last one configured, revert back to the * normal directory linkage. */ if (--tif->tif_nsubifd) tif->tif_subifdoff += 4; else tif->tif_flags &= ~TIFF_INSUBIFD; return (1); } else { uint64_t m; m = tif->tif_diroff; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&m); (void)TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET); if (!WriteOK(tif, &m, 8)) { TIFFErrorExtR(tif, module, "Error writing SubIFD directory link"); return (0); } /* * Advance to the next SubIFD or, if this is * the last one configured, revert back to the * normal directory linkage. */ if (--tif->tif_nsubifd) tif->tif_subifdoff += 8; else tif->tif_flags &= ~TIFF_INSUBIFD; return (1); } } if (!(tif->tif_flags & TIFF_BIGTIFF)) { uint32_t m; uint32_t nextdir; m = (uint32_t)(tif->tif_diroff); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&m); if (tif->tif_header.classic.tiff_diroff == 0) { /* * First directory, overwrite offset in header. */ tif->tif_header.classic.tiff_diroff = (uint32_t)tif->tif_diroff; tif->tif_lastdiroff = tif->tif_diroff; (void)TIFFSeekFile(tif, 4, SEEK_SET); if (!WriteOK(tif, &m, 4)) { TIFFErrorExtR(tif, tif->tif_name, "Error writing TIFF header"); return (0); } return (1); } /* * Not the first directory, search to the last and append. */ if (tif->tif_lastdiroff != 0) { nextdir = (uint32_t)tif->tif_lastdiroff; } else { nextdir = tif->tif_header.classic.tiff_diroff; } while (1) { uint16_t dircount; uint32_t nextnextdir; if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount, 2)) { TIFFErrorExtR(tif, module, "Error fetching directory count"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&dircount); (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12, SEEK_SET); if (!ReadOK(tif, &nextnextdir, 4)) { TIFFErrorExtR(tif, module, "Error fetching directory link"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&nextnextdir); if (nextnextdir == 0) { (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12, SEEK_SET); if (!WriteOK(tif, &m, 4)) { TIFFErrorExtR(tif, module, "Error writing directory link"); return (0); } tif->tif_lastdiroff = tif->tif_diroff; break; } nextdir = nextnextdir; } } else { uint64_t m; uint64_t nextdir; m = tif->tif_diroff; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&m); if (tif->tif_header.big.tiff_diroff == 0) { /* * First directory, overwrite offset in header. */ tif->tif_header.big.tiff_diroff = tif->tif_diroff; tif->tif_lastdiroff = tif->tif_diroff; (void)TIFFSeekFile(tif, 8, SEEK_SET); if (!WriteOK(tif, &m, 8)) { TIFFErrorExtR(tif, tif->tif_name, "Error writing TIFF header"); return (0); } return (1); } /* * Not the first directory, search to the last and append. */ if (tif->tif_lastdiroff != 0) { nextdir = tif->tif_lastdiroff; } else { nextdir = tif->tif_header.big.tiff_diroff; } while (1) { uint64_t dircount64; uint16_t dircount; uint64_t nextnextdir; if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount64, 8)) { TIFFErrorExtR(tif, module, "Error fetching directory count"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&dircount64); if (dircount64 > 0xFFFF) { TIFFErrorExtR( tif, module, "Sanity check on tag count failed, likely corrupt TIFF"); return (0); } dircount = (uint16_t)dircount64; (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20, SEEK_SET); if (!ReadOK(tif, &nextnextdir, 8)) { TIFFErrorExtR(tif, module, "Error fetching directory link"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&nextnextdir); if (nextnextdir == 0) { (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20, SEEK_SET); if (!WriteOK(tif, &m, 8)) { TIFFErrorExtR(tif, module, "Error writing directory link"); return (0); } tif->tif_lastdiroff = tif->tif_diroff; break; } nextdir = nextnextdir; } } return (1); } /************************************************************************/ /* TIFFRewriteField() */ /* */ /* Rewrite a field in the directory on disk without regard to */ /* updating the TIFF directory structure in memory. Currently */ /* only supported for field that already exist in the on-disk */ /* directory. Mainly used for updating stripoffset / */ /* stripbytecount values after the directory is already on */ /* disk. */ /* */ /* Returns zero on failure, and one on success. */ /************************************************************************/ int _TIFFRewriteField(TIFF *tif, uint16_t tag, TIFFDataType in_datatype, tmsize_t count, void *data) { static const char module[] = "TIFFResetField"; /* const TIFFField* fip = NULL; */ uint16_t dircount; tmsize_t dirsize; uint8_t direntry_raw[20]; uint16_t entry_tag = 0; uint16_t entry_type = 0; uint64_t entry_count = 0; uint64_t entry_offset = 0; int value_in_entry = 0; uint64_t read_offset; uint8_t *buf_to_write = NULL; TIFFDataType datatype; /* -------------------------------------------------------------------- */ /* Find field definition. */ /* -------------------------------------------------------------------- */ /*fip =*/TIFFFindField(tif, tag, TIFF_ANY); /* -------------------------------------------------------------------- */ /* Do some checking this is a straight forward case. */ /* -------------------------------------------------------------------- */ if (isMapped(tif)) { TIFFErrorExtR( tif, module, "Memory mapped files not currently supported for this operation."); return 0; } if (tif->tif_diroff == 0) { TIFFErrorExtR( tif, module, "Attempt to reset field on directory not already on disk."); return 0; } /* -------------------------------------------------------------------- */ /* Read the directory entry count. */ /* -------------------------------------------------------------------- */ if (!SeekOK(tif, tif->tif_diroff)) { TIFFErrorExtR(tif, module, "%s: Seek error accessing TIFF directory", tif->tif_name); return 0; } read_offset = tif->tif_diroff; if (!(tif->tif_flags & TIFF_BIGTIFF)) { if (!ReadOK(tif, &dircount, sizeof(uint16_t))) { TIFFErrorExtR(tif, module, "%s: Can not read TIFF directory count", tif->tif_name); return 0; } if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&dircount); dirsize = 12; read_offset += 2; } else { uint64_t dircount64; if (!ReadOK(tif, &dircount64, sizeof(uint64_t))) { TIFFErrorExtR(tif, module, "%s: Can not read TIFF directory count", tif->tif_name); return 0; } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&dircount64); dircount = (uint16_t)dircount64; dirsize = 20; read_offset += 8; } /* -------------------------------------------------------------------- */ /* Read through directory to find target tag. */ /* -------------------------------------------------------------------- */ while (dircount > 0) { if (!ReadOK(tif, direntry_raw, dirsize)) { TIFFErrorExtR(tif, module, "%s: Can not read TIFF directory entry.", tif->tif_name); return 0; } memcpy(&entry_tag, direntry_raw + 0, sizeof(uint16_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&entry_tag); if (entry_tag == tag) break; read_offset += dirsize; } if (entry_tag != tag) { TIFFErrorExtR(tif, module, "%s: Could not find tag %" PRIu16 ".", tif->tif_name, tag); return 0; } /* -------------------------------------------------------------------- */ /* Extract the type, count and offset for this entry. */ /* -------------------------------------------------------------------- */ memcpy(&entry_type, direntry_raw + 2, sizeof(uint16_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&entry_type); if (!(tif->tif_flags & TIFF_BIGTIFF)) { uint32_t value; memcpy(&value, direntry_raw + 4, sizeof(uint32_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&value); entry_count = value; memcpy(&value, direntry_raw + 8, sizeof(uint32_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&value); entry_offset = value; } else { memcpy(&entry_count, direntry_raw + 4, sizeof(uint64_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&entry_count); memcpy(&entry_offset, direntry_raw + 12, sizeof(uint64_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&entry_offset); } /* -------------------------------------------------------------------- */ /* When a dummy tag was written due to TIFFDeferStrileArrayWriting() */ /* -------------------------------------------------------------------- */ if (entry_offset == 0 && entry_count == 0 && entry_type == 0) { if (tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS) { entry_type = (tif->tif_flags & TIFF_BIGTIFF) ? TIFF_LONG8 : TIFF_LONG; } else { int write_aslong8 = 1; if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS) { write_aslong8 = WriteAsLong8(tif, TIFFStripSize64(tif)); } else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS) { write_aslong8 = WriteAsLong8(tif, TIFFTileSize64(tif)); } if (write_aslong8) { entry_type = TIFF_LONG8; } else { int write_aslong4 = 1; if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS) { write_aslong4 = WriteAsLong4(tif, TIFFStripSize64(tif)); } else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS) { write_aslong4 = WriteAsLong4(tif, TIFFTileSize64(tif)); } if (write_aslong4) { entry_type = TIFF_LONG; } else { entry_type = TIFF_SHORT; } } } } /* -------------------------------------------------------------------- */ /* What data type do we want to write this as? */ /* -------------------------------------------------------------------- */ if (TIFFDataWidth(in_datatype) == 8 && !(tif->tif_flags & TIFF_BIGTIFF)) { if (in_datatype == TIFF_LONG8) datatype = entry_type == TIFF_SHORT ? TIFF_SHORT : TIFF_LONG; else if (in_datatype == TIFF_SLONG8) datatype = TIFF_SLONG; else if (in_datatype == TIFF_IFD8) datatype = TIFF_IFD; else datatype = in_datatype; } else { if (in_datatype == TIFF_LONG8 && (entry_type == TIFF_SHORT || entry_type == TIFF_LONG || entry_type == TIFF_LONG8)) datatype = entry_type; else if (in_datatype == TIFF_SLONG8 && (entry_type == TIFF_SLONG || entry_type == TIFF_SLONG8)) datatype = entry_type; else if (in_datatype == TIFF_IFD8 && (entry_type == TIFF_IFD || entry_type == TIFF_IFD8)) datatype = entry_type; else datatype = in_datatype; } /* -------------------------------------------------------------------- */ /* Prepare buffer of actual data to write. This includes */ /* swabbing as needed. */ /* -------------------------------------------------------------------- */ buf_to_write = (uint8_t *)_TIFFCheckMalloc( tif, count, TIFFDataWidth(datatype), "for field buffer."); if (!buf_to_write) return 0; if (datatype == in_datatype) memcpy(buf_to_write, data, count * TIFFDataWidth(datatype)); else if (datatype == TIFF_SLONG && in_datatype == TIFF_SLONG8) { tmsize_t i; for (i = 0; i < count; i++) { ((int32_t *)buf_to_write)[i] = (int32_t)((int64_t *)data)[i]; if ((int64_t)((int32_t *)buf_to_write)[i] != ((int64_t *)data)[i]) { _TIFFfreeExt(tif, buf_to_write); TIFFErrorExtR(tif, module, "Value exceeds 32bit range of output type."); return 0; } } } else if ((datatype == TIFF_LONG && in_datatype == TIFF_LONG8) || (datatype == TIFF_IFD && in_datatype == TIFF_IFD8)) { tmsize_t i; for (i = 0; i < count; i++) { ((uint32_t *)buf_to_write)[i] = (uint32_t)((uint64_t *)data)[i]; if ((uint64_t)((uint32_t *)buf_to_write)[i] != ((uint64_t *)data)[i]) { _TIFFfreeExt(tif, buf_to_write); TIFFErrorExtR(tif, module, "Value exceeds 32bit range of output type."); return 0; } } } else if (datatype == TIFF_SHORT && in_datatype == TIFF_LONG8) { tmsize_t i; for (i = 0; i < count; i++) { ((uint16_t *)buf_to_write)[i] = (uint16_t)((uint64_t *)data)[i]; if ((uint64_t)((uint16_t *)buf_to_write)[i] != ((uint64_t *)data)[i]) { _TIFFfreeExt(tif, buf_to_write); TIFFErrorExtR(tif, module, "Value exceeds 16bit range of output type."); return 0; } } } else { TIFFErrorExtR(tif, module, "Unhandled type conversion."); return 0; } if (TIFFDataWidth(datatype) > 1 && (tif->tif_flags & TIFF_SWAB)) { if (TIFFDataWidth(datatype) == 2) TIFFSwabArrayOfShort((uint16_t *)buf_to_write, count); else if (TIFFDataWidth(datatype) == 4) TIFFSwabArrayOfLong((uint32_t *)buf_to_write, count); else if (TIFFDataWidth(datatype) == 8) TIFFSwabArrayOfLong8((uint64_t *)buf_to_write, count); } /* -------------------------------------------------------------------- */ /* Is this a value that fits into the directory entry? */ /* -------------------------------------------------------------------- */ if (!(tif->tif_flags & TIFF_BIGTIFF)) { if (TIFFDataWidth(datatype) * count <= 4) { entry_offset = read_offset + 8; value_in_entry = 1; } } else { if (TIFFDataWidth(datatype) * count <= 8) { entry_offset = read_offset + 12; value_in_entry = 1; } } if ((tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS) && tif->tif_dir.td_stripoffset_entry.tdir_count == 0 && tif->tif_dir.td_stripoffset_entry.tdir_type == 0 && tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0) { tif->tif_dir.td_stripoffset_entry.tdir_type = datatype; tif->tif_dir.td_stripoffset_entry.tdir_count = count; } else if ((tag == TIFFTAG_TILEBYTECOUNTS || tag == TIFFTAG_STRIPBYTECOUNTS) && tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 && tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 && tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0) { tif->tif_dir.td_stripbytecount_entry.tdir_type = datatype; tif->tif_dir.td_stripbytecount_entry.tdir_count = count; } /* -------------------------------------------------------------------- */ /* If the tag type, and count match, then we just write it out */ /* over the old values without altering the directory entry at */ /* all. */ /* -------------------------------------------------------------------- */ if (entry_count == (uint64_t)count && entry_type == (uint16_t)datatype) { if (!SeekOK(tif, entry_offset)) { _TIFFfreeExt(tif, buf_to_write); TIFFErrorExtR(tif, module, "%s: Seek error accessing TIFF directory", tif->tif_name); return 0; } if (!WriteOK(tif, buf_to_write, count * TIFFDataWidth(datatype))) { _TIFFfreeExt(tif, buf_to_write); TIFFErrorExtR(tif, module, "Error writing directory link"); return (0); } _TIFFfreeExt(tif, buf_to_write); return 1; } /* -------------------------------------------------------------------- */ /* Otherwise, we write the new tag data at the end of the file. */ /* -------------------------------------------------------------------- */ if (!value_in_entry) { entry_offset = TIFFSeekFile(tif, 0, SEEK_END); if (!WriteOK(tif, buf_to_write, count * TIFFDataWidth(datatype))) { _TIFFfreeExt(tif, buf_to_write); TIFFErrorExtR(tif, module, "Error writing directory link"); return (0); } } else { if (count * TIFFDataWidth(datatype) == 4) { uint32_t value; memcpy(&value, buf_to_write, count * TIFFDataWidth(datatype)); entry_offset = value; } else { memcpy(&entry_offset, buf_to_write, count * TIFFDataWidth(datatype)); } } _TIFFfreeExt(tif, buf_to_write); buf_to_write = 0; /* -------------------------------------------------------------------- */ /* Adjust the directory entry. */ /* -------------------------------------------------------------------- */ entry_type = datatype; entry_count = (uint64_t)count; memcpy(direntry_raw + 2, &entry_type, sizeof(uint16_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort((uint16_t *)(direntry_raw + 2)); if (!(tif->tif_flags & TIFF_BIGTIFF)) { uint32_t value; value = (uint32_t)entry_count; memcpy(direntry_raw + 4, &value, sizeof(uint32_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong((uint32_t *)(direntry_raw + 4)); value = (uint32_t)entry_offset; memcpy(direntry_raw + 8, &value, sizeof(uint32_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong((uint32_t *)(direntry_raw + 8)); } else { memcpy(direntry_raw + 4, &entry_count, sizeof(uint64_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8((uint64_t *)(direntry_raw + 4)); memcpy(direntry_raw + 12, &entry_offset, sizeof(uint64_t)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8((uint64_t *)(direntry_raw + 12)); } /* -------------------------------------------------------------------- */ /* Write the directory entry out to disk. */ /* -------------------------------------------------------------------- */ if (!SeekOK(tif, read_offset)) { TIFFErrorExtR(tif, module, "%s: Seek error accessing TIFF directory", tif->tif_name); return 0; } if (!WriteOK(tif, direntry_raw, dirsize)) { TIFFErrorExtR(tif, module, "%s: Can not write TIFF directory entry.", tif->tif_name); return 0; } return 1; }