diff --git a/README.md b/README.md index 4e48e48cb..77747e3f8 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# PyMuPDF 1.14.4 +# PyMuPDF 1.14.5 ![logo](https://github.com/rk700/PyMuPDF/blob/master/demo/pymupdf.jpg) -Release date: December 18, 2018 +Release date: January 5, 2018 **Travis-CI:** [![Build Status](https://travis-ci.org/JorjMcKie/py-mupdf.svg?branch=master)](https://travis-ci.org/JorjMcKie/py-mupdf) @@ -14,7 +14,7 @@ On **[PyPI](https://pypi.org/project/PyMuPDF)** since August 2016: [![](https:// # Introduction -This is **version 1.14.4 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". +This is **version 1.14.5 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. diff --git a/demo/piechart1.pdf b/demo/piechart1.pdf new file mode 100644 index 000000000..e1227fcb0 Binary files /dev/null and b/demo/piechart1.pdf differ diff --git a/demo/piechart2.pdf b/demo/piechart2.pdf new file mode 100644 index 000000000..a22ad223f Binary files /dev/null and b/demo/piechart2.pdf differ diff --git a/demo/sierpinski.py b/demo/sierpinski.py index 81ed1e970..397347879 100644 --- a/demo/sierpinski.py +++ b/demo/sierpinski.py @@ -49,7 +49,7 @@ def punch(pm, x00, y00, x03, y03): d = 3 ** 6 # 729 # create a quadratic pixmap with origin (0,0), where width = height = d should # be a power of 3 -t0 = time.clock() +t0 = time.perf_counter() ir = fitz.IRect(0, 0, d, d) pm = fitz.Pixmap(fitz.csGRAY, ir, 0) # fill image area with "black" and then optionally tint and gamma it @@ -58,8 +58,8 @@ def punch(pm, x00, y00, x03, y03): # pm.gammaWith(0.5) # lighten it up # now punch holes into it, down to 1 pixel granularity punch(pm, 0, 0, d, d) -t1 = time.clock() +t1 = time.perf_counter() pm.writePNG(png_name) -t2 = time.clock() +t2 = time.perf_counter() print("%f sec to create fitz img" % (t1 - t0)) print("%f sec to save fitz img" % (t2 - t1)) diff --git a/fitz/__init__.py b/fitz/__init__.py index a569b1288..557a6b5cf 100644 --- a/fitz/__init__.py +++ b/fitz/__init__.py @@ -1,6 +1,9 @@ from __future__ import absolute_import from fitz.fitz import * -from fitz import _fitz +try: + from fitz import _fitz +except: + pass # define the supported colorspaces for convenience fitz.csRGB = fitz.Colorspace(fitz.CS_RGB) @@ -45,6 +48,7 @@ fitz.Page.drawOval = fitz.utils.drawOval fitz.Page.drawPolyline = fitz.utils.drawPolyline fitz.Page.drawRect = fitz.utils.drawRect +fitz.Page.drawQuad = fitz.utils.drawQuad fitz.Page.drawSector = fitz.utils.drawSector fitz.Page.drawSquiggle = fitz.utils.drawSquiggle fitz.Page.drawZigzag = fitz.utils.drawZigzag @@ -61,11 +65,6 @@ fitz.Page.updateLink = fitz.utils.updateLink fitz.Page.newShape = lambda x: fitz.utils.Shape(x) -#------------------------------------------------------------------------------ -# Pixmap -#------------------------------------------------------------------------------ -fitz.Pixmap.writeImage = fitz.utils.writeImage - #------------------------------------------------------------------------------ # Rect #------------------------------------------------------------------------------ diff --git a/fitz/fitz.i b/fitz/fitz.i index 7d2501505..c576745e4 100644 --- a/fitz/fitz.i +++ b/fitz/fitz.i @@ -3867,10 +3867,10 @@ struct fz_pixmap_s } //---------------------------------------------------------------------- - // getPNGData + // Pixmap._getImageData //---------------------------------------------------------------------- - FITZEXCEPTION(getPNGData, !result) - PyObject *getPNGData(int savealpha=-1) + FITZEXCEPTION(_getImageData, !result) + PyObject *_getImageData(int format, int savealpha=-1) { struct fz_buffer_s *res = NULL; fz_output *out = NULL; @@ -3879,7 +3879,27 @@ struct fz_pixmap_s fz_try(gctx) { res = fz_new_buffer(gctx, 1024); out = fz_new_output_with_buffer(gctx, res); - fz_write_pixmap_as_png(gctx, out, $self); + switch(format) + { + case(1): + fz_write_pixmap_as_png(gctx, out, $self); + break; + case(2): + fz_write_pixmap_as_pnm(gctx, out, $self); + break; + case(3): + fz_write_pixmap_as_pam(gctx, out, $self); + break; + case(4): + fz_write_pixmap_as_tga(gctx, out, $self); + break; + case(5): + fz_write_pixmap_as_psd(gctx, out, $self); + break; + default: + fz_write_pixmap_as_png(gctx, out, $self); + break; + } r = JM_BinFromBuffer(gctx, res); } fz_always(gctx) @@ -3891,6 +3911,19 @@ struct fz_pixmap_s return r; } + %pythoncode %{ +def getImageData(self, output="png"): + valid_formats = {"png": 1, "pnm": 2, "pgm": 2, "ppm": 2, "pbm": 2, + "pam": 3, "tga": 4, "psd": 5} + idx = valid_formats.get(output.lower(), 1) + return self._getImageData(idx) + +def getPNGdata(self): + return self._getImageData(1) +def getPNGData(self, savealpha=-1): + return self._getImageData(1) + %} + //---------------------------------------------------------------------- // _writeIMG //---------------------------------------------------------------------- @@ -3913,14 +3946,26 @@ struct fz_pixmap_s case(4): fz_save_pixmap_as_tga(gctx, $self, filename); break; + case(5): + fz_save_pixmap_as_psd(gctx, $self, filename); + break; + default: + fz_save_pixmap_as_png(gctx, $self, filename); + break; } } fz_catch(gctx) return NULL; return NONE; } %pythoncode %{ - def writePNG(self, filename, savealpha = -1): - return self._writeIMG(filename, 1, savealpha) +def writeImage(self, filename, output="png"): + valid_formats = {"png": 1, "pnm": 2, "pgm": 2, "ppm": 2, "pbm": 2, + "pam": 3, "tga": 4, "psd": 5} + idx = valid_formats.get(output.lower(), 1) + return self._writeIMG(filename, idx) + +def writePNG(self, filename, savealpha = -1): + return self._writeIMG(filename, 1, savealpha) %} //---------------------------------------------------------------------- // invertIRect @@ -3952,6 +3997,15 @@ struct fz_pixmap_s width = w height = h + def pixel(self, x, y): + """Return a tuple representing one pixel. Item values are integers in range + 0 to 255. Last item is the alpha value if Pixmap.alpha is true. + """ + if x not in range(self.width) or y not in range(self.height): + raise IndexError("coordinates outside image") + i = self.stride * y + self.n * x + return tuple(self.samples[i: i + self.n]) + def __len__(self): return self.size diff --git a/fitz/fitz.py b/fitz/fitz.py index cb0d3d0c4..5498a3180 100644 --- a/fitz/fitz.py +++ b/fitz/fitz.py @@ -105,9 +105,9 @@ class _object: VersionFitz = "1.14.0" -VersionBind = "1.14.4" -VersionDate = "2018-12-18 08:56:44" -version = (VersionBind, VersionFitz, "20181218085644") +VersionBind = "1.14.5" +VersionDate = "2019-01-04 10:29:25" +version = (VersionBind, VersionFitz, "20190104102925") class Matrix(): @@ -164,12 +164,7 @@ def invert(self, src=None): dst = TOOLS._invert_matrix(src) if dst[0] == 1: return 1 - self.a = dst[1][0] - self.b = dst[1][1] - self.c = dst[1][2] - self.d = dst[1][3] - self.e = dst[1][4] - self.f = dst[1][5] + self.a, self.b, self.c, self.d, self.e, self.f = dst[1] return 0 def preTranslate(self, tx, ty): @@ -239,13 +234,7 @@ def preRotate(self, theta): def concat(self, one, two): """Multiply two matrices and replace current one.""" - dst = TOOLS._concat_matrix(one, two) - self.a = dst[0] - self.b = dst[1] - self.c = dst[2] - self.d = dst[3] - self.e = dst[4] - self.f = dst[5] + self.a, self.b, self.c, self.d, self.e, self.f = TOOLS._concat_matrix(one, two) return self def __getitem__(self, i): @@ -386,7 +375,7 @@ def __init__(self, *args): raise ValueError("illegal Point constructor") def transform(self, m): - """Replace point by its transformation with matrix m.""" + """Replace point by its transformation with matrix-like m.""" x = self.x self.x = x * m[0] + self.y * m[2] + m[4] self.y = x * m[1] + self.y * m[3] + m[5] @@ -622,29 +611,22 @@ def round(self): def includePoint(self, p): """Extend rectangle to include point p.""" - r0 = TOOLS._include_point_in_rect(self, p); - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] + self.x0, self.y0, self.x1, self.y1 = TOOLS._include_point_in_rect(self, p) return self def includeRect(self, r): """Extend rectangle to include rectangle r.""" - r0 = TOOLS._union_rect(self, r) - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] + self.x0, self.y0, self.x1, self.y1 = TOOLS._union_rect(self, r) return self def intersect(self, r): """Restrict self to common area with rectangle r.""" - r0 = TOOLS._intersect_rect(self, r); - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] + self.x0, self.y0, self.x1, self.y1 = TOOLS._intersect_rect(self, r) + return self + + def transform(self, m): + """Replace rectangle with its transformation by matrix m.""" + self.x0, self.y0, self.x1, self.y1 = TOOLS._transform_rect(self, m) return self def __getitem__(self, i): @@ -703,15 +685,6 @@ def __sub__(self, p): raise ValueError("require rect-like object") return Rect(self.x0 - p[0], self.y0 - p[1], self.x1 - p[2], self.y1 - p[3]) - def transform(self, m): - """Replace rectangle with its transformation by matrix m.""" - r0 = TOOLS._transform_rect(self, m) - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] - return self - def __mul__(self, m): if hasattr(m, "__float__"): return Rect(self.x0 * m, self.y0 * m, self.x1 * m, self.y1 * m) @@ -3189,9 +3162,21 @@ def setAlpha(self, alphavalues=None): return _fitz.Pixmap_setAlpha(self, alphavalues) + def _getImageData(self, format, savealpha=-1): + """_getImageData(self, format, savealpha=-1) -> PyObject *""" + return _fitz.Pixmap__getImageData(self, format, savealpha) + + + def getImageData(self, output="png"): + valid_formats = {"png": 1, "pnm": 2, "pgm": 2, "ppm": 2, "pbm": 2, + "pam": 3, "tga": 4, "psd": 5} + idx = valid_formats.get(output.lower(), 1) + return self._getImageData(idx) + + def getPNGdata(self): + return self._getImageData(1) def getPNGData(self, savealpha=-1): - """getPNGData(self, savealpha=-1) -> PyObject *""" - return _fitz.Pixmap_getPNGData(self, savealpha) + return self._getImageData(1) def _writeIMG(self, filename, format, savealpha=-1): @@ -3199,6 +3184,12 @@ def _writeIMG(self, filename, format, savealpha=-1): return _fitz.Pixmap__writeIMG(self, filename, format, savealpha) + def writeImage(self, filename, output="png"): + valid_formats = {"png": 1, "pnm": 2, "pgm": 2, "ppm": 2, "pbm": 2, + "pam": 3, "tga": 4, "psd": 5} + idx = valid_formats.get(output.lower(), 1) + return self._writeIMG(filename, idx) + def writePNG(self, filename, savealpha = -1): return self._writeIMG(filename, 1, savealpha) @@ -3217,6 +3208,15 @@ def samples(self): width = w height = h + def pixel(self, x, y): + """Return a tuple representing one pixel. Item values are integers in range + 0 to 255. Last item is the alpha value if Pixmap.alpha is true. + """ + if x not in range(self.width) or y not in range(self.height): + raise IndexError("coordinates outside image") + i = self.stride * y + self.n * x + return tuple(self.samples[i: i + self.n]) + def __len__(self): return self.size diff --git a/fitz/fitz_wrap.c b/fitz/fitz_wrap.c index 8c2193814..e2c19dc63 100644 --- a/fitz/fitz_wrap.c +++ b/fitz/fitz_wrap.c @@ -3127,7 +3127,7 @@ int JM_is_valid_quad(fz_quad q) */ //----------------------------------------------------------------------------- -// PySequence to quad. Default: quad of (0, 0) points. +// PySequence to quad. Default: quad of four (0, 0) points. // Four floats are treated as coordinates of a rect, and its corners will // define the quad. //----------------------------------------------------------------------------- @@ -12073,7 +12073,7 @@ SWIGINTERN PyObject *fz_pixmap_s_setAlpha(struct fz_pixmap_s *self,PyObject *alp fz_catch(gctx) return NULL; return NONE; } -SWIGINTERN PyObject *fz_pixmap_s_getPNGData(struct fz_pixmap_s *self,int savealpha){ +SWIGINTERN PyObject *fz_pixmap_s__getImageData(struct fz_pixmap_s *self,int format,int savealpha){ struct fz_buffer_s *res = NULL; fz_output *out = NULL; PyObject *r; @@ -12081,7 +12081,27 @@ SWIGINTERN PyObject *fz_pixmap_s_getPNGData(struct fz_pixmap_s *self,int savealp fz_try(gctx) { res = fz_new_buffer(gctx, 1024); out = fz_new_output_with_buffer(gctx, res); - fz_write_pixmap_as_png(gctx, out, self); + switch(format) + { + case(1): + fz_write_pixmap_as_png(gctx, out, self); + break; + case(2): + fz_write_pixmap_as_pnm(gctx, out, self); + break; + case(3): + fz_write_pixmap_as_pam(gctx, out, self); + break; + case(4): + fz_write_pixmap_as_tga(gctx, out, self); + break; + case(5): + fz_write_pixmap_as_psd(gctx, out, self); + break; + default: + fz_write_pixmap_as_png(gctx, out, self); + break; + } r = JM_BinFromBuffer(gctx, res); } fz_always(gctx) @@ -12109,6 +12129,12 @@ SWIGINTERN PyObject *fz_pixmap_s__writeIMG(struct fz_pixmap_s *self,char *filena case(4): fz_save_pixmap_as_tga(gctx, self, filename); break; + case(5): + fz_save_pixmap_as_psd(gctx, self, filename); + break; + default: + fz_save_pixmap_as_png(gctx, self, filename); + break; } } fz_catch(gctx) return NULL; @@ -18573,33 +18599,42 @@ SWIGINTERN PyObject *_wrap_Pixmap_setAlpha(PyObject *SWIGUNUSEDPARM(self), PyObj } -SWIGINTERN PyObject *_wrap_Pixmap_getPNGData(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { +SWIGINTERN PyObject *_wrap_Pixmap__getImageData(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct fz_pixmap_s *arg1 = (struct fz_pixmap_s *) 0 ; - int arg2 = (int) -1 ; + int arg2 ; + int arg3 = (int) -1 ; void *argp1 = 0 ; int res1 = 0 ; int val2 ; int ecode2 = 0 ; + int val3 ; + int ecode3 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; PyObject *result = 0 ; - if (!PyArg_ParseTuple(args,(char *)"O|O:Pixmap_getPNGData",&obj0,&obj1)) SWIG_fail; + if (!PyArg_ParseTuple(args,(char *)"OO|O:Pixmap__getImageData",&obj0,&obj1,&obj2)) SWIG_fail; res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_fz_pixmap_s, 0 | 0 ); if (!SWIG_IsOK(res1)) { - SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Pixmap_getPNGData" "', argument " "1"" of type '" "struct fz_pixmap_s *""'"); + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Pixmap__getImageData" "', argument " "1"" of type '" "struct fz_pixmap_s *""'"); } arg1 = (struct fz_pixmap_s *)(argp1); - if (obj1) { - ecode2 = SWIG_AsVal_int(obj1, &val2); - if (!SWIG_IsOK(ecode2)) { - SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "Pixmap_getPNGData" "', argument " "2"" of type '" "int""'"); + ecode2 = SWIG_AsVal_int(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "Pixmap__getImageData" "', argument " "2"" of type '" "int""'"); + } + arg2 = (int)(val2); + if (obj2) { + ecode3 = SWIG_AsVal_int(obj2, &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "Pixmap__getImageData" "', argument " "3"" of type '" "int""'"); } - arg2 = (int)(val2); + arg3 = (int)(val3); } { - result = (PyObject *)fz_pixmap_s_getPNGData(arg1,arg2); + result = (PyObject *)fz_pixmap_s__getImageData(arg1,arg2,arg3); if(!result) { PyErr_SetString(PyExc_RuntimeError, fz_caught_message(gctx)); @@ -21574,7 +21609,7 @@ static PyMethodDef SwigMethods[] = { { (char *)"Pixmap_irect", _wrap_Pixmap_irect, METH_VARARGS, (char *)"Pixmap_irect(self) -> PyObject *"}, { (char *)"Pixmap_size", _wrap_Pixmap_size, METH_VARARGS, (char *)"Pixmap_size(self) -> int"}, { (char *)"Pixmap_setAlpha", _wrap_Pixmap_setAlpha, METH_VARARGS, (char *)"Pixmap_setAlpha(self, alphavalues=None) -> PyObject *"}, - { (char *)"Pixmap_getPNGData", _wrap_Pixmap_getPNGData, METH_VARARGS, (char *)"Pixmap_getPNGData(self, savealpha=-1) -> PyObject *"}, + { (char *)"Pixmap__getImageData", _wrap_Pixmap__getImageData, METH_VARARGS, (char *)"Pixmap__getImageData(self, format, savealpha=-1) -> PyObject *"}, { (char *)"Pixmap__writeIMG", _wrap_Pixmap__writeIMG, METH_VARARGS, (char *)"Pixmap__writeIMG(self, filename, format, savealpha=-1) -> PyObject *"}, { (char *)"Pixmap_invertIRect", _wrap_Pixmap_invertIRect, METH_VARARGS, (char *)"Pixmap_invertIRect(self, irect=None)"}, { (char *)"Pixmap_samples", _wrap_Pixmap_samples, METH_VARARGS, (char *)"Pixmap_samples(self) -> PyObject *"}, diff --git a/fitz/helper-geo-c.i b/fitz/helper-geo-c.i index 063e16822..f5a974162 100644 --- a/fitz/helper-geo-c.i +++ b/fitz/helper-geo-c.i @@ -32,7 +32,7 @@ int JM_is_valid_quad(fz_quad q) */ //----------------------------------------------------------------------------- -// PySequence to quad. Default: quad of (0, 0) points. +// PySequence to quad. Default: quad of four (0, 0) points. // Four floats are treated as coordinates of a rect, and its corners will // define the quad. //----------------------------------------------------------------------------- diff --git a/fitz/helper-geo-py.i b/fitz/helper-geo-py.i index f080c61e8..cedd3a0ba 100644 --- a/fitz/helper-geo-py.i +++ b/fitz/helper-geo-py.i @@ -53,12 +53,7 @@ class Matrix(): dst = TOOLS._invert_matrix(src) if dst[0] == 1: return 1 - self.a = dst[1][0] - self.b = dst[1][1] - self.c = dst[1][2] - self.d = dst[1][3] - self.e = dst[1][4] - self.f = dst[1][5] + self.a, self.b, self.c, self.d, self.e, self.f = dst[1] return 0 def preTranslate(self, tx, ty): @@ -128,13 +123,7 @@ class Matrix(): def concat(self, one, two): """Multiply two matrices and replace current one.""" - dst = TOOLS._concat_matrix(one, two) - self.a = dst[0] - self.b = dst[1] - self.c = dst[2] - self.d = dst[3] - self.e = dst[4] - self.f = dst[5] + self.a, self.b, self.c, self.d, self.e, self.f = TOOLS._concat_matrix(one, two) return self def __getitem__(self, i): @@ -275,7 +264,7 @@ class Point(): raise ValueError("illegal Point constructor") def transform(self, m): - """Replace point by its transformation with matrix m.""" + """Replace point by its transformation with matrix-like m.""" x = self.x self.x = x * m[0] + self.y * m[2] + m[4] self.y = x * m[1] + self.y * m[3] + m[5] @@ -511,29 +500,22 @@ class Rect(): def includePoint(self, p): """Extend rectangle to include point p.""" - r0 = TOOLS._include_point_in_rect(self, p); - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] + self.x0, self.y0, self.x1, self.y1 = TOOLS._include_point_in_rect(self, p) return self def includeRect(self, r): """Extend rectangle to include rectangle r.""" - r0 = TOOLS._union_rect(self, r) - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] + self.x0, self.y0, self.x1, self.y1 = TOOLS._union_rect(self, r) return self def intersect(self, r): """Restrict self to common area with rectangle r.""" - r0 = TOOLS._intersect_rect(self, r); - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] + self.x0, self.y0, self.x1, self.y1 = TOOLS._intersect_rect(self, r) + return self + + def transform(self, m): + """Replace rectangle with its transformation by matrix m.""" + self.x0, self.y0, self.x1, self.y1 = TOOLS._transform_rect(self, m) return self def __getitem__(self, i): @@ -592,15 +574,6 @@ class Rect(): raise ValueError("require rect-like object") return Rect(self.x0 - p[0], self.y0 - p[1], self.x1 - p[2], self.y1 - p[3]) - def transform(self, m): - """Replace rectangle with its transformation by matrix m.""" - r0 = TOOLS._transform_rect(self, m) - self.x0 = r0[0] - self.y0 = r0[1] - self.x1 = r0[2] - self.y1 = r0[3] - return self - def __mul__(self, m): if hasattr(m, "__float__"): return Rect(self.x0 * m, self.y0 * m, self.x1 * m, self.y1 * m) diff --git a/fitz/utils.py b/fitz/utils.py index 001760634..e7dde0359 100644 --- a/fitz/utils.py +++ b/fitz/utils.py @@ -15,7 +15,7 @@ def showPDFpage(page, rect, src, pno, overlay = True, keep_proportion = True, # list of existing /Form /XObjects xobjlist = doc._getPageInfo(page.number, 3) ilst = [i[1] for i in xobjlist] - # create a new name not in that list + # create a name that is not in this list n = "fzFrm" i = 0 _imgname = n + "0" @@ -318,49 +318,6 @@ def getRectArea(*args): f = (u[unit][0] / u[unit][1])**2 return f * rect.width * rect.height -#def writeImage(filename, output = "png"): -def writeImage(*arg, **kw): - '''Save pixmap to file.\nfilename: image filename\noutput: requested output format (png, pam, pnm or tga).''' - pix = arg[0] - filename = arg[1] - if "output" in kw: - output = kw["output"] - else: - output = "png" - - c_output = 0 - if output == "png": - c_output = 1 - if not filename.lower().endswith(".png"): - raise ValueError("require .png extension") - if pix.colorspace.n > 3: - raise ValueError(pix.colorspace.name + " not supported for png") - elif output == "tga": - c_output = 4 - if not filename.lower().endswith(".tga"): - raise ValueError("require .tga extension") - if pix.colorspace.n > 3: - raise ValueError(pix.colorspace.name + " not supported for tga") - elif output == "pam": - c_output = 3 - if not filename.lower().endswith(".pam"): - raise ValueError("require .pam extension") - elif output == "pnm": - c_output = 2 - if pix.colorspace.n > 3: - raise ValueError(pix.colorspace.name + " not supported for pnm") - if pix.n <= 2: - if not filename.lower().endswith((".pnm", ".pgm")): - raise ValueError("colorspace requires pnm or pgm extensions") - elif not filename.lower().endswith((".pnm", "ppm")): - raise ValueError("colorspace requires pnm or ppm extensions") - else: - raise ValueError("invalid output parameter") - - rc = pix._writeIMG(filename, c_output) - - return rc - #============================================================================== # Document method Set Metadata #============================================================================== @@ -873,12 +830,12 @@ def drawLine(page, p1, p2, color = (0, 0, 0), dashes = None, """Draw a line from point p1 to point p2. """ img = page.newShape() - img.drawLine(Point(p1), Point(p2)) + p = img.drawLine(Point(p1), Point(p2)) img.finish(color = color, dashes = dashes, width = width, closePath = False, roundCap = roundCap, morph = morph) img.commit(overlay) - return Point(p2) + return p #------------------------------------------------------------------------------- # Page.drawSquiggle @@ -888,12 +845,12 @@ def drawSquiggle(page, p1, p2, breadth = 2, color = (0, 0, 0), dashes = None, """Draw a squiggly line from point p1 to point p2. """ img = page.newShape() - img.drawSquiggle(Point(p1), Point(p2), breadth = breadth) + p = img.drawSquiggle(Point(p1), Point(p2), breadth = breadth) img.finish(color = color, dashes = dashes, width = width, closePath = False, roundCap = roundCap, morph = morph) img.commit(overlay) - return point(p2) + return p #------------------------------------------------------------------------------- # Page.drawZigzag @@ -903,12 +860,12 @@ def drawZigzag(page, p1, p2, breadth = 2, color = (0, 0, 0), dashes = None, """Draw a zigzag line from point p1 to point p2. """ img = page.newShape() - img.drawZigzag(Point(p1), Point(p2), breadth = breadth) + p = img.drawZigzag(Point(p1), Point(p2), breadth = breadth) img.finish(color = color, dashes = dashes, width = width, closePath = False, roundCap = roundCap, morph = morph) img.commit(overlay) - return Point(p2) + return p #------------------------------------------------------------------------------- # Page.drawRect @@ -925,6 +882,21 @@ def drawRect(page, rect, color = (0, 0, 0), fill = None, dashes = None, return Q +#------------------------------------------------------------------------------- +# Page.drawQuad +#------------------------------------------------------------------------------- +def drawQuad(page, quad, color = (0, 0, 0), fill = None, dashes = None, + width = 1, roundCap = True, morph = None, overlay = True): + """Draw a quadrilateral. + """ + img = page.newShape() + Q = img.drawQuad(Quad(quad)) + img.finish(color = color, fill = fill, dashes = dashes, width = width, + roundCap = roundCap, morph = morph) + img.commit(overlay) + + return Q + #------------------------------------------------------------------------------- # Page.drawPolyline #------------------------------------------------------------------------------- @@ -962,10 +934,10 @@ def drawCircle(page, center, radius, color = (0, 0, 0), fill = None, def drawOval(page, rect, color = (0, 0, 0), fill = None, dashes = None, morph = None, width = 1, roundCap = True, overlay = True): - """Draw an oval given its containing rectangle. + """Draw an oval given its containing rectangle or quad. """ img = page.newShape() - Q = img.drawOval(Rect(rect)) + Q = img.drawOval(rect) img.finish(color = color, fill = fill, dashes = dashes, width = width, roundCap = roundCap, morph = morph) img.commit(overlay) @@ -1769,8 +1741,8 @@ def __init__(self, page): self.width = page.MediaBoxSize.x self.x = page.CropBoxPosition.x self.y = page.CropBoxPosition.y - self.p_trans = page._getTransformation() # page transformation matr - self.p_invtrans = ~self.p_trans # inverted transf. matrix + self.pctm = page._getTransformation() # page transf. matrix + self.ipctm = ~self.pctm # inverted transf. matrix self.draw_cont = "" self.text_cont = "" self.totalcont = "" @@ -1795,7 +1767,6 @@ def updateRect(self, x): self.rect.y0 = min(self.rect.y0, x.y0) self.rect.x1 = max(self.rect.x1, x.x1) self.rect.y1 = max(self.rect.y1, x.y1) - def drawLine(self, p1, p2): """Draw a line between two points. @@ -1803,12 +1774,11 @@ def drawLine(self, p1, p2): p1 = Point(p1) p2 = Point(p2) if not (self.lastPoint == p1): - self.draw_cont += "%g %g m\n" % (p1.x + self.x, - self.height - p1.y - self.y) + self.draw_cont += "%g %g m\n" % tuple(p1 * self.ipctm) self.lastPoint = p1 - self.draw_cont += "%g %g l\n" % (p2.x + self.x, - self.height - p2.y - self.y) - self.updateRect(p1) + self.updateRect(p1) + + self.draw_cont += "%g %g l\n" % tuple(p2 * self.ipctm) self.updateRect(p2) self.lastPoint = p2 return self.lastPoint @@ -1819,13 +1789,12 @@ def drawPolyline(self, points): for i, p in enumerate(points): if i == 0: if not (self.lastPoint == Point(p)): - self.draw_cont += "%g %g m\n" % (p[0] + self.x, - self.height - p[1] - self.y) + self.draw_cont += "%g %g m\n" % tuple(Point(p) * self.ipctm) self.lastPoint = Point(p) else: - self.draw_cont += "%g %g l\n" % (p[0] + self.x, - self.height - p[1] - self.y) + self.draw_cont += "%g %g l\n" % tuple(Point(p) * self.ipctm) self.updateRect(p) + self.lastPoint = Point(points[-1]) return self.lastPoint @@ -1836,15 +1805,11 @@ def drawBezier(self, p1, p2, p3, p4): p2 = Point(p2) p3 = Point(p3) p4 = Point(p4) - if not (self.lastPoint == Point(p1)): - self.draw_cont += "%g %g m\n" % (p1[0] + self.x, - self.height - p1[1] - self.y) - self.draw_cont += "%g %g %g %g %g %g c\n" % (p2[0] + self.x, - self.height - p2[1] - self.y, - p3[0] + self.x, - self.height - p3[1] - self.y, - p4[0] + self.x, - self.height - p4[1] - self.y) + if not (self.lastPoint == p1): + self.draw_cont += "%g %g m\n" % tuple(p1 * self.ipctm) + self.draw_cont += "%g %g %g %g %g %g c\n" % tuple(list(p2 * self.ipctm) + \ + list(p3 * self.ipctm) + \ + list(p4 * self.ipctm)) self.updateRect(p1) self.updateRect(p2) self.updateRect(p3) @@ -1852,24 +1817,28 @@ def drawBezier(self, p1, p2, p3, p4): self.lastPoint = p4 return self.lastPoint - def drawOval(self, rect): - """Draw an ellipse inside a rectangle. + def drawOval(self, tetra): + """Draw an ellipse inside a tetrapod. """ - rect = Rect(rect) - if rect.isEmpty or rect.isInfinite: - raise ValueError("rectangle must be finite and not empty") - mt = rect.tl + (rect.tr - rect.tl)*0.5 - mr = rect.tr + (rect.br - rect.tr)*0.5 - mb = rect.bl + (rect.br - rect.bl)*0.5 - ml = rect.tl + (rect.bl - rect.tl)*0.5 + if len(tetra) != 4: + raise ValueError("invalid arg length") + if hasattr(tetra[0], "__float__"): + q = Rect(tetra).quad + else: + q = Quad(tetra) + + mt = q.ul + (q.ur - q.ul) * 0.5 + mr = q.ur + (q.lr - q.ur) * 0.5 + mb = q.ll + (q.lr - q.ll) * 0.5 + ml = q.ul + (q.ll - q.ul) * 0.5 if not (self.lastPoint == ml): - self.draw_cont += "%g %g m\n" % (ml.x + self.x, self.height - ml.y - self.y) + self.draw_cont += "%g %g m\n" % tuple(ml * self.ipctm) self.lastPoint = ml - self.drawCurve(ml, rect.bl, mb) - self.drawCurve(mb, rect.br, mr) - self.drawCurve(mr, rect.tr, mt) - self.drawCurve(mt, rect.tl, ml) - self.updateRect(rect) + self.drawCurve(ml, q.ll, mb) + self.drawCurve(mb, q.lr, mr) + self.drawCurve(mr, q.ur, mt) + self.drawCurve(mt, q.ul, ml) + self.updateRect(q.rect) self.lastPoint = ml return self.lastPoint @@ -1908,14 +1877,17 @@ def drawSector(self, center, point, beta, fullSector = True): while abs(betar) > 2 * math.pi: betar += w360 # bring angle below 360 degrees if not (self.lastPoint == point): - self.draw_cont += l3 % (point.x + self.x, h - point.y - self.y) + self.draw_cont += l3 % tuple(point * self.ipctm) self.lastPoint = point Q = Point(0, 0) # just make sure it exists C = center P = point S = P - C # vector 'center' -> 'point' rad = abs(S) # circle radius - assert rad > 1e-5, "radius must be positive" + + if not rad > 1e-5: + raise ValueError("radius must be positive") + alfa = self.horizontal_angle(center, point) while abs(betar) > abs(w90): # draw 90 degree arcs q1 = C.x + math.cos(alfa + w90) * rad @@ -1928,9 +1900,10 @@ def drawSector(self, center, point, beta, fullSector = True): kappa = kappah * abs(P - Q) cp1 = P + (R - P) * kappa # control point 1 cp2 = Q + (R - Q) * kappa # control point 2 - self.draw_cont += l4 % (cp1.x + self.x, h - cp1.y - self.y, - cp2.x + self.x, h - cp2.y - self.y, - Q.x + self.x, h - Q.y - self.y) # draw + self.draw_cont += l4 % tuple(list(cp1 * self.ipctm) + \ + list(cp2 * self.ipctm) + \ + list(Q * self.ipctm)) + betar -= w90 # reduce parm angle by 90 deg alfa += w90 # advance start angle by 90 deg P = Q # advance to arc end point @@ -1948,13 +1921,13 @@ def drawSector(self, center, point, beta, fullSector = True): kappa = kappah * abs(P - Q) / (1 - math.cos(betar)) cp1 = P + (R - P) * kappa # control point 1 cp2 = Q + (R - Q) * kappa # control point 2 - self.draw_cont += l4 % (cp1.x + self.x, h - cp1.y - self.y, - cp2.x + self.x, h - cp2.y - self.y, - Q.x + self.x, h - Q.y -self.y) # draw + self.draw_cont += l4 % tuple(list(cp1 * self.ipctm) + \ + list(cp2 * self.ipctm) + \ + list(Q * self.ipctm)) if fullSector: - self.draw_cont += l3 % (point.x + self.x, h - point.y - self.y) - self.draw_cont += l5 % (center.x + self.x, h - center.y - self.y) - self.draw_cont += l5 % (Q.x + self.x, h - Q.y - self.y) + self.draw_cont += l3 % tuple(point * self.ipctm) + self.draw_cont += l5 % tuple(center * self.ipctm) + self.draw_cont += l5 % tuple(Q * self.ipctm) self.lastPoint = Q return self.lastPoint @@ -1962,12 +1935,17 @@ def drawRect(self, rect): """Draw a rectangle. """ r = Rect(rect) - self.draw_cont += "%g %g %g %g re\n" % (r.x0 + self.x, - self.height - r.y1 - self.y, - r.width, r.height) + self.draw_cont += "%g %g %g %g re\n" % tuple(list(r.bl * self.ipctm) + \ + [r.width, r.height]) self.updateRect(r) self.lastPoint = r.tl - return r.tl + return self.lastPoint + + def drawQuad(self, quad): + """Draw a Quad. + """ + q = Quad(quad) + return self.drawPolyline([q.ul, q.ll, q.lr, q.ur, q.ul]) def drawZigzag(self, p1, p2, breadth = 2): """Draw a zig-zagged line from p1 to p2. @@ -1980,9 +1958,8 @@ def drawZigzag(self, p1, p2, breadth = 2): if cnt < 4: raise ValueError("points too close") mb = rad / cnt # revised breadth - alfa = self.horizontal_angle(p1, p2) # angle with x-axis - calfa = math.cos(alfa) # need these ... - salfa = math.sin(alfa) # ... values later + matrix = TOOLS._hor_matrix(p1, p2) # normalize line to x-axis + i_mat = ~matrix # get original position points = [] # stores edges for i in range (1, cnt): if i % 4 == 1: # point "above" connection @@ -1991,12 +1968,7 @@ def drawZigzag(self, p1, p2, breadth = 2): p = Point(i, 1) * mb else: # ignore others continue - r = abs(p) - p /= r # now p = (cos, sin) - # this is the point rotated by alfa - np = Point((p.x + self.x) * calfa - (p.y + self.y) * salfa, - (p.y + self.y) * calfa + (p.x + self.x) * salfa) * r - points.append(p1 + np) + points.append(p * i_mat) self.drawPolyline([p1] + points + [p2]) # add start and end points return p2 @@ -2011,24 +1983,20 @@ def drawSquiggle(self, p1, p2, breadth = 2): if cnt < 4: raise ValueError("points too close") mb = rad / cnt # revised breadth - alfa = self.horizontal_angle(p1, p2) # angle with x-axis + matrix = TOOLS._hor_matrix(p1, p2) # normalize line to x-axis + i_mat = ~matrix # get original position k = 2.4142135623765633 # y of drawCurve helper point - calfa = math.cos(alfa) # need these ... - salfa = math.sin(alfa) # ... values later + points = [] # stores edges for i in range (1, cnt): if i % 4 == 1: # point "above" connection p = Point(i, -k) * mb elif i % 4 == 3: # point "below" connection p = Point(i, k) * mb - else: # else on connection + else: # else on connection line p = Point(i, 0) * mb - r = abs(p) - p /= r # now p = (cos, sin) - # this is the point rotated by alfa - np = Point((p.x + self.x) * calfa - (p.y + self.y) * salfa, - (p.y + self.y) * calfa + (p.x + self.x) * salfa) * r - points.append(p1 + np) + points.append(p * i_mat) + points = [p1] + points + [p2] cnt = len(points) i = 0 diff --git a/fitz/version.i b/fitz/version.i index 956df9d2e..9350f9170 100644 --- a/fitz/version.i +++ b/fitz/version.i @@ -1,6 +1,6 @@ %pythoncode %{ VersionFitz = "1.14.0" -VersionBind = "1.14.4" -VersionDate = "2018-12-18 08:56:44" -version = (VersionBind, VersionFitz, "20181218085644") +VersionBind = "1.14.5" +VersionDate = "2019-01-04 10:29:25" +version = (VersionBind, VersionFitz, "20190104102925") %} \ No newline at end of file diff --git a/setup.py b/setup.py index 17acb9f63..9de937f91 100644 --- a/setup.py +++ b/setup.py @@ -68,7 +68,7 @@ long_desc = "\n".join(long_dtab) setup(name = 'PyMuPDF', - version = "1.14.4", + version = "1.14.5", description = 'Python bindings for the PDF rendering library MuPDF', long_description = long_desc, classifiers = classifier,