Improve encoding of TIFF tags (#3861)

* Improve encoding of TIFF tags

- Pass tagtype from v2 directory to libtiff encoder, instead of
autodetecting type.
- Use explicit types. E.g. uint32_t for TIFF_LONG to fix issues on
platforms with 64bit longs.
- Add support for multiple values (arrays). Requires type in v2
directory and values must be passed as a tuple.
- Add support for signed types (e.g. TIFFTypes.TIFF_SIGNED_SHORT).

Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
This commit is contained in:
Oliver Tonnhofer 2019-06-30 20:48:19 +02:00 committed by Hugo
parent a1eb07f276
commit 2af4026201
6 changed files with 416 additions and 189 deletions

View File

@ -3,6 +3,7 @@ from .helper import PillowTestCase, hopper
from PIL import features
from PIL._util import py3
from collections import namedtuple
from ctypes import c_float
import io
import logging
@ -235,12 +236,39 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = False
def test_custom_metadata(self):
tc = namedtuple("test_case", "value,type,supported_by_default")
custom = {
37000: [4, TiffTags.SHORT],
37001: [4.2, TiffTags.RATIONAL],
37002: ["custom tag value", TiffTags.ASCII],
37003: [u"custom tag value", TiffTags.ASCII],
37004: [b"custom tag value", TiffTags.BYTE],
37000 + k: v
for k, v in enumerate(
[
tc(4, TiffTags.SHORT, True),
tc(123456789, TiffTags.LONG, True),
tc(-4, TiffTags.SIGNED_BYTE, False),
tc(-4, TiffTags.SIGNED_SHORT, False),
tc(-123456789, TiffTags.SIGNED_LONG, False),
tc(TiffImagePlugin.IFDRational(4, 7), TiffTags.RATIONAL, True),
tc(4.25, TiffTags.FLOAT, True),
tc(4.25, TiffTags.DOUBLE, True),
tc("custom tag value", TiffTags.ASCII, True),
tc(u"custom tag value", TiffTags.ASCII, True),
tc(b"custom tag value", TiffTags.BYTE, True),
tc((4, 5, 6), TiffTags.SHORT, True),
tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True),
tc((-4, 9, 10), TiffTags.SIGNED_BYTE, False),
tc((-4, 5, 6), TiffTags.SIGNED_SHORT, False),
tc(
(-123456789, 9, 34, 234, 219387, -92432323),
TiffTags.SIGNED_LONG,
False,
),
tc((4.25, 5.25), TiffTags.FLOAT, True),
tc((4.25, 5.25), TiffTags.DOUBLE, True),
# array of TIFF_BYTE requires bytes instead of tuple for backwards
# compatibility
tc(bytes([4]), TiffTags.BYTE, True),
tc(bytes((4, 9, 10)), TiffTags.BYTE, True),
]
)
}
libtiff_version = TiffImagePlugin._libtiff_version()
@ -263,8 +291,13 @@ class TestFileLibTiff(LibTiffTestCase):
reloaded = Image.open(out)
for tag, value in tiffinfo.items():
reloaded_value = reloaded.tag_v2[tag]
if isinstance(reloaded_value, TiffImagePlugin.IFDRational):
reloaded_value = float(reloaded_value)
if (
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
and libtiff
):
# libtiff does not support real RATIONALS
self.assertAlmostEqual(float(reloaded_value), float(value))
continue
if libtiff and isinstance(value, bytes):
value = value.decode()
@ -274,12 +307,19 @@ class TestFileLibTiff(LibTiffTestCase):
# Test with types
ifd = TiffImagePlugin.ImageFileDirectory_v2()
for tag, tagdata in custom.items():
ifd[tag] = tagdata[0]
ifd.tagtype[tag] = tagdata[1]
ifd[tag] = tagdata.value
ifd.tagtype[tag] = tagdata.type
check_tags(ifd)
# Test without types
check_tags({tag: tagdata[0] for tag, tagdata in custom.items()})
# Test without types. This only works for some types, int for example are
# always encoded as LONG and not SIGNED_LONG.
check_tags(
{
tag: tagdata.value
for tag, tagdata in custom.items()
if tagdata.supported_by_default
}
)
TiffImagePlugin.WRITE_LIBTIFF = False
def test_int_dpi(self):

View File

