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:
368 char *s = pivot_value_to_string_defaults (item->text.content);
369 printf ("text %s \"%s\"\n",
370 text_item_subtype_to_string (item->text.subtype), s);
378 output_iterator_init (struct output_iterator *iter,
379 const struct output_item *item)
381 *iter = (struct output_iterator) OUTPUT_ITERATOR_INIT (item);
385 output_iterator_destroy (struct output_iterator *iter)
391 iter->n = iter->allocated = 0;
396 output_iterator_next (struct output_iterator *iter)
398 const struct output_item *cur = iter->cur;
401 if (cur->type == OUTPUT_ITEM_GROUP && cur->group.n_children > 0)
403 if (iter->n >= iter->allocated)
404 iter->nodes = x2nrealloc (iter->nodes, &iter->allocated,
405 sizeof *iter->nodes);
406 iter->nodes[iter->n++] = (struct output_iterator_node) {
410 iter->cur = cur->group.children[0];
414 for (; iter->n > 0; iter->n--)
416 struct output_iterator_node *node = &iter->nodes[iter->n - 1];
417 if (++node->idx < node->group->group.n_children)
419 iter->cur = node->group->group.children[node->idx];
425 output_iterator_destroy (iter);
430 chart_item_create (struct chart *chart)
432 struct output_item *item = xmalloc (sizeof *item);
433 *item = (struct output_item) {
434 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
441 group_item_create (const char *command_name, const char *label)
443 return group_item_create_nocopy (
444 xstrdup_if_nonnull (command_name),
445 xstrdup_if_nonnull (label));
449 group_item_create_nocopy (char *command_name, char *label)
451 struct output_item *item = xmalloc (sizeof *item);
452 *item = (struct output_item) {
453 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP),
455 .command_name = command_name,
460 /* Returns a new group item suitable as the root node of an output document. A
461 root node is a group whose own properties are mostly disregarded. Instead
462 of having root nodes, it would make just as much sense to just keep around
463 arrays of nodes that would serve as the top level of an output document, but
464 we'd need more special cases instead of just using the existing support for
467 root_item_create (void)
469 return group_item_create ("", _("Output"));
472 /* Returns a clone of OLD but without any of its children. */
474 group_item_clone_empty (const struct output_item *old)
476 return output_item_clone_common (old);
479 /* Adds CHILD as a child of group item PARENT. */
481 group_item_add_child (struct output_item *parent, struct output_item *child)
483 assert (parent->type == OUTPUT_ITEM_GROUP);
484 assert (!output_item_is_shared (parent));
485 if (parent->group.n_children >= parent->group.allocated_children)
486 parent->group.children = x2nrealloc (parent->group.children,
487 &parent->group.allocated_children,
488 sizeof *parent->group.children);
489 parent->group.children[parent->group.n_children++] = child;
492 /* Creates and returns a new output item containing IMAGE. Takes ownership of
495 image_item_create (cairo_surface_t *image)
497 struct output_item *item = xmalloc (sizeof *item);
498 *item = (struct output_item) {
499 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
506 message_item_create (const struct msg *msg)
508 struct output_item *item = xmalloc (sizeof *item);
509 *item = (struct output_item) {
510 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
511 .message = msg_dup (msg),
517 message_item_get_msg (const struct output_item *item)
519 assert (item->type == OUTPUT_ITEM_MESSAGE);
520 return item->message;
524 message_item_to_text_item (struct output_item *message_item)
526 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
527 struct output_item *text_item = text_item_create_nocopy (
529 msg_to_string (message_item->message),
530 xstrdup (output_item_get_label (message_item)));
531 output_item_unref (message_item);
536 page_break_item_create (void)
538 struct output_item *item = xmalloc (sizeof *item);
539 *item = (struct output_item) {
540 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
545 /* Returns a new output_item for rendering TABLE. Takes ownership of
548 table_item_create (struct pivot_table *table)
550 pivot_table_assign_label_depth (table);
552 struct output_item *item = xmalloc (sizeof *item);
553 *item = (struct output_item) {
554 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
555 .command_name = xstrdup_if_nonnull (table->command_c),
561 /* Creates and returns a new text item containing TEXT and the specified
562 SUBTYPE and LABEL. The new text item takes ownership of TEXT and LABEL. If
563 LABEL is NULL, uses the default label for SUBTYPE. */
565 text_item_create_nocopy (enum text_item_subtype subtype,
566 char *text, char *label)
568 return text_item_create_value (subtype,
569 pivot_value_new_user_text_nocopy (text),
573 /* Creates and returns a new text item containing a copy of TEXT and the
574 specified SUBTYPE and LABEL. The caller retains ownership of TEXT and
575 LABEL. If LABEL is null, uses a default label for SUBTYPE. */
577 text_item_create (enum text_item_subtype subtype, const char *text,
580 return text_item_create_nocopy (subtype, xstrdup (text),
581 xstrdup_if_nonnull (label));
584 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
585 Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label
588 text_item_create_value (enum text_item_subtype subtype,
589 struct pivot_value *value, char *label)
591 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
593 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
596 ex->font_style = xmalloc (sizeof *value->ex->font_style);
597 *ex->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
600 free (ex->font_style->typeface);
601 ex->font_style->typeface = xstrdup ("Monospaced");
604 struct output_item *item = XZALLOC (struct output_item);
605 *item = (struct output_item) {
606 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
607 .command_name = xstrdup_if_nonnull (output_get_command_name ()),
609 .text = { .subtype = subtype, .content = value },
614 /* Returns ITEM's subtype. */
615 enum text_item_subtype
616 text_item_get_subtype (const struct output_item *item)
618 assert (item->type == OUTPUT_ITEM_TEXT);
619 return item->text.subtype;
622 /* Returns ITEM's text, which the caller must eventually free. */
624 text_item_get_plain_text (const struct output_item *item)
626 assert (item->type == OUTPUT_ITEM_TEXT);
627 return pivot_value_to_string_defaults (item->text.content);
631 nullable_font_style_equal (const struct font_style *a,
632 const struct font_style *b)
634 return a && b ? font_style_equal (a, b) : !a && !b;
637 /* Attempts to append the text in SRC to DST. If successful, returns true,
640 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
643 DST must not be shared. */
645 text_item_append (struct output_item *dst, const struct output_item *src)
647 assert (dst->type == OUTPUT_ITEM_TEXT);
648 assert (src->type == OUTPUT_ITEM_TEXT);
649 assert (!output_item_is_shared (dst));
651 enum text_item_subtype ds = dst->text.subtype;
652 enum text_item_subtype ss = src->text.subtype;
654 struct pivot_value *dc = dst->text.content;
655 const struct pivot_value *sc = src->text.content;
658 || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
659 || strcmp (output_item_get_label (dst), output_item_get_label (src))
660 || !nullable_font_style_equal (dc->ex ? dc->ex->font_style : NULL,
661 sc->ex ? sc->ex->font_style : NULL)
662 || (dc->ex && dc->ex->font_style && dc->ex->font_style->markup)
663 || sc->type != PIVOT_VALUE_TEXT
664 || dc->type != PIVOT_VALUE_TEXT)
668 /* Calculate new text. */
669 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
671 /* Free the old text. */
672 free (dc->text.local);
673 if (dc->text.c != dc->text.local)
675 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
678 /* Put in new text. */
679 dc->text.local = new_text;
680 dc->text.c = new_text;
681 dc->text.id = new_text;
687 static const struct pivot_table_look *
688 text_item_table_look (void)
690 static struct pivot_table_look *look;
693 look = pivot_table_look_new_builtin_default ();
695 for (int a = 0; a < PIVOT_N_AREAS; a++)
696 memset (look->areas[a].cell_style.margin, 0,
697 sizeof look->areas[a].cell_style.margin);
698 for (int b = 0; b < PIVOT_N_BORDERS; b++)
699 look->borders[b].stroke = TABLE_STROKE_NONE;
705 text_item_to_table_item (struct output_item *text_item)
707 assert (text_item->type == OUTPUT_ITEM_TEXT);
709 /* Create a new table whose contents come from TEXT_ITEM. */
710 struct pivot_table *table = pivot_table_create__ (NULL, "Text");
711 pivot_table_set_look (table, text_item_table_look ());
713 struct pivot_dimension *d = pivot_dimension_create (
714 table, PIVOT_AXIS_ROW, N_("Text"));
715 d->hide_all_labels = true;
716 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
718 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
720 /* Free TEXT_ITEM. */
721 output_item_unref (text_item);
723 /* Return a new output item. */
724 return table_item_create (table);
728 text_item_subtype_to_string (enum text_item_subtype subtype)
732 case TEXT_ITEM_PAGE_TITLE:
733 return _("Page Title");
735 case TEXT_ITEM_TITLE:
738 case TEXT_ITEM_SYNTAX:
748 spv_info_destroy (struct spv_info *spv_info)
752 zip_reader_unref (spv_info->zip_reader);
753 free (spv_info->structure_member);
754 free (spv_info->xml_member);
755 free (spv_info->bin_member);
756 free (spv_info->png_member);
762 spv_info_clone (const struct spv_info *old)
767 struct spv_info *new = xmalloc (sizeof *new);
768 *new = (struct spv_info) {
769 .zip_reader = old->zip_reader ? zip_reader_ref (old->zip_reader) : NULL,
771 .structure_member = xstrdup_if_nonnull (old->structure_member),
772 .xml_member = xstrdup_if_nonnull (old->xml_member),
773 .bin_member = xstrdup_if_nonnull (old->bin_member),
774 .png_member = xstrdup_if_nonnull (old->png_member),
780 spv_info_get_members (const struct spv_info *spv_info, const char **members,
781 size_t allocated_members)
787 spv_info->structure_member,
788 spv_info->xml_member,
789 spv_info->bin_member,
790 spv_info->png_member,
793 for (size_t i = 0; i < sizeof s / sizeof *s; i++)
794 if (s[i] && n < allocated_members)