output: Make allocating and freeing drivers independent of configuration.
[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 <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 (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 (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 (struct outp_driver *this, const char *key,
322                const struct string *val)
323 {
324   struct xr_driver_ext *x = this->ext;
325   int subcat;
326   char *value = ds_cstr (val);
327
328   switch (outp_match_keyword (key, option_tab, &subcat))
329     {
330     case -1:
331       error (0, 0,
332              _("unknown configuration parameter `%s' for Cairo device "
333                "driver"), key);
334       break;
335     case output_file_arg:
336       free (x->file_name);
337       x->file_name = xstrdup (value);
338       break;
339     case output_type_arg:
340       if (!strcmp (value, "pdf"))
341         x->file_type = XR_PDF;
342       else if (!strcmp (value, "ps"))
343         x->file_type = XR_PS;
344       else if (!strcmp (value, "svg"))
345         x->file_type = XR_SVG;
346       else
347         {
348           error (0, 0, _("unknown Cairo output type \"%s\""), value);
349           return false;
350         }
351       break;
352     case paper_size_arg:
353       outp_get_paper_size (value, &x->paper_width, &x->paper_length);
354       break;
355     case orientation_arg:
356       if (!strcmp (value, "portrait"))
357         x->portrait = true;
358       else if (!strcmp (value, "landscape"))
359         x->portrait = false;
360       else
361         error (0, 0, _("unknown orientation `%s' (valid orientations are "
362                        "`portrait' and `landscape')"), value);
363       break;
364     case boolean_arg:
365       if (!strcmp (value, "on") || !strcmp (value, "true")
366           || !strcmp (value, "yes") || atoi (value))
367         x->draw_headers = true;
368       else if (!strcmp (value, "off") || !strcmp (value, "false")
369                || !strcmp (value, "no") || !strcmp (value, "0"))
370         x->draw_headers = false;
371       else
372         {
373           error (0, 0, _("boolean value expected for %s"), key);
374           return false;
375         }
376       break;
377     case dimension_arg:
378       {
379         int dimension = outp_evaluate_dimension (value);
380
381         if (dimension <= 0)
382           break;
383         switch (subcat)
384           {
385           case 0:
386             x->left_margin = dimension;
387             break;
388           case 1:
389             x->right_margin = dimension;
390             break;
391           case 2:
392             x->top_margin = dimension;
393             break;
394           case 3:
395             x->bottom_margin = dimension;
396             break;
397           case 4:
398             this->font_height = dimension;
399             break;
400           case 5:
401             x->line_width = dimension;
402             break;
403           case 6:
404             x->line_gutter = dimension;
405             break;
406           case 7:
407             x->line_width = dimension;
408             break;
409           default:
410             NOT_REACHED ();
411           }
412       }
413       break;
414     case string_arg:
415       free (x->fonts[subcat].string);
416       x->fonts[subcat].string = ds_xstrdup (val);
417       break;
418     default:
419       NOT_REACHED ();
420     }
421
422   return true;
423 }
424 \f
425 /* Basic file operations. */
426
427 static void
428 xr_open_page (struct outp_driver *this)
429 {
430   struct xr_driver_ext *x = this->ext;
431
432   x->page_number++;
433
434   if (x->draw_headers)
435     draw_headers (this);
436 }
437
438 static void
439 xr_close_page (struct outp_driver *this)
440 {
441   struct xr_driver_ext *x = this->ext;
442   cairo_show_page (x->cairo);
443 }
444
445 static void
446 xr_submit (struct outp_driver *this UNUSED, struct som_entity *s)
447 {
448   switch (s->type)
449     {
450     case SOM_CHART:
451       break;
452     default:
453       NOT_REACHED ();
454     }
455 }
456 \f
457 /* Draws a line from (x0,y0) to (x1,y1). */
458 static void
459 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
460 {
461   struct xr_driver_ext *x = this->ext;
462   cairo_new_path (x->cairo);
463   cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
464   cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
465   cairo_stroke (x->cairo);
466 }
467
468 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
469    shortening it to X0...X1 if SHORTEN is true.
470    Draws a horizontal line X1...X3 at Y if RIGHT says so,
471    shortening it to X2...X3 if SHORTEN is true. */
472 static void
473 horz_line (struct outp_driver *this,
474            int x0, int x1, int x2, int x3, int y,
475            enum outp_line_style left, enum outp_line_style right,
476            bool shorten)
477 {
478   if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
479     dump_line (this, x0, y, x3, y);
480   else
481     {
482       if (left != OUTP_L_NONE)
483         dump_line (this, x0, y, shorten ? x1 : x2, y);
484       if (right != OUTP_L_NONE)
485         dump_line (this, shorten ? x2 : x1, y, x3, y);
486     }
487 }
488
489 /* Draws a vertical line Y0...Y2 at X if TOP says so,
490    shortening it to Y0...Y1 if SHORTEN is true.
491    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
492    shortening it to Y2...Y3 if SHORTEN is true. */
493 static void
494 vert_line (struct outp_driver *this,
495            int y0, int y1, int y2, int y3, int x,
496            enum outp_line_style top, enum outp_line_style bottom,
497            bool shorten)
498 {
499   if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
500     dump_line (this, x, y0, x, y3);
501   else
502     {
503       if (top != OUTP_L_NONE)
504         dump_line (this, x, y0, x, shorten ? y1 : y2);
505       if (bottom != OUTP_L_NONE)
506         dump_line (this, x, shorten ? y2 : y1, x, y3);
507     }
508 }
509
510 /* Draws a generalized intersection of lines in the rectangle
511    (X0,Y0)-(X3,Y3).  The line coming from the top to the center
512    is of style TOP, from left to center of style LEFT, from
513    bottom to center of style BOTTOM, and from right to center of
514    style RIGHT. */
515 static void
516 xr_line (struct outp_driver *this,
517          int x0, int y0, int x3, int y3,
518          enum outp_line_style top, enum outp_line_style left,
519          enum outp_line_style bottom, enum outp_line_style right)
520 {
521   /* The algorithm here is somewhat subtle, to allow it to handle
522      all the kinds of intersections that we need.
523
524      Three additional ordinates are assigned along the x axis.  The
525      first is xc, midway between x0 and x3.  The others are x1 and
526      x2; for a single vertical line these are equal to xc, and for
527      a double vertical line they are the ordinates of the left and
528      right half of the double line.
529
530      yc, y1, and y2 are assigned similarly along the y axis.
531
532      The following diagram shows the coordinate system and output
533      for double top and bottom lines, single left line, and no
534      right line:
535
536                  x0       x1 xc  x2      x3
537                y0 ________________________
538                   |        #     #       |
539                   |        #     #       |
540                   |        #     #       |
541                   |        #     #       |
542                   |        #     #       |
543      y1 = y2 = yc |#########     #       |
544                   |        #     #       |
545                   |        #     #       |
546                   |        #     #       |
547                   |        #     #       |
548                y3 |________#_____#_______|
549   */
550   struct xr_driver_ext *ext = this->ext;
551
552   /* Offset from center of each line in a pair of double lines. */
553   int double_line_ofs = (ext->line_space + ext->line_width) / 2;
554
555   /* Are the lines along each axis single or double?
556      (It doesn't make sense to have different kinds of line on the
557      same axis, so we don't try to gracefully handle that case.) */
558   bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
559   bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
560
561   /* When horizontal lines are doubled,
562      the left-side line along y1 normally runs from x0 to x2,
563      and the right-side line along y1 from x3 to x1.
564      If the top-side line is also doubled, we shorten the y1 lines,
565      so that the left-side line runs only to x1,
566      and the right-side line only to x2.
567      Otherwise, the horizontal line at y = y1 below would cut off
568      the intersection, which looks ugly:
569                x0       x1     x2      x3
570              y0 ________________________
571                 |        #     #       |
572                 |        #     #       |
573                 |        #     #       |
574                 |        #     #       |
575              y1 |#########     ########|
576                 |                      |
577                 |                      |
578              y2 |######################|
579                 |                      |
580                 |                      |
581              y3 |______________________|
582      It is more of a judgment call when the horizontal line is
583      single.  We actually choose to cut off the line anyhow, as
584      shown in the first diagram above.
585   */
586   bool shorten_y1_lines = top == OUTP_L_DOUBLE;
587   bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
588   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
589   int horz_line_ofs = double_vert ? double_line_ofs : 0;
590   int xc = (x0 + x3) / 2;
591   int x1 = xc - horz_line_ofs;
592   int x2 = xc + horz_line_ofs;
593
594   bool shorten_x1_lines = left == OUTP_L_DOUBLE;
595   bool shorten_x2_lines = right == OUTP_L_DOUBLE;
596   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
597   int vert_line_ofs = double_horz ? double_line_ofs : 0;
598   int yc = (y0 + y3) / 2;
599   int y1 = yc - vert_line_ofs;
600   int y2 = yc + vert_line_ofs;
601
602   if (!double_horz)
603     horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
604   else
605     {
606       horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
607       horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
608     }
609
610   if (!double_vert)
611     vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
612   else
613     {
614       vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
615       vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
616     }
617 }
618
619 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
620    and with the given JUSTIFICATION for THIS driver. */
621 static int
622 draw_text (struct outp_driver *this,
623            const char *string, int x, int y, int max_width,
624            enum outp_justification justification)
625 {
626   struct outp_text text;
627   int width;
628
629   text.font = OUTP_PROPORTIONAL;
630   text.justification = justification;
631   text.string = ss_cstr (string);
632   text.h = max_width;
633   text.v = this->font_height;
634   text.x = x;
635   text.y = y;
636   this->class->text_metrics (this, &text, &width, NULL);
637   this->class->text_draw (this, &text);
638   return width;
639 }
640
641 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
642    and with the given JUSTIFICATION for THIS driver. */
643 static int
644 text_width (struct outp_driver *this, const char *string, enum outp_font font)
645 {
646   struct outp_text text;
647   int width;
648
649   text.font = font;
650   text.justification = OUTP_LEFT;
651   text.string = ss_cstr (string);
652   text.h = INT_MAX;
653   text.v = this->font_height;
654   text.x = 0;
655   text.y = 0;
656   this->class->text_metrics (this, &text, &width, NULL);
657   return width;
658 }
659
660 /* Writes LEFT left-justified and RIGHT right-justified within
661    (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
662 static void
663 draw_header_line (struct outp_driver *this,
664                   const char *left, const char *right,
665                   int x0, int x1, int y)
666 {
667   int right_width = 0;
668   if (right != NULL)
669     right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
670                    + this->prop_em_width);
671   if (left != NULL)
672     draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
673 }
674
675 /* Draw top of page headers for THIS driver. */
676 static void
677 draw_headers (struct outp_driver *this)
678 {
679   struct xr_driver_ext *ext = this->ext;
680   char *r1, *r2;
681   int x0, x1;
682   int y;
683
684   y = -3 * this->font_height;
685   x0 = this->prop_em_width;
686   x1 = this->width - this->prop_em_width;
687
688   /* Draw box. */
689   cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
690                    xr_to_pt (2 * (this->font_height
691                                   + ext->line_width + ext->line_gutter)));
692   cairo_save (ext->cairo);
693   cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
694   cairo_fill_preserve (ext->cairo);
695   cairo_restore (ext->cairo);
696   cairo_stroke (ext->cairo);
697
698   y += ext->line_width + ext->line_gutter;
699
700   r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
701   r2 = xasprintf ("%s - %s", version, host_system);
702
703   draw_header_line (this, outp_title, r1, x0, x1, y);
704   y += this->font_height;
705
706   draw_header_line (this, outp_subtitle, r2, x0, x1, y);
707
708   free (r1);
709   free (r2);
710 }
711 \f
712 /* Format TEXT on THIS driver.
713    If DRAW is nonzero, draw the text.
714    The width of the widest line is stored into *WIDTH, if WIDTH
715    is nonnull.
716    The total height of the text written is stored into *HEIGHT,
717    if HEIGHT is nonnull. */
718 static void
719 text (struct outp_driver *this, const struct outp_text *text, bool draw,
720       int *width, int *height)
721 {
722   struct xr_driver_ext *ext = this->ext;
723   struct xr_font *font = &ext->fonts[text->font];
724
725   pango_layout_set_text (font->layout,
726                          text->string.string, text->string.length);
727   pango_layout_set_alignment (
728     font->layout,
729     (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
730      : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
731      : PANGO_ALIGN_CENTER));
732   pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
733   pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
734   /* XXX need to limit number of lines to those that fit in text->v. */
735
736   if (draw)
737     {
738       int x = text->x;
739       if (text->justification != OUTP_LEFT && text->h != INT_MAX)
740         {
741           int w, h, excess;
742           pango_layout_get_size (font->layout, &w, &h);
743           excess = text->h - w;
744           if (excess > 0)
745             {
746               if (text->justification == OUTP_CENTER)
747                 x += excess / 2;
748               else
749                 x += excess;
750             }
751         }
752       cairo_save (ext->cairo);
753       cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
754       pango_cairo_show_layout (ext->cairo, font->layout);
755       cairo_restore (ext->cairo);
756     }
757
758   if (width != NULL || height != NULL)
759     {
760       int w, h;
761       pango_layout_get_size (font->layout, &w, &h);
762       if (width != NULL)
763         *width = w;
764       if (height != NULL)
765         *height = h;
766     }
767 }
768
769 static void
770 xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
771                  int *width, int *height)
772 {
773   text (this, t, false, width, height);
774 }
775
776 static void
777 xr_text_draw (struct outp_driver *this, const struct outp_text *t)
778 {
779   assert (this->page_open);
780   text (this, t, true, NULL, NULL);
781 }
782 \f
783 static void
784 xr_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
785 {
786 #ifdef NO_CHARTS
787   ch->lp = NULL;
788 #else
789   /* XXX libplot doesn't support Cairo yet. */
790 #endif
791 }
792
793 static void
794 xr_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
795 {
796 #ifndef NO_CHARTS
797   /* XXX libplot doesn't support Cairo yet. */
798 #endif
799 }
800 \f
801 /* Attempts to load FONT, initializing its other members based on
802    its 'string' member and the information in THIS.  Returns true
803    if successful, otherwise false. */
804 static bool
805 load_font (struct outp_driver *this, struct xr_font *font)
806 {
807   struct xr_driver_ext *x = this->ext;
808   PangoContext *context;
809   PangoLanguage *language;
810
811   font->desc = pango_font_description_from_string (font->string);
812   if (font->desc == NULL)
813     {
814       error (0, 0, _("\"%s\": bad font specification"), font->string);
815       return false;
816     }
817   pango_font_description_set_absolute_size (font->desc, this->font_height);
818
819   font->layout = pango_cairo_create_layout (x->cairo);
820   pango_layout_set_font_description (font->layout, font->desc);
821
822   language = pango_language_get_default ();
823   context = pango_layout_get_context (font->layout);
824   font->metrics = pango_context_get_metrics (context, font->desc, language);
825
826   return true;
827 }
828
829 /* Frees FONT. */
830 static void
831 free_font (struct xr_font *font)
832 {
833   free (font->string);
834   if (font->desc != NULL)
835     pango_font_description_free (font->desc);
836   pango_font_metrics_unref (font->metrics);
837   g_object_unref (font->layout);
838 }
839
840 /* Cairo driver class. */
841 const struct outp_class cairo_class =
842 {
843   "cairo",
844   0,
845
846   xr_open_driver,
847   xr_close_driver,
848
849   xr_open_page,
850   xr_close_page,
851   NULL,
852
853   xr_submit,
854
855   xr_line,
856   xr_text_metrics,
857   xr_text_draw,
858
859   xr_chart_initialise,
860   xr_chart_finalise
861 };