Skip to content

Commit

Permalink
Re-fix renderers outliving underlying streams.
Browse files Browse the repository at this point in the history
e.g. `figure(layout="constrained"); imshow([[0, 1]], rasterized=True); savefig("/tmp/test.pdf")`
  • Loading branch information
anntzer committed Nov 7, 2024
1 parent 3e725e4 commit cbb3c1a
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 29 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
next
====

- Fix packaging.
- Fix renderers beind finalized too early.

v0.6 (2024-11-01)
=================

Expand Down
32 changes: 13 additions & 19 deletions ext/_mplcairo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ GraphicsContextRenderer::GraphicsContextRenderer(
{}

cairo_t* GraphicsContextRenderer::cr_from_fileformat_args(
StreamSurfaceType type, std::optional<py::object> file,
StreamSurfaceType type, py::object file,
double width, double height, double dpi)
{
auto surface_create_for_stream =
Expand Down Expand Up @@ -384,22 +384,17 @@ cairo_t* GraphicsContextRenderer::cr_from_fileformat_args(
"cairo was built without {.name} support"_format(type)
.cast<std::string>()};
}
auto const& cb = file
? cairo_write_func_t{
[](void* closure, unsigned char const* data, unsigned int length)
auto const& cb =
[](void* closure, unsigned char const* data, unsigned int length)
-> cairo_status_t {
auto const& write =
py::reinterpret_borrow<py::object>(static_cast<PyObject*>(closure));
auto const& written =
write(py::memoryview::from_memory(data, length)).cast<unsigned int>();
return // NOTE: This does not appear to affect the context status.
written == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
}}
: nullptr;
py::object write =
// TODO: Why does py::none() not work here?
file ? file->attr("write") : py::reinterpret_borrow<py::object>(Py_None);

auto const& write =
py::reinterpret_borrow<py::object>(static_cast<PyObject*>(closure));
auto const& written =
write(py::memoryview::from_memory(data, length)).cast<unsigned int>();
return // NOTE: This does not appear to affect the context status.
written == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
};
auto const& write = file.attr("write");
auto const& surface =
surface_create_for_stream(cb, write.ptr(), width, height);
cairo_surface_set_fallback_resolution(surface, dpi, dpi);
Expand All @@ -416,7 +411,7 @@ cairo_t* GraphicsContextRenderer::cr_from_fileformat_args(
}

GraphicsContextRenderer::GraphicsContextRenderer(
StreamSurfaceType type, std::optional<py::object> file,
StreamSurfaceType type, py::object file,
double width, double height, double dpi) :
GraphicsContextRenderer{
cr_from_fileformat_args(type, file, width, height, dpi), width, height,
Expand Down Expand Up @@ -2137,8 +2132,7 @@ Only intended for debugging purposes.
.def(py::init<double, double, double>())
.def(py::init<
py::object, double, double, double, std::tuple<double, double>>())
.def(py::init<
StreamSurfaceType, std::optional<py::object>, double, double, double>())
.def(py::init<StreamSurfaceType, py::object, double, double, double>())
.def(
py::pickle(
[](GraphicsContextRenderer const& gcr) -> py::tuple {
Expand Down
4 changes: 2 additions & 2 deletions ext/_mplcairo.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ class GraphicsContextRenderer {
double width, double height, double dpi,
std::tuple<double, double> device_scales);
static cairo_t* cr_from_fileformat_args(
StreamSurfaceType type, std::optional<py::object> file,
StreamSurfaceType type, py::object file,
double width, double height, double dpi);
GraphicsContextRenderer(
StreamSurfaceType type, std::optional<py::object> file,
StreamSurfaceType type, py::object file,
double width, double height, double dpi);

static GraphicsContextRenderer make_pattern_gcr(cairo_surface_t* cr);
Expand Down
18 changes: 10 additions & 8 deletions src/mplcairo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import contextlib
from functools import partial, partialmethod
from gzip import GzipFile
from io import BytesIO
import logging
import os
from pathlib import Path
Expand Down Expand Up @@ -75,7 +76,7 @@ def from_pycairo_ctx(cls, ctx, width, height, dpi, orig_scale):

@classmethod
def _for_fmt_output(cls, fmt, stream, width, height, dpi):
if stream is not None and cbook.file_requires_unicode(stream):
if cbook.file_requires_unicode(stream):
if fmt in [_StreamSurfaceType.PS, _StreamSurfaceType.EPS]:
# PS is (typically) ASCII -- Language Reference, section 3.2.
stream = _BytesWritingWrapper(stream, "ascii")
Expand Down Expand Up @@ -286,7 +287,8 @@ def _print_vector(self, renderer_factory,
self.figure.draw(renderer)
except Exception as exc:
draw_raises_done = type(exc).__name__ == "Done"
raise
if not draw_raises_done: # Else, will be re-raised below.
raise
finally:
# _finish() corresponds finalize() in Matplotlib's PDF and SVG
# backends; it is inlined in Matplotlib's PS backend. It must
Expand All @@ -295,14 +297,14 @@ def _print_vector(self, renderer_factory,
# to be done before closing the stream.
renderer._finish()
# ... but sometimes, Matplotlib *wants* a renderer to outlast the
# stream's lifetime, specifically to measure text extents. This
# is done on Matplotlib's side by making Figure.draw throw a Done
# exception. We catch that exception and swap in a no-write renderer
# (stream = None) in that case.
# stream's lifetime, specifically to measure text extents. To do so,
# Matplotlib makes Figure.draw throw a Done exception. We catch that
# exception and swap in a dummy renderer (stream = BytesIO()) in that
# case (no actual writing will be performed).
if draw_raises_done:
renderer = renderer_factory(None, *self.figure.bbox.size, dpi)
renderer = renderer_factory(BytesIO(), *self.figure.bbox.size, dpi)
with _LOCK:
self.figure.draw(renderer)
self.figure.draw(renderer) # Should raise Done().

print_pdf = partialmethod(
_print_vector, GraphicsContextRendererCairo._for_pdf_output)
Expand Down

0 comments on commit cbb3c1a

Please sign in to comment.