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>
31 #include <libnr/n-art-bpath.h>
32 #include <libnr/nr-matrix-ops.h>
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-translate-ops.h>
35 #include <libnr/nr-scale-matrix-ops.h>
37 #include <glib/gmem.h>
39 #include <glibmm/i18n.h>
40 #include "display/nr-arena.h"
41 #include "display/nr-arena-item.h"
42 #include "display/nr-arena-group.h"
43 #include "display/curve.h"
44 #include "display/canvas-bpath.h"
45 #include "sp-item.h"
46 #include "sp-item-group.h"
47 #include "style.h"
48 #include "sp-linear-gradient.h"
49 #include "sp-radial-gradient.h"
50 #include "sp-pattern.h"
51 #include "sp-mask.h"
52 #include "sp-clippath.h"
54 #include <unit-constants.h>
56 #include "cairo-render-context.h"
57 #include "cairo-renderer.h"
58 #include "extension/system.h"
60 #include "io/sys.h"
62 #include <cairo.h>
64 // include support for only the compiled-in surface types
65 #ifdef CAIRO_HAS_PDF_SURFACE
66 #include <cairo-pdf.h>
67 #endif
68 #ifdef CAIRO_HAS_PS_SURFACE
69 #include <cairo-ps.h>
70 #endif
73 #ifndef PANGO_ENABLE_BACKEND
74 #include <cairo-ft.h>
75 #endif
77 #include <pango/pangofc-fontmap.h>
79 //#define TRACE(_args) g_printf _args
80 #define TRACE(_args)
81 //#define TEST(_args) _args
82 #define TEST(_args)
84 // FIXME: expose these from sp-clippath/mask.cpp
85 struct SPClipPathView {
86 SPClipPathView *next;
87 unsigned int key;
88 NRArenaItem *arenaitem;
89 NRRect bbox;
90 };
92 struct SPMaskView {
93 SPMaskView *next;
94 unsigned int key;
95 NRArenaItem *arenaitem;
96 NRRect bbox;
97 };
99 namespace Inkscape {
100 namespace Extension {
101 namespace Internal {
103 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
105 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
106 _dpi(72),
107 _stream(NULL),
108 _is_valid(FALSE),
109 _vector_based_target(FALSE),
110 _cr(NULL),
111 _surface(NULL),
112 _target(CAIRO_SURFACE_TYPE_IMAGE),
113 _target_format(CAIRO_FORMAT_ARGB32),
114 _layout(NULL),
115 _state(NULL),
116 _renderer(parent),
117 _render_mode(RENDER_MODE_NORMAL),
118 _clip_mode(CLIP_MODE_MASK)
119 {}
121 CairoRenderContext::~CairoRenderContext(void)
122 {
123 if (_cr) cairo_destroy(_cr);
124 if (_surface) cairo_surface_destroy(_surface);
125 if (_layout) g_object_unref(_layout);
126 }
128 CairoRenderer*
129 CairoRenderContext::getRenderer(void) const
130 {
131 return _renderer;
132 }
134 CairoRenderState*
135 CairoRenderContext::getCurrentState(void) const
136 {
137 return _state;
138 }
140 CairoRenderState*
141 CairoRenderContext::getParentState(void) const
142 {
143 // if this is the root node just return it
144 if (g_slist_length(_state_stack) == 1) {
145 return _state;
146 } else {
147 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
148 }
149 }
151 void
152 CairoRenderContext::setStateForStyle(SPStyle const *style)
153 {
154 // only opacity & overflow is stored for now
155 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
156 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
158 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
159 _state->merge_opacity = FALSE;
161 // disable rendering of opacity if there's a stroke on the fill
162 if (_state->merge_opacity
163 && !style->fill.isNone()
164 && !style->stroke.isNone())
165 _state->merge_opacity = FALSE;
166 }
168 /**
169 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
170 *
171 * \param width width of the surface to be created
172 * \param height height of the surface to be created
173 */
174 CairoRenderContext*
175 CairoRenderContext::cloneMe(double width, double height) const
176 {
177 g_assert( _is_valid );
178 g_assert( width > 0.0 && height > 0.0 );
180 CairoRenderContext *new_context = _renderer->createContext();
181 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
182 (int)ceil(width), (int)ceil(height));
183 new_context->_cr = cairo_create(surface);
184 new_context->_surface = surface;
185 new_context->_is_valid = TRUE;
187 return new_context;
188 }
190 CairoRenderContext*
191 CairoRenderContext::cloneMe(void) const
192 {
193 g_assert( _is_valid );
195 return cloneMe(_width, _height);
196 }
198 bool
199 CairoRenderContext::setImageTarget(cairo_format_t format)
200 {
201 // format cannot be set on an already initialized surface
202 if (_is_valid)
203 return false;
205 switch (format) {
206 case CAIRO_FORMAT_ARGB32:
207 case CAIRO_FORMAT_RGB24:
208 case CAIRO_FORMAT_A8:
209 case CAIRO_FORMAT_A1:
210 _target_format = format;
211 _target = CAIRO_SURFACE_TYPE_IMAGE;
212 return true;
213 break;
214 default:
215 break;
216 }
218 return false;
219 }
221 bool
222 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
223 {
224 #ifndef CAIRO_HAS_PDF_SURFACE
225 return false;
226 #else
227 _target = CAIRO_SURFACE_TYPE_PDF;
228 _vector_based_target = TRUE;
229 #endif
231 FILE *osf = NULL;
232 FILE *osp = NULL;
234 gsize bytesRead = 0;
235 gsize bytesWritten = 0;
236 GError *error = NULL;
237 gchar *local_fn = g_filename_from_utf8(utf8_fn,
238 -1, &bytesRead, &bytesWritten, &error);
239 gchar const *fn = local_fn;
241 /* TODO: Replace the below fprintf's with something that does the right thing whether in
242 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
243 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
244 * return code.
245 */
246 if (fn != NULL) {
247 if (*fn == '|') {
248 fn += 1;
249 while (isspace(*fn)) fn += 1;
250 #ifndef WIN32
251 osp = popen(fn, "w");
252 #else
253 osp = _popen(fn, "w");
254 #endif
255 if (!osp) {
256 fprintf(stderr, "inkscape: popen(%s): %s\n",
257 fn, strerror(errno));
258 return false;
259 }
260 _stream = osp;
261 } else if (*fn == '>') {
262 fn += 1;
263 while (isspace(*fn)) fn += 1;
264 Inkscape::IO::dump_fopen_call(fn, "K");
265 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
266 if (!osf) {
267 fprintf(stderr, "inkscape: fopen(%s): %s\n",
268 fn, strerror(errno));
269 return false;
270 }
271 _stream = osf;
272 } else {
273 /* put cwd stuff in here */
274 gchar *qn = ( *fn
275 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
276 : g_strdup("lpr") );
277 #ifndef WIN32
278 osp = popen(qn, "w");
279 #else
280 osp = _popen(qn, "w");
281 #endif
282 if (!osp) {
283 fprintf(stderr, "inkscape: popen(%s): %s\n",
284 qn, strerror(errno));
285 return false;
286 }
287 g_free(qn);
288 _stream = osp;
289 }
290 }
292 g_free(local_fn);
294 if (_stream) {
295 /* fixme: this is kinda icky */
296 #if !defined(_WIN32) && !defined(__WIN32__)
297 (void) signal(SIGPIPE, SIG_IGN);
298 #endif
299 }
301 return true;
302 }
304 bool
305 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
306 {
307 #ifndef CAIRO_HAS_PS_SURFACE
308 return false;
309 #else
310 _target = CAIRO_SURFACE_TYPE_PS;
311 _vector_based_target = TRUE;
312 #endif
314 FILE *osf = NULL;
315 FILE *osp = NULL;
317 gsize bytesRead = 0;
318 gsize bytesWritten = 0;
319 GError *error = NULL;
320 gchar *local_fn = g_filename_from_utf8(utf8_fn,
321 -1, &bytesRead, &bytesWritten, &error);
322 gchar const *fn = local_fn;
324 /* TODO: Replace the below fprintf's with something that does the right thing whether in
325 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
326 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
327 * return code.
328 */
329 if (fn != NULL) {
330 if (*fn == '|') {
331 fn += 1;
332 while (isspace(*fn)) fn += 1;
333 #ifndef WIN32
334 osp = popen(fn, "w");
335 #else
336 osp = _popen(fn, "w");
337 #endif
338 if (!osp) {
339 fprintf(stderr, "inkscape: popen(%s): %s\n",
340 fn, strerror(errno));
341 return false;
342 }
343 _stream = osp;
344 } else if (*fn == '>') {
345 fn += 1;
346 while (isspace(*fn)) fn += 1;
347 Inkscape::IO::dump_fopen_call(fn, "K");
348 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
349 if (!osf) {
350 fprintf(stderr, "inkscape: fopen(%s): %s\n",
351 fn, strerror(errno));
352 return false;
353 }
354 _stream = osf;
355 } else {
356 /* put cwd stuff in here */
357 gchar *qn = ( *fn
358 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
359 : g_strdup("lpr") );
360 #ifndef WIN32
361 osp = popen(qn, "w");
362 #else
363 osp = _popen(qn, "w");
364 #endif
365 if (!osp) {
366 fprintf(stderr, "inkscape: popen(%s): %s\n",
367 qn, strerror(errno));
368 return false;
369 }
370 g_free(qn);
371 _stream = osp;
372 }
373 }
375 g_free(local_fn);
377 if (_stream) {
378 /* fixme: this is kinda icky */
379 #if !defined(_WIN32) && !defined(__WIN32__)
380 (void) signal(SIGPIPE, SIG_IGN);
381 #endif
382 }
384 return true;
385 }
387 cairo_surface_t*
388 CairoRenderContext::getSurface(void)
389 {
390 g_assert( _is_valid );
392 return _surface;
393 }
395 bool
396 CairoRenderContext::saveAsPng(const char *file_name)
397 {
398 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
399 if (status)
400 return false;
401 else
402 return true;
403 }
405 void
406 CairoRenderContext::setRenderMode(CairoRenderMode mode)
407 {
408 switch (mode) {
409 case RENDER_MODE_NORMAL:
410 case RENDER_MODE_CLIP:
411 _render_mode = mode;
412 break;
413 default:
414 _render_mode = RENDER_MODE_NORMAL;
415 break;
416 }
417 }
419 CairoRenderContext::CairoRenderMode
420 CairoRenderContext::getRenderMode(void) const
421 {
422 return _render_mode;
423 }
425 void
426 CairoRenderContext::setClipMode(CairoClipMode mode)
427 {
428 switch (mode) {
429 case CLIP_MODE_PATH:
430 case CLIP_MODE_MASK:
431 _clip_mode = mode;
432 break;
433 default:
434 _clip_mode = CLIP_MODE_PATH;
435 break;
436 }
437 }
439 CairoRenderContext::CairoClipMode
440 CairoRenderContext::getClipMode(void) const
441 {
442 return _clip_mode;
443 }
445 CairoRenderState*
446 CairoRenderContext::_createState(void)
447 {
448 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
449 g_assert( state != NULL );
451 state->merge_opacity = TRUE;
452 state->opacity = 1.0;
453 state->need_layer = FALSE;
454 state->has_overflow = FALSE;
455 state->parent_has_userspace = FALSE;
456 state->clip_path = NULL;
457 state->mask = NULL;
459 return state;
460 }
462 void
463 CairoRenderContext::pushLayer(void)
464 {
465 g_assert( _is_valid );
467 TRACE(("--pushLayer\n"));
468 cairo_push_group(_cr);
470 // clear buffer
471 if (!_vector_based_target) {
472 cairo_save(_cr);
473 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
474 cairo_paint(_cr);
475 cairo_restore(_cr);
476 }
477 }
479 void
480 CairoRenderContext::popLayer(void)
481 {
482 g_assert( _is_valid );
484 float opacity = _state->opacity;
485 TRACE(("--popLayer w/ %f\n", opacity));
487 // apply clipPath or mask if present
488 SPClipPath *clip_path = _state->clip_path;
489 SPMask *mask = _state->mask;
490 if (clip_path || mask) {
492 CairoRenderContext *clip_ctx = 0;
493 cairo_surface_t *clip_mask = 0;
495 if (clip_path) {
496 if (_render_mode == RENDER_MODE_CLIP)
497 mask = NULL; // disable mask when performing nested clipping
499 if (_vector_based_target) {
500 setClipMode(CLIP_MODE_PATH);
501 if (!mask) {
502 cairo_pop_group_to_source(_cr);
503 _renderer->applyClipPath(this, clip_path);
504 if (opacity == 1.0)
505 cairo_paint(_cr);
506 else
507 cairo_paint_with_alpha(_cr, opacity);
509 } else {
510 // the clipPath will be applied before masking
511 }
512 } else {
514 // setup a new rendering context
515 clip_ctx = _renderer->createContext();
516 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
517 clip_ctx->setClipMode(CLIP_MODE_MASK);
518 if (!clip_ctx->setupSurface(_width, _height)) {
519 TRACE(("setupSurface failed\n"));
520 _renderer->destroyContext(clip_ctx);
521 return;
522 }
524 // clear buffer
525 cairo_save(clip_ctx->_cr);
526 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
527 cairo_paint(clip_ctx->_cr);
528 cairo_restore(clip_ctx->_cr);
530 // if a mask won't be applied set opacity too
531 if (!mask)
532 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
533 else
534 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
536 // copy over the correct CTM
537 if (_state->parent_has_userspace)
538 clip_ctx->setTransform(&getParentState()->transform);
539 else
540 clip_ctx->setTransform(&_state->transform);
542 // apply the clip path
543 clip_ctx->pushState();
544 _renderer->applyClipPath(clip_ctx, clip_path);
545 clip_ctx->popState();
547 clip_mask = clip_ctx->getSurface();
548 TEST(clip_ctx->saveAsPng("clip_mask.png"));
550 if (!mask) {
551 cairo_pop_group_to_source(_cr);
552 cairo_mask_surface(_cr, clip_mask, 0, 0);
553 _renderer->destroyContext(clip_ctx);
554 }
555 }
556 }
558 if (mask) {
559 // create rendering context for mask
560 CairoRenderContext *mask_ctx = _renderer->createContext();
561 mask_ctx->setupSurface(_width, _height);
563 // set rendering mode to normal
564 setRenderMode(RENDER_MODE_NORMAL);
566 // copy the correct CTM to mask context
567 if (_state->parent_has_userspace)
568 mask_ctx->setTransform(&getParentState()->transform);
569 else
570 mask_ctx->setTransform(&_state->transform);
572 // render mask contents to mask_ctx
573 _renderer->applyMask(mask_ctx, mask);
575 TEST(mask_ctx->saveAsPng("mask.png"));
577 // composite with clip mask
578 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
579 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
580 _renderer->destroyContext(clip_ctx);
581 }
583 cairo_surface_t *mask_image = mask_ctx->getSurface();
584 int width = cairo_image_surface_get_width(mask_image);
585 int height = cairo_image_surface_get_height(mask_image);
586 int stride = cairo_image_surface_get_stride(mask_image);
587 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
589 // premultiply with opacity
590 if (_state->opacity != 1.0) {
591 TRACE(("premul w/ %f\n", opacity));
592 guint8 int_opacity = (guint8)(255 * opacity);
593 for (int row = 0 ; row < height; row++) {
594 unsigned char *row_data = pixels + (row * stride);
595 for (int i = 0 ; i < width; i++) {
596 guint32 *pixel = (guint32 *)row_data + i;
597 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
598 ((*pixel & 0x0000ff00) >> 8) * 46518 +
599 ((*pixel & 0x000000ff) ) * 4688) *
600 int_opacity);
601 }
602 }
603 }
605 cairo_pop_group_to_source(_cr);
606 if (_clip_mode == CLIP_MODE_PATH) {
607 // we have to do the clipping after cairo_pop_group_to_source
608 _renderer->applyClipPath(this, clip_path);
609 }
610 // apply the mask onto the layer
611 cairo_mask_surface(_cr, mask_image, 0, 0);
612 _renderer->destroyContext(mask_ctx);
613 }
614 } else {
615 cairo_pop_group_to_source(_cr);
616 if (opacity == 1.0)
617 cairo_paint(_cr);
618 else
619 cairo_paint_with_alpha(_cr, opacity);
620 }
621 }
623 void
624 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
625 {
626 g_assert( _is_valid );
628 // here it should be checked whether the current clip winding changed
629 // so we could switch back to masked clipping
630 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
631 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
632 } else {
633 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
634 }
635 addBpath(bp);
636 }
638 void
639 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
640 {
641 g_assert( _is_valid );
643 cairo_rectangle(_cr, x, y, width, height);
644 cairo_clip(_cr);
645 }
647 bool
648 CairoRenderContext::setupSurface(double width, double height)
649 {
650 // Is the surface already set up?
651 if (_is_valid)
652 return true;
654 if (_vector_based_target && _stream == NULL)
655 return false;
657 cairo_surface_t *surface = NULL;
658 switch (_target) {
659 case CAIRO_SURFACE_TYPE_IMAGE:
660 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
661 break;
662 #ifdef CAIRO_HAS_PDF_SURFACE
663 case CAIRO_SURFACE_TYPE_PDF:
664 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
665 break;
666 #endif
667 #ifdef CAIRO_HAS_PS_SURFACE
668 case CAIRO_SURFACE_TYPE_PS:
669 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
670 break;
671 #endif
672 default:
673 return false;
674 break;
675 }
677 return _finishSurfaceSetup (surface);
678 }
680 bool
681 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
682 {
683 if (_is_valid || !surface)
684 return false;
686 _vector_based_target = is_vector;
687 bool ret = _finishSurfaceSetup (surface);
688 if (ret)
689 cairo_surface_reference (surface);
690 return ret;
691 }
693 bool
694 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
695 {
696 _cr = cairo_create(surface);
697 _surface = surface;
699 if (_vector_based_target) {
700 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
701 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
702 // set background color on non-alpha surfaces
703 // TODO: bgcolor should be derived from SPDocument
704 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
705 cairo_rectangle(_cr, 0, 0, _width, _height);
706 cairo_fill(_cr);
707 }
709 _is_valid = TRUE;
711 return true;
712 }
714 bool
715 CairoRenderContext::finish(void)
716 {
717 g_assert( _is_valid );
719 if (_vector_based_target)
720 cairo_show_page(_cr);
722 cairo_destroy(_cr);
723 cairo_surface_destroy(_surface);
724 _cr = NULL;
725 _surface = NULL;
727 if (_layout)
728 g_object_unref(_layout);
730 _is_valid = FALSE;
732 if (_vector_based_target && _stream) {
733 /* Flush stream to be sure. */
734 (void) fflush(_stream);
736 fclose(_stream);
737 _stream = NULL;
738 }
740 return true;
741 }
743 void
744 CairoRenderContext::transform(NRMatrix const *transform)
745 {
746 g_assert( _is_valid );
748 cairo_matrix_t matrix;
749 _initCairoMatrix(&matrix, transform);
750 cairo_transform(_cr, &matrix);
752 // store new CTM
753 getTransform(&_state->transform);
754 }
756 void
757 CairoRenderContext::setTransform(NRMatrix const *transform)
758 {
759 g_assert( _is_valid );
761 cairo_matrix_t matrix;
762 _initCairoMatrix(&matrix, transform);
763 cairo_set_matrix(_cr, &matrix);
764 _state->transform = *transform;
765 }
767 void
768 CairoRenderContext::getTransform(NRMatrix *copy) const
769 {
770 g_assert( _is_valid );
772 cairo_matrix_t ctm;
773 cairo_get_matrix(_cr, &ctm);
774 (*copy)[0] = ctm.xx;
775 (*copy)[1] = ctm.yx;
776 (*copy)[2] = ctm.xy;
777 (*copy)[3] = ctm.yy;
778 (*copy)[4] = ctm.x0;
779 (*copy)[5] = ctm.y0;
780 }
782 void
783 CairoRenderContext::getParentTransform(NRMatrix *copy) const
784 {
785 g_assert( _is_valid );
787 CairoRenderState *parent_state = getParentState();
788 memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
789 }
791 void
792 CairoRenderContext::pushState(void)
793 {
794 g_assert( _is_valid );
796 cairo_save(_cr);
798 CairoRenderState *new_state = _createState();
799 // copy current state's transform
800 new_state->transform = _state->transform;
801 _state_stack = g_slist_prepend(_state_stack, new_state);
802 _state = new_state;
803 }
805 void
806 CairoRenderContext::popState(void)
807 {
808 g_assert( _is_valid );
810 cairo_restore(_cr);
812 g_free(_state_stack->data);
813 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
814 _state = (CairoRenderState*)_state_stack->data;
816 g_assert( g_slist_length(_state_stack) > 0 );
817 }
819 static bool pattern_hasItemChildren (SPPattern *pat)
820 {
821 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
822 if (SP_IS_ITEM (child)) {
823 return true;
824 }
825 }
826 return false;
827 }
829 cairo_pattern_t*
830 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
831 {
832 g_assert( SP_IS_PATTERN(paintserver) );
834 SPPattern *pat = SP_PATTERN (paintserver);
836 NRMatrix ps2user, pcs2dev;
837 nr_matrix_set_identity(&ps2user);
838 nr_matrix_set_identity(&pcs2dev);
840 double x = pattern_x(pat);
841 double y = pattern_y(pat);
842 double width = pattern_width(pat);
843 double height = pattern_height(pat);
844 double bbox_width_scaler;
845 double bbox_height_scaler;
847 TRACE(("%f x %f pattern\n", width, height));
849 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
850 //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
851 bbox_width_scaler = pbox->x1 - pbox->x0;
852 bbox_height_scaler = pbox->y1 - pbox->y0;
853 ps2user[4] = x * bbox_width_scaler + pbox->x0;
854 ps2user[5] = y * bbox_height_scaler + pbox->y0;
855 } else {
856 bbox_width_scaler = 1.0;
857 bbox_height_scaler = 1.0;
858 ps2user[4] = x;
859 ps2user[5] = y;
860 }
862 // apply pattern transformation
863 NRMatrix pattern_transform(pattern_patternTransform(pat));
864 nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
866 // create pattern contents coordinate system
867 if (pat->viewBox_set) {
868 NRRect *view_box = pattern_viewBox(pat);
870 double x, y, w, h;
871 double view_width, view_height;
872 x = 0;
873 y = 0;
874 w = width * bbox_width_scaler;
875 h = height * bbox_height_scaler;
877 view_width = view_box->x1 - view_box->x0;
878 view_height = view_box->y1 - view_box->y0;
880 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
881 pcs2dev[0] = w / view_width;
882 pcs2dev[3] = h / view_height;
883 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
884 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
885 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
886 pcs2dev[0] = pbox->x1 - pbox->x0;
887 pcs2dev[3] = pbox->y1 - pbox->y0;
888 }
890 // calculate the size of the surface which has to be created
891 // the scaling needs to be taken into account in the ctm after the pattern transformation
892 NRMatrix temp;
893 nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
894 double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
895 double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
897 if (_vector_based_target) {
898 // eliminate PT_PER_PX mul from these
899 width_scaler *= 1.25;
900 height_scaler *= 1.25;
901 }
902 double surface_width = ceil(bbox_width_scaler * width_scaler * width);
903 double surface_height = ceil(bbox_height_scaler * height_scaler * height);
904 TRACE(("surface size: %f x %f\n", surface_width, surface_height));
905 // create new rendering context
906 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
908 // adjust the size of the painted pattern to fit exactly the created surface
909 // this has to be done because of the rounding to obtain an integer pattern surface width/height
910 double scale_width = surface_width / (bbox_width_scaler * width);
911 double scale_height = surface_height / (bbox_height_scaler * height);
912 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
913 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
914 NRMatrix scale;
915 nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
916 nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
918 nr_matrix_set_scale(&scale, scale_width, scale_height);
919 nr_matrix_multiply(&ps2user, &ps2user, &scale);
920 }
922 pattern_ctx->setTransform(&pcs2dev);
923 pattern_ctx->pushState();
925 // create arena and group
926 NRArena *arena = NRArena::create();
927 unsigned dkey = sp_item_display_key_new(1);
929 // show items and render them
930 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
931 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
932 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
933 if (SP_IS_ITEM (child)) {
934 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
935 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
936 }
937 }
938 break; // do not go further up the chain if children are found
939 }
940 }
942 pattern_ctx->popState();
944 // setup a cairo_pattern_t
945 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
946 TEST(pattern_ctx->saveAsPng("pattern.png"));
947 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
948 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
950 // set pattern transformation
951 cairo_matrix_t pattern_matrix;
952 _initCairoMatrix(&pattern_matrix, &ps2user);
953 cairo_matrix_invert(&pattern_matrix);
954 cairo_pattern_set_matrix(result, &pattern_matrix);
956 delete pattern_ctx;
958 // hide all items
959 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
960 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
961 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
962 if (SP_IS_ITEM (child)) {
963 sp_item_invoke_hide (SP_ITEM (child), dkey);
964 }
965 }
966 break; // do not go further up the chain if children are found
967 }
968 }
970 return result;
971 }
973 cairo_pattern_t*
974 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
975 NRRect const *pbox, float alpha)
976 {
977 cairo_pattern_t *pattern = NULL;
978 bool apply_bbox2user = FALSE;
980 if (SP_IS_LINEARGRADIENT (paintserver)) {
982 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
984 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
986 NR::Point p1 (lg->x1.computed, lg->y1.computed);
987 NR::Point p2 (lg->x2.computed, lg->y2.computed);
988 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
989 // convert to userspace
990 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
991 p1 *= bbox2user;
992 p2 *= bbox2user;
993 }
995 // create linear gradient pattern
996 pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
998 // add stops
999 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1000 float rgb[3];
1001 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1002 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1003 }
1004 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1006 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1008 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1010 NR::Point c (rg->cx.computed, rg->cy.computed);
1011 NR::Point f (rg->fx.computed, rg->fy.computed);
1012 double r = rg->r.computed;
1013 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1014 apply_bbox2user = true;
1016 // create radial gradient pattern
1017 pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1019 // add stops
1020 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1021 float rgb[3];
1022 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1023 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1024 }
1025 } else if (SP_IS_PATTERN (paintserver)) {
1027 pattern = _createPatternPainter(paintserver, pbox);
1028 } else {
1029 return NULL;
1030 }
1032 if (pattern && SP_IS_GRADIENT (paintserver)) {
1033 SPGradient *g = SP_GRADIENT(paintserver);
1035 // set extend type
1036 SPGradientSpread spread = sp_gradient_get_spread(g);
1037 switch (spread) {
1038 case SP_GRADIENT_SPREAD_REPEAT: {
1039 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1040 break;
1041 }
1042 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1043 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1044 break;
1045 }
1046 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1047 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1048 break;
1049 }
1050 default: {
1051 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1052 break;
1053 }
1054 }
1056 cairo_matrix_t pattern_matrix;
1057 if (g->gradientTransform_set) {
1058 // apply gradient transformation
1059 cairo_matrix_init(&pattern_matrix,
1060 g->gradientTransform[0], g->gradientTransform[1],
1061 g->gradientTransform[2], g->gradientTransform[3],
1062 g->gradientTransform[4], g->gradientTransform[5]);
1063 } else {
1064 cairo_matrix_init_identity (&pattern_matrix);
1065 }
1067 if (apply_bbox2user) {
1068 // convert to userspace
1069 cairo_matrix_t bbox2user;
1070 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1071 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1072 }
1073 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1074 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1075 }
1077 return pattern;
1078 }
1080 void
1081 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1082 {
1083 g_return_if_fail( style->fill.isColor()
1084 || style->fill.isPaintserver() );
1086 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1087 if (_state->merge_opacity) {
1088 alpha *= _state->opacity;
1089 TRACE(("merged op=%f\n", alpha));
1090 }
1092 if (style->fill.isColor()) {
1093 float rgb[3];
1094 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1096 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1097 } else {
1098 g_assert( style->fill.isPaintserver()
1099 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1100 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1102 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1104 if (pattern) {
1105 cairo_set_source(_cr, pattern);
1106 cairo_pattern_destroy(pattern);
1107 }
1108 }
1109 }
1111 void
1112 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1113 {
1114 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1115 if (_state->merge_opacity)
1116 alpha *= _state->opacity;
1118 if (style->stroke.isColor()) {
1119 float rgb[3];
1120 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1122 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1123 } else {
1124 g_assert( style->fill.isPaintserver()
1125 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1126 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1128 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1130 if (pattern) {
1131 cairo_set_source(_cr, pattern);
1132 cairo_pattern_destroy(pattern);
1133 }
1134 }
1136 if (style->stroke_dash.n_dash &&
1137 style->stroke_dash.dash )
1138 {
1139 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1140 } else {
1141 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1142 }
1144 cairo_set_line_width(_cr, style->stroke_width.computed);
1146 // set line join type
1147 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1148 switch (style->stroke_linejoin.computed) {
1149 case SP_STROKE_LINEJOIN_MITER:
1150 join = CAIRO_LINE_JOIN_MITER;
1151 break;
1152 case SP_STROKE_LINEJOIN_ROUND:
1153 join = CAIRO_LINE_JOIN_ROUND;
1154 break;
1155 case SP_STROKE_LINEJOIN_BEVEL:
1156 join = CAIRO_LINE_JOIN_BEVEL;
1157 break;
1158 }
1159 cairo_set_line_join(_cr, join);
1161 // set line cap type
1162 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1163 switch (style->stroke_linecap.computed) {
1164 case SP_STROKE_LINECAP_BUTT:
1165 cap = CAIRO_LINE_CAP_BUTT;
1166 break;
1167 case SP_STROKE_LINECAP_ROUND:
1168 cap = CAIRO_LINE_CAP_ROUND;
1169 break;
1170 case SP_STROKE_LINECAP_SQUARE:
1171 cap = CAIRO_LINE_CAP_SQUARE;
1172 break;
1173 }
1174 cairo_set_line_cap(_cr, cap);
1175 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1176 }
1178 bool
1179 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1180 {
1181 g_assert( _is_valid );
1183 if (_render_mode == RENDER_MODE_CLIP) {
1184 if (_clip_mode == CLIP_MODE_PATH) {
1185 addClipPath(bpath->path, &style->fill_rule);
1186 } else {
1187 setBpath(bpath->path);
1188 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1189 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1190 } else {
1191 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1192 }
1193 cairo_fill(_cr);
1194 cairo_surface_write_to_png (_surface, "gtar2.png");
1195 }
1196 return true;
1197 }
1199 if (style->fill.isNone() && style->stroke.isNone())
1200 return true;
1202 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1203 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1205 if (!need_layer)
1206 cairo_save(_cr);
1207 else
1208 pushLayer();
1210 if (!style->fill.isNone()) {
1211 _setFillStyle(style, pbox);
1212 setBpath(bpath->path);
1214 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1215 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1216 } else {
1217 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1218 }
1220 if (style->stroke.isNone())
1221 cairo_fill(_cr);
1222 else
1223 cairo_fill_preserve(_cr);
1224 }
1226 if (!style->stroke.isNone()) {
1227 _setStrokeStyle(style, pbox);
1228 if (style->fill.isNone())
1229 setBpath(bpath->path);
1231 cairo_stroke(_cr);
1232 }
1234 if (need_layer)
1235 popLayer();
1236 else
1237 cairo_restore(_cr);
1239 return true;
1240 }
1242 bool
1243 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1244 NRMatrix const *image_transform, SPStyle const *style)
1245 {
1246 g_assert( _is_valid );
1248 if (_render_mode == RENDER_MODE_CLIP)
1249 return true;
1251 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1252 if (!px_rgba)
1253 return false;
1255 float opacity;
1256 if (_state->merge_opacity)
1257 opacity = _state->opacity;
1258 else
1259 opacity = 1.0;
1261 // make a copy of the original pixbuf with premultiplied alpha
1262 // if we pass the original pixbuf it will get messed up
1263 for (unsigned i = 0; i < h; i++) {
1264 for (unsigned j = 0; j < w; j++) {
1265 guchar const *src = px + i * rs + j * 4;
1266 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1267 guchar r, g, b, alpha_dst;
1269 // calculate opacity-modified alpha
1270 alpha_dst = src[3];
1271 if (opacity != 1.0 && _vector_based_target)
1272 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1274 // premul alpha (needed because this will be undone by cairo-pdf)
1275 r = src[0]*alpha_dst/255;
1276 g = src[1]*alpha_dst/255;
1277 b = src[2]*alpha_dst/255;
1279 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1280 }
1281 }
1283 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1284 if (cairo_surface_status(image_surface)) {
1285 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1286 return false;
1287 }
1289 // setup automatic freeing of the image data when destroying the surface
1290 static cairo_user_data_key_t key;
1291 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1293 cairo_save(_cr);
1295 // scaling by width & height is not needed because it will be done by Cairo
1296 if (image_transform)
1297 transform(image_transform);
1299 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1301 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1302 if (_vector_based_target) {
1303 cairo_new_path(_cr);
1304 cairo_rectangle(_cr, 0, 0, w, h);
1305 cairo_clip(_cr);
1306 }
1308 if (_vector_based_target)
1309 cairo_paint(_cr);
1310 else
1311 cairo_paint_with_alpha(_cr, opacity);
1313 cairo_restore(_cr);
1315 cairo_surface_destroy(image_surface);
1317 return true;
1318 }
1320 #define GLYPH_ARRAY_SIZE 64
1322 unsigned int
1323 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1324 {
1325 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1326 cairo_glyph_t *glyphs = glyph_array;
1327 unsigned int num_glyphs = glyphtext.size();
1328 if (num_glyphs > GLYPH_ARRAY_SIZE)
1329 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1331 unsigned int num_invalid_glyphs = 0;
1332 unsigned int i = 0;
1333 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1334 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1335 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1336 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1337 TRACE(("INVALID GLYPH found\n"));
1338 num_invalid_glyphs++;
1339 continue;
1340 }
1341 glyphs[i - num_invalid_glyphs].index = it_info->index;
1342 glyphs[i - num_invalid_glyphs].x = it_info->x;
1343 glyphs[i - num_invalid_glyphs].y = it_info->y;
1344 i++;
1345 }
1347 if (is_stroke)
1348 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1349 else
1350 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1352 if (num_glyphs > GLYPH_ARRAY_SIZE)
1353 g_free(glyphs);
1355 return num_glyphs - num_invalid_glyphs;
1356 }
1358 bool
1359 CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1360 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1361 {
1362 // create a cairo_font_face from PangoFont
1363 double size = style->font_size.computed;
1364 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1365 FcPattern *fc_pattern = fc_font->font_pattern;
1367 cairo_save(_cr);
1369 #ifndef PANGO_ENABLE_BACKEND
1370 cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1371 cairo_set_font_face(_cr, font_face);
1373 if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1374 size = 12.0;
1376 // set the given font matrix
1377 cairo_matrix_t matrix;
1378 _initCairoMatrix(&matrix, font_matrix);
1379 cairo_set_font_matrix(_cr, &matrix);
1381 if (_render_mode == RENDER_MODE_CLIP) {
1382 if (_clip_mode == CLIP_MODE_MASK) {
1383 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1384 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1385 } else {
1386 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1387 }
1388 cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
1389 cairo_rectangle (_cr, 0, 0, 30, 40);
1390 cairo_fill (_cr);
1391 _showGlyphs(_cr, font, glyphtext, FALSE);
1392 //cairo_fill(_cr);
1393 } else {
1394 // just add the glyph paths to the current context
1395 _showGlyphs(_cr, font, glyphtext, TRUE);
1396 }
1397 } else {
1399 if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
1400 // set fill style
1401 _setFillStyle(style, NULL);
1403 _showGlyphs(_cr, font, glyphtext, FALSE);
1404 }
1406 if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
1407 // set stroke style
1408 _setStrokeStyle(style, NULL);
1410 // paint stroke
1411 _showGlyphs(_cr, font, glyphtext, TRUE);
1412 cairo_stroke(_cr);
1413 }
1414 }
1416 cairo_restore(_cr);
1418 cairo_font_face_destroy(font_face);
1419 #else
1420 (void)size;
1421 (void)fc_pattern;
1422 #endif
1424 return true;
1425 }
1427 /* Helper functions */
1429 void
1430 CairoRenderContext::addBpath(NArtBpath const *bp)
1431 {
1432 bool closed = false;
1433 while (bp->code != NR_END) {
1434 switch (bp->code) {
1435 case NR_MOVETO:
1436 if (closed) {
1437 cairo_close_path(_cr);
1438 }
1439 closed = true;
1440 cairo_move_to(_cr, bp->x3, bp->y3);
1441 break;
1442 case NR_MOVETO_OPEN:
1443 if (closed) {
1444 cairo_close_path(_cr);
1445 }
1446 closed = false;
1447 cairo_move_to(_cr, bp->x3, bp->y3);
1448 break;
1449 case NR_LINETO:
1450 cairo_line_to(_cr, bp->x3, bp->y3);
1451 break;
1452 case NR_CURVETO:
1453 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1454 break;
1455 default:
1456 break;
1457 }
1458 bp += 1;
1459 }
1460 if (closed) {
1461 cairo_close_path(_cr);
1462 }
1463 }
1465 void
1466 CairoRenderContext::setBpath(NArtBpath const *bp)
1467 {
1468 cairo_new_path(_cr);
1469 if (bp)
1470 addBpath(bp);
1471 }
1473 void
1474 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1475 {
1476 cairo_matrix_t matrix;
1478 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1479 cairo_transform(cr, &matrix);
1480 }
1482 void
1483 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1484 {
1485 matrix->xx = (*transform)[0];
1486 matrix->yx = (*transform)[1];
1487 matrix->xy = (*transform)[2];
1488 matrix->yy = (*transform)[3];
1489 matrix->x0 = (*transform)[4];
1490 matrix->y0 = (*transform)[5];
1491 }
1493 void
1494 CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1495 {
1496 _concatTransform(cr, (*transform)[0], (*transform)[1],
1497 (*transform)[2], (*transform)[3],
1498 (*transform)[4], (*transform)[5]);
1499 }
1501 static cairo_status_t
1502 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1503 {
1504 size_t written;
1505 FILE *file = (FILE*)closure;
1507 written = fwrite (data, 1, length, file);
1509 if (written == length)
1510 return CAIRO_STATUS_SUCCESS;
1511 else
1512 return CAIRO_STATUS_WRITE_ERROR;
1513 }
1515 #include "clear-n_.h"
1517 } /* namespace Internal */
1518 } /* namespace Extension */
1519 } /* namespace Inkscape */
1521 #undef TRACE
1522 #undef TEST
1524 /* End of GNU GPL code */
1527 /*
1528 Local Variables:
1529 mode:c++
1530 c-file-style:"stroustrup"
1531 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1532 indent-tabs-mode:nil
1533 fill-column:99
1534 End:
1535 */
1536 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :