1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2011 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/output-item.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/cast.h"
26 #include "libpspp/message.h"
27 #include "libpspp/str.h"
28 #include "libpspp/zip-reader.h"
29 #include "output/chart.h"
30 #include "output/driver.h"
31 #include "output/pivot-table.h"
33 #include "gl/xalloc.h"
36 #define _(msgid) gettext (msgid)
37 #define N_(msgid) msgid
40 output_item_type_to_string (enum output_item_type type)
44 case OUTPUT_ITEM_CHART: return "chart";
45 case OUTPUT_ITEM_GROUP: return "group";
46 case OUTPUT_ITEM_IMAGE: return "image";
47 case OUTPUT_ITEM_MESSAGE: return "message";
48 case OUTPUT_ITEM_PAGE_BREAK: return "page break";
49 case OUTPUT_ITEM_TABLE: return "table";
50 case OUTPUT_ITEM_TEXT: return "text";
56 #define OUTPUT_ITEM_INITIALIZER(TYPE) .type = TYPE, .ref_cnt = 1, .show = true
58 /* Increases ITEM's reference count, indicating that it has an additional
59 owner. An output item that is shared among multiple owners must not be
62 output_item_ref (const struct output_item *item_)
64 struct output_item *item = CONST_CAST (struct output_item *, item_);
65 assert (item->ref_cnt > 0);
70 /* Decreases ITEM's reference count, indicating that it has one fewer owner.
71 If ITEM no longer has any owners, it is freed. */
73 output_item_unref (struct output_item *item)
77 assert (item->ref_cnt > 0);
78 if (--item->ref_cnt == 0)
82 case OUTPUT_ITEM_CHART:
83 chart_unref (item->chart);
86 case OUTPUT_ITEM_GROUP:
87 for (size_t i = 0; i < item->group.n_children; i++)
88 output_item_unref (item->group.children[i]);
89 free (item->group.children);
92 case OUTPUT_ITEM_IMAGE:
93 cairo_surface_destroy (item->image);
96 case OUTPUT_ITEM_MESSAGE:
97 msg_destroy (item->message);
100 case OUTPUT_ITEM_PAGE_BREAK:
103 case OUTPUT_ITEM_TABLE:
104 pivot_table_unref (item->table);
107 case OUTPUT_ITEM_TEXT:
108 pivot_value_destroy (item->text.content);
113 free (item->command_name);
114 free (item->cached_label);
115 spv_info_destroy (item->spv_info);
121 /* Returns true if ITEM has more than one owner. An output item that is shared
122 among multiple owners must not be modified. */
124 output_item_is_shared (const struct output_item *item)
126 return item->ref_cnt > 1;
129 /* Returns a clone of OLD, without initializing type-specific fields. */
130 static struct output_item *
131 output_item_clone_common (const struct output_item *old)
133 struct output_item *new = xmalloc (sizeof *new);
134 *new = (struct output_item) {
136 .label = xstrdup_if_nonnull (old->label),
137 .command_name = xstrdup_if_nonnull (old->command_name),
140 .spv_info = spv_info_clone (old->spv_info),
146 output_item_unshare (struct output_item *old)
148 assert (old->ref_cnt > 0);
149 if (!output_item_is_shared (old))
151 output_item_unref (old);
153 struct output_item *new = output_item_clone_common (old);
156 case OUTPUT_ITEM_CHART:
157 new->chart = chart_ref (old->chart);
160 case OUTPUT_ITEM_GROUP:
161 new->group.children = xmemdup (
163 old->group.n_children * sizeof *old->group.children);
164 new->group.n_children = new->group.allocated_children
165 = old->group.n_children;
167 for (size_t i = 0; i < new->group.n_children; i++)
168 new->group.children[i] = output_item_ref (new->group.children[i]);
171 case OUTPUT_ITEM_IMAGE:
172 new->image = cairo_surface_reference (old->image);
175 case OUTPUT_ITEM_MESSAGE:
176 new->message = msg_dup (old->message);
179 case OUTPUT_ITEM_PAGE_BREAK:
182 case OUTPUT_ITEM_TABLE:
183 new->table = pivot_table_ref (old->table);
186 case OUTPUT_ITEM_TEXT:
187 new->text.subtype = old->text.subtype;
188 new->text.content = pivot_value_clone (old->text.content);
195 output_item_submit (struct output_item *item)
197 output_submit (item);
200 /* If ROOT is a group item, submits each of its children, but not ROOT itself.
201 This is useful if ROOT is being used as a container for output items but it
202 has no significance itself.
204 If ROOT is not a group, submits it the normal way.
206 Takes ownership of ROOT. */
208 output_item_submit_children (struct output_item *root)
210 assert (!output_item_is_shared (root));
211 if (root->type == OUTPUT_ITEM_GROUP)
213 for (size_t i = 0; i < root->group.n_children; i++)
214 output_submit (root->group.children[i]);
215 root->group.n_children = 0;
216 output_item_unref (root);
219 output_submit (root);
222 /* Returns the label for ITEM, which the caller must not modify or free. */
224 output_item_get_label (const struct output_item *item)
231 case OUTPUT_ITEM_CHART:
232 return item->chart->title ? item->chart->title : _("Chart");
234 case OUTPUT_ITEM_GROUP:
235 return item->command_name ? item->command_name : _("Group");
237 case OUTPUT_ITEM_IMAGE:
240 case OUTPUT_ITEM_MESSAGE:
241 return (item->message->severity == MSG_S_ERROR ? _("Error")
242 : item->message->severity == MSG_S_WARNING ? _("Warning")
245 case OUTPUT_ITEM_PAGE_BREAK:
246 return _("Page Break");
248 case OUTPUT_ITEM_TABLE:
249 if (!item->cached_label)
251 if (!item->table->title)
254 struct output_item *item_rw = CONST_CAST (struct output_item *, item);
255 item_rw->cached_label = pivot_value_to_string (item->table->title,
258 return item->cached_label;
260 case OUTPUT_ITEM_TEXT:
261 return text_item_subtype_to_string (item->text.subtype);
267 /* Sets the label for ITEM to LABEL. The caller retains ownership of LABEL.
268 If LABEL is nonnull, it overrides any previously set label and the default
269 label. If LABEL is null, ITEM will now use its default label.
271 ITEM must not be shared. */
273 output_item_set_label (struct output_item *item, const char *label)
275 output_item_set_label_nocopy (item, xstrdup_if_nonnull (label));
278 /* Sets the label for ITEM to LABEL, transferring ownership of LABEL to ITEM.
279 If LABEL is nonnull, it overrides any previously set label and the default
280 label. If LABEL is null, ITEM will now use its default label.
282 ITEM must not be shared. */
284 output_item_set_label_nocopy (struct output_item *item, char *label)
286 assert (!output_item_is_shared (item));
292 output_item_set_command_name (struct output_item *item, const char *name)
294 output_item_set_command_name_nocopy (item, xstrdup_if_nonnull (name));
298 output_item_set_command_name_nocopy (struct output_item *item, char *name)
300 free (item->command_name);
301 item->command_name = name;
305 output_item_get_subtype (const struct output_item *item)
307 return (item->type == OUTPUT_ITEM_TABLE
308 ? pivot_value_to_string (item->table->subtype, item->table)
313 output_item_add_spv_info (struct output_item *item)
315 assert (!output_item_is_shared (item));
317 item->spv_info = xzalloc (sizeof *item->spv_info);
321 indent (int indentation)
323 for (int i = 0; i < indentation * 2; i++)
328 output_item_dump (const struct output_item *item, int indentation)
330 indent (indentation);
332 printf ("label=\"%s\" ", item->label);
333 if (item->command_name)
334 printf ("command=\"%s\" ", item->command_name);
336 printf ("(%s) ", item->type == OUTPUT_ITEM_GROUP ? "collapsed" : "hidden");
340 case OUTPUT_ITEM_CHART:
341 printf ("chart \"%s\"\n", item->chart->title ? item->chart->title : "");
344 case OUTPUT_ITEM_GROUP:
346 for (size_t i = 0; i < item->group.n_children; i++)
347 output_item_dump (item->group.children[i], indentation + 1);
350 case OUTPUT_ITEM_IMAGE:
354 case OUTPUT_ITEM_MESSAGE:
355 printf ("message\n");
358 case OUTPUT_ITEM_PAGE_BREAK:
359 printf ("page break\n");
362 case OUTPUT_ITEM_TABLE:
364 pivot_table_dump (item->table, indentation + 1);
367 case OUTPUT_ITEM_TEXT:
369 char *s = pivot_value_to_string (item->text.content, NULL);
370 printf ("text %s \"%s\"\n",
371 text_item_subtype_to_string (item->text.subtype), s);
379 output_iterator_init (struct output_iterator *iter,
380 const struct output_item *item)
382 *iter = (struct output_iterator) OUTPUT_ITERATOR_INIT (item);
386 output_iterator_destroy (struct output_iterator *iter)
392 iter->n = iter->allocated = 0;
397 output_iterator_next (struct output_iterator *iter)
399 const struct output_item *cur = iter->cur;
402 if (cur->type == OUTPUT_ITEM_GROUP && cur->group.n_children > 0)
404 if (iter->n >= iter->allocated)
405 iter->nodes = x2nrealloc (iter->nodes, &iter->allocated,
406 sizeof *iter->nodes);
407 iter->nodes[iter->n++] = (struct output_iterator_node) {
411 iter->cur = cur->group.children[0];
415 for (; iter->n > 0; iter->n--)
417 struct output_iterator_node *node = &iter->nodes[iter->n - 1];
418 if (++node->idx < node->group->group.n_children)
420 iter->cur = node->group->group.children[node->idx];
426 output_iterator_destroy (iter);
431 chart_item_create (struct chart *chart)
433 struct output_item *item = xmalloc (sizeof *item);
434 *item = (struct output_item) {
435 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
442 group_item_create (const char *command_name, const char *label)
444 return group_item_create_nocopy (
445 xstrdup_if_nonnull (command_name),
446 xstrdup_if_nonnull (label));
450 group_item_create_nocopy (char *command_name, char *label)
452 struct output_item *item = xmalloc (sizeof *item);
453 *item = (struct output_item) {
454 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP),
456 .command_name = command_name,
461 /* Returns a new group item suitable as the root node of an output document. A
462 root node is a group whose own properties are mostly disregarded. Instead
463 of having root nodes, it would make just as much sense to just keep around
464 arrays of nodes that would serve as the top level of an output document, but
465 we'd need more special cases instead of just using the existing support for
468 root_item_create (void)
470 return group_item_create ("", _("Output"));
473 /* Returns a clone of OLD but without any of its children. */
475 group_item_clone_empty (const struct output_item *old)
477 return output_item_clone_common (old);
480 /* Adds CHILD as a child of group item PARENT. */
482 group_item_add_child (struct output_item *parent, struct output_item *child)
484 assert (parent->type == OUTPUT_ITEM_GROUP);
485 assert (!output_item_is_shared (parent));
486 if (parent->group.n_children >= parent->group.allocated_children)
487 parent->group.children = x2nrealloc (parent->group.children,
488 &parent->group.allocated_children,
489 sizeof *parent->group.children);
490 parent->group.children[parent->group.n_children++] = child;
493 /* Creates and returns a new output item containing IMAGE. Takes ownership of
496 image_item_create (cairo_surface_t *image)
498 struct output_item *item = xmalloc (sizeof *item);
499 *item = (struct output_item) {
500 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
507 message_item_create (const struct msg *msg)
509 struct output_item *item = xmalloc (sizeof *item);
510 *item = (struct output_item) {
511 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
512 .message = msg_dup (msg),
518 message_item_get_msg (const struct output_item *item)
520 assert (item->type == OUTPUT_ITEM_MESSAGE);
521 return item->message;
525 message_item_to_text_item (struct output_item *message_item)
527 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
528 struct output_item *text_item = text_item_create_nocopy (
530 msg_to_string (message_item->message),
531 xstrdup (output_item_get_label (message_item)));
532 output_item_unref (message_item);
537 page_break_item_create (void)
539 struct output_item *item = xmalloc (sizeof *item);
540 *item = (struct output_item) {
541 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
546 /* Returns a new output_item for rendering TABLE. Takes ownership of
549 table_item_create (struct pivot_table *table)
551 pivot_table_assign_label_depth (table);
553 struct output_item *item = xmalloc (sizeof *item);
554 *item = (struct output_item) {
555 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
556 .command_name = xstrdup_if_nonnull (table->command_c),
562 /* Creates and returns a new text item containing TEXT and the specified
563 SUBTYPE and LABEL. The new text item takes ownership of TEXT and LABEL. If
564 LABEL is NULL, uses the default label for SUBTYPE. */
566 text_item_create_nocopy (enum text_item_subtype subtype,
567 char *text, char *label)
569 return text_item_create_value (subtype,
570 pivot_value_new_user_text_nocopy (text),
574 /* Creates and returns a new text item containing a copy of TEXT and the
575 specified SUBTYPE and LABEL. The caller retains ownership of TEXT and
576 LABEL. If LABEL is null, uses a default label for SUBTYPE. */
578 text_item_create (enum text_item_subtype subtype, const char *text,
581 return text_item_create_nocopy (subtype, xstrdup (text),
582 xstrdup_if_nonnull (label));
585 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
586 Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label
589 text_item_create_value (enum text_item_subtype subtype,
590 struct pivot_value *value, char *label)
592 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
594 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
597 ex->font_style = xmalloc (sizeof *value->ex->font_style);
598 *ex->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
601 free (ex->font_style->typeface);
602 ex->font_style->typeface = xstrdup ("Monospaced");
605 struct output_item *item = XZALLOC (struct output_item);
606 *item = (struct output_item) {
607 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
608 .command_name = xstrdup_if_nonnull (output_get_command_name ()),
610 .text = { .subtype = subtype, .content = value },
615 /* Returns ITEM's subtype. */
616 enum text_item_subtype
617 text_item_get_subtype (const struct output_item *item)
619 assert (item->type == OUTPUT_ITEM_TEXT);
620 return item->text.subtype;
623 /* Returns ITEM's text, which the caller must eventually free. */
625 text_item_get_plain_text (const struct output_item *item)
627 assert (item->type == OUTPUT_ITEM_TEXT);
628 return pivot_value_to_string (item->text.content, NULL);
632 nullable_font_style_equal (const struct font_style *a,
633 const struct font_style *b)
635 return a && b ? font_style_equal (a, b) : !a && !b;
638 /* Attempts to append the text in SRC to DST. If successful, returns true,
641 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
644 DST must not be shared. */
646 text_item_append (struct output_item *dst, const struct output_item *src)
648 assert (dst->type == OUTPUT_ITEM_TEXT);
649 assert (src->type == OUTPUT_ITEM_TEXT);
650 assert (!output_item_is_shared (dst));
652 enum text_item_subtype ds = dst->text.subtype;
653 enum text_item_subtype ss = src->text.subtype;
655 struct pivot_value *dc = dst->text.content;
656 const struct pivot_value *sc = src->text.content;
659 || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
660 || strcmp (output_item_get_label (dst), output_item_get_label (src))
661 || !nullable_font_style_equal (dc->ex ? dc->ex->font_style : NULL,
662 sc->ex ? sc->ex->font_style : NULL)
663 || (dc->ex && dc->ex->font_style && dc->ex->font_style->markup)
664 || sc->type != PIVOT_VALUE_TEXT
665 || dc->type != PIVOT_VALUE_TEXT)
669 /* Calculate new text. */
670 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
672 /* Free the old text. */
673 free (dc->text.local);
674 if (dc->text.c != dc->text.local)
676 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
679 /* Put in new text. */
680 dc->text.local = new_text;
681 dc->text.c = new_text;
682 dc->text.id = new_text;
688 static const struct pivot_table_look *
689 text_item_table_look (void)
691 static struct pivot_table_look *look;
694 look = pivot_table_look_new_builtin_default ();
696 for (int a = 0; a < PIVOT_N_AREAS; a++)
697 memset (look->areas[a].cell_style.margin, 0,
698 sizeof look->areas[a].cell_style.margin);
699 for (int b = 0; b < PIVOT_N_BORDERS; b++)
700 look->borders[b].stroke = TABLE_STROKE_NONE;
706 text_item_to_table_item (struct output_item *text_item)
708 assert (text_item->type == OUTPUT_ITEM_TEXT);
710 /* Create a new table whose contents come from TEXT_ITEM. */
711 struct pivot_table *table = pivot_table_create__ (NULL, "Text");
712 pivot_table_set_look (table, text_item_table_look ());
714 struct pivot_dimension *d = pivot_dimension_create (
715 table, PIVOT_AXIS_ROW, N_("Text"));
716 d->hide_all_labels = true;
717 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
719 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
721 /* Free TEXT_ITEM. */
722 output_item_unref (text_item);
724 /* Return a new output item. */
725 return table_item_create (table);
729 text_item_subtype_to_string (enum text_item_subtype subtype)
733 case TEXT_ITEM_PAGE_TITLE:
734 return _("Page Title");
736 case TEXT_ITEM_TITLE:
739 case TEXT_ITEM_SYNTAX:
749 spv_info_destroy (struct spv_info *spv_info)
753 zip_reader_unref (spv_info->zip_reader);
754 free (spv_info->structure_member);
755 free (spv_info->xml_member);
756 free (spv_info->bin_member);
757 free (spv_info->png_member);
763 spv_info_clone (const struct spv_info *old)
768 struct spv_info *new = xmalloc (sizeof *new);
769 *new = (struct spv_info) {
770 .zip_reader = old->zip_reader ? zip_reader_ref (old->zip_reader) : NULL,
772 .structure_member = xstrdup_if_nonnull (old->structure_member),
773 .xml_member = xstrdup_if_nonnull (old->xml_member),
774 .bin_member = xstrdup_if_nonnull (old->bin_member),
775 .png_member = xstrdup_if_nonnull (old->png_member),
781 spv_info_get_members (const struct spv_info *spv_info, const char **members,
782 size_t allocated_members)
788 spv_info->structure_member,
789 spv_info->xml_member,
790 spv_info->bin_member,
791 spv_info->png_member,
794 for (size_t i = 0; i < sizeof s / sizeof *s; i++)
795 if (s[i] && n < allocated_members)