1 /** \file
2 * Rendering with Cairo.
3 */
4 /*
5 * Author:
6 * Miklos Erdelyi <erdelyim@gmail.com>
7 * Jon A. Cruz <jon@joncruz.org>
8 * Abhishek Sharma
9 *
10 * Copyright (C) 2006 Miklos Erdelyi
11 *
12 * Licensed under GNU GPL
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #ifndef PANGO_ENABLE_BACKEND
20 #define PANGO_ENABLE_BACKEND
21 #endif
23 #ifndef PANGO_ENABLE_ENGINE
24 #define PANGO_ENABLE_ENGINE
25 #endif
28 #include <signal.h>
29 #include <errno.h>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 //#define TRACE(_args) g_message _args
84 #define TRACE(_args)
85 //#define TEST(_args) _args
86 #define TEST(_args)
88 // FIXME: expose these from sp-clippath/mask.cpp
89 struct SPClipPathView {
90 SPClipPathView *next;
91 unsigned int key;
92 NRArenaItem *arenaitem;
93 NRRect bbox;
94 };
96 struct SPMaskView {
97 SPMaskView *next;
98 unsigned int key;
99 NRArenaItem *arenaitem;
100 NRRect bbox;
101 };
103 namespace Inkscape {
104 namespace Extension {
105 namespace Internal {
107 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
109 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
110 _dpi(72),
111 _pdf_level(1),
112 _ps_level(1),
113 _eps(false),
114 _is_texttopath(FALSE),
115 _is_filtertobitmap(FALSE),
116 _bitmapresolution(72),
117 _stream(NULL),
118 _is_valid(FALSE),
119 _vector_based_target(FALSE),
120 _cr(NULL), // Cairo context
121 _surface(NULL),
122 _target(CAIRO_SURFACE_TYPE_IMAGE),
123 _target_format(CAIRO_FORMAT_ARGB32),
124 _layout(NULL),
125 _state(NULL),
126 _renderer(parent),
127 _render_mode(RENDER_MODE_NORMAL),
128 _clip_mode(CLIP_MODE_MASK)
129 {
130 font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
131 }
133 CairoRenderContext::~CairoRenderContext(void)
134 {
135 if(font_table != NULL) {
136 g_hash_table_remove_all(font_table);
137 }
139 if (_cr) cairo_destroy(_cr);
140 if (_surface) cairo_surface_destroy(_surface);
141 if (_layout) g_object_unref(_layout);
142 }
143 void CairoRenderContext::font_data_free(gpointer data)
144 {
145 cairo_font_face_t *font_face = (cairo_font_face_t *)data;
146 if (font_face) {
147 cairo_font_face_destroy(font_face);
148 }
149 }
151 CairoRenderer*
152 CairoRenderContext::getRenderer(void) const
153 {
154 return _renderer;
155 }
157 CairoRenderState*
158 CairoRenderContext::getCurrentState(void) const
159 {
160 return _state;
161 }
163 CairoRenderState*
164 CairoRenderContext::getParentState(void) const
165 {
166 // if this is the root node just return it
167 if (g_slist_length(_state_stack) == 1) {
168 return _state;
169 } else {
170 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
171 }
172 }
174 void
175 CairoRenderContext::setStateForStyle(SPStyle const *style)
176 {
177 // only opacity & overflow is stored for now
178 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
179 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
180 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
182 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
183 _state->merge_opacity = FALSE;
185 // disable rendering of opacity if there's a stroke on the fill
186 if (_state->merge_opacity
187 && !style->fill.isNone()
188 && !style->stroke.isNone())
189 _state->merge_opacity = FALSE;
190 }
192 /**
193 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
194 *
195 * \param width width of the surface to be created
196 * \param height height of the surface to be created
197 */
198 CairoRenderContext*
199 CairoRenderContext::cloneMe(double width, double height) const
200 {
201 g_assert( _is_valid );
202 g_assert( width > 0.0 && height > 0.0 );
204 CairoRenderContext *new_context = _renderer->createContext();
205 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
206 (int)ceil(width), (int)ceil(height));
207 new_context->_cr = cairo_create(surface);
208 new_context->_surface = surface;
209 new_context->_width = width;
210 new_context->_height = height;
211 new_context->_is_valid = TRUE;
213 return new_context;
214 }
216 CairoRenderContext*
217 CairoRenderContext::cloneMe(void) const
218 {
219 g_assert( _is_valid );
221 return cloneMe(_width, _height);
222 }
224 bool
225 CairoRenderContext::setImageTarget(cairo_format_t format)
226 {
227 // format cannot be set on an already initialized surface
228 if (_is_valid)
229 return false;
231 switch (format) {
232 case CAIRO_FORMAT_ARGB32:
233 case CAIRO_FORMAT_RGB24:
234 case CAIRO_FORMAT_A8:
235 case CAIRO_FORMAT_A1:
236 _target_format = format;
237 _target = CAIRO_SURFACE_TYPE_IMAGE;
238 return true;
239 break;
240 default:
241 break;
242 }
244 return false;
245 }
247 bool
248 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
249 {
250 #ifndef CAIRO_HAS_PDF_SURFACE
251 return false;
252 #else
253 _target = CAIRO_SURFACE_TYPE_PDF;
254 _vector_based_target = TRUE;
255 #endif
257 FILE *osf = NULL;
258 FILE *osp = NULL;
260 gsize bytesRead = 0;
261 gsize bytesWritten = 0;
262 GError *error = NULL;
263 gchar *local_fn = g_filename_from_utf8(utf8_fn,
264 -1, &bytesRead, &bytesWritten, &error);
265 gchar const *fn = local_fn;
267 /* TODO: Replace the below fprintf's with something that does the right thing whether in
268 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
269 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
270 * return code.
271 */
272 if (fn != NULL) {
273 if (*fn == '|') {
274 fn += 1;
275 while (isspace(*fn)) fn += 1;
276 #ifndef WIN32
277 osp = popen(fn, "w");
278 #else
279 osp = _popen(fn, "w");
280 #endif
281 if (!osp) {
282 fprintf(stderr, "inkscape: popen(%s): %s\n",
283 fn, strerror(errno));
284 return false;
285 }
286 _stream = osp;
287 } else if (*fn == '>') {
288 fn += 1;
289 while (isspace(*fn)) fn += 1;
290 Inkscape::IO::dump_fopen_call(fn, "K");
291 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
292 if (!osf) {
293 fprintf(stderr, "inkscape: fopen(%s): %s\n",
294 fn, strerror(errno));
295 return false;
296 }
297 _stream = osf;
298 } else {
299 /* put cwd stuff in here */
300 gchar *qn = ( *fn
301 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
302 : g_strdup("lpr") );
303 #ifndef WIN32
304 osp = popen(qn, "w");
305 #else
306 osp = _popen(qn, "w");
307 #endif
308 if (!osp) {
309 fprintf(stderr, "inkscape: popen(%s): %s\n",
310 qn, strerror(errno));
311 return false;
312 }
313 g_free(qn);
314 _stream = osp;
315 }
316 }
318 g_free(local_fn);
320 if (_stream) {
321 /* fixme: this is kinda icky */
322 #if !defined(_WIN32) && !defined(__WIN32__)
323 (void) signal(SIGPIPE, SIG_IGN);
324 #endif
325 }
327 return true;
328 }
330 bool
331 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
332 {
333 #ifndef CAIRO_HAS_PS_SURFACE
334 return false;
335 #else
336 _target = CAIRO_SURFACE_TYPE_PS;
337 _vector_based_target = TRUE;
338 #endif
340 FILE *osf = NULL;
341 FILE *osp = NULL;
343 gsize bytesRead = 0;
344 gsize bytesWritten = 0;
345 GError *error = NULL;
346 gchar *local_fn = g_filename_from_utf8(utf8_fn,
347 -1, &bytesRead, &bytesWritten, &error);
348 gchar const *fn = local_fn;
350 /* TODO: Replace the below fprintf's with something that does the right thing whether in
351 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
352 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
353 * return code.
354 */
355 if (fn != NULL) {
356 if (*fn == '|') {
357 fn += 1;
358 while (isspace(*fn)) fn += 1;
359 #ifndef WIN32
360 osp = popen(fn, "w");
361 #else
362 osp = _popen(fn, "w");
363 #endif
364 if (!osp) {
365 fprintf(stderr, "inkscape: popen(%s): %s\n",
366 fn, strerror(errno));
367 return false;
368 }
369 _stream = osp;
370 } else if (*fn == '>') {
371 fn += 1;
372 while (isspace(*fn)) fn += 1;
373 Inkscape::IO::dump_fopen_call(fn, "K");
374 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
375 if (!osf) {
376 fprintf(stderr, "inkscape: fopen(%s): %s\n",
377 fn, strerror(errno));
378 return false;
379 }
380 _stream = osf;
381 } else {
382 /* put cwd stuff in here */
383 gchar *qn = ( *fn
384 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
385 : g_strdup("lpr") );
386 #ifndef WIN32
387 osp = popen(qn, "w");
388 #else
389 osp = _popen(qn, "w");
390 #endif
391 if (!osp) {
392 fprintf(stderr, "inkscape: popen(%s): %s\n",
393 qn, strerror(errno));
394 return false;
395 }
396 g_free(qn);
397 _stream = osp;
398 }
399 }
401 g_free(local_fn);
403 if (_stream) {
404 /* fixme: this is kinda icky */
405 #if !defined(_WIN32) && !defined(__WIN32__)
406 (void) signal(SIGPIPE, SIG_IGN);
407 #endif
408 }
410 return true;
411 }
413 void CairoRenderContext::setPSLevel(unsigned int level)
414 {
415 _ps_level = level;
416 }
418 void CairoRenderContext::setEPS(bool eps)
419 {
420 _eps = eps;
421 }
423 unsigned int CairoRenderContext::getPSLevel(void)
424 {
425 return _ps_level;
426 }
428 void CairoRenderContext::setPDFLevel(unsigned int level)
429 {
430 _pdf_level = level;
431 }
433 void CairoRenderContext::setTextToPath(bool texttopath)
434 {
435 _is_texttopath = texttopath;
436 }
438 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
439 {
440 _is_filtertobitmap = filtertobitmap;
441 }
443 bool CairoRenderContext::getFilterToBitmap(void)
444 {
445 return _is_filtertobitmap;
446 }
448 void CairoRenderContext::setBitmapResolution(int resolution)
449 {
450 _bitmapresolution = resolution;
451 }
453 int CairoRenderContext::getBitmapResolution(void)
454 {
455 return _bitmapresolution;
456 }
458 cairo_surface_t*
459 CairoRenderContext::getSurface(void)
460 {
461 g_assert( _is_valid );
463 return _surface;
464 }
466 bool
467 CairoRenderContext::saveAsPng(const char *file_name)
468 {
469 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
470 if (status)
471 return false;
472 else
473 return true;
474 }
476 void
477 CairoRenderContext::setRenderMode(CairoRenderMode mode)
478 {
479 switch (mode) {
480 case RENDER_MODE_NORMAL:
481 case RENDER_MODE_CLIP:
482 _render_mode = mode;
483 break;
484 default:
485 _render_mode = RENDER_MODE_NORMAL;
486 break;
487 }
488 }
490 CairoRenderContext::CairoRenderMode
491 CairoRenderContext::getRenderMode(void) const
492 {
493 return _render_mode;
494 }
496 void
497 CairoRenderContext::setClipMode(CairoClipMode mode)
498 {
499 switch (mode) {
500 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
501 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
502 _clip_mode = mode;
503 break;
504 default:
505 _clip_mode = CLIP_MODE_PATH;
506 break;
507 }
508 }
510 CairoRenderContext::CairoClipMode
511 CairoRenderContext::getClipMode(void) const
512 {
513 return _clip_mode;
514 }
516 CairoRenderState*
517 CairoRenderContext::_createState(void)
518 {
519 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
520 g_assert( state != NULL );
522 state->has_filtereffect = FALSE;
523 state->merge_opacity = TRUE;
524 state->opacity = 1.0;
525 state->need_layer = FALSE;
526 state->has_overflow = FALSE;
527 state->parent_has_userspace = FALSE;
528 state->clip_path = NULL;
529 state->mask = NULL;
531 return state;
532 }
534 void
535 CairoRenderContext::pushLayer(void)
536 {
537 g_assert( _is_valid );
539 TRACE(("--pushLayer\n"));
540 cairo_push_group(_cr);
542 // clear buffer
543 if (!_vector_based_target) {
544 cairo_save(_cr);
545 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
546 cairo_paint(_cr);
547 cairo_restore(_cr);
548 }
549 }
551 void
552 CairoRenderContext::popLayer(void)
553 {
554 g_assert( _is_valid );
556 float opacity = _state->opacity;
557 TRACE(("--popLayer w/ opacity %f\n", opacity));
559 /*
560 At this point, the Cairo source is ready. A Cairo mask must be created if required.
561 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
562 masks independently of the objects they effect while in SVG the clip paths and masks
563 are defined relative to the objects they are attached to.
564 Notes:
565 1. An SVG object may have both a clip path and a mask!
566 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
567 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
568 the page (document). This is also not handled properly.
569 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
570 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
571 alpha. This is handled here by doing a pixel by pixel conversion.
572 */
574 SPClipPath *clip_path = _state->clip_path;
575 SPMask *mask = _state->mask;
576 if (clip_path || mask) {
578 CairoRenderContext *clip_ctx = 0;
579 cairo_surface_t *clip_mask = 0;
581 // Apply any clip path first
582 if (clip_path) {
583 TRACE((" Applying clip\n"));
584 if (_render_mode == RENDER_MODE_CLIP)
585 mask = NULL; // disable mask when performing nested clipping
587 if (_vector_based_target) {
588 setClipMode(CLIP_MODE_PATH); // Vector
589 if (!mask) {
590 cairo_pop_group_to_source(_cr);
591 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
592 if (opacity == 1.0)
593 cairo_paint(_cr);
594 else
595 cairo_paint_with_alpha(_cr, opacity);
597 } else {
598 // the clipPath will be applied before masking
599 }
600 } else {
602 // setup a new rendering context
603 clip_ctx = _renderer->createContext();
604 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
605 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
606 // This code ties the clipping to the document coordinates. It doesn't allow
607 // for a clipped object intially drawn off the page and then translated onto
608 // the page.
609 if (!clip_ctx->setupSurface(_width, _height)) {
610 TRACE(("clip: setupSurface failed\n"));
611 _renderer->destroyContext(clip_ctx);
612 return;
613 }
615 // clear buffer
616 cairo_save(clip_ctx->_cr);
617 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
618 cairo_paint(clip_ctx->_cr);
619 cairo_restore(clip_ctx->_cr);
621 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
622 if (!mask)
623 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
624 else
625 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
627 // copy over the correct CTM
628 // It must be stored in item_transform of current state after pushState.
629 Geom::Matrix item_transform;
630 if (_state->parent_has_userspace)
631 item_transform = getParentState()->transform * _state->item_transform;
632 else
633 item_transform = _state->item_transform;
635 // apply the clip path
636 clip_ctx->pushState();
637 clip_ctx->getCurrentState()->item_transform = item_transform;
638 _renderer->applyClipPath(clip_ctx, clip_path);
639 clip_ctx->popState();
641 clip_mask = clip_ctx->getSurface();
642 TEST(clip_ctx->saveAsPng("clip_mask.png"));
644 if (!mask) {
645 cairo_pop_group_to_source(_cr);
646 cairo_mask_surface(_cr, clip_mask, 0, 0);
647 _renderer->destroyContext(clip_ctx);
648 }
649 }
650 }
652 // Apply any mask second
653 if (mask) {
654 TRACE((" Applying mask\n"));
655 // create rendering context for mask
656 CairoRenderContext *mask_ctx = _renderer->createContext();
658 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
659 // Cairo surface is expecting the mask to be 90 dpi.
660 float surface_width = _width;
661 float surface_height = _height;
662 if( _vector_based_target ) {
663 surface_width *= 1.25;
664 surface_height *= 1.25;
665 }
666 if (!mask_ctx->setupSurface( surface_width, surface_height )) {
667 TRACE(("mask: setupSurface failed\n"));
668 _renderer->destroyContext(mask_ctx);
669 return;
670 }
671 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
673 // Mask should start black, but it is created white.
674 cairo_set_source_rgba(mask_ctx->_cr, 0.0, 0.0, 0.0, 1.0);
675 cairo_rectangle(mask_ctx->_cr, 0, 0, surface_width, surface_height);
676 cairo_fill(mask_ctx->_cr);
678 // set rendering mode to normal
679 setRenderMode(RENDER_MODE_NORMAL);
681 // copy the correct CTM to mask context
682 /*
683 if (_state->parent_has_userspace)
684 mask_ctx->setTransform(&getParentState()->transform);
685 else
686 mask_ctx->setTransform(&_state->transform);
687 */
688 // This is probably not correct... but it seems to do the trick.
689 mask_ctx->setTransform(&_state->item_transform);
691 // render mask contents to mask_ctx
692 _renderer->applyMask(mask_ctx, mask);
694 TEST(mask_ctx->saveAsPng("mask.png"));
696 // composite with clip mask
697 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
698 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
699 _renderer->destroyContext(clip_ctx);
700 }
702 cairo_surface_t *mask_image = mask_ctx->getSurface();
703 int width = cairo_image_surface_get_width(mask_image);
704 int height = cairo_image_surface_get_height(mask_image);
705 int stride = cairo_image_surface_get_stride(mask_image);
706 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
708 // In SVG, the rgb channels as well as the alpha channel is used in masking.
709 // In Cairo, only the alpha channel is used thus requiring this conversion.
710 // SVG specifies that RGB be converted to alpha using luminance-to-alpha.
711 // Notes: This calculation assumes linear RGB values. VERIFY COLOR SPACE!
712 // The incoming pixel values already include alpha, fill-opacity, etc.,
713 // however, opacity must still be applied.
714 TRACE(("premul w/ %f\n", opacity));
715 const float coeff_r = 0.2125 / 255.0;
716 const float coeff_g = 0.7154 / 255.0;
717 const float coeff_b = 0.0721 / 255.0;
718 for (int row = 0 ; row < height; row++) {
719 unsigned char *row_data = pixels + (row * stride);
720 for (int i = 0 ; i < width; i++) {
721 guint32 *pixel = (guint32 *)row_data + i;
722 float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
723 ((*pixel & 0x0000ff00) >> 8) * coeff_g +
724 ((*pixel & 0x000000ff) ) * coeff_b );
725 // lum_alpha can be slightly greater than 1 due to rounding errors...
726 // but this should be OK since it doesn't matter what the lower
727 // six hexadecimal numbers of *pixel are.
728 *pixel = (guint32)(0xff000000 * lum_alpha * opacity);
729 }
730 }
732 cairo_pop_group_to_source(_cr);
733 if (_clip_mode == CLIP_MODE_PATH) {
734 // we have to do the clipping after cairo_pop_group_to_source
735 _renderer->applyClipPath(this, clip_path);
736 }
737 // apply the mask onto the layer
738 cairo_mask_surface(_cr, mask_image, 0, 0);
739 _renderer->destroyContext(mask_ctx);
740 }
741 } else {
742 // No clip path or mask
743 cairo_pop_group_to_source(_cr);
744 if (opacity == 1.0)
745 cairo_paint(_cr);
746 else
747 cairo_paint_with_alpha(_cr, opacity);
748 }
749 }
751 void
752 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
753 {
754 g_assert( _is_valid );
756 // here it should be checked whether the current clip winding changed
757 // so we could switch back to masked clipping
758 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
759 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
760 } else {
761 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
762 }
763 addPathVector(pv);
764 }
766 void
767 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
768 {
769 g_assert( _is_valid );
771 cairo_rectangle(_cr, x, y, width, height);
772 cairo_clip(_cr);
773 }
775 bool
776 CairoRenderContext::setupSurface(double width, double height)
777 {
778 // Is the surface already set up?
779 if (_is_valid)
780 return true;
782 if (_vector_based_target && _stream == NULL)
783 return false;
785 _width = width;
786 _height = height;
788 cairo_surface_t *surface = NULL;
789 cairo_matrix_t ctm;
790 cairo_matrix_init_identity (&ctm);
791 switch (_target) {
792 case CAIRO_SURFACE_TYPE_IMAGE:
793 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
794 break;
795 #ifdef CAIRO_HAS_PDF_SURFACE
796 case CAIRO_SURFACE_TYPE_PDF:
797 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
798 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
799 cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
800 #endif
801 break;
802 #endif
803 #ifdef CAIRO_HAS_PS_SURFACE
804 case CAIRO_SURFACE_TYPE_PS:
805 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
806 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
807 return FALSE;
808 }
809 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
810 cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
811 cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
812 #endif
813 // Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
814 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
815 if (override_bbox) {
816 cairo_ps_dsc_comment(surface, "%%BoundingBox: 100 100 200 200");
817 cairo_ps_dsc_comment(surface, "%%PageBoundingBox: 100 100 200 200");
818 }
819 #endif
820 break;
821 #endif
822 default:
823 return false;
824 break;
825 }
827 return _finishSurfaceSetup (surface, &ctm);
828 }
830 bool
831 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
832 {
833 if (_is_valid || !surface)
834 return false;
836 _vector_based_target = is_vector;
837 bool ret = _finishSurfaceSetup (surface, ctm);
838 if (ret)
839 cairo_surface_reference (surface);
840 return ret;
841 }
843 bool
844 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
845 {
846 if(surface == NULL) {
847 return false;
848 }
849 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
850 return false;
851 }
853 _cr = cairo_create(surface);
854 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
855 return false;
856 }
857 if (ctm)
858 cairo_set_matrix(_cr, ctm);
859 _surface = surface;
861 if (_vector_based_target) {
862 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
863 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
864 // set background color on non-alpha surfaces
865 // TODO: bgcolor should be derived from SPDocument
866 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
867 cairo_rectangle(_cr, 0, 0, _width, _height);
868 cairo_fill(_cr);
869 }
871 _is_valid = TRUE;
873 return true;
874 }
876 bool
877 CairoRenderContext::finish(void)
878 {
879 g_assert( _is_valid );
881 if (_vector_based_target)
882 cairo_show_page(_cr);
884 cairo_destroy(_cr);
885 cairo_surface_finish(_surface);
886 cairo_status_t status = cairo_surface_status(_surface);
887 cairo_surface_destroy(_surface);
888 _cr = NULL;
889 _surface = NULL;
891 if (_layout)
892 g_object_unref(_layout);
894 _is_valid = FALSE;
896 if (_vector_based_target && _stream) {
897 /* Flush stream to be sure. */
898 (void) fflush(_stream);
900 fclose(_stream);
901 _stream = NULL;
902 }
904 if (status == CAIRO_STATUS_SUCCESS)
905 return true;
906 else
907 return false;
908 }
910 void
911 CairoRenderContext::transform(Geom::Matrix const *transform)
912 {
913 g_assert( _is_valid );
915 cairo_matrix_t matrix;
916 _initCairoMatrix(&matrix, transform);
917 cairo_transform(_cr, &matrix);
919 // store new CTM
920 getTransform(&_state->transform);
921 }
923 void
924 CairoRenderContext::setTransform(Geom::Matrix const *transform)
925 {
926 g_assert( _is_valid );
928 cairo_matrix_t matrix;
929 _initCairoMatrix(&matrix, transform);
930 cairo_set_matrix(_cr, &matrix);
931 _state->transform = *transform;
932 }
934 void
935 CairoRenderContext::getTransform(Geom::Matrix *copy) const
936 {
937 g_assert( _is_valid );
939 cairo_matrix_t ctm;
940 cairo_get_matrix(_cr, &ctm);
941 (*copy)[0] = ctm.xx;
942 (*copy)[1] = ctm.yx;
943 (*copy)[2] = ctm.xy;
944 (*copy)[3] = ctm.yy;
945 (*copy)[4] = ctm.x0;
946 (*copy)[5] = ctm.y0;
947 }
949 void
950 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
951 {
952 g_assert( _is_valid );
954 CairoRenderState *parent_state = getParentState();
955 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
956 }
958 void
959 CairoRenderContext::pushState(void)
960 {
961 g_assert( _is_valid );
963 cairo_save(_cr);
965 CairoRenderState *new_state = _createState();
966 // copy current state's transform
967 new_state->transform = _state->transform;
968 _state_stack = g_slist_prepend(_state_stack, new_state);
969 _state = new_state;
970 }
972 void
973 CairoRenderContext::popState(void)
974 {
975 g_assert( _is_valid );
977 cairo_restore(_cr);
979 g_free(_state_stack->data);
980 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
981 _state = (CairoRenderState*)_state_stack->data;
983 g_assert( g_slist_length(_state_stack) > 0 );
984 }
986 static bool pattern_hasItemChildren(SPPattern *pat)
987 {
988 bool hasItems = false;
989 for ( SPObject *child = pat->firstChild() ; child && !hasItems; child = child->getNext() ) {
990 if (SP_IS_ITEM (child)) {
991 hasItems = true;
992 }
993 }
994 return hasItems;
995 }
997 cairo_pattern_t*
998 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
999 {
1000 g_assert( SP_IS_PATTERN(paintserver) );
1002 SPPattern *pat = SP_PATTERN (paintserver);
1004 Geom::Matrix ps2user, pcs2dev;
1005 ps2user = Geom::identity();
1006 pcs2dev = Geom::identity();
1008 double x = pattern_x(pat);
1009 double y = pattern_y(pat);
1010 double width = pattern_width(pat);
1011 double height = pattern_height(pat);
1012 double bbox_width_scaler;
1013 double bbox_height_scaler;
1015 TRACE(("%f x %f pattern\n", width, height));
1017 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1018 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1019 bbox_width_scaler = pbox->x1 - pbox->x0;
1020 bbox_height_scaler = pbox->y1 - pbox->y0;
1021 ps2user[4] = x * bbox_width_scaler + pbox->x0;
1022 ps2user[5] = y * bbox_height_scaler + pbox->y0;
1023 } else {
1024 bbox_width_scaler = 1.0;
1025 bbox_height_scaler = 1.0;
1026 ps2user[4] = x;
1027 ps2user[5] = y;
1028 }
1030 // apply pattern transformation
1031 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1032 ps2user *= pattern_transform;
1033 Geom::Point ori (ps2user[4], ps2user[5]);
1035 // create pattern contents coordinate system
1036 if (pat->viewBox_set) {
1037 NRRect *view_box = pattern_viewBox(pat);
1039 double x, y, w, h;
1040 double view_width, view_height;
1041 x = 0;
1042 y = 0;
1043 w = width * bbox_width_scaler;
1044 h = height * bbox_height_scaler;
1046 view_width = view_box->x1 - view_box->x0;
1047 view_height = view_box->y1 - view_box->y0;
1049 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1050 pcs2dev[0] = w / view_width;
1051 pcs2dev[3] = h / view_height;
1052 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1053 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1054 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1055 pcs2dev[0] = pbox->x1 - pbox->x0;
1056 pcs2dev[3] = pbox->y1 - pbox->y0;
1058 }
1060 // Calculate the size of the surface which has to be created
1061 #define SUBPIX_SCALE 100
1062 // Cairo requires an integer pattern surface width/height.
1063 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1064 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1065 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1066 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1067 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1068 // create new rendering context
1069 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1071 // adjust the size of the painted pattern to fit exactly the created surface
1072 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1073 double scale_width = surface_width / (bbox_width_scaler * width);
1074 double scale_height = surface_height / (bbox_height_scaler * height);
1075 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1076 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1077 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1078 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1079 }
1081 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1082 ps2user[4] = ori[Geom::X];
1083 ps2user[5] = ori[Geom::Y];
1085 pattern_ctx->setTransform(&pcs2dev);
1086 pattern_ctx->pushState();
1088 // create arena and group
1089 NRArena *arena = NRArena::create();
1090 unsigned dkey = SPItem::display_key_new(1);
1092 // show items and render them
1093 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1094 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1095 for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
1096 if (SP_IS_ITEM (child)) {
1097 SP_ITEM (child)->invoke_show (arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1098 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1099 }
1100 }
1101 break; // do not go further up the chain if children are found
1102 }
1103 }
1105 pattern_ctx->popState();
1107 // setup a cairo_pattern_t
1108 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1109 TEST(pattern_ctx->saveAsPng("pattern.png"));
1110 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1111 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1113 // set pattern transformation
1114 cairo_matrix_t pattern_matrix;
1115 _initCairoMatrix(&pattern_matrix, &ps2user);
1116 cairo_matrix_invert(&pattern_matrix);
1117 cairo_pattern_set_matrix(result, &pattern_matrix);
1119 delete pattern_ctx;
1121 // hide all items
1122 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1123 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1124 for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
1125 if (SP_IS_ITEM (child)) {
1126 SP_ITEM (child)->invoke_hide (dkey);
1127 }
1128 }
1129 break; // do not go further up the chain if children are found
1130 }
1131 }
1133 return result;
1134 }
1136 cairo_pattern_t*
1137 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1138 NRRect const *pbox, float alpha)
1139 {
1140 cairo_pattern_t *pattern = NULL;
1141 bool apply_bbox2user = FALSE;
1143 if (SP_IS_LINEARGRADIENT (paintserver)) {
1145 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1147 SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1149 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1150 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1151 if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1152 // convert to userspace
1153 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1154 p1 *= bbox2user;
1155 p2 *= bbox2user;
1156 }
1158 // create linear gradient pattern
1159 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1161 // add stops
1162 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1163 float rgb[3];
1164 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1165 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1166 }
1167 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1169 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1171 SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1173 Geom::Point c (rg->cx.computed, rg->cy.computed);
1174 Geom::Point f (rg->fx.computed, rg->fy.computed);
1175 double r = rg->r.computed;
1176 if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1177 apply_bbox2user = true;
1179 // create radial gradient pattern
1180 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1182 // add stops
1183 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1184 float rgb[3];
1185 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1186 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1187 }
1188 } else if (SP_IS_PATTERN (paintserver)) {
1190 pattern = _createPatternPainter(paintserver, pbox);
1191 } else {
1192 return NULL;
1193 }
1195 if (pattern && SP_IS_GRADIENT (paintserver)) {
1196 SPGradient *g = SP_GRADIENT(paintserver);
1198 // set extend type
1199 SPGradientSpread spread = g->fetchSpread();
1200 switch (spread) {
1201 case SP_GRADIENT_SPREAD_REPEAT: {
1202 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1203 break;
1204 }
1205 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1206 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1207 break;
1208 }
1209 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1210 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1211 break;
1212 }
1213 default: {
1214 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1215 break;
1216 }
1217 }
1219 cairo_matrix_t pattern_matrix;
1220 if (g->gradientTransform_set) {
1221 // apply gradient transformation
1222 cairo_matrix_init(&pattern_matrix,
1223 g->gradientTransform[0], g->gradientTransform[1],
1224 g->gradientTransform[2], g->gradientTransform[3],
1225 g->gradientTransform[4], g->gradientTransform[5]);
1226 } else {
1227 cairo_matrix_init_identity (&pattern_matrix);
1228 }
1230 if (apply_bbox2user) {
1231 // convert to userspace
1232 cairo_matrix_t bbox2user;
1233 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1234 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1235 }
1236 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1237 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1238 }
1240 return pattern;
1241 }
1243 void
1244 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1245 {
1246 g_return_if_fail( !style->fill.set
1247 || style->fill.isColor()
1248 || style->fill.isPaintserver() );
1250 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1251 if (_state->merge_opacity) {
1252 alpha *= _state->opacity;
1253 TRACE(("merged op=%f\n", alpha));
1254 }
1256 if (style->fill.isColor()) {
1257 float rgb[3];
1258 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1260 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1262 } else if (!style->fill.set) { // unset fill is black
1263 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1265 } else {
1266 g_assert( style->fill.isPaintserver()
1267 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1268 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1270 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1272 if (pattern) {
1273 cairo_set_source(_cr, pattern);
1274 cairo_pattern_destroy(pattern);
1275 }
1276 }
1277 }
1279 void
1280 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1281 {
1282 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1283 if (_state->merge_opacity)
1284 alpha *= _state->opacity;
1286 if (style->stroke.isColor()) {
1287 float rgb[3];
1288 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1290 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1291 } else {
1292 g_assert( style->stroke.isPaintserver()
1293 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1294 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1296 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1298 if (pattern) {
1299 cairo_set_source(_cr, pattern);
1300 cairo_pattern_destroy(pattern);
1301 }
1302 }
1304 if (style->stroke_dash.n_dash &&
1305 style->stroke_dash.dash )
1306 {
1307 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1308 } else {
1309 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1310 }
1312 cairo_set_line_width(_cr, style->stroke_width.computed);
1314 // set line join type
1315 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1316 switch (style->stroke_linejoin.computed) {
1317 case SP_STROKE_LINEJOIN_MITER:
1318 join = CAIRO_LINE_JOIN_MITER;
1319 break;
1320 case SP_STROKE_LINEJOIN_ROUND:
1321 join = CAIRO_LINE_JOIN_ROUND;
1322 break;
1323 case SP_STROKE_LINEJOIN_BEVEL:
1324 join = CAIRO_LINE_JOIN_BEVEL;
1325 break;
1326 }
1327 cairo_set_line_join(_cr, join);
1329 // set line cap type
1330 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1331 switch (style->stroke_linecap.computed) {
1332 case SP_STROKE_LINECAP_BUTT:
1333 cap = CAIRO_LINE_CAP_BUTT;
1334 break;
1335 case SP_STROKE_LINECAP_ROUND:
1336 cap = CAIRO_LINE_CAP_ROUND;
1337 break;
1338 case SP_STROKE_LINECAP_SQUARE:
1339 cap = CAIRO_LINE_CAP_SQUARE;
1340 break;
1341 }
1342 cairo_set_line_cap(_cr, cap);
1343 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1344 }
1346 bool
1347 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1348 {
1349 g_assert( _is_valid );
1351 if (_render_mode == RENDER_MODE_CLIP) {
1352 if (_clip_mode == CLIP_MODE_PATH) {
1353 addClipPath(pathv, &style->fill_rule);
1354 } else {
1355 setPathVector(pathv);
1356 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1357 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1358 } else {
1359 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1360 }
1361 cairo_fill(_cr);
1362 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1363 }
1364 return true;
1365 }
1367 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1368 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1369 style->stroke_opacity.value == 0;
1371 if (no_fill && no_stroke)
1372 return true;
1374 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1375 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1377 if (!need_layer)
1378 cairo_save(_cr);
1379 else
1380 pushLayer();
1382 if (!no_fill) {
1383 _setFillStyle(style, pbox);
1384 setPathVector(pathv);
1386 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1387 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1388 } else {
1389 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1390 }
1392 if (no_stroke)
1393 cairo_fill(_cr);
1394 else
1395 cairo_fill_preserve(_cr);
1396 }
1398 if (!no_stroke) {
1399 _setStrokeStyle(style, pbox);
1400 if (no_fill)
1401 setPathVector(pathv);
1403 cairo_stroke(_cr);
1404 }
1406 if (need_layer)
1407 popLayer();
1408 else
1409 cairo_restore(_cr);
1411 return true;
1412 }
1414 bool
1415 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1416 Geom::Matrix const *image_transform, SPStyle const *style)
1417 {
1418 g_assert( _is_valid );
1420 if (_render_mode == RENDER_MODE_CLIP)
1421 return true;
1423 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1424 if (!px_rgba)
1425 return false;
1427 float opacity;
1428 if (_state->merge_opacity)
1429 opacity = _state->opacity;
1430 else
1431 opacity = 1.0;
1433 // make a copy of the original pixbuf with premultiplied alpha
1434 // if we pass the original pixbuf it will get messed up
1435 for (unsigned i = 0; i < h; i++) {
1436 for (unsigned j = 0; j < w; j++) {
1437 guchar const *src = px + i * rs + j * 4;
1438 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1439 guchar r, g, b, alpha_dst;
1441 // calculate opacity-modified alpha
1442 alpha_dst = src[3];
1443 if (opacity != 1.0 && _vector_based_target)
1444 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1446 // premul alpha (needed because this will be undone by cairo-pdf)
1447 r = src[0]*alpha_dst/255;
1448 g = src[1]*alpha_dst/255;
1449 b = src[2]*alpha_dst/255;
1451 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1452 }
1453 }
1455 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1456 if (cairo_surface_status(image_surface)) {
1457 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1458 return false;
1459 }
1461 // setup automatic freeing of the image data when destroying the surface
1462 static cairo_user_data_key_t key;
1463 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1465 cairo_save(_cr);
1467 // scaling by width & height is not needed because it will be done by Cairo
1468 if (image_transform)
1469 transform(image_transform);
1471 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1473 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1474 if (_vector_based_target) {
1475 cairo_new_path(_cr);
1476 cairo_rectangle(_cr, 0, 0, w, h);
1477 cairo_clip(_cr);
1478 }
1480 if (_vector_based_target)
1481 cairo_paint(_cr);
1482 else
1483 cairo_paint_with_alpha(_cr, opacity);
1485 cairo_restore(_cr);
1487 cairo_surface_destroy(image_surface);
1489 return true;
1490 }
1492 #define GLYPH_ARRAY_SIZE 64
1494 unsigned int
1495 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1496 {
1497 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1498 cairo_glyph_t *glyphs = glyph_array;
1499 unsigned int num_glyphs = glyphtext.size();
1500 if (num_glyphs > GLYPH_ARRAY_SIZE)
1501 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1503 unsigned int num_invalid_glyphs = 0;
1504 unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1505 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1506 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1507 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1508 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1509 TRACE(("INVALID GLYPH found\n"));
1510 g_message("Invalid glyph found, continuing...");
1511 num_invalid_glyphs++;
1512 continue;
1513 }
1514 glyphs[i].index = it_info->index;
1515 glyphs[i].x = it_info->x;
1516 glyphs[i].y = it_info->y;
1517 i++;
1518 }
1520 if (path) {
1521 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1522 } else {
1523 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1524 }
1526 if (num_glyphs > GLYPH_ARRAY_SIZE)
1527 g_free(glyphs);
1529 return num_glyphs - num_invalid_glyphs;
1530 }
1532 bool
1533 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1534 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1535 {
1536 // create a cairo_font_face from PangoFont
1537 double size = style->font_size.computed;
1538 gpointer fonthash = (gpointer)font;
1539 cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1541 FcPattern *fc_pattern = NULL;
1543 #ifdef USE_PANGO_WIN32
1544 # ifdef CAIRO_HAS_WIN32_FONT
1545 LOGFONTA *lfa = pango_win32_font_logfont(font);
1546 LOGFONTW lfw;
1548 ZeroMemory(&lfw, sizeof(LOGFONTW));
1549 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1550 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1552 if(font_face == NULL) {
1553 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1554 g_hash_table_insert(font_table, fonthash, font_face);
1555 }
1556 # endif
1557 #else
1558 # ifdef CAIRO_HAS_FT_FONT
1559 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1560 fc_pattern = fc_font->font_pattern;
1561 if(font_face == NULL) {
1562 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1563 g_hash_table_insert(font_table, fonthash, font_face);
1564 }
1565 # endif
1566 #endif
1568 cairo_save(_cr);
1569 cairo_set_font_face(_cr, font_face);
1571 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1572 size = 12.0;
1574 // set the given font matrix
1575 cairo_matrix_t matrix;
1576 _initCairoMatrix(&matrix, font_matrix);
1577 cairo_set_font_matrix(_cr, &matrix);
1579 if (_render_mode == RENDER_MODE_CLIP) {
1580 if (_clip_mode == CLIP_MODE_MASK) {
1581 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1582 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1583 } else {
1584 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1585 }
1586 _showGlyphs(_cr, font, glyphtext, FALSE);
1587 } else {
1588 // just add the glyph paths to the current context
1589 _showGlyphs(_cr, font, glyphtext, TRUE);
1590 }
1591 } else {
1592 bool fill = false, stroke = false, have_path = false;
1593 if (style->fill.isColor() || style->fill.isPaintserver()) {
1594 fill = true;
1595 }
1597 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1598 stroke = true;
1599 }
1600 if (fill) {
1601 _setFillStyle(style, NULL);
1602 if (_is_texttopath) {
1603 _showGlyphs(_cr, font, glyphtext, true);
1604 have_path = true;
1605 if (stroke) cairo_fill_preserve(_cr);
1606 else cairo_fill(_cr);
1607 } else {
1608 _showGlyphs(_cr, font, glyphtext, false);
1609 }
1610 }
1611 if (stroke) {
1612 _setStrokeStyle(style, NULL);
1613 if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1614 cairo_stroke(_cr);
1615 }
1616 }
1618 cairo_restore(_cr);
1620 // if (font_face)
1621 // cairo_font_face_destroy(font_face);
1623 return true;
1624 }
1626 /* Helper functions */
1628 void
1629 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1630 {
1631 cairo_new_path(_cr);
1632 addPathVector(pv);
1633 }
1635 void
1636 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1637 {
1638 feed_pathvector_to_cairo(_cr, pv);
1639 }
1641 void
1642 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1643 {
1644 cairo_matrix_t matrix;
1646 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1647 cairo_transform(cr, &matrix);
1648 }
1650 void
1651 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1652 {
1653 matrix->xx = (*transform)[0];
1654 matrix->yx = (*transform)[1];
1655 matrix->xy = (*transform)[2];
1656 matrix->yy = (*transform)[3];
1657 matrix->x0 = (*transform)[4];
1658 matrix->y0 = (*transform)[5];
1659 }
1661 void
1662 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1663 {
1664 _concatTransform(cr, (*transform)[0], (*transform)[1],
1665 (*transform)[2], (*transform)[3],
1666 (*transform)[4], (*transform)[5]);
1667 }
1669 static cairo_status_t
1670 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1671 {
1672 size_t written;
1673 FILE *file = (FILE*)closure;
1675 written = fwrite (data, 1, length, file);
1677 if (written == length)
1678 return CAIRO_STATUS_SUCCESS;
1679 else
1680 return CAIRO_STATUS_WRITE_ERROR;
1681 }
1683 #include "clear-n_.h"
1685 } /* namespace Internal */
1686 } /* namespace Extension */
1687 } /* namespace Inkscape */
1689 #undef TRACE
1690 #undef TEST
1692 /* End of GNU GPL code */
1695 /*
1696 Local Variables:
1697 mode:c++
1698 c-file-style:"stroustrup"
1699 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1700 indent-tabs-mode:nil
1701 fill-column:99
1702 End:
1703 */
1704 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :