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