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]);
91 case OUTPUT_ITEM_IMAGE:
92 cairo_surface_destroy (item->image);
95 case OUTPUT_ITEM_MESSAGE:
96 msg_destroy (item->message);
99 case OUTPUT_ITEM_PAGE_BREAK:
102 case OUTPUT_ITEM_TABLE:
103 pivot_table_unref (item->table);
106 case OUTPUT_ITEM_TEXT:
107 pivot_value_destroy (item->text.content);
112 free (item->command_name);
113 free (item->cached_label);
114 spv_info_destroy (item->spv_info);
120 /* Returns true if ITEM has more than one owner. An output item that is shared
121 among multiple owners must not be modified. */
123 output_item_is_shared (const struct output_item *item)
125 return item->ref_cnt > 1;
128 /* Returns a clone of OLD, without initializing type-specific fields. */
129 static struct output_item *
130 output_item_clone_common (const struct output_item *old)
132 struct output_item *new = xmalloc (sizeof *new);
133 *new = (struct output_item) {
135 .label = xstrdup_if_nonnull (old->label),
136 .command_name = xstrdup_if_nonnull (old->command_name),
139 .spv_info = spv_info_clone (old->spv_info),
145 output_item_unshare (struct output_item *old)
147 assert (old->ref_cnt > 0);
148 if (!output_item_is_shared (old))
150 output_item_unref (old);
152 struct output_item *new = output_item_clone_common (old);
155 case OUTPUT_ITEM_CHART:
156 new->chart = chart_ref (old->chart);
159 case OUTPUT_ITEM_GROUP:
160 new->group.children = xmemdup (
162 old->group.n_children * sizeof *old->group.children);
163 new->group.n_children = new->group.allocated_children
164 = old->group.n_children;
166 for (size_t i = 0; i < new->group.n_children; i++)
167 output_item_ref (new->group.children[i]);
170 case OUTPUT_ITEM_IMAGE:
171 new->image = cairo_surface_reference (old->image);
174 case OUTPUT_ITEM_MESSAGE:
175 new->message = msg_dup (old->message);
178 case OUTPUT_ITEM_PAGE_BREAK:
181 case OUTPUT_ITEM_TABLE:
182 new->table = pivot_table_ref (old->table);
185 case OUTPUT_ITEM_TEXT:
186 new->text.subtype = old->text.subtype;
187 new->text.content = pivot_value_clone (old->text.content);
194 output_item_submit (struct output_item *item)
196 output_submit (item);
199 /* If ROOT is a group item, submits each of its children, but not ROOT itself.
200 This is useful if ROOT is being used as a container for output items but it
201 has no significance itself.
203 If ROOT is not a group, submits it the normal way.
205 Takes ownership of ROOT. */
207 output_item_submit_children (struct output_item *root)
209 assert (!output_item_is_shared (root));
210 if (root->type == OUTPUT_ITEM_GROUP)
212 for (size_t i = 0; i < root->group.n_children; i++)
213 output_submit (root->group.children[i]);
214 root->group.n_children = 0;
215 output_item_unref (root);
218 output_submit (root);
221 /* Returns the label for ITEM, which the caller must not modify or free. */
223 output_item_get_label (const struct output_item *item)
230 case OUTPUT_ITEM_CHART:
231 return item->chart->title ? item->chart->title : _("Chart");
233 case OUTPUT_ITEM_GROUP:
234 return item->command_name ? item->command_name : _("Group");
236 case OUTPUT_ITEM_IMAGE:
239 case OUTPUT_ITEM_MESSAGE:
240 return (item->message->severity == MSG_S_ERROR ? _("Error")
241 : item->message->severity == MSG_S_WARNING ? _("Warning")
244 case OUTPUT_ITEM_PAGE_BREAK:
245 return _("Page Break");
247 case OUTPUT_ITEM_TABLE:
248 if (!item->cached_label)
250 if (!item->table->title)
253 struct output_item *item_rw = CONST_CAST (struct output_item *, item);
254 item_rw->cached_label = pivot_value_to_string (item->table->title,
257 return item->cached_label;
259 case OUTPUT_ITEM_TEXT:
260 return text_item_subtype_to_string (item->text.subtype);
266 /* Sets the label for ITEM to LABEL. The caller retains ownership of LABEL.
267 If LABEL is nonnull, it overrides any previously set label and the default
268 label. If LABEL is null, ITEM will now use its default label.
270 ITEM must not be shared. */
272 output_item_set_label (struct output_item *item, const char *label)
274 output_item_set_label_nocopy (item, xstrdup_if_nonnull (label));
277 /* Sets the label for ITEM to LABEL, transferring ownership of LABEL to ITEM.
278 If LABEL is nonnull, it overrides any previously set label and the default
279 label. If LABEL is null, ITEM will now use its default label.
281 ITEM must not be shared. */
283 output_item_set_label_nocopy (struct output_item *item, char *label)
285 assert (!output_item_is_shared (item));
291 output_item_set_command_name (struct output_item *item, const char *name)
293 output_item_set_command_name_nocopy (item, xstrdup_if_nonnull (name));
297 output_item_set_command_name_nocopy (struct output_item *item, char *name)
299 free (item->command_name);
300 item->command_name = name;
304 output_item_get_subtype (const struct output_item *item)
306 return (item->type == OUTPUT_ITEM_TABLE
307 ? pivot_value_to_string (item->table->subtype, item->table)
312 output_item_add_spv_info (struct output_item *item)
314 assert (!output_item_is_shared (item));
316 item->spv_info = xzalloc (sizeof *item->spv_info);
320 indent (int indentation)
322 for (int i = 0; i < indentation * 2; i++)
327 output_item_dump (const struct output_item *item, int indentation)
329 indent (indentation);
331 printf ("label=\"%s\" ", item->label);
332 if (item->command_name)
333 printf ("command=\"%s\" ", item->command_name);
335 printf ("(%s) ", item->type == OUTPUT_ITEM_GROUP ? "collapsed" : "hidden");
339 case OUTPUT_ITEM_CHART:
340 printf ("chart \"%s\"\n", item->chart->title ? item->chart->title : "");
343 case OUTPUT_ITEM_GROUP:
345 for (size_t i = 0; i < item->group.n_children; i++)
346 output_item_dump (item->group.children[i], indentation + 1);
349 case OUTPUT_ITEM_IMAGE:
353 case OUTPUT_ITEM_MESSAGE:
354 printf ("message\n");
357 case OUTPUT_ITEM_PAGE_BREAK:
358 printf ("page break\n");
361 case OUTPUT_ITEM_TABLE:
362 pivot_table_dump (item->table, indentation + 1);
365 case OUTPUT_ITEM_TEXT:
366 printf ("text %s \"%s\"\n",
367 text_item_subtype_to_string (item->text.subtype),
368 pivot_value_to_string_defaults (item->text.content));
374 output_iterator_init (struct output_iterator *iter,
375 const struct output_item *item)
377 *iter = (struct output_iterator) OUTPUT_ITERATOR_INIT (item);
381 output_iterator_destroy (struct output_iterator *iter)
387 iter->n = iter->allocated = 0;
392 output_iterator_next (struct output_iterator *iter)
394 const struct output_item *cur = iter->cur;
397 if (cur->type == OUTPUT_ITEM_GROUP && cur->group.n_children > 0)
399 if (iter->n >= iter->allocated)
400 iter->nodes = x2nrealloc (iter->nodes, &iter->allocated,
401 sizeof *iter->nodes);
402 iter->nodes[iter->n++] = (struct output_iterator_node) {
406 iter->cur = cur->group.children[0];
410 for (; iter->n > 0; iter->n--)
412 struct output_iterator_node *node = &iter->nodes[iter->n - 1];
413 if (++node->idx < node->group->group.n_children)
415 iter->cur = node->group->group.children[node->idx];
421 output_iterator_destroy (iter);
426 chart_item_create (struct chart *chart)
428 struct output_item *item = xmalloc (sizeof *item);
429 *item = (struct output_item) {
430 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
437 group_item_create (const char *command_name, const char *label)
439 return group_item_create_nocopy (
440 xstrdup_if_nonnull (command_name),
441 xstrdup_if_nonnull (label));
445 group_item_create_nocopy (char *command_name, char *label)
447 struct output_item *item = xmalloc (sizeof *item);
448 *item = (struct output_item) {
449 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP),
451 .command_name = command_name,
456 /* Returns a new group item suitable as the root node of an output document. A
457 root node is a group whose own properties are mostly disregarded. Instead
458 of having root nodes, it would make just as much sense to just keep around
459 arrays of nodes that would serve as the top level of an output document, but
460 we'd need more special cases instead of just using the existing support for
463 root_item_create (void)
465 return group_item_create ("", _("Output"));
468 /* Returns a clone of OLD but without any of its children. */
470 group_item_clone_empty (const struct output_item *old)
472 return output_item_clone_common (old);
475 /* Adds CHILD as a child of group item PARENT. */
477 group_item_add_child (struct output_item *parent, struct output_item *child)
479 assert (parent->type == OUTPUT_ITEM_GROUP);
480 assert (!output_item_is_shared (parent));
481 if (parent->group.n_children >= parent->group.allocated_children)
482 parent->group.children = x2nrealloc (parent->group.children,
483 &parent->group.allocated_children,
484 sizeof *parent->group.children);
485 parent->group.children[parent->group.n_children++] = child;
488 /* Creates and returns a new output item containing IMAGE. Takes ownership of
491 image_item_create (cairo_surface_t *image)
493 struct output_item *item = xmalloc (sizeof *item);
494 *item = (struct output_item) {
495 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
502 message_item_create (const struct msg *msg)
504 struct output_item *item = xmalloc (sizeof *item);
505 *item = (struct output_item) {
506 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
507 .message = msg_dup (msg),
513 message_item_get_msg (const struct output_item *item)
515 assert (item->type == OUTPUT_ITEM_MESSAGE);
516 return item->message;
520 message_item_to_text_item (struct output_item *message_item)
522 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
523 struct output_item *text_item = text_item_create_nocopy (
525 msg_to_string (message_item->message),
526 xstrdup (output_item_get_label (message_item)));
527 output_item_unref (message_item);
532 page_break_item_create (void)
534 struct output_item *item = xmalloc (sizeof *item);
535 *item = (struct output_item) {
536 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
541 /* Returns a new output_item for rendering TABLE. Takes ownership of
544 table_item_create (struct pivot_table *table)
546 pivot_table_assign_label_depth (table);
548 struct output_item *item = xmalloc (sizeof *item);
549 *item = (struct output_item) {
550 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
551 .command_name = xstrdup_if_nonnull (table->command_c),
557 /* Creates and returns a new text item containing TEXT and the specified
558 SUBTYPE and LABEL. The new text item takes ownership of TEXT and LABEL. If
559 LABEL is NULL, uses the default label for SUBTYPE. */
561 text_item_create_nocopy (enum text_item_subtype subtype,
562 char *text, char *label)
564 return text_item_create_value (subtype,
565 pivot_value_new_user_text_nocopy (text),
569 /* Creates and returns a new text item containing a copy of TEXT and the
570 specified SUBTYPE and LABEL. The caller retains ownership of TEXT and
571 LABEL. If LABEL is null, uses a default label for SUBTYPE. */
573 text_item_create (enum text_item_subtype subtype, const char *text,
576 return text_item_create_nocopy (subtype, xstrdup (text),
577 xstrdup_if_nonnull (label));
580 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
581 Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label
584 text_item_create_value (enum text_item_subtype subtype,
585 struct pivot_value *value, char *label)
587 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
589 if (!value->font_style)
591 value->font_style = xmalloc (sizeof *value->font_style);
592 *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
595 free (value->font_style->typeface);
596 value->font_style->typeface = xstrdup ("Monospaced");
599 struct output_item *item = xzalloc (sizeof *item);
600 *item = (struct output_item) {
601 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
602 .command_name = xstrdup_if_nonnull (output_get_command_name ()),
604 .text = { .subtype = subtype, .content = value },
609 /* Returns ITEM's subtype. */
610 enum text_item_subtype
611 text_item_get_subtype (const struct output_item *item)
613 assert (item->type == OUTPUT_ITEM_TEXT);
614 return item->text.subtype;
617 /* Returns ITEM's text, which the caller must eventually free. */
619 text_item_get_plain_text (const struct output_item *item)
621 assert (item->type == OUTPUT_ITEM_TEXT);
622 return pivot_value_to_string_defaults (item->text.content);
626 nullable_font_style_equal (const struct font_style *a,
627 const struct font_style *b)
629 return a && b ? font_style_equal (a, b) : !a && !b;
632 /* Attempts to append the text in SRC to DST. If successful, returns true,
635 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
638 DST must not be shared. */
640 text_item_append (struct output_item *dst, const struct output_item *src)
642 assert (dst->type == OUTPUT_ITEM_TEXT);
643 assert (src->type == OUTPUT_ITEM_TEXT);
644 assert (!output_item_is_shared (dst));
646 enum text_item_subtype ds = dst->text.subtype;
647 enum text_item_subtype ss = src->text.subtype;
649 struct pivot_value *dc = dst->text.content;
650 const struct pivot_value *sc = src->text.content;
653 || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
654 || strcmp (output_item_get_label (dst), output_item_get_label (src))
655 || !nullable_font_style_equal (dc->font_style, sc->font_style)
656 || (dc->font_style && dc->font_style->markup)
657 || sc->type != PIVOT_VALUE_TEXT
658 || dc->type != PIVOT_VALUE_TEXT)
662 /* Calculate new text. */
663 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
665 /* Free the old text. */
666 free (dc->text.local);
667 if (dc->text.c != dc->text.local)
669 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
672 /* Put in new text. */
673 dc->text.local = new_text;
674 dc->text.c = new_text;
675 dc->text.id = new_text;
681 static const struct pivot_table_look *
682 text_item_table_look (void)
684 static struct pivot_table_look *look;
687 look = pivot_table_look_new_builtin_default ();
689 for (int a = 0; a < PIVOT_N_AREAS; a++)
690 memset (look->areas[a].cell_style.margin, 0,
691 sizeof look->areas[a].cell_style.margin);
692 for (int b = 0; b < PIVOT_N_BORDERS; b++)
693 look->borders[b].stroke = TABLE_STROKE_NONE;
699 text_item_to_table_item (struct output_item *text_item)
701 assert (text_item->type == OUTPUT_ITEM_TEXT);
703 /* Create a new table whose contents come from TEXT_ITEM. */
704 struct pivot_table *table = pivot_table_create__ (NULL, "Text");
705 pivot_table_set_look (table, text_item_table_look ());
707 struct pivot_dimension *d = pivot_dimension_create (
708 table, PIVOT_AXIS_ROW, N_("Text"));
709 d->hide_all_labels = true;
710 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
712 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
714 /* Free TEXT_ITEM. */
715 output_item_unref (text_item);
717 /* Return a new output item. */
718 return table_item_create (table);
722 text_item_subtype_to_string (enum text_item_subtype subtype)
726 case TEXT_ITEM_PAGE_TITLE:
727 return _("Page Title");
729 case TEXT_ITEM_TITLE:
732 case TEXT_ITEM_SYNTAX:
742 spv_info_destroy (struct spv_info *spv_info)
746 zip_reader_unref (spv_info->zip_reader);
747 free (spv_info->structure_member);
748 free (spv_info->xml_member);
749 free (spv_info->bin_member);
750 free (spv_info->png_member);
756 spv_info_clone (const struct spv_info *old)
761 struct spv_info *new = xmalloc (sizeof *new);
762 *new = (struct spv_info) {
763 .zip_reader = old->zip_reader ? zip_reader_ref (old->zip_reader) : NULL,
765 .structure_member = xstrdup_if_nonnull (old->structure_member),
766 .xml_member = xstrdup_if_nonnull (old->xml_member),
767 .bin_member = xstrdup_if_nonnull (old->bin_member),
768 .png_member = xstrdup_if_nonnull (old->png_member),
774 spv_info_get_members (const struct spv_info *spv_info, const char **members,
775 size_t allocated_members)
781 spv_info->structure_member,
782 spv_info->xml_member,
783 spv_info->bin_member,
784 spv_info->png_member,
787 for (size_t i = 0; i < sizeof s / sizeof *s; i++)
788 if (s[i] && n < allocated_members)