]> gitweb.michael.orlitzky.com - libsvgtiny-pixbuf.git/commitdiff
io-svg.c: miscellaneous documentation and error-handling improvements.
authorMichael Orlitzky <michael@orlitzky.com>
Sat, 5 Aug 2023 01:27:31 +0000 (21:27 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Sat, 5 Aug 2023 01:27:31 +0000 (21:27 -0400)
io-svg.c

index 521ed5818944e4fcd4b9b85aa7bb43f1839b5700..750ea254b22bd945a7e2d707d1f2302987308d96 100644 (file)
--- a/io-svg.c
+++ b/io-svg.c
@@ -1,9 +1,9 @@
 #include <stdio.h> /* fopen, fprintf, fread, printf */
-#include <stdlib.h> /* malloc */
+#include <string.h> /* memcpy */
 
 #include <cairo.h>
 #include <gdk/gdk.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf/gdk-pixbuf.h> /* includes glib.h */
 #include <svgtiny.h>
 
 /*
@@ -65,64 +65,67 @@ static void render_path(cairo_t* cr, shape_t* path) {
       break;
     case svgtiny_PATH_LINE:
       cairo_line_to(cr,
-                   path->path[j + 1],
-                   path->path[j + 2]);
+                    path->path[j + 1],
+                    path->path[j + 2]);
       j += 3;
       break;
     case svgtiny_PATH_BEZIER:
       cairo_curve_to(cr,
-                    path->path[j + 1],
-                    path->path[j + 2],
-                    path->path[j + 3],
-                    path->path[j + 4],
-                    path->path[j + 5],
-                    path->path[j + 6]);
+                     path->path[j + 1],
+                     path->path[j + 2],
+                     path->path[j + 3],
+                     path->path[j + 4],
+                     path->path[j + 5],
+                     path->path[j + 6]);
       j += 7;
-      break;
-    default:
-      fprintf(stderr, "error: unmatched case in render_path\n");
-      j += 1;
     }
   }
   if (path->fill != svgtiny_TRANSPARENT) {
     cairo_set_source_rgba(cr,
-                         svgtiny_RED(path->fill) / 255.0,
-                         svgtiny_GREEN(path->fill) / 255.0,
-                         svgtiny_BLUE(path->fill) / 255.0,
-                         1);
+                          svgtiny_RED(path->fill) / 255.0,
+                          svgtiny_GREEN(path->fill) / 255.0,
+                          svgtiny_BLUE(path->fill) / 255.0,
+                          1);
     cairo_fill_preserve(cr);
   }
   if (path->stroke != svgtiny_TRANSPARENT) {
     cairo_set_source_rgba(cr,
-                         svgtiny_RED(path->stroke) / 255.0,
-                         svgtiny_GREEN(path->stroke) / 255.0,
-                         svgtiny_BLUE(path->stroke) / 255.0,
-                         1);
+                          svgtiny_RED(path->stroke) / 255.0,
+                          svgtiny_GREEN(path->stroke) / 255.0,
+                          svgtiny_BLUE(path->stroke) / 255.0,
+                          1);
     cairo_set_line_width(cr, path->stroke_width);
     cairo_stroke_preserve(cr);
   }
 }
 
 /**
- * @brief Parse an SVG file into a diagram_t structure.
+ * @brief Parse a buffer of SVG data into a diagram_t structure.
  *
- * @param fp
- *   A pointer to an open file stream.
+ * @param buffer
+ *   The buffer containing the SVG document.
+ *
+ * @param bytecount
+ *   The number of bytes in @c buffer.
  *
  * @return If successful, a pointer to a @c diagram_t structure is
  *   returned; if not, @c NULL is returned. You are expected to @c
  *   svgtiny_free the result if it is valid.
  */
 static diagram_t* svgtiny_diagram_from_buffer(char* buffer,
-                                            size_t bytecount,
-                                            int width,
-                                            int height) {
+                                             size_t bytecount,
+                                             int width,
+                                             int height,
+                                             GError** error) {
   diagram_t* diagram;
   svgtiny_code code;
 
   diagram = svgtiny_create();
   if (!diagram) {
-    fprintf(stderr, "svgtiny_create() failed\n");
+    g_set_error_literal(error,
+                        GDK_PIXBUF_ERROR,
+                        GDK_PIXBUF_ERROR_FAILED,
+                        "svgtiny_create() failed");
     return NULL;
   }
 
@@ -130,27 +133,34 @@ static diagram_t* svgtiny_diagram_from_buffer(char* buffer,
   free(buffer);
 
   if (code != svgtiny_OK) {
-    fprintf(stderr, "svgtiny_parse failed with ");
     switch (code) {
     case svgtiny_OUT_OF_MEMORY:
-      fprintf(stderr, "svgtiny_OUT_OF_MEMORY");
+      g_set_error_literal(error,
+                          G_FILE_ERROR,
+                          G_FILE_ERROR_NOMEM,
+                          "out of memory in svgtiny_parse()");
       break;
     case svgtiny_LIBDOM_ERROR:
-      fprintf(stderr, "svgtiny_LIBDOM_ERROR");
+      g_set_error_literal(error,
+                          GDK_PIXBUF_ERROR,
+                          GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                          "libdom error in svgtiny_parse()");
       break;
     case svgtiny_NOT_SVG:
-      fprintf(stderr, "svgtiny_NOT_SVG");
+      g_set_error_literal(error,
+                          GDK_PIXBUF_ERROR,
+                          GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                          "encountered svgtiny_NOT_SVG in svgtiny_parse()");
       break;
     case svgtiny_SVG_ERROR:
-      fprintf(stderr, "svgtiny_SVG_ERROR: line %i: %s",
-             diagram->error_line,
-             diagram->error_message);
-      break;
-    default:
-      fprintf(stderr, "unknown svgtiny_code %i", code);
+      g_set_error(error,
+                  GDK_PIXBUF_ERROR,
+                  GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                  "SVG error in svgtiny_parse() on line %i: %s",
+                  diagram->error_line,
+                  diagram->error_message);
       break;
     }
-    fprintf(stderr, "\n");
     return NULL;
   }
 
@@ -174,14 +184,14 @@ static cairo_t* cairo_context_from_diagram(diagram_t* diagram) {
   unsigned int i;
 
   surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
-                                      diagram->width,
-                                      diagram->height);
+                                       diagram->width,
+                                       diagram->height);
 
   crs = cairo_surface_status(surface);
   if (crs != CAIRO_STATUS_SUCCESS) {
     fprintf(stderr,
-           "cairo_image_surface_create failed: %s\n",
-           cairo_status_to_string(crs));
+            "cairo_image_surface_create failed: %s\n",
+            cairo_status_to_string(crs));
     cairo_surface_destroy(surface);
     return NULL;
   }
@@ -195,8 +205,8 @@ static cairo_t* cairo_context_from_diagram(diagram_t* diagram) {
 
   if (crs != CAIRO_STATUS_SUCCESS) {
     fprintf(stderr,
-           "cairo_create failed: %s\n",
-           cairo_status_to_string(crs));
+            "cairo_create failed: %s\n",
+            cairo_status_to_string(crs));
     cairo_destroy(cr);
     return NULL;
   }
@@ -215,17 +225,17 @@ static cairo_t* cairo_context_from_diagram(diagram_t* diagram) {
     /* If this shape is text... */
     if (diagram->shape[i].text) {
       /* Figure out what color to use from the R/G/B components of the
-        shape's stroke. */
+         shape's stroke. */
       cairo_set_source_rgba(cr,
-                           svgtiny_RED(diagram->shape[i].stroke) / 255.0,
-                           svgtiny_GREEN(diagram->shape[i].stroke) / 255.0,
-                           svgtiny_BLUE(diagram->shape[i].stroke) / 255.0,
-                           1);
+                            svgtiny_RED(diagram->shape[i].stroke) / 255.0,
+                            svgtiny_GREEN(diagram->shape[i].stroke) / 255.0,
+                            svgtiny_BLUE(diagram->shape[i].stroke) / 255.0,
+                            1);
       /* Then move to the actual position of the text within the
-        shape... */
+         shape... */
       cairo_move_to(cr,
-                   diagram->shape[i].text_x,
-                   diagram->shape[i].text_y);
+                    diagram->shape[i].text_x,
+                    diagram->shape[i].text_y);
 
       /* and draw it. */
       cairo_show_text(cr, diagram->shape[i].text);
@@ -246,22 +256,36 @@ static cairo_t* cairo_context_from_diagram(diagram_t* diagram) {
   return cr;
 }
 
+/**
+ * @brief Create a GdkPixbuf from a buffer of SVG data.
+ *
+ * @param buffer
+ *   The buffer containing the SVG document.
+ *
+ * @param bytecount
+ *   The number of bytes in @c buffer.
+ *
+ * @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_buffer(char* buffer,
                                             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);
+                                       VIEWPORT_HEIGHT,
+                                       &sub_error);
   if (!diagram) {
-    g_set_error_literal(error,
-                       GDK_PIXBUF_ERROR,
-                       GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                       "Could not parse SVG diagram from file");
+    g_propagate_error(error, sub_error);
     return NULL;
   }
 
@@ -271,7 +295,7 @@ static GdkPixbuf* gdk_pixbuf_from_svg_buffer(char* buffer,
     g_set_error_literal(error,
                        GDK_PIXBUF_ERROR,
                        GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                       "Could not create Cairo surface from SVG diagram");
+                       "could not create Cairo surface from SVG diagram");
     return NULL;
   }
 
@@ -297,12 +321,28 @@ static GdkPixbuf* gdk_pixbuf_from_svg_buffer(char* buffer,
     g_set_error_literal(error,
                        GDK_PIXBUF_ERROR,
                        GDK_PIXBUF_ERROR_FAILED,
-                       "Failed to obtain a GdkPixbuf from Cairo surface");
+                       "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;
@@ -312,19 +352,19 @@ static GdkPixbuf* gdk_pixbuf_from_svg_file_stream(FILE *fp, GError** error) {
   bytecount = ftell(fp);
   rewind(fp);
 
-  buffer = malloc(bytecount);
-  if (!buffer) {
-    fprintf(stderr, "Unable to allocate %zd bytes\n", bytecount);
-    return NULL;
-  }
+  /* YOLO, no error checking */
+  buffer = g_malloc(bytecount);
 
   bytesread = fread(buffer, 1, bytecount, fp);
   if (bytesread != bytecount) {
-    fprintf(stderr, "Read only %zd of %zd bytes from stream\n",
-           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;
   }
-  fclose(fp);
 
   return gdk_pixbuf_from_svg_buffer(buffer, bytecount, error);
 }
@@ -336,22 +376,16 @@ static gpointer gdk_pixbuf_begin_load(GdkPixbufModuleSizeFunc size_func,
                                      gpointer user_data,
                                      GError **error) {
 
-  SvgTinyContext* context = g_new0(SvgTinyContext, 1);
+  SvgTinyContext* context = g_new(SvgTinyContext, 1);
 
   context->size_func      = size_func;
   context->prepared_func  = prep_func;
   context->updated_func   = updated_func;
   context->user_data      = user_data;
 
-  context->svg_data       = malloc(SVG_BUFFER_INCREMENT);
-  if (context->svg_data) {
-    context->svg_data_size = 0;
-    context->svg_data_max = SVG_BUFFER_INCREMENT;
-  }
-  else {
-    fprintf(stderr, "Unable to allocate %zd bytes\n", SVG_BUFFER_INCREMENT);
-    return NULL;
-  }
+  /* YOLO, no error checking */
+  context->svg_data       = g_malloc(SVG_BUFFER_INCREMENT);
+  context->svg_data_size  = 0;
 
   return context;
 }
@@ -360,26 +394,24 @@ static gboolean gdk_pixbuf_load_increment(gpointer data,
                                          const guchar* buf,
                                          guint size,
                                          GError** error) {
+  size_t increment = 0;
   SvgTinyContext* context = (SvgTinyContext*)data;
 
   if (context->svg_data_size + size > context->svg_data_max) {
-    size_t increment = 0;
     if (size > SVG_BUFFER_INCREMENT) {
       increment = size;
     }
     else {
       increment = SVG_BUFFER_INCREMENT;
     }
-    context->svg_data = realloc(context->svg_data,
-                               context->svg_data_max + increment);
-    if (context->svg_data) {
-      context->svg_data_max += increment;
-    }
-    else {
-      fprintf(stderr, "Unable to (re)allocate %zd bytes\n", increment);
-      return FALSE;
-    }
+
+    /* 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;
 
@@ -406,7 +438,6 @@ static void emit_prepared(SvgTinyContext* context, GdkPixbuf* pixbuf) {
 
 /*
 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) {
@@ -415,25 +446,28 @@ static void emit_size(SvgTinyContext* context, GdkPixbuf* pixbuf) {
 }
 */
 
+
 static gboolean gdk_pixbuf_stop_load(gpointer data, GError **error) {
   SvgTinyContext* context = (SvgTinyContext*)data;
   GdkPixbuf* pixbuf = NULL;
   gboolean result = TRUE;
+  GError* sub_error = NULL;
 
   pixbuf = gdk_pixbuf_from_svg_buffer(context->svg_data,
                                      context->svg_data_size,
-                                     error);
+                                     &sub_error);
 
   if (pixbuf != NULL) {
-    /* emit_size(context, pixbuf); */
+    /*emit_size(context, pixbuf);*/
     emit_prepared(context, pixbuf);
     emit_updated(context, pixbuf);
     g_object_unref(pixbuf);
   }
   else {
+    g_propagate_error(error, sub_error);
     result = FALSE;
   }
-  g_free (context);
+  g_free(context);
 
   return result;
 }
@@ -510,9 +544,9 @@ int main(int argc, char** argv) {
   pb = gdk_pixbuf_from_svg_file_stream(fp, &err);
   if (!pb) {
     fprintf(stderr,
-           "Error %d in gdk_pixbuf_from_svg_file_stream: %s\n",
-           err->code,
-           err->message);
+            "Error %d in gdk_pixbuf_from_svg_file_stream: %s\n",
+            err->code,
+            err->message);
     g_error_free(err);
     return 1;
   }