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 import features
from PIL._util import py3 from PIL._util import py3
from collections import namedtuple
from ctypes import c_float from ctypes import c_float
import io import io
import logging import logging
@ -235,12 +236,39 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.WRITE_LIBTIFF = False
def test_custom_metadata(self): def test_custom_metadata(self):
tc = namedtuple("test_case", "value,type,supported_by_default")
custom = { custom = {
37000: [4, TiffTags.SHORT], 37000 + k: v
37001: [4.2, TiffTags.RATIONAL], for k, v in enumerate(
37002: ["custom tag value", TiffTags.ASCII], [
37003: [u"custom tag value", TiffTags.ASCII], tc(4, TiffTags.SHORT, True),
37004: [b"custom tag value", TiffTags.BYTE], 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() libtiff_version = TiffImagePlugin._libtiff_version()
@ -263,8 +291,13 @@ class TestFileLibTiff(LibTiffTestCase):
reloaded = Image.open(out) reloaded = Image.open(out)
for tag, value in tiffinfo.items(): for tag, value in tiffinfo.items():
reloaded_value = reloaded.tag_v2[tag] reloaded_value = reloaded.tag_v2[tag]
if isinstance(reloaded_value, TiffImagePlugin.IFDRational): if (
reloaded_value = float(reloaded_value) 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): if libtiff and isinstance(value, bytes):
value = value.decode() value = value.decode()
@ -274,12 +307,19 @@ class TestFileLibTiff(LibTiffTestCase):
# Test with types # Test with types
ifd = TiffImagePlugin.ImageFileDirectory_v2() ifd = TiffImagePlugin.ImageFileDirectory_v2()
for tag, tagdata in custom.items(): for tag, tagdata in custom.items():
ifd[tag] = tagdata[0] ifd[tag] = tagdata.value
ifd.tagtype[tag] = tagdata[1] ifd.tagtype[tag] = tagdata.type
check_tags(ifd) check_tags(ifd)
# Test without types # Test without types. This only works for some types, int for example are
check_tags({tag: tagdata[0] for tag, tagdata in custom.items()}) # 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 TiffImagePlugin.WRITE_LIBTIFF = False
def test_int_dpi(self): 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 :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` object may
be passed in this field. However, this is deprecated. be passed in this field. However, this is deprecated.
.. versionadded:: 3.0.0 .. versionadded:: 5.4.0
.. note:: Previous versions only supported some tags when writing using
Only some tags are currently supported when writing using
libtiff. The supported list is found in libtiff. The supported list is found in
:py:attr:`~PIL:TiffTags.LIBTIFF_CORE`. :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** **compression**
A string containing the desired compression method for the A string containing the desired compression method for the
file. (valid only with libtiff installed) Valid compression file. (valid only with libtiff installed) Valid compression

View File

@ -99,6 +99,7 @@ X_RESOLUTION = 282
Y_RESOLUTION = 283 Y_RESOLUTION = 283
PLANAR_CONFIGURATION = 284 PLANAR_CONFIGURATION = 284
RESOLUTION_UNIT = 296 RESOLUTION_UNIT = 296
TRANSFERFUNCTION = 301
SOFTWARE = 305 SOFTWARE = 305
DATE_TIME = 306 DATE_TIME = 306
ARTIST = 315 ARTIST = 315
@ -108,6 +109,7 @@ TILEOFFSETS = 324
EXTRASAMPLES = 338 EXTRASAMPLES = 338
SAMPLEFORMAT = 339 SAMPLEFORMAT = 339
JPEGTABLES = 347 JPEGTABLES = 347
REFERENCEBLACKWHITE = 532
COPYRIGHT = 33432 COPYRIGHT = 33432
IPTC_NAA_CHUNK = 33723 # newsphoto properties IPTC_NAA_CHUNK = 33723 # newsphoto properties
PHOTOSHOP_CHUNK = 34377 # photoshop properties PHOTOSHOP_CHUNK = 34377 # photoshop properties
@ -1538,9 +1540,24 @@ def _save(im, fp, filename):
except io.UnsupportedOperation: except io.UnsupportedOperation:
pass 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 # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library
# based on the data in the strip. # 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 = {} atts = {}
# bits per sample is a single short in the tiff directory, not a list. # bits per sample is a single short in the tiff directory, not a list.
atts[BITSPERSAMPLE] = bits[0] atts[BITSPERSAMPLE] = bits[0]
@ -1555,15 +1572,19 @@ def _save(im, fp, filename):
): ):
# Libtiff can only process certain core items without adding # Libtiff can only process certain core items without adding
# them to the custom dictionary. # them to the custom dictionary.
# Support for custom items has only been been added # Custom items are supported for int, float, unicode, string and byte
# for int, float, unicode, string and byte values # values. Other types and tuples require a tagtype.
if tag not in TiffTags.LIBTIFF_CORE: if tag not in TiffTags.LIBTIFF_CORE:
if TiffTags.lookup(tag).type == TiffTags.UNDEFINED: if TiffTags.lookup(tag).type == TiffTags.UNDEFINED:
continue continue
if ( if distutils.version.StrictVersion(
distutils.version.StrictVersion(_libtiff_version()) _libtiff_version()
< distutils.version.StrictVersion("4.0") ) < distutils.version.StrictVersion("4.0"):
) or not ( continue
if tag in ifd.tagtype:
types[tag] = ifd.tagtype[tag]
elif not (
isinstance(value, (int, float, str, bytes)) isinstance(value, (int, float, str, bytes))
or (not py3 and isinstance(value, unicode)) # noqa: F821 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"): if im.mode in ("I;16B", "I;16"):
rawmode = "I;16N" 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 = Image._getencoder(im.mode, "libtiff", a, im.encoderconfig)
e.setimage(im.im, (0, 0) + im.size) e.setimage(im.im, (0, 0) + im.size)
while True: while True:

View File

@ -613,6 +613,310 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
#endif #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 */ /* JPEG */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
@ -787,168 +1091,6 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
#endif #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 */ /* 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; TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
char field_name[10]; char field_name[10];
uint32 n; uint32 n;
int status = 0; int status = 0;
const TIFFFieldInfo info[] = { // custom fields added with ImagingLibTiffMergeFieldInfo are only used for
{ key, 0, 1, field_type, FIELD_CUSTOM, 1, 0, field_name } // 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]); n = sizeof(info) / sizeof(info[0]);
// Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7 // 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 ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset);
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp); 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, ...); extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);