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