psppire: Use default GTK+ font in output shown in GUI.
[pspp] / src / output / cairo.c
index 612878035eb98baf98e20020c1c33919fad19355..977a414420fa207cc8f4bd383bc44cad9bb75fa2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
 
 #include <libpspp/assertion.h>
 #include <libpspp/cast.h>
+#include <libpspp/message.h>
 #include <libpspp/start-date.h>
 #include <libpspp/str.h>
 #include <libpspp/string-map.h>
@@ -33,6 +34,7 @@
 #include <output/charts/roc-chart.h>
 #include <output/charts/scree.h>
 #include <output/driver-provider.h>
+#include <output/message-item.h>
 #include <output/options.h>
 #include <output/render.h>
 #include <output/tab.h>
@@ -126,6 +128,7 @@ struct xr_driver
 
     /* Internal state. */
     struct render_params *params;
+    char *command_name;
     char *title;
     char *subtitle;
     cairo_t *cairo;
@@ -133,6 +136,8 @@ struct xr_driver
     int y;
   };
 
+static const struct output_driver_class cairo_driver_class;
+
 static void xr_show_page (struct xr_driver *);
 static void draw_headers (struct xr_driver *);
 
@@ -154,7 +159,7 @@ static void xr_draw_cell (void *, const struct table_cell *,
 static struct xr_driver *
 xr_driver_cast (struct output_driver *driver)
 {
-  assert (driver->class == &cairo_class);
+  assert (driver->class == &cairo_driver_class);
   return UP_CAST (driver, struct xr_driver, driver);
 }
 
@@ -173,7 +178,7 @@ xr_allocate (const char *name, int device_type, struct string_map *o)
 
   xr = xzalloc (sizeof *xr);
   d = &xr->driver;
-  output_driver_init (d, &cairo_class, name, device_type);
+  output_driver_init (d, &cairo_driver_class, name, device_type);
   xr->headers = true;
   xr->font_height = XR_POINT * 10;
   xr->fonts[XR_FONT_FIXED].string
@@ -232,32 +237,23 @@ xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
 }
 
 static struct output_driver *
