zip-reader: Make the zip_reader reference counted.
[pspp] / src / output / spv / spv.c
index 83fba9631cc9287f2e3d3cb5b1de99f4493f17fe..fc058281eccd633f1e1824cd78c12a728de8fb51 100644 (file)
@@ -31,7 +31,7 @@
 #include "libpspp/message.h"
 #include "libpspp/str.h"
 #include "libpspp/zip-reader.h"
-#include "output/page-setup-item.h"
+#include "output/page-setup.h"
 #include "output/pivot-table.h"
 #include "output/spv/detail-xml-parser.h"
 #include "output/spv/light-binary-parser.h"
@@ -39,6 +39,7 @@
 #include "output/spv/spv-legacy-data.h"
 #include "output/spv/spv-legacy-decoder.h"
 #include "output/spv/spv-light-decoder.h"
+#include "output/spv/spv-table-look.h"
 #include "output/spv/structure-xml-parser.h"
 
 #include "gl/c-ctype.h"
@@ -54,7 +55,6 @@
 
 struct spv_reader
   {
-    struct string zip_errs;
     struct zip_reader *zip;
     struct spv_item *root;
     struct page_setup *page_setup;
@@ -76,7 +76,7 @@ spv_item_type_to_string (enum spv_item_type type)
     case SPV_ITEM_TABLE: return "table";
     case SPV_ITEM_GRAPH: return "graph";
     case SPV_ITEM_MODEL: return "model";
-    case SPV_ITEM_OBJECT: return "object";
+    case SPV_ITEM_IMAGE: return "image";
     default: return "**error**";
     }
 }
@@ -101,7 +101,7 @@ spv_item_class_from_string (const char *name)
   SPV_CLASSES
 #undef SPV_CLASS
 
-  return SPV_N_CLASSES;
+  return (enum spv_item_class) SPV_N_CLASSES;
 }
 
 enum spv_item_type
@@ -139,7 +139,7 @@ spv_item_get_class (const struct spv_item *item)
     case SPV_ITEM_MODEL:
       return SPV_CLASS_MODELS;
 
-    case SPV_ITEM_OBJECT:
+    case SPV_ITEM_IMAGE:
       return SPV_CLASS_OTHER;
 
     case SPV_ITEM_TREE:
@@ -194,6 +194,51 @@ spv_item_get_text (const struct spv_item *item)
   return item->text;
 }
 