@ -716,14 +716,20 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
:py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` object may
be passed in this field. However, this is deprecated.
.. versionadded:: 3.0.0
.. versionadded:: 5.4.0
.. note::
Only some tags are currently supported when writing using
Previous versions only supported some tags when writing using
libtiff. The supported list is found in
:py:attr:`~PIL:TiffTags.LIBTIFF_CORE`.
.. versionadded:: 6.1.0
Added support for signed types (e.g. ``TIFF_SIGNED_LONG``) and multiple values.
Multiple values for a single tag must be to
:py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` as a tuple and
require a matching type in
:py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype` tagtype.
**compression**
A string containing the desired compression method for the
file. (valid only with libtiff installed) Valid compression

View File

@ -99,6 +99,7 @@ X_RESOLUTION = 282
Y_RESOLUTION = 283
PLANAR_CONFIGURATION = 284
RESOLUTION_UNIT = 296
TRANSFERFUNCTION = 301
SOFTWARE = 305
DATE_TIME = 306
ARTIST = 315
@ -108,6 +109,7 @@ TILEOFFSETS = 324
EXTRASAMPLES = 338
SAMPLEFORMAT = 339
JPEGTABLES = 347
REFERENCEBLACKWHITE = 532
COPYRIGHT = 33432
IPTC_NAA_CHUNK = 33723 # newsphoto properties
PHOTOSHOP_CHUNK = 34377 # photoshop properties
@ -1538,9 +1540,24 @@ def _save(im, fp, filename):
except io.UnsupportedOperation:
pass
# optional types for non core tags
types = {}
# SAMPLEFORMAT is determined by the image format and should not be copied
# from legacy_ifd.
# STRIPOFFSETS and STRIPBYTECOUNTS are added by the library
# based on the data in the strip.
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, SAMPLEFORMAT]
# The other tags expect arrays with a certain length (fixed or depending on
# BITSPERSAMPLE, etc), passing arrays with a different length will result in
# segfaults. Block these tags until we add extra validation.
blocklist = [
COLORMAP,
REFERENCEBLACKWHITE,
SAMPLEFORMAT,
STRIPBYTECOUNTS,
STRIPOFFSETS,
TRANSFERFUNCTION,
]
atts = {}
# bits per sample is a single short in the tiff directory, not a list.
atts[BITSPERSAMPLE] = bits[0]
@ -1555,15 +1572,19 @@ def _save(im, fp, filename):
):
# Libtiff can only process certain core items without adding
# them to the custom dictionary.
# Support for custom items has only been been added
# for int, float, unicode, string and byte values
# Custom items are supported for int, float, unicode, string and byte
# values. Other types and tuples require a tagtype.
if tag not in TiffTags.LIBTIFF_CORE:
if TiffTags.lookup(tag).type == TiffTags.UNDEFINED:
continue
if (
distutils.version.StrictVersion(_libtiff_version())
< distutils.version.StrictVersion("4.0")
) or not (
if distutils.version.StrictVersion(
_libtiff_version()
) < distutils.version.StrictVersion("4.0"):
continue
if tag in ifd.tagtype:
types[tag] = ifd.tagtype[tag]
elif not (
isinstance(value, (int, float, str, bytes))
or (not py3 and isinstance(value, unicode)) # noqa: F821
):
@ -1586,7 +1607,7 @@ def _save(im, fp, filename):
if im.mode in ("I;16B", "I;16"):
rawmode = "I;16N"
a = (rawmode, compression, _fp, filename, atts)
a = (rawmode, compression, _fp, filename, atts, types)
e = Image._getencoder(im.mode, "libtiff", a, im.encoderconfig)
e.setimage(im.im, (0, 0) + im.size)
while True:

View File

@ -613,6 +613,310 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
#endif
/* -------------------------------------------------------------------- */
/* LibTiff */
/* -------------------------------------------------------------------- */
#ifdef HAVE_LIBTIFF
#include "TiffDecode.h"
#include <string.h>
PyObject*
PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
{
ImagingEncoderObject* encoder;
char* mode;
char* rawmode;
char* compname;
char* filename;
Py_ssize_t fp;
PyObject *tags, *types;
PyObject *key, *value;
Py_ssize_t pos = 0;
int key_int, status, is_core_tag, is_var_length, num_core_tags, i;
TIFFDataType type = TIFF_NOTYPE;
// This list also exists in TiffTags.py
const int core_tags[] = {
256, 257, 258, 259, 262, 263, 266, 269, 274, 277, 278, 280, 281, 340,
341, 282, 283, 284, 286, 287, 296, 297, 321, 338, 32995, 32998, 32996,
339, 32997, 330, 531, 530
};
Py_ssize_t d_size;
PyObject *keys, *values;
if (! PyArg_ParseTuple(args, "sssnsOO", &mode, &rawmode, &compname, &fp, &filename, &tags, &types)) {
return NULL;
}
if (!PyDict_Check(tags)) {
PyErr_SetString(PyExc_ValueError, "Invalid tags dictionary");
return NULL;
} else {
d_size = PyDict_Size(tags);
TRACE(("dict size: %d\n", (int)d_size));
keys = PyDict_Keys(tags);
values = PyDict_Values(tags);
for (pos=0;pos<d_size;pos++){
TRACE((" key: %d\n", (int)PyInt_AsLong(PyList_GetItem(keys,pos))));
}
pos = 0;
}
if (!PyDict_Check(types)) {
PyErr_SetString(PyExc_ValueError, "Invalid types dictionary");
return NULL;
}
TRACE(("new tiff encoder %s fp: %d, filename: %s \n", compname, fp, filename));
encoder = PyImaging_EncoderNew(sizeof(TIFFSTATE));
if (encoder == NULL)
return NULL;
if (get_packer(encoder, mode, rawmode) < 0)
return NULL;
if (! ImagingLibTiffEncodeInit(&encoder->state, filename, fp)) {
Py_DECREF(encoder);
PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
return NULL;
}
num_core_tags = sizeof(core_tags) / sizeof(int);
for (pos = 0; pos < d_size; pos++) {
key = PyList_GetItem(keys, pos);
key_int = (int)PyInt_AsLong(key);
value = PyList_GetItem(values, pos);
status = 0;
is_core_tag = 0;
is_var_length = 0;
type = TIFF_NOTYPE;
for (i=0; i<num_core_tags; i++) {
if (core_tags[i] == key_int) {
is_core_tag = 1;
break;
}
}
if (!is_core_tag) {
PyObject *tag_type = PyDict_GetItem(types, key);
if (tag_type) {
int type_int = PyInt_AsLong(tag_type);
if (type_int >= TIFF_BYTE && type_int <= TIFF_DOUBLE) {
type = (TIFFDataType)type_int;
}
}
}
if (type == TIFF_NOTYPE) {
// Autodetect type. Types should not be changed for backwards
// compatibility.
if (PyInt_Check(value)) {
type = TIFF_LONG;
} else if (PyFloat_Check(value)) {
type = TIFF_DOUBLE;
} else if (PyBytes_Check(value)) {
type = TIFF_ASCII;
}
}
if (PyBytes_Check(value) &&
(type == TIFF_BYTE || type == TIFF_UNDEFINED)) {
// For backwards compatibility
type = TIFF_ASCII;
}
if (PyTuple_Check(value)) {
Py_ssize_t len;
len = PyTuple_Size(value);
is_var_length = 1;
if (!len) {
continue;
}
if (type == TIFF_NOTYPE) {
// Autodetect type based on first item. Types should not be
// changed for backwards compatibility.
if (PyInt_Check(PyTuple_GetItem(value,0))) {
type = TIFF_LONG;
} else if (PyFloat_Check(PyTuple_GetItem(value,0))) {
type = TIFF_FLOAT;
}
}
}
if (!is_core_tag) {
// Register field for non core tags.
if (ImagingLibTiffMergeFieldInfo(&encoder->state, type, key_int, is_var_length)) {
continue;
}
}
if (is_var_length) {
Py_ssize_t len,i;
TRACE(("Setting from Tuple: %d \n", key_int));
len = PyTuple_Size(value);
if (type == TIFF_BYTE) {
UINT8 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(UINT8));
if (av) {
for (i=0;i<len;i++) {
av[i] = (UINT8)PyInt_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_SHORT) {
UINT16 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(UINT16));
if (av) {
for (i=0;i<len;i++) {
av[i] = (UINT16)PyInt_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_LONG) {
UINT32 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(UINT32));
if (av) {
for (i=0;i<len;i++) {
av[i] = (UINT32)PyInt_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_SBYTE) {
INT8 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(INT8));
if (av) {
for (i=0;i<len;i++) {
av[i] = (INT8)PyInt_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_SSHORT) {
INT16 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(INT16));
if (av) {
for (i=0;i<len;i++) {
av[i] = (INT16)PyInt_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_SLONG) {
INT32 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(INT32));
if (av) {
for (i=0;i<len;i++) {
av[i] = (INT32)PyInt_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_FLOAT) {
FLOAT32 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(FLOAT32));
if (av) {
for (i=0;i<len;i++) {
av[i] = (FLOAT32)PyFloat_AsDouble(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_DOUBLE) {
FLOAT64 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(FLOAT64));
if (av) {
for (i=0;i<len;i++) {
av[i] = PyFloat_AsDouble(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
}
} else {
if (type == TIFF_SHORT) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(UINT16)PyInt_AsLong(value));
} else if (type == TIFF_LONG) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(UINT32)PyInt_AsLong(value));
} else if (type == TIFF_SSHORT) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(INT16)PyInt_AsLong(value));
} else if (type == TIFF_SLONG) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(INT32)PyInt_AsLong(value));
} else if (type == TIFF_FLOAT) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(FLOAT32)PyFloat_AsDouble(value));
} else if (type == TIFF_DOUBLE) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(FLOAT64)PyFloat_AsDouble(value));
} else if (type == TIFF_BYTE) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(UINT8)PyInt_AsLong(value));
} else if (type == TIFF_SBYTE) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(INT8)PyInt_AsLong(value));
} else if (type == TIFF_ASCII) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
PyBytes_AsString(value));
} else if (type == TIFF_RATIONAL) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(FLOAT64)PyFloat_AsDouble(value));
} else {
TRACE(("Unhandled type for key %d : %s \n",
key_int,
PyBytes_AsString(PyObject_Str(value))));
}
}
if (!status) {
TRACE(("Error setting Field\n"));
Py_DECREF(encoder);
PyErr_SetString(PyExc_RuntimeError, "Error setting from dictionary");
return NULL;
}
}
encoder->encode = ImagingLibTiffEncode;
return (PyObject*) encoder;
}
#endif
/* -------------------------------------------------------------------- */
/* JPEG */
/* -------------------------------------------------------------------- */
@ -787,168 +1091,6 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
#endif
/* -------------------------------------------------------------------- */
/* LibTiff */
/* -------------------------------------------------------------------- */
#ifdef HAVE_LIBTIFF
#include "TiffDecode.h"
#include <string.h>
PyObject*
PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
{
ImagingEncoderObject* encoder;
char* mode;
char* rawmode;
char* compname;
char* filename;
Py_ssize_t fp;
PyObject *dir;
PyObject *key, *value;
Py_ssize_t pos = 0;
int key_int, status, is_core_tag, number_of_tags, i;
// This list also exists in TiffTags.py
const int tags[] = {
256, 257, 258, 259, 262, 263, 266, 269, 274, 277, 278, 280, 281, 340,
341, 282, 283, 284, 286, 287, 296, 297, 321, 338, 32995, 32998, 32996,
339, 32997, 330, 531, 530
};
Py_ssize_t d_size;
PyObject *keys, *values;
if (! PyArg_ParseTuple(args, "sssnsO", &mode, &rawmode, &compname, &fp, &filename, &dir)) {
return NULL;
}
if (!PyDict_Check(dir)) {
PyErr_SetString(PyExc_ValueError, "Invalid Dictionary");
return NULL;
} else {
d_size = PyDict_Size(dir);
TRACE(("dict size: %d\n", (int)d_size));
keys = PyDict_Keys(dir);
values = PyDict_Values(dir);
for (pos=0;pos<d_size;pos++){
TRACE((" key: %d\n", (int)PyInt_AsLong(PyList_GetItem(keys,pos))));
}
pos = 0;
}
TRACE(("new tiff encoder %s fp: %d, filename: %s \n", compname, fp, filename));
encoder = PyImaging_EncoderNew(sizeof(TIFFSTATE));
if (encoder == NULL)
return NULL;
if (get_packer(encoder, mode, rawmode) < 0)
return NULL;
if (! ImagingLibTiffEncodeInit(&encoder->state, filename, fp)) {
Py_DECREF(encoder);
PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
return NULL;
}
number_of_tags = sizeof(tags) / sizeof(int);
for (pos = 0; pos < d_size; pos++) {
key = PyList_GetItem(keys, pos);
key_int = (int)PyInt_AsLong(key);
value = PyList_GetItem(values, pos);
status = 0;
is_core_tag = 0;
for (i=0; i<number_of_tags; i++) {
if (tags[i] == key_int) {
is_core_tag = 1;
break;
}
}
TRACE(("Attempting to set key: %d\n", key_int));
if (PyInt_Check(value)) {
TRACE(("Setting from Int: %d %ld \n", key_int, PyInt_AsLong(value)));
if (is_core_tag || !ImagingLibTiffMergeFieldInfo(&encoder->state, TIFF_LONG, key_int)) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
PyInt_AsLong(value));
}
} else if (PyFloat_Check(value)) {
TRACE(("Setting from Float: %d, %f \n", key_int, PyFloat_AsDouble(value)));
if (is_core_tag || !ImagingLibTiffMergeFieldInfo(&encoder->state, TIFF_DOUBLE, key_int)) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
(double)PyFloat_AsDouble(value));
}
} else if (PyBytes_Check(value)) {
TRACE(("Setting from Bytes: %d, %s \n", key_int, PyBytes_AsString(value)));
if (is_core_tag || !ImagingLibTiffMergeFieldInfo(&encoder->state, TIFF_ASCII, key_int)) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
PyBytes_AsString(value));
}
} else if (PyTuple_Check(value)) {
Py_ssize_t len,i;
float *floatav;
int *intav;
TRACE(("Setting from Tuple: %d \n", key_int));
len = PyTuple_Size(value);
if (len) {
if (PyInt_Check(PyTuple_GetItem(value,0))) {
TRACE((" %d elements, setting as ints \n", (int)len));
/* malloc check ok, calloc checks for overflow */
intav = calloc(len, sizeof(int));
if (intav) {
for (i=0;i<len;i++) {
intav[i] = (int)PyInt_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
len, intav);
free(intav);
}
} else if (PyFloat_Check(PyTuple_GetItem(value,0))) {
TRACE((" %d elements, setting as floats \n", (int)len));
/* malloc check ok, calloc checks for overflow */
floatav = calloc(len, sizeof(float));
if (floatav) {
for (i=0;i<len;i++) {
floatav[i] = (float)PyFloat_AsDouble(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
len, floatav);
free(floatav);
}
} else {
TRACE(("Unhandled type in tuple for key %d : %s \n",
key_int,
PyBytes_AsString(PyObject_Str(value))));
}
}
} else {
TRACE(("Unhandled type for key %d : %s \n",
key_int,
PyBytes_AsString(PyObject_Str(value))));
}
if (!status) {
TRACE(("Error setting Field\n"));
Py_DECREF(encoder);
PyErr_SetString(PyExc_RuntimeError, "Error setting from dictionary");
return NULL;
}
}
encoder->encode = ImagingLibTiffEncode;
return (PyObject*) encoder;
}
#endif
/* -------------------------------------------------------------------- */
/* JPEG 2000 */

View File

@ -520,15 +520,33 @@ int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
}
int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key){
int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length){
// Refer to libtiff docs (http://www.simplesystems.org/libtiff/addingtags.html)
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
char field_name[10];
uint32 n;
int status = 0;
const TIFFFieldInfo info[] = {
{ key, 0, 1, field_type, FIELD_CUSTOM, 1, 0, field_name }
// custom fields added with ImagingLibTiffMergeFieldInfo are only used for
// decoding, ignore readcount;
int readcount = 0;
// we support writing a single value, or a variable number of values
int writecount = 1;
// whether the first value should encode the number of values.
int passcount = 0;
TIFFFieldInfo info[] = {
{ key, readcount, writecount, field_type, FIELD_CUSTOM, 1, passcount, field_name }
};
if (is_var_length) {
info[0].field_writecount = -1;
}
if (is_var_length && field_type != TIFF_ASCII) {
info[0].field_passcount = 1;
}
n = sizeof(info) / sizeof(info[0]);
// Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7

View File

@ -45,7 +45,7 @@ typedef struct {
extern int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset);
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
extern int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key);
extern int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length);
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);