Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 83eb3c0

Browse files
committed
Save images to Svg files without writing the image data out as a
temporary file. svn path=/branches/transforms/; revision=4716
1 parent 255eb33 commit 83eb3c0

2 files changed

Lines changed: 113 additions & 84 deletions

File tree

lib/matplotlib/backends/backend_svg.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -245,43 +245,43 @@ def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
245245

246246
h,w = im.get_size_out()
247247

248-
if rcParams['svg.image_inline']:
249-
filename = os.path.join (tempfile.gettempdir(),
250-
tempfile.gettempprefix() + '.png'
251-
)
252-
253-
verbose.report ('Writing temporary image file for inlining: %s' % filename)
254-
# im.write_png() accepts a filename, not file object, would be
255-
# good to avoid using files and write to mem with StringIO
248+
self._svgwriter.write (
249+
'<image x="%s" y="%s" width="%s" height="%s" '
250+
'%s xlink:href="'%(x/trans[0], (self.height-y)/trans[3]-h, w, h, transstr)
251+
)
256252

257-
# JDH: it *would* be good, but I don't know how to do this
258-
# since libpng seems to want a FILE* and StringIO doesn't seem
259-
# to provide one. I suspect there is a way, but I don't know
260-
# it
253+
if rcParams['svg.image_inline']:
254+
class Base64Writer(object):
255+
def __init__(self, write_method):
256+
self._write_method = write_method
257+
self._buffer = ''
258+
def write(self, data):
259+
self._buffer += data
260+
while len(self._buffer) >= 64:
261+
self._write_method(base64.encodestring(buffer[:64]))
262+
self._write_method('\n')
263+
self._buffer = self._buffer[64:]
264+
def flush(self):
265+
self._write_method(base64.encodestring(self._buffer))
266+
self._write_method('\n')
267+
268+
self._svgwriter.write("data:image/png;base64,\n")
269+
base64writer = Base64Writer(self._svgwriter.write)
261270

262271
im.flipud_out()
263-
im.write_png(filename)
272+
im.write_png(base64writer)
264273
im.flipud_out()
265-
266-
imfile = file (filename, 'rb')
267-
image64 = base64.encodestring (imfile.read())
268-
imfile.close()
269-
os.remove(filename)
270-
hrefstr = 'data:image/png;base64,\n' + image64
271-
274+
base64writer.flush()
272275
else:
273276
self._imaged[self.basename] = self._imaged.get(self.basename,0) + 1
274277
filename = '%s.image%d.png'%(self.basename, self._imaged[self.basename])
275278
verbose.report( 'Writing image file for inclusion: %s' % filename)
276279
im.flipud_out()
277280
im.write_png(filename)
278281
im.flipud_out()
279-
hrefstr = filename
282+
self._svgwriter.write(filename)
280283

281-
self._svgwriter.write (
282-
'<image x="%s" y="%s" width="%s" height="%s" '
283-
'xlink:href="%s" %s/>\n'%(x/trans[0], (self.height-y)/trans[3]-h, w, h, hrefstr, transstr)
284-
)
284+
self._svgwriter.write('"/>\n')
285285

286286
def draw_text(self, gc, x, y, s, prop, angle, ismath):
287287
if ismath:

src/_image.cpp

Lines changed: 88 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,25 @@ Image::set_interpolation(const Py::Tuple& args) {
575575

576576
}
577577

578+
static void write_png_data(png_structp png_ptr, png_bytep data, png_size_t length) {
579+
PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr);
580+
PyObject* write_method = PyObject_GetAttrString(py_file_obj, "write");
581+
PyObject* result = NULL;
582+
if (write_method)
583+
result = PyObject_CallFunction(write_method, "s#", data, length);
584+
Py_XDECREF(write_method);
585+
Py_XDECREF(result);
586+
}
578587

588+
static void flush_png_data(png_structp png_ptr) {
589+
PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr);
590+
PyObject* flush_method = PyObject_GetAttrString(py_file_obj, "flush");
591+
PyObject* result = NULL;
592+
if (flush_method)
593+
result = PyObject_CallFunction(flush_method, "");
594+
Py_XDECREF(flush_method);
595+
Py_XDECREF(result);
596+
}
579597

580598
// this code is heavily adapted from the paint license, which is in
581599
// the file paint.license (BSD compatible) included in this
@@ -593,79 +611,90 @@ Image::write_png(const Py::Tuple& args)
593611

594612
args.verify_length(1);
595613

596-
std::pair<agg::int8u*,bool> bufpair = _get_output_buffer();
614+
FILE *fp = NULL;
615+
Py::Object py_fileobj = Py::Object(args[0]);
616+
if (py_fileobj.isString()) {
617+
std::string fileName = Py::String(py_fileobj);
618+
const char *file_name = fileName.c_str();
619+
if ((fp = fopen(file_name, "wb")) == NULL)
620+
throw Py::RuntimeError( Printf("Could not open file %s", file_name).str() );
621+
}
622+
else {
623+
PyObject* write_method = PyObject_GetAttrString(py_fileobj.ptr(), "write");
624+
if (!(write_method && PyCallable_Check(write_method))) {
625+
Py_XDECREF(write_method);
626+
throw Py::TypeError("Object does not appear to be a path or a Python file-like object");
627+
}
628+
Py_XDECREF(write_method);
629+
}
597630

