#include "output/spv/spv-writer.h"
+#include <cairo.h>
#include <inttypes.h>
#include <libxml/xmlwriter.h>
#include <math.h>
#include "libpspp/temp-file.h"
#include "libpspp/version.h"
#include "libpspp/zip-writer.h"
-#include "output/page-setup-item.h"
+#include "output/cairo-chart.h"
+#include "output/driver.h"
+#include "output/output-item.h"
+#include "output/page-setup.h"
#include "output/pivot-table.h"
-#include "output/text-item.h"
#include "gl/xalloc.h"
#include "gl/xvasprintf.h"
bool need_page_break;
};
+static void spv_writer_close_heading (struct spv_writer *);
+
char * WARN_UNUSED_RESULT
spv_writer_open (const char *filename, struct spv_writer **writerp)
{
return error;
}
-void
-spv_writer_set_page_setup (struct spv_writer *w,
- const struct page_setup *page_setup)
-{
- page_setup_destroy (w->page_setup);
- w->page_setup = page_setup_clone (page_setup);
-}
-
static void
write_attr (struct spv_writer *w, const char *name, const char *value)
{
time_t t = time (NULL);
struct tm *tm = gmtime (&t);
- char *tm_s = asctime (tm);
- write_attr (w, "creation-date-time", tm_s);
+ char tm_s[128];
+ if (strftime (tm_s, sizeof tm_s, "%x %X", tm))
+ write_attr (w, "creation-date-time", tm_s);
write_attr (w, "creator", version);
return true;
}
-void
-spv_writer_open_heading (struct spv_writer *w, const char *command_id,
- const char *label)
+static void
+spv_writer_open_heading (struct spv_writer *w, const struct output_item *item)
{
if (!w->heading)
{
w->heading_depth++;
start_elem (w, "heading");
- write_attr (w, "commandName", command_id);
+ if (item->command_name)
+ write_attr (w, "commandName", item->command_name);
+ if (!item->show)
+ write_attr (w, "visibility", "collapsed");
/* XXX locale */
/* XXX olang */
start_elem (w, "label");
- write_text (w, label);
+ write_text (w, output_item_get_label (item));
end_elem (w);
}
w->heading = NULL;
}
-void
+static void
spv_writer_close_heading (struct spv_writer *w)
{
const char *infix = "";
}
static void
-start_container (struct spv_writer *w)
+open_container (struct spv_writer *w, const struct output_item *item,
+ const char *inner_elem)
{
start_elem (w, "container");
- write_attr (w, "visibility", "visible");
+ write_attr (w, "visibility", item->show ? "visible" : "hidden");
if (w->need_page_break)
{
write_attr (w, "page-break-before", "always");
w->need_page_break = false;
}
+
+ start_elem (w, "label");
+ write_text (w, output_item_get_label (item));
+ end_elem (w);
+
+ start_elem (w, inner_elem);
+ if (item->command_name)
+ write_attr (w, "commandName", item->command_name);
}
-void
-spv_writer_put_text (struct spv_writer *w, const struct text_item *text,
- const char *command_id)
+static void
+close_container (struct spv_writer *w)
+{
+ end_elem (w);
+ end_elem (w);
+}
+
+static void
+spv_writer_put_text (struct spv_writer *w, struct output_item *item)
{
bool initial_depth = w->heading_depth;
if (!initial_depth)
spv_writer_open_file (w);
- start_container (w);
+ open_container (w, item, "vtx:text");
+ write_attr (w, "type",
+ (item->text.subtype == TEXT_ITEM_TITLE ? "title"
+ : item->text.subtype == TEXT_ITEM_PAGE_TITLE ? "page-title"
+ : "log"));
- start_elem (w, "label");
- write_text (w, (text->type == TEXT_ITEM_TITLE ? "Title"
- : text->type == TEXT_ITEM_PAGE_TITLE ? "Page Title"
- : "Log"));
+ start_elem (w, "html");
+ char *s = text_item_get_plain_text (item);
+ write_text (w, s);
+ free (s);
end_elem (w);
- start_elem (w, "vtx:text");
- write_attr (w, "type", (text->type == TEXT_ITEM_TITLE ? "title"
- : text->type == TEXT_ITEM_PAGE_TITLE ? "page-title"
- : "log"));
- if (command_id)
- write_attr (w, "commandName", command_id);
-
- start_elem (w, "html");
- write_text (w, text->text); /* XXX */
- end_elem (w); /* html */
- end_elem (w); /* vtx:text */
- end_elem (w); /* container */
+ close_container (w);
if (!initial_depth)
spv_writer_close_file (w, "");
+
+ output_item_unref (item);
}
-void
-spv_writer_eject_page (struct spv_writer *w)
+static cairo_status_t
+write_to_zip (void *zw_, const unsigned char *data, unsigned int length)
{
- w->need_page_break = true;
+ struct zip_writer *zw = zw_;
+
+ zip_writer_add_write (zw, data, length);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+spv_writer_put_image (struct spv_writer *w, const struct output_item *item,
+ cairo_surface_t *image)
+{
+ bool initial_depth = w->heading_depth;
+ if (!initial_depth)
+ spv_writer_open_file (w);
+
+ char *uri = xasprintf ("%010d_Imagegeneric.png", ++w->n_tables);
+
+ open_container (w, item, "object");
+ write_attr (w, "type", "unknown");
+ write_attr (w, "uri", uri);
+ close_container (w);
+
+ if (!initial_depth)
+ spv_writer_close_file (w, "");
+
+ zip_writer_add_start (w->zw, uri);
+ cairo_surface_write_to_png_stream (image, write_to_zip, w->zw);
+ zip_writer_add_finish (w->zw);
+
+ free (uri);
}
\f
#define H TABLE_HORZ
put_value_mod (struct buf *buf, const struct pivot_value *value,
const char *template)
{
- if (value->n_footnotes || value->n_subscripts
- || template || value->font_style || value->cell_style)
+ if ((value->ex
+ && (value->ex->n_footnotes
+ || value->ex->n_subscripts
+ || value->ex->font_style
+ || value->ex->cell_style))
+ || template)
{
+ const struct pivot_value_ex *ex = pivot_value_ex (value);
put_byte (buf, 0x31);
/* Footnotes. */
- put_u32 (buf, value->n_footnotes);
- for (size_t i = 0; i < value->n_footnotes; i++)
- put_u16 (buf, value->footnote_indexes[i]);
+ put_u32 (buf, ex->n_footnotes);
+ for (size_t i = 0; i < ex->n_footnotes; i++)
+ put_u16 (buf, ex->footnote_indexes[i]);
/* Subscripts. */
- put_u32 (buf, value->n_subscripts);
- for (size_t i = 0; i < value->n_subscripts; i++)
- put_string (buf, value->subscripts[i]);
+ put_u32 (buf, ex->n_subscripts);
+ for (size_t i = 0; i < ex->n_subscripts; i++)
+ put_string (buf, ex->subscripts[i]);
/* Template and style. */
uint32_t v3_start = start_count (buf);
put_string (buf, template);
}
end_count_u32 (buf, template_string_start);
- put_style_pair (buf, value->font_style, value->cell_style);
+ put_style_pair (buf, ex->font_style, ex->cell_style);
end_count_u32 (buf, v3_start);
}
else
}
static void
-put_format (struct buf *buf, const struct fmt_spec *f)
+put_format (struct buf *buf, const struct fmt_spec *f, bool honor_small)
{
- put_u32 (buf, (fmt_to_io (f->type) << 16) | (f->w << 8) | f->d);
+ int type = f->type == FMT_F && honor_small ? 40 : fmt_to_io (f->type);
+ put_u32 (buf, (type << 16) | (f->w << 8) | f->d);
}
static int
{
put_byte (buf, 2);
put_value_mod (buf, value, NULL);
- put_format (buf, &value->numeric.format);
+ put_format (buf, &value->numeric.format, value->numeric.honor_small);
put_double (buf, value->numeric.x);
put_string (buf, value->numeric.var_name);
put_string (buf, value->numeric.value_label);
{
put_byte (buf, 1);
put_value_mod (buf, value, NULL);
- put_format (buf, &value->numeric.format);
+ put_format (buf, &value->numeric.format, value->numeric.honor_small);
put_double (buf, value->numeric.x);
}
break;
case PIVOT_VALUE_STRING:
put_byte (buf, 4);
put_value_mod (buf, value, NULL);
- put_format (buf,
- &(struct fmt_spec) { FMT_A, strlen (value->string.s), 0 });
+ size_t len = strlen (value->string.s);
+ if (value->string.hex)
+ put_format (buf, &(struct fmt_spec) { .type = FMT_AHEX, .w = len * 2 },
+ false);
+ else
+ put_format (buf, &(struct fmt_spec) { .type = FMT_A, .w = len }, false);
put_string (buf, value->string.value_label);
put_string (buf, value->string.var_name);
put_show_values (buf, value->string.show);
else
{
put_bytes (buf, "\0\0\1", 3);
- put_u32 (buf, 0);
+ put_u32 (buf, 0); /* x23 */
put_u32 (buf, -1);
put_u32 (buf, c->n_subs);
for (size_t i = 0; i < c->n_subs; i++)
{
put_u32 (buf, table->settings.epoch);
put_byte (buf, table->settings.decimal);
- put_byte (buf, table->grouping);
+ put_byte (buf, ',');
}
static void
static void
put_x1 (struct buf *buf, const struct pivot_table *table)
{
- put_byte (buf, 0);
+ put_byte (buf, 0); /* x14 */
put_byte (buf, table->show_title ? 1 : 10);
- put_byte (buf, 0);
- put_byte (buf, 0);
+ put_byte (buf, 0); /* x16 */
+ put_byte (buf, 0); /* lang */
put_show_values (buf, table->show_variables);
put_show_values (buf, table->show_values);
- put_u32 (buf, -1);
- put_u32 (buf, -1);
+ put_u32 (buf, -1); /* x18 */
+ put_u32 (buf, -1); /* x19 */
for (int i = 0; i < 17; i++)
put_byte (buf, 0);
- put_bool (buf, false);
+ put_bool (buf, false); /* x20 */
put_byte (buf, table->show_caption);
}
}
static void
-put_x3 (struct buf *buf, const struct pivot_table *table)
+put_y1 (struct buf *buf, const struct pivot_table *table)
{
- put_bytes (buf, "\1\0\4\0\0\0", 6);
put_string (buf, table->command_c);
put_string (buf, table->command_local);
put_string (buf, table->language);
put_string (buf, table->locale);
put_bytes (buf, "\0\0\1\1", 4);
put_y0 (buf, table);
+}
+
+static void
+put_y2 (struct buf *buf, const struct pivot_table *table)
+{
+ put_custom_currency (buf, table);
+ put_byte (buf, '.');
+ put_bool (buf, 0);
+}
+
+static void
+put_x3 (struct buf *buf, const struct pivot_table *table)
+{
+ put_byte (buf, 1);
+ put_byte (buf, 0);
+ put_byte (buf, 4); /* x21 */
+ put_byte (buf, 0);
+ put_byte (buf, 0);
+ put_byte (buf, 0);
+ put_y1 (buf, table);
put_double (buf, table->small);
put_byte (buf, 1);
put_string (buf, table->dataset);
put_u32 (buf, 0);
put_u32 (buf, table->date);
put_u32 (buf, 0);
+ put_y2 (buf, table);
+}
- /* Y2. */
- put_custom_currency (buf, table);
- put_byte (buf, '.');
- put_bool (buf, 0);
+static uint32_t
+encode_current_layer (const struct pivot_table *table)
+{
+ uint32_t current_layer = 0;
+
+ const struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
+ for (size_t i = axis->n_dimensions - 1; i < axis->n_dimensions; i--)
+ {
+ const struct pivot_dimension *d = axis->dimensions[i];
+ current_layer = current_layer * d->n_leaves + table->current_layer[i];
+ }
+
+ return current_layer;
}
static void
put_u32 (buf, table->n_footnotes);
for (size_t i = 0; i < table->n_footnotes; i++)
{
- put_value (buf, table->footnotes[i]->content);
- put_optional_value (buf, table->footnotes[i]->marker);
- put_u32 (buf, 0);
+ const struct pivot_footnote *f = table->footnotes[i];
+ put_value (buf, f->content);
+ put_optional_value (buf, f->marker);
+ put_u32 (buf, f->show ? 1 : -1);
}
/* Areas. */
uint32_t ts_start = start_count (buf);
put_be32 (buf, 1);
put_be32 (buf, 4);
- put_be32 (buf, 0); /* XXX current_layer */
+ put_be32 (buf, encode_current_layer (table));
put_bool (buf, table->look->omit_empty);
put_bool (buf, table->look->row_labels_in_corner);
put_bool (buf, !table->look->show_numeric_markers);
{
const struct pivot_dimension *d = table->dimensions[i];
put_value (buf, d->root->name);
- put_byte (buf, 0);
+ put_byte (buf, 0); /* x1 */
put_byte (buf, x2[i]);
- put_u32 (buf, 2);
+ put_u32 (buf, 2); /* x3 */
put_bool (buf, !d->root->show_label);
put_bool (buf, d->hide_all_labels);
put_bool (buf, 1);
}
}
-void
-spv_writer_put_table (struct spv_writer *w, const struct pivot_table *table)
+static void
+spv_writer_put_table (struct spv_writer *w, const struct output_item *item)
{
- struct pivot_table *table_rw = CONST_CAST (struct pivot_table *, table);
- if (!table_rw->subtype)
- table_rw->subtype = pivot_value_new_user_text ("unknown", -1);
-
int table_id = ++w->n_tables;
bool initial_depth = w->heading_depth;
if (!initial_depth)
spv_writer_open_file (w);
- start_container (w);
-
- char *title = pivot_value_to_string (table->title, table);
- char *subtype = pivot_value_to_string (table->subtype, table);
-
- start_elem (w, "label");
- write_text (w, title);
- end_elem (w);
+ open_container (w, item, "vtb:table");
- start_elem (w, "vtb:table");
- write_attr (w, "commandName", table->command_c);
write_attr (w, "type", "table"); /* XXX */
- write_attr (w, "subType", subtype);
write_attr_format (w, "tableId", "%d", table_id);
-
+ char *subtype = (item->table->subtype
+ ? pivot_value_to_string (item->table->subtype, item->table)
+ : xstrdup ("unknown"));
+ write_attr (w, "subType", subtype);
free (subtype);
- free (title);
start_elem (w, "vtb:tableStructure");
start_elem (w, "vtb:dataPath");
write_text (w, data_path);
end_elem (w); /* vtb:dataPath */
end_elem (w); /* vtb:tableStructure */
- end_elem (w); /* vtb:table */
- end_elem (w); /* container */
+
+ close_container (w);
if (!initial_depth)
spv_writer_close_file (w, "");
struct buf buf = { NULL, 0, 0 };
- put_light_table (&buf, table_id, table);
+ put_light_table (&buf, table_id, item->table);
zip_writer_add_memory (w->zw, data_path, buf.data, buf.len);
free (buf.data);
free (data_path);
}
+\f
+void
+spv_writer_write (struct spv_writer *w, const struct output_item *item)
+{
+ switch (item->type)
+ {
+ case OUTPUT_ITEM_CHART:
+ {
+ cairo_surface_t *surface = xr_draw_image_chart (
+ item->chart,
+ &(struct cell_color) CELL_COLOR_BLACK,
+ &(struct cell_color) CELL_COLOR_WHITE);
+ if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
+ spv_writer_put_image (w, item, surface);
+ cairo_surface_destroy (surface);
+ }
+ break;
+
+ case OUTPUT_ITEM_GROUP:
+ spv_writer_open_heading (w, item);
+ for (size_t i = 0; i < item->group.n_children; i++)
+ spv_writer_write (w, item->group.children[i]);
+ spv_writer_close_heading (w);
+ break;
+
+ case OUTPUT_ITEM_IMAGE:
+ spv_writer_put_image (w, item, item->image);
+ break;
+
+ case OUTPUT_ITEM_MESSAGE:
+ spv_writer_put_text (
+ w, message_item_to_text_item (output_item_ref (item)));
+ break;
+
+ case OUTPUT_ITEM_PAGE_BREAK:
+ w->need_page_break = true;
+ break;
+
+ case OUTPUT_ITEM_TABLE:
+ spv_writer_put_table (w, item);
+ break;
+
+ case OUTPUT_ITEM_TEXT:
+ spv_writer_put_text (w, output_item_ref (item));
+ break;
+ }
+}
+
+void
+spv_writer_set_page_setup (struct spv_writer *w,
+ const struct page_setup *ps)
+{
+ page_setup_destroy (w->page_setup);
+ w->page_setup = page_setup_clone (ps);
+}