mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-17 18:54:46 +03:00
initial take of proper tiff metadata support
This commit is contained in:
parent
3f372ef54a
commit
18b0a376e3
|
@ -1430,8 +1430,18 @@ def _save(im, fp, filename):
|
||||||
# based on the data in the strip.
|
# based on the data in the strip.
|
||||||
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS]
|
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS]
|
||||||
atts = {}
|
atts = {}
|
||||||
|
# atts is a dict of key: tuple(type, array, count, value)
|
||||||
|
# where type is the tifftype int
|
||||||
|
# array is 0/1 for single or array value
|
||||||
|
# count is the number of items
|
||||||
|
# value is the value, or a tuple of the items.
|
||||||
|
# Note that we've got some items where there's an unspecified length
|
||||||
|
# in the spec (or 0), and they may have 1 item, so they need to be
|
||||||
|
# passed in in the array interface as an array of one item.
|
||||||
|
|
||||||
# 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]
|
tag_info = TiffTags.lookup(BITSPERSAMPLE)
|
||||||
|
atts[BITSPERSAMPLE] = (tag_info.type, 0, 1, bits[0])
|
||||||
# Merge the ones that we have with (optional) more bits from
|
# Merge the ones that we have with (optional) more bits from
|
||||||
# the original file, e.g x,y resolution so that we can
|
# the original file, e.g x,y resolution so that we can
|
||||||
# save(load('')) == original file.
|
# save(load('')) == original file.
|
||||||
|
@ -1448,16 +1458,38 @@ def _save(im, fp, filename):
|
||||||
# UNDONE -- add code for the custom dictionary
|
# UNDONE -- add code for the custom dictionary
|
||||||
if tag not in TiffTags.LIBTIFF_CORE:
|
if tag not in TiffTags.LIBTIFF_CORE:
|
||||||
continue
|
continue
|
||||||
if tag not in atts and tag not in blocklist:
|
if tag in atts:
|
||||||
if isinstance(value, unicode if bytes is str else str):
|
continue
|
||||||
atts[tag] = value.encode('ascii', 'replace') + b"\0"
|
if tag in blocklist:
|
||||||
elif isinstance(value, IFDRational):
|
continue
|
||||||
atts[tag] = float(value)
|
tag_info = TiffTags.lookup(tag)
|
||||||
else:
|
# numeric types
|
||||||
atts[tag] = value
|
if tag_info.length == 1:
|
||||||
|
if tag_info.type in (3,4,6,8,9):
|
||||||
|
atts[tag] = (tag_info.type, 0, 1, int(value))
|
||||||
|
elif tag_info.type in (5,10,11,12):
|
||||||
|
atts[tag] = (tag_info.type, 0, 1, float(value))
|
||||||
|
elif tag_info.type == 2:
|
||||||
|
if isinstance(value, unicode if bytes is str else str):
|
||||||
|
atts[tag] = (tag_info.type, 0, 1,
|
||||||
|
value.encode('ascii', 'replace') + b"\0")
|
||||||
|
else:
|
||||||
|
atts[tag] = (tag_info.type, 0, 1, value)
|
||||||
|
# we're not sending bytes to libtiff, as they require custom fields.
|
||||||
|
#elif tag_info.type == 7:
|
||||||
|
# atts[tag] = (tag_info.type, 0, 1, value)
|
||||||
|
|
||||||
|
else: # undefined or set length.
|
||||||
|
if tag_info.type in (3,4,6,8,9):
|
||||||
|
atts[tag] = (tag_info.type, 1, len(value), tuple(map(int,value)))
|
||||||
|
elif tag_info.type in (5,10,11,12):
|
||||||
|
atts[tag] = (tag_info.type, 1, len(value), tuple(map(float,value)))
|
||||||
|
# stringish types
|
||||||
|
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print("Converted items: %s" % sorted(atts.items()))
|
print("Converted items: %s" % sorted(atts.items()))
|
||||||
|
print("Length: %s" % len(atts))
|
||||||
|
|
||||||
# libtiff always expects the bytes in native order.
|
# libtiff always expects the bytes in native order.
|
||||||
# we're storing image byte order. So, if the rawmode
|
# we're storing image byte order. So, if the rawmode
|
||||||
|
|
|
@ -130,6 +130,7 @@ TAGS_V2 = {
|
||||||
324: ("TileOffsets", LONG, 0),
|
324: ("TileOffsets", LONG, 0),
|
||||||
325: ("TileByteCounts", LONG, 0),
|
325: ("TileByteCounts", LONG, 0),
|
||||||
|
|
||||||
|
330: ("SubIFD", SHORT, 1),
|
||||||
332: ("InkSet", SHORT, 1),
|
332: ("InkSet", SHORT, 1),
|
||||||
333: ("InkNames", ASCII, 1),
|
333: ("InkNames", ASCII, 1),
|
||||||
334: ("NumberOfInks", SHORT, 1),
|
334: ("NumberOfInks", SHORT, 1),
|
||||||
|
@ -158,6 +159,12 @@ TAGS_V2 = {
|
||||||
531: ("YCbCrPositioning", SHORT, 1),
|
531: ("YCbCrPositioning", SHORT, 1),
|
||||||
532: ("ReferenceBlackWhite", LONG, 0),
|
532: ("ReferenceBlackWhite", LONG, 0),
|
||||||
|
|
||||||
|
# sgi, in core
|
||||||
|
32995:("Matteing", SHORT, 1),
|
||||||
|
32996:("DataType", SHORT, 0),
|
||||||
|
32997:("ImageDepth", LONG, 1),
|
||||||
|
32998:("TileDepth", LONG, 1),
|
||||||
|
|
||||||
33432: ("Copyright", ASCII, 1),
|
33432: ("Copyright", ASCII, 1),
|
||||||
|
|
||||||
# FIXME add more tags here
|
# FIXME add more tags here
|
||||||
|
@ -417,6 +424,7 @@ TYPES = {}
|
||||||
# 393: case TIFFTAG_INKNAMES:
|
# 393: case TIFFTAG_INKNAMES:
|
||||||
|
|
||||||
# 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
|
||||||
|
# Anything included here needs to have the correct type in TAGS_V2 above
|
||||||
|
|
||||||
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,
|
||||||
|
@ -430,6 +438,8 @@ LIBTIFF_CORE.remove(320) # Array of short, crashes
|
||||||
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
||||||
LIBTIFF_CORE.remove(532) # Array of long, crashes
|
LIBTIFF_CORE.remove(532) # Array of long, crashes
|
||||||
|
|
||||||
|
LIBTIFF_CORE.remove(330) # subifd, requires extra support for uint64 payload
|
||||||
|
|
||||||
LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes
|
LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes
|
||||||
LIBTIFF_CORE.remove(322) # We don't have support for tiled images in libtiff
|
LIBTIFF_CORE.remove(322) # We don't have support for tiled images in libtiff
|
||||||
LIBTIFF_CORE.remove(323) # Tiled images
|
LIBTIFF_CORE.remove(323) # Tiled images
|
||||||
|
|
171
encode.c
171
encode.c
|
@ -749,6 +749,19 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#define PUSHMETAARRAY(_type, _PyFunction)\
|
||||||
|
arrav = calloc(len, sizeof(_type));\
|
||||||
|
if (arrav) {\
|
||||||
|
for (i=0;i<len;i++) {\
|
||||||
|
((_type *)arrav)[i] = (_type)_PyFunction(PyTuple_GetItem(value,i)); \
|
||||||
|
}\
|
||||||
|
status = ImagingLibTiffSetField(&encoder->state,\
|
||||||
|
(ttag_t) PyInt_AsLong(key),\
|
||||||
|
len, arrav);\
|
||||||
|
free(arrav);\
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
@ -761,7 +774,8 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
||||||
int fp;
|
int fp;
|
||||||
|
|
||||||
PyObject *dir;
|
PyObject *dir;
|
||||||
PyObject *key, *value;
|
PyObject *key, *value, *valuetuple;
|
||||||
|
int type, length, flarray;
|
||||||
Py_ssize_t pos = 0;
|
Py_ssize_t pos = 0;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
@ -804,67 +818,116 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
for (pos = 0; pos < d_size; pos++) {
|
for (pos = 0; pos < d_size; pos++) {
|
||||||
key = PyList_GetItem(keys, pos);
|
key = PyList_GetItem(keys, pos);
|
||||||
value = PyList_GetItem(values, pos);
|
valuetuple = PyList_GetItem(values, pos);
|
||||||
status = 0;
|
status = 0;
|
||||||
TRACE(("Attempting to set key: %d\n", (int)PyInt_AsLong(key)));
|
TRACE(("Attempting to set key: %d\n", (int)PyInt_AsLong(key)));
|
||||||
if (PyInt_Check(value)) {
|
|
||||||
TRACE(("Setting from Int: %d %ld \n", (int)PyInt_AsLong(key),PyInt_AsLong(value)));
|
if (! PyTuple_Check(valuetuple)) {
|
||||||
status = ImagingLibTiffSetField(&encoder->state,
|
PyErr_SetString(PyExc_ValueError, "Expected a tuple for tiff metadata");
|
||||||
(ttag_t) PyInt_AsLong(key),
|
return NULL;
|
||||||
PyInt_AsLong(value));
|
}
|
||||||
} else if (PyFloat_Check(value)) {
|
/* undone checks */
|
||||||
TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value)));
|
type = PyInt_AsLong(PyTuple_GetItem(valuetuple,0));
|
||||||
status = ImagingLibTiffSetField(&encoder->state,
|
flarray = PyInt_AsLong(PyTuple_GetItem(valuetuple,1));
|
||||||
(ttag_t) PyInt_AsLong(key),
|
length = PyInt_AsLong(PyTuple_GetItem(valuetuple,2));
|
||||||
(float)PyFloat_AsDouble(value));
|
value = PyTuple_GetItem(valuetuple,3);
|
||||||
} else if (PyBytes_Check(value)) {
|
|
||||||
TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value)));
|
if (flarray == 0) {
|
||||||
status = ImagingLibTiffSetField(&encoder->state,
|
if (length != 1) {
|
||||||
(ttag_t) PyInt_AsLong(key),
|
PyErr_SetString(PyExc_ValueError, "Expected length == 1 for non-array item");
|
||||||
PyBytes_AsString(value));
|
return NULL;
|
||||||
} else if (PyTuple_Check(value)) {
|
}
|
||||||
Py_ssize_t len,i;
|
switch (type) {
|
||||||
float *floatav;
|
case 3:
|
||||||
int *intav;
|
case 4:
|
||||||
TRACE(("Setting from Tuple: %d \n", (int)PyInt_AsLong(key)));
|
case 6:
|
||||||
len = PyTuple_Size(value);
|
case 8:
|
||||||
if (len) {
|
case 9:
|
||||||
if (PyInt_Check(PyTuple_GetItem(value,0))) {
|
if (PyInt_Check(value)) {
|
||||||
TRACE((" %d elements, setting as ints \n", (int)len));
|
TRACE(("Setting %d from Int: %d %ld \n",
|
||||||
/* 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",
|
|
||||||
(int)PyInt_AsLong(key),
|
(int)PyInt_AsLong(key),
|
||||||
PyBytes_AsString(PyObject_Str(value))));
|
type,
|
||||||
|
PyInt_AsLong(value)));
|
||||||
|
status = ImagingLibTiffSetField(&encoder->state,
|
||||||
|
(ttag_t) PyInt_AsLong(key),
|
||||||
|
PyInt_AsLong(value));
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Expected int for metadata value");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
case 10:
|
||||||
|
case 11:
|
||||||
|
case 12:
|
||||||
|
if (PyFloat_Check(value)) {
|
||||||
|
TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value)));
|
||||||
|
status = ImagingLibTiffSetField(&encoder->state,
|
||||||
|
(ttag_t) PyInt_AsLong(key),
|
||||||
|
(float)PyFloat_AsDouble(value));
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Expected floatlike for metadata value");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (PyBytes_Check(value)) {
|
||||||
|
TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value)));
|
||||||
|
status = ImagingLibTiffSetField(&encoder->state,
|
||||||
|
(ttag_t) PyInt_AsLong(key),
|
||||||
|
PyBytes_AsString(value));
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Expected stringlike for metadata value");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Py_ssize_t len,i;
|
||||||
|
void *arrav;
|
||||||
|
|
||||||
|
TRACE(("Setting from Tuple: %d \n", (int)PyInt_AsLong(key)));
|
||||||
|
len = PyTuple_Size(value);
|
||||||
|
if ((int)len == length) {
|
||||||
|
switch (type) {
|
||||||
|
case 3:
|
||||||
|
TRACE((" %d elements, setting as short \n", (int)len));
|
||||||
|
PUSHMETAARRAY(short, PyInt_AsLong);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
TRACE((" %d elements, setting as ints \n", (int)len));
|
||||||
|
PUSHMETAARRAY(int, PyInt_AsLong);
|
||||||
|
/*arrav = calloc(len, sizeof(int));
|
||||||
|
if (arrav) {
|
||||||
|
for (i=0;i<len;i++) {
|
||||||
|
(int *arrav)[i] = (int)PyInt_AsLong(PyTuple_GetItem(value,i));
|
||||||
|
}
|
||||||
|
status = ImagingLibTiffSetField(&encoder->state,
|
||||||
|
(ttag_t) PyInt_AsLong(key),
|
||||||
|
len, arrav);
|
||||||
|
free(arrav);
|
||||||
|
}*/
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
case 10:
|
||||||
|
case 11:
|
||||||
|
TRACE((" %d elements, setting as floats \n", (int)len));
|
||||||
|
/* malloc check ok, calloc checks for overflow */
|
||||||
|
PUSHMETAARRAY(float, PyFloat_AsDouble);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
TRACE((" %d elements, setting as double \n", (int)len));
|
||||||
|
PUSHMETAARRAY(double, PyFloat_AsDouble);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TRACE(("Unhandled type in tuple for key %d : %d, len: %d \n",
|
||||||
|
(int)PyInt_AsLong(key),
|
||||||
|
type, length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* } else {
|
||||||
TRACE(("Unhandled type for key %d : %s \n",
|
TRACE(("Unhandled type for key %d : %s \n",
|
||||||
(int)PyInt_AsLong(key),
|
(int)PyInt_AsLong(key),
|
||||||
PyBytes_AsString(PyObject_Str(value))));
|
PyBytes_AsString(PyObject_Str(value))));*/
|
||||||
}
|
}
|
||||||
if (!status) {
|
if (!status) {
|
||||||
TRACE(("Error setting Field\n"));
|
TRACE(("Error setting Field\n"));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user