diff --git a/PKG-INFO b/PKG-INFO
index 4eb4c32e0..16e646bf8 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: PyMuPDF
-Version: 1.14.12
+Version: 1.14.13
Author: Ruikai Liu
Author-email: lrk700@gmail.com
Maintainer: Jorj X. McKie
@@ -21,7 +21,7 @@ Description:
Introduction
============
- This is **version 1.14.12 of PyMuPDF**, a Python binding for `MuPDF `_ - "a lightweight PDF and XPS viewer".
+ This is **version 1.14.13 of PyMuPDF**, a Python binding for `MuPDF `_ - "a lightweight PDF and XPS viewer".
MuPDF can access files in PDF, XPS, OpenXPS, epub, comic and fiction book formats, and it is known for both, its top performance and high rendering quality.
diff --git a/README.md b/README.md
index 65053d1db..09faf9dcd 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-# PyMuPDF 1.14.12
+# PyMuPDF 1.14.13

-Release date: January 15, 2018
+Release date: April 7, 2019
**Travis-CI:** [](https://travis-ci.org/JorjMcKie/py-mupdf)
@@ -14,7 +14,7 @@ On **[PyPI](https://pypi.org/project/PyMuPDF)** since August 2016: [**, a Python binding with support for [MuPDF 1.14.x](http://mupdf.com/) - "a lightweight PDF, XPS, and E-book viewer".
+This is **version 1.14.13 of PyMuPDF (formerly python-fitz)**, a Python binding with support for [MuPDF 1.14.x](http://mupdf.com/) - "a lightweight PDF, XPS, and E-book viewer".
MuPDF can access files in PDF, XPS, OpenXPS, CBZ, EPUB and FB2 (e-books) formats, and it is known for its top performance and high rendering quality.
@@ -79,7 +79,7 @@ Our **documentation**, written using Sphinx, is available in various formats fro
* You can view it online at [Read the Docs](https://pymupdf.readthedocs.io/). For **best quality downloads** you should however use the following links.
* zipped [HTML](https://github.com/pymupdf/PyMuPDF/tree/master/doc/html.zip)
-* [Windows CHM](https://github.com/JorjMcKie/PyMuPDF-optional-material/tree/master/doc/PyMuPDF.chm)
+* [Windows CHM](https://github.com/pymupdf/PyMuPDF-optional-material/tree/master/doc/PyMuPDF.chm)
* [PDF](https://github.com/pymupdf/PyMuPDF/blob/master/doc/PyMuPDF.pdf)
# Earlier Versions
diff --git a/doc/PyMuPDF.pdf b/doc/PyMuPDF.pdf
index 8388c3075..6873aec5c 100644
Binary files a/doc/PyMuPDF.pdf and b/doc/PyMuPDF.pdf differ
diff --git a/doc/html.zip b/doc/html.zip
index 78b2b7bf3..132476fc2 100644
Binary files a/doc/html.zip and b/doc/html.zip differ
diff --git a/fitz/fitz.i b/fitz/fitz.i
index b02b3f976..dc7d05008 100644
--- a/fitz/fitz.i
+++ b/fitz/fitz.i
@@ -101,7 +101,7 @@
# endif
// define Python None object
-#define NONE Py_BuildValue("", NULL)
+#define NONE Py_BuildValue("")
#include
#include
@@ -171,6 +171,7 @@ struct DeviceWrapper {
%pythoncode %{
import os
import weakref
+import io
from binascii import hexlify
import math
@@ -213,28 +214,36 @@ struct fz_document_s
if not filename or type(filename) is str:
pass
else:
- if fitz_py2: # Python 2
+ if fitz_py2: # Python 2
if type(filename) is unicode:
filename = filename.encode("utf8")
else:
- filename = str(filename) # should take care of pathlib
+ filename = str(filename) # should take care of pathlib
- self.streamlen = len(stream) if stream else 0
+ if stream:
+ if not (filename or filetype):
+ raise ValueError("need filetype for opening a stream")
+
+ if type(stream) is bytes:
+ self.stream = stream
+ elif type(stream) is bytearray:
+ self.stream = bytes(stream)
+ elif type(stream) is io.BytesIO:
+ self.stream = stream.getvalue()
+ else:
+ raise ValueError("'stream' has bad type")
+ stream = self.stream
+ else:
+ self.stream = None
- self.name = ""
- if filename and self.streamlen == 0:
+ if filename and not stream:
self.name = filename
-
- if self.streamlen > 0:
- if not (filename or filetype):
- raise ValueError("filetype missing with stream specified")
- if type(stream) not in (bytes, bytearray):
- raise ValueError("stream must be bytes or bytearray")
+ else:
+ self.name = ""
self.isClosed = False
self.isEncrypted = 0
self.metadata = None
- self.stream = stream # prevent garbage collecting this
self.openErrCode = 0
self.openErrMsg = ''
self.FontInfos = []
@@ -256,16 +265,17 @@ struct fz_document_s
self.thisown = False
%}
- fz_document_s(const char *filename = NULL, PyObject *stream = NULL,
- const char *filetype = NULL, PyObject *rect = NULL,
- float width = 0, float height = 0,
- float fontsize = 11)
+ fz_document_s(const char *filename=NULL, PyObject *stream=NULL,
+ const char *filetype=NULL, PyObject *rect=NULL,
+ float width=0, float height=0,
+ float fontsize=11)
{
gctx->error->errcode = 0; // reset any error code
gctx->error->message[0] = 0; // reset any error message
struct fz_document_s *doc = NULL;
+ char *c = NULL;
+ size_t len = 0;
fz_stream *data = NULL;
- char *streamdata;
float w = width, h = height;
fz_rect r = JM_rect_from_py(rect);
if (!(fz_is_empty_rect(r) && !fz_is_infinite_rect(r)))
@@ -274,12 +284,13 @@ struct fz_document_s
h = r.y1 - r.y0;
}
- size_t streamlen = JM_CharFromBytesOrArray(stream, &streamdata);
fz_try(gctx)
{
- if (streamlen > 0)
+ if (stream != NONE) // stream given, MUST be bytes!
{
- data = fz_open_memory(gctx, streamdata, streamlen);
+ c = PyBytes_AsString(stream); // just a pointer, no new obj
+ len = (size_t) PyBytes_Size(stream);
+ data = fz_open_memory(gctx, c, len);
char *magic = (char *)filename;
if (!magic) magic = (char *)filetype;
doc = fz_open_document_with_stream(gctx, magic, data);
@@ -330,6 +341,7 @@ struct fz_document_s
self.Graftmaps = {}
self.ShownPages = {}
%}
+
%pythonappend close %{self.thisown = False%}
void close()
{
@@ -492,7 +504,6 @@ struct fz_document_s
}
FITZEXCEPTION(embeddedFileUpd, !result)
- CLOSECHECK(embeddedFileUpd)
%feature("autodoc","Change an embedded file given its entry number or name.") embeddedFileUpd;
PyObject *embeddedFileUpd(PyObject *id, PyObject *buffer = NULL, char *filename = NULL, char *ufilename = NULL, char *desc = NULL)
{
@@ -509,19 +520,20 @@ struct fz_document_s
pdf_obj *entry = pdf_portfolio_entry_obj(gctx, pdf, n);
pdf_obj *filespec = pdf_dict_getl(gctx, entry, PDF_NAME(EF),
PDF_NAME(F), NULL);
+ if (!filespec) THROWMSG("bad PDF: /EF object not found");
- char *data = NULL;
- size_t len = JM_CharFromBytesOrArray(buffer, &data);
- if (len > 0)
+ res = JM_BufferFromBytes(gctx, buffer);
+ if (buffer && !res) THROWMSG("'buffer' has bad type");
+ if (res)
{
- if (!filespec) THROWMSG("/EF object not found");
- res = fz_new_buffer_from_copied_data(gctx, data, len);
JM_update_stream(gctx, pdf, filespec, res);
// adjust /DL and /Size parameters
- pdf_obj *l = pdf_new_int(gctx, (int64_t) len);
+ int64_t len = (int64_t) fz_buffer_storage(gctx, res, NULL);
+ pdf_obj *l = pdf_new_int(gctx, len);
pdf_dict_put(gctx, filespec, PDF_NAME(DL), l);
pdf_dict_putl(gctx, filespec, l, PDF_NAME(Params), PDF_NAME(Size), NULL);
}
+
if (filename)
pdf_dict_put_text_string(gctx, entry, PDF_NAME(F), filename);
@@ -568,7 +580,10 @@ struct fz_document_s
}
FITZEXCEPTION(embeddedFileAdd, !result)
- CLOSECHECK(embeddedFileAdd)
+ %pythonprepend embeddedFileAdd %{
+if self.isClosed or self.isEncrypted:
+ raise ValueError("operation illegal for closed / encrypted doc")
+%}
%feature("autodoc","Embed a new file.") embeddedFileAdd;
PyObject *embeddedFileAdd(PyObject *buffer, const char *name, char *filename=NULL, char *ufilename=NULL, char *desc=NULL)
{
@@ -589,8 +604,9 @@ struct fz_document_s
fz_try(gctx)
{
assert_PDF(pdf);
- size = JM_CharFromBytesOrArray(buffer, &buffdata);
- if (size < 1) THROWMSG("buffer not bytes / bytearray");
+ data = JM_BufferFromBytes(gctx, buffer);
+ if (!data) THROWMSG("bad type 'buffer'");
+ size = fz_buffer_storage(gctx, data, &buffdata);
// we do not allow duplicate names
entry = JM_find_embedded(gctx, Py_BuildValue("s", name), pdf);
@@ -623,7 +639,6 @@ struct fz_document_s
// (2) insert the real file contents
pdf_obj *filespec = pdf_dict_getl(gctx, o, PDF_NAME(EF),
PDF_NAME(F), NULL);
- data = fz_new_buffer_from_copied_data(gctx, buffdata, size);
JM_update_stream(gctx, pdf, filespec, data);
// finally update some size attributes
pdf_obj *l = pdf_new_int(gctx, (int64_t) size);
@@ -910,7 +925,7 @@ struct fz_document_s
if self.pageCount < 1:
raise ValueError("cannot save with zero pages")
if incremental:
- if self.name != filename or self.streamlen > 0:
+ if self.name != filename or self.stream:
raise ValueError("incremental needs original file")
%}
@@ -1065,7 +1080,7 @@ if links:
PyObject *_newPage(int pno=-1, float width=595, float height=842)
{
pdf_document *pdf = pdf_specifics(gctx, $self);
- fz_rect mediabox = { 0, 0, 595, 842 }; // ISO-A4 portrait values
+ fz_rect mediabox = fz_unit_rect;
mediabox.x1 = width;
mediabox.y1 = height;
pdf_obj *resources = NULL, *page_obj = NULL;
@@ -1073,7 +1088,7 @@ if links:
fz_try(gctx)
{
assert_PDF(pdf);
- if (pno < -1) THROWMSG("invalid page number(s)");
+ if (pno < -1) THROWMSG("bad page number(s)");
// create /Resources and /Contents objects
resources = pdf_add_object_drop(gctx, pdf, pdf_new_dict(gctx, pdf, 1));
page_obj = pdf_add_page(gctx, pdf, mediabox, 0, resources, contents);
@@ -1226,7 +1241,7 @@ if links:
fz_var(pageref);
fz_try(gctx)
{
- if (n >= pageCount) THROWMSG("invalid page number(s)");
+ if (n >= pageCount) THROWMSG("bad page number(s)");
assert_PDF(pdf);
pageref = pdf_lookup_page_obj(gctx, pdf, n);
}
@@ -1256,7 +1271,7 @@ if links:
fz_var(liste);
fz_try(gctx)
{
- if (n >= pageCount) THROWMSG("invalid page number(s)");
+ if (n >= pageCount) THROWMSG("bad page number(s)");
assert_PDF(pdf);
pageref = pdf_lookup_page_obj(gctx, pdf, n);
rsrc = pdf_dict_get(gctx, pageref, PDF_NAME(Resources));
@@ -1991,17 +2006,17 @@ if links:
def __repr__(self):
m = "closed " if self.isClosed else ""
- if self.streamlen == 0:
- if self.name == "":
- return m + "fitz.Document()"
+ if self.stream is None:
+ if self.name is "":
+ return m + "fitz.Document()" % self._graft_id
return m + "fitz.Document('%s')" % (self.name,)
- return m + "fitz.Document('%s', )" % (self.name,)
+ return m + "fitz.Document('%s', )" % (self.name, self._graft_id)
def __getitem__(self, i=0):
if type(i) is not int:
- raise ValueError("invalid page number(s)")
+ raise ValueError("bad page number(s)")
if i >= len(self):
- raise IndexError("invalid page number(s)")
+ raise IndexError("bad page number(s)")
return self.loadPage(i)
def __len__(self):
@@ -2030,11 +2045,12 @@ if links:
for gmap in self.Graftmaps:
self.Graftmaps[gmap] = None
if hasattr(self, "this") and self.thisown:
- self.thisown = False
self.__swig_destroy__(self)
+ self.thisown = False
+
self.Graftmaps = {}
self.ShownPages = {}
- self.stream = None
+ self.stream = None
self._reset_page_refs = DUMMY
self.__swig_destroy__ = DUMMY
self.isClosed = True
@@ -2301,11 +2317,11 @@ struct fz_page_s {
fz_try(gctx)
{
assert_PDF(page);
+ filebuf = JM_BufferFromBytes(gctx, buffer);
+ if (!filebuf) THROWMSG("bad 'buffer' data");
annot = pdf_create_annot(gctx, page, ANNOT_FILEATTACHMENT);
pdf_set_annot_rect(gctx, annot, r);
pdf_set_annot_icon_name(gctx, annot, "PushPin");
- len = JM_CharFromBytesOrArray(buffer, &data);
- filebuf = fz_new_buffer_from_shared_data(gctx, data, len);
pdf_obj *val = JM_embed_file(gctx, page->doc, filebuf,
filename, uf, d);
pdf_dict_put(gctx, annot->obj, PDF_NAME(FS), val);
@@ -3028,11 +3044,9 @@ fannot._erase()
fz_pixmap *pix = NULL;
fz_image *mask = NULL;
fz_separations *seps = NULL;
- pdf_obj *resources, *subres, *ref;
+ pdf_obj *resources, *xobject, *ref;
fz_buffer *res = NULL, *nres = NULL, *imgbuf = NULL;
fz_matrix mat = JM_matrix_from_py(matrix); // pre-calculated
- char *streamdata = NULL;
- size_t streamlen = JM_CharFromBytesOrArray(stream, &streamdata);
const char *template = " q %g %g %g %g %g %g cm /%s Do Q ";
char *cont = NULL;
@@ -3040,33 +3054,23 @@ fannot._erase()
fz_image *zimg = NULL, *image = NULL;
fz_try(gctx)
{
- pdf = page->doc;
-
- // get objects "Resources" & "XObject"
- resources = pdf_dict_get(gctx, page->obj, PDF_NAME(Resources));
- subres = pdf_dict_get(gctx, resources, PDF_NAME(XObject));
- if (!subres) // has no XObject yet, create one
- {
- subres = pdf_new_dict(gctx, pdf, 10);
- pdf_dict_putl_drop(gctx, page->obj, subres, PDF_NAME(Resources), PDF_NAME(XObject), NULL);
- }
-
+ //-------------------------------------------------------------
// create the image
- if (filename || streamlen > 0)
+ //-------------------------------------------------------------
+ if (filename || stream)
{
- if (!streamlen)
+ if (filename)
image = fz_new_image_from_file(gctx, filename);
else
{
- imgbuf = fz_new_buffer_from_copied_data(gctx,
- streamdata, streamlen);
+ imgbuf = JM_BufferFromBytes(gctx, stream);
image = fz_new_image_from_buffer(gctx, imgbuf);
}
- // test alpha channel (would require SMask creation)
+ // test for alpha (which would require making an SMask)
pix = fz_get_pixmap_from_image(gctx, image, NULL, NULL, 0, 0);
if (pix->alpha == 1)
- { // have alpha, therefore create a mask
+ { // have alpha: create an SMask
pm = fz_convert_pixmap(gctx, pix, NULL, NULL, NULL, NULL, 1);
pm->alpha = 0;
pm->colorspace = fz_keep_colorspace(gctx, fz_device_gray(gctx));
@@ -3082,7 +3086,7 @@ fannot._erase()
if (pixmap->alpha == 0)
image = fz_new_image_from_pixmap(gctx, pixmap, NULL);
else
- { // pixmap has alpha, therefore create an SMask
+ { // pixmap has alpha: create an SMask
pm = fz_convert_pixmap(gctx, pixmap, NULL, NULL, NULL, NULL, 1);
pm->alpha = 0;
pm->colorspace = fz_keep_colorspace(gctx, fz_device_gray(gctx));
@@ -3090,11 +3094,25 @@ fannot._erase()
image = fz_new_image_from_pixmap(gctx, pixmap, mask);
}
}
+
+ //-------------------------------------------------------------
// image created - now put it in the PDF
+ //-------------------------------------------------------------
+ pdf = page->doc; // owning PDF
+
+ // get /Resources, /XObject
+ resources = pdf_dict_get(gctx, page->obj, PDF_NAME(Resources));
+ xobject = pdf_dict_get(gctx, resources, PDF_NAME(XObject));
+ if (!xobject) // has no XObject yet, create one
+ {
+ xobject = pdf_new_dict(gctx, pdf, 10);
+ pdf_dict_putl_drop(gctx, page->obj, xobject, PDF_NAME(Resources), PDF_NAME(XObject), NULL);
+ }
+
ref = pdf_add_image(gctx, pdf, image, 0);
- pdf_dict_puts(gctx, subres, _imgname, ref); // store ref-name
+ pdf_dict_puts(gctx, xobject, _imgname, ref); // update XObject
- // prep contents stream buffer with invoking command
+ // make contents stream that invokes the image
nres = fz_new_buffer(gctx, 50);
fz_append_printf(gctx, nres, template,
mat.a, mat.b, mat.c, mat.d, mat.e, mat.f,
@@ -3193,6 +3211,7 @@ def insertFont(self, fontname="helv", fontfile=None, fontbuffer=None,
pdf_document *pdf;
pdf_obj *resources, *fonts, *font_obj;
fz_font *font;
+ fz_buffer *res = NULL;
char *data = NULL;
int size, ixref = 0, index = 0, simple = 0;
PyObject *value;
@@ -3240,9 +3259,9 @@ def insertFont(self, fontname="helv", fontfile=None, fontbuffer=None,
font = fz_new_font_from_file(gctx, NULL, fontfile, idx, 0);
else
{
- size = (int) JM_CharFromBytesOrArray(fontbuffer, &data);
- if (!size) THROWMSG("one of fontfile, fontbuffer must be given");
- font = fz_new_font_from_memory(gctx, NULL, data, size, idx, 0);
+ res = JM_BufferFromBytes(gctx, fontbuffer);
+ if (!res) THROWMSG("one of fontfile, fontbuffer must be given");
+ font = fz_new_font_from_buffer(gctx, NULL, res, idx, 0);
}
if (!set_simple)
@@ -3284,6 +3303,10 @@ def insertFont(self, fontname="helv", fontfile=None, fontbuffer=None,
pdf_drop_obj(gctx, font_obj);
fz_drop_font(gctx, font);
}
+ fz_always(gctx)
+ {
+ fz_drop_buffer(gctx, res);
+ }
fz_catch(gctx) return NULL;
pdf->dirty = 1;
return value;
@@ -3372,19 +3395,19 @@ def insertFont(self, fontname="helv", fontfile=None, fontbuffer=None,
def __str__(self):
CheckParent(self)
x = self.parent.name
- if self.parent.streamlen > 0:
- x += " (memory)"
+ if self.parent.stream is not None:
+ x = "" % (self.parent._graft_id,)
if x == "":
- x = ""
+ x = "" % self.parent._graft_id
return "page %s of %s" % (self.number, x)
def __repr__(self):
CheckParent(self)
x = self.parent.name
- if self.parent.streamlen > 0:
- x += " (memory)"
+ if self.parent.stream is not None:
+ x = "" % (self.parent._graft_id,)
if x == "":
- x = ""
+ x = "" % self.parent._graft_id
return "page %s of %s" % (self.number, x)
def _forget_annot(self, annot):
@@ -3565,19 +3588,30 @@ struct fz_pixmap_s
//---------------------------------------------------------------------
fz_pixmap_s(struct fz_colorspace_s *cs, int w, int h, PyObject *samples, int alpha = 0)
{
- char *data = NULL;
int n = fz_colorspace_n(gctx, cs);
int stride = (n + alpha)*w;
fz_separations *seps = NULL;
+ fz_buffer *res = NULL;
fz_pixmap *pm = NULL;
- size_t size = JM_CharFromBytesOrArray(samples, &data);
fz_try(gctx)
{
- if (size < 1) THROWMSG("invalid arg type samples");
- if (stride * h != size) THROWMSG("invalid arg len samples");
- pm = fz_new_pixmap_with_data(gctx, cs, w, h, seps, alpha, stride, data);
+ size_t size = 0;
+ unsigned char *c = NULL;
+ res = JM_BufferFromBytes(gctx, samples);
+ if (!res) THROWMSG("bad samples data");
+ size = fz_buffer_storage(gctx, res, &c);
+ if (stride * h != size) THROWMSG("bad samples length");
+ pm = fz_new_pixmap(gctx, cs, w, h, seps, alpha);
+ memcpy(pm->samples, c, size);
+ }
+ fz_always(gctx)
+ {
+ fz_drop_buffer(gctx, res);
+ }
+ fz_catch(gctx)
+ {
+ return NULL;
}
- fz_catch(gctx) return NULL;
return pm;
}
@@ -3589,12 +3623,17 @@ struct fz_pixmap_s
fz_image *img = NULL;
fz_pixmap *pm = NULL;
fz_try(gctx) {
- if (!filename) THROWMSG("invalid argument type");
img = fz_new_image_from_file(gctx, filename);
pm = fz_get_pixmap_from_image(gctx, img, NULL, NULL, NULL, NULL);
}
- fz_always(gctx) fz_drop_image(gctx, img);
- fz_catch(gctx) return NULL;
+ fz_always(gctx)
+ {
+ fz_drop_image(gctx, img);
+ }
+ fz_catch(gctx)
+ {
+ return NULL;
+ }
return pm;
}
@@ -3603,23 +3642,20 @@ struct fz_pixmap_s
//---------------------------------------------------------------------
fz_pixmap_s(PyObject *imagedata)
{
- size_t size = 0;
- char *streamdata;
- fz_buffer *data = NULL;
+ fz_buffer *res = NULL;
fz_image *img = NULL;
fz_pixmap *pm = NULL;
fz_try(gctx)
{
- size = JM_CharFromBytesOrArray(imagedata, &streamdata);
- if (size < 1) THROWMSG("bad image data");
- data = fz_new_buffer_from_shared_data(gctx,
- streamdata, size);
- img = fz_new_image_from_buffer(gctx, data);
+ res = JM_BufferFromBytes(gctx, imagedata);
+ if (!res) THROWMSG("bad image data");
+ img = fz_new_image_from_buffer(gctx, res);
pm = fz_get_pixmap_from_image(gctx, img, NULL, NULL, NULL, NULL);
}
fz_always(gctx)
{
fz_drop_image(gctx, img);
+ fz_drop_buffer(gctx, res);
}
fz_catch(gctx) return NULL;
return pm;
@@ -3747,6 +3783,7 @@ struct fz_pixmap_s
FITZEXCEPTION(setAlpha, !result)
PyObject *setAlpha(PyObject *alphavalues=NULL)
{
+ fz_buffer *res = NULL;
fz_try(gctx)
{
if ($self->alpha == 0) THROWMSG("pixmap has no alpha");
@@ -3758,9 +3795,14 @@ struct fz_pixmap_s
int data_len = 0;
if (alphavalues)
{
- data_len = (int) JM_CharFromBytesOrArray(alphavalues, &data);
- if (data_len && data_len < w * h)
- THROWMSG("not enough alpha values");
+ res = JM_BufferFromBytes(gctx, alphavalues);
+ if (res)
+ {
+ data_len = (int) fz_buffer_storage(gctx, res, &data);
+ if (data && data_len < w * h)
+ THROWMSG("not enough alpha values");
+ }
+ else THROWMSG("bad type 'alphavalues'");
}
int i = 0, k = 0;
while (i < balen)
@@ -3771,7 +3813,14 @@ struct fz_pixmap_s
k += 1;
}
}
- fz_catch(gctx) return NULL;
+ fz_always(gctx)
+ {
+ fz_drop_buffer(gctx, res);
+ }
+ fz_catch(gctx)
+ {
+ return NULL;
+ }
return NONE;
}
@@ -4497,10 +4546,8 @@ struct fz_annot_s
if (!apobj) THROWMSG("annot has no /AP/N object");
if (!pdf_is_stream(gctx, apobj))
THROWMSG("/AP/N object is no stream");
- char *c = NULL;
- size_t len = JM_CharFromBytesOrArray(ap, &c);
- if (!c) THROWMSG("invalid /AP stream argument");
- res = fz_new_buffer_from_copied_data(gctx, c, strlen(c));
+ res = JM_BufferFromBytes(gctx, ap);
+ if (!res) THROWMSG("invalid /AP stream argument");
JM_update_stream(gctx, annot->page->doc, apobj, res);
if (rect)
{
@@ -5128,11 +5175,14 @@ struct fz_annot_s
}
//---------------------------------------------------------------------
- // annotation update attached file content
+ // annotation update attached file
//---------------------------------------------------------------------
FITZEXCEPTION(fileUpd, !result)
- PARENTCHECK(fileUpd)
- %feature("autodoc","Update annotation attached file content.") fileUpd;
+ %pythonprepend fileUpd %{
+CheckParent(self)
+%}
+
+ %feature("autodoc","Update annotation attached file.") fileUpd;
PyObject *fileUpd(PyObject *buffer=NULL, char *filename=NULL, char *ufilename=NULL, char *desc=NULL)
{
pdf_annot *annot = pdf_annot_from_fz_annot(gctx, $self);
@@ -5144,41 +5194,43 @@ struct fz_annot_s
fz_try(gctx)
{
assert_PDF(annot); // must be a PDF
- pdf = annot->page->doc; // this is the PDF
+ pdf = annot->page->doc; // the owning PDF
int type = (int) pdf_annot_type(gctx, annot);
if (type != ANNOT_FILEATTACHMENT)
- THROWMSG("not a file attachment annot");
+ THROWMSG("no FileAttachment annot");
stream = pdf_dict_getl(gctx, annot->obj, PDF_NAME(FS),
PDF_NAME(EF), PDF_NAME(F), NULL);
// the object for file content
- if (!stream) THROWMSG("bad PDF: file entry not found");
+ if (!stream) THROWMSG("bad PDF: /EF object not found");
fs = pdf_dict_get(gctx, annot->obj, PDF_NAME(FS));
- // file content is ignored if not bytes / bytearray
- size = (int64_t) JM_CharFromBytesOrArray(buffer, &data);
- if (size > 0)
+ // file content given
+ res = JM_BufferFromBytes(gctx, buffer);
+ if (buffer && !res) THROWMSG("'buffer' has bad type");
+ if (res)
{
- pdf_obj *s = pdf_new_int(gctx, size);
- pdf_dict_put(gctx, stream, PDF_NAME(Filter),
- PDF_NAME(FlateDecode));
-
- pdf_dict_putl_drop(gctx, stream, s,
- PDF_NAME(Params), PDF_NAME(Size), NULL);
- res = JM_deflatebuf(gctx, data, size);
- pdf_update_stream(gctx, pdf, stream, res, 1);
+ JM_update_stream(gctx, pdf, stream, res);
+ // adjust /DL and /Size parameters
+ int64_t len = (int64_t) fz_buffer_storage(gctx, res, NULL);
+ pdf_obj *l = pdf_new_int(gctx, len);
+ pdf_dict_put(gctx, stream, PDF_NAME(DL), l);
+ pdf_dict_putl(gctx, stream, l, PDF_NAME(Params), PDF_NAME(Size), NULL);
}
if (filename) // new filename given
{
pdf_dict_put_text_string(gctx, stream, PDF_NAME(F), filename);
pdf_dict_put_text_string(gctx, fs, PDF_NAME(F), filename);
+ pdf_dict_put_text_string(gctx, stream, PDF_NAME(UF), filename);
+ pdf_dict_put_text_string(gctx, fs, PDF_NAME(UF), filename);
+ pdf_dict_put_text_string(gctx, annot->obj, PDF_NAME(Contents), filename);
}
if (ufilename)
{
- pdf_dict_put_text_string(gctx, stream, PDF_NAME(UF), filename);
- pdf_dict_put_text_string(gctx, fs, PDF_NAME(UF), filename);
+ pdf_dict_put_text_string(gctx, stream, PDF_NAME(UF), ufilename);
+ pdf_dict_put_text_string(gctx, fs, PDF_NAME(UF), ufilename);
}
if (desc) // new description given
@@ -6241,6 +6293,12 @@ struct Tools
return Py_BuildValue("i", (int) gctx->store->size);
}
+ %feature("autodoc","Determine dimension and other image data.") image_size;
+ PyObject *image_size(PyObject *imagedata)
+ {
+ return JM_image_size(gctx, imagedata);
+ }
+
%feature("autodoc","Current store size.") store_size;
%pythoncode%{@property%}
PyObject *store_size()
diff --git a/fitz/fitz.py b/fitz/fitz.py
index 7131ed795..4db6f532d 100644
--- a/fitz/fitz.py
+++ b/fitz/fitz.py
@@ -98,6 +98,7 @@ class _object:
import os
import weakref
+import io
from binascii import hexlify
import math
@@ -105,9 +106,9 @@ class _object:
VersionFitz = "1.14.0"
-VersionBind = "1.14.12"
-VersionDate = "2019-03-21 06:59:25"
-version = (VersionBind, VersionFitz, "20190321065925")
+VersionBind = "1.14.13"
+VersionDate = "2019-04-07 06:43:20"
+version = (VersionBind, VersionFitz, "20190407064320")
class Matrix():
@@ -174,7 +175,7 @@ def preTranslate(self, tx, ty):
return self
def preScale(self, sx, sy):
- """Calculate pre scaling and replacing current matrix."""
+ """Calculate pre scaling and replace current matrix."""
self.a *= sx
self.b *= sx
self.c *= sy
@@ -1782,28 +1783,36 @@ def __init__(self, filename=None, stream=None, filetype=None, rect=None, width=0
if not filename or type(filename) is str:
pass
else:
- if fitz_py2: # Python 2
+ if fitz_py2: # Python 2
if type(filename) is unicode:
filename = filename.encode("utf8")
else:
- filename = str(filename) # should take care of pathlib
+ filename = str(filename) # should take care of pathlib
- self.streamlen = len(stream) if stream else 0
+ if stream:
+ if not (filename or filetype):
+ raise ValueError("need filetype for opening a stream")
+
+ if type(stream) is bytes:
+ self.stream = stream
+ elif type(stream) is bytearray:
+ self.stream = bytes(stream)
+ elif type(stream) is io.BytesIO:
+ self.stream = stream.getvalue()
+ else:
+ raise ValueError("'stream' has bad type")
+ stream = self.stream
+ else:
+ self.stream = None
- self.name = ""
- if filename and self.streamlen == 0:
+ if filename and not stream:
self.name = filename
-
- if self.streamlen > 0:
- if not (filename or filetype):
- raise ValueError("filetype missing with stream specified")
- if type(stream) not in (bytes, bytearray):
- raise ValueError("stream must be bytes or bytearray")
+ else:
+ self.name = ""
self.isClosed = False
self.isEncrypted = 0
self.metadata = None
- self.stream = stream # prevent garbage collecting this
self.openErrCode = 0
self.openErrMsg = ''
self.FontInfos = []
@@ -1920,9 +1929,6 @@ def embeddedFileInfo(self, id):
def embeddedFileUpd(self, id, buffer=None, filename=None, ufilename=None, desc=None):
"""Change an embedded file given its entry number or name."""
- if self.isClosed or self.isEncrypted:
- raise ValueError("operation illegal for closed / encrypted doc")
-
return _fitz.Document_embeddedFileUpd(self, id, buffer, filename, ufilename, desc)
@@ -1941,9 +1947,11 @@ def embeddedFileGet(self, id):
def embeddedFileAdd(self, buffer, name, filename=None, ufilename=None, desc=None):
"""Embed a new file."""
+
if self.isClosed or self.isEncrypted:
raise ValueError("operation illegal for closed / encrypted doc")
+
return _fitz.Document_embeddedFileAdd(self, buffer, name, filename, ufilename, desc)
@@ -2126,7 +2134,7 @@ def save(self, filename, garbage=0, clean=0, deflate=0, incremental=0, ascii=0,
if self.pageCount < 1:
raise ValueError("cannot save with zero pages")
if incremental:
- if self.name != filename or self.streamlen > 0:
+ if self.name != filename or self.stream:
raise ValueError("incremental needs original file")
@@ -2461,17 +2469,17 @@ def saveIncr(self):
def __repr__(self):
m = "closed " if self.isClosed else ""
- if self.streamlen == 0:
- if self.name == "":
- return m + "fitz.Document()"
+ if self.stream is None:
+ if self.name is "":
+ return m + "fitz.Document()" % self._graft_id
return m + "fitz.Document('%s')" % (self.name,)
- return m + "fitz.Document('%s', )" % (self.name,)
+ return m + "fitz.Document('%s', )" % (self.name, self._graft_id)
def __getitem__(self, i=0):
if type(i) is not int:
- raise ValueError("invalid page number(s)")
+ raise ValueError("bad page number(s)")
if i >= len(self):
- raise IndexError("invalid page number(s)")
+ raise IndexError("bad page number(s)")
return self.loadPage(i)
def __len__(self):
@@ -2500,11 +2508,12 @@ def __del__(self):
for gmap in self.Graftmaps:
self.Graftmaps[gmap] = None
if hasattr(self, "this") and self.thisown:
- self.thisown = False
self.__swig_destroy__(self)
+ self.thisown = False
+
self.Graftmaps = {}
self.ShownPages = {}
- self.stream = None
+ self.stream = None
self._reset_page_refs = DUMMY
self.__swig_destroy__ = DUMMY
self.isClosed = True
@@ -3045,19 +3054,19 @@ def _setContents(self, xref=0):
def __str__(self):
CheckParent(self)
x = self.parent.name
- if self.parent.streamlen > 0:
- x += " (memory)"
+ if self.parent.stream is not None:
+ x = "" % (self.parent._graft_id,)
if x == "":
- x = ""
+ x = "" % self.parent._graft_id
return "page %s of %s" % (self.number, x)
def __repr__(self):
CheckParent(self)
x = self.parent.name
- if self.parent.streamlen > 0:
- x += " (memory)"
+ if self.parent.stream is not None:
+ x = "" % (self.parent._graft_id,)
if x == "":
- x = ""
+ x = "" % self.parent._graft_id
return "page %s of %s" % (self.number, x)
def _forget_annot(self, annot):
@@ -3877,9 +3886,11 @@ def fileGet(self):
def fileUpd(self, buffer=None, filename=None, ufilename=None, desc=None):
- """Update annotation attached file content."""
+ """Update annotation attached file."""
+
CheckParent(self)
+
return _fitz.Annot_fileUpd(self, buffer, filename, ufilename, desc)
@property
@@ -4382,6 +4393,11 @@ def store_shrink(self, percent):
"""Free 'percent' of current store size."""
return _fitz.Tools_store_shrink(self, percent)
+
+ def image_size(self, imagedata):
+ """Determine dimension and other image data."""
+ return _fitz.Tools_image_size(self, imagedata)
+
@property
def store_size(self):
diff --git a/fitz/fitz_wrap.c b/fitz/fitz_wrap.c
index 3cb184379..eb694720a 100644
--- a/fitz/fitz_wrap.c
+++ b/fitz/fitz_wrap.c
@@ -3073,7 +3073,7 @@ static swig_module_info swig_module = {swig_types, 13, 0, 0, 0, 0};
# endif
// define Python None object
-#define NONE Py_BuildValue("", NULL)
+#define NONE Py_BuildValue("")
#include
#include
@@ -3515,9 +3515,9 @@ void JM_color_FromSequence(PyObject *color, int *n, float col[4])
PyObject *JM_BinFromBuffer(fz_context *ctx, fz_buffer *buffer)
{
PyObject *bytes = PyBytes_FromString("");
- char *c = NULL;
if (buffer)
{
+ char *c = NULL;
size_t len = fz_buffer_storage(gctx, buffer, &c);
bytes = PyBytes_FromStringAndSize(c, (Py_ssize_t) len);
}
@@ -3691,44 +3691,42 @@ void hexlify(int n, unsigned char *in, unsigned char *out)
}
//----------------------------------------------------------------------------
-// Turn a bytes or bytearray object into char* string
-// using the "_AsString" functions. Returns string size or 0 on error.
-//----------------------------------------------------------------------------
-size_t JM_CharFromBytesOrArray(PyObject *stream, char **data)
-{
- *data = NULL;
- size_t len = 0;
- if (!stream) return 0;
- if (PyBytes_Check(stream))
- {
- *data = PyBytes_AsString(stream);
- len = (size_t) PyBytes_Size(stream);
- }
- else if (PyByteArray_Check(stream))
- {
- *data = PyByteArray_AsString(stream);
- len = (size_t) PyByteArray_Size(stream);
- }
- return len;
-}
-
-//----------------------------------------------------------------------------
-// Return fz_buffer from a PyBytes or PyByteArray object
-// Attention: must be freed by caller!
+// Make fz_buffer from a PyBytes, PyByteArray, io.BytesIO object
//----------------------------------------------------------------------------
fz_buffer *JM_BufferFromBytes(fz_context *ctx, PyObject *stream)
{
if (!stream) return NULL;
+ if (stream == NONE) return NULL;
char *c = NULL;
- size_t len = JM_CharFromBytesOrArray(stream, &c);
- if (!c) return NULL;
+ PyObject *mybytes = NULL;
+ size_t len = 0;
fz_buffer *res = NULL;
fz_var(res);
fz_try(ctx)
{
- res = fz_new_buffer(ctx, len);
- fz_append_data(ctx, res, c, len);
- fz_terminate_buffer(ctx, res);
+ if (PyBytes_Check(stream))
+ {
+ c = PyBytes_AsString(stream);
+ len = (size_t) PyBytes_Size(stream);
+ }
+ else if (PyByteArray_Check(stream))
+ {
+ c = PyByteArray_AS_STRING(stream);
+ len = (size_t) PyByteArray_Size(stream);
+ }
+ else if (PyObject_HasAttrString(stream, "getvalue"))
+ { // we assume here that this delivers what we expect
+ mybytes = PyObject_CallMethod(stream, "getvalue", NULL);
+ c = PyBytes_AsString(mybytes);
+ len = (size_t) PyBytes_Size(mybytes);
+ }
+ // all the above leave c as NULL pointer if unsuccessful
+ if (c) res = fz_new_buffer_from_copied_data(ctx, c, len);
+ }
+ fz_always(ctx)
+ {
+ Py_CLEAR(mybytes);
+ PyErr_Clear();
}
fz_catch(ctx)
{
@@ -7233,6 +7231,29 @@ JM_invert_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_irect b)
return 1;
}
+PyObject *JM_image_size(fz_context *ctx, PyObject *imagedata)
+{
+ fz_buffer *res = NULL;
+ fz_image *image = NULL;
+ PyObject *result = NULL;
+ fz_try(ctx)
+ {
+ res = JM_BufferFromBytes(ctx, imagedata);
+ image = fz_new_image_from_buffer(ctx, res);
+ result = Py_BuildValue("iiii", image->w, image->h, (int) image->n, (int) image->bpc);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, res);
+ fz_drop_image(ctx, image);
+ }
+ fz_catch(ctx)
+ {
+ result = NONE;
+ }
+ return result;
+}
+
//----------------------------------------------------------------------------
@@ -9298,6 +9319,7 @@ PyObject *JM_convert_to_pdf(fz_context *ctx, fz_document *doc, int fp, int tp, i
// PDF created - now write it to Python bytearray
PyObject *r = NULL;
fz_output *out = NULL;
+ fz_buffer *res = NULL;
// prepare write options structure
int errors = 0;
pdf_write_options opts = { 0 };
@@ -9316,14 +9338,20 @@ PyObject *JM_convert_to_pdf(fz_context *ctx, fz_document *doc, int fp, int tp, i
opts.errors = &errors;
fz_try(ctx)
{
- r = PyByteArray_FromStringAndSize("", 0);
- out = JM_OutFromBarray(gctx, r);
+ res = fz_new_buffer(ctx, 8192);
+ out = fz_new_output_with_buffer(ctx, res);
pdf_write_document(ctx, pdfout, out, &opts);
+ char *c = NULL;
+ size_t len = fz_buffer_storage(gctx, res, &c);
+ r = PyBytes_FromStringAndSize(c, (Py_ssize_t) len);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_output(ctx, out);
+ fz_drop_buffer(ctx, res);
}
- fz_always(ctx) fz_drop_output(ctx, out);
fz_catch(ctx)
{
- Py_CLEAR(r);
fz_rethrow(ctx);
}
return r;
@@ -9573,8 +9601,9 @@ SWIGINTERN struct fz_document_s *new_fz_document_s(char const *filename,PyObject
gctx->error->errcode = 0; // reset any error code
gctx->error->message[0] = 0; // reset any error message
struct fz_document_s *doc = NULL;
+ char *c = NULL;
+ size_t len = 0;
fz_stream *data = NULL;
- char *streamdata;
float w = width, h = height;
fz_rect r = JM_rect_from_py(rect);
if (!(fz_is_empty_rect(r) && !fz_is_infinite_rect(r)))
@@ -9583,12 +9612,13 @@ SWIGINTERN struct fz_document_s *new_fz_document_s(char const *filename,PyObject
h = r.y1 - r.y0;
}
- size_t streamlen = JM_CharFromBytesOrArray(stream, &streamdata);
fz_try(gctx)
{
- if (streamlen > 0)
+ if (stream != NONE) // stream given, MUST be bytes!
{
- data = fz_open_memory(gctx, streamdata, streamlen);
+ c = PyBytes_AsString(stream); // just a pointer, no new obj
+ len = (size_t) PyBytes_Size(stream);
+ data = fz_open_memory(gctx, c, len);
char *magic = (char *)filename;
if (!magic) magic = (char *)filetype;
doc = fz_open_document_with_stream(gctx, magic, data);
@@ -9857,19 +9887,20 @@ SWIGINTERN PyObject *fz_document_s_embeddedFileUpd(struct fz_document_s *self,Py
pdf_obj *entry = pdf_portfolio_entry_obj(gctx, pdf, n);
pdf_obj *filespec = pdf_dict_getl(gctx, entry, PDF_NAME(EF),
PDF_NAME(F), NULL);
+ if (!filespec) THROWMSG("bad PDF: /EF object not found");
- char *data = NULL;
- size_t len = JM_CharFromBytesOrArray(buffer, &data);
- if (len > 0)
+ res = JM_BufferFromBytes(gctx, buffer);
+ if (buffer && !res) THROWMSG("'buffer' has bad type");
+ if (res)
{
- if (!filespec) THROWMSG("/EF object not found");
- res = fz_new_buffer_from_copied_data(gctx, data, len);
JM_update_stream(gctx, pdf, filespec, res);
// adjust /DL and /Size parameters
- pdf_obj *l = pdf_new_int(gctx, (int64_t) len);
+ int64_t len = (int64_t) fz_buffer_storage(gctx, res, NULL);
+ pdf_obj *l = pdf_new_int(gctx, len);
pdf_dict_put(gctx, filespec, PDF_NAME(DL), l);
pdf_dict_putl(gctx, filespec, l, PDF_NAME(Params), PDF_NAME(Size), NULL);
}
+
if (filename)
pdf_dict_put_text_string(gctx, entry, PDF_NAME(F), filename);
@@ -9921,8 +9952,9 @@ SWIGINTERN PyObject *fz_document_s_embeddedFileAdd(struct fz_document_s *self,Py
fz_try(gctx)
{
assert_PDF(pdf);
- size = JM_CharFromBytesOrArray(buffer, &buffdata);
- if (size < 1) THROWMSG("buffer not bytes / bytearray");
+ data = JM_BufferFromBytes(gctx, buffer);
+ if (!data) THROWMSG("bad type 'buffer'");
+ size = fz_buffer_storage(gctx, data, &buffdata);
// we do not allow duplicate names
entry = JM_find_embedded(gctx, Py_BuildValue("s", name), pdf);
@@ -9955,7 +9987,6 @@ SWIGINTERN PyObject *fz_document_s_embeddedFileAdd(struct fz_document_s *self,Py
// (2) insert the real file contents
pdf_obj *filespec = pdf_dict_getl(gctx, o, PDF_NAME(EF),
PDF_NAME(F), NULL);
- data = fz_new_buffer_from_copied_data(gctx, buffdata, size);
JM_update_stream(gctx, pdf, filespec, data);
// finally update some size attributes
pdf_obj *l = pdf_new_int(gctx, (int64_t) size);
@@ -10327,7 +10358,7 @@ SWIGINTERN PyObject *fz_document_s_insertPDF(struct fz_document_s *self,struct f
}
SWIGINTERN PyObject *fz_document_s__newPage(struct fz_document_s *self,int pno,float width,float height){
pdf_document *pdf = pdf_specifics(gctx, self);
- fz_rect mediabox = { 0, 0, 595, 842 }; // ISO-A4 portrait values
+ fz_rect mediabox = fz_unit_rect;
mediabox.x1 = width;
mediabox.y1 = height;
pdf_obj *resources = NULL, *page_obj = NULL;
@@ -10335,7 +10366,7 @@ SWIGINTERN PyObject *fz_document_s__newPage(struct fz_document_s *self,int pno,f
fz_try(gctx)
{
assert_PDF(pdf);
- if (pno < -1) THROWMSG("invalid page number(s)");
+ if (pno < -1) THROWMSG("bad page number(s)");
// create /Resources and /Contents objects
resources = pdf_add_object_drop(gctx, pdf, pdf_new_dict(gctx, pdf, 1));
page_obj = pdf_add_page(gctx, pdf, mediabox, 0, resources, contents);
@@ -10458,7 +10489,7 @@ SWIGINTERN PyObject *fz_document_s__getPageObjNumber(struct fz_document_s *self,
fz_var(pageref);
fz_try(gctx)
{
- if (n >= pageCount) THROWMSG("invalid page number(s)");
+ if (n >= pageCount) THROWMSG("bad page number(s)");
assert_PDF(pdf);
pageref = pdf_lookup_page_obj(gctx, pdf, n);
}
@@ -10477,7 +10508,7 @@ SWIGINTERN PyObject *fz_document_s__getPageInfo(struct fz_document_s *self,int p
fz_var(liste);
fz_try(gctx)
{
- if (n >= pageCount) THROWMSG("invalid page number(s)");
+ if (n >= pageCount) THROWMSG("bad page number(s)");
assert_PDF(pdf);
pageref = pdf_lookup_page_obj(gctx, pdf, n);
rsrc = pdf_dict_get(gctx, pageref, PDF_NAME(Resources));
@@ -11199,11 +11230,11 @@ SWIGINTERN struct fz_annot_s *fz_page_s_addFileAnnot(struct fz_page_s *self,PyOb
fz_try(gctx)
{
assert_PDF(page);
+ filebuf = JM_BufferFromBytes(gctx, buffer);
+ if (!filebuf) THROWMSG("bad 'buffer' data");
annot = pdf_create_annot(gctx, page, ANNOT_FILEATTACHMENT);
pdf_set_annot_rect(gctx, annot, r);
pdf_set_annot_icon_name(gctx, annot, "PushPin");
- len = JM_CharFromBytesOrArray(buffer, &data);
- filebuf = fz_new_buffer_from_shared_data(gctx, data, len);
pdf_obj *val = JM_embed_file(gctx, page->doc, filebuf,
filename, uf, d);
pdf_dict_put(gctx, annot->obj, PDF_NAME(FS), val);
@@ -11679,11 +11710,9 @@ SWIGINTERN PyObject *fz_page_s__insertImage(struct fz_page_s *self,char const *f
fz_pixmap *pix = NULL;
fz_image *mask = NULL;
fz_separations *seps = NULL;
- pdf_obj *resources, *subres, *ref;
+ pdf_obj *resources, *xobject, *ref;
fz_buffer *res = NULL, *nres = NULL, *imgbuf = NULL;
fz_matrix mat = JM_matrix_from_py(matrix); // pre-calculated
- char *streamdata = NULL;
- size_t streamlen = JM_CharFromBytesOrArray(stream, &streamdata);
const char *template = " q %g %g %g %g %g %g cm /%s Do Q ";
char *cont = NULL;
@@ -11691,33 +11720,23 @@ SWIGINTERN PyObject *fz_page_s__insertImage(struct fz_page_s *self,char const *f
fz_image *zimg = NULL, *image = NULL;
fz_try(gctx)
{
- pdf = page->doc;
-
- // get objects "Resources" & "XObject"
- resources = pdf_dict_get(gctx, page->obj, PDF_NAME(Resources));
- subres = pdf_dict_get(gctx, resources, PDF_NAME(XObject));
- if (!subres) // has no XObject yet, create one
- {
- subres = pdf_new_dict(gctx, pdf, 10);
- pdf_dict_putl_drop(gctx, page->obj, subres, PDF_NAME(Resources), PDF_NAME(XObject), NULL);
- }
-
+ //-------------------------------------------------------------
// create the image
- if (filename || streamlen > 0)
+ //-------------------------------------------------------------
+ if (filename || stream)
{
- if (!streamlen)
+ if (filename)
image = fz_new_image_from_file(gctx, filename);
else
{
- imgbuf = fz_new_buffer_from_copied_data(gctx,
- streamdata, streamlen);
+ imgbuf = JM_BufferFromBytes(gctx, stream);
image = fz_new_image_from_buffer(gctx, imgbuf);
}
- // test alpha channel (would require SMask creation)
+ // test for alpha (which would require making an SMask)
pix = fz_get_pixmap_from_image(gctx, image, NULL, NULL, 0, 0);
if (pix->alpha == 1)
- { // have alpha, therefore create a mask
+ { // have alpha: create an SMask
pm = fz_convert_pixmap(gctx, pix, NULL, NULL, NULL, NULL, 1);
pm->alpha = 0;
pm->colorspace = fz_keep_colorspace(gctx, fz_device_gray(gctx));
@@ -11733,7 +11752,7 @@ SWIGINTERN PyObject *fz_page_s__insertImage(struct fz_page_s *self,char const *f
if (pixmap->alpha == 0)
image = fz_new_image_from_pixmap(gctx, pixmap, NULL);
else
- { // pixmap has alpha, therefore create an SMask
+ { // pixmap has alpha: create an SMask
pm = fz_convert_pixmap(gctx, pixmap, NULL, NULL, NULL, NULL, 1);
pm->alpha = 0;
pm->colorspace = fz_keep_colorspace(gctx, fz_device_gray(gctx));
@@ -11741,11 +11760,25 @@ SWIGINTERN PyObject *fz_page_s__insertImage(struct fz_page_s *self,char const *f
image = fz_new_image_from_pixmap(gctx, pixmap, mask);
}
}
+
+ //-------------------------------------------------------------
// image created - now put it in the PDF
+ //-------------------------------------------------------------
+ pdf = page->doc; // owning PDF
+
+ // get /Resources, /XObject
+ resources = pdf_dict_get(gctx, page->obj, PDF_NAME(Resources));
+ xobject = pdf_dict_get(gctx, resources, PDF_NAME(XObject));
+ if (!xobject) // has no XObject yet, create one
+ {
+ xobject = pdf_new_dict(gctx, pdf, 10);
+ pdf_dict_putl_drop(gctx, page->obj, xobject, PDF_NAME(Resources), PDF_NAME(XObject), NULL);
+ }
+
ref = pdf_add_image(gctx, pdf, image, 0);
- pdf_dict_puts(gctx, subres, _imgname, ref); // store ref-name
+ pdf_dict_puts(gctx, xobject, _imgname, ref); // update XObject
- // prep contents stream buffer with invoking command
+ // make contents stream that invokes the image
nres = fz_new_buffer(gctx, 50);
fz_append_printf(gctx, nres, template,
mat.a, mat.b, mat.c, mat.d, mat.e, mat.f,
@@ -11770,6 +11803,7 @@ SWIGINTERN PyObject *fz_page_s__insertFont(struct fz_page_s *self,char *fontname
pdf_document *pdf;
pdf_obj *resources, *fonts, *font_obj;
fz_font *font;
+ fz_buffer *res = NULL;
char *data = NULL;
int size, ixref = 0, index = 0, simple = 0;
PyObject *value;
@@ -11817,9 +11851,9 @@ SWIGINTERN PyObject *fz_page_s__insertFont(struct fz_page_s *self,char *fontname
font = fz_new_font_from_file(gctx, NULL, fontfile, idx, 0);
else
{
- size = (int) JM_CharFromBytesOrArray(fontbuffer, &data);
- if (!size) THROWMSG("one of fontfile, fontbuffer must be given");
- font = fz_new_font_from_memory(gctx, NULL, data, size, idx, 0);
+ res = JM_BufferFromBytes(gctx, fontbuffer);
+ if (!res) THROWMSG("one of fontfile, fontbuffer must be given");
+ font = fz_new_font_from_buffer(gctx, NULL, res, idx, 0);
}
if (!set_simple)
@@ -11861,6 +11895,10 @@ SWIGINTERN PyObject *fz_page_s__insertFont(struct fz_page_s *self,char *fontname
pdf_drop_obj(gctx, font_obj);
fz_drop_font(gctx, font);
}
+ fz_always(gctx)
+ {
+ fz_drop_buffer(gctx, res);
+ }
fz_catch(gctx) return NULL;
pdf->dirty = 1;
return value;
@@ -12004,51 +12042,64 @@ SWIGINTERN struct fz_pixmap_s *new_fz_pixmap_s__SWIG_3(struct fz_pixmap_s *spix,
return pm;
}
SWIGINTERN struct fz_pixmap_s *new_fz_pixmap_s__SWIG_4(struct fz_colorspace_s *cs,int w,int h,PyObject *samples,int alpha){
- char *data = NULL;
int n = fz_colorspace_n(gctx, cs);
int stride = (n + alpha)*w;
fz_separations *seps = NULL;
+ fz_buffer *res = NULL;
fz_pixmap *pm = NULL;
- size_t size = JM_CharFromBytesOrArray(samples, &data);
fz_try(gctx)
{
- if (size < 1) THROWMSG("invalid arg type samples");
- if (stride * h != size) THROWMSG("invalid arg len samples");
- pm = fz_new_pixmap_with_data(gctx, cs, w, h, seps, alpha, stride, data);
+ size_t size = 0;
+ unsigned char *c = NULL;
+ res = JM_BufferFromBytes(gctx, samples);
+ if (!res) THROWMSG("bad samples data");
+ size = fz_buffer_storage(gctx, res, &c);
+ if (stride * h != size) THROWMSG("bad samples length");
+ pm = fz_new_pixmap(gctx, cs, w, h, seps, alpha);
+ memcpy(pm->samples, c, size);
+ }
+ fz_always(gctx)
+ {
+ fz_drop_buffer(gctx, res);
+ }
+ fz_catch(gctx)
+ {
+ return NULL;
}
- fz_catch(gctx) return NULL;
return pm;
}
SWIGINTERN struct fz_pixmap_s *new_fz_pixmap_s__SWIG_5(char *filename){
fz_image *img = NULL;
fz_pixmap *pm = NULL;
fz_try(gctx) {
- if (!filename) THROWMSG("invalid argument type");
img = fz_new_image_from_file(gctx, filename);
pm = fz_get_pixmap_from_image(gctx, img, NULL, NULL, NULL, NULL);
}
- fz_always(gctx) fz_drop_image(gctx, img);
- fz_catch(gctx) return NULL;
+ fz_always(gctx)
+ {
+ fz_drop_image(gctx, img);
+ }
+ fz_catch(gctx)
+ {
+ return NULL;
+ }
return pm;
}
SWIGINTERN struct fz_pixmap_s *new_fz_pixmap_s__SWIG_6(PyObject *imagedata){
- size_t size = 0;
- char *streamdata;
- fz_buffer *data = NULL;
+ fz_buffer *res = NULL;
fz_image *img = NULL;
fz_pixmap *pm = NULL;
fz_try(gctx)
{
- size = JM_CharFromBytesOrArray(imagedata, &streamdata);
- if (size < 1) THROWMSG("bad image data");
- data = fz_new_buffer_from_shared_data(gctx,
- streamdata, size);
- img = fz_new_image_from_buffer(gctx, data);
+ res = JM_BufferFromBytes(gctx, imagedata);
+ if (!res) THROWMSG("bad image data");
+ img = fz_new_image_from_buffer(gctx, res);
pm = fz_get_pixmap_from_image(gctx, img, NULL, NULL, NULL, NULL);
}
fz_always(gctx)
{
fz_drop_image(gctx, img);
+ fz_drop_buffer(gctx, res);
}
fz_catch(gctx) return NULL;
return pm;
@@ -12125,6 +12176,7 @@ SWIGINTERN PyObject *fz_pixmap_s_copyPixmap(struct fz_pixmap_s *self,struct fz_p
return NONE;
}
SWIGINTERN PyObject *fz_pixmap_s_setAlpha(struct fz_pixmap_s *self,PyObject *alphavalues){
+ fz_buffer *res = NULL;
fz_try(gctx)
{
if (self->alpha == 0) THROWMSG("pixmap has no alpha");
@@ -12136,9 +12188,14 @@ SWIGINTERN PyObject *fz_pixmap_s_setAlpha(struct fz_pixmap_s *self,PyObject *alp
int data_len = 0;
if (alphavalues)
{
- data_len = (int) JM_CharFromBytesOrArray(alphavalues, &data);
- if (data_len && data_len < w * h)
- THROWMSG("not enough alpha values");
+ res = JM_BufferFromBytes(gctx, alphavalues);
+ if (res)
+ {
+ data_len = (int) fz_buffer_storage(gctx, res, &data);
+ if (data && data_len < w * h)
+ THROWMSG("not enough alpha values");
+ }
+ else THROWMSG("bad type 'alphavalues'");
}
int i = 0, k = 0;
while (i < balen)
@@ -12149,7 +12206,14 @@ SWIGINTERN PyObject *fz_pixmap_s_setAlpha(struct fz_pixmap_s *self,PyObject *alp
k += 1;
}
}
- fz_catch(gctx) return NULL;
+ fz_always(gctx)
+ {
+ fz_drop_buffer(gctx, res);
+ }
+ fz_catch(gctx)
+ {
+ return NULL;
+ }
return NONE;
}
SWIGINTERN PyObject *fz_pixmap_s__getImageData(struct fz_pixmap_s *self,int format){
@@ -12464,10 +12528,8 @@ SWIGINTERN PyObject *fz_annot_s__setAP(struct fz_annot_s *self,PyObject *ap,int
if (!apobj) THROWMSG("annot has no /AP/N object");
if (!pdf_is_stream(gctx, apobj))
THROWMSG("/AP/N object is no stream");
- char *c = NULL;
- size_t len = JM_CharFromBytesOrArray(ap, &c);
- if (!c) THROWMSG("invalid /AP stream argument");
- res = fz_new_buffer_from_copied_data(gctx, c, strlen(c));
+ res = JM_BufferFromBytes(gctx, ap);
+ if (!res) THROWMSG("invalid /AP stream argument");
JM_update_stream(gctx, annot->page->doc, apobj, res);
if (rect)
{
@@ -12833,41 +12895,43 @@ SWIGINTERN PyObject *fz_annot_s_fileUpd(struct fz_annot_s *self,PyObject *buffer
fz_try(gctx)
{
assert_PDF(annot); // must be a PDF
- pdf = annot->page->doc; // this is the PDF
+ pdf = annot->page->doc; // the owning PDF
int type = (int) pdf_annot_type(gctx, annot);
if (type != 16)
- THROWMSG("not a file attachment annot");
+ THROWMSG("no FileAttachment annot");
stream = pdf_dict_getl(gctx, annot->obj, PDF_NAME(FS),
PDF_NAME(EF), PDF_NAME(F), NULL);
// the object for file content
- if (!stream) THROWMSG("bad PDF: file entry not found");
+ if (!stream) THROWMSG("bad PDF: /EF object not found");
fs = pdf_dict_get(gctx, annot->obj, PDF_NAME(FS));
- // file content is ignored if not bytes / bytearray
- size = (int64_t) JM_CharFromBytesOrArray(buffer, &data);
- if (size > 0)
+ // file content given
+ res = JM_BufferFromBytes(gctx, buffer);
+ if (buffer && !res) THROWMSG("'buffer' has bad type");
+ if (res)
{
- pdf_obj *s = pdf_new_int(gctx, size);
- pdf_dict_put(gctx, stream, PDF_NAME(Filter),
- PDF_NAME(FlateDecode));
-
- pdf_dict_putl_drop(gctx, stream, s,
- PDF_NAME(Params), PDF_NAME(Size), NULL);
- res = JM_deflatebuf(gctx, data, size);
- pdf_update_stream(gctx, pdf, stream, res, 1);
+ JM_update_stream(gctx, pdf, stream, res);
+ // adjust /DL and /Size parameters
+ int64_t len = (int64_t) fz_buffer_storage(gctx, res, NULL);
+ pdf_obj *l = pdf_new_int(gctx, len);
+ pdf_dict_put(gctx, stream, PDF_NAME(DL), l);
+ pdf_dict_putl(gctx, stream, l, PDF_NAME(Params), PDF_NAME(Size), NULL);
}
if (filename) // new filename given
{
pdf_dict_put_text_string(gctx, stream, PDF_NAME(F), filename);
pdf_dict_put_text_string(gctx, fs, PDF_NAME(F), filename);
+ pdf_dict_put_text_string(gctx, stream, PDF_NAME(UF), filename);
+ pdf_dict_put_text_string(gctx, fs, PDF_NAME(UF), filename);
+ pdf_dict_put_text_string(gctx, annot->obj, PDF_NAME(Contents), filename);
}
if (ufilename)
{
- pdf_dict_put_text_string(gctx, stream, PDF_NAME(UF), filename);
- pdf_dict_put_text_string(gctx, fs, PDF_NAME(UF), filename);
+ pdf_dict_put_text_string(gctx, stream, PDF_NAME(UF), ufilename);
+ pdf_dict_put_text_string(gctx, fs, PDF_NAME(UF), ufilename);
}
if (desc) // new description given
@@ -13495,6 +13559,9 @@ SWIGINTERN PyObject *Tools_store_shrink(struct Tools *self,int percent){
if (percent > 0) fz_shrink_store(gctx, 100 - percent);
return Py_BuildValue("i", (int) gctx->store->size);
}
+SWIGINTERN PyObject *Tools_image_size(struct Tools *self,PyObject *imagedata){
+ return JM_image_size(gctx, imagedata);
+ }
SWIGINTERN PyObject *Tools_store_size(struct Tools *self){
return Py_BuildValue("i", (int) gctx->store->size);
}
@@ -21290,6 +21357,31 @@ SWIGINTERN PyObject *_wrap_Tools_store_shrink(PyObject *SWIGUNUSEDPARM(self), Py
}
+SWIGINTERN PyObject *_wrap_Tools_image_size(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ struct Tools *arg1 = (struct Tools *) 0 ;
+ PyObject *arg2 = (PyObject *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject *result = 0 ;
+
+ if (!PyArg_ParseTuple(args,(char *)"OO:Tools_image_size",&obj0,&obj1)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Tools, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Tools_image_size" "', argument " "1"" of type '" "struct Tools *""'");
+ }
+ arg1 = (struct Tools *)(argp1);
+ arg2 = obj1;
+ result = (PyObject *)Tools_image_size(arg1,arg2);
+ resultobj = result;
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
SWIGINTERN PyObject *_wrap_Tools_store_size(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
struct Tools *arg1 = (struct Tools *) 0 ;
@@ -22027,7 +22119,7 @@ static PyMethodDef SwigMethods[] = {
{ (char *)"Annot_widget_choices", _wrap_Annot_widget_choices, METH_VARARGS, (char *)"Annot_widget_choices(self) -> PyObject *"},
{ (char *)"Annot_fileInfo", _wrap_Annot_fileInfo, METH_VARARGS, (char *)"Retrieve attached file information."},
{ (char *)"Annot_fileGet", _wrap_Annot_fileGet, METH_VARARGS, (char *)"Retrieve annotation attached file content."},
- { (char *)"Annot_fileUpd", _wrap_Annot_fileUpd, METH_VARARGS, (char *)"Update annotation attached file content."},
+ { (char *)"Annot_fileUpd", _wrap_Annot_fileUpd, METH_VARARGS, (char *)"Update annotation attached file."},
{ (char *)"Annot_info", _wrap_Annot_info, METH_VARARGS, (char *)"Annot_info(self) -> PyObject *"},
{ (char *)"Annot_setInfo", _wrap_Annot_setInfo, METH_VARARGS, (char *)"Annot_setInfo(self, info) -> PyObject *"},
{ (char *)"Annot_border", _wrap_Annot_border, METH_VARARGS, (char *)"Annot_border(self) -> PyObject *"},
@@ -22069,6 +22161,7 @@ static PyMethodDef SwigMethods[] = {
{ (char *)"Graftmap_swigregister", Graftmap_swigregister, METH_VARARGS, NULL},
{ (char *)"Tools_gen_id", _wrap_Tools_gen_id, METH_VARARGS, (char *)"Return a unique positive integer."},
{ (char *)"Tools_store_shrink", _wrap_Tools_store_shrink, METH_VARARGS, (char *)"Free 'percent' of current store size."},
+ { (char *)"Tools_image_size", _wrap_Tools_image_size, METH_VARARGS, (char *)"Determine dimension and other image data."},
{ (char *)"Tools_store_size", _wrap_Tools_store_size, METH_VARARGS, (char *)"Current store size."},
{ (char *)"Tools_store_maxsize", _wrap_Tools_store_maxsize, METH_VARARGS, (char *)"Maximum store size."},
{ (char *)"Tools_fitz_config", _wrap_Tools_fitz_config, METH_VARARGS, (char *)"Show configuration data."},
diff --git a/fitz/helper-convert.i b/fitz/helper-convert.i
index 07efd5d6c..ba0982ed9 100644
--- a/fitz/helper-convert.i
+++ b/fitz/helper-convert.i
@@ -52,6 +52,7 @@ PyObject *JM_convert_to_pdf(fz_context *ctx, fz_document *doc, int fp, int tp, i
// PDF created - now write it to Python bytearray
PyObject *r = NULL;
fz_output *out = NULL;
+ fz_buffer *res = NULL;
// prepare write options structure
int errors = 0;
pdf_write_options opts = { 0 };
@@ -70,14 +71,20 @@ PyObject *JM_convert_to_pdf(fz_context *ctx, fz_document *doc, int fp, int tp, i
opts.errors = &errors;
fz_try(ctx)
{
- r = PyByteArray_FromStringAndSize("", 0);
- out = JM_OutFromBarray(gctx, r);
+ res = fz_new_buffer(ctx, 8192);
+ out = fz_new_output_with_buffer(ctx, res);
pdf_write_document(ctx, pdfout, out, &opts);
+ char *c = NULL;
+ size_t len = fz_buffer_storage(gctx, res, &c);
+ r = PyBytes_FromStringAndSize(c, (Py_ssize_t) len);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_output(ctx, out);
+ fz_drop_buffer(ctx, res);
}
- fz_always(ctx) fz_drop_output(ctx, out);
fz_catch(ctx)
{
- Py_CLEAR(r);
fz_rethrow(ctx);
}
return r;
diff --git a/fitz/helper-geo-py.i b/fitz/helper-geo-py.i
index 161e9319e..22578d0db 100644
--- a/fitz/helper-geo-py.i
+++ b/fitz/helper-geo-py.i
@@ -63,7 +63,7 @@ class Matrix():
return self
def preScale(self, sx, sy):
- """Calculate pre scaling and replacing current matrix."""
+ """Calculate pre scaling and replace current matrix."""
self.a *= sx
self.b *= sx
self.c *= sy
diff --git a/fitz/helper-other.i b/fitz/helper-other.i
index 115581e36..e74265485 100644
--- a/fitz/helper-other.i
+++ b/fitz/helper-other.i
@@ -173,9 +173,9 @@ void JM_color_FromSequence(PyObject *color, int *n, float col[4])
PyObject *JM_BinFromBuffer(fz_context *ctx, fz_buffer *buffer)
{
PyObject *bytes = PyBytes_FromString("");
- char *c = NULL;
if (buffer)
{
+ char *c = NULL;
size_t len = fz_buffer_storage(gctx, buffer, &c);
bytes = PyBytes_FromStringAndSize(c, (Py_ssize_t) len);
}
@@ -349,44 +349,42 @@ void hexlify(int n, unsigned char *in, unsigned char *out)
}
//----------------------------------------------------------------------------
-// Turn a bytes or bytearray object into char* string
-// using the "_AsString" functions. Returns string size or 0 on error.
-//----------------------------------------------------------------------------
-size_t JM_CharFromBytesOrArray(PyObject *stream, char **data)
-{
- *data = NULL;
- size_t len = 0;
- if (!stream) return 0;
- if (PyBytes_Check(stream))
- {
- *data = PyBytes_AsString(stream);
- len = (size_t) PyBytes_Size(stream);
- }
- else if (PyByteArray_Check(stream))
- {
- *data = PyByteArray_AsString(stream);
- len = (size_t) PyByteArray_Size(stream);
- }
- return len;
-}
-
-//----------------------------------------------------------------------------
-// Return fz_buffer from a PyBytes or PyByteArray object
-// Attention: must be freed by caller!
+// Make fz_buffer from a PyBytes, PyByteArray, io.BytesIO object
//----------------------------------------------------------------------------
fz_buffer *JM_BufferFromBytes(fz_context *ctx, PyObject *stream)
{
if (!stream) return NULL;
+ if (stream == NONE) return NULL;
char *c = NULL;
- size_t len = JM_CharFromBytesOrArray(stream, &c);
- if (!c) return NULL;
+ PyObject *mybytes = NULL;
+ size_t len = 0;
fz_buffer *res = NULL;
fz_var(res);
fz_try(ctx)
{
- res = fz_new_buffer(ctx, len);
- fz_append_data(ctx, res, c, len);
- fz_terminate_buffer(ctx, res);
+ if (PyBytes_Check(stream))
+ {
+ c = PyBytes_AsString(stream);
+ len = (size_t) PyBytes_Size(stream);
+ }
+ else if (PyByteArray_Check(stream))
+ {
+ c = PyByteArray_AS_STRING(stream);
+ len = (size_t) PyByteArray_Size(stream);
+ }
+ else if (PyObject_HasAttrString(stream, "getvalue"))
+ { // we assume here that this delivers what we expect
+ mybytes = PyObject_CallMethod(stream, "getvalue", NULL);
+ c = PyBytes_AsString(mybytes);
+ len = (size_t) PyBytes_Size(mybytes);
+ }
+ // all the above leave c as NULL pointer if unsuccessful
+ if (c) res = fz_new_buffer_from_copied_data(ctx, c, len);
+ }
+ fz_always(ctx)
+ {
+ Py_CLEAR(mybytes);
+ PyErr_Clear();
}
fz_catch(ctx)
{
diff --git a/fitz/helper-pixmap.i b/fitz/helper-pixmap.i
index 3876cd876..52a8552d9 100644
--- a/fitz/helper-pixmap.i
+++ b/fitz/helper-pixmap.i
@@ -126,4 +126,27 @@ JM_invert_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_irect b)
return 1;
}
+PyObject *JM_image_size(fz_context *ctx, PyObject *imagedata)
+{
+ fz_buffer *res = NULL;
+ fz_image *image = NULL;
+ PyObject *result = NULL;
+ fz_try(ctx)
+ {
+ res = JM_BufferFromBytes(ctx, imagedata);
+ image = fz_new_image_from_buffer(ctx, res);
+ result = Py_BuildValue("iiii", image->w, image->h, (int) image->n, (int) image->bpc);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, res);
+ fz_drop_image(ctx, image);
+ }
+ fz_catch(ctx)
+ {
+ result = NONE;
+ }
+ return result;
+}
+
%}
\ No newline at end of file
diff --git a/fitz/utils.py b/fitz/utils.py
index 05ddf41e0..4e5fb77a6 100644
--- a/fitz/utils.py
+++ b/fitz/utils.py
@@ -1,6 +1,8 @@
from fitz import *
import math
+import os
import warnings
+import io
warnings.simplefilter("once")
"""
The following is a collection of functions to extend PyMupdf.
@@ -80,6 +82,8 @@ def calc_matrix(sr, tr, keep=True, rotate=0):
if reuse_xref > 0:
warnings.warn("ignoring 'reuse_xref'", DeprecationWarning)
+ while pno < 0: # support negative page numbers
+ pno += len(src)
src_page = src[pno] # load ource page
if len(src_page._getContents()) == 0:
raise ValueError("nothing to show - source page empty")
@@ -150,51 +154,112 @@ def insertImage(page, rect, filename=None, pixmap=None, stream=None, rotate=0,
overlay: (bool) put in foreground
"""
+ def calc_matrix(fw, fh, tr, rotate=0):
+ """ Calculate transformation matrix for image insertion.
+
+ Notes:
+ The image will preserve its aspect ratio if and only if arguments
+ fw, fh are both equal to 1.
+ Args:
+ fw, fh: width / height ratio factors of image - floats in (0,1].
+ At least one of them (corresponding to the longer side) is equal to 1.
+ tr: target rect in PDF coordinates
+ rotate: rotation angle in degrees
+ Returns:
+ Transformation matrix.
+ """
+ # center point of target rect
+ tmp = Point((tr.x1 + tr.x0) / 2., (tr.y1 + tr.y0) / 2.)
+
+ rot = Matrix(rotate) # rotation matrix
+
+ # matrix m moves image center to (0, 0), then rotates
+ m = Matrix(1, 0, 0, 1, -0.5, -0.5) * rot
+
+ #sr1 = sr * m # resulting image rect
+
+ # --------------------------------------------------------------------
+ # calculate the scale matrix
+ # --------------------------------------------------------------------
+ small = min(fw, fh) # factor of the smaller side
+
+ if rotate not in (0, 180):
+ fw, fh = fh, fw # width / height exchange their roles
+
+ if fw < 1: # portrait
+ if (float(tr.width) / fw) > (float(tr.height) / fh):
+ w = tr.height * small
+ h = tr.height
+ else:
+ w = tr.width
+ h = tr.width / small
+
+ elif fw != fh: # landscape
+ if (float(tr.width) / fw) > (float(tr.height) / fh):
+ w = tr.height / small
+ h = tr.height
+ else:
+ w = tr.width
+ h = tr.width * small
+
+ else: # (treated as) equal sided
+ w = tr.width
+ h = tr.height
+
+ m *= Matrix(w, h) # concat scale matrix
+
+ m *= Matrix(1, 0, 0, 1, tmp.x, tmp.y) # concat move to target center
+
+ return m
+ # -------------------------------------------------------------------------
+
CheckParent(page)
doc = page.parent
if not doc.isPDF:
raise ValueError("not a PDF")
- if sum([bool(filename), bool(stream), bool(pixmap)]) != 1:
+ if bool(filename) + bool(stream) + bool(pixmap) != 1:
raise ValueError("need exactly one of filename, pixmap, stream")
+ if filename and not os.path.exists(filename):
+ raise FileNotFoundError("No such file: '%s'" % filename)
+ elif stream and type(stream) not in (bytes, bytearray, io.BytesIO):
+ raise ValueError("stream must be bytes-like or BytesIO")
+ elif pixmap and type(pixmap) is not Pixmap:
+ raise ValueError("pixmap must be a Pixmap")
+
while rotate < 0:
rotate += 360
while rotate > 360:
rotate -= 360
- if rotate % 90 != 0:
+ if rotate not in (0, 90, 180, 270):
raise ValueError("bad rotate value")
r = page.rect & rect
if r.isEmpty or r.isInfinite:
raise ValueError("rect must be finite and not empty")
- clip = r * ~page._getTransformation() # rect in PDF coordinates
-
- rot = Matrix(rotate)
-
- fw = r.width
- fh = r.height
- if keep_proportion:
- fw = fh = min(fw, fh)
- my = r.height - max(fw, fh)
- mx = r.width - max(fw, fh)
- else:
- my = mx = 0
-
- if rotate == 0:
- m3 = Matrix(fw, fh)
- m0 = Matrix(1, 0, 0, 1, clip.x0, clip.y0 + my)
- elif rotate == 180:
- m3 = Matrix(fw, fh)
- m0 = Matrix(1, 0, 0, 1, clip.x1 - mx, clip.y1)
- elif rotate == 90:
- m3 = Matrix(fh, fw)
- m0 = Matrix(1, 0, 0, 1, clip.x1 - mx, clip.y0 + my)
+ if keep_proportion is True: # for this we need the image dimension
+ if pixmap: # this is the easy case
+ w = pixmap.width
+ h = pixmap.height
+ elif stream: # use tool to access the information
+ w, h, _, _ = TOOLS.image_size(stream)
+ else: # worst case, we need to read the file ourselves
+ img = open(filename, "rb")
+ stream = img.read() # re-use this as parameter
+ w, h, _, _ = TOOLS.image_size(stream)
+ filename = None # prevent using this again
+ img.close() # close iamge file
+
+ maxf = max(w, h).__float__()
+ fw = w / maxf
+ fh = h / maxf
else:
- m3 = Matrix(fh, fw)
- m0 = Matrix(1, 0, 0, 1, clip.x0, clip.y1)
+ fw = fh = 1.0
+
+ clip = r * ~page._getTransformation() # target rect in PDF coordinates
- matrix = m3 * rot * m0
+ matrix = calc_matrix(fw, fh, clip, rotate=rotate)
ilst = [i[7] for i in doc.getPageImageList(page.number)]
n = "fzImg"
diff --git a/fitz/version.i b/fitz/version.i
index 34d831e87..4384a51f6 100644
--- a/fitz/version.i
+++ b/fitz/version.i
@@ -1,6 +1,6 @@
%pythoncode %{
VersionFitz = "1.14.0"
-VersionBind = "1.14.12"
-VersionDate = "2019-03-21 06:59:25"
-version = (VersionBind, VersionFitz, "20190321065925")
+VersionBind = "1.14.13"
+VersionDate = "2019-04-07 06:43:20"
+version = (VersionBind, VersionFitz, "20190407064320")
%}
\ No newline at end of file
diff --git a/nano_setup.py b/nano_setup.py
index 4b08385cf..ed1887b2a 100644
--- a/nano_setup.py
+++ b/nano_setup.py
@@ -60,7 +60,7 @@
setup(
name="PyMuPDF",
- version="1.14.12",
+ version="1.14.13",
description="Python bindings for the PDF rendering library MuPDF",
classifiers=[
"Development Status :: 5 - Production/Stable",
diff --git a/setup.py b/setup.py
index 29fb3cd54..5de1952e0 100644
--- a/setup.py
+++ b/setup.py
@@ -73,7 +73,7 @@
setup(
name="PyMuPDF",
- version="1.14.12",
+ version="1.14.13",
description="Python bindings for the PDF rendering library MuPDF",
long_description=long_desc,
classifiers=classifier,