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