PLplot  5.9.9
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
cairo.c
Go to the documentation of this file.
1 // June 2, 2007
2 //
3 // Graphics drivers that are based on the Cairo / Pango Libraries.
4 //
5 // Copyright (C) 2008 Hazen Babcock
6 // Copyright (C) 2009, 2010 Hezekiah M. Carty
7 //
8 // This file is part of PLplot.
9 //
10 // PLplot is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU Library General Public License as published
12 // by the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // PLplot is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Library General Public License for more details.
19 //
20 // You should have received a copy of the GNU Library General Public License
21 // along with PLplot; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 //
25 
26 //--------------------------------------------------------------------------
27 // Header files
28 //--------------------------------------------------------------------------
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <math.h>
33 
34 #include <cairo.h>
35 #include <pango/pangocairo.h>
36 
37 // PLplot header files (must occur before driver-dependent includes)
38 
39 #include "plDevs.h"
40 #include "plplotP.h"
41 #include "drivers.h"
42 
43 // Driver-dependent includes
44 #if defined ( PLD_xcairo )
45 #include <cairo-xlib.h>
46 #include <X11/X.h>
47 #include <X11/Xlib.h>
48 #include <X11/Xutil.h>
49 #include <X11/cursorfont.h>
50 #include <X11/keysym.h>
51 #endif
52 #if defined ( PLD_pdfcairo )
53 #include <cairo-pdf.h>
54 #endif
55 #if defined ( PLD_pscairo )
56 #include <cairo-ps.h>
57 #endif
58 #if defined ( PLD_svgcairo )
59 #include <cairo-svg.h>
60 #endif
61 #if defined ( PLD_wincairo )
62 #include <windows.h>
63 #endif
64 
65 //--------------------------------------------------------------------------
66 // Constants & global (to this file) variables
67 //--------------------------------------------------------------------------
68 
69 #define DPI 72
70 #define PLCAIRO_DEFAULT_X 720
71 #define PLCAIRO_DEFAULT_Y 540
72 
73 #define MAX_STRING_LEN 500
74 #define MAX_MARKUP_LEN MAX_STRING_LEN * 10
75 
76 static int text_clipping;
77 static int text_anti_aliasing;
79 static int external_drawable;
80 static int rasterize_image;
81 static int set_background;
82 static int image_buffering;
83 static int already_warned = 0;
84 
85 static DrvOpt cairo_options[] = { { "text_clipping", DRV_INT, &text_clipping, "Use text clipping (text_clipping=0|1)" },
86  { "text_anti_aliasing", DRV_INT, &text_anti_aliasing, "Set desired text anti-aliasing (text_anti_aliasing=0|1|2|3). The numbers are in the same order as the cairo_antialias_t enumeration documented at http://cairographics.org/manual/cairo-cairo-t.html#cairo-antialias-t)" },
87  { "graphics_anti_aliasing", DRV_INT, &graphics_anti_aliasing, "Set desired graphics anti-aliasing (graphics_anti_aliasing=0|1|2|3). The numbers are in the same order as the cairo_antialias_t enumeration documented at http://cairographics.org/manual/cairo-cairo-t.html#cairo-antialias-t" },
88  { "external_drawable", DRV_INT, &external_drawable, "Plot to external X drawable" },
89  { "rasterize_image", DRV_INT, &rasterize_image, "Raster or vector image rendering (rasterize_image=0|1)" },
90  { "set_background", DRV_INT, &set_background, "Set the background for the extcairo device (set_background=0|1). If 1 then the plot background will set by PLplot" },
91  { "image_buffering", DRV_INT, &image_buffering, "Buffered offscreen rendering for the xcairo device (image_buffering=0|1)." },
92  { NULL, DRV_INT, NULL, NULL } };
93 
94 typedef struct
95 {
96  cairo_surface_t *cairoSurface;
97  cairo_t *cairoContext;
98  cairo_surface_t *cairoSurface_raster;
106  double downscale;
108  short upDown;
109  float fontSize;
110 
111  // These are arguments for plP_script_scale which must be retained
112  // in aStream for the alt_unicode approach. level has an
113  // identical meaning to upDown above, but it is incremented and
114  // decremented in plP_script_scale as well as other places in the
115  // code so the only safe thing to do is to treat level separately
116  // from upDown.
117  PLFLT old_sscale, sscale, old_soffset, soffset;
119 
120 #if defined ( PLD_xcairo )
121  cairo_surface_t *cairoSurface_X;
122  cairo_t *cairoContext_X;
123  short exit_event_loop;
124  Display *XDisplay;
125  Window XWindow;
126  unsigned int xdrawable_mode;
127 #endif
128 #if defined ( PLD_memcairo )
129  unsigned char *memory;
130  unsigned char *cairo_format_memory;
131  char bigendian;
132 #endif
133 #if defined ( PLD_wincairo )
134  cairo_surface_t *cairoSurface_win;
135  cairo_t *cairoContext_win;
136  WNDCLASSEX wndclass;
137  HWND hwnd;
138  MSG msg;
139  HDC hdc;
140  HDC SCRN_hdc;
141  COLORREF oldcolour;
142  RECT rect;
143 #endif
144 } PLCairo;
145 
147 #if defined ( PLD_xcairo )
148  "xcairo:Cairo X Windows Driver:1:cairo:100:xcairo\n"
149 #endif
150 #if defined ( PLD_pdfcairo )
151  "pdfcairo:Cairo PDF Driver:0:cairo:101:pdfcairo\n"
152 #endif
153 #if defined ( PLD_pscairo )
154  "pscairo:Cairo PS Driver:0:cairo:102:pscairo\n"
155 #endif
156 #if defined ( PLD_epscairo )
157  "epscairo:Cairo EPS Driver:0:cairo:103:epscairo\n"
158 #endif
159 #if defined ( PLD_svgcairo )
160  "svgcairo:Cairo SVG Driver:0:cairo:104:svgcairo\n"
161 #endif
162 #if defined ( PLD_pngcairo )
163  "pngcairo:Cairo PNG Driver:0:cairo:105:pngcairo\n"
164 #endif
165 #if defined ( PLD_memcairo )
166  "memcairo:Cairo Memory Driver:0:cairo:106:memcairo\n"
167 #endif
168 #if defined ( PLD_extcairo )
169  "extcairo:Cairo External Context Driver:0:cairo:107:extcairo\n"
170 #endif
171 #if defined ( PLD_wincairo )
172  "wincairo:Cairo Microscoft Windows Driver:0:cairo:108:wincairo\n"
173 #endif
174 ;
175 
176 //
177 // Structure for passing external drawables to xcairo devices via
178 // the PLESC_DEVINIT escape function.
179 //
180 #if defined ( PLD_xcairo )
181 typedef struct
182 {
183  Display *display;
184  Drawable drawable;
185 } PLXcairoDrawableInfo;
186 #endif
187 
188 //--------------------------------------------------------------------------
189 // Font style and weight lookup tables (copied
190 // from the psttf driver).
191 //--------------------------------------------------------------------------
192 
193 #define NPANGOLOOKUP 5
194 
196  "sans",
197  "serif",
198  "monospace",
199  "sans,serif",
200  "sans,serif"
201 };
202 
204  "PLPLOT_FREETYPE_SANS_FAMILY",
205  "PLPLOT_FREETYPE_SERIF_FAMILY",
206  "PLPLOT_FREETYPE_MONO_FAMILY",
207  "PLPLOT_FREETYPE_SCRIPT_FAMILY",
208  "PLPLOT_FREETYPE_SYMBOL_FAMILY"
209 };
210 
211 #define FAMILY_LOOKUP_LEN 1024
213 
214 #define TAG_LEN 200
215 
216 const char *weightLookup[2] = {
217  "normal",
218  "bold"
219 };
220 
221 const char *styleLookup[3] = {
222  "normal",
223  "italic",
224  "oblique"
225 };
226 
227 //--------------------------------------------------------------------------
228 //--------------------------------------------------------------------------
229 //
230 // That which is common to all the Cairo Drivers
231 //
232 //--------------------------------------------------------------------------
233 //--------------------------------------------------------------------------
234 
235 //--------------------------------------------------------------------------
236 // function declarations
237 //--------------------------------------------------------------------------
238 
239 // General
240 
242 cairo_status_t write_to_stream( void *, unsigned char *, unsigned int );
243 void set_clip( PLStream *pls );
244 int cairo_family_check( PLStream *pls );
245 
246 // String processing
247 
248 static void proc_str( PLStream *, EscText * );
249 static void text_begin_cairo( PLStream *pls, EscText *args );
250 static void text_char_cairo( PLStream *pls, EscText *args );
251 static void text_esc_cairo( PLStream *pls, EscText *args );
252 static void text_end_cairo( PLStream *pls, EscText *args );
253 static char *ucs4_to_pango_markup_format( PLUNICODE *, int, float );
254 static void open_span_tag( char *, PLUNICODE, float, int );
255 static void close_span_tag( char *, int );
256 static char *rise_span_tag( int, float, float, float );
257 
258 // Graphics
259 
260 static void set_current_context( PLStream * );
261 static void poly_line( PLStream *, short *, short *, PLINT );
262 static void filled_polygon( PLStream *pls, short *xa, short *ya, PLINT npts );
263 static void gradient( PLStream *pls, short *xa, short *ya, PLINT npts );
264 static void arc( PLStream *, arc_struct * );
265 static void rotate_cairo_surface( PLStream *, float, float, float, float, float, float, PLBOOL );
266 static void blit_to_x( PLStream *pls, double x, double y, double w, double h );
267 // Rasterization of plotted material
268 static void start_raster( PLStream* );
269 static void end_raster( PLStream* );
270 // Get/set drawing mode
271 static void set_mode( PLStream*, PLINT* );
272 static void get_mode( PLStream*, PLINT* );
273 // Get / set line properties
274 void get_line_properties( PLCairo *aStream, cairo_line_join_t *join, cairo_line_cap_t *cap );
275 void set_line_properties( PLCairo *aStream, cairo_line_join_t join, cairo_line_cap_t cap );
276 
277 
278 // PLplot interface functions
279 
280 // general
281 void plD_bop_cairo( PLStream * );
282 void plD_eop_cairo( PLStream * );
283 void plD_state_cairo( PLStream *, PLINT );
284 void plD_esc_cairo( PLStream *, PLINT, void * );
285 void plD_tidy_cairo( PLStream * );
286 void plD_line_cairo( PLStream *, short, short, short, short );
287 void plD_polyline_cairo( PLStream *, short *, short *, PLINT );
288 
289 //--------------------------------------------------------------------------
290 // start_raster()
291 //
292 // Set up off-screen rasterized rendering
293 //--------------------------------------------------------------------------
294 
295 void start_raster( PLStream *pls )
296 {
297  PLCairo *aStream;
298  cairo_surface_t *tmp_sfc;
299  cairo_t *tmp_context;
300 
301  aStream = (PLCairo *) pls->dev;
302 
303  // Do not use the external surface if the user says not to
304  if ( !aStream->rasterize_image )
305  return;
306 
307  // Create an image surface and context for the offscreen rendering
308  aStream->cairoSurface_raster =
309  //
310  // cairo_surface_create_similar( aStream->cairoSurface,
311  // CAIRO_CONTENT_COLOR,
312  // pls->xlength, pls->ylength );
313  //
314  cairo_image_surface_create( CAIRO_FORMAT_ARGB32,
315  pls->xlength, pls->ylength );
316  aStream->cairoContext_raster = cairo_create( aStream->cairoSurface_raster );
317 
318  // Disable antialiasing for the raster surface. The output seems to look
319  // better that way.
320  cairo_set_antialias( aStream->cairoContext_raster, CAIRO_ANTIALIAS_NONE );
321 
322  // Swap the raster and main plot surfaces and contexts
323  tmp_sfc = aStream->cairoSurface;
324  tmp_context = aStream->cairoContext;
325  aStream->cairoSurface = aStream->cairoSurface_raster;
326  aStream->cairoContext = aStream->cairoContext_raster;
327  // Save the main plot surface and context for when we are finished
328  aStream->cairoSurface_raster = tmp_sfc;
329  aStream->cairoContext_raster = tmp_context;
330 }
331 
332 //--------------------------------------------------------------------------
333 // end_raster()
334 //
335 // Finish off-screen rasterized rendering and copy the result on to the
336 // main plot surface.
337 //--------------------------------------------------------------------------
338 
339 void end_raster( PLStream *pls )
340 {
341  PLCairo *aStream;
342  cairo_surface_t *tmp_sfc;
343  cairo_t *tmp_context;
344 
345  aStream = (PLCairo *) pls->dev;
346 
347  // TODO FIXME: This should really only copy the used portion of the
348  // offscreen pixmap.
349 
350  // Do not use the external surface if the user says not to
351  if ( !aStream->rasterize_image )
352  return;
353 
354  // Some Cairo devices support delayed device setup (eg: xcairo with
355  // external drawable and extcairo with an external context).
356  if ( aStream->cairoContext == NULL )
357  plexit( "Can not plot to a Cairo device with no context" );
358 
359  // Restore the main plot surface and context for future plotting
360  tmp_sfc = aStream->cairoSurface;
361  tmp_context = aStream->cairoContext;
362  aStream->cairoSurface = aStream->cairoSurface_raster;
363  aStream->cairoContext = aStream->cairoContext_raster;
364  aStream->cairoSurface_raster = tmp_sfc;
365  aStream->cairoContext_raster = tmp_context;
366 
367  // Blit the raster surface on to the main plot
368  cairo_set_source_surface( aStream->cairoContext, aStream->cairoSurface_raster, 0.0, 0.0 );
369  cairo_paint( aStream->cairoContext );
370 
371  // Free the now extraneous surface and context
372  cairo_destroy( aStream->cairoContext_raster );
373  cairo_surface_destroy( aStream->cairoSurface_raster );
374 }
375 
376 //--------------------------------------------------------------------------
377 // plD_bop_cairo()
378 //
379 // Set up for the next page.
380 //--------------------------------------------------------------------------
381 
383 {
384  PLCairo *aStream;
385 
386  aStream = (PLCairo *) pls->dev;
387 
388  // Some Cairo devices support delayed device setup (eg: xcairo with
389  // external drawable and extcairo with an external context).
390  if ( aStream->cairoContext == NULL )
391  return;
392 
393  // Fill in the window with the background color.
394  cairo_rectangle( aStream->cairoContext, 0.0, 0.0, pls->xlength, pls->ylength );
395  if ( (double) pls->cmap0[0].a < 1.0 )
396  {
397  cairo_set_source_rgba( aStream->cairoContext, 1.0, 1.0, 1.0, 1.0 );
398  cairo_fill_preserve( aStream->cairoContext );
399  }
400  cairo_set_source_rgba( aStream->cairoContext,
401  (double) pls->cmap0[0].r / 255.0,
402  (double) pls->cmap0[0].g / 255.0,
403  (double) pls->cmap0[0].b / 255.0,
404  (double) pls->cmap0[0].a );
405  cairo_fill( aStream->cairoContext );
406 }
407 
408 //--------------------------------------------------------------------------
409 // plD_line_cairo()
410 //
411 // Draw a line in the current color from (x1,y1) to (x2,y2).
412 //--------------------------------------------------------------------------
413 
414 //--------------------------------------------------------------------------
415 // (get|set)_line_properties
416 //
417 // (Get|Set) the current Cairo line drawing properties.
418 //--------------------------------------------------------------------------
419 void get_line_properties( PLCairo *aStream, cairo_line_join_t *join, cairo_line_cap_t *cap )
420 {
421  *join = cairo_get_line_join( aStream->cairoContext );
422  *cap = cairo_get_line_cap( aStream->cairoContext );
423 }
424 
425 void set_line_properties( PLCairo *aStream, cairo_line_join_t join, cairo_line_cap_t cap )
426 {
427  cairo_set_line_join( aStream->cairoContext, join );
428  cairo_set_line_cap( aStream->cairoContext, cap );
429 }
430 
431 void plD_line_cairo( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
432 {
433  PLCairo *aStream;
434 
435  aStream = (PLCairo *) pls->dev;
436 
437  set_current_context( pls );
438 
439  cairo_save( aStream->cairoContext );
440 
441  set_line_properties( aStream, CAIRO_LINE_JOIN_BEVEL, CAIRO_LINE_CAP_ROUND );
442 
443  cairo_move_to( aStream->cairoContext, aStream->downscale * (double) x1a, aStream->downscale * (double) y1a );
444  cairo_line_to( aStream->cairoContext, aStream->downscale * (double) x2a, aStream->downscale * (double) y2a );
445 
446  cairo_stroke( aStream->cairoContext );
447 
448  cairo_restore( aStream->cairoContext );
449 }
450 
451 //--------------------------------------------------------------------------
452 // plD_polyline_cairo()
453 //
454 // Draw a polyline in the current color.
455 //--------------------------------------------------------------------------
456 
457 void plD_polyline_cairo( PLStream *pls, short *xa, short *ya, PLINT npts )
458 {
459  PLCairo *aStream;
460 
461  aStream = (PLCairo *) pls->dev;
462 
463  cairo_save( aStream->cairoContext );
464 
465  set_line_properties( aStream, CAIRO_LINE_JOIN_BEVEL, CAIRO_LINE_CAP_BUTT );
466 
467  poly_line( pls, xa, ya, npts );
468 
469  cairo_stroke( aStream->cairoContext );
470 
471  cairo_restore( aStream->cairoContext );
472 }
473 
474 //--------------------------------------------------------------------------
475 // plD_eop_cairo()
476 //
477 // Generic end of page.
478 //--------------------------------------------------------------------------
479 
481 {
482  PLCairo *aStream;
483 
484  aStream = (PLCairo *) pls->dev;
485 
486  cairo_show_page( aStream->cairoContext );
487 }
488 
489 //--------------------------------------------------------------------------
490 // plD_tidy_cairo()
491 //
492 // General: Close graphics file or otherwise clean up.
493 //--------------------------------------------------------------------------
494 
496 {
497  PLCairo *aStream;
498 
499  aStream = (PLCairo *) pls->dev;
500 
501  // Free the cairo context and surface.
502  cairo_destroy( aStream->cairoContext );
503  cairo_surface_destroy( aStream->cairoSurface );
504 
505  plCloseFile( pls );
506 }
507 
508 //--------------------------------------------------------------------------
509 // plD_state_cairo()
510 //
511 // Handle change in PLStream state (color, pen width, fill attribute, etc).
512 //
513 // Nothing is done here because these attributes are acquired from
514 // PLStream for each element that is drawn.
515 //--------------------------------------------------------------------------
516 
518 {
519 }
520 
521 //--------------------------------------------------------------------------
522 // plD_esc_cairo()
523 //
524 // Generic escape function.
525 //--------------------------------------------------------------------------
526 
527 void plD_esc_cairo( PLStream *pls, PLINT op, void *ptr )
528 {
529  PLCairo *aStream;
530 
531  aStream = (PLCairo *) pls->dev;
532 
533  switch ( op )
534  {
535  case PLESC_FILL: // filled polygon
536  filled_polygon( pls, pls->dev_x, pls->dev_y, pls->dev_npts );
537  break;
538  case PLESC_GRADIENT: // render a gradient within a polygon.
539  gradient( pls, pls->dev_x, pls->dev_y, pls->dev_npts );
540  break;
541  case PLESC_HAS_TEXT:
542  if ( !pls->alt_unicode )
543  {
544  proc_str( pls, (EscText *) ptr );
545  }
546  break;
547  case PLESC_BEGIN_TEXT: // get ready to get a handle a string of text
548  text_begin_cairo( pls, (EscText *) ptr );
549  break;
550  case PLESC_TEXT_CHAR: // handle a character of text to display
551  text_char_cairo( pls, (EscText *) ptr );
552  break;
553  case PLESC_CONTROL_CHAR: // handle a control character (super/subscript of fontchange)
554  text_esc_cairo( pls, (EscText *) ptr );
555  break;
556  case PLESC_END_TEXT: // finish a string of text
557  text_end_cairo( pls, (EscText *) ptr );
558  break;
559  case PLESC_START_RASTERIZE: // Start offscreen/rasterized rendering
560  start_raster( pls );
561  break;
562  case PLESC_END_RASTERIZE: // End offscreen/rasterized rendering
563  end_raster( pls );
564  break;
565  case PLESC_ARC: // Draw an arc, either filled or outline
566  arc( pls, (arc_struct *) ptr );
567  break;
568  case PLESC_MODESET: // Set drawing mode
569  set_mode( pls, (int *) ptr );
570  break;
571  case PLESC_MODEGET: // Get drawing mode
572  get_mode( pls, (int *) ptr );
573  break;
574  }
575 }
576 
577 
578 //--------------------------------------------------------------------------
579 // set_mode
580 //
581 // Set drawing mode.
582 //--------------------------------------------------------------------------
583 void set_mode( PLStream *pls, PLINT *mode )
584 {
585  PLCairo *aStream;
586 
587  aStream = (PLCairo *) pls->dev;
588 
589  switch ( *mode )
590  {
591  case PL_DRAWMODE_UNKNOWN: // Invalid - do nothing
592  break;
593  case PL_DRAWMODE_DEFAULT:
594  cairo_set_operator( aStream->cairoContext, CAIRO_OPERATOR_OVER );
595  break;
596  case PL_DRAWMODE_REPLACE:
597  cairo_set_operator( aStream->cairoContext, CAIRO_OPERATOR_SOURCE );
598  break;
599  case PL_DRAWMODE_XOR:
600  cairo_set_operator( aStream->cairoContext, CAIRO_OPERATOR_XOR );
601  break;
602  }
603  return;
604 }
605 
606 //--------------------------------------------------------------------------
607 // get_mode
608 //
609 // Get drawing mode.
610 //--------------------------------------------------------------------------
611 void get_mode( PLStream *pls, PLINT *mode )
612 {
613  PLCairo *aStream;
614  cairo_operator_t op;
615 
616  aStream = (PLCairo *) pls->dev;
617 
618  op = cairo_get_operator( aStream->cairoContext );
619 
620  switch ( op )
621  {
622  case CAIRO_OPERATOR_OVER:
623  *mode = PL_DRAWMODE_DEFAULT;
624  break;
625  case CAIRO_OPERATOR_SOURCE:
626  *mode = PL_DRAWMODE_REPLACE;
627  break;
628  case CAIRO_OPERATOR_XOR:
629  *mode = PL_DRAWMODE_XOR;
630  break;
631  default:
632  *mode = PL_DRAWMODE_UNKNOWN;
633  }
634  return;
635 }
636 
637 //--------------------------------------------------------------------------
638 // text_begin_cairo()
639 //
640 // Begin text.
641 //--------------------------------------------------------------------------
642 
643 void text_begin_cairo( PLStream *pls, EscText *args )
644 {
645  int i;
646  PLCairo *aStream;
647 
648  aStream = (PLCairo *) pls->dev;
649  aStream->upDown = 0;
650  aStream->level = 0;
651  aStream->pangoMarkupString = (char *) malloc( sizeof ( char ) * MAX_MARKUP_LEN );
652  // Calculate the font size (in points since DPI = 72).
653  aStream->fontSize = (float) ( pls->chrht * DPI / 25.4 );
654  for ( i = 0; i < MAX_MARKUP_LEN; i++ )
655  {
656  aStream->pangoMarkupString[i] = 0;
657  }
658  open_span_tag( aStream->pangoMarkupString, args->n_fci, aStream->fontSize, 0 );
659 }
660 
661 //--------------------------------------------------------------------------
662 // text_char_cairo()
663 //
664 // Add text.
665 //--------------------------------------------------------------------------
666 
667 void text_char_cairo( PLStream *pls, EscText *args )
668 {
669  char utf8[5];
670  PLCairo *aStream;
671 
672  aStream = (PLCairo *) pls->dev;
673  // make sure we are not too close to the end of the string
674  if ( strlen( aStream->pangoMarkupString ) < ( MAX_MARKUP_LEN - 50 ) )
675  {
676  switch ( args->n_char )
677  {
678  case 38:
679  strncat( aStream->pangoMarkupString, "&#38;", MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
680  break;
681  case 60:
682  strncat( aStream->pangoMarkupString, "&#60;", MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
683  break;
684  case 62:
685  strncat( aStream->pangoMarkupString, "&#62;", MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
686  break;
687  default:
688  ucs4_to_utf8( args->n_char, utf8 );
689  strncat( aStream->pangoMarkupString, utf8, MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
690  break;
691  }
692  }
693 }
694 
695 //--------------------------------------------------------------------------
696 // text_esc_cairo()
697 //
698 // A font change, superscript, subscript, etc...
699 //--------------------------------------------------------------------------
700 
701 void text_esc_cairo( PLStream *pls, EscText *args )
702 {
703  PLCairo *aStream;
704 
705  aStream = (PLCairo *) pls->dev;
706  switch ( args->n_ctrl_char )
707  {
708  case PLTEXT_FONTCHANGE:
709  close_span_tag( aStream->pangoMarkupString, aStream->upDown );
710  open_span_tag( aStream->pangoMarkupString, args->n_fci, aStream->fontSize, aStream->upDown );
711  break;
712  case PLTEXT_SUPERSCRIPT:
713  if ( aStream->upDown < 0 )
714  {
715  strncat( aStream->pangoMarkupString, "</span>", MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
716  aStream->level++;
717  }
718  else
719  {
720  plP_script_scale( TRUE, &aStream->level,
721  &aStream->old_sscale, &aStream->sscale, &aStream->old_soffset, &aStream->soffset );
722  strncat( aStream->pangoMarkupString,
723  rise_span_tag( TRUE, aStream->fontSize, (float) aStream->sscale, (float) aStream->soffset ),
724  MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
725  }
726  aStream->upDown++;
727  break;
728  case PLTEXT_SUBSCRIPT:
729  if ( aStream->upDown > 0 )
730  {
731  strncat( aStream->pangoMarkupString, "</span>", MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
732  aStream->level--;
733  }
734  else
735  {
736  plP_script_scale( FALSE, &aStream->level,
737  &aStream->old_sscale, &aStream->sscale, &aStream->old_soffset, &aStream->soffset );
738  strncat( aStream->pangoMarkupString,
739  rise_span_tag( FALSE, aStream->fontSize, (float) aStream->sscale, (float) aStream->soffset ),
740  MAX_MARKUP_LEN - 1 - strlen( aStream->pangoMarkupString ) );
741  }
742  aStream->upDown--;
743  break;
744  }
745 }
746 
747 //--------------------------------------------------------------------------
748 // text_end_cairo()
749 //
750 // Draw the text and clean up.
751 //--------------------------------------------------------------------------
752 
753 void text_end_cairo( PLStream *pls, EscText *args )
754 {
755  int textXExtent, textYExtent, baseline;
756  PLFLT rotation, shear, stride, cos_rot, sin_rot, cos_shear, sin_shear;
757  cairo_matrix_t *cairoTransformMatrix;
758  cairo_font_options_t *cairoFontOptions;
759  PangoContext *context;
760  PangoLayout *layout;
761  PLCairo *aStream;
762 
763  aStream = (PLCairo *) pls->dev;
764 
765  set_current_context( pls );
766 
767  // Close the last span tag.
768  close_span_tag( aStream->pangoMarkupString, aStream->upDown );
769 
770  // printf("%s\n", aStream->pangoMarkupString);
771 
772  // Create the Pango text layout so we can figure out how big it is
773  layout = pango_cairo_create_layout( aStream->cairoContext );
774  pango_layout_set_markup( layout, aStream->pangoMarkupString, -1 );
775  pango_layout_get_pixel_size( layout, &textXExtent, &textYExtent );
776  baseline = pango_layout_get_baseline( layout );
777 
778  // If asked, set the string length (in mm) and return
779  if ( pls->get_string_length )
780  {
781  pls->string_length = (PLFLT) textXExtent * 25.4 / DPI;
782  }
783  else
784  {
785  // Set font aliasing
786  context = pango_layout_get_context( layout );
787  cairoFontOptions = cairo_font_options_create();
788  cairo_font_options_set_antialias( cairoFontOptions, aStream->text_anti_aliasing );
789  pango_cairo_context_set_font_options( context, cairoFontOptions );
790  pango_layout_context_changed( layout );
791  cairo_font_options_destroy( cairoFontOptions );
792 
793  // Save current transform matrix & clipping region
794  cairo_save( aStream->cairoContext );
795 
796  // Set up the clipping region if we are doing text clipping
797  if ( aStream->text_clipping )
798  {
799  set_clip( pls );
800  }
801 
802  // Move to the string reference point
803  cairo_move_to( aStream->cairoContext, aStream->downscale * (double) args->x, aStream->downscale * (double) args->y );
804 
805  // Invert the coordinate system so that the text is drawn right side up
806  cairoTransformMatrix = (cairo_matrix_t *) malloc( sizeof ( cairo_matrix_t ) );
807  cairo_matrix_init( cairoTransformMatrix, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 );
808  cairo_transform( aStream->cairoContext, cairoTransformMatrix );
809 
810  // Extract rotation angle and shear from the PLplot tranformation matrix.
811  // Compute sines and cosines of the angles as an optimization.
812  plRotationShear( args->xform, &rotation, &shear, &stride );
813  rotation -= pls->diorot * PI / 2.0;
814  cos_rot = cos( rotation );
815  sin_rot = sin( rotation );
816  cos_shear = cos( shear );
817  sin_shear = sin( shear );
818 
819  // Apply the transform matrix
820  cairo_matrix_init( cairoTransformMatrix,
821  cos_rot * stride,
822  -sin_rot * stride,
823  cos_rot * sin_shear + sin_rot * cos_shear,
824  -sin_rot * sin_shear + cos_rot * cos_shear,
825  0, 0 );
826  cairo_transform( aStream->cairoContext, cairoTransformMatrix );
827  free( cairoTransformMatrix );
828 
829  // Move to the text starting point
830  // printf("baseline %d %d\n", baseline, textYExtent);
831  cairo_rel_move_to( aStream->cairoContext,
832  (double) ( -1.0 * args->just * (double) textXExtent ),
833  (double) 0.5 * aStream->fontSize - baseline / 1024.0 );
834 
835  // Render the text
836  pango_cairo_show_layout( aStream->cairoContext, layout );
837 
838  // Restore the transform matrix to its state prior to the text transform.
839  cairo_restore( aStream->cairoContext );
840  }
841 
842  // Free the layout object and the markup string
843  g_object_unref( layout );
844  free( aStream->pangoMarkupString );
845 }
846 
847 //--------------------------------------------------------------------------
848 // proc_str()
849 //
850 // Processes strings for display.
851 //--------------------------------------------------------------------------
852 
853 void proc_str( PLStream *pls, EscText *args )
854 {
855  float fontSize;
856  int textXExtent, textYExtent, baseline;
857  char *textWithPangoMarkup;
858  PLFLT rotation, shear, stride, cos_rot, sin_rot, cos_shear, sin_shear;
859  cairo_matrix_t *cairoTransformMatrix;
860  cairo_font_options_t *cairoFontOptions;
861  PangoContext *context;
862  PangoLayout *layout;
863  PLCairo *aStream;
864 
865  aStream = (PLCairo *) pls->dev;
866 
867  set_current_context( pls );
868 
869  // Check that we got unicode, warning message and return if not
870  if ( args->unicode_array_len == 0 )
871  {
872  printf( "Non unicode string passed to a cairo driver, ignoring\n" );
873  return;
874  }
875 
876  // Check that unicode string isn't longer then the max we allow
877  if ( args->unicode_array_len >= MAX_STRING_LEN )
878  {
879  printf( "Sorry, the cairo drivers only handles strings of length < %d\n", MAX_STRING_LEN );
880  return;
881  }
882 
883  // Calculate the font size (in points since DPI = 72).
884  fontSize = (float) ( pls->chrht * DPI / 25.4 );
885 
886  // Convert the escape characters into the appropriate Pango markup
887  textWithPangoMarkup = ucs4_to_pango_markup_format( args->unicode_array, args->unicode_array_len, fontSize );
888 
889  // Create the Pango text layout so we can figure out how big it is
890  layout = pango_cairo_create_layout( aStream->cairoContext );
891  pango_layout_set_markup( layout, textWithPangoMarkup, -1 );
892  pango_layout_get_pixel_size( layout, &textXExtent, &textYExtent );
893  baseline = pango_layout_get_baseline( layout );
894 
895  // If asked, set the string length (in mm) and return
896  if ( pls->get_string_length )
897  {
898  pls->string_length = (PLFLT) textXExtent * 25.4 / DPI;
899  return;
900  }
901 
902  // Set font aliasing
903  context = pango_layout_get_context( layout );
904  cairoFontOptions = cairo_font_options_create();
905  cairo_font_options_set_antialias( cairoFontOptions, aStream->text_anti_aliasing );
906  pango_cairo_context_set_font_options( context, cairoFontOptions );
907  pango_layout_context_changed( layout );
908  cairo_font_options_destroy( cairoFontOptions );
909 
910  // Save current transform matrix & clipping region
911  cairo_save( aStream->cairoContext );
912 
913  // Set up the clipping region if we are doing text clipping
914  if ( aStream->text_clipping )
915  {
916  set_clip( pls );
917  }
918 
919  // Move to the string reference point
920  cairo_move_to( aStream->cairoContext, aStream->downscale * (double) args->x, aStream->downscale * (double) args->y );
921 
922  // Invert the coordinate system so that the text is drawn right side up
923  cairoTransformMatrix = (cairo_matrix_t *) malloc( sizeof ( cairo_matrix_t ) );
924  cairo_matrix_init( cairoTransformMatrix, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 );
925  cairo_transform( aStream->cairoContext, cairoTransformMatrix );
926 
927  // Extract rotation angle and shear from the PLplot tranformation matrix.
928  // Compute sines and cosines of the angles as an optimization.
929  plRotationShear( args->xform, &rotation, &shear, &stride );
930  rotation -= pls->diorot * PI / 2.0;
931  cos_rot = cos( rotation );
932  sin_rot = sin( rotation );
933  cos_shear = cos( shear );
934  sin_shear = sin( shear );
935 
936  // Apply the transform matrix
937  cairo_matrix_init( cairoTransformMatrix,
938  cos_rot * stride,
939  -sin_rot * stride,
940  cos_rot * sin_shear + sin_rot * cos_shear,
941  -sin_rot * sin_shear + cos_rot * cos_shear,
942  0, 0 );
943  cairo_transform( aStream->cairoContext, cairoTransformMatrix );
944  free( cairoTransformMatrix );
945 
946  // printf("baseline (ps) %d %d %f\n", baseline, textYExtent, aStream->fontSize);
947  // Move to the text starting point
948  cairo_rel_move_to( aStream->cairoContext,
949  (double) ( -1.0 * args->just * (double) textXExtent ),
950  (double) 0.5 * fontSize - baseline / 1024.0 );
951 
952  // Render the text
953  pango_cairo_show_layout( aStream->cairoContext, layout );
954 
955  // Restore the transform matrix to its state prior to the text transform.
956  cairo_restore( aStream->cairoContext );
957 
958  // Free the layout object and the markup string.
959  g_object_unref( layout );
960  free( textWithPangoMarkup );
961 }
962 
963 //--------------------------------------------------------------------------
964 // ucs4_to_pango_markup_format()
965 //
966 // Converts the plplot string (in ucs4) to a utf8 string that includes
967 // pango markup.
968 //
969 // http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html
970 //--------------------------------------------------------------------------
971 
972 char *ucs4_to_pango_markup_format( PLUNICODE *ucs4, int ucs4Len, float fontSize )
973 {
974  char plplotEsc;
975  int i;
976  int upDown = 0;
977  PLUNICODE fci;
978  char utf8[5];
979  char *pangoMarkupString;
980  PLFLT old_sscale, sscale, old_soffset, soffset;
981  PLINT level = 0.;
982 
983  // Will this be big enough? We might have lots of markup.
984  pangoMarkupString = (char *) malloc( sizeof ( char ) * MAX_MARKUP_LEN );
985  for ( i = 0; i < MAX_MARKUP_LEN; i++ )
986  {
987  pangoMarkupString[i] = 0;
988  }
989 
990  // Get PLplot escape character
991  plgesc( &plplotEsc );
992 
993  // Get the curent font and open the first span tag
994  plgfci( &fci );
995  open_span_tag( pangoMarkupString, fci, fontSize, 0 );
996 
997  // Parse the string to generate the tags
998  i = 0;
999  while ( i < ucs4Len )
1000  {
1001  // Try to avoid going off the end of the string
1002  if ( strlen( pangoMarkupString ) > ( MAX_MARKUP_LEN - 50 ) )
1003  {
1004  continue;
1005  }
1006  if ( ucs4[i] < PL_FCI_MARK ) // not a font change
1007  {
1008  if ( ucs4[i] != (PLUNICODE) plplotEsc ) // a character to display
1009  { // we have to handle "<", ">" and "&" separately since they throw off the XML
1010  switch ( ucs4[i] )
1011  {
1012  case 38:
1013  strncat( pangoMarkupString, "&#38;", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1014  break;
1015  case 60:
1016  strncat( pangoMarkupString, "&#60;", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1017  break;
1018  case 62:
1019  strncat( pangoMarkupString, "&#62;", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1020  break;
1021  default:
1022  ucs4_to_utf8( ucs4[i], utf8 );
1023  strncat( pangoMarkupString, utf8, MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1024  break;
1025  }
1026  i++;
1027  continue;
1028  }
1029  i++;
1030  if ( ucs4[i] == (PLUNICODE) plplotEsc ) // a escape character to display
1031  {
1032  ucs4_to_utf8( ucs4[i], utf8 );
1033  strncat( pangoMarkupString, utf8, MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1034  i++;
1035  continue;
1036  }
1037  else
1038  {
1039  if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript
1040  {
1041  if ( upDown < 0 )
1042  {
1043  strncat( pangoMarkupString, "</span>", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1044  level++;
1045  }
1046  else
1047  {
1048  plP_script_scale( TRUE, &level,
1049  &old_sscale, &sscale, &old_soffset, &soffset );
1050  strncat( pangoMarkupString,
1051  rise_span_tag( TRUE, fontSize, (float) sscale, (float) soffset ),
1052  MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1053  }
1054  upDown++;
1055  }
1056  if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript
1057  {
1058  if ( upDown > 0 )
1059  {
1060  strncat( pangoMarkupString, "</span>", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1061  level--;
1062  }
1063  else
1064  {
1065  plP_script_scale( FALSE, &level,
1066  &old_sscale, &sscale, &old_soffset, &soffset );
1067  strncat( pangoMarkupString,
1068  rise_span_tag( FALSE, fontSize, (float) sscale, (float) soffset ),
1069  MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1070  }
1071  upDown--;
1072  }
1073  i++;
1074  }
1075  }
1076  else // a font change
1077  {
1078  close_span_tag( pangoMarkupString, upDown );
1079  open_span_tag( pangoMarkupString, ucs4[i], fontSize, upDown );
1080  i++;
1081  }
1082  }
1083 
1084  // Close the last span tag.
1085  close_span_tag( pangoMarkupString, upDown );
1086 
1087  // printf("%s\n", pangoMarkupString);
1088 
1089  return pangoMarkupString;
1090 }
1091 
1092 //--------------------------------------------------------------------------
1093 // open_span_tag
1094 //
1095 // 1. Opens a span tag with the appropriate font description given the
1096 // current fci.
1097 // 2. Add the appropriate number of <sub> or <sup> tags to bring us
1098 // back to our current sub/super-script level.
1099 //--------------------------------------------------------------------------
1100 
1101 void open_span_tag( char *pangoMarkupString, PLUNICODE fci, float fontSize, int upDown )
1102 {
1103  unsigned char fontFamily, fontStyle, fontWeight;
1104  char openTag[TAG_LEN];
1105  int upDown_level;
1106  PLFLT old_sscale, sscale, old_soffset, soffset;
1107  PLINT level = 0.;
1108 
1109  // Generate the font info for the open tag & concatenate this
1110  // onto the markup string.
1111  plP_fci2hex( fci, &fontFamily, PL_FCI_FAMILY );
1112  plP_fci2hex( fci, &fontStyle, PL_FCI_STYLE );
1113  plP_fci2hex( fci, &fontWeight, PL_FCI_WEIGHT );
1114 
1115  // From http://library.gnome.org/devel/pango/unstable/PangoMarkupFormat.html
1116  // size = font size in 1024ths of a point.
1117  snprintf( openTag, TAG_LEN, "<span font_desc=\"%s\" size=\"%d\" ", familyLookup[fontFamily], (int) ( fontSize * 1024. ) );
1118  strncat( pangoMarkupString, openTag, MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1119 
1120  snprintf( openTag, TAG_LEN, "style=\"%s\" ", styleLookup[fontStyle] );
1121  strncat( pangoMarkupString, openTag, MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1122 
1123  snprintf( openTag, TAG_LEN, "weight=\"%s\">", weightLookup[fontWeight] );
1124  strncat( pangoMarkupString, openTag, MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1125 
1126  // Move to the right superscript/subscript level
1127  for ( upDown_level = 0; upDown_level < upDown; upDown_level++ )
1128  {
1129  plP_script_scale( TRUE, &level,
1130  &old_sscale, &sscale, &old_soffset, &soffset );
1131  strncat( pangoMarkupString,
1132  rise_span_tag( TRUE, fontSize, (float) sscale, (float) soffset ),
1133  MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1134  }
1135  for ( upDown_level = 0; upDown_level > upDown; upDown_level-- )
1136  {
1137  plP_script_scale( FALSE, &level,
1138  &old_sscale, &sscale, &old_soffset, &soffset );
1139  strncat( pangoMarkupString,
1140  rise_span_tag( FALSE, fontSize, (float) sscale, (float) soffset ),
1141  MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1142  }
1143 }
1144 
1145 //--------------------------------------------------------------------------
1146 // close_span_tag
1147 //
1148 // Close a span tag & brings us down to zero sub/super-script level.
1149 //--------------------------------------------------------------------------
1150 
1151 void close_span_tag( char *pangoMarkupString, int upDown )
1152 {
1153  if ( upDown > 0 )
1154  {
1155  while ( upDown > 0 )
1156  {
1157  strncat( pangoMarkupString, "</span>", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1158  upDown--;
1159  }
1160  }
1161  if ( upDown < 0 )
1162  {
1163  while ( upDown < 0 )
1164  {
1165  strncat( pangoMarkupString, "</span>", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1166  upDown++;
1167  }
1168  }
1169 
1170  strncat( pangoMarkupString, "</span>", MAX_MARKUP_LEN - 1 - strlen( pangoMarkupString ) );
1171 }
1172 
1173 // 0.8 mimics the offset of first superscript/subscript level implemented
1174 // in plstr (plsym.c) for Hershey fonts. Indeed when comparing with
1175 // -dev xwin results this factor appears to offset the centers of the
1176 // letters appropriately (but not their edges since different font sizes
1177 // are involved).
1178 # define RISE_FACTOR 0.8
1179 
1180 //--------------------------------------------------------------------------
1181 // rise_span_tag
1182 //
1183 // Create a rise span tag w/ appropriate font size & baseline offset
1184 // fontSize is the baseline font size in points (1/72 of an inch),
1185 // multiplier is a scaling factor for that font size for superscript
1186 // or subscript, and rise is the vertical offset (in units of font
1187 // size) for that superscript or subscript.
1188 
1189 //--------------------------------------------------------------------------
1190 
1191 char *rise_span_tag( int ifsuperscript, float fontSize, float multiplier, float rise )
1192 {
1193  float offset;
1194  static char tag[100];
1195 
1196  // http://developer.gnome.org/pango/unstable/PangoMarkupFormat.html says
1197  // rise should be in units of 10000 em's, but empirical evidence shows
1198  // it is in units of 1024th of a point. Therefore, since FontSize
1199  // is in points, a rise of 1024. * fontSize corresponds a rise of
1200  // a full character height.
1201  rise = 1024.f * fontSize * (float) RISE_FACTOR * rise;
1202 
1203  // This is the correction for the difference between baseline and
1204  // middle coordinate systems. This offset should be
1205  // 0.5*(fontSize - superscript/subscript fontSize).
1206  offset = 1024.f * 0.5f * fontSize * ( 1.0f - multiplier );
1207 
1208  if ( ifsuperscript )
1209  {
1210  sprintf( tag, "<span rise=\"%d\" size=\"%d\">",
1211  (int) ( rise + offset ), (int) ( fontSize * 1024. * multiplier ) );
1212  }
1213  else
1214  {
1215  sprintf( tag, "<span rise=\"%d\" size=\"%d\">",
1216  (int) -( rise - offset ), (int) ( fontSize * 1024. * multiplier ) );
1217  }
1218 
1219  return ( tag );
1220 }
1221 
1222 //--------------------------------------------------------------------------
1223 // write_to_stream()
1224 //
1225 // Writes data to a open file stream. This function is passed to the
1226 // Cairo file IO devices.
1227 //--------------------------------------------------------------------------
1228 
1229 cairo_status_t write_to_stream( void *filePointer, unsigned char *data, unsigned int length )
1230 {
1231  unsigned int bytes_written;
1232 
1233  bytes_written = (unsigned int) fwrite( data, 1, (size_t) length, (FILE *) filePointer );
1234  if ( bytes_written == length )
1235  {
1236  return CAIRO_STATUS_SUCCESS;
1237  }
1238  else
1239  {
1240  return CAIRO_STATUS_WRITE_ERROR;
1241  }
1242 }
1243 
1244 //--------------------------------------------------------------------------
1245 // stream_and_font_setup()
1246 //
1247 // Initializes the PLStream structure for the cairo devices.
1248 // Initializes the font lookup table.
1249 // Checks for cairo specific user options.
1250 // Returns a new PLCairo structure.
1251 //--------------------------------------------------------------------------
1252 
1253 PLCairo *stream_and_font_setup( PLStream *pls, int interactive )
1254 {
1255  int i;
1256  char *a;
1257  PLCairo *aStream;
1258  PLFLT downscale;
1259  downscale = 0.0;
1260 
1261  // Stream setup
1262  pls->termin = interactive; // Interactive device
1263  pls->dev_flush = 1; // Handles flushes
1264  pls->color = 1; // Supports color
1265  pls->dev_text = 1; // Handles text
1266  pls->dev_unicode = 1; // Wants unicode text
1267  pls->dev_clear = 0;
1268  pls->alt_unicode = 1; // Wants to handle unicode character by character
1269  pls->page = 0;
1270  pls->dev_fill0 = 1; // Supports hardware solid fills
1271  pls->dev_gradient = 1; // driver renders gradient
1272  pls->dev_arc = 1; // Supports driver-level arcs
1273  pls->plbuf_write = interactive; // Activate plot buffer
1274  pls->has_string_length = 1; // Driver supports string length calculations
1275  pls->dev_modeset = 1; // Driver supports drawing mode setting
1276 
1277  if ( pls->xlength <= 0 || pls->ylength <= 0 )
1278  {
1279  pls->xlength = PLCAIRO_DEFAULT_X;
1280  pls->ylength = PLCAIRO_DEFAULT_Y;
1281  }
1282  // Calculate ratio of (smaller) external coordinates used for cairo
1283  // devices to (larger) internal PLplot coordinates.
1284  if ( pls->xlength > pls->ylength )
1285  downscale = (double) pls->xlength / (double) ( PIXELS_X - 1 );
1286  else
1287  downscale = (double) pls->ylength / (double) PIXELS_Y;
1288  plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / downscale ), (PLINT) 0, (PLINT) ( pls->ylength / downscale ) );
1289  plP_setpxl( DPI / 25.4 / downscale, DPI / 25.4 / downscale );
1290 
1291  // Initialize font table with either enviroment variables or defaults.
1292  // This was copied from the psttf driver.
1293  for ( i = 0; i < NPANGOLOOKUP; i++ )
1294  {
1295  if ( ( a = getenv( envFamilyLookup[i] ) ) != NULL )
1296  {
1297  strncpy( familyLookup[i], a, FAMILY_LOOKUP_LEN - 1 );
1298  familyLookup[i][FAMILY_LOOKUP_LEN - 1] = '\0';
1299  }
1300  else
1301  {
1302  strncpy( familyLookup[i], defaultFamilyLookup[i], FAMILY_LOOKUP_LEN - 1 );
1303  familyLookup[i][FAMILY_LOOKUP_LEN - 1] = '\0';
1304  }
1305  }
1306 
1307  // Allocate a cairo stream structure
1308  aStream = malloc( sizeof ( PLCairo ) );
1309 #if defined ( PLD_xcairo )
1310  aStream->XDisplay = NULL;
1311  aStream->XWindow = 0;
1312 #endif
1313  aStream->cairoSurface = NULL;
1314  aStream->cairoContext = NULL;
1315  aStream->downscale = downscale;
1316 
1317  // Set text clipping on by default since it makes little difference in
1318  // speed for a modern cairo stack.
1319  aStream->text_clipping = 1;
1320  text_clipping = 1;
1321  text_anti_aliasing = 0; // use 'default' text aliasing by default
1322  graphics_anti_aliasing = 0; // use 'default' graphics aliasing by default
1323  rasterize_image = 1; // Enable rasterization by default
1324  set_background = 0; // Default for extcairo is that PLplot not change the background
1325  image_buffering = 1; // Default to image-based buffered rendering
1326 
1327  // Check for cairo specific options
1328  plParseDrvOpts( cairo_options );
1329 
1330  // Turn off text clipping if the user desires this
1331  if ( !text_clipping )
1332  {
1333  aStream->text_clipping = 0;
1334  }
1335 
1336  // Record users desired text and graphics aliasing and rasterization
1337  aStream->text_anti_aliasing = (short) text_anti_aliasing;
1339  aStream->rasterize_image = (short) rasterize_image;
1340  aStream->set_background = (short) set_background;
1341  aStream->image_buffering = (short) image_buffering;
1342 
1343  return aStream;
1344 }
1345 
1346 //--------------------------------------------------------------------------
1347 // set_current_context()
1348 //
1349 // Updates the cairo graphics context with the current values in
1350 // PLStream.
1351 //--------------------------------------------------------------------------
1352 
1354 {
1355  PLCairo *aStream;
1356 
1357  aStream = (PLCairo *) pls->dev;
1358  cairo_set_source_rgba( aStream->cairoContext,
1359  (double) pls->curcolor.r / 255.0,
1360  (double) pls->curcolor.g / 255.0,
1361  (double) pls->curcolor.b / 255.0,
1362  (double) pls->curcolor.a );
1363  // In Cairo, zero width lines are not hairlines, they are completely invisible.
1364  if ( pls->width <= 0. )
1365  {
1366  cairo_set_line_width( aStream->cairoContext, 1.0 );
1367  }
1368  else
1369  {
1370  cairo_set_line_width( aStream->cairoContext, (double) pls->width );
1371  }
1372 }
1373 
1374 //--------------------------------------------------------------------------
1375 // poly_line()
1376 //
1377 // Draws a multi-segmented line. It is then up to the calling function
1378 // to decide whether to just draw the line, or fill in the area
1379 // enclosed by the line.
1380 //--------------------------------------------------------------------------
1381 
1382 void poly_line( PLStream *pls, short *xa, short *ya, PLINT npts )
1383 {
1384  int i;
1385  PLCairo *aStream;
1386 
1387  aStream = (PLCairo *) pls->dev;
1388 
1389  set_current_context( pls );
1390 
1391  cairo_move_to( aStream->cairoContext, aStream->downscale * (double) xa[0], aStream->downscale * (double) ya[0] );
1392  for ( i = 1; i < npts; i++ )
1393  {
1394  cairo_line_to( aStream->cairoContext, aStream->downscale * (double) xa[i], aStream->downscale * (double) ya[i] );
1395  }
1396 }
1397 
1398 //--------------------------------------------------------------------------
1399 // filled_polygon()
1400 //
1401 // Draws a filled polygon.
1402 //--------------------------------------------------------------------------
1403 
1404 void filled_polygon( PLStream *pls, short *xa, short *ya, PLINT npts )
1405 {
1406  PLCairo *aStream;
1407 
1408  aStream = (PLCairo *) pls->dev;
1409 
1410  cairo_save( aStream->cairoContext );
1411 
1412  // Draw the polygons
1413  poly_line( pls, xa, ya, npts );
1414 
1415  cairo_set_source_rgba( aStream->cairoContext,
1416  (double) pls->curcolor.r / 255.0,
1417  (double) pls->curcolor.g / 255.0,
1418  (double) pls->curcolor.b / 255.0,
1419  (double) pls->curcolor.a );
1420 
1421  if ( cairo_get_antialias( aStream->cairoContext ) != CAIRO_ANTIALIAS_NONE )
1422  {
1423  cairo_fill_preserve( aStream->cairoContext );
1424 
1425  // These line properties make for a nicer looking polygon mesh
1426  set_line_properties( aStream, CAIRO_LINE_JOIN_BEVEL, CAIRO_LINE_CAP_BUTT );
1427  // Comment out the following call to cairo_set_line width
1428  // since the hard-coded width value of 1.0 is not appropriate
1429  // for fills of small areas. Instead, use the line width that
1430  // has already been set by the user via the above call of
1431  // poly_line which in turn calls set_current_context which in
1432  // turn calls cairo_set_line_width for the user-specified
1433  // width.
1434  // cairo_set_line_width( aStream->cairoContext, 1.0 );
1435  cairo_stroke( aStream->cairoContext );
1436  }
1437  else
1438  {
1439  cairo_fill( aStream->cairoContext );
1440  }
1441 
1442  cairo_restore( aStream->cairoContext );
1443 }
1444 
1445 //--------------------------------------------------------------------------
1446 // gradient()
1447 //
1448 // Render a gradient within a polygon.
1449 //--------------------------------------------------------------------------
1450 
1451 void gradient( PLStream *pls, short *xa, short *ya, PLINT npts )
1452 {
1453  int i;
1454  PLCairo *aStream;
1455  cairo_pattern_t *linear_gradient;
1456 
1457  aStream = (PLCairo *) pls->dev;
1458 
1459  // These line properties make for a nicer looking polygon mesh
1460  set_line_properties( aStream, CAIRO_LINE_JOIN_BEVEL, CAIRO_LINE_CAP_BUTT );
1461 
1462  linear_gradient = cairo_pattern_create_linear(
1463  aStream->downscale * pls->xgradient[0],
1464  aStream->downscale * pls->ygradient[0],
1465  aStream->downscale * pls->xgradient[1],
1466  aStream->downscale * pls->ygradient[1] );
1467 
1468  cairo_pattern_reference( linear_gradient );
1469  for ( i = 0; i < pls->ncol1; i++ )
1470  {
1471  cairo_pattern_add_color_stop_rgba( linear_gradient,
1472  (double) i / (double) ( pls->ncol1 - 1 ),
1473  (double) pls->cmap1[i].r / 255.,
1474  (double) pls->cmap1[i].g / 255.,
1475  (double) pls->cmap1[i].b / 255.,
1476  (double) pls->cmap1[i].a );
1477  }
1478 
1479  // Draw the polygon using the gradient.
1480  poly_line( pls, xa, ya, npts );
1481 
1482  cairo_set_source( aStream->cairoContext, linear_gradient );
1483  cairo_fill( aStream->cairoContext );
1484  cairo_pattern_destroy( linear_gradient );
1485 }
1486 
1487 //--------------------------------------------------------------------------
1488 // set_clip()
1489 //
1490 // Set the clipping region to the plot window.
1491 // NOTE: cairo_save() and cairo_restore() should probably be called before
1492 // and after this, respectively.
1493 //--------------------------------------------------------------------------
1494 
1495 void set_clip( PLStream *pls )
1496 {
1497  PLINT rcx[4], rcy[4];
1498  PLCairo *aStream;
1499  aStream = (PLCairo *) pls->dev;
1500 
1501  // Use PLplot core routine to get the corners of the clipping rectangle
1502  difilt_clip( rcx, rcy );
1503 
1504  // Layout the bounds of the clipping region
1505  // Should we convert PLINT to short and use the polyline routine?
1506  cairo_move_to( aStream->cairoContext,
1507  aStream->downscale * (double) rcx[0],
1508  aStream->downscale * (double) rcy[0] );
1509  cairo_line_to( aStream->cairoContext,
1510  aStream->downscale * (double) rcx[1],
1511  aStream->downscale * (double) rcy[1] );
1512  cairo_line_to( aStream->cairoContext,
1513  aStream->downscale * (double) rcx[2],
1514  aStream->downscale * (double) rcy[2] );
1515  cairo_line_to( aStream->cairoContext,
1516  aStream->downscale * (double) rcx[3],
1517  aStream->downscale * (double) rcy[3] );
1518  cairo_line_to( aStream->cairoContext,
1519  aStream->downscale * (double) rcx[0],
1520  aStream->downscale * (double) rcy[0] );
1521 
1522  // Set the clipping region
1523  cairo_clip( aStream->cairoContext );
1524 
1525  // Apparently, in some older Cairo versions, cairo_clip does not consume
1526  // the current path.
1527  cairo_new_path( aStream->cairoContext );
1528 }
1529 
1530 //--------------------------------------------------------------------------
1531 // cairo_family_check ()
1532 //
1533 // support function to help supress more than one page if family file
1534 // output not specified by the user (e.g., with the -fam command-line option).
1535 //--------------------------------------------------------------------------
1536 
1538 {
1539  if ( pls->family || pls->page == 1 )
1540  {
1541  return 0;
1542  }
1543  else
1544  {
1545  if ( !already_warned )
1546  {
1547  already_warned = 1;
1548  plwarn( "All pages after the first skipped because family file output not specified.\n" );
1549  }
1550  return 1;
1551  }
1552 }
1553 
1554 //--------------------------------------------------------------------------
1555 // arc()
1556 //
1557 // Draws an arc, possibly filled.
1558 //--------------------------------------------------------------------------
1559 
1560 void arc( PLStream *pls, arc_struct *arc_info )
1561 {
1562  PLCairo *aStream;
1563  double x, y, a, b;
1564  double angle1, angle2, rotate;
1565 
1566  set_current_context( pls );
1567 
1568  aStream = (PLCairo *) pls->dev;
1569 
1570  // Scale to the proper Cairo coordinates
1571  x = aStream->downscale * arc_info->x;
1572  y = aStream->downscale * arc_info->y;
1573  a = aStream->downscale * arc_info->a;
1574  b = aStream->downscale * arc_info->b;
1575 
1576  // Degrees to radians
1577  angle1 = arc_info->angle1 * M_PI / 180.0;
1578  angle2 = arc_info->angle2 * M_PI / 180.0;
1579  rotate = arc_info->rotate * M_PI / 180.0;
1580 
1581  cairo_save( aStream->cairoContext );
1582 
1583  // Clip the output to the plotting window
1584  set_clip( pls );
1585 
1586  // Make sure the arc is properly shaped and oriented
1587  cairo_save( aStream->cairoContext );
1588  cairo_translate( aStream->cairoContext, x, y );
1589  cairo_rotate( aStream->cairoContext, rotate );
1590  cairo_scale( aStream->cairoContext, a, b );
1591  cairo_arc( aStream->cairoContext, 0.0, 0.0, 1.0, angle1, angle2 );
1592  if ( arc_info->fill )
1593  cairo_line_to( aStream->cairoContext, 0.0, 0.0 );
1594  cairo_restore( aStream->cairoContext );
1595 
1596  cairo_set_source_rgba( aStream->cairoContext,
1597  (double) pls->curcolor.r / 255.0,
1598  (double) pls->curcolor.g / 255.0,
1599  (double) pls->curcolor.b / 255.0,
1600  (double) pls->curcolor.a );
1601  if ( arc_info->fill )
1602  {
1603  cairo_fill( aStream->cairoContext );
1604  }
1605  else
1606  {
1607  cairo_stroke( aStream->cairoContext );
1608  }
1609  cairo_restore( aStream->cairoContext );
1610 }
1611 
1612 //--------------------------------------------------------------------------
1613 // rotate_cairo_surface()
1614 //
1615 // Rotates the cairo surface to the appropriate orientation.
1616 //--------------------------------------------------------------------------
1617 
1618 void rotate_cairo_surface( PLStream *pls, float x11, float x12, float x21, float x22, float x0, float y0, PLBOOL is_xcairo )
1619 {
1620  cairo_matrix_t *matrix;
1621  PLCairo *aStream;
1622 
1623  aStream = (PLCairo *) pls->dev;
1624 
1625  matrix = (cairo_matrix_t *) malloc( sizeof ( cairo_matrix_t ) );
1626  cairo_matrix_init( matrix, x11, x12, x21, x22, x0, y0 );
1627 #if defined ( PLD_xcairo )
1628  if ( is_xcairo )
1629  {
1630  cairo_transform( aStream->cairoContext_X, matrix );
1631  }
1632  else
1633  {
1634  cairo_transform( aStream->cairoContext, matrix );
1635  }
1636 #else
1637  cairo_transform( aStream->cairoContext, matrix );
1638 #endif
1639  free( matrix );
1640 }
1641 
1642 //--------------------------------------------------------------------------
1643 //--------------------------------------------------------------------------
1644 //
1645 // That which is common to all familying Cairo Drivers
1646 //
1647 //--------------------------------------------------------------------------
1648 //--------------------------------------------------------------------------
1649 #if defined ( PLD_pngcairo ) || defined ( PLD_svgcairo ) || defined ( PLD_epscairo )
1650 
1651 void plD_bop_cairo_fam( PLStream * );
1652 void plD_eop_cairo_fam( PLStream * );
1653 void plD_state_cairo_fam( PLStream *, PLINT );
1654 void plD_esc_cairo_fam( PLStream *, PLINT, void * );
1655 void plD_tidy_cairo_fam( PLStream * );
1656 void plD_line_cairo_fam( PLStream *, short, short, short, short );
1657 void plD_polyline_cairo_fam( PLStream *, short *, short *, PLINT );
1658 
1659 //--------------------------------------------------------------------------
1660 // plD_bop_cairo_fam()
1661 //
1662 // Familying Devices: Set up for the next page.
1663 //--------------------------------------------------------------------------
1664 
1665 void plD_bop_cairo_fam( PLStream *pls )
1666 {
1667  PLCairo *aStream;
1668 
1669  // Plot familying stuff. Not really understood, just copying gd.c
1670  plGetFam( pls );
1671 
1672  aStream = (PLCairo *) pls->dev;
1673 
1674  pls->famadv = 1;
1675  pls->page++;
1676 
1677  // Suppress multi-page output if family file output is not
1678  // specified by the user.
1679  if ( cairo_family_check( pls ) )
1680  {
1681  return;
1682  }
1683 
1684  // Fill in the window with the background color.
1685  cairo_rectangle( aStream->cairoContext, 0.0, 0.0, pls->xlength, pls->ylength );
1686  cairo_set_source_rgba( aStream->cairoContext,
1687  (double) pls->cmap0[0].r / 255.0,
1688  (double) pls->cmap0[0].g / 255.0,
1689  (double) pls->cmap0[0].b / 255.0,
1690  (double) pls->cmap0[0].a );
1691  cairo_fill( aStream->cairoContext );
1692 }
1693 
1694 //--------------------------------------------------------------------------
1695 // plD_eop_cairo()
1696 //
1697 // End of page.
1698 //--------------------------------------------------------------------------
1699 
1700 void plD_eop_cairo_fam( PLStream *pls )
1701 {
1702  if ( cairo_family_check( pls ) )
1703  {
1704  return;
1705  }
1706 
1707  plD_eop_cairo( pls );
1708 }
1709 
1710 //--------------------------------------------------------------------------
1711 // plD_state_cairo_fam()
1712 //
1713 // Handle change in PLStream state (color, pen width, fill attribute, etc).
1714 //--------------------------------------------------------------------------
1715 
1716 void plD_state_cairo_fam( PLStream *pls, PLINT op )
1717 {
1718  if ( cairo_family_check( pls ) )
1719  {
1720  return;
1721  }
1722 
1723  plD_state_cairo( pls, op );
1724 }
1725 
1726 //--------------------------------------------------------------------------
1727 // plD_esc_cairo_fam()
1728 //
1729 // Generic escape function.
1730 //--------------------------------------------------------------------------
1731 
1732 void plD_esc_cairo_fam( PLStream *pls, PLINT op, void *ptr )
1733 {
1734  if ( cairo_family_check( pls ) )
1735  {
1736  return;
1737  }
1738 
1739  plD_esc_cairo( pls, op, ptr );
1740 }
1741 
1742 //--------------------------------------------------------------------------
1743 // plD_tidy_cairo_fam()
1744 //
1745 // Close graphics file or otherwise clean up.
1746 //--------------------------------------------------------------------------
1747 
1748 void plD_tidy_cairo_fam( PLStream *pls )
1749 {
1750  plD_tidy_cairo( pls );
1751 }
1752 
1753 //--------------------------------------------------------------------------
1754 // plD_line_cairo_fam()
1755 //
1756 // Draw a line.
1757 //--------------------------------------------------------------------------
1758 
1759 void plD_line_cairo_fam( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
1760 {
1761  if ( cairo_family_check( pls ) )
1762  {
1763  return;
1764  }
1765 
1766  plD_line_cairo( pls, x1a, y1a, x2a, y2a );
1767 }
1768 
1769 //--------------------------------------------------------------------------
1770 // plD_polyline_cairo_fam()
1771 //
1772 // Draw a polyline in the current color.
1773 //--------------------------------------------------------------------------
1774 
1775 void plD_polyline_cairo_fam( PLStream *pls, short *xa, short *ya, PLINT npts )
1776 {
1777  if ( cairo_family_check( pls ) )
1778  {
1779  return;
1780  }
1781 
1782  plD_polyline_cairo( pls, xa, ya, npts );
1783 }
1784 
1785 #endif
1786 //--------------------------------------------------------------------------
1787 //--------------------------------------------------------------------------
1788 //
1789 // That which is specific to the xcairo driver.
1790 //
1791 //--------------------------------------------------------------------------
1792 //--------------------------------------------------------------------------
1793 
1794 #if defined ( PLD_xcairo )
1795 
1796 static int XScreen;
1797 static Window rootWindow;
1798 
1800 void plD_init_xcairo( PLStream * );
1801 void plD_bop_xcairo( PLStream * );
1802 void plD_eop_xcairo( PLStream * );
1803 void plD_tidy_xcairo( PLStream * );
1804 void plD_esc_xcairo( PLStream *, PLINT, void * );
1805 static void xcairo_get_cursor( PLStream *, PLGraphicsIn * );
1806 
1807 //--------------------------------------------------------------------------
1808 // plD_dispatch_init_xcairo()
1809 //
1810 // xcairo dispatch table initialization.
1811 //--------------------------------------------------------------------------
1812 
1814 {
1815 #ifndef ENABLE_DYNDRIVERS
1816  pdt->pl_MenuStr = "Cairo X Windows Driver";
1817  pdt->pl_DevName = "xcairo";
1818 #endif
1820  pdt->pl_seq = 100;
1821  pdt->pl_init = (plD_init_fp) plD_init_xcairo;
1824  pdt->pl_eop = (plD_eop_fp) plD_eop_xcairo;
1825  pdt->pl_bop = (plD_bop_fp) plD_bop_xcairo;
1826  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_xcairo;
1828  pdt->pl_esc = (plD_esc_fp) plD_esc_xcairo;
1829 }
1830 
1831 //--------------------------------------------------------------------------
1832 // xcairo_init_cairo()
1833 //
1834 // Configures Cairo to use whichever X Drawable is set up in the given
1835 // stream. This is called by plD_init_xcairo() in the event we are
1836 // drawing into a plplot-managed window, and plD_esc_xcairo() if
1837 // we are using an external X Drawable.
1838 //
1839 // A return value of 0 indicates success. Currently this function only
1840 // returns 0.
1841 //--------------------------------------------------------------------------
1842 
1843 static signed int xcairo_init_cairo( PLStream *pls )
1844 {
1845  PLCairo *aStream;
1846  Visual *defaultVisual;
1847 
1848  aStream = (PLCairo *) pls->dev;
1849 
1850  // Create an cairo surface & context that are associated with the X window.
1851  defaultVisual = DefaultVisual( aStream->XDisplay, 0 );
1852  // Dimension units are pixels from cairo documentation.
1853  // This is the X window Cairo surface.
1854  aStream->cairoSurface_X = cairo_xlib_surface_create( aStream->XDisplay, aStream->XWindow, defaultVisual, pls->xlength, pls->ylength );
1855  aStream->cairoContext_X = cairo_create( aStream->cairoSurface_X );
1856  // This is the Cairo surface PLplot will actually plot to.
1857  if ( aStream->image_buffering == 0 )
1858  {
1859  aStream->cairoSurface = cairo_surface_create_similar( aStream->cairoSurface_X, CAIRO_CONTENT_COLOR_ALPHA, pls->xlength, pls->ylength );
1860  aStream->cairoContext = cairo_create( aStream->cairoSurface );
1861  }
1862  else
1863  {
1864  // Plot to an off-screen image
1865  aStream->cairoSurface =
1866  cairo_image_surface_create( CAIRO_FORMAT_ARGB32,
1867  pls->xlength, pls->ylength );
1868  aStream->cairoContext = cairo_create( aStream->cairoSurface );
1869  }
1870 
1871  // Invert the surface so that the graphs are drawn right side up.
1872  rotate_cairo_surface( pls, 1.0, 0.0, 0.0, -1.0, 0.0, (float) pls->ylength, TRUE );
1873 
1874  // Set graphics aliasing
1875  cairo_set_antialias( aStream->cairoContext, aStream->graphics_anti_aliasing );
1876 
1877  // Set fill rule for the case of self-intersecting boundaries.
1878  if ( pls->dev_eofill )
1879  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
1880  else
1881  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
1882 
1883  // Fill in the X window with the background color to avoid starting out
1884  // with a blank window of an unexpected color.
1885  cairo_rectangle( aStream->cairoContext_X, 0.0, 0.0, pls->xlength, pls->ylength );
1886  cairo_set_source_rgba( aStream->cairoContext_X,
1887  (double) pls->cmap0[0].r / 255.0,
1888  (double) pls->cmap0[0].g / 255.0,
1889  (double) pls->cmap0[0].b / 255.0,
1890  (double) pls->cmap0[0].a );
1891  cairo_fill( aStream->cairoContext_X );
1892 
1893  XFlush( aStream->XDisplay );
1894 
1895  return 0;
1896 }
1897 
1898 //--------------------------------------------------------------------------
1899 // plD_init_xcairo()
1900 //
1901 // Initialize Cairo X Windows device.
1902 //--------------------------------------------------------------------------
1903 
1904 void plD_init_xcairo( PLStream *pls )
1905 {
1906  PLCairo *aStream;
1907  Atom wmDelete;
1908 
1909  // Setup the PLStream and the font lookup table.
1910  aStream = stream_and_font_setup( pls, 1 );
1911 
1912  // Save the pointer to the structure in the PLplot stream
1913  pls->dev = aStream;
1914 
1915  // Create a X Window if required.
1916  if ( external_drawable != 0 )
1917  {
1918  aStream->xdrawable_mode = 1;
1919  }
1920  else
1921  {
1922  // X Windows setup
1923  aStream->XDisplay = NULL;
1924  aStream->XDisplay = XOpenDisplay( NULL );
1925  if ( aStream->XDisplay == NULL )
1926  {
1927  printf( "Failed to open X Windows display\n" );
1928  // some sort of error here
1929  }
1930  XScreen = DefaultScreen( aStream->XDisplay );
1931  rootWindow = RootWindow( aStream->XDisplay, XScreen );
1932 
1933  aStream->XWindow = XCreateSimpleWindow( aStream->XDisplay, rootWindow, 0, 0, (unsigned int) pls->xlength, (unsigned int) pls->ylength,
1934  1, BlackPixel( aStream->XDisplay, XScreen ), BlackPixel( aStream->XDisplay, XScreen ) );
1935  XStoreName( aStream->XDisplay, aStream->XWindow, pls->plwindow );
1936  XSelectInput( aStream->XDisplay, aStream->XWindow, NoEventMask );
1937  XMapWindow( aStream->XDisplay, aStream->XWindow );
1938  aStream->xdrawable_mode = 0;
1939 
1940  wmDelete = XInternAtom( aStream->XDisplay, "WM_DELETE_WINDOW", True );
1941  XSetWMProtocols( aStream->XDisplay, aStream->XWindow, &wmDelete, 1 );
1942 
1943  xcairo_init_cairo( pls );
1944  }
1945 
1946  aStream->exit_event_loop = 0;
1947 }
1948 
1949 //--------------------------------------------------------------------------
1950 // blit_to_x()
1951 //
1952 //
1953 // Blit the offscreen image to the X window.
1954 //--------------------------------------------------------------------------
1955 
1956 void blit_to_x( PLStream *pls, double x, double y, double w, double h )
1957 {
1958  PLCairo *aStream;
1959 
1960  aStream = pls->dev;
1961 
1962  cairo_save( aStream->cairoContext );
1963  // "Flatten" any transparent regions to look like they were drawn over the
1964  // correct background color
1965  cairo_rectangle( aStream->cairoContext, x, y, w, h );
1966  cairo_set_operator( aStream->cairoContext, CAIRO_OPERATOR_DEST_OVER );
1967  cairo_set_source_rgba( aStream->cairoContext,
1968  (double) pls->cmap0[0].r / 255.0,
1969  (double) pls->cmap0[0].g / 255.0,
1970  (double) pls->cmap0[0].b / 255.0,
1971  (double) pls->cmap0[0].a );
1972  cairo_fill( aStream->cairoContext );
1973  cairo_restore( aStream->cairoContext );
1974 
1975  cairo_save( aStream->cairoContext_X );
1976  // Copy a portion of the surface
1977  cairo_rectangle( aStream->cairoContext_X, x, y, w, h );
1978  cairo_set_operator( aStream->cairoContext_X, CAIRO_OPERATOR_SOURCE );
1979  cairo_set_source_surface( aStream->cairoContext_X,
1980  aStream->cairoSurface, 0.0, 0.0 );
1981  cairo_fill( aStream->cairoContext_X );
1982  cairo_restore( aStream->cairoContext_X );
1983 }
1984 
1985 //--------------------------------------------------------------------------
1986 // plD_bop_xcairo()
1987 //
1988 // X Windows specific start of page.
1989 //--------------------------------------------------------------------------
1990 
1991 void plD_bop_xcairo( PLStream *pls )
1992 {
1993  PLCairo *aStream;
1994 
1995  aStream = (PLCairo *) pls->dev;
1996 
1997  plD_bop_cairo( pls );
1998 
1999  if ( aStream->xdrawable_mode )
2000  return;
2001 
2002  XFlush( aStream->XDisplay );
2003 }
2004 
2005 //--------------------------------------------------------------------------
2006 // plD_eop_xcairo()
2007 //
2008 // X Windows specific end of page.
2009 //--------------------------------------------------------------------------
2010 
2011 void plD_eop_xcairo( PLStream *pls )
2012 {
2013  int number_chars;
2014  long event_mask;
2015  char event_string[10];
2016  KeySym keysym;
2017  XComposeStatus cs;
2018  XEvent event;
2019  XExposeEvent *expose;
2020  PLCairo *aStream;
2021 
2022  aStream = (PLCairo *) pls->dev;
2023 
2024  // Blit the offscreen image to the X window.
2025  blit_to_x( pls, 0.0, 0.0, pls->xlength, pls->ylength );
2026 
2027  if ( aStream->xdrawable_mode )
2028  return;
2029 
2030  // Only pause if nopause is unset.
2031  if ( pls->nopause )
2032  aStream->exit_event_loop = 1;
2033 
2034  // Loop, handling selected events, till the user elects to close the plot.
2035  event_mask = ButtonPressMask | KeyPressMask | ExposureMask;
2036  XSelectInput( aStream->XDisplay, aStream->XWindow, event_mask );
2037  while ( !aStream->exit_event_loop )
2038  {
2039  //XWindowEvent( aStream->XDisplay, aStream->XWindow, event_mask, &event );
2040  XNextEvent( aStream->XDisplay, &event );
2041  switch ( event.type )
2042  {
2043  case KeyPress:
2044  number_chars = XLookupString( (XKeyEvent *) &event, event_string, 10, &keysym, &cs );
2045  event_string[number_chars] = '\0';
2046  if ( keysym == XK_Return )
2047  {
2048  aStream->exit_event_loop = 1;
2049  }
2050  break;
2051  case ButtonPress:
2052  if ( ( (XButtonEvent *) &event )->button == Button3 )
2053  aStream->exit_event_loop = 1;
2054  break;
2055  case ClientMessage:
2056  // plexit("X Window closed");
2057  pls->stream_closed = TRUE;
2058  aStream->exit_event_loop = 1;
2059  break;
2060  case Expose:
2061  // Blit the image again after an expose event, but only for the last
2062  // available event. Otherwise multiple redraws occur needlessly.
2063  expose = (XExposeEvent *) &event;
2064  if ( expose->count == 0 )
2065  {
2066  blit_to_x( pls, expose->x, expose->y,
2067  expose->width, expose->height );
2068  }
2069  break;
2070  }
2071  }
2072  aStream->exit_event_loop = 0;
2073 }
2074 
2075 //--------------------------------------------------------------------------
2076 // plD_tidy_xcairo()
2077 //
2078 // X Windows: close graphics file or otherwise clean up.
2079 //--------------------------------------------------------------------------
2080 
2081 void plD_tidy_xcairo( PLStream *pls )
2082 {
2083  PLCairo *aStream;
2084 
2085  aStream = (PLCairo *) pls->dev;
2086 
2087  plD_tidy_cairo( pls );
2088 
2089  // Also free up the Cairo X surface and context
2090  cairo_destroy( aStream->cairoContext_X );
2091  cairo_surface_destroy( aStream->cairoSurface_X );
2092 
2093  if ( aStream->xdrawable_mode )
2094  return;
2095 
2096  // Close the window and the display.
2097  XFlush( aStream->XDisplay );
2098 
2099  XDestroyWindow( aStream->XDisplay, aStream->XWindow );
2100 
2101  XCloseDisplay( aStream->XDisplay );
2102 }
2103 
2104 //--------------------------------------------------------------------------
2105 // plD_esc_xcairo()
2106 //
2107 // Escape function, specialized for the xcairo driver
2108 //--------------------------------------------------------------------------
2109 
2110 void plD_esc_xcairo( PLStream *pls, PLINT op, void *ptr )
2111 {
2112  PLCairo *aStream;
2113 
2114  aStream = (PLCairo *) pls->dev;
2115 
2116  switch ( op )
2117  {
2118  case PLESC_FLUSH: // forced update of the window
2119  blit_to_x( pls, 0.0, 0.0, pls->xlength, pls->ylength );
2120  XFlush( aStream->XDisplay );
2121  break;
2122  case PLESC_GETC: // get cursor position
2123  blit_to_x( pls, 0.0, 0.0, pls->xlength, pls->ylength );
2124  XFlush( aStream->XDisplay );
2125  xcairo_get_cursor( pls, (PLGraphicsIn *) ptr );
2126  break;
2127  case PLESC_DEVINIT: { // Set external drawable
2128  Window rootwin;
2129  PLXcairoDrawableInfo *xinfo = (PLXcairoDrawableInfo *) ptr;
2130  signed int x, y;
2131  unsigned int w, h, b, d;
2132  if ( xinfo == NULL )
2133  {
2134  printf( "xcairo: PLESC_DEVINIT ignored, no drawable info provided\n" );
2135  return;
2136  }
2137  if ( aStream->xdrawable_mode == 0 )
2138  {
2139  printf( "xcairo: PLESC_DEVINIT called with drawable but stream not in xdrawable mode\n" );
2140  return;
2141  }
2142  aStream->XDisplay = xinfo->display;
2143  aStream->XWindow = xinfo->drawable;
2144 
2145  // Ensure plplot knows the real dimensions of the drawable
2146  XGetGeometry( aStream->XDisplay, aStream->XWindow, &rootwin,
2147  &x, &y, &w, &h, &b, &d );
2148  pls->xlength = (PLINT) w;
2149  pls->ylength = (PLINT) h;
2150  plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / aStream->downscale ), (PLINT) 0,
2151  (PLINT) ( pls->ylength / aStream->downscale ) );
2152 
2153  // Associate cairo with the supplied drawable
2154  xcairo_init_cairo( pls );
2155 
2156  // Recalculate dimensions and the like now that the drawable is known
2157  plbop();
2158 
2159  break;
2160  }
2161  default:
2162  plD_esc_cairo( pls, op, ptr );
2163  break;
2164  }
2165 }
2166 
2167 //--------------------------------------------------------------------------
2168 // xcairo_get_cursor()
2169 //
2170 // X Windows: returns the location of the next mouse click or key press.
2171 //--------------------------------------------------------------------------
2172 
2173 void xcairo_get_cursor( PLStream *pls, PLGraphicsIn *gin )
2174 {
2175  const char *ksname;
2176  char str[257];
2177  KeySym keysym;
2178  XEvent event;
2179  XButtonEvent *xButtonEvent;
2180  Cursor xHairCursor;
2181  PLCairo *aStream;
2182 
2183  aStream = (PLCairo *) pls->dev;
2184 
2185  // Initialize PLplot mouse event structure
2186  plGinInit( gin );
2187 
2188  // Create cross hair cursor & switch to using it
2189  xHairCursor = XCreateFontCursor( aStream->XDisplay, XC_crosshair );
2190  XDefineCursor( aStream->XDisplay, aStream->XWindow, xHairCursor );
2191 
2192  // Get the next mouse button release or key press event
2193  XSelectInput( aStream->XDisplay, aStream->XWindow, ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | ButtonMotionMask );
2194  XMaskEvent( aStream->XDisplay, ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | ButtonMotionMask, &event );
2195  XSelectInput( aStream->XDisplay, aStream->XWindow, NoEventMask );
2196 
2197  // Update PLplot's mouse event structure
2198  xButtonEvent = (XButtonEvent *) &event;
2199  gin->state = xButtonEvent->state;
2200  gin->button = xButtonEvent->button;
2201  gin->pX = event.xbutton.x;
2202  gin->pY = pls->ylength - event.xbutton.y;
2203  gin->dX = (PLFLT) event.xbutton.x / ( (PLFLT) ( pls->xlength ) );
2204  gin->dY = (PLFLT) ( pls->ylength - event.xbutton.y ) / ( (PLFLT) ( pls->ylength ) );
2205 
2206  // Get key pressed (if any)
2207  if ( event.type == KeyPress || event.type == KeyRelease )
2208  {
2209  XLookupString( (XKeyEvent *) &event, str, 100, &keysym, NULL );
2210  if ( keysym == NoSymbol )
2211  ksname = "NoSymbol";
2212  else if ( !( ksname = XKeysymToString( keysym ) ) )
2213  ksname = "(no name)";
2214  strcpy( gin->string, ksname );
2215  // gin->string[number_chars] = '\0';
2216  switch ( keysym )
2217  {
2218  case XK_BackSpace:
2219  case XK_Tab:
2220  case XK_Linefeed:
2221  case XK_Return:
2222  case XK_Escape:
2223  case XK_Delete:
2224  gin->keysym = 0xFF & keysym;
2225  break;
2226  default:
2227  gin->keysym = (unsigned int) keysym;
2228  }
2229  }
2230  else // button press
2231  {
2232  sprintf( gin->string, "button %u", gin->button );
2233  gin->keysym = 0x20;
2234  }
2235 
2236  // Switch back to normal cursor
2237  XUndefineCursor( aStream->XDisplay, aStream->XWindow );
2238  XFlush( aStream->XDisplay );
2239 }
2240 
2241 #endif
2242 
2243 
2244 //--------------------------------------------------------------------------
2245 //--------------------------------------------------------------------------
2246 //
2247 // That which is specific to the cairo PDF driver.
2248 //
2249 //--------------------------------------------------------------------------
2250 //--------------------------------------------------------------------------
2251 
2252 #if defined ( PLD_pdfcairo )
2253 
2255 void plD_init_pdfcairo( PLStream * );
2256 
2257 //--------------------------------------------------------------------------
2258 // dispatch_init_init()
2259 //
2260 // Initialize device dispatch table
2261 //--------------------------------------------------------------------------
2262 
2263 // pdfcairo
2265 {
2266 #ifndef ENABLE_DYNDRIVERS
2267  pdt->pl_MenuStr = "Cairo PDF Driver";
2268  pdt->pl_DevName = "pdfcairo";
2269 #endif
2271  pdt->pl_seq = 101;
2272  pdt->pl_init = (plD_init_fp) plD_init_pdfcairo;
2275  pdt->pl_eop = (plD_eop_fp) plD_eop_cairo;
2276  pdt->pl_bop = (plD_bop_fp) plD_bop_cairo;
2279  pdt->pl_esc = (plD_esc_fp) plD_esc_cairo;
2280 }
2281 
2282 //--------------------------------------------------------------------------
2283 // plD_init_pdfcairo()
2284 //
2285 // Initialize Cairo PDF device
2286 //--------------------------------------------------------------------------
2287 
2288 void plD_init_pdfcairo( PLStream *pls )
2289 {
2290  PLCairo *aStream;
2291 
2292  // Setup the PLStream and the font lookup table
2293  aStream = stream_and_font_setup( pls, 0 );
2294 
2295  // Prompt for a file name if not already set.
2296  plOpenFile( pls );
2297 
2298  // Create an cairo surface & context for PDF file.
2299  // Dimension units are pts = 1/72 inches from cairo documentation.
2300  aStream->cairoSurface = cairo_pdf_surface_create_for_stream( (cairo_write_func_t) write_to_stream, pls->OutFile, (double) pls->xlength, (double) pls->ylength );
2301  aStream->cairoContext = cairo_create( aStream->cairoSurface );
2302 
2303  // Save the pointer to the structure in the PLplot stream
2304  pls->dev = aStream;
2305 
2306  // Invert the surface so that the graphs are drawn right side up.
2307  rotate_cairo_surface( pls, 1.0, 0.0, 0.0, -1.0, 0.0, (float) pls->ylength, FALSE );
2308 
2309  // Set graphics aliasing
2310  cairo_set_antialias( aStream->cairoContext, aStream->graphics_anti_aliasing );
2311 
2312  // Set fill rule for the case of self-intersecting boundaries.
2313  if ( pls->dev_eofill )
2314  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
2315  else
2316  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
2317 }
2318 
2319 #endif
2320 
2321 
2322 //--------------------------------------------------------------------------
2323 //--------------------------------------------------------------------------
2324 //
2325 // That which is specific to the cairo PS driver.
2326 //
2327 //--------------------------------------------------------------------------
2328 //--------------------------------------------------------------------------
2329 
2330 #if defined ( PLD_pscairo )
2331 
2333 void plD_init_pscairo( PLStream * );
2334 
2335 //--------------------------------------------------------------------------
2336 // dispatch_init_init()
2337 //
2338 // Initialize device dispatch table
2339 //--------------------------------------------------------------------------
2340 
2341 // pscairo
2343 {
2344 #ifndef ENABLE_DYNDRIVERS
2345  pdt->pl_MenuStr = "Cairo PS Driver";
2346  pdt->pl_DevName = "pscairo";
2347 #endif
2349  pdt->pl_seq = 102;
2350  pdt->pl_init = (plD_init_fp) plD_init_pscairo;
2353  pdt->pl_eop = (plD_eop_fp) plD_eop_cairo;
2354  pdt->pl_bop = (plD_bop_fp) plD_bop_cairo;
2357  pdt->pl_esc = (plD_esc_fp) plD_esc_cairo;
2358 }
2359 
2360 //--------------------------------------------------------------------------
2361 // plD_init_pscairo()
2362 //
2363 // Initialize Cairo PS device
2364 //--------------------------------------------------------------------------
2365 
2366 void plD_init_pscairo( PLStream *pls )
2367 {
2368  PLCairo *aStream;
2369 
2370  // Setup the PLStream and the font lookup table
2371  aStream = stream_and_font_setup( pls, 0 );
2372 
2373  // Prompt for a file name if not already set.
2374  plOpenFile( pls );
2375 
2376  // Create an cairo surface & context for PS file.
2377  // Dimension units are pts = 1/72 inches from cairo documentation.
2378  aStream->cairoSurface = cairo_ps_surface_create_for_stream( (cairo_write_func_t) write_to_stream, pls->OutFile, (double) pls->ylength, (double) pls->xlength );
2379  aStream->cairoContext = cairo_create( aStream->cairoSurface );
2380 
2381  // Save the pointer to the structure in the PLplot stream
2382  pls->dev = aStream;
2383 
2384  // Handle portrait or landscape
2385  if ( pls->portrait )
2386  {
2387  plsdiori( 1 );
2388  pls->freeaspect = 1;
2389  }
2390  rotate_cairo_surface( pls, 0.0, -1.0, -1.0, 0.0, (float) pls->ylength, (float) pls->xlength, FALSE );
2391 
2392  // Set fill rule for the case of self-intersecting boundaries.
2393  if ( pls->dev_eofill )
2394  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
2395  else
2396  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
2397 }
2398 
2399 #endif
2400 
2401 
2402 //--------------------------------------------------------------------------
2403 //--------------------------------------------------------------------------
2404 //
2405 // That which is specific to the cairo EPS driver.
2406 //
2407 //--------------------------------------------------------------------------
2408 //--------------------------------------------------------------------------
2409 
2410 #if defined ( PLD_epscairo )
2411 
2413 void plD_init_epscairo( PLStream * );
2414 
2415 //--------------------------------------------------------------------------
2416 // dispatch_init_init()
2417 //
2418 // Initialize device dispatch table
2419 //--------------------------------------------------------------------------
2420 
2421 // epscairo
2423 {
2424 #ifndef ENABLE_DYNDRIVERS
2425  pdt->pl_MenuStr = "Cairo EPS Driver";
2426  pdt->pl_DevName = "epscairo";
2427 #endif
2429  pdt->pl_seq = 102;
2430  pdt->pl_init = (plD_init_fp) plD_init_epscairo;
2431  pdt->pl_line = (plD_line_fp) plD_line_cairo_fam;
2432  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_cairo_fam;
2433  pdt->pl_eop = (plD_eop_fp) plD_eop_cairo_fam;
2434  pdt->pl_bop = (plD_bop_fp) plD_bop_cairo_fam;
2435  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_cairo_fam;
2436  pdt->pl_state = (plD_state_fp) plD_state_cairo_fam;
2437  pdt->pl_esc = (plD_esc_fp) plD_esc_cairo_fam;
2438 }
2439 
2440 //--------------------------------------------------------------------------
2441 // plD_init_epscairo()
2442 //
2443 // Initialize Cairo EPS device
2444 //--------------------------------------------------------------------------
2445 
2446 void plD_init_epscairo( PLStream *pls )
2447 {
2448  PLCairo *aStream;
2449 
2450  // Setup the PLStream and the font lookup table and allocate a cairo
2451  // stream structure.
2452  //
2453  // NOTE: The check below is necessary since, in family mode, this function
2454  // will be called multiple times. While you might think that it is
2455  // sufficient to update what *should* be the only pointer to the contents
2456  // of pls->dev, i.e. the pointer pls->dev itself, it appears that
2457  // something else somewhere else is also pointing to pls->dev. If you
2458  // change what pls->dev points to then you will get a "bus error", from
2459  // which I infer the existence of said bad stale pointer.
2460  //
2461  if ( pls->dev == NULL )
2462  {
2463  aStream = stream_and_font_setup( pls, 0 );
2464  }
2465  else
2466  {
2467  stream_and_font_setup( pls, 0 );
2468  aStream = pls->dev;
2469  }
2470 
2471  // Initialize family file info
2472  plFamInit( pls );
2473 
2474  // Prompt for a file name if not already set.
2475  plOpenFile( pls );
2476 
2477  // Create an cairo surface & context for EPS file.
2478  // Dimension units are pts = 1/72 inches from cairo documentation.
2479  aStream->cairoSurface = cairo_ps_surface_create_for_stream( (cairo_write_func_t) write_to_stream, pls->OutFile, (double) pls->ylength, (double) pls->xlength );
2480  aStream->cairoContext = cairo_create( aStream->cairoSurface );
2481 
2482  // Set the PS surface to be EPS.
2483  cairo_ps_surface_set_eps( aStream->cairoSurface, 1 );
2484 
2485  // Save the pointer to the structure in the PLplot stream
2486  pls->dev = aStream;
2487 
2488  // Handle portrait or landscape
2489  if ( pls->portrait )
2490  {
2491  plsdiori( 1 );
2492  pls->freeaspect = 1;
2493  }
2494  rotate_cairo_surface( pls, 0.0, -1.0, -1.0, 0.0, (float) pls->ylength, (float) pls->xlength, FALSE );
2495 
2496  // Set fill rule for the case of self-intersecting boundaries.
2497  if ( pls->dev_eofill )
2498  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
2499  else
2500  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
2501 }
2502 
2503 #endif
2504 
2505 
2506 //--------------------------------------------------------------------------
2507 //--------------------------------------------------------------------------
2508 //
2509 // That which is specific to the cairo SVG driver.
2510 //
2511 //--------------------------------------------------------------------------
2512 //--------------------------------------------------------------------------
2513 
2514 #if defined ( PLD_svgcairo )
2515 
2517 void plD_init_svgcairo( PLStream * );
2518 
2519 //--------------------------------------------------------------------------
2520 // dispatch_init_init()
2521 //
2522 // Initialize device dispatch table
2523 //--------------------------------------------------------------------------
2524 
2525 // svgcairo
2527 {
2528 #ifndef ENABLE_DYNDRIVERS
2529  pdt->pl_MenuStr = "Cairo SVG Driver";
2530  pdt->pl_DevName = "svgcairo";
2531 #endif
2533  pdt->pl_seq = 103;
2534  pdt->pl_init = (plD_init_fp) plD_init_svgcairo;
2535  pdt->pl_line = (plD_line_fp) plD_line_cairo_fam;
2536  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_cairo_fam;
2537  pdt->pl_eop = (plD_eop_fp) plD_eop_cairo_fam;
2538  pdt->pl_bop = (plD_bop_fp) plD_bop_cairo_fam;
2539  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_cairo_fam;
2540  pdt->pl_state = (plD_state_fp) plD_state_cairo_fam;
2541  pdt->pl_esc = (plD_esc_fp) plD_esc_cairo_fam;
2542 }
2543 
2544 //--------------------------------------------------------------------------
2545 // plD_init_svgcairo()
2546 //
2547 // Initialize Cairo SVG device
2548 //--------------------------------------------------------------------------
2549 
2550 void plD_init_svgcairo( PLStream *pls )
2551 {
2552  PLCairo *aStream;
2553 
2554  // Setup the PLStream and the font lookup table and allocate a cairo
2555  // stream structure.
2556  //
2557  // NOTE: The check below is necessary since, in family mode, this function
2558  // will be called multiple times. While you might think that it is
2559  // sufficient to update what *should* be the only pointer to the contents
2560  // of pls->dev, i.e. the pointer pls->dev itself, it appears that
2561  // something else somewhere else is also pointing to pls->dev. If you
2562  // change what pls->dev points to then you will get a "bus error", from
2563  // which I infer the existence of said bad stale pointer.
2564  //
2565  if ( pls->dev == NULL )
2566  {
2567  aStream = stream_and_font_setup( pls, 0 );
2568  }
2569  else
2570  {
2571  stream_and_font_setup( pls, 0 );
2572  aStream = pls->dev;
2573  }
2574 
2575  // Initialize family file info
2576  plFamInit( pls );
2577 
2578  // Prompt for a file name if not already set.
2579  plOpenFile( pls );
2580 
2581  // Save the pointer to the structure in the PLplot stream
2582  pls->dev = aStream;
2583 
2584  // Create an cairo surface & context for SVG file.
2585  // Dimension units are pts = 1/72 inches from cairo documentation.
2586  aStream->cairoSurface = cairo_svg_surface_create_for_stream( (cairo_write_func_t) write_to_stream, pls->OutFile, (double) pls->xlength, (double) pls->ylength );
2587  aStream->cairoContext = cairo_create( aStream->cairoSurface );
2588 
2589  // Invert the surface so that the graphs are drawn right side up.
2590  rotate_cairo_surface( pls, 1.0, 0.0, 0.0, -1.0, 0.0, (float) pls->ylength, FALSE );
2591 
2592  // Set graphics aliasing
2593  cairo_set_antialias( aStream->cairoContext, aStream->graphics_anti_aliasing );
2594 
2595  // Set fill rule for the case of self-intersecting boundaries.
2596  if ( pls->dev_eofill )
2597  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
2598  else
2599  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
2600 }
2601 
2602 #endif
2603 
2604 
2605 //--------------------------------------------------------------------------
2606 //--------------------------------------------------------------------------
2607 //
2608 // That which is specific to the cairo PNG driver.
2609 //
2610 //--------------------------------------------------------------------------
2611 //--------------------------------------------------------------------------
2612 
2613 #if defined ( PLD_pngcairo )
2614 
2616 void plD_init_pngcairo( PLStream * );
2617 void plD_eop_pngcairo( PLStream * );
2618 
2619 //--------------------------------------------------------------------------
2620 // dispatch_init_init()
2621 //
2622 // Initialize device dispatch table
2623 //--------------------------------------------------------------------------
2624 
2625 // pngcairo
2627 {
2628 #ifndef ENABLE_DYNDRIVERS
2629  pdt->pl_MenuStr = "Cairo PNG Driver";
2630  pdt->pl_DevName = "pngcairo";
2631 #endif
2633  pdt->pl_seq = 104;
2634  pdt->pl_init = (plD_init_fp) plD_init_pngcairo;
2635  pdt->pl_line = (plD_line_fp) plD_line_cairo_fam;
2636  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_cairo_fam;
2637  pdt->pl_eop = (plD_eop_fp) plD_eop_pngcairo;
2638  pdt->pl_bop = (plD_bop_fp) plD_bop_cairo_fam;
2639  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_cairo_fam;
2640  pdt->pl_state = (plD_state_fp) plD_state_cairo_fam;
2641  pdt->pl_esc = (plD_esc_fp) plD_esc_cairo_fam;
2642 }
2643 
2644 //--------------------------------------------------------------------------
2645 // plD_init_pngcairo()
2646 //
2647 // Initialize Cairo PNG device
2648 //--------------------------------------------------------------------------
2649 
2650 void plD_init_pngcairo( PLStream *pls )
2651 {
2652  PLCairo *aStream;
2653 
2654  // Setup the PLStream and the font lookup table and allocate a cairo
2655  // stream structure.
2656  //
2657  // NOTE: The check below is necessary since, in family mode, this function
2658  // will be called multiple times. While you might think that it is
2659  // sufficient to update what *should* be the only pointer to the contents
2660  // of pls->dev, i.e. the pointer pls->dev itself, it appears that
2661  // something else somewhere else is also pointing to pls->dev. If you
2662  // change what pls->dev points to then you will get a "bus error", from
2663  // which I infer the existence of said bad stale pointer.
2664  //
2665  if ( pls->dev == NULL )
2666  {
2667  aStream = stream_and_font_setup( pls, 0 );
2668  }
2669  else
2670  {
2671  stream_and_font_setup( pls, 0 );
2672  aStream = pls->dev;
2673  }
2674 
2675  // Initialize family file info
2676  plFamInit( pls );
2677 
2678  // Prompt for a file name if not already set.
2679  plOpenFile( pls );
2680 
2681  // Save the pointer to the structure in the PLplot stream
2682  pls->dev = aStream;
2683 
2684  // Create a new cairo surface & context for PNG file.
2685  // Dimension units are pixels from cairo documentation.
2686  aStream->cairoSurface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, (int) pls->xlength, (int) pls->ylength );
2687  aStream->cairoContext = cairo_create( aStream->cairoSurface );
2688 
2689  // Invert the surface so that the graphs are drawn right side up.
2690  rotate_cairo_surface( pls, 1.0, 0.0, 0.0, -1.0, 0.0, (float) pls->ylength, FALSE );
2691 
2692  // Set graphics aliasing
2693  cairo_set_antialias( aStream->cairoContext, aStream->graphics_anti_aliasing );
2694 
2695  // Set fill rule for the case of self-intersecting boundaries.
2696  if ( pls->dev_eofill )
2697  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
2698  else
2699  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
2700 }
2701 
2702 //--------------------------------------------------------------------------
2703 // plD_eop_pngcairo()
2704 //
2705 // PNG: End of page.
2706 //--------------------------------------------------------------------------
2707 
2708 void plD_eop_pngcairo( PLStream *pls )
2709 {
2710  PLCairo *aStream;
2711 
2712  if ( cairo_family_check( pls ) )
2713  {
2714  return;
2715  }
2716 
2717  aStream = (PLCairo *) pls->dev;
2718  cairo_surface_write_to_png_stream( aStream->cairoSurface, (cairo_write_func_t) write_to_stream, pls->OutFile );
2719 }
2720 
2721 #endif
2722 
2723 
2724 //--------------------------------------------------------------------------
2725 //--------------------------------------------------------------------------
2726 //
2727 // That which is specific to the cairo memory driver.
2728 //
2729 //--------------------------------------------------------------------------
2730 //--------------------------------------------------------------------------
2731 
2732 #if defined ( PLD_memcairo )
2733 
2735 void plD_init_memcairo( PLStream * );
2736 void plD_eop_memcairo( PLStream * );
2737 void plD_bop_memcairo( PLStream * );
2738 
2739 //--------------------------------------------------------------------------
2740 // dispatch_init_init()
2741 //
2742 // Initialize device dispatch table
2743 //--------------------------------------------------------------------------
2744 
2745 // memcairo
2747 {
2748 #ifndef ENABLE_DYNDRIVERS
2749  pdt->pl_MenuStr = "Cairo memory driver";
2750  pdt->pl_DevName = "memcairo";
2751 #endif
2753  pdt->pl_seq = 105;
2754  pdt->pl_init = (plD_init_fp) plD_init_memcairo;
2757  pdt->pl_eop = (plD_eop_fp) plD_eop_memcairo;
2758  pdt->pl_bop = (plD_bop_fp) plD_bop_memcairo;
2761  pdt->pl_esc = (plD_esc_fp) plD_esc_cairo;
2762 }
2763 
2764 //--------------------------------------------------------------------------
2765 // plD_bop_memcairo()
2766 //
2767 // Set up for the next page.
2768 //--------------------------------------------------------------------------
2769 
2770 void plD_bop_memcairo( PLStream * PL_UNUSED( pls ) )
2771 {
2772  // nothing to do here (we want to preserve the memory as it is)
2773 }
2774 
2775 //--------------------------------------------------------------------------
2776 // plD_init_memcairo()
2777 //
2778 // Initialize Cairo memory device
2779 //--------------------------------------------------------------------------
2780 
2781 void plD_init_memcairo( PLStream *pls )
2782 {
2783  PLCairo *aStream;
2784  int stride, i;
2785  unsigned char *cairo_mem;
2786  unsigned char *input_mem;
2787 
2788  // used for checking byte order
2789  union
2790  {
2791  int testWord;
2792  char testByte[sizeof ( int )];
2793  } endianTest;
2794  endianTest.testWord = 1;
2795 
2796  // Set the plot size to the memory buffer size, on the off chance
2797  // that they are different.
2798  pls->xlength = pls->phyxma;
2799  pls->ylength = pls->phyyma;
2800 
2801 
2802  // Setup the PLStream and the font lookup table
2803  aStream = stream_and_font_setup( pls, 0 );
2804 
2805  // Test byte order
2806  if ( endianTest.testByte[0] == 1 )
2807  aStream->bigendian = 0;
2808  else
2809  aStream->bigendian = 1;
2810 
2811  // Check that user supplied us with some memory to draw in
2812  if ( pls->dev == NULL )
2813  {
2814  plexit( "Must call plsmem first to set user plotting area!" );
2815  }
2816 
2817  // Save a pointer to the memory.
2818  aStream->memory = pls->dev;
2819 
2820  // Create a cairo surface & context. Copy data in from the input memory area
2821 
2822  // Malloc memory the way cairo likes it. Aligned on the stride computed by cairo_format_stride_for_width
2823  // and in the RGB24 format (from http://cairographics.org/manual/cairo-Image-Surfaces.html):
2824  // Each pixel is a 32-bit quantity, with the upper 8 bits unused.
2825  // Red, Green, and Blue are stored in the remaining 24 bits in that order
2826  stride = pls->xlength * 4;
2827  // stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, pls->xlength); This function missing from version 1.4 :-(
2828  aStream->cairo_format_memory = (unsigned char *) calloc( (size_t) ( stride * pls->ylength ), 1 );
2829 
2830  // Copy the input data into the Cairo data format
2831  cairo_mem = aStream->cairo_format_memory;
2832  input_mem = aStream->memory;
2833 
2834  // 32 bit word order
2835  // cairo mem: Big endian: 0=A, 1=R, 2=G, 3=B
2836  // Little endian: 3=A, 2=R, 1=G, 0=B
2837 
2838  if ( aStream->bigendian )
2839  {
2840  for ( i = 0; i < ( pls->xlength * pls->ylength ); i++ )
2841  {
2842  cairo_mem[1] = input_mem[0]; // R
2843  cairo_mem[2] = input_mem[1]; // G
2844  cairo_mem[3] = input_mem[2]; // B
2845  if ( pls->dev_mem_alpha == 1 )
2846  {
2847  cairo_mem[0] = input_mem[3];
2848  input_mem += 4;
2849  }
2850  else
2851  {
2852  input_mem += 3;
2853  }
2854  cairo_mem += 4;
2855  }
2856  }
2857  else
2858  {
2859  for ( i = 0; i < ( pls->xlength * pls->ylength ); i++ )
2860  {
2861  cairo_mem[2] = input_mem[0]; // R
2862  cairo_mem[1] = input_mem[1]; // G
2863  cairo_mem[0] = input_mem[2]; // B
2864  if ( pls->dev_mem_alpha == 1 )
2865  {
2866  cairo_mem[3] = input_mem[3];
2867  input_mem += 4;
2868  }
2869  else
2870  {
2871  input_mem += 3;
2872  }
2873  cairo_mem += 4;
2874  }
2875  }
2876 
2877  // Create a Cairo drawing surface from the input data
2878  aStream->cairoSurface =
2879  // Dimension units are width, height of buffer image from cairo
2880  // documentation.
2881  cairo_image_surface_create_for_data( aStream->cairo_format_memory, CAIRO_FORMAT_RGB24, pls->xlength, pls->ylength, stride );
2882  aStream->cairoContext = cairo_create( aStream->cairoSurface );
2883 
2884  // Save the pointer to the structure in the PLplot stream.
2885  // Note that this wipes out the direct pointer to the memory buffer.
2886  pls->dev = aStream;
2887 
2888  // Invert the surface so that the graphs are drawn right side up.
2889  rotate_cairo_surface( pls, 1.0, 0.0, 0.0, -1.0, 0.0, (float) pls->ylength, FALSE );
2890 
2891  // Set graphics aliasing
2892  cairo_set_antialias( aStream->cairoContext, aStream->graphics_anti_aliasing );
2893 
2894  // Set fill rule for the case of self-intersecting boundaries.
2895  if ( pls->dev_eofill )
2896  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
2897  else
2898  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
2899 }
2900 
2901 //--------------------------------------------------------------------------
2902 // plD_eop_memcairo()
2903 //
2904 // Memory device specific end of page. This copies the contents
2905 // of the cairo surface into the user supplied memory buffer.
2906 //--------------------------------------------------------------------------
2907 
2908 void plD_eop_memcairo( PLStream *pls )
2909 {
2910  int i;
2911  unsigned char *memory;
2912  unsigned char *cairo_surface_data;
2913  PLCairo *aStream;
2914 
2915  aStream = (PLCairo *) pls->dev;
2916  memory = aStream->memory;
2917  cairo_surface_data = cairo_image_surface_get_data( aStream->cairoSurface );
2918  // 32 bit word order
2919  // cairo mem: Big endian: 0=A, 1=R, 2=G, 3=B
2920  // Little endian: 3=A, 2=R, 1=G, 0=B
2921  if ( aStream->bigendian )
2922  {
2923  for ( i = 0; i < ( pls->xlength * pls->ylength ); i++ )
2924  {
2925  memory[0] = cairo_surface_data[1]; // R
2926  memory[1] = cairo_surface_data[2]; // G
2927  memory[2] = cairo_surface_data[3]; // B
2928  if ( pls->dev_mem_alpha == 1 )
2929  {
2930  memory[3] = cairo_surface_data[0];
2931  memory += 4;
2932  }
2933  else
2934  {
2935  memory += 3;
2936  }
2937  cairo_surface_data += 4;
2938  }
2939  }
2940  else
2941  {
2942  for ( i = 0; i < ( pls->xlength * pls->ylength ); i++ )
2943  {
2944  memory[0] = cairo_surface_data[2]; // R
2945  memory[1] = cairo_surface_data[1]; // G
2946  memory[2] = cairo_surface_data[0]; // B
2947  if ( pls->dev_mem_alpha == 1 )
2948  {
2949  memory[3] = cairo_surface_data[3];
2950  memory += 4;
2951  }
2952  else
2953  {
2954  memory += 3;
2955  }
2956  cairo_surface_data += 4;
2957  }
2958  }
2959 
2960  // Free up the temporary memory malloc'ed in plD_init_memcairo
2961  free( aStream->cairo_format_memory );
2962 }
2963 
2964 #endif
2965 
2966 //--------------------------------------------------------------------------
2967 //--------------------------------------------------------------------------
2968 //
2969 // That which is specific to the cairo external context driver.
2970 //
2971 //--------------------------------------------------------------------------
2972 //--------------------------------------------------------------------------
2973 
2974 #if defined ( PLD_extcairo )
2975 
2976 void extcairo_setbackground( PLStream * );
2978 void plD_init_extcairo( PLStream * );
2979 void plD_bop_extcairo( PLStream * );
2980 void plD_eop_extcairo( PLStream * );
2981 void plD_esc_extcairo( PLStream *, PLINT, void * );
2982 void plD_tidy_extcairo( PLStream * );
2983 
2984 //--------------------------------------------------------------------------
2985 // extcairo_setbackground()
2986 //
2987 // Set the background color for the extcairo device
2988 //--------------------------------------------------------------------------
2989 
2990 void extcairo_setbackground( PLStream *pls )
2991 {
2992  PLCairo *aStream;
2993 
2994  aStream = (PLCairo *) pls->dev;
2995 
2996  // Fill the context with the background color if the user so desires.
2997  if ( aStream->cairoContext != NULL )
2998  {
2999  cairo_rectangle( aStream->cairoContext, 0.0, 0.0, pls->xlength, pls->ylength );
3000  cairo_set_source_rgba( aStream->cairoContext,
3001  (double) pls->cmap0[0].r / 255.0,
3002  (double) pls->cmap0[0].g / 255.0,
3003  (double) pls->cmap0[0].b / 255.0,
3004  (double) pls->cmap0[0].a );
3005  cairo_fill( aStream->cairoContext );
3006  }
3007 }
3008 
3009 //--------------------------------------------------------------------------
3010 // dispatch_init_init()
3011 //
3012 // Initialize device dispatch table
3013 //--------------------------------------------------------------------------
3014 
3015 // extcairo
3017 {
3018 #ifndef ENABLE_DYNDRIVERS
3019  pdt->pl_MenuStr = "Cairo external context driver";
3020  pdt->pl_DevName = "extcairo";
3021 #endif
3023  pdt->pl_seq = 106;
3024  pdt->pl_init = (plD_init_fp) plD_init_extcairo;
3027  pdt->pl_bop = (plD_bop_fp) plD_bop_extcairo;
3028  pdt->pl_eop = (plD_eop_fp) plD_eop_extcairo;
3029  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_extcairo;
3031  pdt->pl_esc = (plD_esc_fp) plD_esc_extcairo;
3032 }
3033 
3034 //--------------------------------------------------------------------------
3035 // plD_init_extcairo()
3036 //
3037 // Initialize Cairo external context driver.
3038 //--------------------------------------------------------------------------
3039 
3040 void plD_init_extcairo( PLStream *pls )
3041 {
3042  PLCairo *aStream;
3043 
3044  // Setup the PLStream and the font lookup table
3045  aStream = stream_and_font_setup( pls, 0 );
3046 
3047  // Save the pointer to the structure in the PLplot stream
3048  pls->dev = aStream;
3049 }
3050 
3051 //--------------------------------------------------------------------------
3052 // plD_bop_extcairo()
3053 //
3054 // Set up for the next page.
3055 //--------------------------------------------------------------------------
3056 
3057 void plD_bop_extcairo( PLStream *pls )
3058 {
3059  PLCairo *aStream;
3060 
3061  aStream = (PLCairo *) pls->dev;
3062 
3063  // Set background if desired
3064  if ( aStream->set_background )
3065  {
3066  extcairo_setbackground( pls );
3067  }
3068 }
3069 
3070 //--------------------------------------------------------------------------
3071 // plD_eop_extcairo()
3072 //
3073 // End of page.
3074 //--------------------------------------------------------------------------
3075 
3076 void plD_eop_extcairo( PLStream * PL_UNUSED( pls ) )
3077 {
3078  // nothing to do here, we leave it to the calling program to display
3079  // (or not) the update cairo context.
3080 }
3081 
3082 //--------------------------------------------------------------------------
3083 // plD_esc_extcairo()
3084 //
3085 // The generic escape function, extended so that user can pass in
3086 // an external Cairo context to use for rendering.
3087 //--------------------------------------------------------------------------
3088 
3089 void plD_esc_extcairo( PLStream *pls, PLINT op, void *ptr )
3090 {
3091  PLCairo *aStream;
3092 
3093  aStream = (PLCairo *) pls->dev;
3094 
3095  switch ( op )
3096  {
3097  case PLESC_DEVINIT: // Set external context
3098  aStream->cairoContext = (cairo_t *) ptr;
3099  // Set graphics aliasing
3100  cairo_set_antialias( aStream->cairoContext, aStream->graphics_anti_aliasing );
3101 
3102  // Invert the surface so that the graphs are drawn right side up.
3103  rotate_cairo_surface( pls, 1.0, 0.0, 0.0, -1.0, 0.0, (float) pls->ylength, FALSE );
3104 
3105  // Should adjust plot size to fit in the given cairo context?
3106  // Cairo does not provide a way to query the dimensions of a context?
3107 
3108  // Set background if desired
3109  if ( aStream->set_background )
3110  {
3111  extcairo_setbackground( pls );
3112  }
3113 
3114  // Set fill rule for the case of self-intersecting boundaries.
3115  if ( pls->dev_eofill )
3116  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
3117  else
3118  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
3119  break;
3120  default: // Fall back on default Cairo actions
3121  plD_esc_cairo( pls, op, ptr );
3122  break;
3123  }
3124 }
3125 
3126 //--------------------------------------------------------------------------
3127 // plD_tidy_extcairo()
3128 //
3129 // This is nop, it is up to the calling program to clean up the Cairo
3130 // context, etc...
3131 //--------------------------------------------------------------------------
3132 
3133 void plD_tidy_extcairo( PLStream * PL_UNUSED( pls ) )
3134 {
3135 }
3136 
3137 #endif
3138 
3139 
3140 //--------------------------------------------------------------------------
3141 //--------------------------------------------------------------------------
3142 //
3143 // That which is specific to the cairo microsoft windows driver.
3144 //
3145 // Much of the Windows specific code here was lifted from the wingcc
3146 // driver.
3147 //
3148 //--------------------------------------------------------------------------
3149 //--------------------------------------------------------------------------
3150 
3151 #if defined ( PLD_wincairo )
3152 
3153 static char* szWndClass = "PLplot WinCairo";
3154 
3156 void plD_init_wincairo( PLStream * );
3157 //void plD_bop_extcairo( PLStream * );
3158 void plD_eop_wincairo( PLStream * );
3159 void plD_esc_wincairo( PLStream *, PLINT, void * );
3160 void plD_tidy_wincairo( PLStream * );
3161 
3162 //--------------------------------------------------------------------------
3163 // blit_to_win()
3164 //
3165 // Blit the offscreen image to the Windows window.
3166 //--------------------------------------------------------------------------
3167 
3168 void blit_to_win( PLCairo *aStream )
3169 {
3170  cairo_set_source_surface( aStream->cairoContext_win, aStream->cairoSurface, 0.0, 0.0 );
3171  cairo_paint( aStream->cairoContext_win );
3172 }
3173 
3174 //--------------------------------------------------------------------------
3175 // This is the window function for the plot window. Whenever a message is
3176 // dispatched using DispatchMessage (or sent with SendMessage) this function
3177 // gets called with the contents of the message.
3178 //--------------------------------------------------------------------------
3179 
3180 LRESULT CALLBACK PlplotCairoWndProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
3181 {
3182  PLStream *pls = NULL;
3183  PLCairo *dev = NULL;
3184 
3185 //
3186 // The window carries a 32bit user defined pointer which points to the
3187 // plplot stream (pls). This is used for tracking the window.
3188 // Unfortunately, this is "attached" to the window AFTER it is created
3189 // so we can not initialise PLStream or wincairo "blindly" because
3190 // they may not yet have been initialised.
3191 // WM_CREATE is called before we get to initialise those variables, so
3192 // we wont try to set them.
3193 //
3194 
3195  if ( nMsg == WM_CREATE )
3196  {
3197  return ( 0 );
3198  }
3199  else
3200  {
3201  pls = (PLStream *) GetWindowLong( hwnd, GWL_USERDATA ); // Try to get the address to pls for this window
3202  if ( pls ) // If we got it, then we will initialise this windows plplot private data area
3203  {
3204  dev = (PLCairo *) pls->dev;
3205  }
3206  }
3207 
3208 //
3209 // Process the windows messages
3210 //
3211 // Everything except WM_CREATE is done here and it is generally hoped that
3212 // pls and dev are defined already by this stage.
3213 // That will be true MOST of the time. Some times WM_PAINT will be called
3214 // before we get to initialise the user data area of the window with the
3215 // pointer to the windows plplot stream
3216 //
3217 
3218  switch ( nMsg )
3219  {
3220  case WM_DESTROY:
3221  // if ( dev )
3222  // Debug( "WM_DESTROY\t" );
3223  PostQuitMessage( 0 );
3224  return ( 0 );
3225  break;
3226 
3227  case WM_PAINT:
3228  blit_to_win( dev );
3229  return ( 1 );
3230  break;
3231 
3232  case WM_SIZE:
3233  GetClientRect( dev->hwnd, &dev->rect );
3234  return ( 0 );
3235  break;
3236 
3237  case WM_ENTERSIZEMOVE:
3238  return ( 0 );
3239  break;
3240 
3241  case WM_EXITSIZEMOVE:
3242  return ( 0 );
3243  break;
3244 
3245  case WM_ERASEBKGND:
3246  if ( dev )
3247  {
3248  dev->oldcolour = SetBkColor( dev->hdc, RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) );
3249  ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &dev->rect, "", 0, 0 );
3250  SetBkColor( dev->hdc, dev->oldcolour );
3251  }
3252  return ( 1 );
3253  break;
3254 
3255  case WM_COMMAND:
3256  return ( 0 );
3257  break;
3258  }
3259 
3260  // If we don't handle a message completely we hand it to the system
3261  // provided default window function.
3262  return DefWindowProc( hwnd, nMsg, wParam, lParam );
3263 }
3264 
3265 //--------------------------------------------------------------------------
3266 // handle_locate()
3267 //
3268 // Handle getting the cursor location.
3269 //--------------------------------------------------------------------------
3270 
3271 void
3272 handle_locate( PLStream *pls, PLGraphicsIn *gin )
3273 {
3274  int located = 0;
3275  PLCairo *aStream = (PLCairo *) pls->dev;
3276 
3277  // Initialize PLplot mouse event structure
3278  plGinInit( gin );
3279 
3280  while ( GetMessage( &aStream->msg, NULL, 0, 0 ) && !located )
3281  {
3282  TranslateMessage( &aStream->msg );
3283 
3284  switch ( (int) aStream->msg.message )
3285  {
3286  case WM_MOUSEMOVE:
3287  case WM_LBUTTONDOWN:
3288  gin->state = 1;
3289  gin->button = 1;
3290  gin->pX = LOWORD( aStream->msg.lParam );
3291  gin->pY = pls->ylength - HIWORD( aStream->msg.lParam );
3292  gin->dX = (PLFLT) LOWORD( aStream->msg.lParam ) / ( (PLFLT) pls->xlength );
3293  gin->dY = (PLFLT) ( pls->ylength - HIWORD( aStream->msg.lParam ) ) / ( (PLFLT) pls->ylength );
3294  break;
3295  case WM_CHAR:
3296  gin->keysym = aStream->msg.wParam;
3297  located = 1;
3298  break;
3299 
3300  default:
3301  DispatchMessage( &aStream->msg );
3302  break;
3303  }
3304  }
3305 }
3306 
3307 //--------------------------------------------------------------------------
3308 // dispatch_init_init()
3309 //
3310 // Initialize device dispatch table
3311 //--------------------------------------------------------------------------
3312 
3313 // extcairo
3315 {
3316 #ifndef ENABLE_DYNDRIVERS
3317  pdt->pl_MenuStr = "Cairo Microsoft Windows driver";
3318  pdt->pl_DevName = "wincairo";
3319 #endif
3321  pdt->pl_seq = 107;
3322  pdt->pl_init = (plD_init_fp) plD_init_wincairo;
3325  pdt->pl_bop = (plD_bop_fp) plD_bop_cairo;
3326  pdt->pl_eop = (plD_eop_fp) plD_eop_wincairo;
3327  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wincairo;
3329  pdt->pl_esc = (plD_esc_fp) plD_esc_wincairo;
3330 }
3331 
3332 //--------------------------------------------------------------------------
3333 // plD_init_wincairo()
3334 //
3335 // Initialize Cairo Microsoft Windows driver.
3336 //--------------------------------------------------------------------------
3337 
3338 void plD_init_wincairo( PLStream *pls )
3339 {
3340  PLCairo *aStream;
3341 
3342  // Setup the PLStream and the font lookup table
3343  aStream = stream_and_font_setup( pls, 1 );
3344 
3345  // Save the pointer to the structure in the PLplot stream
3346  pls->dev = aStream;
3347 
3348  // Create window
3349  memset( &aStream->wndclass, 0, sizeof ( WNDCLASSEX ) );
3350 
3351  // This class is called WinTestWin
3352  aStream->wndclass.lpszClassName = szWndClass;
3353 
3354  // cbSize gives the size of the structure for extensibility.
3355  aStream->wndclass.cbSize = sizeof ( WNDCLASSEX );
3356 
3357  // All windows of this class redraw when resized.
3358  aStream->wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC | CS_PARENTDC;
3359 
3360  // All windows of this class use the PlplotCairoWndProc window function.
3361  aStream->wndclass.lpfnWndProc = PlplotCairoWndProc;
3362 
3363  // This class is used with the current program instance.
3364 
3365  aStream->wndclass.hInstance = GetModuleHandle( NULL );
3366 
3367  // Use standard application icon and arrow cursor provided by the OS
3368  aStream->wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
3369  aStream->wndclass.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
3370  aStream->wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
3371  // Color the background white
3372  aStream->wndclass.hbrBackground = NULL;
3373 
3374  aStream->wndclass.cbWndExtra = sizeof ( pls );
3375 
3376 
3377  //
3378  // Now register the window class for use.
3379  //
3380 
3381  RegisterClassEx( &aStream->wndclass );
3382 
3383  //
3384  // Create our main window using that window class.
3385  //
3386  aStream->hwnd = CreateWindowEx( WS_EX_WINDOWEDGE + WS_EX_LEFT,
3387  szWndClass, // Class name
3388  pls->program, // Caption
3389  WS_OVERLAPPEDWINDOW, // Style
3390  pls->xoffset, // Initial x (use default)
3391  pls->yoffset, // Initial y (use default)
3392  // This is a little lame since the window border size might change.
3393  pls->xlength + 5, // Initial x size (use default)
3394  pls->ylength + 30, // Initial y size (use default)
3395  NULL, // No parent window
3396  NULL, // No menu
3397  aStream->wndclass.hInstance, // This program instance
3398  NULL // Creation parameters
3399  );
3400 
3401 
3402 //
3403 // Attach a pointer to the stream to the window's user area
3404 // this pointer will be used by the windows call back for
3405 // process this window
3406 //
3407 
3408  SetWindowLong( aStream->hwnd, GWL_USERDATA, (long) pls );
3409  aStream->SCRN_hdc = aStream->hdc = GetDC( aStream->hwnd );
3410 
3411 //
3412 // Setup the popup menu
3413 //
3414 
3415 //
3416 // dev->PopupMenu = CreatePopupMenu();
3417 // AppendMenu( dev->PopupMenu, MF_STRING, PopupPrint, "Print" );
3418 // AppendMenu( dev->PopupMenu, MF_STRING, PopupNextPage, "Next Page" );
3419 // AppendMenu( dev->PopupMenu, MF_STRING, PopupQuit, "Quit" );
3420 //
3421 
3422  // plD_state_wingcc( pls, PLSTATE_COLOR0 );
3423  //
3424  // Display the window which we just created (using the nShow
3425  // passed by the OS, which allows for start minimized and that
3426  // sort of thing).
3427  //
3428  ShowWindow( aStream->hwnd, SW_SHOWDEFAULT );
3429  SetForegroundWindow( aStream->hwnd );
3430 
3431 //
3432 // Now we have to find out, from windows, just how big our drawing area is
3433 // when we specified the page size earlier on, that includes the borders,
3434 // title bar etc... so now that windows has done all its initialisations,
3435 // we will ask how big the drawing area is, and tell plplot
3436 //
3437 
3438 //
3439 // GetClientRect( dev->hwnd, &dev->rect );
3440 // dev->width = dev->rect.right;
3441 // dev->height = dev->rect.bottom;
3442 //
3443 
3444 //
3445 // Initialize Cairo Surface using the windows hdc.
3446 //
3447 
3448  // This is the Win32 Cairo surface.
3449  aStream->cairoSurface_win = (cairo_surface_t *) cairo_win32_surface_create( aStream->hdc );
3450  aStream->cairoContext_win = cairo_create( aStream->cairoSurface_win );
3451 
3452  // This is the Cairo surface PLplot will actually plot to.
3453  aStream->cairoSurface = cairo_image_surface_create( CAIRO_FORMAT_RGB24, pls->xlength, pls->ylength );
3454  aStream->cairoContext = cairo_create( aStream->cairoSurface );
3455 
3456  // Invert the surface so that the graphs are drawn right side up.
3457  rotate_cairo_surface( pls, 1.0, 0.0, 0.0, -1.0, 0.0, pls->ylength, FALSE );
3458 
3459  // Set graphics aliasing
3460  cairo_set_antialias( aStream->cairoContext, aStream->graphics_anti_aliasing );
3461 
3462  // Set fill rule for the case of self-intersecting boundaries.
3463  if ( pls->dev_eofill )
3464  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_EVEN_ODD );
3465  else
3466  cairo_set_fill_rule( aStream->cairoContext, CAIRO_FILL_RULE_WINDING );
3467 }
3468 
3469 //--------------------------------------------------------------------------
3470 // plD_eop_wincairo()
3471 //
3472 // Clean up Cairo Microsoft Windows driver.
3473 //--------------------------------------------------------------------------
3474 
3475 void
3476 plD_eop_wincairo( PLStream *pls )
3477 {
3478  PLCairo *aStream = (PLCairo *) pls->dev;
3479 
3480  if ( !pls->nopause )
3481  {
3482  while ( GetMessage( &aStream->msg, NULL, 0, 0 ) )
3483  {
3484  TranslateMessage( &aStream->msg );
3485  switch ( (int) aStream->msg.message )
3486  {
3487  case WM_CHAR:
3488  if ( ( (TCHAR) ( aStream->msg.wParam ) == 13 ) ||
3489  ( (TCHAR) ( aStream->msg.wParam ) == 'q' ) ||
3490  ( (TCHAR) ( aStream->msg.wParam ) == 'Q' ) )
3491  {
3492  PostQuitMessage( 0 );
3493  }
3494  break;
3495 
3496  default:
3497  DispatchMessage( &aStream->msg );
3498  break;
3499  }
3500  }
3501  }
3502 }
3503 
3504 //--------------------------------------------------------------------------
3505 // plD_tidy_wincairo()
3506 //
3507 // Clean up Cairo Microsoft Windows driver.
3508 //--------------------------------------------------------------------------
3509 
3510 void plD_tidy_wincairo( PLStream *pls )
3511 {
3512  PLCairo *aStream = (PLCairo *) pls->dev;
3513 
3514  plD_tidy_cairo( pls );
3515 
3516  // Also free up the Cairo win32 surface and context
3517  cairo_destroy( aStream->cairoContext_win );
3518  cairo_surface_destroy( aStream->cairoSurface_win );
3519 
3520  if ( aStream != NULL )
3521  {
3522  if ( aStream->hdc != NULL )
3523  ReleaseDC( aStream->hwnd, aStream->hdc );
3524  free_mem( pls->dev );
3525  }
3526 }
3527 
3528 //--------------------------------------------------------------------------
3529 // plD_esc_wincairo()
3530 //
3531 // Escape function, specialized for the wincairo driver
3532 //--------------------------------------------------------------------------
3533 
3534 void plD_esc_wincairo( PLStream *pls, PLINT op, void *ptr )
3535 {
3536  PLCairo *aStream;
3537 
3538  aStream = (PLCairo *) pls->dev;
3539 
3540  switch ( op )
3541  {
3542  case PLESC_FLUSH:
3543  InvalidateRect( aStream->hwnd, NULL, TRUE );
3544  break;
3545  case PLESC_GETC:
3546  handle_locate( pls, (PLGraphicsIn *) ptr );
3547  break;
3548  default:
3549  plD_esc_cairo( pls, op, ptr );
3550  break;
3551  }
3552 }
3553 
3554 #endif