+bool
+spv_item_is_image (const struct spv_item *item)
+{
+  return item->type == SPV_ITEM_IMAGE;
+}
+
+static cairo_status_t
+read_from_zip_member (void *zm_, unsigned char *data, unsigned int length)
+{
+  struct zip_member *zm = zm_;
+  if (!zm)
+    return CAIRO_STATUS_READ_ERROR;
+
+  while (length > 0)
+    {
+      int n = zip_member_read (zm, data, length);
+      if (n <= 0)
+        return CAIRO_STATUS_READ_ERROR;
+
+      data += n;
+      length -= n;
+    }
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+spv_item_get_image (const struct spv_item *item_)
+{
+  struct spv_item *item = CONST_CAST (struct spv_item *, item_);
+  assert (spv_item_is_image (item));
+
+  if (!item->image)
+    {
+      struct zip_member *zm;
+      char *error = zip_member_open (item->spv->zip, item->png_member, &zm);
+      item->image = cairo_image_surface_create_from_png_stream (
+        read_from_zip_member, zm);
+      zip_member_finish (zm);
+      free (error);
+    }
+
+  return item->image;
+}
+
 struct spv_item *
 spv_item_next (const struct spv_item *item)
 {
@@ -259,15 +304,16 @@ spv_item_destroy (struct spv_item *item)
       free (item->children);
 
       pivot_table_unref (item->table);
-      spv_legacy_properties_destroy (item->legacy_properties);
+      pivot_table_look_unref (item->table_look);
       free (item->bin_member);
       free (item->xml_member);
       free (item->subtype);
 
       pivot_value_destroy (item->text);
 
-      free (item->object_type);
-      free (item->uri);
+      free (item->png_member);
+      if (item->image)
+        cairo_surface_destroy (item->image);
 
       free (item);
     }
@@ -572,6 +618,8 @@ spv_item_load (const struct spv_item *item)
 {
   if (spv_item_is_table (item))
     spv_item_get_table (item);
+  else if (spv_item_is_image (item))
+    spv_item_get_image (item);
 }
 
 bool
@@ -680,9 +728,10 @@ spv_read_xml_member (struct spv_reader *spv, const char *member_name,
 {
   *docp = NULL;
 
-  struct zip_member *zm = zip_member_open (spv->zip, member_name);
-  if (!zm)
-    return ds_steal_cstr (&spv->zip_errs);
+  struct zip_member *zm;
+  char *error = zip_member_open (spv->zip, member_name, &zm);
+  if (error)
+    return error;
 
   xmlParserCtxt *parser;
   xmlKeepBlanksDefault (keep_blanks);
@@ -705,7 +754,7 @@ spv_read_xml_member (struct spv_reader *spv, const char *member_name,
 
   if (retval < 0)
     {
-      char *error = ds_steal_cstr (&spv->zip_errs);
+      char *error = zip_member_steal_error (zm);
       zip_member_finish (zm);
       xmlFreeDoc (doc);
       return error;
@@ -827,7 +876,7 @@ pivot_table_open_legacy (struct spv_item *item)
   error = spvxml_context_finish (&ctx, &v->node_);
 
   if (!error)
-    error = decode_spvdx_table (v, item->subtype, item->legacy_properties,
+    error = decode_spvdx_table (v, item->subtype, item->table_look,
                                 &data, &item->table);
 
   if (error)
@@ -848,7 +897,7 @@ pivot_table_open_legacy (struct spv_item *item)
   return error;
 }
 
-struct pivot_table *
+const struct pivot_table *
 spv_item_get_table (const struct spv_item *item_)
 {
   struct spv_item *item = CONST_CAST (struct spv_item *, item_);
@@ -905,8 +954,10 @@ spv_decode_container (const struct spvsx_container *c,
       if (ts->path)
         {
           item->xml_member = ts->path ? xstrdup (ts->path->text) : NULL;
-          char *error = decode_spvsx_legacy_properties (
-            table->table_properties, &item->legacy_properties);
+          char *error = (table->table_properties
+                         ? spv_table_look_decode (table->table_properties,
+                                                  &item->table_look)
+                         : xstrdup ("Legacy table lacks tableProperties"));
           if (error)
             {
               spv_item_destroy (item);
@@ -931,24 +982,17 @@ spv_decode_container (const struct spvsx_container *c,
   else if (spvsx_is_object (content))
     {
       struct spvsx_object *object = spvsx_cast_object (content);
-      item->type = SPV_ITEM_OBJECT;
-      item->object_type = xstrdup (object->type);
-      item->uri = xstrdup (object->uri);
+      item->type = SPV_ITEM_IMAGE;
+      item->png_member = xstrdup (object->uri);
     }
   else if (spvsx_is_image (content))
     {
       struct spvsx_image *image = spvsx_cast_image (content);
-      item->type = SPV_ITEM_OBJECT;
-      item->object_type = xstrdup ("image");
-      item->uri = xstrdup (image->data_path->text);
+      item->type = SPV_ITEM_IMAGE;
+      item->png_member = xstrdup (image->data_path->text);
     }
   else if (spvsx_is_tree (content))
-    {
-      struct spvsx_tree *tree = spvsx_cast_tree (content);
-      item->type = SPV_ITEM_TREE;
-      item->object_type = xstrdup ("tree");
-      item->uri = xstrdup (tree->data_path->text);
-    }
+    item->type = SPV_ITEM_TREE;
   else
     NOT_REACHED ();
 
@@ -1109,16 +1153,14 @@ spv_detect__ (struct zip_reader *zip, char **errorp)
 char * WARN_UNUSED_RESULT
 spv_detect (const char *filename)
 {
-  struct string zip_error;
-  struct zip_reader *zip = zip_reader_create (filename, &zip_error);
-  if (!zip)
-    return ds_steal_cstr (&zip_error);
+  struct zip_reader *zip;
+  char *error = zip_reader_create (filename, &zip);
+  if (error)
+    return error;
 
-  char *error;
   if (spv_detect__ (zip, &error) <= 0 && !error)
     error = xasprintf("%s: not an SPV file", filename);
-  zip_reader_destroy (zip);
-  ds_destroy (&zip_error);
+  zip_reader_unref (zip);
   return error;
 }
 
@@ -1128,16 +1170,13 @@ spv_open (const char *filename, struct spv_reader **spvp)
   *spvp = NULL;
 
   struct spv_reader *spv = xzalloc (sizeof *spv);
-  ds_init_empty (&spv->zip_errs);
-  spv->zip = zip_reader_create (filename, &spv->zip_errs);
-  if (!spv->zip)
+  char *error = zip_reader_create (filename, &spv->zip);
+  if (error)
     {
-      char *error = ds_steal_cstr (&spv->zip_errs);
       spv_close (spv);
       return error;
     }
 
-  char *error;
   int detect = spv_detect__ (spv->zip, &error);
   if (detect <= 0)
     {
@@ -1176,14 +1215,31 @@ spv_close (struct spv_reader *spv)
 {
   if (spv)
     {
-      ds_destroy (&spv->zip_errs);
-      zip_reader_destroy (spv->zip);
+      zip_reader_unref (spv->zip);
       spv_item_destroy (spv->root);
       page_setup_destroy (spv->page_setup);
       free (spv);
     }
 }
 
+void
+spv_item_set_table_look (struct spv_item *item,
+                         const struct pivot_table_look *look)
+{
+  /* If this is a table, install the table look in it.
+
+     (We can't just set item->table_look because light tables ignore it and
+     legacy tables sometimes override it.) */
+  if (spv_item_is_table (item))
+    {
+      spv_item_load (item);
+      pivot_table_set_look (item->table, look);
+    }
+
+  for (size_t i = 0; i < item->n_children; i++)
+    spv_item_set_table_look (item->children[i], look);
+}
+
 char * WARN_UNUSED_RESULT
 spv_decode_fmt_spec (uint32_t u32, struct fmt_spec *out)
 {