598-
std::string fileName = Py::String(args[0]);
599-
const char *file_name = fileName.c_str();
600-
FILE *fp;
601631
png_structp png_ptr;
602632
png_infop info_ptr;
603-
struct png_color_8_struct sig_bit;
633+
struct png_color_8_struct sig_bit;
604634
png_uint_32 row=0;
605635

606636
//todo: allocate on heap
607-
png_bytep *row_pointers = new png_bytep[rowsOut];
608-
609-
for (row = 0; row < rowsOut; ++row)
610-
row_pointers[row] = bufpair.first + row * colsOut * 4;
611-
612-
fp = fopen(file_name, "wb");
613-
if (fp == NULL) {
614-
if (bufpair.second) delete [] bufpair.first;
615-
delete [] row_pointers;
616-
throw Py::RuntimeError(Printf("Could not open file %s", file_name).str());
617-
}
618-
619-
620-
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
621-
if (png_ptr == NULL) {
622-
if (bufpair.second) delete [] bufpair.first;
623-
fclose(fp);
624-
delete [] row_pointers;
625-
throw Py::RuntimeError("Could not create write struct");
626-
}
627-
628-
info_ptr = png_create_info_struct(png_ptr);
629-
if (info_ptr == NULL) {
630-
if (bufpair.second) delete [] bufpair.first;
631-
fclose(fp);
637+
png_bytep *row_pointers = NULL;
638+
std::pair<agg::int8u*,bool> bufpair;
639+
bufpair.first = NULL;
640+
bufpair.second = false;
641+
642+
try {
643+
row_pointers = new png_bytep[rowsOut];
644+
if (!row_pointers)
645+
throw Py::RuntimeError("Out of memory");
646+
647+
bufpair = _get_output_buffer();
648+
for (row = 0; row < rowsOut; ++row)
649+
row_pointers[row] = bufpair.first + row * colsOut * 4;
650+
651+
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
652+
if (png_ptr == NULL)
653+
throw Py::RuntimeError("Could not create write struct");
654+
655+
info_ptr = png_create_info_struct(png_ptr);
656+
if (info_ptr == NULL)
657+
throw Py::RuntimeError("Could not create info struct");
658+
659+
if (setjmp(png_ptr->jmpbuf))
660+
throw Py::RuntimeError("Error building image");
661+
662+
if (fp) {
663+
png_init_io(png_ptr, fp);
664+
} else {
665+
png_set_write_fn(png_ptr, (void*)py_fileobj.ptr(),
666+
&write_png_data, &flush_png_data);
667+
}
668+
png_set_IHDR(png_ptr, info_ptr,
669+
colsOut, rowsOut, 8,
670+
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
671+
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
672+
673+
// this a a color image!
674+
sig_bit.gray = 0;
675+
sig_bit.red = 8;
676+
sig_bit.green = 8;
677+
sig_bit.blue = 8;
678+
/* if the image has an alpha channel then */
679+
sig_bit.alpha = 8;
680+
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
681+
682+
png_write_info(png_ptr, info_ptr);
683+
png_write_image(png_ptr, row_pointers);
684+
png_write_end(png_ptr, info_ptr);
632685
png_destroy_write_struct(&png_ptr, &info_ptr);
633-
delete [] row_pointers;
634-
throw Py::RuntimeError("Could not create info struct");
635-
}
636-
637-
if (setjmp(png_ptr->jmpbuf)) {
686+
} catch (...) {
638687
if (bufpair.second) delete [] bufpair.first;
639-
fclose(fp);
688+
if (fp) fclose(fp);
640689
png_destroy_write_struct(&png_ptr, &info_ptr);
641690
delete [] row_pointers;
642-
throw Py::RuntimeError("Error building image");
691+
throw;
643692
}
644693

645-
png_init_io(png_ptr, fp);
646-
png_set_IHDR(png_ptr, info_ptr,
647-
colsOut, rowsOut, 8,
648-
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
649-
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
650-
651-
// this a a color image!
652-
sig_bit.gray = 0;
653-
sig_bit.red = 8;
654-
sig_bit.green = 8;
655-
sig_bit.blue = 8;
656-
/* if the image has an alpha channel then */
657-
sig_bit.alpha = 8;
658-
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
659-
660-
png_write_info(png_ptr, info_ptr);
661-
png_write_image(png_ptr, row_pointers);
662-
png_write_end(png_ptr, info_ptr);
663-
png_destroy_write_struct(&png_ptr, &info_ptr);
664-
fclose(fp);
665-
694+
if (fp) fclose(fp);
666695
delete [] row_pointers;
667-
668696
if (bufpair.second) delete [] bufpair.first;
697+
669698
return Py::Object();
670699
}
671700

0 commit comments

Comments
 (0)