output-item: Collapse the inheritance hierarchy into a single struct.
[pspp] / src / output / spv / spv-writer.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2019 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "output/spv/spv-writer.h"
20
21 #include <cairo.h>
22 #include <inttypes.h>
23 #include <libxml/xmlwriter.h>
24 #include <math.h>
25 #include <stdlib.h>
26 #include <time.h>
27
28 #include "libpspp/array.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/cast.h"
31 #include "libpspp/float-format.h"
32 #include "libpspp/integer-format.h"
33 #include "libpspp/temp-file.h"
34 #include "libpspp/version.h"
35 #include "libpspp/zip-writer.h"
36 #include "output/cairo-chart.h"
37 #include "output/driver.h"
38 #include "output/output-item.h"
39 #include "output/page-setup.h"
40 #include "output/pivot-table.h"
41
42 #include "gl/xalloc.h"
43 #include "gl/xvasprintf.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47 #define N_(msgid) (msgid)
48
49 struct spv_writer
50   {
51     struct zip_writer *zw;
52
53     FILE *heading;
54     int heading_depth;
55     xmlTextWriter *xml;
56
57     int n_tables;
58
59     int n_headings;
60     struct page_setup *page_setup;
61     bool need_page_break;
62   };
63
64 static void spv_writer_close_heading (struct spv_writer *);
65
66 char * WARN_UNUSED_RESULT
67 spv_writer_open (const char *filename, struct spv_writer **writerp)
68 {
69   *writerp = NULL;
70
71   struct zip_writer *zw = zip_writer_create (filename);
72   if (!zw)
73     return xasprintf (_("%s: create failed"), filename);
74
75   struct spv_writer *w = xmalloc (sizeof *w);
76   *w = (struct spv_writer) { .zw = zw };
77   *writerp = w;
78   return NULL;
79 }
80
81 char * WARN_UNUSED_RESULT
82 spv_writer_close (struct spv_writer *w)
83 {
84   if (!w)
85     return NULL;
86
87   zip_writer_add_string (w->zw, "META-INF/MANIFEST.MF", "allowPivoting=true");
88
89   while (w->heading_depth)
90     spv_writer_close_heading (w);
91
92   char *error = NULL;
93   if (!zip_writer_close (w->zw))
94     error = xstrdup (_("I/O error writing SPV file"));
95
96   page_setup_destroy (w->page_setup);
97   free (w);
98   return error;
99 }
100
101 static void
102 write_attr (struct spv_writer *w, const char *name, const char *value)
103 {
104   xmlTextWriterWriteAttribute (w->xml,
105                                CHAR_CAST (xmlChar *, name),
106                                CHAR_CAST (xmlChar *, value));
107 }
108
109 static void PRINTF_FORMAT (3, 4)
110 write_attr_format (struct spv_writer *w, const char *name,
111                    const char *format, ...)
112 {
113   va_list args;
114   va_start (args, format);
115   char *value = xvasprintf (format, args);
116   va_end (args);
117
118   write_attr (w, name, value);
119   free (value);
120 }
121
122 static void
123 start_elem (struct spv_writer *w, const char *name)
124 {
125   xmlTextWriterStartElement (w->xml, CHAR_CAST (xmlChar *, name));
126 }
127
128 static void
129 end_elem (struct spv_writer *w)
130 {
131   xmlTextWriterEndElement (w->xml);
132 }
133
134 static void
135 write_text (struct spv_writer *w, const char *text)
136 {
137   xmlTextWriterWriteString (w->xml, CHAR_CAST (xmlChar *, text));
138 }
139
140 static void
141 write_page_heading (struct spv_writer *w, const struct page_heading *h,
142                     const char *name)
143 {
144   start_elem (w, name);
145   if (h->n)
146     {
147       start_elem (w, "pageParagraph");
148       for (size_t i = 0; i < h->n; i++)
149         {
150           start_elem (w, "text");
151           write_attr (w, "type", "title");
152           write_text (w, h->paragraphs[i].markup); /* XXX */
153           end_elem (w);
154         }
155       end_elem (w);
156     }
157   end_elem (w);
158 }
159
160 static void
161 write_page_setup (struct spv_writer *w, const struct page_setup *ps)
162 {
163   start_elem (w, "pageSetup");
164   write_attr_format (w, "initial-page-number", "%d", ps->initial_page_number);
165   write_attr (w, "chart-size",
166               (ps->chart_size == PAGE_CHART_AS_IS ? "as-is"
167                : ps->chart_size == PAGE_CHART_FULL_HEIGHT ? "full-height"
168                : ps->chart_size == PAGE_CHART_HALF_HEIGHT ? "half-height"
169                : "quarter-height"));
170   write_attr_format (w, "margin-left", "%.2fin", ps->margins[TABLE_HORZ][0]);
171   write_attr_format (w, "margin-right", "%.2fin", ps->margins[TABLE_HORZ][1]);
172   write_attr_format (w, "margin-top", "%.2fin", ps->margins[TABLE_VERT][0]);
173   write_attr_format (w, "margin-bottom", "%.2fin", ps->margins[TABLE_VERT][1]);
174   write_attr_format (w, "paper-height", "%.2fin", ps->paper[TABLE_VERT]);
175   write_attr_format (w, "paper-width", "%.2fin", ps->paper[TABLE_HORZ]);
176   write_attr (w, "reference-orientation",
177               ps->orientation == PAGE_PORTRAIT ? "portrait" : "landscape");
178   write_attr_format (w, "space-after", "%.1fpt", ps->object_spacing * 72.0);
179   write_page_heading (w, &ps->headings[0], "pageHeader");
180   write_page_heading (w, &ps->headings[1], "pageFooter");
181   end_elem (w);
182 }
183
184 static bool
185 spv_writer_open_file (struct spv_writer *w)
186 {
187   w->heading = create_temp_file ();
188   if (!w->heading)
189     return false;
190
191   w->xml = xmlNewTextWriter (xmlOutputBufferCreateFile (w->heading, NULL));
192   xmlTextWriterStartDocument (w->xml, NULL, "UTF-8", NULL);
193   start_elem (w, "heading");
194
195   time_t t = time (NULL);
196   struct tm *tm = gmtime (&t);
197   char *tm_s = asctime (tm);
198   write_attr (w, "creation-date-time", tm_s);
199
200   write_attr (w, "creator", version);
201
202   write_attr (w, "creator-version", "21");
203
204   write_attr (w, "xmlns", "http://xml.spss.com/spss/viewer/viewer-tree");
205   write_attr (w, "xmlns:vps", "http://xml.spss.com/spss/viewer/viewer-pagesetup");
206   write_attr (w, "xmlns:vtx", "http://xml.spss.com/spss/viewer/viewer-text");
207   write_attr (w, "xmlns:vtb", "http://xml.spss.com/spss/viewer/viewer-table");
208
209   start_elem (w, "label");
210   write_text (w, _("Output"));
211   end_elem (w);
212
213   if (w->page_setup)
214     {
215       write_page_setup (w, w->page_setup);
216
217       page_setup_destroy (w->page_setup);
218       w->page_setup = NULL;
219     }
220
221   return true;
222 }
223
224 static void
225 spv_writer_open_heading (struct spv_writer *w, const struct output_item *item)
226 {
227   if (!w->heading)
228     {
229       if (!spv_writer_open_file (w))
230         return;
231     }
232
233   w->heading_depth++;
234   start_elem (w, "heading");
235   if (item->command_name)
236     write_attr (w, "commandName", item->command_name);
237   /* XXX locale */
238   /* XXX olang */
239
240   start_elem (w, "label");
241   write_text (w, output_item_get_label (item));
242   end_elem (w);
243 }
244
245 static void
246 spv_writer_close_file (struct spv_writer *w, const char *infix)
247 {
248   if (!w->heading)
249     return;
250
251   end_elem (w);
252   xmlTextWriterEndDocument (w->xml);
253   xmlFreeTextWriter (w->xml);
254
255   char *member_name = xasprintf ("outputViewer%010d%s.xml",
256                                  w->n_headings++, infix);
257   zip_writer_add (w->zw, w->heading, member_name);
258   free (member_name);
259
260   w->heading = NULL;
261 }
262
263 static void
264 spv_writer_close_heading (struct spv_writer *w)
265 {
266   const char *infix = "";
267   if (w->heading_depth)
268     {
269       infix = "_heading";
270       end_elem (w);
271       w->heading_depth--;
272     }
273
274   if (!w->heading_depth)
275     spv_writer_close_file (w, infix);
276 }
277
278 static void
279 open_container (struct spv_writer *w, const struct output_item *item,
280                 const char *inner_elem)
281 {
282   start_elem (w, "container");
283   write_attr (w, "visibility", "visible");
284   if (w->need_page_break)
285     {
286       write_attr (w, "page-break-before", "always");
287       w->need_page_break = false;
288     }
289
290   start_elem (w, "label");
291   write_text (w, output_item_get_label (item));
292   end_elem (w);
293
294   start_elem (w, inner_elem);
295   if (item->command_name)
296     write_attr (w, "commandName", item->command_name);
297 }
298
299 static void
300 close_container (struct spv_writer *w)
301 {
302   end_elem (w);
303   end_elem (w);
304 }
305
306 static void
307 spv_writer_put_text (struct spv_writer *w, struct output_item *item)
308 {
309   bool initial_depth = w->heading_depth;
310   if (!initial_depth)
311     spv_writer_open_file (w);
312
313   open_container (w, item, "vtx:text");
314   write_attr (w, "type",
315               (item->text.subtype == TEXT_ITEM_TITLE ? "title"
316                : item->text.subtype == TEXT_ITEM_PAGE_TITLE ? "page-title"
317                : "log"));
318
319   start_elem (w, "html");
320   char *s = text_item_get_plain_text (item);
321   write_text (w, s);
322   free (s);
323   end_elem (w);
324
325   close_container (w);
326
327   if (!initial_depth)
328     spv_writer_close_file (w, "");
329
330   output_item_unref (item);
331 }
332
333 static cairo_status_t
334 write_to_zip (void *zw_, const unsigned char *data, unsigned int length)
335 {
336   struct zip_writer *zw = zw_;
337
338   zip_writer_add_write (zw, data, length);
339   return CAIRO_STATUS_SUCCESS;
340 }
341
342 static void
343 spv_writer_put_image (struct spv_writer *w, const struct output_item *item,
344                       cairo_surface_t *image)
345 {
346   bool initial_depth = w->heading_depth;
347   if (!initial_depth)
348     spv_writer_open_file (w);
349
350   char *uri = xasprintf ("%010d_Imagegeneric.png", ++w->n_tables);
351
352   open_container (w, item, "object");
353   write_attr (w, "type", "unknown");
354   write_attr (w, "uri", uri);
355   close_container (w);
356
357   if (!initial_depth)
358     spv_writer_close_file (w, "");
359
360   zip_writer_add_start (w->zw, uri);
361   cairo_surface_write_to_png_stream (image, write_to_zip, w->zw);
362   zip_writer_add_finish (w->zw);
363
364   free (uri);
365 }
366 \f
367 #define H TABLE_HORZ
368 #define V TABLE_VERT
369
370 struct buf
371   {
372     uint8_t *data;
373     size_t len;
374     size_t allocated;
375   };
376
377 static uint8_t *
378 put_uninit (struct buf *b, size_t n)
379 {
380   while (b->allocated - b->len < n)
381     b->data = x2nrealloc (b->data, &b->allocated, sizeof b->data);
382   uint8_t *p = &b->data[b->len];
383   b->len += n;
384   return p;
385 }
386
387 static void
388 put_byte (struct buf *b, uint8_t byte)
389 {
390   *put_uninit (b, 1) = byte;
391 }
392
393 static void
394 put_bool (struct buf *b, bool boolean)
395 {
396   put_byte (b, boolean);
397 }
398
399 static void
400 put_bytes (struct buf *b, const char *bytes, size_t n)
401 {
402   memcpy (put_uninit (b, n), bytes, n);
403 }
404
405 static void
406 put_u16 (struct buf *b, uint16_t x)
407 {
408   put_uint16 (native_to_le16 (x), put_uninit (b, sizeof x));
409 }
410
411 static void
412 put_u32 (struct buf *b, uint32_t x)
413 {
414   put_uint32 (native_to_le32 (x), put_uninit (b, sizeof x));
415 }
416
417 static void
418 put_u64 (struct buf *b, uint64_t x)
419 {
420   put_uint64 (native_to_le64 (x), put_uninit (b, sizeof x));
421 }
422
423 static void
424 put_be32 (struct buf *b, uint32_t x)
425 {
426   put_uint32 (native_to_be32 (x), put_uninit (b, sizeof x));
427 }
428
429 static void
430 put_double (struct buf *b, double x)
431 {
432   float_convert (FLOAT_NATIVE_DOUBLE, &x,
433                  FLOAT_IEEE_DOUBLE_LE, put_uninit (b, 8));
434 }
435
436 static void
437 put_float (struct buf *b, float x)
438 {
439   float_convert (FLOAT_NATIVE_FLOAT, &x,
440                  FLOAT_IEEE_SINGLE_LE, put_uninit (b, 4));
441 }
442
443 static void
444 put_string (struct buf *b, const char *s_)
445 {
446   const char *s = s_ ? s_ : "";
447   size_t len = strlen (s);
448   put_u32 (b, len);
449   memcpy (put_uninit (b, len), s, len);
450 }
451
452 static void
453 put_bestring (struct buf *b, const char *s_)
454 {
455   const char *s = s_ ? s_ : "";
456   size_t len = strlen (s);
457   put_be32 (b, len);
458   memcpy (put_uninit (b, len), s, len);
459 }
460
461 static size_t
462 start_count (struct buf *b)
463 {
464   put_u32 (b, 0);
465   return b->len;
466 }
467
468 static void
469 end_count_u32 (struct buf *b, size_t start)
470 {
471   put_uint32 (native_to_le32 (b->len - start), &b->data[start - 4]);
472 }
473
474 static void
475 end_count_be32 (struct buf *b, size_t start)
476 {
477   put_uint32 (native_to_be32 (b->len - start), &b->data[start - 4]);
478 }
479
480 static void
481 put_color (struct buf *buf, const struct cell_color *color)
482 {
483   char *s = xasprintf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8,
484                        color->r, color->g, color->b);
485   put_string (buf, s);
486   free (s);
487 }
488
489 static void
490 put_font_style (struct buf *buf, const struct font_style *font_style)
491 {
492   put_bool (buf, font_style->bold);
493   put_bool (buf, font_style->italic);
494   put_bool (buf, font_style->underline);
495   put_bool (buf, 1);
496   put_color (buf, &font_style->fg[0]);
497   put_color (buf, &font_style->bg[0]);
498   put_string (buf, font_style->typeface ? font_style->typeface : "SansSerif");
499   put_byte (buf, ceil (font_style->size * 1.33));
500 }
501
502 static void
503 put_halign (struct buf *buf, enum table_halign halign,
504             uint32_t mixed, uint32_t decimal)
505 {
506   put_u32 (buf, (halign == TABLE_HALIGN_RIGHT ? 4
507                  : halign == TABLE_HALIGN_LEFT ? 2
508                  : halign == TABLE_HALIGN_CENTER ? 0
509                  : halign == TABLE_HALIGN_MIXED ? mixed
510                  : decimal));
511 }
512
513 static void
514 put_valign (struct buf *buf, enum table_valign valign)
515 {
516   put_u32 (buf, (valign == TABLE_VALIGN_TOP ? 1
517                  : valign == TABLE_VALIGN_CENTER ? 0
518                  : 3));
519 }
520
521 static void
522 put_cell_style (struct buf *buf, const struct cell_style *cell_style)
523 {
524   put_halign (buf, cell_style->halign, 0xffffffad, 6);
525   put_valign (buf, cell_style->valign);
526   put_double (buf, cell_style->decimal_offset);
527   put_u16 (buf, cell_style->margin[H][0]);
528   put_u16 (buf, cell_style->margin[H][1]);
529   put_u16 (buf, cell_style->margin[V][0]);
530   put_u16 (buf, cell_style->margin[V][1]);
531 }
532
533 static void UNUSED
534 put_style_pair (struct buf *buf, const struct font_style *font_style,
535                 const struct cell_style *cell_style)
536 {
537   if (font_style)
538     {
539       put_byte (buf, 0x31);
540       put_font_style (buf, font_style);
541     }
542   else
543     put_byte (buf, 0x58);
544
545   if (cell_style)
546     {
547       put_byte (buf, 0x31);
548       put_cell_style (buf, cell_style);
549     }
550   else
551     put_byte (buf, 0x58);
552 }
553
554 static void
555 put_value_mod (struct buf *buf, const struct pivot_value *value,
556                const char *template)
557 {
558   if (value->n_footnotes || value->n_subscripts
559       || template || value->font_style || value->cell_style)
560     {
561       put_byte (buf, 0x31);
562
563       /* Footnotes. */
564       put_u32 (buf, value->n_footnotes);
565       for (size_t i = 0; i < value->n_footnotes; i++)
566         put_u16 (buf, value->footnote_indexes[i]);
567
568       /* Subscripts. */
569       put_u32 (buf, value->n_subscripts);
570       for (size_t i = 0; i < value->n_subscripts; i++)
571         put_string (buf, value->subscripts[i]);
572
573       /* Template and style. */
574       uint32_t v3_start = start_count (buf);
575       uint32_t template_string_start = start_count (buf);
576       if (template)
577         {
578           uint32_t inner_start = start_count (buf);
579           end_count_u32 (buf, inner_start);
580
581           put_byte (buf, 0x31);
582           put_string (buf, template);
583         }
584       end_count_u32 (buf, template_string_start);
585       put_style_pair (buf, value->font_style, value->cell_style);
586       end_count_u32 (buf, v3_start);
587     }
588   else
589     put_byte (buf, 0x58);
590 }
591
592 static void
593 put_format (struct buf *buf, const struct fmt_spec *f, bool honor_small)
594 {
595   int type = f->type == FMT_F && honor_small ? 40 : fmt_to_io (f->type);
596   put_u32 (buf, (type << 16) | (f->w << 8) | f->d);
597 }
598
599 static int
600 show_values_to_spvlb (enum settings_value_show show)
601 {
602   return (show == SETTINGS_VALUE_SHOW_DEFAULT ? 0
603           : show == SETTINGS_VALUE_SHOW_VALUE ? 1
604           : show == SETTINGS_VALUE_SHOW_LABEL ? 2
605           : 3);
606 }
607
608 static void
609 put_show_values (struct buf *buf, enum settings_value_show show)
610 {
611   put_byte (buf, show_values_to_spvlb (show));
612 }
613
614 static void
615 put_value (struct buf *buf, const struct pivot_value *value)
616 {
617   switch (value->type)
618     {
619     case PIVOT_VALUE_NUMERIC:
620       if (value->numeric.var_name || value->numeric.value_label)
621         {
622           put_byte (buf, 2);
623           put_value_mod (buf, value, NULL);
624           put_format (buf, &value->numeric.format, value->numeric.honor_small);
625           put_double (buf, value->numeric.x);
626           put_string (buf, value->numeric.var_name);
627           put_string (buf, value->numeric.value_label);
628           put_show_values (buf, value->numeric.show);
629         }
630       else
631         {
632           put_byte (buf, 1);
633           put_value_mod (buf, value, NULL);
634           put_format (buf, &value->numeric.format, value->numeric.honor_small);
635           put_double (buf, value->numeric.x);
636         }
637       break;
638
639     case PIVOT_VALUE_STRING:
640       put_byte (buf, 4);
641       put_value_mod (buf, value, NULL);
642       size_t len = strlen (value->string.s);
643       if (value->string.hex)
644         put_format (buf, &(struct fmt_spec) { FMT_AHEX, len * 2, 0 }, false);
645       else
646         put_format (buf, &(struct fmt_spec) { FMT_A, len, 0 }, false);
647       put_string (buf, value->string.value_label);
648       put_string (buf, value->string.var_name);
649       put_show_values (buf, value->string.show);
650       put_string (buf, value->string.s);
651       break;
652
653     case PIVOT_VALUE_VARIABLE:
654       put_byte (buf, 5);
655       put_value_mod (buf, value, NULL);
656       put_string (buf, value->variable.var_name);
657       put_string (buf, value->variable.var_label);
658       put_show_values (buf, value->variable.show);
659       break;
660
661     case PIVOT_VALUE_TEXT:
662       put_byte (buf, 3);
663       put_string (buf, value->text.local);
664       put_value_mod (buf, value, NULL);
665       put_string (buf, value->text.id);
666       put_string (buf, value->text.c);
667       put_byte (buf, 1);        /* XXX user-provided */
668       break;
669
670     case PIVOT_VALUE_TEMPLATE:
671       put_byte (buf, 0);
672       put_value_mod (buf, value, value->template.id);
673       put_string (buf, value->template.local);
674       put_u32 (buf, value->template.n_args);
675       for (size_t i = 0; i < value->template.n_args; i++)
676         {
677           const struct pivot_argument *arg = &value->template.args[i];
678           assert (arg->n >= 1);
679           if (arg->n > 1)
680             {
681               put_u32 (buf, arg->n);
682               put_u32 (buf, 0);
683               for (size_t j = 0; j < arg->n; j++)
684                 {
685                   if (j > 0)
686                     put_bytes (buf, "\0\0\0\0", 4);
687                   put_value (buf, arg->values[j]);
688                 }
689             }
690           else
691             {
692               put_u32 (buf, 0);
693               put_value (buf, arg->values[0]);
694             }
695         }
696       break;
697
698     default:
699       NOT_REACHED ();
700     }
701 }
702
703 static void
704 put_optional_value (struct buf *buf, const struct pivot_value *value)
705 {
706   if (value)
707     {
708       put_byte (buf, 0x31);
709       put_value (buf, value);
710     }
711   else
712     put_byte (buf, 0x58);
713 }
714
715 static void
716 put_category (struct buf *buf, const struct pivot_category *c)
717 {
718   put_value (buf, c->name);
719   if (pivot_category_is_leaf (c))
720     {
721       put_bytes (buf, "\0\0\0", 3);
722       put_u32 (buf, 2);
723       put_u32 (buf, c->data_index);
724       put_u32 (buf, 0);
725     }
726   else
727     {
728       put_bytes (buf, "\0\0\1", 3);
729       put_u32 (buf, 0);         /* x23 */
730       put_u32 (buf, -1);
731       put_u32 (buf, c->n_subs);
732       for (size_t i = 0; i < c->n_subs; i++)
733         put_category (buf, c->subs[i]);
734     }
735 }
736
737 static void
738 put_y0 (struct buf *buf, const struct pivot_table *table)
739 {
740   put_u32 (buf, table->settings.epoch);
741   put_byte (buf, table->settings.decimal);
742   put_byte (buf, ',');
743 }
744
745 static void
746 put_custom_currency (struct buf *buf, const struct pivot_table *table)
747 {
748   put_u32 (buf, 5);
749   for (int i = 0; i < 5; i++)
750     {
751       enum fmt_type types[5] = { FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE };
752       char *cc = fmt_number_style_to_string (fmt_settings_get_style (
753                                                &table->settings, types[i]));
754       put_string (buf, cc);
755       free (cc);
756     }
757 }
758
759 static void
760 put_x1 (struct buf *buf, const struct pivot_table *table)
761 {
762   put_byte (buf, 0);            /* x14 */
763   put_byte (buf, table->show_title ? 1 : 10);
764   put_byte (buf, 0);            /* x16 */
765   put_byte (buf, 0);            /* lang */
766   put_show_values (buf, table->show_variables);
767   put_show_values (buf, table->show_values);
768   put_u32 (buf, -1);            /* x18 */
769   put_u32 (buf, -1);            /* x19 */
770   for (int i = 0; i < 17; i++)
771     put_byte (buf, 0);
772   put_bool (buf, false);        /* x20 */
773   put_byte (buf, table->show_caption);
774 }
775
776 static void
777 put_x2 (struct buf *buf)
778 {
779   put_u32 (buf, 0);             /* n-row-heights */
780   put_u32 (buf, 0);             /* n-style-map */
781   put_u32 (buf, 0);             /* n-styles */
782   put_u32 (buf, 0);
783 }
784
785 static void
786 put_y1 (struct buf *buf, const struct pivot_table *table)
787 {
788   put_string (buf, table->command_c);
789   put_string (buf, table->command_local);
790   put_string (buf, table->language);
791   put_string (buf, "UTF-8");    /* XXX */
792   put_string (buf, table->locale);
793   put_bytes (buf, "\0\0\1\1", 4);
794   put_y0 (buf, table);
795 }
796
797 static void
798 put_y2 (struct buf *buf, const struct pivot_table *table)
799 {
800   put_custom_currency (buf, table);
801   put_byte (buf, '.');
802   put_bool (buf, 0);
803 }
804
805 static void
806 put_x3 (struct buf *buf, const struct pivot_table *table)
807 {
808   put_byte (buf, 1);
809   put_byte (buf, 0);
810   put_byte (buf, 4);            /* x21 */
811   put_byte (buf, 0);
812   put_byte (buf, 0);
813   put_byte (buf, 0);
814   put_y1 (buf, table);
815   put_double (buf, table->small);
816   put_byte (buf, 1);
817   put_string (buf, table->dataset);
818   put_string (buf, table->datafile);
819   put_u32 (buf, 0);
820   put_u32 (buf, table->date);
821   put_u32 (buf, 0);
822   put_y2 (buf, table);
823 }
824
825 static uint32_t
826 encode_current_layer (const struct pivot_table *table)
827 {
828   uint32_t current_layer = 0;
829
830   const struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
831   for (size_t i = axis->n_dimensions - 1; i < axis->n_dimensions; i--)
832     {
833       const struct pivot_dimension *d = axis->dimensions[i];
834       current_layer = current_layer * d->n_leaves + table->current_layer[i];
835     }
836
837   return current_layer;
838 }
839
840 static void
841 put_light_table (struct buf *buf, uint64_t table_id,
842                  const struct pivot_table *table)
843 {
844   /* Header. */
845   put_bytes (buf, "\1\0", 2);
846   put_u32 (buf, 3);
847   put_bool (buf, true);
848   put_bool (buf, false);
849   put_bool (buf, table->rotate_inner_column_labels);
850   put_bool (buf, table->rotate_outer_row_labels);
851   put_bool (buf, true);
852   put_u32 (buf, 0x15);
853   put_u32 (buf, table->look->width_ranges[H][0]);
854   put_u32 (buf, table->look->width_ranges[H][1]);
855   put_u32 (buf, table->look->width_ranges[V][0]);
856   put_u32 (buf, table->look->width_ranges[V][1]);
857   put_u64 (buf, table_id);
858
859   /* Titles. */
860   put_value (buf, table->title);
861   put_value (buf, table->subtype);
862   put_optional_value (buf, table->title);
863   put_optional_value (buf, table->corner_text);
864   put_optional_value (buf, table->caption);
865
866   /* Footnotes. */
867   put_u32 (buf, table->n_footnotes);
868   for (size_t i = 0; i < table->n_footnotes; i++)
869     {
870       const struct pivot_footnote *f = table->footnotes[i];
871       put_value (buf, f->content);
872       put_optional_value (buf, f->marker);
873       put_u32 (buf, f->show ? 1 : -1);
874     }
875
876   /* Areas. */
877   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
878     {
879       const struct table_area_style *a = &table->look->areas[i];
880       put_byte (buf, i + 1);
881       put_byte (buf, 0x31);
882       put_string (buf, (a->font_style.typeface
883                         ? a->font_style.typeface
884                         : "SansSerif"));
885       put_float (buf, ceil (a->font_style.size * 1.33));
886       put_u32 (buf, ((a->font_style.bold ? 1 : 0)
887                      | (a->font_style.italic ? 2 : 0)));
888       put_bool (buf, a->font_style.underline);
889       put_halign (buf, a->cell_style.halign, 64173, 61453);
890       put_valign (buf, a->cell_style.valign);
891
892       put_color (buf, &a->font_style.fg[0]);
893       put_color (buf, &a->font_style.bg[0]);
894
895       bool alt
896         = (!cell_color_equal (&a->font_style.fg[0], &a->font_style.fg[1])
897            || !cell_color_equal (&a->font_style.bg[0], &a->font_style.bg[1]));
898       put_bool (buf, alt);
899       if (alt)
900         {
901           put_color (buf, &a->font_style.fg[1]);
902           put_color (buf, &a->font_style.bg[1]);
903         }
904       else
905         {
906           put_string (buf, "");
907           put_string (buf, "");
908         }
909
910       put_u32 (buf, a->cell_style.margin[H][0]);
911       put_u32 (buf, a->cell_style.margin[H][1]);
912       put_u32 (buf, a->cell_style.margin[V][0]);
913       put_u32 (buf, a->cell_style.margin[V][1]);
914     }
915
916   /* Borders. */
917   uint32_t borders_start = start_count (buf);
918   put_be32 (buf, 1);
919   put_be32 (buf, PIVOT_N_BORDERS);
920   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
921     {
922       const struct table_border_style *b = &table->look->borders[i];
923       put_be32 (buf, i);
924       put_be32 (buf, (b->stroke == TABLE_STROKE_NONE ? 0
925                       : b->stroke == TABLE_STROKE_SOLID ? 1
926                       : b->stroke == TABLE_STROKE_DASHED ? 2
927                       : b->stroke == TABLE_STROKE_THICK ? 3
928                       : b->stroke == TABLE_STROKE_THIN ? 4
929                       : 5));
930       put_be32 (buf, ((b->color.alpha << 24)
931                       | (b->color.r << 16)
932                       | (b->color.g << 8)
933                       | b->color.b));
934     }
935   put_bool (buf, table->show_grid_lines);
936   put_bytes (buf, "\0\0\0", 3);
937   end_count_u32 (buf, borders_start);
938
939   /* Print Settings. */
940   uint32_t ps_start = start_count (buf);
941   put_be32 (buf, 1);
942   put_bool (buf, table->look->print_all_layers);
943   put_bool (buf, table->look->paginate_layers);
944   put_bool (buf, table->look->shrink_to_fit[H]);
945   put_bool (buf, table->look->shrink_to_fit[V]);
946   put_bool (buf, table->look->top_continuation);
947   put_bool (buf, table->look->bottom_continuation);
948   put_be32 (buf, table->look->n_orphan_lines);
949   put_bestring (buf, table->look->continuation);
950   end_count_u32 (buf, ps_start);
951
952   /* Table Settings. */
953   uint32_t ts_start = start_count (buf);
954   put_be32 (buf, 1);
955   put_be32 (buf, 4);
956   put_be32 (buf, encode_current_layer (table));
957   put_bool (buf, table->look->omit_empty);
958   put_bool (buf, table->look->row_labels_in_corner);
959   put_bool (buf, !table->look->show_numeric_markers);
960   put_bool (buf, table->look->footnote_marker_superscripts);
961   put_byte (buf, 0);
962   uint32_t keep_start = start_count (buf);
963   put_be32 (buf, 0);            /* n-row-breaks */
964   put_be32 (buf, 0);            /* n-column-breaks */
965   put_be32 (buf, 0);            /* n-row-keeps */
966   put_be32 (buf, 0);            /* n-column-keeps */
967   put_be32 (buf, 0);            /* n-row-point-keeps */
968   put_be32 (buf, 0);            /* n-column-point-keeps */
969   end_count_be32 (buf, keep_start);
970   put_bestring (buf, table->notes);
971   put_bestring (buf, table->look->name);
972   for (size_t i = 0; i < 82; i++)
973     put_byte (buf, 0);
974   end_count_u32 (buf, ts_start);
975
976   /* Formats. */
977   put_u32 (buf, 0);             /* n-widths */
978   put_string (buf, "en_US.ISO_8859-1:1987"); /* XXX */
979   put_u32 (buf, 0);                /* XXX current-layer */
980   put_bool (buf, 0);
981   put_bool (buf, 0);
982   put_bool (buf, 1);
983   put_y0 (buf, table);
984   put_custom_currency (buf, table);
985   uint32_t formats_start = start_count (buf);
986   uint32_t x1_start = start_count (buf);
987   put_x1 (buf, table);
988   uint32_t x2_start = start_count (buf);
989   put_x2 (buf);
990   end_count_u32 (buf, x2_start);
991   end_count_u32 (buf, x1_start);
992   uint32_t x3_start = start_count (buf);
993   put_x3 (buf, table);
994   end_count_u32 (buf, x3_start);
995   end_count_u32 (buf, formats_start);
996
997   /* Dimensions. */
998   put_u32 (buf, table->n_dimensions);
999   int *x2 = xnmalloc (table->n_dimensions, sizeof *x2);
1000   for (size_t i = 0; i < table->axes[PIVOT_AXIS_LAYER].n_dimensions; i++)
1001     x2[i] = 2;
1002   for (size_t i = 0; i < table->axes[PIVOT_AXIS_ROW].n_dimensions; i++)
1003     x2[i + table->axes[PIVOT_AXIS_LAYER].n_dimensions] = 0;
1004   for (size_t i = 0; i < table->axes[PIVOT_AXIS_COLUMN].n_dimensions; i++)
1005     x2[i
1006        + table->axes[PIVOT_AXIS_LAYER].n_dimensions
1007        + table->axes[PIVOT_AXIS_ROW].n_dimensions] = 1;
1008   for (size_t i = 0; i < table->n_dimensions; i++)
1009     {
1010       const struct pivot_dimension *d = table->dimensions[i];
1011       put_value (buf, d->root->name);
1012       put_byte (buf, 0);        /* x1 */
1013       put_byte (buf, x2[i]);
1014       put_u32 (buf, 2);         /* x3 */
1015       put_bool (buf, !d->root->show_label);
1016       put_bool (buf, d->hide_all_labels);
1017       put_bool (buf, 1);
1018       put_u32 (buf, i);
1019
1020       put_u32 (buf, d->root->n_subs);
1021       for (size_t j = 0; j < d->root->n_subs; j++)
1022         put_category (buf, d->root->subs[j]);
1023     }
1024   free (x2);
1025
1026   /* Axes. */
1027   put_u32 (buf, table->axes[PIVOT_AXIS_LAYER].n_dimensions);
1028   put_u32 (buf, table->axes[PIVOT_AXIS_ROW].n_dimensions);
1029   put_u32 (buf, table->axes[PIVOT_AXIS_COLUMN].n_dimensions);
1030   for (size_t i = 0; i < table->axes[PIVOT_AXIS_LAYER].n_dimensions; i++)
1031     put_u32 (buf, table->axes[PIVOT_AXIS_LAYER].dimensions[i]->top_index);
1032   for (size_t i = 0; i < table->axes[PIVOT_AXIS_ROW].n_dimensions; i++)
1033     put_u32 (buf, table->axes[PIVOT_AXIS_ROW].dimensions[i]->top_index);
1034   for (size_t i = 0; i < table->axes[PIVOT_AXIS_COLUMN].n_dimensions; i++)
1035     put_u32 (buf, table->axes[PIVOT_AXIS_COLUMN].dimensions[i]->top_index);
1036
1037   /* Cells. */
1038   put_u32 (buf, hmap_count (&table->cells));
1039   const struct pivot_cell *cell;
1040   HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1041     {
1042       uint64_t index = 0;
1043       for (size_t j = 0; j < table->n_dimensions; j++)
1044         index = (table->dimensions[j]->n_leaves * index) + cell->idx[j];
1045       put_u64 (buf, index);
1046
1047       put_value (buf, cell->value);
1048     }
1049 }
1050
1051 static void
1052 spv_writer_put_table (struct spv_writer *w, const struct output_item *item)
1053 {
1054   int table_id = ++w->n_tables;
1055
1056   bool initial_depth = w->heading_depth;
1057   if (!initial_depth)
1058     spv_writer_open_file (w);
1059
1060   open_container (w, item, "vtb:table");
1061
1062   write_attr (w, "type", "table"); /* XXX */
1063   write_attr_format (w, "tableId", "%d", table_id);
1064   char *subtype = (item->table->subtype
1065                    ? pivot_value_to_string (item->table->subtype, item->table)
1066                    : xstrdup ("unknown"));
1067   write_attr (w, "subType", subtype);
1068   free (subtype);
1069
1070   start_elem (w, "vtb:tableStructure");
1071   start_elem (w, "vtb:dataPath");
1072   char *data_path = xasprintf ("%010d_lightTableData.bin", table_id);
1073   write_text (w, data_path);
1074   end_elem (w); /* vtb:dataPath */
1075   end_elem (w); /* vtb:tableStructure */
1076
1077   close_container (w);
1078
1079   if (!initial_depth)
1080     spv_writer_close_file (w, "");
1081
1082   struct buf buf = { NULL, 0, 0 };
1083   put_light_table (&buf, table_id, item->table);
1084   zip_writer_add_memory (w->zw, data_path, buf.data, buf.len);
1085   free (buf.data);
1086
1087   free (data_path);
1088 }
1089 \f
1090 void
1091 spv_writer_write (struct spv_writer *w, const struct output_item *item)
1092 {
1093   switch (item->type)
1094     {
1095     case OUTPUT_ITEM_CHART:
1096       {
1097         cairo_surface_t *surface = xr_draw_image_chart (
1098           item->chart,
1099           &(struct cell_color) CELL_COLOR_BLACK,
1100           &(struct cell_color) CELL_COLOR_WHITE);
1101         if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
1102           spv_writer_put_image (w, item, surface);
1103         cairo_surface_destroy (surface);
1104       }
1105       break;
1106
1107     case OUTPUT_ITEM_GROUP_OPEN:
1108       spv_writer_open_heading (w, item);
1109       break;
1110
1111     case OUTPUT_ITEM_GROUP_CLOSE:
1112       spv_writer_close_heading (w);
1113       break;
1114
1115     case OUTPUT_ITEM_IMAGE:
1116       spv_writer_put_image (w, item, item->image);
1117       break;
1118
1119     case OUTPUT_ITEM_MESSAGE:
1120       spv_writer_put_text (
1121         w, message_item_to_text_item (output_item_ref (item)));
1122       break;
1123
1124     case OUTPUT_ITEM_PAGE_BREAK:
1125       w->need_page_break = true;
1126       break;
1127
1128     case OUTPUT_ITEM_PAGE_SETUP:
1129       page_setup_destroy (w->page_setup);
1130       w->page_setup = page_setup_clone (item->page_setup);
1131       break;
1132
1133     case OUTPUT_ITEM_TABLE:
1134       spv_writer_put_table (w, item);
1135       break;
1136
1137     case OUTPUT_ITEM_TEXT:
1138       spv_writer_put_text (w, output_item_ref (item));
1139       break;
1140     }
1141 }