Re: Pycairo: Access cairo_surface_set_mime_data through pycairo

Simon Sapin <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
Le 20/12/2012 12:49, Simon Sapin a écrit :
> Please find attached a patch (with tests and docs) for adding the
> .set_mime_data() and .get_mime_data() methods to cairo.Surface.
>
> See also:
> https://github.com/SimonSapin/py2cairo/commit/ebe487cabd40a3eac

Same for pycairo (Python 3). Patch attached, web interface here:
https://github.com/SimonSapin/pycairo/commit/ad27ca278fc68

Cheers,
-- 
Simon Sapin

-- 
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
mime_data_py3.patch (text/x-patch, 8.6 KB)
From ad27ca278fc68f1a00b6e7099ecb32f82b79f4e7 Mon Sep 17 00:00:00 2001
From: Simon Sapin <[email protected]>
Date: Tue, 18 Dec 2012 15:44:16 +0100
Subject: [PATCH] Add Surface.set_mime_data() and .get_mime_data()

---
 doc/reference/surfaces.rst |   44 ++++++++++++++++++++++++
 src/cairomodule.c          |   15 +++++++++
 src/private.h              |    1 +
 src/surface.c              |   80 ++++++++++++++++++++++++++++++++++++++++++++
 test/api_test.py           |   42 +++++++++++++++++++++++
 5 files changed, 182 insertions(+)

diff --git a/doc/reference/surfaces.rst b/doc/reference/surfaces.rst
index ef01c8e..36cedf8 100644
--- a/doc/reference/surfaces.rst
+++ b/doc/reference/surfaces.rst
@@ -169,6 +169,19 @@ class Surface()
       rendering on them, print surfaces to disable hinting of metrics and so
       forth. The result can then be used with :class:`ScaledFont`.
 
+   .. method:: get_mime_data(mime_type)
+
+      :param mime_type: the MIME type of the image data
+      :type mime_type: str
+      :returns: bytes or :obj:`None`
+
+      Return mime data previously attached to surface
+      with :meth:`.set_mime_data` using the specified mime type.
+      If no data has been attached with the given mime type,
+      :obj:`None` is returned.
+
+      .. versionadded:: 1.10.1
+
    .. method:: mark_dirty()
 
       Tells cairo that drawing has been done to *Surface* using means other
@@ -244,6 +257,37 @@ class Surface()
 
       .. versionadded:: 1.2
 
+   .. method:: set_mime_data(mime_type, data)
+
+      :param mime_type: the MIME type of the image data
+      :type mime_type: str
+      :param data: the image data to attach to the surface
+      :type data: buffer
+
+      Attach an image in the format :obj:`mime_type` to *Surface*.
+      To remove the data from a surface,
+      call this function with same mime type and :obj:`None` for data.
+
+      The attached image (or filename) data can later be used
+      by backends which support it
+      (currently: PDF, PS, SVG and Win32 Printing surfaces)
+      to emit this data instead of making a snapshot of the surface.
+      This approach tends to be faster and requires less memory and disk space.
+
+      The recognized MIME types are the following:
+      ``"image/jpeg"``,
+      ``"image/png"``,
+      ``"image/jp2"``,
+      ``"text/x-uri"``.
+
+      See corresponding backend surface docs for details
+      about which MIME types it can handle.
+      Caution: the associated MIME data will be discarded
+      if you draw on the surface afterwards.
+      Use this function with care.
+
+      .. versionadded:: 1.10.1
+
    .. method:: show_page()
 
       Emits and clears the current page for backends that support multiple
diff --git a/src/cairomodule.c b/src/cairomodule.c
index b1f2caa..c3164a7 100644
--- a/src/cairomodule.c
+++ b/src/cairomodule.c
@@ -48,6 +48,10 @@
 /* A module specific exception */
 PyObject *CairoError = NULL;
 
