1 #define __SP_CAIRO_RENDER_CONTEXT_C__
3 /** \file
4 * Rendering with Cairo.
5 */
6 /*
7 * Author:
8 * Miklos Erdelyi <erdelyim@gmail.com>
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 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
989 if (SP_IS_ITEM (child)) {
990 return true;
991 }
992 }
993 return false;
994 }
996 cairo_pattern_t*
997 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
998 {
999 g_assert( SP_IS_PATTERN(paintserver) );
1001 SPPattern *pat = SP_PATTERN (paintserver);
1003 Geom::Matrix ps2user, pcs2dev;
1004 ps2user = Geom::identity();
1005 pcs2dev = Geom::identity();
1007 double x = pattern_x(pat);
1008 double y = pattern_y(pat);
1009 double width = pattern_width(pat);
1010 double height = pattern_height(pat);
1011 double bbox_width_scaler;
1012 double bbox_height_scaler;
1014 TRACE(("%f x %f pattern\n", width, height));
1016 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1017 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1018 bbox_width_scaler = pbox->x1 - pbox->x0;
1019 bbox_height_scaler = pbox->y1 - pbox->y0;
1020 ps2user[4] = x * bbox_width_scaler + pbox->x0;
1021 ps2user[5] = y * bbox_height_scaler + pbox->y0;
1022 } else {
1023 bbox_width_scaler = 1.0;
1024 bbox_height_scaler = 1.0;
1025 ps2user[4] = x;
1026 ps2user[5] = y;
1027 }
1029 // apply pattern transformation
1030 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1031 ps2user *= pattern_transform;
1032 Geom::Point ori (ps2user[4], ps2user[5]);
1034 // create pattern contents coordinate system
1035 if (pat->viewBox_set) {
1036 NRRect *view_box = pattern_viewBox(pat);
1038 double x, y, w, h;
1039 double view_width, view_height;
1040 x = 0;
1041 y = 0;
1042 w = width * bbox_width_scaler;
1043 h = height * bbox_height_scaler;
1045 view_width = view_box->x1 - view_box->x0;
1046 view_height = view_box->y1 - view_box->y0;
1048 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1049 pcs2dev[0] = w / view_width;
1050 pcs2dev[3] = h / view_height;
1051 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1052 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1053 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1054 pcs2dev[0] = pbox->x1 - pbox->x0;
1055 pcs2dev[3] = pbox->y1 - pbox->y0;
1057 }
1059 // Calculate the size of the surface which has to be created
1060 #define SUBPIX_SCALE 100
1061 // Cairo requires an integer pattern surface width/height.
1062 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1063 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1064 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1065 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1066 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1067 // create new rendering context
1068 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1070 // adjust the size of the painted pattern to fit exactly the created surface
1071 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1072 double scale_width = surface_width / (bbox_width_scaler * width);
1073 double scale_height = surface_height / (bbox_height_scaler * height);
1074 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1075 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1076 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1077 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1078 }
1080 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1081 ps2user[4] = ori[Geom::X];
1082 ps2user[5] = ori[Geom::Y];
1084 pattern_ctx->setTransform(&pcs2dev);
1085 pattern_ctx->pushState();
1087 // create arena and group
1088 NRArena *arena = NRArena::create();
1089 unsigned dkey = sp_item_display_key_new(1);
1091 // show items and render them
1092 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1093 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1094 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1095 if (SP_IS_ITEM (child)) {
1096 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1097 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1098 }
1099 }
1100 break; // do not go further up the chain if children are found
1101 }
1102 }
1104 pattern_ctx->popState();
1106 // setup a cairo_pattern_t
1107 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1108 TEST(pattern_ctx->saveAsPng("pattern.png"));
1109 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1110 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1112 // set pattern transformation
1113 cairo_matrix_t pattern_matrix;
1114 _initCairoMatrix(&pattern_matrix, &ps2user);
1115 cairo_matrix_invert(&pattern_matrix);
1116 cairo_pattern_set_matrix(result, &pattern_matrix);
1118 delete pattern_ctx;
1120 // hide all items
1121 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1122 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1123 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1124 if (SP_IS_ITEM (child)) {
1125 sp_item_invoke_hide (SP_ITEM (child), dkey);
1126 }
1127 }
1128 break; // do not go further up the chain if children are found
1129 }
1130 }
1132 return result;
1133 }
1135 cairo_pattern_t*
1136 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1137 NRRect const *pbox, float alpha)
1138 {
1139 cairo_pattern_t *pattern = NULL;
1140 bool apply_bbox2user = FALSE;
1142 if (SP_IS_LINEARGRADIENT (paintserver)) {
1144 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1146 SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1148 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1149 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1150 if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1151 // convert to userspace
1152 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1153 p1 *= bbox2user;
1154 p2 *= bbox2user;
1155 }
1157 // create linear gradient pattern
1158 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1160 // add stops
1161 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1162 float rgb[3];
1163 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1164 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1165 }
1166 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1168 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1170 SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1172 Geom::Point c (rg->cx.computed, rg->cy.computed);
1173 Geom::Point f (rg->fx.computed, rg->fy.computed);
1174 double r = rg->r.computed;
1175 if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1176 apply_bbox2user = true;
1178 // create radial gradient pattern
1179 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1181 // add stops
1182 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1183 float rgb[3];
1184 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1185 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1186 }
1187 } else if (SP_IS_PATTERN (paintserver)) {
1189 pattern = _createPatternPainter(paintserver, pbox);
1190 } else {
1191 return NULL;
1192 }
1194 if (pattern && SP_IS_GRADIENT (paintserver)) {
1195 SPGradient *g = SP_GRADIENT(paintserver);
1197 // set extend type
1198 SPGradientSpread spread = g->fetchSpread();
1199 switch (spread) {
1200 case SP_GRADIENT_SPREAD_REPEAT: {
1201 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1202 break;
1203 }
1204 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1205 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1206 break;
1207 }
1208 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1209 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1210 break;
1211 }
1212 default: {
1213 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1214 break;
1215 }
1216 }
1218 cairo_matrix_t pattern_matrix;
1219 if (g->gradientTransform_set) {
1220 // apply gradient transformation
1221 cairo_matrix_init(&pattern_matrix,
1222 g->gradientTransform[0], g->gradientTransform[1],
1223 g->gradientTransform[2], g->gradientTransform[3],
1224 g->gradientTransform[4], g->gradientTransform[5]);
1225 } else {
1226 cairo_matrix_init_identity (&pattern_matrix);
1227 }
1229 if (apply_bbox2user) {
1230 // convert to userspace
1231 cairo_matrix_t bbox2user;
1232 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1233 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1234 }
1235 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1236 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1237 }
1239 return pattern;
1240 }
1242 void
1243 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1244 {
1245 g_return_if_fail( !style->fill.set
1246 || style->fill.isColor()
1247 || style->fill.isPaintserver() );
1249 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1250 if (_state->merge_opacity) {
1251 alpha *= _state->opacity;
1252 TRACE(("merged op=%f\n", alpha));
1253 }
1255 if (style->fill.isColor()) {
1256 float rgb[3];
1257 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1259 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1261 } else if (!style->fill.set) { // unset fill is black
1262 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1264 } else {
1265 g_assert( style->fill.isPaintserver()
1266 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1267 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1269 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1271 if (pattern) {
1272 cairo_set_source(_cr, pattern);
1273 cairo_pattern_destroy(pattern);
1274 }
1275 }
1276 }
1278 void
1279 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1280 {
1281 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1282 if (_state->merge_opacity)
1283 alpha *= _state->opacity;
1285 if (style->stroke.isColor()) {
1286 float rgb[3];
1287 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1289 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1290 } else {
1291 g_assert( style->stroke.isPaintserver()
1292 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1293 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1295 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1297 if (pattern) {
1298 cairo_set_source(_cr, pattern);
1299 cairo_pattern_destroy(pattern);
1300 }
1301 }
1303 if (style->stroke_dash.n_dash &&
1304 style->stroke_dash.dash )
1305 {
1306 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1307 } else {
1308 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1309 }
1311 cairo_set_line_width(_cr, style->stroke_width.computed);
1313 // set line join type
1314 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1315 switch (style->stroke_linejoin.computed) {
1316 case SP_STROKE_LINEJOIN_MITER:
1317 join = CAIRO_LINE_JOIN_MITER;
1318 break;
1319 case SP_STROKE_LINEJOIN_ROUND:
1320 join = CAIRO_LINE_JOIN_ROUND;
1321 break;
1322 case SP_STROKE_LINEJOIN_BEVEL:
1323 join = CAIRO_LINE_JOIN_BEVEL;
1324 break;
1325 }
1326 cairo_set_line_join(_cr, join);
1328 // set line cap type
1329 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1330 switch (style->stroke_linecap.computed) {
1331 case SP_STROKE_LINECAP_BUTT:
1332 cap = CAIRO_LINE_CAP_BUTT;
1333 break;
1334 case SP_STROKE_LINECAP_ROUND:
1335 cap = CAIRO_LINE_CAP_ROUND;
1336 break;
1337 case SP_STROKE_LINECAP_SQUARE:
1338 cap = CAIRO_LINE_CAP_SQUARE;
1339 break;
1340 }
1341 cairo_set_line_cap(_cr, cap);
1342 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1343 }
1345 bool
1346 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1347 {
1348 g_assert( _is_valid );
1350 if (_render_mode == RENDER_MODE_CLIP) {
1351 if (_clip_mode == CLIP_MODE_PATH) {
1352 addClipPath(pathv, &style->fill_rule);
1353 } else {
1354 setPathVector(pathv);
1355 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1356 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1357 } else {
1358 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1359 }
1360 cairo_fill(_cr);
1361 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1362 }
1363 return true;
1364 }
1366 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1367 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1368 style->stroke_opacity.value == 0;
1370 if (no_fill && no_stroke)
1371 return true;
1373 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1374 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1376 if (!need_layer)
1377 cairo_save(_cr);
1378 else
1379 pushLayer();
1381 if (!no_fill) {
1382 _setFillStyle(style, pbox);
1383 setPathVector(pathv);
1385 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1386 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1387 } else {
1388 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1389 }
1391 if (no_stroke)
1392 cairo_fill(_cr);
1393 else
1394 cairo_fill_preserve(_cr);
1395 }
1397 if (!no_stroke) {
1398 _setStrokeStyle(style, pbox);
1399 if (no_fill)
1400 setPathVector(pathv);
1402 cairo_stroke(_cr);
1403 }
1405 if (need_layer)
1406 popLayer();
1407 else
1408 cairo_restore(_cr);
1410 return true;
1411 }
1413 bool
1414 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1415 Geom::Matrix const *image_transform, SPStyle const *style)
1416 {
1417 g_assert( _is_valid );
1419 if (_render_mode == RENDER_MODE_CLIP)
1420 return true;
1422 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1423 if (!px_rgba)
1424 return false;
1426 float opacity;
1427 if (_state->merge_opacity)
1428 opacity = _state->opacity;
1429 else
1430 opacity = 1.0;
1432 // make a copy of the original pixbuf with premultiplied alpha
1433 // if we pass the original pixbuf it will get messed up
1434 for (unsigned i = 0; i < h; i++) {
1435 for (unsigned j = 0; j < w; j++) {
1436 guchar const *src = px + i * rs + j * 4;
1437 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1438 guchar r, g, b, alpha_dst;
1440 // calculate opacity-modified alpha
1441 alpha_dst = src[3];
1442 if (opacity != 1.0 && _vector_based_target)
1443 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1445 // premul alpha (needed because this will be undone by cairo-pdf)
1446 r = src[0]*alpha_dst/255;
1447 g = src[1]*alpha_dst/255;
1448 b = src[2]*alpha_dst/255;
1450 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1451 }
1452 }
1454 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1455 if (cairo_surface_status(image_surface)) {
1456 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1457 return false;
1458 }
1460 // setup automatic freeing of the image data when destroying the surface
1461 static cairo_user_data_key_t key;
1462 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1464 cairo_save(_cr);
1466 // scaling by width & height is not needed because it will be done by Cairo
1467 if (image_transform)
1468 transform(image_transform);
1470 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1472 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1473 if (_vector_based_target) {
1474 cairo_new_path(_cr);
1475 cairo_rectangle(_cr, 0, 0, w, h);
1476 cairo_clip(_cr);
1477 }
1479 if (_vector_based_target)
1480 cairo_paint(_cr);
1481 else
1482 cairo_paint_with_alpha(_cr, opacity);
1484 cairo_restore(_cr);
1486 cairo_surface_destroy(image_surface);
1488 return true;
1489 }
1491 #define GLYPH_ARRAY_SIZE 64
1493 unsigned int
1494 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1495 {
1496 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1497 cairo_glyph_t *glyphs = glyph_array;
1498 unsigned int num_glyphs = glyphtext.size();
1499 if (num_glyphs > GLYPH_ARRAY_SIZE)
1500 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1502 unsigned int num_invalid_glyphs = 0;
1503 unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1504 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1505 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1506 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1507 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1508 TRACE(("INVALID GLYPH found\n"));
1509 g_message("Invalid glyph found, continuing...");
1510 num_invalid_glyphs++;
1511 continue;
1512 }
1513 glyphs[i].index = it_info->index;
1514 glyphs[i].x = it_info->x;
1515 glyphs[i].y = it_info->y;
1516 i++;
1517 }
1519 if (path) {
1520 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1521 } else {
1522 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1523 }
1525 if (num_glyphs > GLYPH_ARRAY_SIZE)
1526 g_free(glyphs);
1528 return num_glyphs - num_invalid_glyphs;
1529 }
1531 bool
1532 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1533 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1534 {
1535 // create a cairo_font_face from PangoFont
1536 double size = style->font_size.computed;
1537 gpointer fonthash = (gpointer)font;
1538 cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1540 FcPattern *fc_pattern = NULL;
1542 #ifdef USE_PANGO_WIN32
1543 # ifdef CAIRO_HAS_WIN32_FONT
1544 LOGFONTA *lfa = pango_win32_font_logfont(font);
1545 LOGFONTW lfw;
1547 ZeroMemory(&lfw, sizeof(LOGFONTW));
1548 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1549 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1551 if(font_face == NULL) {
1552 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1553 g_hash_table_insert(font_table, fonthash, font_face);
1554 }
1555 # endif
1556 #else
1557 # ifdef CAIRO_HAS_FT_FONT
1558 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1559 fc_pattern = fc_font->font_pattern;
1560 if(font_face == NULL) {
1561 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1562 g_hash_table_insert(font_table, fonthash, font_face);
1563 }
1564 # endif
1565 #endif
1567 cairo_save(_cr);
1568 cairo_set_font_face(_cr, font_face);
1570 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1571 size = 12.0;
1573 // set the given font matrix
1574 cairo_matrix_t matrix;
1575 _initCairoMatrix(&matrix, font_matrix);
1576 cairo_set_font_matrix(_cr, &matrix);
1578 if (_render_mode == RENDER_MODE_CLIP) {
1579 if (_clip_mode == CLIP_MODE_MASK) {
1580 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1581 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1582 } else {
1583 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1584 }
1585 _showGlyphs(_cr, font, glyphtext, FALSE);
1586 } else {
1587 // just add the glyph paths to the current context
1588 _showGlyphs(_cr, font, glyphtext, TRUE);
1589 }
1590 } else {
1591 bool fill = false, stroke = false, have_path = false;
1592 if (style->fill.isColor() || style->fill.isPaintserver()) {
1593 fill = true;
1594 }
1596 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1597 stroke = true;
1598 }
1599 if (fill) {
1600 _setFillStyle(style, NULL);
1601 if (_is_texttopath) {
1602 _showGlyphs(_cr, font, glyphtext, true);
1603 have_path = true;
1604 if (stroke) cairo_fill_preserve(_cr);
1605 else cairo_fill(_cr);
1606 } else {
1607 _showGlyphs(_cr, font, glyphtext, false);
1608 }
1609 }
1610 if (stroke) {
1611 _setStrokeStyle(style, NULL);
1612 if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1613 cairo_stroke(_cr);
1614 }
1615 }
1617 cairo_restore(_cr);
1619 // if (font_face)
1620 // cairo_font_face_destroy(font_face);
1622 return true;
1623 }
1625 /* Helper functions */
1627 void
1628 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1629 {
1630 cairo_new_path(_cr);
1631 addPathVector(pv);
1632 }
1634 void
1635 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1636 {
1637 feed_pathvector_to_cairo(_cr, pv);
1638 }
1640 void
1641 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1642 {
1643 cairo_matrix_t matrix;
1645 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1646 cairo_transform(cr, &matrix);
1647 }
1649 void
1650 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1651 {
1652 matrix->xx = (*transform)[0];
1653 matrix->yx = (*transform)[1];
1654 matrix->xy = (*transform)[2];
1655 matrix->yy = (*transform)[3];
1656 matrix->x0 = (*transform)[4];
1657 matrix->y0 = (*transform)[5];
1658 }
1660 void
1661 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1662 {
1663 _concatTransform(cr, (*transform)[0], (*transform)[1],
1664 (*transform)[2], (*transform)[3],
1665 (*transform)[4], (*transform)[5]);
1666 }
1668 static cairo_status_t
1669 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1670 {
1671 size_t written;
1672 FILE *file = (FILE*)closure;
1674 written = fwrite (data, 1, length, file);
1676 if (written == length)
1677 return CAIRO_STATUS_SUCCESS;
1678 else
1679 return CAIRO_STATUS_WRITE_ERROR;
1680 }
1682 #include "clear-n_.h"
1684 } /* namespace Internal */
1685 } /* namespace Extension */
1686 } /* namespace Inkscape */
1688 #undef TRACE
1689 #undef TEST
1691 /* End of GNU GPL code */
1694 /*
1695 Local Variables:
1696 mode:c++
1697 c-file-style:"stroustrup"
1698 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1699 indent-tabs-mode:nil
1700 fill-column:99
1701 End:
1702 */
1703 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :