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