+/* A dict of python strings to PyCapsule objects containing pointers
+ * to CAIRO_MIME_TYPE_* constants. */
+PyObject *Pycairo_mime_type_map = NULL;
+
 int
 Pycairo_Check_Status (cairo_status_t status) {
   if (PyErr_Occurred() != NULL)
@@ -407,6 +411,17 @@ struct cairo_state {
 		     (PyObject *)&PycairoXlibSurface_Type);
 #endif
 
+  if (Pycairo_mime_type_map == NULL) {
+    Pycairo_mime_type_map = PyDict_New();
+    #define ADD_MIME_TYPE(x) \
+    PyDict_SetItemString(Pycairo_mime_type_map, x, \
+                         PyCapsule_New(x, NULL, NULL));
+    ADD_MIME_TYPE(CAIRO_MIME_TYPE_JPEG);
+    ADD_MIME_TYPE(CAIRO_MIME_TYPE_PNG);
+    ADD_MIME_TYPE(CAIRO_MIME_TYPE_JP2);
+    ADD_MIME_TYPE(CAIRO_MIME_TYPE_URI);
+  }
+
   /* constants */
 #if CAIRO_HAS_ATSUI_FONT
   PyModule_AddIntConstant(m, "HAS_ATSUI_FONT", 1);
diff --git a/src/private.h b/src/private.h
index 4a6b6ad..8854b91 100644
--- a/src/private.h
+++ b/src/private.h
@@ -32,6 +32,7 @@
 
 
 extern PyObject *CairoError;
+extern PyObject *Pycairo_mime_type_map;
 
 extern PyTypeObject PycairoContext_Type;
 PyObject *PycairoContext_FromContext (cairo_t *ctx, PyTypeObject *type,
diff --git a/src/surface.c b/src/surface.c
index 204af68..90066ca 100644
--- a/src/surface.c
+++ b/src/surface.c
@@ -160,6 +160,16 @@
   return CAIRO_STATUS_SUCCESS;
 }
 
+/* for use with
+ * cairo_surface_set_mime_data()
+ */
+void
+_decref_func (void *closure) {
+  PyGILState_STATE gstate = PyGILState_Ensure();
+  Py_DECREF((PyObject *)closure);
+  PyGILState_Release(gstate);
+}
+
 static void
 surface_dealloc (PycairoSurface *o) {
   if (o->surface) {
@@ -355,6 +365,74 @@
 }
 #endif  /* CAIRO_HAS_PNG_FUNCTIONS */
 
+const char*
+_get_mime_type(PyObject *obj) {
+  PyObject *capsule;
+  const char *result;
+
+  capsule = PyObject_GetItem(Pycairo_mime_type_map, obj);
+  if (capsule == NULL) {
+    return NULL;
+  }
+
+  result = PyCapsule_GetPointer(capsule, NULL);
+  Py_DECREF(capsule);
+  return result;
+}
+
+static PyObject *
+surface_set_mime_data (PycairoSurface *o, PyObject *args) {
+  PyObject *obj, *mime_type;
+  const char *mime_type_c;
+  const unsigned char *buffer;
+  unsigned long buffer_len;
+  cairo_status_t status;
+
+  if (!PyArg_ParseTuple(args, "OO:ImageSurface.set_mime_data",
+                        &mime_type, &obj))
+    return NULL;
+
+  mime_type_c = _get_mime_type(mime_type);
+  if (mime_type_c == NULL)
+    return NULL;
+
+  if (obj == Py_None) {
+    buffer = NULL;
+    buffer_len = 0;
+  } else {
+    if (!PyArg_ParseTuple(args, "Oy#:ImageSurface.set_mime_data",
+                          &mime_type, &buffer, &buffer_len))
+      return NULL;
+  }
+
+  status = cairo_surface_set_mime_data (
+    o->surface, mime_type_c, buffer, buffer_len, _decref_func, obj);
+  RETURN_NULL_IF_CAIRO_ERROR(status);
+  if (obj != Py_None) {
+    Py_INCREF(obj);
+  }
+  Py_RETURN_NONE;
+}
+
+static PyObject *
+surface_get_mime_data (PycairoSurface *o, PyObject *args) {
+  PyObject *mime_type;
+  const char *mime_type_c;
+  const unsigned char *buffer;
+  unsigned long buffer_len;
+
+  if (!PyArg_ParseTuple(args, "O:ImageSurface.get_mime_data", &mime_type))
+    return NULL;
+
+  mime_type_c = _get_mime_type(mime_type);
+  if (mime_type_c == NULL) {
+    PyErr_Clear();
+    Py_RETURN_NONE;
+  }
+
+  cairo_surface_get_mime_data (o->surface, mime_type_c, &buffer, &buffer_len);
+  return Py_BuildValue("y#", buffer, buffer_len);
+}
 
 static PyMethodDef surface_methods[] = {
   /* methods never exposed in a language binding:
@@ -385,6 +463,8 @@
 #ifdef CAIRO_HAS_PNG_FUNCTIONS
   {"write_to_png",   (PyCFunction)surface_write_to_png,       METH_VARARGS},
 #endif
+  {"set_mime_data",  (PyCFunction)surface_set_mime_data,      METH_VARARGS},
+  {"get_mime_data",  (PyCFunction)surface_get_mime_data,      METH_VARARGS},
   {NULL, NULL, 0, NULL},
 };
 
diff --git a/test/api_test.py b/test/api_test.py
index 86317b2..25c0d25 100644
--- a/test/api_test.py
+++ b/test/api_test.py
@@ -3,6 +3,9 @@
 - is not able to test the quality of images created. We assume cairo itself
   tests for this.
 '''
+import io
+import zlib
+import base64
 import tempfile as tfi
 
 import cairo
@@ -150,3 +153,42 @@ def test_region():
       cairo.RectangleInt(1, 14, 8, 1),
       cairo.RectangleInt(1, 13, 10, 1),
       ])
+
+
+def test_mime_data():
+    # A 1x1 pixel white image:
+    png_bytes = base64.b64decode(
+        b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQV'
+        b'QI12P4DwABAQEAG7buVgAAAABJRU5ErkJggg==')
+    jpeg_bytes = zlib.decompress(base64.b64decode(
+        b'eJz7f+P/AwYBLzdPNwZGRkYGDyBk+H+bwRnEowj8P8TAzcHACDJHkOH/EQYRIBsV'
+        b'cP6/xcDBCBJlrLcHqRBAV8EAVcHIylSPVwGbPQEFjPaK9XDrBAipBSq4CQB9jiS0'))
+
+    def render(image, surface_type):
+        file_like = io.BytesIO()
+        surface = surface_type(file_like, 100, 100)
+        context = cairo.Context(surface)
+        context.set_source_surface(image, 0, 0)
+        context.paint()
+        surface.finish()
+        pdf_bytes = file_like.getvalue()
+        return pdf_bytes
+
+    image = cairo.ImageSurface.create_from_png(io.BytesIO(png_bytes))
+    assert image.get_mime_data('image/jpeg') is None
+
+    pdf_bytes = render(image, cairo.PDFSurface)
+    assert pdf_bytes.startswith(b'%PDF')
+    assert b'/Filter /DCTDecode' not in pdf_bytes
+
+    image.set_mime_data('image/jpeg', jpeg_bytes)
+    jpeg_bytes = jpeg_bytes[:]  # Copy, drop a reference to the old object.
+    assert image.get_mime_data('image/jpeg')[:] == jpeg_bytes
+
+    pdf_bytes = render(image, cairo.PDFSurface)
+    assert pdf_bytes.startswith(b'%PDF')
+    # JPEG-encoded image:
+    assert b'/Filter /DCTDecode' in pdf_bytes
+
+    image.set_mime_data('image/jpeg', None)
+    assert image.get_mime_data('image/jpeg') is None
-- 
1.7.10
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.