From 59f9b2cc028feabe2f48201fb2b3211fedfe6799 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 29 Apr 2025 10:57:47 +0100 Subject: [PATCH 1/4] start hacking --- libvips/foreign/tiff2vips.c | 9 +++++++++ libvips/include/vips/header.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index 718678f0a7..a48217414e 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -271,6 +271,7 @@ #include "jpeg.h" #endif /*HAVE_JPEG*/ + /* Aperio TIFFs (svs) use these compression types for jp2k-compressed tiles. */ #define JP2K_YCC 33003 @@ -280,6 +281,11 @@ */ #define JP2K_LOSSY 33004 +// only in recent libtiffs +#ifndef TIFFTAG_IMAGESOURCEDATA +#define TIFFTAG_IMAGESOURCEDATA (37724) +#endif + /* Compression types we handle ourselves. */ static int rtiff_we_decompress[] = { @@ -1904,6 +1910,9 @@ rtiff_set_header(Rtiff *rtiff, VipsImage *out) if (TIFFGetField(rtiff->tiff, TIFFTAG_PHOTOSHOP, &data_len, &data)) vips_image_set_blob_copy(out, VIPS_META_PHOTOSHOP_NAME, data, data_len); + if (TIFFGetField(rtiff->tiff, TIFFTAG_IMAGESOURCEDATA, &data_len, &data)) + vips_image_set_blob_copy(out, VIPS_META_PHOTOSHOP_DATA, data, data_len); + if (rtiff->header.image_description) vips_image_set_string(out, VIPS_META_IMAGEDESCRIPTION, rtiff->header.image_description); diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 16c3df2fc9..64194a0b6b 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -67,6 +67,14 @@ extern "C" { */ #define VIPS_META_PHOTOSHOP_NAME "photoshop-data" +/** + * VIPS_META_PHOTOSHOP_DATA: + * + * The name that TIFF read and write operations use for the image's + * TIFFTAG_IMAGESOURCEDATA data. + */ +#define VIPS_META_PHOTOSHOP_DATA "photoshop-image-data" + /** * VIPS_META_ICC_NAME: * From 6f736d0823554d67924651575ca743694a065487 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 29 Apr 2025 12:45:28 +0200 Subject: [PATCH 2/4] read and write TIFFTAG_IMAGESOURCEDATA tags Photoshop uses another private tag to store some extra document data. see https://github.com/libvips/libvips/discussions/4478 --- ChangeLog | 1 + libvips/foreign/tiff.h | 14 +++++++++ libvips/foreign/tiff2vips.c | 15 ---------- libvips/foreign/vips2tiff.c | 59 +++++++++++++++++++++++-------------- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9bc530f7aa..2e2e03c062 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,7 @@ master - share and reuse openslide connections - drop support for openslide 3.3 - fix vips_quadratic() +- add read/write of TIFFTAG_IMAGESOURCEDATA metadata (photoshop document data) 8.16.1 diff --git a/libvips/foreign/tiff.h b/libvips/foreign/tiff.h index 67d21ad46c..21c2b33905 100644 --- a/libvips/foreign/tiff.h +++ b/libvips/foreign/tiff.h @@ -33,6 +33,20 @@ #include +/* Aperio TIFFs (svs) use these compression types for jp2k-compressed tiles. + */ +#define JP2K_YCC (33003) +#define JP2K_RGB (33005) + +/* Bioformats uses this tag for jp2k compressed tiles. + */ +#define JP2K_LOSSY (33004) + +// only in recent libtiffs +#ifndef TIFFTAG_IMAGESOURCEDATA +#define TIFFTAG_IMAGESOURCEDATA (37724) +#endif + #ifdef __cplusplus extern "C" { #endif /*__cplusplus*/ diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index a48217414e..92b1c04f50 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -271,21 +271,6 @@ #include "jpeg.h" #endif /*HAVE_JPEG*/ - -/* Aperio TIFFs (svs) use these compression types for jp2k-compressed tiles. - */ -#define JP2K_YCC 33003 -#define JP2K_RGB 33005 - -/* Bioformats uses this tag for jp2k compressed tiles. - */ -#define JP2K_LOSSY 33004 - -// only in recent libtiffs -#ifndef TIFFTAG_IMAGESOURCEDATA -#define TIFFTAG_IMAGESOURCEDATA (37724) -#endif - /* Compression types we handle ourselves. */ static int rtiff_we_decompress[] = { diff --git a/libvips/foreign/vips2tiff.c b/libvips/foreign/vips2tiff.c index 89f23930c1..3c4a9e5512 100644 --- a/libvips/foreign/vips2tiff.c +++ b/libvips/foreign/vips2tiff.c @@ -285,10 +285,6 @@ */ #define MAX_ALPHA (64) -/* Bioformats uses this tag for lossy jp2k compressed tiles. - */ -#define JP2K_LOSSY 33004 - /* Compression types we handle ourselves. */ static int wtiff_we_compress[] = { @@ -396,6 +392,15 @@ struct _Wtiff { GMutex lock; }; +/* libvips uses size_t for length, but libtiff wants uint32. + */ +static void +set_data64(TIFF *tif, guint32 tag, size_t length, const void *data) +{ + if (length < UINT_MAX) + TIFFSetField(tif, tag, (guint32) length, data); +} + /* Write an ICC Profile from a file into the JPEG stream. */ static int @@ -410,7 +415,7 @@ embed_profile_file(TIFF *tif, const char *profile) size_t length; const void *data = vips_blob_get(blob, &length); - TIFFSetField(tif, TIFFTAG_ICCPROFILE, length, data); + set_data64(tif, TIFFTAG_ICCPROFILE, length, data); #ifdef DEBUG printf("vips2tiff: attached profile \"%s\"\n", profile); @@ -432,7 +437,7 @@ embed_profile_meta(TIFF *tif, VipsImage *im) if (vips_image_get_blob(im, VIPS_META_ICC_NAME, &data, &length)) return -1; - TIFFSetField(tif, TIFFTAG_ICCPROFILE, length, data); + set_data64(tif, TIFFTAG_ICCPROFILE, length, data); #ifdef DEBUG printf("vips2tiff: attached profile from meta\n"); @@ -576,7 +581,7 @@ wtiff_embed_xmp(Wtiff *wtiff, TIFF *tif) if (vips_image_get_blob(wtiff->ready, VIPS_META_XMP_NAME, &data, &size)) return -1; - TIFFSetField(tif, TIFFTAG_XMLPACKET, size, data); + set_data64(tif, TIFFTAG_XMLPACKET, size, data); #ifdef DEBUG printf("vips2tiff: attached XMP from meta\n"); @@ -608,7 +613,7 @@ wtiff_embed_iptc(Wtiff *wtiff, TIFF *tif) else size /= 4; - TIFFSetField(tif, TIFFTAG_RICHTIFFIPTC, size, data); + set_data64(tif, TIFFTAG_RICHTIFFIPTC, size, data); #ifdef DEBUG printf("vips2tiff: attached IPTC from meta\n"); @@ -623,16 +628,27 @@ wtiff_embed_photoshop(Wtiff *wtiff, TIFF *tif) const void *data; size_t size; - if (!vips_image_get_typeof(wtiff->ready, VIPS_META_PHOTOSHOP_NAME)) - return 0; - if (vips_image_get_blob(wtiff->ready, VIPS_META_PHOTOSHOP_NAME, - &data, &size)) - return -1; - TIFFSetField(tif, TIFFTAG_PHOTOSHOP, size, data); + if (vips_image_get_typeof(wtiff->ready, VIPS_META_PHOTOSHOP_NAME)) { + if (vips_image_get_blob(wtiff->ready, VIPS_META_PHOTOSHOP_NAME, + &data, &size)) + return -1; + set_data64(tif, TIFFTAG_PHOTOSHOP, size, data); + +#ifdef DEBUG + printf("vips2tiff: attached %zd bytes of photoshop data\n", size); +#endif /*DEBUG*/ + } + + if (vips_image_get_typeof(wtiff->ready, VIPS_META_PHOTOSHOP_DATA)) { + if (vips_image_get_blob(wtiff->ready, VIPS_META_PHOTOSHOP_DATA, + &data, &size)) + return -1; + set_data64(tif, TIFFTAG_IMAGESOURCEDATA, size, data); #ifdef DEBUG - printf("vips2tiff: attached photoshop data from meta\n"); + printf("vips2tiff: attached %zd bytes of photoshop image data\n", size); #endif /*DEBUG*/ + } return 0; } @@ -885,10 +901,10 @@ wtiff_write_header(Wtiff *wtiff, Layer *layer) if (wtiff->compression == COMPRESSION_ZSTD) { // Set zstd compression level - only accept valid values (1-22) if (wtiff->level) - TIFFSetField(tif, TIFFTAG_ZSTD_LEVEL, VIPS_CLIP(1, wtiff->level, 22)); - if (wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE) TIFFSetField(tif, - TIFFTAG_PREDICTOR, wtiff->predictor); + TIFFTAG_ZSTD_LEVEL, VIPS_CLIP(1, wtiff->level, 22)); + if (wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE) + TIFFSetField(tif, TIFFTAG_PREDICTOR, wtiff->predictor); } #endif /*HAVE_TIFF_COMPRESSION_WEBP*/ @@ -960,8 +976,7 @@ wtiff_write_header(Wtiff *wtiff, Layer *layer) int alpha_bands; - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, - wtiff->ready->Bands); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, wtiff->ready->Bands); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, vips_format_sizeof(wtiff->ready->BandFmt) << 3); @@ -1109,7 +1124,7 @@ wtiff_write_header(Wtiff *wtiff, Layer *layer) printf("setting %zd bytes of table data\n", length); #endif /*DEBUG*/ - TIFFSetField(tif, TIFFTAG_JPEGTABLES, length, buffer); + set_data64(tif, TIFFTAG_JPEGTABLES, length, buffer); g_free(buffer); } @@ -2297,7 +2312,7 @@ wtiff_write_lines(Wtiff *wtiff, VipsRegion *region, VipsRect *lines) */ #define CopyField(tag, v) \ if (TIFFGetField(in, tag, &v)) \ - TIFFSetField(out, tag, v) + TIFFSetField(out, tag, v) static int wtiff_copy_tiles(Wtiff *wtiff, TIFF *out, TIFF *in) From 5194a5cc6dcdba01d0e4947ba37e2e528beb011e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 30 Apr 2025 11:59:14 +0100 Subject: [PATCH 3/4] remove the IMAGEDATA stuff, not useful but the improvement to TIFFSetField() seems worth keeping --- ChangeLog | 1 - libvips/foreign/tiff.h | 5 ----- libvips/foreign/tiff2vips.c | 3 --- libvips/foreign/vips2tiff.c | 16 +++------------- 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2e2e03c062..9bc530f7aa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,7 +23,6 @@ master - share and reuse openslide connections - drop support for openslide 3.3 - fix vips_quadratic() -- add read/write of TIFFTAG_IMAGESOURCEDATA metadata (photoshop document data) 8.16.1 diff --git a/libvips/foreign/tiff.h b/libvips/foreign/tiff.h index 21c2b33905..8bfc8ea5bf 100644 --- a/libvips/foreign/tiff.h +++ b/libvips/foreign/tiff.h @@ -42,11 +42,6 @@ */ #define JP2K_LOSSY (33004) -// only in recent libtiffs -#ifndef TIFFTAG_IMAGESOURCEDATA -#define TIFFTAG_IMAGESOURCEDATA (37724) -#endif - #ifdef __cplusplus extern "C" { #endif /*__cplusplus*/ diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index 92b1c04f50..1eb03edd3d 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -1895,9 +1895,6 @@ rtiff_set_header(Rtiff *rtiff, VipsImage *out) if (TIFFGetField(rtiff->tiff, TIFFTAG_PHOTOSHOP, &data_len, &data)) vips_image_set_blob_copy(out, VIPS_META_PHOTOSHOP_NAME, data, data_len); - if (TIFFGetField(rtiff->tiff, TIFFTAG_IMAGESOURCEDATA, &data_len, &data)) - vips_image_set_blob_copy(out, VIPS_META_PHOTOSHOP_DATA, data, data_len); - if (rtiff->header.image_description) vips_image_set_string(out, VIPS_META_IMAGEDESCRIPTION, rtiff->header.image_description); diff --git a/libvips/foreign/vips2tiff.c b/libvips/foreign/vips2tiff.c index 3c4a9e5512..4c37b0e4fe 100644 --- a/libvips/foreign/vips2tiff.c +++ b/libvips/foreign/vips2tiff.c @@ -392,12 +392,13 @@ struct _Wtiff { GMutex lock; }; -/* libvips uses size_t for length, but libtiff wants uint32. +/* libvips uses size_t for the length of binary data items, but libtiff wants + * uint32. */ static void set_data64(TIFF *tif, guint32 tag, size_t length, const void *data) { - if (length < UINT_MAX) + if (length <= UINT_MAX) TIFFSetField(tif, tag, (guint32) length, data); } @@ -639,17 +640,6 @@ wtiff_embed_photoshop(Wtiff *wtiff, TIFF *tif) #endif /*DEBUG*/ } - if (vips_image_get_typeof(wtiff->ready, VIPS_META_PHOTOSHOP_DATA)) { - if (vips_image_get_blob(wtiff->ready, VIPS_META_PHOTOSHOP_DATA, - &data, &size)) - return -1; - set_data64(tif, TIFFTAG_IMAGESOURCEDATA, size, data); - -#ifdef DEBUG - printf("vips2tiff: attached %zd bytes of photoshop image data\n", size); -#endif /*DEBUG*/ - } - return 0; } From 2a31302526ea813cd25bb0e5980c353e9d06208a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 30 Apr 2025 13:00:22 +0100 Subject: [PATCH 4/4] remove stray define --- libvips/include/vips/header.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 64194a0b6b..16c3df2fc9 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -67,14 +67,6 @@ extern "C" { */ #define VIPS_META_PHOTOSHOP_NAME "photoshop-data" -/** - * VIPS_META_PHOTOSHOP_DATA: - * - * The name that TIFF read and write operations use for the image's - * TIFFTAG_IMAGESOURCEDATA data. - */ -#define VIPS_META_PHOTOSHOP_DATA "photoshop-image-data" - /** * VIPS_META_ICC_NAME: *