1 /*
2   drvcairo.cpp : This file is part of pstoedit
3   Copyright (C) 2009 - 2014 Dan McMahill dan_AT_mcmahill_DOT_net
4 
5   This driver used drvSAMPL.cpp as a reference.
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 #include "drvcairo.h"
23 #include I_fstream
24 #include I_stdio
25 #include I_stdlib
26 #include <iostream>
27 #include <fstream>
28 
29 // #include "version.h"
30 
31 // TODO
32 //
33 // - fix text font selection
34 //   = When using -pango, the text all appears to be offset in the y-direction from where it
35 //     should be.  Also bold and italics don't work with -pango.
36 //
37 //   = Is it possible to get the postscript bounding box for the text?  If so then one idea is I
38 //     create the pango or cairo text, get its size and then use cairo_transform to scale it so the pango or
39 //     cairo bounding box matches the postscript one.  This way even if the fonts are a little different
40 //     we end up with something as close as possible.
41 //
42 //   = What about symbol font?  It looks like the way this has to happen is I need to convert
43 //     to the greek alphabet in unicode stored in UTF-8 and that sounds like a pretty big hack
44 //     when the chances of really getting fonts to be right are slim.  The reality is that
45 //     fonts probably won't look right unless you use -dt as a pstoedit option
46 //
47 // - fix bounding box glitches.
48 //   = examples/colrtest.ps
49 //
50 // - image and imagemask support
51 //   Can I implement this directly?
52 //    = examples/imagetest_1.ps
53 //    = examples/imagetest_2.ps
54 //    = examples/imagetest_3.ps
55 //    = examples/oneimg.ps
56 //    = examples/woglim.ps
57 //   If I can figure out how to get at the pixel data, I can generate cairo code that will render it.
58 //   Cairo can also load png from a file however I'd prefer to not to have to do that since it means
59 //   the end users program now needs to know how to locate the .png files at runtime and it is not
60 //   a given that the users code knows how to reliably do this so I'd rather embed the picture.
61 //
62 // - Is there a better way of getting the name for the header file that is created?
63 //
64 
derivedConstructor(drvCAIRO)65 drvCAIRO::derivedConstructor(drvCAIRO):
66   //(const char * driveroptions_p,ostream & theoutStream,ostream & theerrStream): // Constructor
67   constructBase, imgcount(0)
68 {
69   ofstream outh;
70 
71   // driver specific initializations
72   // and writing of header to output file
73   outf << "/* ***** Generated from pstoedit ***** */" << endl;
74   outf << "#include <cairo.h>" << endl;
75   if (options->pango.value) {
76   outf << "#include <pango/pangocairo.h>" << endl;
77   }
78   outf << "#include <stdio.h>" << endl;
79   outf << endl;
80 
81   maxw = 0;
82   maxh = 0;
83   evenoddmode = false;
84 
85 
86   // Generate the header file
87   outh.open(options->header.value.c_str(), ios::out);
88 
89   outh << "/* " << options->header.value << " */" << endl;
90   outh << "/* ***** Generated from pstoedit ***** */" << endl;
91   outh << "#ifndef __" << options->funcname.value << "_H__" << endl;
92   outh << "#define __" << options->funcname.value << "_H__" << endl;
93   outh << "#include <cairo.h>" << endl;
94   outh << "extern cairo_t * (*" << options->funcname.value << "_render[])(cairo_surface_t *, cairo_t *);" << endl;
95   outh << "extern int " << options->funcname.value << "_total_pages;" << endl;
96   outh << "extern int " << options->funcname.value << "_width[];" << endl;
97   outh << "extern int " << options->funcname.value << "_height[];" << endl;
98   outh << "extern void " << options->funcname.value << "_init(void);" << endl;
99   outh << "#endif /* __" << options->funcname.value << "_H__ */" << endl;
100   outh << endl;
101   outh.close();
102 
103 }
104 
~drvCAIRO()105 drvCAIRO::~drvCAIRO()
106 {
107   unsigned int i;
108 
109   // driver specific deallocations
110   // and writing of trailer to output file
111   outf << endl;
112   outf << "/* Total number of pages */" << endl;
113   outf << "int " << options->funcname.value << "_" << "total_pages;" << endl;
114   outf << endl;
115   outf << "/* Array of the individual page render functions */" << endl;
116   outf << "cairo_t * (*" << options->funcname.value << "_render[" << totalNumberOfPages() << "])(cairo_surface_t *, cairo_t *);" << endl;
117   outf << endl;
118   outf << "/* array of pointers to the widths and heights */" << endl;
119   outf << "int " << options->funcname.value << "_width[" << totalNumberOfPages() << "];" << endl;
120   outf << "int " << options->funcname.value << "_height[" << totalNumberOfPages() << "];" << endl;
121   outf << endl;
122 
123   outf << "/* This function should be called at the beginning of the user program */" << endl;
124   outf << "void " << options->funcname.value << "_init(void)" << endl;
125   outf << "{" << endl;
126   outf << endl;
127   outf << "  " << options->funcname.value << "_" << "total_pages = " << totalNumberOfPages() << ";" << endl;
128   outf << endl;
129 
130   // Now spit out an array of pointers to the render functions, so we can deal with multiple pages
131   for (i = 1 ; i <= drvbase::totalNumberOfPages() ; i++) {
132     outf << "  " << options->funcname.value << "_render[" << i-1 << "] = ";
133     outf  << options->funcname.value << "_page_" << i << "_render;" << endl;
134   }
135   outf << endl;
136 
137   for (i = 1 ; i <= drvbase::totalNumberOfPages() ; i++) {
138     outf << "  " << options->funcname.value << "_width[" << i-1 << "] = ";
139     outf << options->funcname.value << "_page_" << i << "_width;" << endl;
140 
141   }
142 
143   for (i = 1 ; i <= drvbase::totalNumberOfPages() ; i++) {
144     outf << "  " << options->funcname.value << "_height[" << i-1 << "] = ";
145     outf << options->funcname.value << "_page_" << i << "_height;" << endl;
146 
147   }
148   outf << "}" << endl;
149   outf << endl;
150 
151   outf << "float " << options->funcname.value << "_width_max = " << maxw << ";" << endl;
152   outf << "float " << options->funcname.value << "_height_max = " << maxh << ";" << endl;
153 
154 }
155 
print_coords()156 void drvCAIRO::print_coords()
157 {
158 
159   for (unsigned int n = 0; n < numberOfElementsInPath(); n++) {
160     const basedrawingelement & elem = pathElement(n);
161     switch (elem.getType()) {
162     case moveto:{
163       const Point & p = elem.getPoint(0);
164       outf << "  cairo_move_to (cr, ";
165       outf << p.x_ + x_offset << ", " << /*   currentDeviceHeight -  */ -1*p.y_ + y_offset << ");";
166     }
167       break;
168     case lineto:{
169       const Point & p = elem.getPoint(0);
170       outf << "  cairo_line_to (cr, ";
171       outf << p.x_ + x_offset << ", " << /*   currentDeviceHeight -  */ -1*p.y_ + y_offset << ");";
172     }
173       break;
174     case closepath:
175       outf << "  cairo_close_path (cr);";
176       break;
177     case curveto:{
178       outf << "  cairo_curve_to (cr";
179       for (unsigned int cp = 0; cp < 3; cp++) {
180 	const Point & p = elem.getPoint(cp);
181 	outf <<
182 	  ", " << (p.x_ + x_offset) <<
183 	  ", " << /*   currentDeviceHeight -  */ (-1*p.y_ + y_offset);
184       }
185       outf << ");" << endl;
186     }
187       break;
188     default:
189       errf << "\t\tFatal: unexpected case in drvcairo " << endl;
190       abort();
191       break;
192     }
193     outf << endl;
194   }
195 }
196 
197 
open_page()198 void drvCAIRO::open_page()
199 {
200   BBox mybox;
201 
202 
203   mybox = getCurrentBBox();
204 
205   x_offset = -mybox.ll.x_;
206   y_offset = mybox.ur.y_;
207   //cout << "Set offset to (" << x_offset << ", " << y_offset << ")" << endl;
208 
209   outf << "/*" << endl;
210   outf << " * Original bounding box = for page # " << currentPageNumber << " is" << endl;
211   outf << " * " << mybox << endl;
212   outf << " * The figure has been offset by (" << x_offset << ", " << y_offset << ")" << endl;
213   outf << " * to move LL to (0,0).  The width and height" << endl;
214   outf << " * can be read from the following two variables:" << endl;
215   outf << " */" << endl;
216 
217   outf << "static int " << options->funcname.value <<"_page_" << currentPageNumber << "_width = " <<
218     mybox.ur.x_ - mybox.ll.x_ << ";" << endl;
219   outf << "static int " << options->funcname.value << "_page_" << currentPageNumber << "_height = " <<
220        mybox.ur.y_ - mybox.ll.y_ << ";" << endl;
221   outf << endl;
222 
223   if (mybox.ur.x_ - mybox.ll.x_ > maxw) {
224     maxw = mybox.ur.x_ - mybox.ll.x_;
225   }
226 
227   if (mybox.ur.y_ - mybox.ll.y_ > maxh) {
228     maxh = mybox.ur.y_ - mybox.ll.y_;
229   }
230 
231   outf << "static cairo_t * " << options->funcname.value << "_page_" << currentPageNumber << "_render";
232   outf << "(cairo_surface_t *cs, cairo_t *cr)" << endl;
233   outf << "{" << endl;
234 
235   outf << endl;
236   outf << "  if (cr == NULL && cs == NULL) {" << endl;
237   outf << "    return NULL;" << endl;
238   outf << "  } else if(cr == NULL && cs != NULL) {" << endl;
239   outf << "    cr = cairo_create (cs);" << endl;
240   outf << "  } else if(cr != NULL && cs == NULL) {" << endl;
241   outf << "  } else if(cr != NULL && cs != NULL) {" << endl;
242   outf << "  }" << endl;
243   outf << endl;
244 
245   outf << "  cairo_save (cr);" << endl;
246   outf << endl;
247   if (!options->pango.value) {
248 
249     outf << "  /* set an initial font */" << endl;
250     outf << "  cairo_select_font_face (cr, \"monospace\"," <<
251       " CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);" << endl;
252   }
253   outf << endl;
254 
255 }
256 
close_page()257 void drvCAIRO::close_page()
258 {
259   outf << "  cairo_restore (cr);" << endl;
260   outf << endl;
261   outf << "  return cr;" << endl;
262   outf << "} /* end of " << options->funcname.value << "_page_" << (currentPageNumber) << "_render() */" ;
263   outf << endl;
264 }
265 
show_text(const TextInfo & textinfo)266 void drvCAIRO::show_text(const TextInfo & textinfo)
267 {
268   outf << "  /*" << endl;
269   outf << "   * " << "X " << textinfo.x << " Y " << textinfo.y << endl;
270   outf << "   * " << "X_END " << textinfo.x_end << " Y_END " << textinfo.y_end << endl;
271   outf << "   * " << "currentFontName: " << textinfo.currentFontName.c_str() << endl;
272   outf << "   * " << "is_non_standard_font: " << textinfo.is_non_standard_font << endl;
273   outf << "   * " << "currentFontFamilyName: " << textinfo.currentFontFamilyName.c_str() << endl;
274   outf << "   * " << "currentFontFullName: " << textinfo.currentFontFullName.c_str() << endl;
275   outf << "   * " << "currentFontWeight: " << textinfo.currentFontWeight.c_str() << endl;
276   outf << "   * " << "currentFontAngle: " << textinfo.currentFontAngle << endl;
277 
278   const float *CTM = getCurrentFontMatrix();
279   const char *family;
280 
281   outf << "   * " << "currentFontMatrix: [";
282   for (unsigned int i = 0; i < 6; i++) {
283     outf << " " << CTM[i];
284   }
285   outf << ']' << endl;
286   outf << "   */" << endl;
287 
288   outf << "  {" << endl;
289   outf << "    cairo_matrix_t matrix, save_matrix;" << endl;
290   if (options->pango.value) {
291     outf << "    PangoFontDescription *desc;" << endl;
292     outf << "    PangoLayout *layout;" << endl;
293   }
294   outf << "    const char *text = \"" << textinfo.thetext.c_str() << "\";" << endl;
295   outf << endl;
296 
297   outf << "    cairo_set_source_rgb (cr, " << textinfo.currentR << "," <<
298     textinfo.currentG << "," << textinfo.currentB << ");" << endl;
299 
300   outf << "    cairo_get_matrix (cr, &save_matrix);" << endl;
301 
302   // cairo_matrix_init (xx, yx, xy, yy, x0, y0)
303   //    x_new = xx * x + xy * y + x0;
304   //    y_new = yx * x + yy * y + y0;
305   outf << "    cairo_save (cr);" << endl;
306   outf << "    cairo_matrix_init (&matrix,"
307        << CTM[0]/textinfo.currentFontSize << ", "
308        << -1.0*CTM[1]/textinfo.currentFontSize << ", "
309        << -1.0*CTM[2]/textinfo.currentFontSize << ", "
310        <<  1.0*CTM[3]/textinfo.currentFontSize << ", "
311        << CTM[4] + x_offset << ", "
312        << -1.0*CTM[5] + y_offset << ");" << endl;
313 
314   outf << "    cairo_transform (cr, &matrix);" << endl;
315   outf << "    cairo_move_to (cr, 0, 0);" << endl;
316   outf << endl;
317 
318   family = "monospace";
319   if (strstr(textinfo.currentFontName.c_str(), "Times") ||
320       strstr(textinfo.currentFontName.c_str(), "Roman")) {
321     family = "serif";
322   } else if (strstr(textinfo.currentFontName.c_str(), "Helvetica") ||
323 	     strstr(textinfo.currentFontName.c_str(), "Sans")) {
324     family = "sans-serif";
325   } else if (strstr(textinfo.currentFontName.c_str(), "Courier") ||
326 	     strstr(textinfo.currentFontName.c_str(), "Mono")) {
327     family = "monospace";
328   } else if (strstr(textinfo.currentFontName.c_str(), "Symbol") ) {
329     // In this case, what unfortunately needs to happen is I need to convert the
330     // ASCII string to UTF-8 encoded string but with mapping from the
331     family = "symbol";
332   } else {
333     errf << "currentFontName: " << textinfo.currentFontName.c_str() << " is not known." << endl;
334     errf << "                 Defaulting to " << family << endl;
335   }
336 
337   if (options->pango.value) {
338     outf << "    /* Set pango font */" << endl;
339     outf << "    layout = pango_cairo_create_layout (cr);" << endl;
340     outf << "    desc = pango_font_description_from_string (\"" <<
341       family << "\");" << endl;
342 
343     outf << "    /* A size value of 10 * PANGO_SCALE is a 10 point font. */" << endl;
344     outf << "    pango_font_description_set_size (desc,  " <<
345       textinfo.currentFontSize << " * PANGO_SCALE);" << endl;
346     outf << "    pango_layout_set_font_description (layout, desc);" << endl;
347     outf << "    pango_font_description_free (desc);" << endl;
348     outf << "    pango_layout_set_text (layout, text, -1);" << endl;
349     outf << "    pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);" << endl;
350 
351     outf << "    pango_cairo_show_layout (cr, layout);" << endl;
352     outf << "    g_object_unref (layout);" << endl;
353 
354   } else {
355     const char *weight, *slant;
356     // cairo_select_font_face (cairo_t *cr,
357     //                         const char *family,
358     //                         cairo_font_slant_t slant,
359     //                         cairo_font_weight_t weight);
360     //
361     // family:
362     //  standard CSS2 generic family names, ("serif", "sans-serif",
363     //  "cursive", "fantasy", "monospace"), are likely to work as expected.
364     //
365 
366     // FIXME -- figure out how to get the family set is a better way
367 
368     // slant:
369     //   CAIRO_FONT_SLANT_NORMAL  - Upright font style
370     //   CAIRO_FONT_SLANT_ITALIC  - Italic font style
371     //   CAIRO_FONT_SLANT_OBLIQUE - Oblique font style
372     //
373 
374     // FIXME -- set the slant more robustly
375     slant = "CAIRO_FONT_SLANT_NORMAL";
376     if (strstr(textinfo.currentFontFullName.c_str(), "Italic")) {
377       slant = "CAIRO_FONT_SLANT_ITALIC";
378     } else  if (strstr(textinfo.currentFontFullName.c_str(), "Oblique")) {
379       slant = "CAIRO_FONT_SLANT_OBLIQUE";
380     }
381 
382     // weight:
383     //   CAIRO_FONT_WEIGHT_NORMAL - Normal font weight
384     //   CAIRO_FONT_WEIGHT_BOLD   - Bold font weight
385     //
386 
387     // FIXME -- set the weight more robustly
388     weight = "CAIRO_FONT_WEIGHT_NORMAL";
389     if (strstr(textinfo.currentFontWeight.c_str(), "bold") ||
390 	strstr(textinfo.currentFontWeight.c_str(), "Bold") ) {
391       weight = "CAIRO_FONT_WEIGHT_BOLD";
392     }
393 
394     outf << "    cairo_select_font_face (cr, \"" << family << "\"," << endl;
395     outf << "                            " << slant << "," << endl;
396     outf << "                            " << weight << ");" << endl;
397 
398     // outf << "    status = cairo_status (cr);" << endl;
399     //outf << "    printf(\"cairo_select_font_face() returned \\\"%s\\\"\\n\", cairo_status_to_string (status));" << endl;
400 
401     outf << "    cairo_set_font_size (cr, " << textinfo.currentFontSize << ");" << endl;
402     outf << "    cairo_show_text (cr, text);" << endl;
403   }
404 
405   outf << "    cairo_set_matrix (cr, &save_matrix);" << endl;
406   outf << "    cairo_restore (cr);" << endl;
407   outf << "    cairo_move_to (cr, " << textinfo.x_end + x_offset
408        << ", " << -1*textinfo.y_end + y_offset << ");" << endl;
409   outf << "  }" << endl;
410   outf << endl;
411 
412 }
413 
ClipPath(cliptype type)414 void drvCAIRO::ClipPath(cliptype type)
415 {
416   // type is 'clip' or 'eoclip'
417   evenoddmode = (type == drvbase::eoclip);
418 
419   outf << "  cairo_save (cr);" << endl;
420   outf << "  cairo_reset_clip (cr);" << endl;
421 
422   if (evenoddmode) {
423     outf << "  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);" << endl;
424   } else {
425     outf << "  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);" << endl;
426   }
427 
428   print_coords();
429   outf << "  cairo_clip (cr);" << endl;
430   outf << "  cairo_restore (cr);" << endl;
431 }
432 
show_path()433 void drvCAIRO::show_path()
434 {
435   DashPattern dp(dashPattern());
436 
437   outf << endl;
438   outf << "  /*" << endl;
439   outf << "   * Path # " << currentNr() ;
440   if (isPolygon())
441     outf << " (polygon):" << endl;
442   else
443     outf << " (polyline):" << endl;
444   outf << "   */" << endl;
445   outf << endl;
446 
447   outf << "  cairo_save (cr);" << endl;
448   outf << "  cairo_set_line_width (cr, " << currentLineWidth() << ");" << endl;
449 
450   // CAIRO_LINE_CAP_BUTT   - start(stop) the line exactly at the start(end) point
451   // CAIRO_LINE_CAP_ROUND  - use a round ending, the center of the circle is the end point
452   // CAIRO_LINE_CAP_SQUARE - use squared ending, the center of the square is the end point
453   outf << "  cairo_set_line_cap (cr, ";
454   switch( currentLineCap() ) {
455   case 0:
456     outf << "CAIRO_LINE_CAP_BUTT);" << endl;
457     break;
458 
459   case 1:
460     outf << "CAIRO_LINE_CAP_ROUND);" << endl;
461     break;
462 
463   case 2:
464     outf << "CAIRO_LINE_CAP_SQUARE);" << endl;
465     break;
466 
467   default:
468     errf << "Unexpected currentLineCap() in cairo driver:  " << currentLineCap() << endl;
469     outf << "CAIRO_LINE_CAP_ROUND);" << endl;
470     break;
471   }
472   // cairo_set_dash (cairo_t *cr, const double *dashes, int num_dashes, double offset);
473   // dashes :
474   //     an array specifying alternate lengths of on and off stroke portions
475   //
476   // num_dashes :
477   //     the length of the dashes array
478   //
479   // offset :
480   //     an offset into the dash pattern at which the stroke should start
481   //
482   // dashPattern:  has nrOfEntries, float *numbers, float offset
483 
484   if (dp.nrOfEntries > 0) {
485     outf << "  {" << endl;
486     outf << "    double pat[" << dp.nrOfEntries << "] = {" << endl;
487     for (int i = 0; i < dp.nrOfEntries; i++) {
488       outf << "                      " << dp.numbers[i] << ", " << endl;
489     }
490     outf << "                   };" << endl;
491     outf << endl;
492     outf << "    cairo_set_dash (cr, pat, " << dp.nrOfEntries << ", " << dp.offset << ");" << endl;
493     outf << "   }" << endl;
494   } else {
495     outf << "  cairo_set_dash (cr, NULL, 0, 0.0);" << endl;
496   }
497 
498   // cairo_move_to (cr, 0.25, 0.25);
499   // cairo_line_to (cr, 0.5, 0.375);
500   outf << "  /* Path Elements 0 to " << numberOfElementsInPath() - 1 << " */" << endl;
501   print_coords();
502 
503 
504 
505   switch (currentShowType()) {
506   case drvbase::stroke:
507     outf << "  cairo_set_source_rgb (cr, " << edgeR() << "," << edgeG() << "," << edgeB() << ");" << endl;
508     outf << "  cairo_stroke (cr);" << endl;
509     break;
510 
511   case drvbase::eofill:
512     outf << "  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);" << endl;
513     evenoddmode = true;
514     /* no break */
515 
516   case drvbase::fill:
517 
518     outf << "  cairo_set_source_rgb (cr, " << fillR() << "," << fillG() << "," << fillB() << ");" << endl;
519     outf << "  cairo_fill_preserve (cr);" << endl;
520     if (evenoddmode) {
521       outf << "  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);" << endl;
522       evenoddmode = false;
523     }
524     outf << "  cairo_set_source_rgb (cr, " << edgeR() << "," << edgeG() << "," << edgeB() << ");" << endl;
525     outf << "  cairo_stroke (cr);" << endl;
526     break;
527 
528   default:
529     // cannot happen
530     outf << "  // unexpected ShowType " << (int) currentShowType();
531     break;
532   }
533   outf << "  cairo_restore (cr);" << endl;
534 
535 }
536 
show_rectangle(const float llx,const float lly,const float urx,const float ury)537 void drvCAIRO::show_rectangle(const float llx, const float lly, const float urx, const float ury)
538 {
539   // FIXME -- I need to get some rectangle calls into my sample so i can test this...
540   // cairo_rectangle (cr, x, y, width, height);
541   outf << "  cairo_rectangle (cr, " << llx << "," << lly << ", " << urx-llx << "," << ury-lly << ");" << endl;
542   // just do show_path for a first guess
543   show_path();
544 }
545 
show_image(const PSImage & image)546 void drvCAIRO::show_image(const PSImage & image)
547 {
548   // first retrieve bounding box
549   Point lowerLeft, upperRight;
550   image.getBoundingBox(lowerLeft, upperRight);
551 
552   // not only bounding box must account for scale,
553   // but also transformation matrix!
554 
555   // scale bounding box
556   lowerLeft.x_ *= getScale();
557   lowerLeft.y_ *= getScale();
558   upperRight.x_ *= getScale();
559   upperRight.y_ *= getScale();
560 
561   const long width  = abs(i_transX(upperRight.x_) - i_transX(lowerLeft.x_));
562   const long height = abs(i_transY(upperRight.y_) - i_transY(lowerLeft.y_));
563 
564   if (Verbose()) {
565     errf << "image.Width:" << image.width << " image.Height: " << image.height << endl;
566     errf << "Width:" << width << " Height: " << height << endl;
567   }
568 
569 #if 0
570 
571   // This is an example of how to take image data and get it into a cairo surface.
572   // If I ever figure out the pstoedit end, I can try one of these.
573   int stride;
574   unsigned char *data;
575   cairo_surface_t *surface;
576   g_double w, h;
577     /*
578     CAIRO_FORMAT_ARGB32,
579     CAIRO_FORMAT_RGB24,
580     CAIRO_FORMAT_A8,
581     CAIRO_FORMAT_A1
582   */
583 
584   stride = cairo_format_stride_for_width (format, width);
585   data = malloc (stride * height);
586   surface = cairo_image_surface_create_for_data (data, format,
587 						 width, height,
588 						 stride);
589   surface = cairo_image_surface_create_from_png_stream (data, format,
590 						 width, height,
591 						 stride);
592 #endif
593 
594 
595   // calc long-padded size of scanline
596   const long scanlineLen = ((width * 3) + 3) & ~3L;
597 
598   // now lets get some mem
599   unsigned char *const output = new unsigned char[scanlineLen * height];
600 
601   for (long i = 0; i < scanlineLen * height; i++)
602     output[i] = 255;		// default is background (white)
603 
604   if (!output) {
605     errf << "ERROR: Cannot allocate memory for image" << endl;
606     return;
607   }
608   // setup inverse transformation matrix (scaled, too!)
609   const float matrixScale(image.normalizedImageCurrentMatrix[0] *
610 			  image.normalizedImageCurrentMatrix[3] -
611 			  image.normalizedImageCurrentMatrix[2] *
612 			  image.normalizedImageCurrentMatrix[1]);
613   const float inverseMatrix[] = {
614     image.normalizedImageCurrentMatrix[3] / matrixScale / getScale(),
615     -image.normalizedImageCurrentMatrix[1] / matrixScale / getScale(),
616     -image.normalizedImageCurrentMatrix[2] / matrixScale / getScale(),
617     image.normalizedImageCurrentMatrix[0] / matrixScale / getScale(),
618     (image.normalizedImageCurrentMatrix[2] *
619      image.normalizedImageCurrentMatrix[5] -
620      image.normalizedImageCurrentMatrix[4] *
621      image.normalizedImageCurrentMatrix[3]) / matrixScale,
622     (image.normalizedImageCurrentMatrix[4] *
623      image.normalizedImageCurrentMatrix[1] -
624      image.normalizedImageCurrentMatrix[0] *
625      image.normalizedImageCurrentMatrix[5]) / matrixScale
626   };
627 
628   // now transform image
629   for (long ypos = 0; ypos < height; ypos++) {
630     // buffer current output scanline (saves us some multiplications)
631     unsigned char *const currOutput = &output[scanlineLen * ypos];
632 
633     for (long xpos = 0; xpos < width; xpos++) {
634       // now transform from device coordinate space to image space
635 
636       // apply transformation
637       const Point currPoint = Point(xpos + lowerLeft.x_,
638 				    ypos + lowerLeft.y_).transform(inverseMatrix);
639 
640       // round to integers
641       const long sourceX = (long) (currPoint.x_ + .5);
642       const long sourceY = (long) (currPoint.y_ + .5);
643 
644       // is the pixel out of bounds? If yes, no further processing necessary
645       if (sourceX >= 0L && (unsigned long) sourceX < image.width &&
646 	  sourceY >= 0L && (unsigned long) sourceY < image.height) {
647 	// okay, fetch source pixel value into
648 	// RGB triplet
649 
650 	unsigned char r(255), g(255), b(255), c, m, y, k;
651 
652 	// how many components?
653 	switch (image.ncomp) {
654 	case 1:
655 	  r = g = b = image.getComponent(sourceX, sourceY, 0);
656 	  break;
657 
658 	case 3:
659 	  r = image.getComponent(sourceX, sourceY, 0);
660 	  g = image.getComponent(sourceX, sourceY, 1);
661 	  b = image.getComponent(sourceX, sourceY, 2);
662 	  break;
663 
664 	case 4:
665 	  c = image.getComponent(sourceX, sourceY, 0);
666 	  m = image.getComponent(sourceX, sourceY, 1);
667 	  y = image.getComponent(sourceX, sourceY, 2);
668 	  k = image.getComponent(sourceX, sourceY, 3);
669 
670 	  // account for key
671 	  c += k;
672 	  m += k;
673 	  y += k;
674 
675 	  // convert color
676 	  r = 255 - c;
677 	  g = 255 - m;
678 	  b = 255 - y;
679 	  break;
680 
681 	default:
682 	  errf << "\t\tFatal: unexpected case in drvcairo (line "
683 	       << __LINE__ << ")" << endl;
684 	  abort();
685 	  return;
686 	}
687 
688 	// set color triple
689 	currOutput[3 * xpos] = b;
690 	currOutput[3 * xpos + 1] = g;
691 	currOutput[3 * xpos + 2] = r;
692       }
693     }
694   }
695 
696   delete[]output;
697 }
698 
699 static DriverDescriptionT < drvCAIRO > D_cairo("cairo",  // name
700 					       "cairo driver", // short description
701 					       "generates compilable c code for rendering with cairo", // long description
702 					       "c", // output file suffix
703 					       true,	// backend supports subpathes
704 					       // if subpathes are supported, the backend must deal with
705 					       // sequences of the following form
706 					       // moveto (start of subpath)
707 					       // lineto (a line segment)
708 					       // lineto
709 					       // moveto (start of a new subpath)
710 					       // lineto (a line segment)
711 					       // lineto
712 					       //
713 					       // If this argument is set to false each subpath is drawn
714 					       // individually which might not necessarily represent
715 					       // the original drawing.
716 					       true,	// backend supports curves
717 					       true,	// backend supports elements which are filled and have edges
718 					       true,	// backend supports text
719 					       DriverDescription::memoryeps,	// format to be used for raster imagese
720 					       DriverDescription::normalopen, // binary or ascii output file
721 					       true,	// if format supports multiple pages in one file
722 					       true  /*clipping */
723 					       );
724