-#include <stdio.h> /* fopen, fprintf, fread, printf */
+#include <stdio.h> /* fprintf, printf */
#include <string.h> /* memcpy */
#include <cairo.h>
#define VIEWPORT_WIDTH 512
#define VIEWPORT_HEIGHT 512
-#define SVG_BUFFER_INCREMENT (size_t)4194304
-
/* Convenient typedefs for libsvgtiny */
typedef struct svgtiny_diagram diagram_t;
typedef struct svgtiny_shape shape_t;
GdkPixbufModuleSizeFunc size_func;
gpointer user_data;
- /* The "file" */
+ /* The SVG "file" that we're building in memory. */
char* svg_data;
- size_t svg_data_size;
- size_t svg_data_max;
+
+ /* How far into svg_data are we? This should always point to the
+ next empty byte. If (for example) svg_data_size is 2, then
+ svg_data[0] and svg_data[1] are used, but svg_data[2] is free. */
+ size_t svg_data_size;
} SvgTinyContext;
switch ((int) path->path[j]) {
case svgtiny_PATH_MOVE:
cairo_move_to(cr,
- path->path[j + 1],
- path->path[j + 2]);
+ path->path[j + 1],
+ path->path[j + 2]);
j += 3;
break;
case svgtiny_PATH_CLOSE:
* svgtiny_free the result if it is valid.
*/
static diagram_t* svgtiny_diagram_from_buffer(char* buffer,
- size_t bytecount,
- int width,
- int height,
- GError** error) {
+ size_t bytecount,
+ int width,
+ int height,
+ GError** error) {
diagram_t* diagram;
svgtiny_code code;
diagram = svgtiny_create();
if (!diagram) {
g_set_error_literal(error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_FAILED,
- "svgtiny_create() failed");
+ G_FILE_ERROR,
+ G_FILE_ERROR_NOMEM,
+ "out of memory in svgtiny_create()");
return NULL;
}
code = svgtiny_parse(diagram, buffer, bytecount, "", width, height);
- free(buffer);
if (code != svgtiny_OK) {
switch (code) {
g_set_error_literal(error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- "libdom error in svgtiny_parse()");
+ "invalid XML DOM in svgtiny_parse()");
break;
case svgtiny_NOT_SVG:
g_set_error_literal(error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- "encountered svgtiny_NOT_SVG in svgtiny_parse()");
+ "missing <svg> element in svgtiny_parse()");
break;
case svgtiny_SVG_ERROR:
g_set_error(error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- "SVG error in svgtiny_parse() on line %i: %s",
+ "SVG format error in svgtiny_parse() on line %i: %s",
diagram->error_line,
diagram->error_message);
break;
crs = cairo_status(cr);
if (crs != CAIRO_STATUS_SUCCESS) {
fprintf(stderr,
- "cairo error: %s\n",
- cairo_status_to_string(crs));
+ "cairo error: %s\n",
+ cairo_status_to_string(crs));
cairo_destroy(cr);
return NULL;
}
* returned; if not, @c NULL is returned and @c error is populated.
*/
static GdkPixbuf* gdk_pixbuf_from_svg_buffer(char* buffer,
- size_t bytecount,
- GError** error) {
+ size_t bytecount,
+ GError** error) {
diagram_t* diagram;
cairo_t* cr = 0;
GdkPixbuf* pb;
GError* sub_error = NULL;
diagram = svgtiny_diagram_from_buffer(buffer,
- bytecount,
- VIEWPORT_WIDTH,
- VIEWPORT_HEIGHT,
- &sub_error);
+ bytecount,
+ VIEWPORT_WIDTH,
+ VIEWPORT_HEIGHT,
+ &sub_error);
if (!diagram) {
g_propagate_error(error, sub_error);
return NULL;
if (!cr) {
svgtiny_free(diagram);
g_set_error_literal(error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- "could not create Cairo surface from SVG diagram");
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ "could not create Cairo surface from SVG diagram");
return NULL;
}
- /* We're using the viewport width and height and not the diagram
- * width/height for the image. The diagram can be of a different
- * size and aspect ratio than the viewport, and our main use case is
- * for icons that are generally square and reasonably sized. If the
- * diagram is "small," then we want to scale it up until it fits
- * nicely in the viewport before rendering it. That's as opposed to
- * rendering the image small, and letting GDK scale it up. Of course
- * this reasoning makes the assumption that the viewport is usually
- * larger than the diagram.
- */
+ /* I've gone back and forth on this about five times: we use the
+ * diagram width and height, and not the viewport width and height.
+ * This can ultimately render an image that's larger than the
+ * viewport size, but I think GDK will resize the final pixbuf
+ * anyway. More importantly, rendering small icons at a larger
+ * (viewport) size seems to make the whole thing go ape-shit.
+ * So for now I'm back in the diagram camp.
+ */
pb = gdk_pixbuf_get_from_surface(cairo_get_target(cr),
- 0,
- 0,
- VIEWPORT_WIDTH,
- VIEWPORT_HEIGHT);
+ 0,
+ 0,
+ diagram->width,
+ diagram->height);
if (!pb) {
g_set_error_literal(error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_FAILED,
- "failed to obtain a GdkPixbuf from Cairo surface");
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ "failed to obtain a GdkPixbuf from Cairo surface");
}
return pb;
}
-/**
- * @brief Create a GdkPixbuf from an SVG filestream.
- *
- * This is essentially a wrapper around @gdk_pixbuf_from_svg_buffer
- * that reads a @c FILE pointer into a buffer.
- *
- * @param fp
- * A pointer to a @c FILE containing the SVG document.
- *
- * @param error
- * The address of a @c GError pointer that we use to return errors.
- *
- * @return If successful, a valid pointer to a @c GdkPixbuf is
- * returned; if not, @c NULL is returned and @c error is populated.
- */
-static GdkPixbuf* gdk_pixbuf_from_svg_file_stream(FILE *fp, GError** error) {
- size_t bytecount, bytesread;
- char* buffer;
-
- /* Find the size of the file stream */
- fseek(fp, 0L, SEEK_END);
- bytecount = ftell(fp);
- rewind(fp);
-
- /* YOLO, no error checking */
- buffer = g_malloc(bytecount);
-
- bytesread = fread(buffer, 1, bytecount, fp);
- if (bytesread != bytecount) {
- g_set_error(error,
- G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- "read only %zd of %zd bytes from stream",
- bytesread,
- bytecount);
- return NULL;
- }
-
- return gdk_pixbuf_from_svg_buffer(buffer, bytecount, error);
-}
-
-
static gpointer gdk_pixbuf_begin_load(GdkPixbufModuleSizeFunc size_func,
- GdkPixbufModulePreparedFunc prep_func,
- GdkPixbufModuleUpdatedFunc updated_func,
- gpointer user_data,
- GError **error) {
+ GdkPixbufModulePreparedFunc prep_func,
+ GdkPixbufModuleUpdatedFunc updated_func,
+ gpointer user_data,
+ GError **error) {
SvgTinyContext* context = g_new(SvgTinyContext, 1);
context->updated_func = updated_func;
context->user_data = user_data;
- /* YOLO, no error checking */
- context->svg_data = g_malloc(SVG_BUFFER_INCREMENT);
+ context->svg_data = NULL;
context->svg_data_size = 0;
return context;
}
static gboolean gdk_pixbuf_load_increment(gpointer data,
- const guchar* buf,
- guint size,
- GError** error) {
+ const guchar* buf,
+ guint buf_size,
+ GError** error) {
size_t increment = 0;
SvgTinyContext* context = (SvgTinyContext*)data;
- if (context->svg_data_size + size > context->svg_data_max) {
- if (size > SVG_BUFFER_INCREMENT) {
- increment = size;
- }
- else {
- increment = SVG_BUFFER_INCREMENT;
- }
-
- /* YOLO, no error checking */
- context->svg_data = g_realloc(context->svg_data,
- context->svg_data_max + increment);
-
- context->svg_data_max += increment;
- }
-
- memcpy(context->svg_data + context->svg_data_size, buf, size);
- context->svg_data_size += size;
+ /* YOLO, no error checking */
+ context->svg_data = g_realloc(context->svg_data,
+ context->svg_data_size + buf_size);
+ memcpy(context->svg_data + context->svg_data_size, buf, buf_size);
+ context->svg_data_size += buf_size;
return TRUE;
}
static void emit_updated(SvgTinyContext* context, GdkPixbuf* pixbuf) {
if (context->updated_func != NULL) {
(*context->updated_func)(pixbuf,
- 0,
- 0,
- gdk_pixbuf_get_width(pixbuf),
- gdk_pixbuf_get_height(pixbuf),
- context->user_data);
+ 0,
+ 0,
+ gdk_pixbuf_get_width(pixbuf),
+ gdk_pixbuf_get_height(pixbuf),
+ context->user_data);
}
}
}
-/*
-static void emit_size(SvgTinyContext* context, GdkPixbuf* pixbuf) {
- int w = gdk_pixbuf_get_width(pixbuf);
- int h = gdk_pixbuf_get_height(pixbuf);
- if (context->size_func != NULL) {
- (*context->size_func)(&w, &h, context->user_data);
- }
-}
-*/
-
-
static gboolean gdk_pixbuf_stop_load(gpointer data, GError **error) {
SvgTinyContext* context = (SvgTinyContext*)data;
GdkPixbuf* pixbuf = NULL;
GError* sub_error = NULL;
pixbuf = gdk_pixbuf_from_svg_buffer(context->svg_data,
- context->svg_data_size,
- &sub_error);
+ context->svg_data_size,
+ &sub_error);
if (pixbuf != NULL) {
- /*emit_size(context, pixbuf);*/
emit_prepared(context, pixbuf);
emit_updated(context, pixbuf);
g_object_unref(pixbuf);
g_propagate_error(error, sub_error);
result = FALSE;
}
+ g_free(context->svg_data);
g_free(context);
return result;
module->begin_load = gdk_pixbuf_begin_load;
module->load_increment = gdk_pixbuf_load_increment;
module->stop_load = gdk_pixbuf_stop_load;
- module->load = gdk_pixbuf_from_svg_file_stream;
}
G_MODULE_EXPORT void fill_info(GdkPixbufFormat *info);
svgpath = argv[1];
pngpath = argv[2];
- fp = fopen(svgpath, "rb");
- if (!fp) {
- perror(svgpath);
- return 1;
- }
-
- pb = gdk_pixbuf_from_svg_file_stream(fp, &err);
+ pb = gdk_pixbuf_new_from_file(svgpath, &err);
if (!pb) {
fprintf(stderr,
- "Error %d in gdk_pixbuf_from_svg_file_stream: %s\n",
+ "Error %d in gdk_pixbuf_new_from_file: %s\n",
err->code,
err->message);
g_error_free(err);