Merge pull request #3350 from radarhere/custom_tags

Added custom int and float TIFF tags
This commit is contained in:
Hugo 2018-12-26 13:07:46 +02:00 committed by GitHub
commit 78bc4da131
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 109 additions and 20 deletions

View File

@ -8,6 +8,7 @@ import io
import logging import logging
import itertools import itertools
import os import os
import distutils.version
from PIL import Image, TiffImagePlugin, TiffTags from PIL import Image, TiffImagePlugin, TiffTags
@ -231,6 +232,32 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.WRITE_LIBTIFF = False
def test_custom_metadata(self):
custom = {
37000: 4,
37001: 4.2
}
libtiff_version = TiffImagePlugin._libtiff_version()
libtiffs = [False]
if distutils.version.StrictVersion(libtiff_version) >= \
distutils.version.StrictVersion("4.0"):
libtiffs.append(True)
for libtiff in libtiffs:
TiffImagePlugin.WRITE_LIBTIFF = libtiff
im = hopper()
out = self.tempfile("temp.tif")
im.save(out, tiffinfo=custom)
TiffImagePlugin.WRITE_LIBTIFF = False
reloaded = Image.open(out)
for tag, value in custom.items():
self.assertEqual(reloaded.tag_v2[tag], value)
def test_int_dpi(self): def test_int_dpi(self):
# issue #1765 # issue #1765
im = hopper('RGB') im = hopper('RGB')

View File

@ -135,7 +135,7 @@ class TestFileTiffMetadata(PillowTestCase):
for k, v in original.items(): for k, v in original.items():
if isinstance(v, IFDRational): if isinstance(v, IFDRational):
original[k] = IFDRational(*_limit_rational(v, 2**31)) original[k] = IFDRational(*_limit_rational(v, 2**31))
if isinstance(v, tuple) and isinstance(v[0], IFDRational): elif isinstance(v, tuple) and isinstance(v[0], IFDRational):
original[k] = tuple(IFDRational(*_limit_rational(elt, 2**31)) original[k] = tuple(IFDRational(*_limit_rational(elt, 2**31))
for elt in v) for elt in v)

View File

@ -10,6 +10,7 @@ def version(module, version):
version(Image, "jpeglib") version(Image, "jpeglib")
version(Image, "zlib") version(Image, "zlib")
version(Image, "libtiff")
try: try:
from PIL import ImageFont from PIL import ImageFont

View File

@ -54,6 +54,7 @@ import os
import struct import struct
import sys import sys
import warnings import warnings
import distutils.version
from .TiffTags import TYPES from .TiffTags import TYPES
@ -284,6 +285,10 @@ def _limit_rational(val, max_val):
return n_d[::-1] if inv else n_d return n_d[::-1] if inv else n_d
def _libtiff_version():
return Image.core.libtiff_version.split("\n")[0].split("Version ")[1]
## ##
# Wrapper for TIFF IFDs. # Wrapper for TIFF IFDs.
@ -1508,11 +1513,12 @@ def _save(im, fp, filename):
getattr(im, 'tag_v2', {}).items(), getattr(im, 'tag_v2', {}).items(),
legacy_ifd.items()): legacy_ifd.items()):
# Libtiff can only process certain core items without adding # Libtiff can only process certain core items without adding
# them to the custom dictionary. It will segfault if it attempts # them to the custom dictionary. Support has only been been added
# to add a custom tag without the dictionary entry # for int and float values
#
# UNDONE -- add code for the custom dictionary
if tag not in TiffTags.LIBTIFF_CORE: if tag not in TiffTags.LIBTIFF_CORE:
if (distutils.version.StrictVersion(_libtiff_version()) <
distutils.version.StrictVersion("4.0")) \
or not (isinstance(value, int) or isinstance(value, float)):
continue continue
if tag not in atts and tag not in blocklist: if tag not in atts and tag not in blocklist:
if isinstance(value, str if py3 else unicode): # noqa: F821 if isinstance(value, str if py3 else unicode): # noqa: F821

View File

@ -425,6 +425,7 @@ TYPES = {}
# some of these are not in our TAGS_V2 dict and were included from tiff.h # some of these are not in our TAGS_V2 dict and were included from tiff.h
# This list also exists in encode.c
LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277, LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
278, 280, 281, 340, 341, 282, 283, 284, 286, 287, 278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
296, 297, 321, 320, 338, 32995, 322, 323, 32998, 296, 297, 321, 320, 338, 32995, 322, 323, 32998,

View File

