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