From 444b5eb14bc49119a3abedab85f982147a3012b1 Mon Sep 17 00:00:00 2001 From: "Jorj X. McKie" Date: Thu, 4 Feb 2021 17:13:21 -0400 Subject: [PATCH] upload v1.18.8. --- PKG-INFO | 6 ++-- README.md | 6 ++-- docs/changes.rst | 9 ++++++ docs/conf.py | 2 +- docs/version.rst | 2 +- fitz/fitz.i | 74 ++++++++++++++++++++++++++++++++++-------------- fitz/utils.py | 56 ++++++++++++++++++------------------ fitz/version.i | 8 +++--- setup.py | 2 +- 9 files changed, 103 insertions(+), 62 deletions(-) diff --git a/PKG-INFO b/PKG-INFO index 4b5863f16..4ea16c1df 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: PyMuPDF -Version: 1.18.7 +Version: 1.18.8 Author: Jorj McKie Author-email: jorj.x.mckie@outlook.de Maintainer: Jorj McKie @@ -9,7 +9,7 @@ Home-page: https://github.com/pymupdf/PyMuPDF Download-url: https://github.com/pymupdf/PyMuPDF Summary: PyMuPDF is a Python binding for the PDF rendering library MuPDF Description: - Release date: January 31, 2021 + Release date: February 3, 2021 Authors ======= @@ -20,7 +20,7 @@ Description: Introduction ============ - This is **version 1.18.7 of PyMuPDF**, a Python binding for `MuPDF `_ - "a lightweight PDF and XPS viewer". + This is **version 1.18.8 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 8af5a2f57..a84b47655 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# PyMuPDF 1.18.7 +# PyMuPDF 1.18.8 ![logo](https://github.com/pymupdf/PyMuPDF/blob/master/demo/pymupdf.jpg) -Release date: January 31, 2021 +Release date: February 3, 2021 **Travis-CI:** [![Build Status](https://travis-ci.org/JorjMcKie/py-mupdf.svg?branch=master)](https://travis-ci.org/JorjMcKie/py-mupdf) @@ -15,7 +15,7 @@ On **[PyPI](https://pypi.org/project/PyMuPDF)** since August 2016: [![Downloads] # Introduction -This is **version 1.18.7 of PyMuPDF**, a Python binding with support for [MuPDF 1.18.*](http://mupdf.com/) - "a lightweight PDF, XPS, and E-book viewer". +This is **version 1.18.8 of PyMuPDF**, a Python binding with support for [MuPDF 1.18.*](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. diff --git a/docs/changes.rst b/docs/changes.rst index fc58c1f1b..77ba9bcd1 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -1,6 +1,15 @@ Change Logs =============== +Changes in Version 1.18.8 +------------------------- + +This is a bug fix version only. We are publishing early because of the potentially widely used functions. + +* **Fixed** issue `#881 `_. Fixed a memory leak in :meth:`Page.insert_image` when inserting images from files or memory. +* **Fixed** issue `#878 `_. ``pathlib.Path`` objects should now correctly handle file path hierarchies. + + Changes in Version 1.18.7 ------------------------- diff --git a/docs/conf.py b/docs/conf.py index d3ce03fd7..17424e143 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -40,7 +40,7 @@ # built documents. # # The full version, including alpha/beta/rc tags. -release = "1.18.7" +release = "1.18.8" # The short X.Y version version = release diff --git a/docs/version.rst b/docs/version.rst index 9663750e3..da58bd301 100644 --- a/docs/version.rst +++ b/docs/version.rst @@ -1,6 +1,6 @@ Covered Version -------------------- -This documentation covers PyMuPDF v1.18.7 features as of **2021-01-31 00:00:01**. +This documentation covers PyMuPDF v1.18.8 features as of **2021-02-03 19:56:11**. .. note:: The major and minor versions of **PyMuPDF** and **MuPDF** will always be the same. Only the third qualifier (patch level) may deviate from that of MuPDF. \ No newline at end of file diff --git a/fitz/fitz.i b/fitz/fitz.i index a064662b8..ec45bdb45 100644 --- a/fitz/fitz.i +++ b/fitz/fitz.i @@ -295,6 +295,8 @@ struct Document """ if not filename or type(filename) is str: pass + elif hasattr(filename, "absolute"): + filename = str(filename) elif hasattr(filename, "name"): filename = filename.name else: @@ -691,9 +693,10 @@ struct Document _extend_toc_items(PyObject *items) { pdf_document *pdf = pdf_specifics(gctx, (fz_document *)$self); - pdf_obj *bm, *col; + pdf_obj *bm, *col, *obj; int count, flags; - PyObject *item=NULL, *itemdict=NULL, *xrefs, *bold, *italic, *collapse; + PyObject *item=NULL, *itemdict=NULL, *xrefs, *bold, *italic, *collapse, *zoom; + zoom = PyUnicode_FromString("zoom"); bold = PyUnicode_FromString("bold"); italic = PyUnicode_FromString("italic"); collapse = PyUnicode_FromString("collapse"); @@ -704,7 +707,7 @@ struct Document if (!olroot) goto finished; pdf_obj *first = pdf_dict_get(gctx, olroot, PDF_NAME(First)); if (!first) goto finished; - xrefs = PyList_New(0); + xrefs = PyList_New(0); // pre-allocate an empty list xrefs = JM_outline_xrefs(gctx, first, xrefs); Py_ssize_t i, n = PySequence_Size(xrefs); if (!n) goto finished; @@ -743,6 +746,15 @@ struct Document PyTuple_SET_ITEM(color, 2, Py_BuildValue("f", pdf_to_real(gctx, pdf_array_get(gctx, col, 2)))); DICT_SETITEM_DROP(itemdict, dictkey_color, color); } + float z=0; + obj = pdf_dict_get(gctx, bm, PDF_NAME(Dest)); + if (!obj || !pdf_is_array(gctx, obj)) { + obj = pdf_dict_getl(gctx, bm, PDF_NAME(A), PDF_NAME(D), NULL); + } + if (pdf_is_array(gctx, obj) && pdf_array_len(gctx, obj) == 5) { + z = pdf_to_real(gctx, pdf_array_get(gctx, obj, 4)); + } + DICT_SETITEM_DROP(itemdict, zoom, Py_BuildValue("f", z)); PyList_SetItem(item, 3, itemdict); PyList_SetItem(items, i, item); pdf_drop_obj(gctx, bm); @@ -755,6 +767,7 @@ struct Document Py_CLEAR(bold); Py_CLEAR(italic); Py_CLEAR(collapse); + Py_CLEAR(zoom); pdf_drop_obj(gctx, bm); PyErr_Clear(); } @@ -1749,8 +1762,10 @@ struct Document pass elif hasattr(filename, "open"): # assume: pathlib.Path filename = str(filename) - elif not hasattr(filename, "seek"): # assume: file pointer - raise ValueError("filename must be str, Path or file pointer") + elif hasattr(filename, "name"): # assume: file object + filename = filename.name + elif not hasattr(filename, "seek"): # assume file object + raise ValueError("filename must be str, Path or file object") if filename == self.name and not incremental: raise ValueError("save to original must be incremental") if self.page_count < 1: @@ -3876,7 +3891,7 @@ if basestate: raise ValueError("bad page number(s)") # remove TOC bookmarks pointing to deleted page - old_toc = self.getToC() + old_toc = self.get_toc() for i, item in enumerate(old_toc): if item[2] == pno + 1: self.del_toc_item(i) @@ -3904,7 +3919,7 @@ if basestate: if not f <= t < page_count: raise ValueError("bad page number(s)") - old_toc = self.getToC() + old_toc = self.get_toc() for i, item in enumerate(old_toc): if f + 1 <= item[2] <= t + 1: self.del_toc_item(i) @@ -5509,9 +5524,9 @@ def get_oc_items(self) -> list: fz_rect cropbox = fz_empty_rect; fz_rect r = JM_rect_from_py(rect); cropbox.x0 = r.x0; - cropbox.y0 = mediabox.y1 - r.y1; cropbox.x1 = r.x1; - cropbox.y1 = mediabox.y1 - r.y0; + cropbox.y0 = mediabox.y1 - r.y1 + mediabox.y0; + cropbox.y1 = mediabox.y1 - r.y0 + mediabox.y0; pdf_dict_put_drop(gctx, page->obj, PDF_NAME(CropBox), pdf_new_rect(gctx, page->doc, cropbox)); } @@ -5920,7 +5935,7 @@ if not sanitize and not self.is_wrapped: // insert an image //---------------------------------------------------------------- FITZEXCEPTION(_insertImage, !result) - PyObject *_insertImage(const char *filename=NULL, struct Pixmap *pixmap=NULL, PyObject *stream=NULL, PyObject *imask=NULL, int overlay=1, int oc=0, int xref = 0, PyObject *matrix=NULL, + PyObject *_insertImage(const char *filename=NULL, struct Pixmap *pixmap=NULL, PyObject *stream=NULL, PyObject *imask=NULL, int overlay=1, int oc=0, int xref=0, int alpha=0, PyObject *matrix=NULL, const char *_imgname=NULL, PyObject *_imgpointer=NULL) { pdf_page *page = pdf_page_from_fz_page(gctx, (fz_page *) $self); @@ -5944,8 +5959,7 @@ if not sanitize and not self.is_wrapped: //------------------------------------------------------------- // create the image //------------------------------------------------------------- - if (filename || EXISTS(stream) || EXISTS(_imgpointer)) - { + if (filename || EXISTS(stream) || EXISTS(_imgpointer)) { if (filename) { image = fz_new_image_from_file(gctx, filename); } else if (EXISTS(stream)) { @@ -5968,11 +5982,11 @@ if not sanitize and not self.is_wrapped: fz_drop_image(gctx, image); image = zimg; zimg = NULL; - } else { - pix = fz_get_pixmap_from_image(gctx, image, NULL, NULL, 0, 0); - pix->xres = xres; - pix->yres = yres; - if (pix->alpha == 1) { // have alpha: create an SMask + } else if (alpha == 1) { + // have alpha: create an SMask + pix = fz_get_pixmap_from_image(gctx, image, NULL, NULL, 0, 0); + pix->xres = xres; + pix->yres = yres; pm = fz_convert_pixmap(gctx, pix, NULL, NULL, NULL, fz_default_color_params, 1); pm->alpha = 0; pm->colorspace = fz_keep_colorspace(gctx, fz_device_gray(gctx)); @@ -5981,10 +5995,6 @@ if not sanitize and not self.is_wrapped: fz_drop_image(gctx, image); image = zimg; zimg = NULL; - } else { - fz_drop_pixmap(gctx, pix); - pix = NULL; - } } } else { // pixmap specified fz_pixmap *arg_pix = (fz_pixmap *) pixmap; @@ -7212,6 +7222,28 @@ Use pillowWrite to reflect this in output image."""%} return rc; } + //----------------------------------------------------------------- + // check if monochrome + //----------------------------------------------------------------- + %pythoncode %{@property%} + %pythonprepend is_monochrome %{"""Check if pixmap is monochrome."""%} + PyObject *is_monochrome() + { + return JM_BOOL(fz_is_pixmap_monochrome(gctx, (fz_pixmap *) $self)); + } + + //----------------------------------------------------------------- + // MD5 digest of pixmap + //----------------------------------------------------------------- + %pythoncode %{@property%} + %pythonprepend digest %{"""MD5 digest of pixmap (bytes)."""%} + PyObject *digest() + { + unsigned char digest[16]; + fz_md5_pixmap(gctx, (fz_pixmap *) $self, digest); + return Py_BuildValue("y", digest); + } + //----------------------------------------------------------------- // get length of one image row //----------------------------------------------------------------- diff --git a/fitz/utils.py b/fitz/utils.py index e735c9585..97b1e5c6f 100644 --- a/fitz/utils.py +++ b/fitz/utils.py @@ -240,7 +240,7 @@ def insertImage(*args, **kwargs) -> None: overlay = bool(kwargs.get("overlay", True)) def calc_hash(stream): - m = hashlib.sha1() + m = hashlib.md5() m.update(stream) return m.digest() @@ -336,31 +336,30 @@ def calc_matrix(fw, fh, tr, rotate=0): # to the actual C-level function (_imgpointer), and set all other # parameters to None. # ------------------------------------------------------------------------- - 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 - digest = calc_hash(pixmap.samples) - - elif stream: # use tool to access the information - # we also pass through the generated fz_image address - if type(stream) is io.BytesIO: - stream = stream.getvalue() - img_prof = TOOLS.image_profile(stream, keep_image=True) - w, h = img_prof["width"], img_prof["height"] - digest = calc_hash(stream) - stream = None # make sure this arg is NOT used - _imgpointer = img_prof["image"] # pointer to fz_image - - else: # worst case: must read the file - stream = open(filename, "rb").read() - digest = calc_hash(stream) - img_prof = TOOLS.image_profile(stream, keep_image=True) - w, h = img_prof["width"], img_prof["height"] - stream = None # make sure this arg is NOT used - filename = None # make sure this arg is NOT used - _imgpointer = img_prof["image"] # pointer to fz_image + if pixmap: + w = pixmap.width + h = pixmap.height + alpha = pixmap.alpha + digest = calc_hash(pixmap.samples) + elif stream: + pix = Pixmap(stream) + w = pix.width + h = pix.height + alpha = pix.alpha + digest = calc_hash(pix.samples) + del pix + if type(stream) is io.BytesIO: + stream = stream.getvalue() + else: + pix = Pixmap(filename) + w = pix.width + h = pix.height + alpha = pix.alpha + digest = calc_hash(pix.samples) + del pix + + if keep_proportion is True: # for this we need the image dimension maxf = max(w, h) fw = w / maxf fh = h / maxf @@ -393,6 +392,7 @@ def calc_matrix(fw, fh, tr, rotate=0): overlay=overlay, oc=oc, # optional content object xref=xref, + alpha=alpha, _imgname=_imgname, # generated PDF resource name _imgpointer=_imgpointer, # address of fz_image ) @@ -831,7 +831,7 @@ def set_toc_item( title: OptStr = None, to: point_like = None, filename: OptStr = None, - zoom: int = 0, + zoom: float = 0, ) -> None: """Update TOC item by index. @@ -978,8 +978,8 @@ def getDestStr(xref: int, ddict: dict) -> str: """ if not ddict: return "" - str_goto = "/A<>" - str_gotor1 = "/A<>>>" + str_goto = "/A<>" + str_gotor1 = "/A<>>>" str_gotor2 = "/A<>>>" str_launch = "/A<>>>" str_uri = "/A<>" diff --git a/fitz/version.i b/fitz/version.i index f2470ca84..7d10e4d01 100644 --- a/fitz/version.i +++ b/fitz/version.i @@ -1,6 +1,6 @@ %pythoncode %{ VersionFitz = "1.18.0" -VersionBind = "1.18.7" -VersionDate = "2021-01-31 00:00:01" -version = (VersionBind, VersionFitz, "202101310001") -%} +VersionBind = "1.18.8" +VersionDate = "2021-02-03 19:56:11" +version = (VersionBind, VersionFitz, "20210203195611") +%} \ No newline at end of file diff --git a/setup.py b/setup.py index 951e5df51..af6e4619a 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ def load_libraries(): setup( name="PyMuPDF", - version="1.18.7", + version="1.18.8", description="Python bindings for the PDF rendering library MuPDF", long_description=long_desc, classifiers=classifier,