-xr_create (const char *name, enum output_device_type device_type,
-           struct string_map *o)
+xr_create (const char *file_name, enum settings_output_devices device_type,
+           struct string_map *o, enum xr_output_type file_type)
 {
-  enum { MIN_LENGTH = 3 };
+  enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
   struct output_driver *d;
   struct xr_driver *xr;
   cairo_surface_t *surface;
   cairo_status_t status;
   double width_pt, length_pt;
   int paper_width, paper_length;
-  char *file_name;
 
-  xr = xr_allocate (name, device_type, o);
+  xr = xr_allocate (file_name, device_type, o);
   d = &xr->driver;
 
   xr->headers = parse_boolean (opt (d, o, "headers", "true"));
 
-  xr->file_type = parse_enum (opt (d, o, "output-type", "pdf"),
-                              "pdf", XR_PDF,
-                              "ps", XR_PS,
-                              "svg", XR_SVG,
-                              (char *) NULL);
-  file_name = parse_string (opt (d, o, "output-file",
-                                 (xr->file_type == XR_PDF ? "pspp.pdf"
-                                  : xr->file_type == XR_PS ? "pspp.ps"
-                                  : "pspp.svg")));
+  xr->file_type = file_type;
 
   parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
   xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
@@ -284,7 +280,7 @@ xr_create (const char *name, enum output_device_type device_type,
   status = cairo_surface_status (surface);
   if (status != CAIRO_STATUS_SUCCESS)
     {
-      error (0, 0, _("opening output file \"%s\": %s"),
+      error (0, 0, _("error opening output file \"%s\": %s"),
              file_name, cairo_status_to_string (status));
       cairo_surface_destroy (surface);
       goto error;
@@ -300,6 +296,16 @@ xr_create (const char *name, enum output_device_type device_type,
   if (!xr_set_cairo (xr, xr->cairo))
     goto error;
 
+  if (xr->width / (xr->font_height / 2) < MIN_WIDTH)
+    {
+      error (0, 0, _("The defined page is not wide enough to hold at least %d "
+                     "characters in the default font.  In fact, there's only "
+                     "room for %d characters."),
+             MIN_WIDTH,
+             xr->width / (xr->font_height / 2));
+      goto error;
+    }
+
   if (xr->length / xr->font_height < MIN_LENGTH)
     {
       error (0, 0, _("The defined page is not long "
@@ -311,7 +317,6 @@ xr_create (const char *name, enum output_device_type device_type,
       goto error;
     }
 
-  free (file_name);
   return &xr->driver;
 
  error:
@@ -319,6 +324,27 @@ xr_create (const char *name, enum output_device_type device_type,
   return NULL;
 }
 
+static struct output_driver *
+xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
+               struct string_map *o)
+{
+  return xr_create (file_name, device_type, o, XR_PDF);
+}
+
+static struct output_driver *
+xr_ps_create (const char *file_name, enum settings_output_devices device_type,
+               struct string_map *o)
+{
+  return xr_create (file_name, device_type, o, XR_PS);
+}
+
+static struct output_driver *
+xr_svg_create (const char *file_name, enum settings_output_devices device_type,
+               struct string_map *o)
+{
+  return xr_create (file_name, device_type, o, XR_SVG);
+}
+
 static void
 xr_destroy (struct output_driver *driver)
 {
@@ -341,6 +367,7 @@ xr_destroy (struct output_driver *driver)
       cairo_destroy (xr->cairo);
     }
 
+  free (xr->command_name);
   for (i = 0; i < XR_N_FONTS; i++)
     free_font (&xr->fonts[i]);
   free (xr->params);
@@ -383,67 +410,81 @@ xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
 }
 
 static void
-xr_submit (struct output_driver *driver, const struct output_item *output_item)
+xr_output_table_item (struct xr_driver *xr,
+                      const struct table_item *table_item)
 {
-  struct xr_driver *xr = xr_driver_cast (driver);
-  if (is_table_item (output_item))
-    {
-      struct table_item *table_item = to_table_item (output_item);
-      struct render_break x_break;
-      struct render_page *page;
-      int caption_height;
+  struct render_break x_break;
+  struct render_page *page;
+  int caption_height;
 
-      if (xr->y > 0)
-        xr->y += xr->font_height;
+  if (xr->y > 0)
+    xr->y += xr->font_height;
+
+  page = xr_render_table_item (xr, table_item, &caption_height);
+  xr->params->size[V] = xr->length - caption_height;
+  for (render_break_init (&x_break, page, H);
+       render_break_has_next (&x_break); )
+    {
+      struct render_page *x_slice;
+      struct render_break y_break;
 
-      page = xr_render_table_item (xr, table_item, &caption_height);
-      xr->params->size[V] = xr->length - caption_height;
-      for (render_break_init (&x_break, page, H);
-           render_break_has_next (&x_break); )
+      x_slice = render_break_next (&x_break, xr->width);
+      for (render_break_init (&y_break, x_slice, V);
+           render_break_has_next (&y_break); )
         {
-          struct render_page *x_slice;
-          struct render_break y_break;
+          int space = xr->length - xr->y;
+          struct render_page *y_slice;
 
-          x_slice = render_break_next (&x_break, xr->width);
-          for (render_break_init (&y_break, x_slice, V);
-               render_break_has_next (&y_break); )
+          /* XXX doesn't allow for caption or space between segments */
+          if (render_break_next_size (&y_break) > space)
             {
-              int space = xr->length - xr->y;
-              struct render_page *y_slice;
-
-              /* XXX doesn't allow for caption or space between segments */
-              if (render_break_next_size (&y_break) > space)
-                {
-                  assert (xr->y > 0);
-                  xr_show_page (xr);
-                  continue;
-                }
-
-              y_slice = render_break_next (&y_break, space);
-              if (caption_height)
-                {
-                  struct table_cell cell;
-                  int bb[TABLE_N_AXES][2];
-
-                  xr_init_caption_cell (table_item_get_caption (table_item),
-                                        &cell);
-                  bb[H][0] = 0;
-                  bb[H][1] = xr->width;
-                  bb[V][0] = 0;
-                  bb[V][1] = caption_height;
-                  xr_draw_cell (xr, &cell, bb, bb);
-                  xr->y += caption_height;
-                  caption_height = 0;
-                }
-
-              render_page_draw (y_slice);
-              xr->y += render_page_get_size (y_slice, V);
-              render_page_unref (y_slice);
+              assert (xr->y > 0);
+              xr_show_page (xr);
+              continue;
             }
-          render_break_destroy (&y_break);
+
+          y_slice = render_break_next (&y_break, space);
+          if (caption_height)
+            {
+              struct table_cell cell;
+              int bb[TABLE_N_AXES][2];
+
+              xr_init_caption_cell (table_item_get_caption (table_item),
+                                    &cell);
+              bb[H][0] = 0;
+              bb[H][1] = xr->width;
+              bb[V][0] = 0;
+              bb[V][1] = caption_height;
+              xr_draw_cell (xr, &cell, bb, bb);
+              xr->y += caption_height;
+              caption_height = 0;
+            }
+
+          render_page_draw (y_slice);
+          xr->y += render_page_get_size (y_slice, V);
+          render_page_unref (y_slice);
         }
-      render_break_destroy (&x_break);
+      render_break_destroy (&y_break);
     }
+  render_break_destroy (&x_break);
+}
+
+static void
+xr_output_text (struct xr_driver *xr, const char *text)
+{
+  struct table_item *table_item;
+
+  table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
+  xr_output_table_item (xr, table_item);
+  table_item_unref (table_item);
+}
+
+static void
+xr_submit (struct output_driver *driver, const struct output_item *output_item)
+{
+  struct xr_driver *xr = xr_driver_cast (driver);
+  if (is_table_item (output_item))
+    xr_output_table_item (xr, to_table_item (output_item));
   else if (is_chart_item (output_item))
     {
       if (xr->y > 0)
@@ -484,16 +525,17 @@ xr_submit (struct output_driver *driver, const struct output_item *output_item)
           break;
 
         default:
-          {
-            struct table_item *item;
-
-            item = table_item_create (table_from_string (0, text), NULL);
-            xr_submit (&xr->driver, &item->output_item);
-            table_item_unref (item);
-          }
+          xr_output_text (xr, text);
           break;
         }
-
+    }
+  else if (is_message_item (output_item))
+    {
+      const struct message_item *message_item = to_message_item (output_item);
+      const struct msg *msg = message_item_get_msg (message_item);
+      char *s = msg_to_string (msg, xr->command_name);
+      xr_output_text (xr, s);
+      free (s);
     }
 }
 
@@ -884,14 +926,17 @@ free_font (struct xr_font *font)
   if (font->desc != NULL)
     pango_font_description_free (font->desc);
   pango_font_metrics_unref (font->metrics);
-  g_object_unref (font->layout);
+  if (font->layout != NULL)
+    g_object_unref (font->layout);
 }
 
-/* Cairo driver class. */
-const struct output_driver_class cairo_class =
+struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
+struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
+struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
+
+static const struct output_driver_class cairo_driver_class =
 {
   "cairo",
-  xr_create,
   xr_destroy,
   xr_submit,
   xr_flush,
@@ -914,15 +959,9 @@ struct xr_rendering
 #define CHART_HEIGHT 375
 
 struct xr_driver *
-xr_create_driver (cairo_t *cairo)
+xr_create_driver (cairo_t *cairo, struct string_map *options)
 {
-  struct xr_driver *xr;
-  struct string_map map;
-
-  string_map_init (&map);
-  xr = xr_allocate ("cairo", 0, &map);
-  string_map_destroy (&map);
-
+  struct xr_driver *xr = xr_allocate ("cairo", 0, options);
   xr->width = INT_MAX / 8;
   xr->length = INT_MAX / 8;
   if (!xr_set_cairo (xr, cairo))
@@ -933,6 +972,19 @@ xr_create_driver (cairo_t *cairo)
   return xr;
 }
 
+static struct xr_rendering *
+xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
+{
+  struct table_item *table_item;
+  struct xr_rendering *r;
+
+  table_item = table_item_create (table_from_string (0, text), NULL);
+  r = xr_rendering_create (xr, &table_item->output_item, cr);
+  table_item_unref (table_item);
+
+  return r;
+}
+
 struct xr_rendering *
 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
                      cairo_t *cr)
@@ -940,14 +992,15 @@ xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
   struct xr_rendering *r = NULL;
 
   if (is_text_item (item))
+    r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
+                                  cr);
+  else if (is_message_item (item))
     {
-      const struct text_item *text_item = to_text_item (item);
-      const char *text = text_item_get_text (text_item);
-      struct table_item *table_item;
-
-      table_item = table_item_create (table_from_string (0, text), NULL);
-      r = xr_rendering_create (xr, &table_item->output_item, cr);
-      table_item_unref (table_item);
+      const struct message_item *message_item = to_message_item (item);
+      const struct msg *msg = message_item_get_msg (message_item);
+      char *s = msg_to_string (msg, NULL);
+      r = xr_rendering_create_text (xr, s, cr);
+      free (s);
     }
   else if (is_table_item (item))
     {
@@ -1060,7 +1113,7 @@ xr_draw_png_chart (const struct chart_item *item,
 
   status = cairo_surface_write_to_png (surface, file_name);
   if (status != CAIRO_STATUS_SUCCESS)
-    error (0, 0, _("writing output file \"%s\": %s"),
+    error (0, 0, _("error writing output file \"%s\": %s"),
            file_name, cairo_status_to_string (status));
 
   cairo_destroy (cr);