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/page-setup.h"
32 #include "output/pivot-table.h"
34 #include "gl/xalloc.h"
37 #define _(msgid) gettext (msgid)
38 #define N_(msgid) msgid
41 output_item_type_to_string (enum output_item_type type)
45 case OUTPUT_ITEM_CHART: return "chart";
46 case OUTPUT_ITEM_GROUP: return "group";
47 case OUTPUT_ITEM_IMAGE: return "image";
48 case OUTPUT_ITEM_MESSAGE: return "message";
49 case OUTPUT_ITEM_PAGE_BREAK: return "page break";
50 case OUTPUT_ITEM_PAGE_SETUP: return "page setup";
51 case OUTPUT_ITEM_TABLE: return "table";
52 case OUTPUT_ITEM_TEXT: return "text";
58 #define OUTPUT_ITEM_INITIALIZER(TYPE) .type = TYPE, .ref_cnt = 1, .show = true
60 /* Increases ITEM's reference count, indicating that it has an additional
61 owner. An output item that is shared among multiple owners must not be
64 output_item_ref (const struct output_item *item_)
66 struct output_item *item = CONST_CAST (struct output_item *, item_);
67 assert (item->ref_cnt > 0);
72 /* Decreases ITEM's reference count, indicating that it has one fewer owner.
73 If ITEM no longer has any owners, it is freed. */
75 output_item_unref (struct output_item *item)
79 assert (item->ref_cnt > 0);
80 if (--item->ref_cnt == 0)
84 case OUTPUT_ITEM_CHART:
85 chart_unref (item->chart);
88 case OUTPUT_ITEM_GROUP:
89 for (size_t i = 0; i < item->group.n_children; i++)
90 output_item_unref (item->group.children[i]);
93 case OUTPUT_ITEM_IMAGE:
94 cairo_surface_destroy (item->image);
97 case OUTPUT_ITEM_MESSAGE:
98 msg_destroy (item->message);
101 case OUTPUT_ITEM_PAGE_BREAK:
104 case OUTPUT_ITEM_PAGE_SETUP:
105 page_setup_destroy (item->page_setup);
108 case OUTPUT_ITEM_TABLE:
109 pivot_table_unref (item->table);
112 case OUTPUT_ITEM_TEXT:
113 pivot_value_destroy (item->text.content);
118 free (item->command_name);
119 free (item->cached_label);
120 spv_info_destroy (item->spv_info);
126 /* Returns true if ITEM has more than one owner. An output item that is shared
127 among multiple owners must not be modified. */
129 output_item_is_shared (const struct output_item *item)
131 return item->ref_cnt > 1;
134 /* Returns a clone of OLD, without initializing type-specific fields. */
135 static struct output_item *
136 output_item_clone_common (const struct output_item *old)
138 struct output_item *new = xmalloc (sizeof *new);
139 *new = (struct output_item) {
141 .label = xstrdup_if_nonnull (old->label),
142 .command_name = xstrdup_if_nonnull (old->command_name),
145 .spv_info = spv_info_clone (old->spv_info),
151 output_item_unshare (struct output_item *old)
153 assert (old->ref_cnt > 0);
154 if (!output_item_is_shared (old))
156 output_item_unref (old);
158 struct output_item *new = output_item_clone_common (old);
161 case OUTPUT_ITEM_CHART:
162 new->chart = chart_ref (old->chart);
165 case OUTPUT_ITEM_GROUP:
166 new->group.children = xmemdup (
168 old->group.n_children * sizeof *old->group.children);
169 new->group.n_children = new->group.allocated_children
170 = old->group.n_children;
172 for (size_t i = 0; i < new->group.n_children; i++)
173 output_item_ref (new->group.children[i]);
176 case OUTPUT_ITEM_IMAGE:
177 new->image = cairo_surface_reference (old->image);
180 case OUTPUT_ITEM_MESSAGE:
181 new->message = msg_dup (old->message);
184 case OUTPUT_ITEM_PAGE_BREAK:
187 case OUTPUT_ITEM_PAGE_SETUP:
188 new->page_setup = page_setup_clone (old->page_setup);
191 case OUTPUT_ITEM_TABLE:
192 new->table = pivot_table_ref (old->table);
195 case OUTPUT_ITEM_TEXT:
196 new->text.subtype = old->text.subtype;
197 new->text.content = pivot_value_clone (old->text.content);
204 output_item_submit (struct output_item *item)
206 output_submit (item);
209 /* If ROOT is a group item, submits each of its children, but not ROOT itself.
210 This is useful if ROOT is being used as a container for output items but it
211 has no significance itself.
213 If ROOT is not a group, submits it the normal way.
215 Takes ownership of ROOT. */
217 output_item_submit_children (struct output_item *root)
219 assert (!output_item_is_shared (root));
220 if (root->type == OUTPUT_ITEM_GROUP)
222 for (size_t i = 0; i < root->group.n_children; i++)
223 output_submit (root->group.children[i]);
224 root->group.n_children = 0;
225 output_item_unref (root);
228 output_submit (root);
231 /* Returns the label for ITEM, which the caller must not modify or free. */
233 output_item_get_label (const struct output_item *item)
240 case OUTPUT_ITEM_CHART:
241 return item->chart->title ? item->chart->title : _("Chart");
243 case OUTPUT_ITEM_GROUP:
244 return item->command_name ? item->command_name : _("Group");
246 case OUTPUT_ITEM_IMAGE:
249 case OUTPUT_ITEM_MESSAGE:
250 return (item->message->severity == MSG_S_ERROR ? _("Error")
251 : item->message->severity == MSG_S_WARNING ? _("Warning")
254 case OUTPUT_ITEM_PAGE_BREAK:
255 return _("Page Break");
257 case OUTPUT_ITEM_PAGE_SETUP:
258 /* Not marked for translation: user should never see it. */
261 case OUTPUT_ITEM_TABLE:
262 if (!item->cached_label)
264 if (!item->table->title)
267 struct output_item *item_rw = CONST_CAST (struct output_item *, item);
268 item_rw->cached_label = pivot_value_to_string (item->table->title,
271 return item->cached_label;
273 case OUTPUT_ITEM_TEXT:
274 return text_item_subtype_to_string (item->text.subtype);
280 /* Sets the label for ITEM to LABEL. The caller retains ownership of LABEL.
281 If LABEL is nonnull, it overrides any previously set label and the default
282 label. If LABEL is null, ITEM will now use its default label.
284 ITEM must not be shared. */
286 output_item_set_label (struct output_item *item, const char *label)
288 output_item_set_label_nocopy (item, xstrdup_if_nonnull (label));
291 /* Sets the label for ITEM to LABEL, transferring ownership of LABEL to ITEM.
292 If LABEL is nonnull, it overrides any previously set label and the default
293 label. If LABEL is null, ITEM will now use its default label.
295 ITEM must not be shared. */
297 output_item_set_label_nocopy (struct output_item *item, char *label)
299 assert (!output_item_is_shared (item));
305 output_item_set_command_name (struct output_item *item, const char *name)
307 output_item_set_command_name_nocopy (item, xstrdup_if_nonnull (name));
311 output_item_set_command_name_nocopy (struct output_item *item, char *name)
313 free (item->command_name);
314 item->command_name = name;
318 output_item_get_subtype (const struct output_item *item)
320 return (item->type == OUTPUT_ITEM_TABLE
321 ? pivot_value_to_string (item->table->subtype, item->table)
326 output_item_add_spv_info (struct output_item *item)
328 assert (!output_item_is_shared (item));
330 item->spv_info = xzalloc (sizeof *item->spv_info);
334 indent (int indentation)
336 for (int i = 0; i < indentation * 2; i++)
341 output_item_dump (const struct output_item *item, int indentation)
343 indent (indentation);
345 printf ("label=\"%s\" ", item->label);
346 if (item->command_name)
347 printf ("command=\"%s\" ", item->command_name);
349 printf ("(%s) ", item->type == OUTPUT_ITEM_GROUP ? "collapsed" : "hidden");
353 case OUTPUT_ITEM_CHART:
354 printf ("chart \"%s\"\n", item->chart->title ? item->chart->title : "");
357 case OUTPUT_ITEM_GROUP:
359 for (size_t i = 0; i < item->group.n_children; i++)
360 output_item_dump (item->group.children[i], indentation + 1);
363 case OUTPUT_ITEM_IMAGE:
367 case OUTPUT_ITEM_MESSAGE:
368 printf ("message\n");
371 case OUTPUT_ITEM_PAGE_BREAK:
372 printf ("page break\n");
375 case OUTPUT_ITEM_PAGE_SETUP:
376 printf ("page setup\n");
379 case OUTPUT_ITEM_TABLE:
380 pivot_table_dump (item->table, indentation + 1);
383 case OUTPUT_ITEM_TEXT:
384 printf ("text %s \"%s\"\n",
385 text_item_subtype_to_string (item->text.subtype),
386 pivot_value_to_string_defaults (item->text.content));
392 output_iterator_init (struct output_iterator *iter,
393 const struct output_item *item)
395 *iter = (struct output_iterator) OUTPUT_ITERATOR_INIT (item);
399 output_iterator_destroy (struct output_iterator *iter)
405 iter->n = iter->allocated = 0;
410 output_iterator_next (struct output_iterator *iter)
412 const struct output_item *cur = iter->cur;
415 if (cur->type == OUTPUT_ITEM_GROUP && cur->group.n_children > 0)
417 if (iter->n >= iter->allocated)
418 iter->nodes = x2nrealloc (iter->nodes, &iter->allocated,
419 sizeof *iter->nodes);
420 iter->nodes[iter->n++] = (struct output_iterator_node) {
424 iter->cur = cur->group.children[0];
428 for (; iter->n > 0; iter->n--)
430 struct output_iterator_node *node = &iter->nodes[iter->n - 1];
431 if (++node->idx < node->group->group.n_children)
433 iter->cur = node->group->group.children[node->idx];
439 output_iterator_destroy (iter);
444 chart_item_create (struct chart *chart)
446 struct output_item *item = xmalloc (sizeof *item);
447 *item = (struct output_item) {
448 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
455 group_item_create (const char *command_name, const char *label)
457 return group_item_create_nocopy (
458 xstrdup_if_nonnull (command_name),
459 xstrdup_if_nonnull (label));
463 group_item_create_nocopy (char *command_name, char *label)
465 struct output_item *item = xmalloc (sizeof *item);
466 *item = (struct output_item) {
467 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP),
469 .command_name = command_name,
474 /* Returns a new group item suitable as the root node of an output document. A
475 root node is a group whose own properties are mostly disregarded. Instead
476 of having root nodes, it would make just as much sense to just keep around
477 arrays of nodes that would serve as the top level of an output document, but
478 we'd need more special cases instead of just using the existing support for
481 root_item_create (void)
483 return group_item_create ("", _("Output"));
486 /* Returns a clone of OLD but without any of its children. */
488 group_item_clone_empty (const struct output_item *old)
490 return output_item_clone_common (old);
493 /* Adds CHILD as a child of group item PARENT. */
495 group_item_add_child (struct output_item *parent, struct output_item *child)
497 assert (parent->type == OUTPUT_ITEM_GROUP);
498 assert (!output_item_is_shared (parent));
499 if (parent->group.n_children >= parent->group.allocated_children)
500 parent->group.children = x2nrealloc (parent->group.children,
501 &parent->group.allocated_children,
502 sizeof *parent->group.children);
503 parent->group.children[parent->group.n_children++] = child;
506 /* Creates and returns a new output item containing IMAGE. Takes ownership of
509 image_item_create (cairo_surface_t *image)
511 struct output_item *item = xmalloc (sizeof *item);
512 *item = (struct output_item) {
513 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
520 message_item_create (const struct msg *msg)
522 struct output_item *item = xmalloc (sizeof *item);
523 *item = (struct output_item) {
524 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
525 .message = msg_dup (msg),
531 message_item_get_msg (const struct output_item *item)
533 assert (item->type == OUTPUT_ITEM_MESSAGE);
534 return item->message;
538 message_item_to_text_item (struct output_item *message_item)
540 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
541 struct output_item *text_item = text_item_create_nocopy (
543 msg_to_string (message_item->message),
544 xstrdup (output_item_get_label (message_item)));
545 output_item_unref (message_item);
550 page_break_item_create (void)
552 struct output_item *item = xmalloc (sizeof *item);
553 *item = (struct output_item) {
554 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
560 page_setup_item_create (const struct page_setup *ps)
562 struct output_item *item = xmalloc (sizeof *item);
563 *item = (struct output_item) {
564 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_SETUP),
565 .page_setup = page_setup_clone (ps),
570 /* Returns a new output_item for rendering TABLE. Takes ownership of
573 table_item_create (struct pivot_table *table)
575 pivot_table_assign_label_depth (table);
577 struct output_item *item = xmalloc (sizeof *item);
578 *item = (struct output_item) {
579 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
580 .command_name = xstrdup_if_nonnull (table->command_c),
586 /* Creates and returns a new text item containing TEXT and the specified
587 SUBTYPE and LABEL. The new text item takes ownership of TEXT and LABEL. If
588 LABEL is NULL, uses the default label for SUBTYPE. */
590 text_item_create_nocopy (enum text_item_subtype subtype,
591 char *text, char *label)
593 return text_item_create_value (subtype,
594 pivot_value_new_user_text_nocopy (text),
598 /* Creates and returns a new text item containing a copy of TEXT and the
599 specified SUBTYPE and LABEL. The caller retains ownership of TEXT and
600 LABEL. If LABEL is null, uses a default label for SUBTYPE. */
602 text_item_create (enum text_item_subtype subtype, const char *text,
605 return text_item_create_nocopy (subtype, xstrdup (text),
606 xstrdup_if_nonnull (label));
609 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
610 Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label
613 text_item_create_value (enum text_item_subtype subtype,
614 struct pivot_value *value, char *label)
616 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
618 if (!value->font_style)
620 value->font_style = xmalloc (sizeof *value->font_style);
621 *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
624 free (value->font_style->typeface);
625 value->font_style->typeface = xstrdup ("Monospaced");
628 struct output_item *item = xzalloc (sizeof *item);
629 *item = (struct output_item) {
630 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
631 .command_name = xstrdup_if_nonnull (output_get_command_name ()),
633 .text = { .subtype = subtype, .content = value },
638 /* Returns ITEM's subtype. */
639 enum text_item_subtype
640 text_item_get_subtype (const struct output_item *item)
642 assert (item->type == OUTPUT_ITEM_TEXT);
643 return item->text.subtype;
646 /* Returns ITEM's text, which the caller must eventually free. */
648 text_item_get_plain_text (const struct output_item *item)
650 assert (item->type == OUTPUT_ITEM_TEXT);
651 return pivot_value_to_string_defaults (item->text.content);
655 nullable_font_style_equal (const struct font_style *a,
656 const struct font_style *b)
658 return a && b ? font_style_equal (a, b) : !a && !b;
661 /* Attempts to append the text in SRC to DST. If successful, returns true,
664 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
667 DST must not be shared. */
669 text_item_append (struct output_item *dst, const struct output_item *src)
671 assert (dst->type == OUTPUT_ITEM_TEXT);
672 assert (src->type == OUTPUT_ITEM_TEXT);
673 assert (!output_item_is_shared (dst));
675 enum text_item_subtype ds = dst->text.subtype;
676 enum text_item_subtype ss = src->text.subtype;
678 struct pivot_value *dc = dst->text.content;
679 const struct pivot_value *sc = src->text.content;
682 || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
683 || strcmp (output_item_get_label (dst), output_item_get_label (src))
684 || !nullable_font_style_equal (dc->font_style, sc->font_style)
685 || (dc->font_style && dc->font_style->markup)
686 || sc->type != PIVOT_VALUE_TEXT
687 || dc->type != PIVOT_VALUE_TEXT)
691 /* Calculate new text. */
692 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
694 /* Free the old text. */
695 free (dc->text.local);
696 if (dc->text.c != dc->text.local)
698 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
701 /* Put in new text. */
702 dc->text.local = new_text;
703 dc->text.c = new_text;
704 dc->text.id = new_text;
710 static const struct pivot_table_look *
711 text_item_table_look (void)
713 static struct pivot_table_look *look;
716 look = pivot_table_look_new_builtin_default ();
718 for (int a = 0; a < PIVOT_N_AREAS; a++)
719 memset (look->areas[a].cell_style.margin, 0,
720 sizeof look->areas[a].cell_style.margin);
721 for (int b = 0; b < PIVOT_N_BORDERS; b++)
722 look->borders[b].stroke = TABLE_STROKE_NONE;
728 text_item_to_table_item (struct output_item *text_item)
730 assert (text_item->type == OUTPUT_ITEM_TEXT);
732 /* Create a new table whose contents come from TEXT_ITEM. */
733 struct pivot_table *table = pivot_table_create__ (NULL, "Text");
734 pivot_table_set_look (table, text_item_table_look ());
736 struct pivot_dimension *d = pivot_dimension_create (
737 table, PIVOT_AXIS_ROW, N_("Text"));
738 d->hide_all_labels = true;
739 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
741 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
743 /* Free TEXT_ITEM. */
744 output_item_unref (text_item);
746 /* Return a new output item. */
747 return table_item_create (table);
751 text_item_subtype_to_string (enum text_item_subtype subtype)
755 case TEXT_ITEM_PAGE_TITLE:
756 return _("Page Title");
758 case TEXT_ITEM_TITLE:
761 case TEXT_ITEM_SYNTAX:
771 spv_info_destroy (struct spv_info *spv_info)
775 zip_reader_unref (spv_info->zip_reader);
776 free (spv_info->structure_member);
777 free (spv_info->xml_member);
778 free (spv_info->bin_member);
779 free (spv_info->png_member);
785 spv_info_clone (const struct spv_info *old)
790 struct spv_info *new = xmalloc (sizeof *new);
791 *new = (struct spv_info) {
792 .zip_reader = old->zip_reader ? zip_reader_ref (old->zip_reader) : NULL,
794 .structure_member = xstrdup_if_nonnull (old->structure_member),
795 .xml_member = xstrdup_if_nonnull (old->xml_member),
796 .bin_member = xstrdup_if_nonnull (old->bin_member),
797 .png_member = xstrdup_if_nonnull (old->png_member),
803 spv_info_get_members (const struct spv_info *spv_info, const char **members,
804 size_t allocated_members)
810 spv_info->structure_member,
811 spv_info->xml_member,
812 spv_info->bin_member,
813 spv_info->png_member,
816 for (size_t i = 0; i < sizeof s / sizeof *s; i++)
817 if (s[i] && n < allocated_members)