@ -3870,6 +3870,13 @@ setup_module(PyObject* m) {
} }
#endif #endif
#ifdef HAVE_LIBTIFF
{
extern const char * ImagingTiffVersion(void);
PyDict_SetItemString(d, "libtiff_version", PyUnicode_FromString(ImagingTiffVersion()));
}
#endif
PyDict_SetItemString(d, "PILLOW_VERSION", PyUnicode_FromString(version)); PyDict_SetItemString(d, "PILLOW_VERSION", PyUnicode_FromString(version));
return 0; return 0;

View File

@ -810,7 +810,13 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
PyObject *dir; PyObject *dir;
PyObject *key, *value; PyObject *key, *value;
Py_ssize_t pos = 0; Py_ssize_t pos = 0;
int status; 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; Py_ssize_t d_size;
PyObject *keys, *values; PyObject *keys, *values;
@ -849,23 +855,36 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
return NULL; return NULL;
} }
number_of_tags = sizeof(tags) / sizeof(int);
for (pos = 0; pos < d_size; pos++) { for (pos = 0; pos < d_size; pos++) {
key = PyList_GetItem(keys, pos); key = PyList_GetItem(keys, pos);
key_int = (int)PyInt_AsLong(key);
value = PyList_GetItem(values, pos); value = PyList_GetItem(values, pos);
status = 0; status = 0;
TRACE(("Attempting to set key: %d\n", (int)PyInt_AsLong(key))); 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)) { if (PyInt_Check(value)) {
TRACE(("Setting from Int: %d %ld \n", (int)PyInt_AsLong(key),PyInt_AsLong(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, status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key), (ttag_t) PyInt_AsLong(key),
PyInt_AsLong(value)); PyInt_AsLong(value));
}
} else if (PyFloat_Check(value)) { } else if (PyFloat_Check(value)) {
TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(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, status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key), (ttag_t) PyInt_AsLong(key),
(float)PyFloat_AsDouble(value)); (double)PyFloat_AsDouble(value));
}
} else if (PyBytes_Check(value)) { } else if (PyBytes_Check(value)) {
TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value))); TRACE(("Setting from Bytes: %d, %s \n", key_int, PyBytes_AsString(value)));
status = ImagingLibTiffSetField(&encoder->state, status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key), (ttag_t) PyInt_AsLong(key),
PyBytes_AsString(value)); PyBytes_AsString(value));
@ -873,7 +892,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
Py_ssize_t len,i; Py_ssize_t len,i;
float *floatav; float *floatav;
int *intav; int *intav;
TRACE(("Setting from Tuple: %d \n", (int)PyInt_AsLong(key))); TRACE(("Setting from Tuple: %d \n", key_int));
len = PyTuple_Size(value); len = PyTuple_Size(value);
if (len) { if (len) {
if (PyInt_Check(PyTuple_GetItem(value,0))) { if (PyInt_Check(PyTuple_GetItem(value,0))) {
@ -904,13 +923,13 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
} }
} else { } else {
TRACE(("Unhandled type in tuple for key %d : %s \n", TRACE(("Unhandled type in tuple for key %d : %s \n",
(int)PyInt_AsLong(key), key_int,
PyBytes_AsString(PyObject_Str(value)))); PyBytes_AsString(PyObject_Str(value))));
} }
} }
} else { } else {
TRACE(("Unhandled type for key %d : %s \n", TRACE(("Unhandled type for key %d : %s \n",
(int)PyInt_AsLong(key), key_int,
PyBytes_AsString(PyObject_Str(value)))); PyBytes_AsString(PyObject_Str(value))));
} }
if (!status) { if (!status) {

View File

@ -402,6 +402,26 @@ int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
} }
int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key){
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 }
};
n = sizeof(info) / sizeof(info[0]);
// Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7
#if TIFFLIB_VERSION >= 20111221 && TIFFLIB_VERSION != 20120218 && TIFFLIB_VERSION != 20120922
status = TIFFMergeFieldInfo(clientstate->tiff, info, n);
#else
TIFFMergeFieldInfo(clientstate->tiff, info, n);
#endif
return status;
}
int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){ int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){
// after tif_dir.c->TIFFSetField. // after tif_dir.c->TIFFSetField.
TIFFSTATE *clientstate = (TIFFSTATE *)state->context; TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
@ -501,4 +521,11 @@ int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int
state->errcode = IMAGING_CODEC_END; state->errcode = IMAGING_CODEC_END;
return 0; return 0;
} }
const char*
ImagingTiffVersion(void)
{
return TIFFGetVersion();
}
#endif #endif

View File

@ -45,6 +45,7 @@ typedef struct {
extern int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset); extern int ImagingLibTiffInit(ImagingCodecState state, int fp, int 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 ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);