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