9384232841c304f72019abdf403e2449e62dad07
[pspp] / 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.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=Times-Roman
61    emph-font=Times-Italic
62    fixed-font=Courier
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 \f
506 /* Draws a line from (x0,y0) to (x1,y1). */
507 static void
508 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
509 {
510   struct xr_driver_ext *x = this->ext;
511   cairo_new_path (x->cairo);
512   cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
513   cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
514   cairo_stroke (x->cairo);
515 }
516
517 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
518    shortening it to X0...X1 if SHORTEN is true.
519    Draws a horizontal line X1...X3 at Y if RIGHT says so,
520    shortening it to X2...X3 if SHORTEN is true. */
521 static void
522 horz_line (struct outp_driver *this,
523            int x0, int x1, int x2, int x3, int y,
524            enum outp_line_style left, enum outp_line_style right,
525            bool shorten)
526 {
527   if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
528     dump_line (this, x0, y, x3, y);
529   else
530     {
531       if (left != OUTP_L_NONE)
532         dump_line (this, x0, y, shorten ? x1 : x2, y);
533       if (right != OUTP_L_NONE)
534         dump_line (this, shorten ? x2 : x1, y, x3, y);
535     }
536 }
537
538 /* Draws a vertical line Y0...Y2 at X if TOP says so,
539    shortening it to Y0...Y1 if SHORTEN is true.
540    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
541    shortening it to Y2...Y3 if SHORTEN is true. */
542 static void
543 vert_line (struct outp_driver *this,
544            int y0, int y1, int y2, int y3, int x,
545            enum outp_line_style top, enum outp_line_style bottom,
546            bool shorten)
547 {
548   if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
549     dump_line (this, x, y0, x, y3);
550   else
551     {
552       if (top != OUTP_L_NONE)
553         dump_line (this, x, y0, x, shorten ? y1 : y2);
554       if (bottom != OUTP_L_NONE)
555         dump_line (this, x, shorten ? y2 : y1, x, y3);
556     }
557 }
558
559 /* Draws a generalized intersection of lines in the rectangle
560    (X0,Y0)-(X3,Y3).  The line coming from the top to the center
561    is of style TOP, from left to center of style LEFT, from
562    bottom to center of style BOTTOM, and from right to center of
563    style RIGHT. */
564 static void
565 xr_line (struct outp_driver *this,
566          int x0, int y0, int x3, int y3,
567          enum outp_line_style top, enum outp_line_style left,
568          enum outp_line_style bottom, enum outp_line_style right)
569 {
570   /* The algorithm here is somewhat subtle, to allow it to handle
571      all the kinds of intersections that we need.
572
573      Three additional ordinates are assigned along the x axis.  The
574      first is xc, midway between x0 and x3.  The others are x1 and
575      x2; for a single vertical line these are equal to xc, and for
576      a double vertical line they are the ordinates of the left and
577      right half of the double line.
578
579      yc, y1, and y2 are assigned similarly along the y axis.
580
581      The following diagram shows the coordinate system and output
582      for double top and bottom lines, single left line, and no
583      right line:
584
585                  x0       x1 xc  x2      x3
586                y0 ________________________
587                   |        #     #       |
588                   |        #     #       |
589                   |        #     #       |
590                   |        #     #       |
591                   |        #     #       |
592      y1 = y2 = yc |#########     #       |
593                   |        #     #       |
594                   |        #     #       |
595                   |        #     #       |
596                   |        #     #       |
597                y3 |________#_____#_______|
598   */
599   struct xr_driver_ext *ext = this->ext;
600
601   /* Offset from center of each line in a pair of double lines. */
602   int double_line_ofs = (ext->line_space + ext->line_width) / 2;
603
604   /* Are the lines along each axis single or double?
605      (It doesn't make sense to have different kinds of line on the
606      same axis, so we don't try to gracefully handle that case.) */
607   bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
608   bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
609
610   /* When horizontal lines are doubled,
611      the left-side line along y1 normally runs from x0 to x2,
612      and the right-side line along y1 from x3 to x1.
613      If the top-side line is also doubled, we shorten the y1 lines,
614      so that the left-side line runs only to x1,
615      and the right-side line only to x2.
616      Otherwise, the horizontal line at y = y1 below would cut off
617      the intersection, which looks ugly:
618                x0       x1     x2      x3
619              y0 ________________________
620                 |        #     #       |
621                 |        #     #       |
622                 |        #     #       |
623                 |        #     #       |
624              y1 |#########     ########|
625                 |                      |
626                 |                      |
627              y2 |######################|
628                 |                      |
629                 |                      |
630              y3 |______________________|
631      It is more of a judgment call when the horizontal line is
632      single.  We actually choose to cut off the line anyhow, as
633      shown in the first diagram above.
634   */
635   bool shorten_y1_lines = top == OUTP_L_DOUBLE;
636   bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
637   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
638   int horz_line_ofs = double_vert ? double_line_ofs : 0;
639   int xc = (x0 + x3) / 2;
640   int x1 = xc - horz_line_ofs;
641   int x2 = xc + horz_line_ofs;
642
643   bool shorten_x1_lines = left == OUTP_L_DOUBLE;
644   bool shorten_x2_lines = right == OUTP_L_DOUBLE;
645   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
646   int vert_line_ofs = double_horz ? double_line_ofs : 0;
647   int yc = (y0 + y3) / 2;
648   int y1 = yc - vert_line_ofs;
649   int y2 = yc + vert_line_ofs;
650
651   if (!double_horz)
652     horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
653   else
654     {
655       horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
656       horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
657     }
658
659   if (!double_vert)
660     vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
661   else
662     {
663       vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
664       vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
665     }
666 }
667
668 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
669    and with the given JUSTIFICATION for THIS driver. */
670 static int
671 draw_text (struct outp_driver *this,
672            const char *string, int x, int y, int max_width,
673            enum outp_justification justification)
674 {
675   struct outp_text text;
676   int width;
677
678   text.font = OUTP_PROPORTIONAL;
679   text.justification = justification;
680   text.string = ss_cstr (string);
681   text.h = max_width;
682   text.v = this->font_height;
683   text.x = x;
684   text.y = y;
685   this->class->text_metrics (this, &text, &width, NULL);
686   this->class->text_draw (this, &text);
687   return width;
688 }
689
690 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
691    and with the given JUSTIFICATION for THIS driver. */
692 static int
693 text_width (struct outp_driver *this, const char *string, enum outp_font font)
694 {
695   struct outp_text text;
696   int width;
697
698   text.font = font;
699   text.justification = OUTP_LEFT;
700   text.string = ss_cstr (string);
701   text.h = INT_MAX;
702   text.v = this->font_height;
703   text.x = 0;
704   text.y = 0;
705   this->class->text_metrics (this, &text, &width, NULL);
706   return width;
707 }
708
709 /* Writes LEFT left-justified and RIGHT right-justified within
710    (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
711 static void
712 draw_header_line (struct outp_driver *this,
713                   const char *left, const char *right,
714                   int x0, int x1, int y)
715 {
716   int right_width = 0;
717   if (right != NULL)
718     right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
719                    + this->prop_em_width);
720   if (left != NULL)
721     draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
722 }
723
724 /* Draw top of page headers for THIS driver. */
725 static void
726 draw_headers (struct outp_driver *this)
727 {
728   struct xr_driver_ext *ext = this->ext;
729   char *r1, *r2;
730   int x0, x1;
731   int y;
732
733   y = -3 * this->font_height;
734   x0 = this->prop_em_width;
735   x1 = this->width - this->prop_em_width;
736
737   /* Draw box. */
738   cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
739                    xr_to_pt (2 * (this->font_height
740                                   + ext->line_width + ext->line_gutter)));
741   cairo_save (ext->cairo);
742   cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
743   cairo_fill_preserve (ext->cairo);
744   cairo_restore (ext->cairo);
745   cairo_stroke (ext->cairo);
746
747   y += ext->line_width + ext->line_gutter;
748
749   r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
750   r2 = xasprintf ("%s - %s", version, host_system);
751
752   draw_header_line (this, outp_title, r1, x0, x1, y);
753   y += this->font_height;
754
755   draw_header_line (this, outp_subtitle, r2, x0, x1, y);
756
757   free (r1);
758   free (r2);
759 }
760 \f
761 /* Format TEXT on THIS driver.
762    If DRAW is nonzero, draw the text.
763    The width of the widest line is stored into *WIDTH, if WIDTH
764    is nonnull.
765    The total height of the text written is stored into *HEIGHT,
766    if HEIGHT is nonnull. */
767 static void
768 text (struct outp_driver *this, const struct outp_text *text, bool draw,
769       int *width, int *height)
770 {
771   struct xr_driver_ext *ext = this->ext;
772   struct xr_font *font = &ext->fonts[text->font];
773
774   pango_layout_set_text (font->layout,
775                          text->string.string, text->string.length);
776   pango_layout_set_alignment (
777     font->layout,
778     (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
779      : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
780      : PANGO_ALIGN_CENTER));
781   pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
782   pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
783   /* XXX need to limit number of lines to those that fit in text->v. */
784
785   if (draw)
786     {
787       int x = text->x;
788       if (text->justification != OUTP_LEFT && text->h != INT_MAX)
789         {
790           int w, h, excess;
791           pango_layout_get_size (font->layout, &w, &h);
792           excess = text->h - w;
793           if (excess > 0)
794             {
795               if (text->justification == OUTP_CENTER)
796                 x += excess / 2;
797               else
798                 x += excess;
799             }
800         }
801       cairo_save (ext->cairo);
802       cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
803       pango_cairo_show_layout (ext->cairo, font->layout);
804       cairo_restore (ext->cairo);
805     }
806
807   if (width != NULL || height != NULL)
808     {
809       int w, h;
810       pango_layout_get_size (font->layout, &w, &h);
811       if (width != NULL)
812         *width = w;
813       if (height != NULL)
814         *height = h;
815     }
816 }
817
818 static void
819 xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
820                  int *width, int *height)
821 {
822   text (this, t, false, width, height);
823 }
824
825 static void
826 xr_text_draw (struct outp_driver *this, const struct outp_text *t)
827 {
828   text (this, t, true, NULL, NULL);
829 }
830 \f
831 /* Attempts to load FONT, initializing its other members based on
832    its 'string' member and the information in THIS.  Returns true
833    if successful, otherwise false. */
834 static bool
835 load_font (struct outp_driver *this, struct xr_font *font)
836 {
837   struct xr_driver_ext *x = this->ext;
838   PangoContext *context;
839   PangoLanguage *language;
840
841   font->desc = pango_font_description_from_string (font->string);
842   if (font->desc == NULL)
843     {
844       error (0, 0, _("\"%s\": bad font specification"), font->string);
845       return false;
846     }
847   pango_font_description_set_absolute_size (font->desc, this->font_height);
848
849   font->layout = pango_cairo_create_layout (x->cairo);
850   pango_layout_set_font_description (font->layout, font->desc);
851
852   language = pango_language_get_default ();
853   context = pango_layout_get_context (font->layout);
854   font->metrics = pango_context_get_metrics (context, font->desc, language);
855
856   return true;
857 }
858
859 /* Frees FONT. */
860 static void
861 free_font (struct xr_font *font)
862 {
863   free (font->string);
864   if (font->desc != NULL)
865     pango_font_description_free (font->desc);
866   pango_font_metrics_unref (font->metrics);
867   g_object_unref (font->layout);
868 }
869
870 /* Cairo driver class. */
871 const struct outp_class cairo_class =
872 {
873   "cairo",
874   0,
875
876   xr_open_driver,
877   xr_close_driver,
878
879   xr_open_page,
880   xr_close_page,
881   NULL,
882
883   NULL,
884
885   NULL,
886
887   xr_line,
888   xr_text_metrics,
889   xr_text_draw,
890 };