From 45a97bb43049117e79f61dba8a870bdda6d34572 Mon Sep 17 00:00:00 2001 From: d-schmidt Date: Thu, 10 Jan 2013 20:07:28 +0100 Subject: [PATCH 1/4] fixed an error with utf-8 chars in color profile names --- _imagingcms.c | 10 +++++----- py3.h | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 6b215cabb..b29203471 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -522,19 +522,19 @@ static struct PyMethodDef cms_profile_methods[] = { static PyObject* cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) { - return PyUnicode_FromString(cmsTakeProductName(self->profile)); + return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); } static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) { - return PyUnicode_FromString(cmsTakeProductDesc(self->profile)); + return PyUnicode_DecodeFSDefault(cmsTakeProductDesc(self->profile)); } static PyObject* cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) { - return PyUnicode_FromString(cmsTakeProductInfo(self->profile)); + return PyUnicode_DecodeFSDefault(cmsTakeProductInfo(self->profile)); } static PyObject* @@ -546,13 +546,13 @@ cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) static PyObject* cms_profile_getattr_pcs(CmsProfileObject* self, void* closure) { - return PyUnicode_FromString(findICmode(cmsGetPCS(self->profile))); + return PyUnicode_DecodeFSDefault(findICmode(cmsGetPCS(self->profile))); } static PyObject* cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) { - return PyUnicode_FromString(findICmode(cmsGetColorSpace(self->profile))); + return PyUnicode_DecodeFSDefault(findICmode(cmsGetColorSpace(self->profile))); } /* FIXME: add more properties (creation_datetime etc) */ diff --git a/py3.h b/py3.h index e0658b0bb..85979c81c 100644 --- a/py3.h +++ b/py3.h @@ -30,6 +30,7 @@ #undef PyUnicode_FromStringAndSize #undef PyUnicode_FromString #undef PyUnicode_FromFormat +#undef PyUnicode_DecodeFSDefault #define PyUnicode_AsString PyString_AsString #define PyUnicode_AS_STRING PyString_AS_STRING @@ -37,6 +38,7 @@ #define PyUnicode_FromStringAndSize PyString_FromStringAndSize #define PyUnicode_FromString PyString_FromString #define PyUnicode_FromFormat PyString_FromFormat +#define PyUnicode_DecodeFSDefault PyString_FromString #endif /* Map PyBytes -> PyString */ From 900f3a8ff7a6a8c0a150370c118a6f0e0c679c1f Mon Sep 17 00:00:00 2001 From: d-schmidt Date: Thu, 10 Jan 2013 20:34:58 +0100 Subject: [PATCH 2/4] Added possibility to save gifs with a custom palette when using color mode P --- PIL/GifImagePlugin.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 97bd416e5..a8461f753 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -248,7 +248,12 @@ def _save(im, fp, filename): rawmode = "L" # header - for s in getheader(imOut, im.encoderinfo): + try: + palette = im.encoderinfo["palette"] + except KeyError: + palette = None + + for s in getheader(imOut, palette, im.encoderinfo): fp.write(s) flags = 0 @@ -319,7 +324,7 @@ def _save_netpbm(im, fp, filename): # -------------------------------------------------------------------- # GIF utilities -def getheader(im, info=None): +def getheader(im, palette, info=None): """Return a list of strings representing a GIF header""" optimize = info and info.get("optimize", 0) @@ -347,7 +352,13 @@ def getheader(im, info=None): # global palette if im.mode == "P": # colour palette - s.append(im.im.getpalette("RGB")[:maxcolor*3]) + if palette is not None and Image.isBytesType(palette): + paletteBytes = palette + else: + paletteBytes =im.im.getpalette("RGB")[:maxcolor*3] + + s.append(paletteBytes) + else: # greyscale for i in range(maxcolor): From 54d4f5eb3c1a465db145698079148103e3a9039f Mon Sep 17 00:00:00 2001 From: d-schmidt Date: Thu, 10 Jan 2013 20:44:41 +0100 Subject: [PATCH 3/4] added option to load truncated image-files --- PIL/ImageFile.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index eb4caef24..688c33a1d 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -35,6 +35,8 @@ MAXBLOCK = 65536 SAFEBLOCK = 1024*1024 +LOAD_TRUNCATED_IMAGES = False + ERRORS = { -1: "image buffer overrun error", -2: "decoding error", @@ -196,10 +198,22 @@ class ImageFile(Image.Image): b = prefix t = len(b) while True: - s = read(self.decodermaxblock) - if not s: + try: + s = read(self.decodermaxblock) + except IndexError as ie: # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IndexError(ie) + + if not s: # truncated jpeg self.tile = [] - raise IOError("image file is truncated (%d bytes not processed)" % len(b)) + + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IOError("image file is truncated (%d bytes not processed)" % len(b)) + b = b + s n, e = d.decode(b) if n < 0: @@ -212,7 +226,9 @@ class ImageFile(Image.Image): self.fp = None # might be shared - if not self.map and e < 0: + if not LOAD_TRUNCATED_IMAGES and not self.map and e < 0: + # Note: If loading a truncated image results in an all black Image, + # the decoder wasn't able to decode anything. raise_ioerror(e) # post processing From addf0f4d95c2b35af12bade794b5d3e43c18b637 Mon Sep 17 00:00:00 2001 From: d-schmidt Date: Thu, 10 Jan 2013 21:36:21 +0100 Subject: [PATCH 4/4] Added possibility to save exif information in jpeg-files --- PIL/JpegImagePlugin.py | 1 + encode.c | 26 +++++++++++++++++++++----- libImaging/Jpeg.h | 3 +++ libImaging/JpegEncode.c | 18 +++++++++++++----- py3.h | 4 ++-- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index aa6745ee8..3416711a4 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -465,6 +465,7 @@ def _save(im, fp, filename): dpi[0], dpi[1], subsampling, extra, + info.get("exif", "") ) ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)]) diff --git a/encode.c b/encode.c index 479da7d51..4f5902e5b 100644 --- a/encode.c +++ b/encode.c @@ -15,7 +15,7 @@ * 1999-02-07 fl Added PCX encoder * * Copyright (c) 1997-2001 by Secret Labs AB - * Copyright (c) 1996-1997 by Fredrik Lundh + * Copyright (c) 1996-1997 by Fredrik Lundh * * See the README file for information on usage and redistribution. */ @@ -93,7 +93,7 @@ _dealloc(ImagingEncoderObject* encoder) PyObject_Del(encoder); } -static PyObject* +static PyObject* _encode(ImagingEncoderObject* encoder, PyObject* args) { PyObject* buf; @@ -125,7 +125,7 @@ _encode(ImagingEncoderObject* encoder, PyObject* args) return result; } -static PyObject* +static PyObject* _encode_to_file(ImagingEncoderObject* encoder, PyObject* args) { UINT8* buf; @@ -520,11 +520,16 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */ int xdpi = 0, ydpi = 0; int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */ - char* extra = NULL; int extra_size; + char* extra = NULL; + int extra_size; + char* rawExif = NULL; + int rawExifLen = 0; + if (!PyArg_ParseTuple(args, "ss|iiiiiiii"PY_ARG_BYTES_LENGTH, &mode, &rawmode, &quality, &progressive, &smooth, &optimize, &streamtype, - &xdpi, &ydpi, &subsampling, &extra, &extra_size)) + &xdpi, &ydpi, &subsampling, &extra, &extra_size, + &rawExif, &rawExifLen)) return NULL; encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE)); @@ -543,6 +548,15 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) } else extra = NULL; + if (rawExif && rawExifLen > 0) { + char* pp = malloc(rawExifLen); + if (!pp) + return PyErr_NoMemory(); + memcpy(pp, rawExif, rawExifLen); + rawExif = pp; + } else + rawExif = NULL; + encoder->encode = ImagingJpegEncode; ((JPEGENCODERSTATE*)encoder->state.context)->quality = quality; @@ -555,6 +569,8 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) ((JPEGENCODERSTATE*)encoder->state.context)->ydpi = ydpi; ((JPEGENCODERSTATE*)encoder->state.context)->extra = extra; ((JPEGENCODERSTATE*)encoder->state.context)->extra_size = extra_size; + ((JPEGENCODERSTATE*)encoder->state.context)->rawExif = rawExif; + ((JPEGENCODERSTATE*)encoder->state.context)->rawExifLen = rawExifLen; return (PyObject*) encoder; } diff --git a/libImaging/Jpeg.h b/libImaging/Jpeg.h index d39165f3c..749d42a22 100644 --- a/libImaging/Jpeg.h +++ b/libImaging/Jpeg.h @@ -100,5 +100,8 @@ typedef struct { JPEGDESTINATION destination; int extra_offset; + + int rawExifLen; /* EXIF data length */ + char* rawExif; /* EXIF buffer pointer */ } JPEGENCODERSTATE; diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c index 1e2191a69..50ddf61da 100644 --- a/libImaging/JpegEncode.c +++ b/libImaging/JpegEncode.c @@ -24,9 +24,9 @@ #ifdef HAVE_LIBJPEG -#undef HAVE_PROTOTYPES -#undef HAVE_STDLIB_H -#undef HAVE_STDDEF_H +#undef HAVE_PROTOTYPES +#undef HAVE_STDLIB_H +#undef HAVE_STDDEF_H #undef UINT8 #undef UINT16 #undef UINT32 @@ -145,7 +145,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) jpeg_set_defaults(&context->cinfo); if (context->quality > 0) jpeg_set_quality(&context->cinfo, context->quality, 1); - + /* Set subsampling options */ switch (context->subsampling) { @@ -205,11 +205,19 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) jpeg_start_compress(&context->cinfo, FALSE); /* suppress extra section */ context->extra_offset = context->extra_size; + //add exif header + if (context->rawExifLen > 0) + jpeg_write_marker(&context->cinfo, JPEG_APP0+1, context->rawExif, context->rawExifLen); + break; default: /* interchange stream */ jpeg_start_compress(&context->cinfo, TRUE); - break; + //add exif header + if (context->rawExifLen > 0) + jpeg_write_marker(&context->cinfo, JPEG_APP0+1, context->rawExif, context->rawExifLen); + + break; } state->state++; /* fall through */ diff --git a/py3.h b/py3.h index 85979c81c..bee457e06 100644 --- a/py3.h +++ b/py3.h @@ -11,7 +11,7 @@ */ #if PY_VERSION_HEX >= 0x03000000 -#define PY_ARG_BYTES_LENGTH "y#" +#define PY_ARG_BYTES_LENGTH "y#y#" /* Map PyInt -> PyLong */ #define PyInt_AsLong PyLong_AsLong @@ -20,7 +20,7 @@ #define PyInt_AS_LONG PyLong_AS_LONG #else /* PY_VERSION_HEX < 0x03000000 */ -#define PY_ARG_BYTES_LENGTH "s#" +#define PY_ARG_BYTES_LENGTH "s#s#" #if !defined(KEEP_PY_UNICODE) /* Map PyUnicode -> PyString */