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