output: Use Cairo and Pango to draw charts, instead of libplot.
[pspp-builds.git] / src / output / cairo.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <output/cairo.h>
20
21 #include <libpspp/assertion.h>
22 #include <libpspp/start-date.h>
23 #include <libpspp/version.h>
24 #include <output/afm.h>
25 #include <output/chart-provider.h>
26 #include <output/manager.h>
27 #include <output/output.h>
28
29 #include <cairo/cairo-pdf.h>
30 #include <cairo/cairo-ps.h>
31 #include <cairo/cairo-svg.h>
32 #include <cairo/cairo.h>
33 #include <pango/pango-font.h>
34 #include <pango/pango-layout.h>
35 #include <pango/pango.h>
36 #include <pango/pangocairo.h>
37 #include <stdlib.h>
38
39 #include "error.h"
40 #include "intprops.h"
41 #include "minmax.h"
42 #include "xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46
47 /* Cairo driver options: (defaults listed first)
48
49    output-file="pspp.pdf"
50    output-type=pdf|ps|png|svg
51    paper-size=letter (see "papersize" file)
52    orientation=portrait|landscape
53    headers=on|off
54
55    left-margin=0.5in
56    right-margin=0.5in
57    top-margin=0.5in
58    bottom-margin=0.5in
59
60    prop-font=serif
61    emph-font=serif italic
62    fixed-font=monospace
63    font-size=10000
64
65    line-gutter=1pt
66    line-spacing=1pt
67    line-width=0.5pt
68  */
69
70 /* Measurements as we present to the rest of PSPP. */
71 #define XR_POINT PANGO_SCALE
72 #define XR_INCH (XR_POINT * 72)
73
74 /* Conversions to and from points. */
75 static double
76 xr_to_pt (int x)
77 {
78   return x / (double) XR_POINT;
79 }
80
81 static int
82 pt_to_xr (double x)
83 {
84   return x * XR_POINT + 0.5;
85 }
86
87 /* Output types. */
88 enum xr_output_type
89   {
90     XR_PDF,
91     XR_PS,
92     XR_SVG
93   };
94
95 /* A font for use with Cairo. */
96 struct xr_font
97   {
98     char *string;
99     PangoFontDescription *desc;
100     PangoLayout *layout;
101     PangoFontMetrics *metrics;
102   };
103
104 /* Cairo output driver extension record. */
105 struct xr_driver_ext
106   {
107     cairo_t *cairo;
108     struct xr_font fonts[OUTP_FONT_CNT];
109
110     bool draw_headers;          /* Draw headers at top of page? */
111     int page_number;            /* Current page number. */
112
113     int line_gutter;            /* Space around lines. */
114     int line_space;             /* Space between lines. */
115     int line_width;             /* Width of lines. */
116   };
117
118 struct xr_driver_options
119   {
120     struct outp_driver *driver;
121
122     char *file_name;            /* Output file name. */
123     enum xr_output_type file_type; /* Type of output file. */
124
125
126     bool portrait;              /* Portrait mode? */
127
128     int paper_width;            /* Width of paper before dropping margins. */
129     int paper_length;           /* Length of paper before dropping margins. */
130     int left_margin;            /* Left margin in XR units. */
131     int right_margin;           /* Right margin in XR units. */
132     int top_margin;             /* Top margin in XR units. */
133     int bottom_margin;          /* Bottom margin in XR units. */
134   };
135
136 static bool handle_option (void *options, const char *key,
137                            const struct string *val);
138 static void draw_headers (struct outp_driver *this);
139
140 static bool load_font (struct outp_driver *this, struct xr_font *);
141 static void free_font (struct xr_font *);
142 static int text_width (struct outp_driver *, const char *, enum outp_font);
143 \f
144 /* Driver initialization. */
145
146 static struct outp_driver *
147 xr_allocate (const char *name, int types)
148 {
149   struct outp_driver *this;
150   struct xr_driver_ext *x;
151   size_t i;
152
153   this = outp_allocate_driver (&cairo_class, name, types);
154   this->width = this->length = 0;
155   this->font_height = XR_POINT * 10;
156   this->ext = x = xzalloc (sizeof *x);
157   x->cairo = NULL;
158   x->fonts[OUTP_FIXED].string = xstrdup ("monospace");
159   x->fonts[OUTP_PROPORTIONAL].string = xstrdup ("serif");
160   x->fonts[OUTP_EMPHASIS].string = xstrdup ("serif italic");
161   for (i = 0; i < OUTP_FONT_CNT; i++)
162     {
163       struct xr_font *font = &x->fonts[i];
164       font->desc = NULL;
165       font->metrics = NULL;
166       font->layout = NULL;
167     }
168   x->draw_headers = true;
169   x->page_number = 0;
170   x->line_gutter = XR_POINT;
171   x->line_space = XR_POINT;
172   x->line_width = XR_POINT / 2;
173
174   return this;
175 }
176
177 static bool
178 xr_set_cairo (struct outp_driver *this, cairo_t *cairo)
179 {
180   struct xr_driver_ext *x = this->ext;
181   int i;
182
183   x->cairo = cairo;
184
185   cairo_set_line_width (x->cairo, xr_to_pt (x->line_width));
186
187   for (i = 0; i < OUTP_FONT_CNT; i++)
188     if (!load_font (this, &x->fonts[i]))
189       return false;
190
191   this->fixed_width = text_width (this, "0", OUTP_FIXED);
192   this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
193
194   this->horiz_line_width[OUTP_L_NONE] = 0;
195   this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
196   this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
197                                            + 2 * x->line_width);
198   memcpy (this->vert_line_width, this->horiz_line_width,
199           sizeof this->vert_line_width);
200
201   return true;
202 }
203
204 struct outp_driver *
205 xr_create_driver (cairo_t *cairo)
206 {
207   struct outp_driver *this;
208
209   this = xr_allocate ("cairo", 0);
210   this->width = INT_MAX / 8;
211   this->length = INT_MAX / 8;
212   if (!xr_set_cairo (this, cairo))
213     {
214       this->class->close_driver (this);
215       outp_free_driver (this);
216       return NULL;
217     }
218   return this;
219 }
220
221 static bool
222 xr_open_driver (const char *name, int types, struct substring option_string)
223 {
224   struct outp_driver *this;
225   struct xr_driver_ext *x;
226   struct xr_driver_options options;
227   cairo_surface_t *surface;
228   cairo_status_t status;
229   double width_pt, length_pt;
230
231   this = xr_allocate (name, types);
232   x = this->ext;
233
234   options.driver = this;
235   options.file_name = xstrdup ("pspp.pdf");
236   options.file_type = XR_PDF;
237   options.portrait = true;
238   outp_get_paper_size ("", &options.paper_width, &options.paper_length);
239   options.left_margin = XR_INCH / 2;
240   options.right_margin = XR_INCH / 2;
241   options.top_margin = XR_INCH / 2;
242   options.bottom_margin = XR_INCH / 2;
243
244   outp_parse_options (this->name, option_string, handle_option, &options);
245
246   width_pt = options.paper_width / 1000.0;
247   length_pt = options.paper_length / 1000.0;
248   if (options.portrait)
249     {
250       this->width = pt_to_xr (width_pt);
251       this->length = pt_to_xr (length_pt);
252     }
253   else
254     {
255       this->width = pt_to_xr (width_pt);
256       this->length = pt_to_xr (length_pt);
257     }
258   if (x->draw_headers)
259     options.top_margin += 3 * this->font_height;
260   this->width -= options.left_margin + options.right_margin;
261   this->length -= options.top_margin + options.bottom_margin;
262
263   if (options.file_type == XR_PDF)
264     surface = cairo_pdf_surface_create (options.file_name,
265                                         width_pt, length_pt);
266   else if (options.file_type == XR_PS)
267     surface = cairo_ps_surface_create (options.file_name, width_pt, length_pt);
268   else if (options.file_type == XR_SVG)
269     surface = cairo_svg_surface_create (options.file_name,
270                                         width_pt, length_pt);
271   else
272     NOT_REACHED ();
273
274   status = cairo_surface_status (surface);
275   if (status != CAIRO_STATUS_SUCCESS)
276     {
277       error (0, 0, _("opening output file \"%s\": %s"),
278              options.file_name, cairo_status_to_string (status));
279       cairo_surface_destroy (surface);
280       goto error;
281     }
282
283   x->cairo = cairo_create (surface);
284   cairo_surface_destroy (surface);
285
286   cairo_translate (x->cairo,
287                    xr_to_pt (options.left_margin),
288                    xr_to_pt (options.top_margin));
289
290   if (this->length / this->font_height < 15)
291     {
292       error (0, 0, _("The defined page is not long "
293                      "enough to hold margins and headers, plus least 15 "
294                      "lines of the default fonts.  In fact, there's only "
295                      "room for %d lines."),
296              this->length / this->font_height);
297       goto error;
298     }
299
300   if (!xr_set_cairo (this, x->cairo))
301     goto error;
302
303   outp_register_driver (this);
304   free (options.file_name);
305   return true;
306
307  error:
308   this->class->close_driver (this);
309   outp_free_driver (this);
310   free (options.file_name);
311   return false;
312 }
313
314 static bool
315 xr_close_driver (struct outp_driver *this)
316 {
317   struct xr_driver_ext *x = this->ext;
318   bool ok = true;
319   size_t i;
320
321   if (x->cairo != NULL)
322     {
323       cairo_status_t status;
324
325       cairo_surface_finish (cairo_get_target (x->cairo));
326       status = cairo_status (x->cairo);
327       if (status != CAIRO_STATUS_SUCCESS)
328         error (0, 0, _("error writing output file for %s driver: %s"),
329                this->name, cairo_status_to_string (status));
330       cairo_destroy (x->cairo);
331     }
332
333   for (i = 0; i < OUTP_FONT_CNT; i++)
334     free_font (&x->fonts[i]);
335   free (x);
336
337   return ok;
338 }
339
340 /* Generic option types. */
341 enum
342 {
343   output_file_arg,
344   output_type_arg,
345   paper_size_arg,
346   orientation_arg,
347   line_style_arg,
348   boolean_arg,
349   dimension_arg,
350   string_arg,
351   nonneg_int_arg
352 };
353
354 /* All the options that the Cairo driver supports. */
355 static const struct outp_option option_tab[] =
356 {
357   {"output-file",               output_file_arg,0},
358   {"output-type",               output_type_arg,0},
359   {"paper-size",                paper_size_arg, 0},
360   {"orientation",               orientation_arg,0},
361
362   {"headers",                   boolean_arg,    1},
363
364   {"prop-font",                 string_arg,     OUTP_PROPORTIONAL},
365   {"emph-font",                 string_arg,     OUTP_EMPHASIS},
366   {"fixed-font",                string_arg,     OUTP_FIXED},
367
368   {"left-margin",               dimension_arg,  0},
369   {"right-margin",              dimension_arg,  1},
370   {"top-margin",                dimension_arg,  2},
371   {"bottom-margin",             dimension_arg,  3},
372   {"font-size",                 dimension_arg,  4},
373   {"line-width",                dimension_arg,  5},
374   {"line-gutter",               dimension_arg,  6},
375   {"line-width",                dimension_arg,  7},
376   {NULL, 0, 0},
377 };
378
379 static bool
380 handle_option (void *options_, const char *key, const struct string *val)
381 {
382   struct xr_driver_options *options = options_;
383   struct outp_driver *this = options->driver;
384   struct xr_driver_ext *x = this->ext;
385   int subcat;
386   char *value = ds_cstr (val);
387
388   switch (outp_match_keyword (key, option_tab, &subcat))
389     {
390     case -1:
391       error (0, 0,
392              _("unknown configuration parameter `%s' for Cairo device "
393                "driver"), key);
394       break;
395     case output_file_arg:
396       free (options->file_name);
397       options->file_name = xstrdup (value);
398       break;
399     case output_type_arg:
400       if (!strcmp (value, "pdf"))
401         options->file_type = XR_PDF;
402       else if (!strcmp (value, "ps"))
403         options->file_type = XR_PS;
404       else if (!strcmp (value, "svg"))
405         options->file_type = XR_SVG;
406       else
407         {
408           error (0, 0, _("unknown Cairo output type \"%s\""), value);
409           return false;
410         }
411       break;
412     case paper_size_arg:
413       outp_get_paper_size (value,
414                            &options->paper_width, &options->paper_length);
415       break;
416     case orientation_arg:
417       if (!strcmp (value, "portrait"))
418         options->portrait = true;
419       else if (!strcmp (value, "landscape"))
420         options->portrait = false;
421       else
422         error (0, 0, _("unknown orientation `%s' (valid orientations are "
423                        "`portrait' and `landscape')"), value);
424       break;
425     case boolean_arg:
426       if (!strcmp (value, "on") || !strcmp (value, "true")
427           || !strcmp (value, "yes") || atoi (value))
428         x->draw_headers = true;
429       else if (!strcmp (value, "off") || !strcmp (value, "false")
430                || !strcmp (value, "no") || !strcmp (value, "0"))
431         x->draw_headers = false;
432       else
433         {
434           error (0, 0, _("boolean value expected for %s"), key);
435           return false;
436         }
437       break;
438     case dimension_arg:
439       {
440         int dimension = outp_evaluate_dimension (value);
441
442         if (dimension <= 0)
443           break;
444         switch (subcat)
445           {
446           case 0:
447             options->left_margin = dimension;
448             break;
449           case 1:
450             options->right_margin = dimension;
451             break;
452           case 2:
453             options->top_margin = dimension;
454             break;
455           case 3:
456             options->bottom_margin = dimension;
457             break;
458           case 4:
459             this->font_height = dimension;
460             break;
461           case 5:
462             x->line_width = dimension;
463             break;
464           case 6:
465             x->line_gutter = dimension;
466             break;
467           case 7:
468             x->line_width = dimension;
469             break;
470           default:
471             NOT_REACHED ();
472           }
473       }
474       break;
475     case string_arg:
476       free (x->fonts[subcat].string);
477       x->fonts[subcat].string = ds_xstrdup (val);
478       break;
479     default:
480       NOT_REACHED ();
481     }
482
483   return true;
484 }
485 \f
486 /* Basic file operations. */
487
488 static void
489 xr_open_page (struct outp_driver *this)
490 {
491   struct xr_driver_ext *x = this->ext;
492
493   x->page_number++;
494
495   if (x->draw_headers)
496     draw_headers (this);
497 }
498
499 static void
500 xr_close_page (struct outp_driver *this)
501 {
502   struct xr_driver_ext *x = this->ext;
503   cairo_show_page (x->cairo);
504 }
505
506 static void
507 xr_output_chart (struct outp_driver *this, const struct chart *chart)
508 {
509   struct xr_driver_ext *x = this->ext;
510   struct chart_geometry geom;
511
512   outp_eject_page (this);
513   outp_open_page (this);
514
515   cairo_save (x->cairo);
516   cairo_translate (x->cairo, 0.0, xr_to_pt (this->length));
517   cairo_scale (x->cairo, 1.0, -1.0);
518   chart_geometry_init (x->cairo, &geom,
519                        xr_to_pt (this->width), xr_to_pt (this->length));
520   chart_draw (chart, x->cairo, &geom);
521   chart_geometry_free (x->cairo);
522   cairo_restore (x->cairo);
523
524   outp_close_page (this);
525 }
526 \f
527 /* Draws a line from (x0,y0) to (x1,y1). */
528 static void
529 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
530 {
531   struct xr_driver_ext *x = this->ext;
532   cairo_new_path (x->cairo);
533   cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
534   cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
535   cairo_stroke (x->cairo);
536 }
537
538 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
539    shortening it to X0...X1 if SHORTEN is true.
540    Draws a horizontal line X1...X3 at Y if RIGHT says so,
541    shortening it to X2...X3 if SHORTEN is true. */
542 static void
543 horz_line (struct outp_driver *this,
544            int x0, int x1, int x2, int x3, int y,
545            enum outp_line_style left, enum outp_line_style right,
546            bool shorten)
547 {
548   if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
549     dump_line (this, x0, y, x3, y);
550   else
551     {
552       if (left != OUTP_L_NONE)
553         dump_line (this, x0, y, shorten ? x1 : x2, y);
554       if (right != OUTP_L_NONE)
555         dump_line (this, shorten ? x2 : x1, y, x3, y);
556     }
557 }
558
559 /* Draws a vertical line Y0...Y2 at X if TOP says so,
560    shortening it to Y0...Y1 if SHORTEN is true.
561    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
562    shortening it to Y2...Y3 if SHORTEN is true. */
563 static void
564 vert_line (struct outp_driver *this,
565            int y0, int y1, int y2, int y3, int x,
566            enum outp_line_style top, enum outp_line_style bottom,
567            bool shorten)
568 {
569   if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
570     dump_line (this, x, y0, x, y3);
571   else
572     {
573       if (top != OUTP_L_NONE)
574         dump_line (this, x, y0, x, shorten ? y1 : y2);
575       if (bottom != OUTP_L_NONE)
576         dump_line (this, x, shorten ? y2 : y1, x, y3);
577     }
578 }
579
580 /* Draws a generalized intersection of lines in the rectangle
581    (X0,Y0)-(X3,Y3).  The line coming from the top to the center
582    is of style TOP, from left to center of style LEFT, from
583    bottom to center of style BOTTOM, and from right to center of
584    style RIGHT. */
585 static void
586 xr_line (struct outp_driver *this,
587          int x0, int y0, int x3, int y3,
588          enum outp_line_style top, enum outp_line_style left,
589          enum outp_line_style bottom, enum outp_line_style right)
590 {
591   /* The algorithm here is somewhat subtle, to allow it to handle
592      all the kinds of intersections that we need.
593
594      Three additional ordinates are assigned along the x axis.  The
595      first is xc, midway between x0 and x3.  The others are x1 and
596      x2; for a single vertical line these are equal to xc, and for
597      a double vertical line they are the ordinates of the left and
598      right half of the double line.
599
600      yc, y1, and y2 are assigned similarly along the y axis.
601
602      The following diagram shows the coordinate system and output
603      for double top and bottom lines, single left line, and no
604      right line:
605
606                  x0       x1 xc  x2      x3
607                y0 ________________________
608                   |        #     #       |
609                   |        #     #       |
610                   |        #     #       |
611                   |        #     #       |
612                   |        #     #       |
613      y1 = y2 = yc |#########     #       |
614                   |        #     #       |
615                   |        #     #       |
616                   |        #     #       |
617                   |        #     #       |
618                y3 |________#_____#_______|
619   */
620   struct xr_driver_ext *ext = this->ext;
621
622   /* Offset from center of each line in a pair of double lines. */
623   int double_line_ofs = (ext->line_space + ext->line_width) / 2;
624
625   /* Are the lines along each axis single or double?
626      (It doesn't make sense to have different kinds of line on the
627      same axis, so we don't try to gracefully handle that case.) */
628   bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
629   bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
630
631   /* When horizontal lines are doubled,
632      the left-side line along y1 normally runs from x0 to x2,
633      and the right-side line along y1 from x3 to x1.
634      If the top-side line is also doubled, we shorten the y1 lines,
635      so that the left-side line runs only to x1,
636      and the right-side line only to x2.
637      Otherwise, the horizontal line at y = y1 below would cut off
638      the intersection, which looks ugly:
639                x0       x1     x2      x3
640              y0 ________________________
641                 |        #     #       |
642                 |        #     #       |
643                 |        #     #       |
644                 |        #     #       |
645              y1 |#########     ########|
646                 |                      |
647                 |                      |
648              y2 |######################|
649                 |                      |
650                 |                      |
651              y3 |______________________|
652      It is more of a judgment call when the horizontal line is
653      single.  We actually choose to cut off the line anyhow, as
654      shown in the first diagram above.
655   */
656   bool shorten_y1_lines = top == OUTP_L_DOUBLE;
657   bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
658   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
659   int horz_line_ofs = double_vert ? double_line_ofs : 0;
660   int xc = (x0 + x3) / 2;
661   int x1 = xc - horz_line_ofs;
662   int x2 = xc + horz_line_ofs;
663
664   bool shorten_x1_lines = left == OUTP_L_DOUBLE;
665   bool shorten_x2_lines = right == OUTP_L_DOUBLE;
666   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
667   int vert_line_ofs = double_horz ? double_line_ofs : 0;
668   int yc = (y0 + y3) / 2;
669   int y1 = yc - vert_line_ofs;
670   int y2 = yc + vert_line_ofs;
671
672   if (!double_horz)
673     horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
674   else
675     {
676       horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
677       horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
678     }
679
680   if (!double_vert)
681     vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
682   else
683     {
684       vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
685       vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
686     }
687 }
688
689 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
690    and with the given JUSTIFICATION for THIS driver. */
691 static int
692 draw_text (struct outp_driver *this,
693            const char *string, int x, int y, int max_width,
694            enum outp_justification justification)
695 {
696   struct outp_text text;
697   int width;
698
699   text.font = OUTP_PROPORTIONAL;
700   text.justification = justification;
701   text.string = ss_cstr (string);
702   text.h = max_width;
703   text.v = this->font_height;
704   text.x = x;
705   text.y = y;
706   this->class->text_metrics (this, &text, &width, NULL);
707   this->class->text_draw (this, &text);
708   return width;
709 }
710
711 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
712    and with the given JUSTIFICATION for THIS driver. */
713 static int
714 text_width (struct outp_driver *this, const char *string, enum outp_font font)
715 {
716   struct outp_text text;
717   int width;
718
719   text.font = font;
720   text.justification = OUTP_LEFT;
721   text.string = ss_cstr (string);
722   text.h = INT_MAX;
723   text.v = this->font_height;
724   text.x = 0;
725   text.y = 0;
726   this->class->text_metrics (this, &text, &width, NULL);
727   return width;
728 }
729
730 /* Writes LEFT left-justified and RIGHT right-justified within
731    (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
732 static void
733 draw_header_line (struct outp_driver *this,
734                   const char *left, const char *right,
735                   int x0, int x1, int y)
736 {
737   int right_width = 0;
738   if (right != NULL)
739     right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
740                    + this->prop_em_width);
741   if (left != NULL)
742     draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
743 }
744
745 /* Draw top of page headers for THIS driver. */
746 static void
747 draw_headers (struct outp_driver *this)
748 {
749   struct xr_driver_ext *ext = this->ext;
750   char *r1, *r2;
751   int x0, x1;
752   int y;
753
754   y = -3 * this->font_height;
755   x0 = this->prop_em_width;
756   x1 = this->width - this->prop_em_width;
757
758   /* Draw box. */
759   cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
760                    xr_to_pt (2 * (this->font_height
761                                   + ext->line_width + ext->line_gutter)));
762   cairo_save (ext->cairo);
763   cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
764   cairo_fill_preserve (ext->cairo);
765   cairo_restore (ext->cairo);
766   cairo_stroke (ext->cairo);
767
768   y += ext->line_width + ext->line_gutter;
769
770   r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
771   r2 = xasprintf ("%s - %s", version, host_system);
772
773   draw_header_line (this, outp_title, r1, x0, x1, y);
774   y += this->font_height;
775
776   draw_header_line (this, outp_subtitle, r2, x0, x1, y);
777
778   free (r1);
779   free (r2);
780 }
781 \f
782 /* Format TEXT on THIS driver.
783    If DRAW is nonzero, draw the text.
784    The width of the widest line is stored into *WIDTH, if WIDTH
785    is nonnull.
786    The total height of the text written is stored into *HEIGHT,
787    if HEIGHT is nonnull. */
788 static void
789 text (struct outp_driver *this, const struct outp_text *text, bool draw,
790       int *width, int *height)
791 {
792   struct xr_driver_ext *ext = this->ext;
793   struct xr_font *font = &ext->fonts[text->font];
794
795   pango_layout_set_text (font->layout,
796                          text->string.string, text->string.length);
797   pango_layout_set_alignment (
798     font->layout,
799     (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
800      : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
801      : PANGO_ALIGN_CENTER));
802   pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
803   pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
804   /* XXX need to limit number of lines to those that fit in text->v. */
805
806   if (draw)
807     {
808       int x = text->x;
809       if (text->justification != OUTP_LEFT && text->h != INT_MAX)
810         {
811           int w, h, excess;
812           pango_layout_get_size (font->layout, &w, &h);
813           excess = text->h - w;
814           if (excess > 0)
815             {
816               if (text->justification == OUTP_CENTER)
817                 x += excess / 2;
818               else
819                 x += excess;
820             }
821         }
822       cairo_save (ext->cairo);
823       cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
824       pango_cairo_show_layout (ext->cairo, font->layout);
825       cairo_restore (ext->cairo);
826     }
827
828   if (width != NULL || height != NULL)
829     {
830       int w, h;
831       pango_layout_get_size (font->layout, &w, &h);
832       if (width != NULL)
833         *width = w;
834       if (height != NULL)
835         *height = h;
836     }
837 }
838
839 static void
840 xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
841                  int *width, int *height)
842 {
843   text (this, t, false, width, height);
844 }
845
846 static void
847 xr_text_draw (struct outp_driver *this, const struct outp_text *t)
848 {
849   text (this, t, true, NULL, NULL);
850 }
851 \f
852 /* Attempts to load FONT, initializing its other members based on
853    its 'string' member and the information in THIS.  Returns true
854    if successful, otherwise false. */
855 static bool
856 load_font (struct outp_driver *this, struct xr_font *font)
857 {
858   struct xr_driver_ext *x = this->ext;
859   PangoContext *context;
860   PangoLanguage *language;
861
862   font->desc = pango_font_description_from_string (font->string);
863   if (font->desc == NULL)
864     {
865       error (0, 0, _("\"%s\": bad font specification"), font->string);
866       return false;
867     }
868   pango_font_description_set_absolute_size (font->desc, this->font_height);
869
870   font->layout = pango_cairo_create_layout (x->cairo);
871   pango_layout_set_font_description (font->layout, font->desc);
872
873   language = pango_language_get_default ();
874   context = pango_layout_get_context (font->layout);
875   font->metrics = pango_context_get_metrics (context, font->desc, language);
876
877   return true;
878 }
879
880 /* Frees FONT. */
881 static void
882 free_font (struct xr_font *font)
883 {
884   free (font->string);
885   if (font->desc != NULL)
886     pango_font_description_free (font->desc);
887   pango_font_metrics_unref (font->metrics);
888   g_object_unref (font->layout);
889 }
890
891 /* Cairo driver class. */
892 const struct outp_class cairo_class =
893 {
894   "cairo",
895   0,
896
897   xr_open_driver,
898   xr_close_driver,
899
900   xr_open_page,
901   xr_close_page,
902   NULL,
903
904   xr_output_chart,
905
906   NULL,
907
908   xr_line,
909   xr_text_metrics,
910   xr_text_draw,
911 };