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:
363 pivot_table_dump (item->table, indentation + 1);
366 case OUTPUT_ITEM_TEXT:
367 printf ("text %s \"%s\"\n",
368 text_item_subtype_to_string (item->text.subtype),
369 pivot_value_to_string_defaults (item->text.content));
375 output_iterator_init (struct output_iterator *iter,
376 const struct output_item *item)
378 *iter = (struct output_iterator) OUTPUT_ITERATOR_INIT (item);
382 output_iterator_destroy (struct output_iterator *iter)
388 iter->n = iter->allocated = 0;
393 output_iterator_next (struct output_iterator *iter)
395 const struct output_item *cur = iter->cur;
398 if (cur->type == OUTPUT_ITEM_GROUP && cur->group.n_children > 0)
400 if (iter->n >= iter->allocated)
401 iter->nodes = x2nrealloc (iter->nodes, &iter->allocated,
402 sizeof *iter->nodes);
403 iter->nodes[iter->n++] = (struct output_iterator_node) {
407 iter->cur = cur->group.children[0];
411 for (; iter->n > 0; iter->n--)
413 struct output_iterator_node *node = &iter->nodes[iter->n - 1];
414 if (++node->idx < node->group->group.n_children)
416 iter->cur = node->group->group.children[node->idx];
422 output_iterator_destroy (iter);
427 chart_item_create (struct chart *chart)
429 struct output_item *item = xmalloc (sizeof *item);
430 *item = (struct output_item) {
431 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
438 group_item_create (const char *command_name, const char *label)
440 return group_item_create_nocopy (
441 xstrdup_if_nonnull (command_name),
442 xstrdup_if_nonnull (label));
446 group_item_create_nocopy (char *command_name, char *label)
448 struct output_item *item = xmalloc (sizeof *item);
449 *item = (struct output_item) {
450 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP),
452 .command_name = command_name,
457 /* Returns a new group item suitable as the root node of an output document. A
458 root node is a group whose own properties are mostly disregarded. Instead
459 of having root nodes, it would make just as much sense to just keep around
460 arrays of nodes that would serve as the top level of an output document, but
461 we'd need more special cases instead of just using the existing support for
464 root_item_create (void)
466 return group_item_create ("", _("Output"));
469 /* Returns a clone of OLD but without any of its children. */
471 group_item_clone_empty (const struct output_item *old)
473 return output_item_clone_common (old);
476 /* Adds CHILD as a child of group item PARENT. */
478 group_item_add_child (struct output_item *parent, struct output_item *child)
480 assert (parent->type == OUTPUT_ITEM_GROUP);
481 assert (!output_item_is_shared (parent));
482 if (parent->group.n_children >= parent->group.allocated_children)
483 parent->group.children = x2nrealloc (parent->group.children,
484 &parent->group.allocated_children,
485 sizeof *parent->group.children);
486 parent->group.children[parent->group.n_children++] = child;
489 /* Creates and returns a new output item containing IMAGE. Takes ownership of
492 image_item_create (cairo_surface_t *image)
494 struct output_item *item = xmalloc (sizeof *item);
495 *item = (struct output_item) {
496 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
503 message_item_create (const struct msg *msg)
505 struct output_item *item = xmalloc (sizeof *item);
506 *item = (struct output_item) {
507 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
508 .message = msg_dup (msg),
514 message_item_get_msg (const struct output_item *item)
516 assert (item->type == OUTPUT_ITEM_MESSAGE);
517 return item->message;
521 message_item_to_text_item (struct output_item *message_item)
523 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
524 struct output_item *text_item = text_item_create_nocopy (
526 msg_to_string (message_item->message),
527 xstrdup (output_item_get_label (message_item)));
528 output_item_unref (message_item);
533 page_break_item_create (void)
535 struct output_item *item = xmalloc (sizeof *item);
536 *item = (struct output_item) {
537 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
542 /* Returns a new output_item for rendering TABLE. Takes ownership of
545 table_item_create (struct pivot_table *table)
547 pivot_table_assign_label_depth (table);
549 struct output_item *item = xmalloc (sizeof *item);
550 *item = (struct output_item) {
551 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
552 .command_name = xstrdup_if_nonnull (table->command_c),
558 /* Creates and returns a new text item containing TEXT and the specified
559 SUBTYPE and LABEL. The new text item takes ownership of TEXT and LABEL. If
560 LABEL is NULL, uses the default label for SUBTYPE. */
562 text_item_create_nocopy (enum text_item_subtype subtype,
563 char *text, char *label)
565 return text_item_create_value (subtype,
566 pivot_value_new_user_text_nocopy (text),
570 /* Creates and returns a new text item containing a copy of TEXT and the
571 specified SUBTYPE and LABEL. The caller retains ownership of TEXT and
572 LABEL. If LABEL is null, uses a default label for SUBTYPE. */
574 text_item_create (enum text_item_subtype subtype, const char *text,
577 return text_item_create_nocopy (subtype, xstrdup (text),
578 xstrdup_if_nonnull (label));
581 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
582 Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label
585 text_item_create_value (enum text_item_subtype subtype,
586 struct pivot_value *value, char *label)
588 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
590 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
593 ex->font_style = xmalloc (sizeof *value->ex->font_style);
594 *ex->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
597 free (ex->font_style->typeface);
598 ex->font_style->typeface = xstrdup ("Monospaced");
601 struct output_item *item = XZALLOC (struct output_item);
602 *item = (struct output_item) {
603 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
604 .command_name = xstrdup_if_nonnull (output_get_command_name ()),
606 .text = { .subtype = subtype, .content = value },
611 /* Returns ITEM's subtype. */
612 enum text_item_subtype
613 text_item_get_subtype (const struct output_item *item)
615 assert (item->type == OUTPUT_ITEM_TEXT);
616 return item->text.subtype;
619 /* Returns ITEM's text, which the caller must eventually free. */
621 text_item_get_plain_text (const struct output_item *item)
623 assert (item->type == OUTPUT_ITEM_TEXT);
624 return pivot_value_to_string_defaults (item->text.content);
628 nullable_font_style_equal (const struct font_style *a,
629 const struct font_style *b)
631 return a && b ? font_style_equal (a, b) : !a && !b;
634 /* Attempts to append the text in SRC to DST. If successful, returns true,
637 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
640 DST must not be shared. */
642 text_item_append (struct output_item *dst, const struct output_item *src)
644 assert (dst->type == OUTPUT_ITEM_TEXT);
645 assert (src->type == OUTPUT_ITEM_TEXT);
646 assert (!output_item_is_shared (dst));
648 enum text_item_subtype ds = dst->text.subtype;
649 enum text_item_subtype ss = src->text.subtype;
651 struct pivot_value *dc = dst->text.content;
652 const struct pivot_value *sc = src->text.content;
655 || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
656 || strcmp (output_item_get_label (dst), output_item_get_label (src))
657 || !nullable_font_style_equal (dc->ex ? dc->ex->font_style : NULL,
658 sc->ex ? sc->ex->font_style : NULL)
659 || (dc->ex && dc->ex->font_style && dc->ex->font_style->markup)
660 || sc->type != PIVOT_VALUE_TEXT
661 || dc->type != PIVOT_VALUE_TEXT)
665 /* Calculate new text. */
666 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
668 /* Free the old text. */
669 free (dc->text.local);
670 if (dc->text.c != dc->text.local)
672 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
675 /* Put in new text. */
676 dc->text.local = new_text;
677 dc->text.c = new_text;
678 dc->text.id = new_text;
684 static const struct pivot_table_look *
685 text_item_table_look (void)
687 static struct pivot_table_look *look;
690 look = pivot_table_look_new_builtin_default ();
692 for (int a = 0; a < PIVOT_N_AREAS; a++)
693 memset (look->areas[a].cell_style.margin, 0,
694 sizeof look->areas[a].cell_style.margin);
695 for (int b = 0; b < PIVOT_N_BORDERS; b++)
696 look->borders[b].stroke = TABLE_STROKE_NONE;
702 text_item_to_table_item (struct output_item *text_item)
704 assert (text_item->type == OUTPUT_ITEM_TEXT);
706 /* Create a new table whose contents come from TEXT_ITEM. */
707 struct pivot_table *table = pivot_table_create__ (NULL, "Text");
708 pivot_table_set_look (table, text_item_table_look ());
710 struct pivot_dimension *d = pivot_dimension_create (
711 table, PIVOT_AXIS_ROW, N_("Text"));
712 d->hide_all_labels = true;
713 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
715 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
717 /* Free TEXT_ITEM. */
718 output_item_unref (text_item);
720 /* Return a new output item. */
721 return table_item_create (table);
725 text_item_subtype_to_string (enum text_item_subtype subtype)
729 case TEXT_ITEM_PAGE_TITLE:
730 return _("Page Title");
732 case TEXT_ITEM_TITLE:
735 case TEXT_ITEM_SYNTAX:
745 spv_info_destroy (struct spv_info *spv_info)
749 zip_reader_unref (spv_info->zip_reader);
750 free (spv_info->structure_member);
751 free (spv_info->xml_member);
752 free (spv_info->bin_member);
753 free (spv_info->png_member);
759 spv_info_clone (const struct spv_info *old)
764 struct spv_info *new = xmalloc (sizeof *new);
765 *new = (struct spv_info) {
766 .zip_reader = old->zip_reader ? zip_reader_ref (old->zip_reader) : NULL,
768 .structure_member = xstrdup_if_nonnull (old->structure_member),
769 .xml_member = xstrdup_if_nonnull (old->xml_member),
770 .bin_member = xstrdup_if_nonnull (old->bin_member),
771 .png_member = xstrdup_if_nonnull (old->png_member),
777 spv_info_get_members (const struct spv_info *spv_info, const char **members,
778 size_t allocated_members)
784 spv_info->structure_member,
785 spv_info->xml_member,
786 spv_info->bin_member,
787 spv_info->png_member,
790 for (size_t i = 0; i < sizeof s / sizeof *s; i++)
791 if (s[i] && n < allocated_members)