228f596dd375ca89bad540c6396ef1f7f93e289d
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 _pdf_level(0),
108 _ps_level(1),
109 _is_texttopath(FALSE),
110 _is_filtertobitmap(FALSE),
111 _stream(NULL),
112 _is_valid(FALSE),
113 _vector_based_target(FALSE),
114 _cr(NULL),
115 _surface(NULL),
116 _target(CAIRO_SURFACE_TYPE_IMAGE),
117 _target_format(CAIRO_FORMAT_ARGB32),
118 _layout(NULL),
119 _state(NULL),
120 _renderer(parent),
121 _render_mode(RENDER_MODE_NORMAL),
122 _clip_mode(CLIP_MODE_MASK)
123 {}
125 CairoRenderContext::~CairoRenderContext(void)
126 {
127 if (_cr) cairo_destroy(_cr);
128 if (_surface) cairo_surface_destroy(_surface);
129 if (_layout) g_object_unref(_layout);
130 }
132 CairoRenderer*
133 CairoRenderContext::getRenderer(void) const
134 {
135 return _renderer;
136 }
138 CairoRenderState*
139 CairoRenderContext::getCurrentState(void) const
140 {
141 return _state;
142 }
144 CairoRenderState*
145 CairoRenderContext::getParentState(void) const
146 {
147 // if this is the root node just return it
148 if (g_slist_length(_state_stack) == 1) {
149 return _state;
150 } else {
151 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
152 }
153 }
155 void
156 CairoRenderContext::setStateForStyle(SPStyle const *style)
157 {
158 // only opacity & overflow is stored for now
159 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
160 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
161 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
163 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
164 _state->merge_opacity = FALSE;
166 // disable rendering of opacity if there's a stroke on the fill
167 if (_state->merge_opacity
168 && !style->fill.isNone()
169 && !style->stroke.isNone())
170 _state->merge_opacity = FALSE;
171 }
173 /**
174 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
175 *
176 * \param width width of the surface to be created
177 * \param height height of the surface to be created
178 */
179 CairoRenderContext*
180 CairoRenderContext::cloneMe(double width, double height) const
181 {
182 g_assert( _is_valid );
183 g_assert( width > 0.0 && height > 0.0 );
185 CairoRenderContext *new_context = _renderer->createContext();
186 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
187 (int)ceil(width), (int)ceil(height));
188 new_context->_cr = cairo_create(surface);
189 new_context->_surface = surface;
190 new_context->_is_valid = TRUE;
192 return new_context;
193 }
195 CairoRenderContext*
196 CairoRenderContext::cloneMe(void) const
197 {
198 g_assert( _is_valid );
200 return cloneMe(_width, _height);
201 }
203 bool
204 CairoRenderContext::setImageTarget(cairo_format_t format)
205 {
206 // format cannot be set on an already initialized surface
207 if (_is_valid)
208 return false;
210 switch (format) {
211 case CAIRO_FORMAT_ARGB32:
212 case CAIRO_FORMAT_RGB24:
213 case CAIRO_FORMAT_A8:
214 case CAIRO_FORMAT_A1:
215 _target_format = format;
216 _target = CAIRO_SURFACE_TYPE_IMAGE;
217 return true;
218 break;
219 default:
220 break;
221 }
223 return false;
224 }
226 bool
227 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
228 {
229 #ifndef CAIRO_HAS_PDF_SURFACE
230 return false;
231 #else
232 _target = CAIRO_SURFACE_TYPE_PDF;
233 _vector_based_target = TRUE;
234 #endif
236 FILE *osf = NULL;
237 FILE *osp = NULL;
239 gsize bytesRead = 0;
240 gsize bytesWritten = 0;
241 GError *error = NULL;
242 gchar *local_fn = g_filename_from_utf8(utf8_fn,
243 -1, &bytesRead, &bytesWritten, &error);
244 gchar const *fn = local_fn;
246 /* TODO: Replace the below fprintf's with something that does the right thing whether in
247 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
248 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
249 * return code.
250 */
251 if (fn != NULL) {
252 if (*fn == '|') {
253 fn += 1;
254 while (isspace(*fn)) fn += 1;
255 #ifndef WIN32
256 osp = popen(fn, "w");
257 #else
258 osp = _popen(fn, "w");
259 #endif
260 if (!osp) {
261 fprintf(stderr, "inkscape: popen(%s): %s\n",
262 fn, strerror(errno));
263 return false;
264 }
265 _stream = osp;
266 } else if (*fn == '>') {
267 fn += 1;
268 while (isspace(*fn)) fn += 1;
269 Inkscape::IO::dump_fopen_call(fn, "K");
270 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
271 if (!osf) {
272 fprintf(stderr, "inkscape: fopen(%s): %s\n",
273 fn, strerror(errno));
274 return false;
275 }
276 _stream = osf;
277 } else {
278 /* put cwd stuff in here */
279 gchar *qn = ( *fn
280 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
281 : g_strdup("lpr") );
282 #ifndef WIN32
283 osp = popen(qn, "w");
284 #else
285 osp = _popen(qn, "w");
286 #endif
287 if (!osp) {
288 fprintf(stderr, "inkscape: popen(%s): %s\n",
289 qn, strerror(errno));
290 return false;
291 }
292 g_free(qn);
293 _stream = osp;
294 }
295 }
297 g_free(local_fn);
299 if (_stream) {
300 /* fixme: this is kinda icky */
301 #if !defined(_WIN32) && !defined(__WIN32__)
302 (void) signal(SIGPIPE, SIG_IGN);
303 #endif
304 }
306 return true;
307 }
309 bool
310 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
311 {
312 #ifndef CAIRO_HAS_PS_SURFACE
313 return false;
314 #else
315 _target = CAIRO_SURFACE_TYPE_PS;
316 _vector_based_target = TRUE;
317 #endif
319 FILE *osf = NULL;
320 FILE *osp = NULL;
322 gsize bytesRead = 0;
323 gsize bytesWritten = 0;
324 GError *error = NULL;
325 gchar *local_fn = g_filename_from_utf8(utf8_fn,
326 -1, &bytesRead, &bytesWritten, &error);
327 gchar const *fn = local_fn;
329 /* TODO: Replace the below fprintf's with something that does the right thing whether in
330 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
331 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
332 * return code.
333 */
334 if (fn != NULL) {
335 if (*fn == '|') {
336 fn += 1;
337 while (isspace(*fn)) fn += 1;
338 #ifndef WIN32
339 osp = popen(fn, "w");
340 #else
341 osp = _popen(fn, "w");
342 #endif
343 if (!osp) {
344 fprintf(stderr, "inkscape: popen(%s): %s\n",
345 fn, strerror(errno));
346 return false;
347 }
348 _stream = osp;
349 } else if (*fn == '>') {
350 fn += 1;
351 while (isspace(*fn)) fn += 1;
352 Inkscape::IO::dump_fopen_call(fn, "K");
353 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
354 if (!osf) {
355 fprintf(stderr, "inkscape: fopen(%s): %s\n",
356 fn, strerror(errno));
357 return false;
358 }
359 _stream = osf;
360 } else {
361 /* put cwd stuff in here */
362 gchar *qn = ( *fn
363 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
364 : g_strdup("lpr") );
365 #ifndef WIN32
366 osp = popen(qn, "w");
367 #else
368 osp = _popen(qn, "w");
369 #endif
370 if (!osp) {
371 fprintf(stderr, "inkscape: popen(%s): %s\n",
372 qn, strerror(errno));
373 return false;
374 }
375 g_free(qn);
376 _stream = osp;
377 }
378 }
380 g_free(local_fn);
382 if (_stream) {
383 /* fixme: this is kinda icky */
384 #if !defined(_WIN32) && !defined(__WIN32__)
385 (void) signal(SIGPIPE, SIG_IGN);
386 #endif
387 }
389 return true;
390 }
392 void CairoRenderContext::setPSLevel(unsigned int level)
393 {
394 _ps_level = level;
395 }
397 void CairoRenderContext::setPDFLevel(unsigned int level)
398 {
399 _pdf_level = level;
400 }
402 void CairoRenderContext::setTextToPath(bool texttopath)
403 {
404 _is_texttopath = texttopath;
405 }
407 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
408 {
409 _is_filtertobitmap = filtertobitmap;
410 }
412 cairo_surface_t*
413 CairoRenderContext::getSurface(void)
414 {
415 g_assert( _is_valid );
417 return _surface;
418 }
420 bool
421 CairoRenderContext::saveAsPng(const char *file_name)
422 {
423 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
424 if (status)
425 return false;
426 else
427 return true;
428 }
430 void
431 CairoRenderContext::setRenderMode(CairoRenderMode mode)
432 {
433 switch (mode) {
434 case RENDER_MODE_NORMAL:
435 case RENDER_MODE_CLIP:
436 _render_mode = mode;
437 break;
438 default:
439 _render_mode = RENDER_MODE_NORMAL;
440 break;
441 }
442 }
444 CairoRenderContext::CairoRenderMode
445 CairoRenderContext::getRenderMode(void) const
446 {
447 return _render_mode;
448 }
450 void
451 CairoRenderContext::setClipMode(CairoClipMode mode)
452 {
453 switch (mode) {
454 case CLIP_MODE_PATH:
455 case CLIP_MODE_MASK:
456 _clip_mode = mode;
457 break;
458 default:
459 _clip_mode = CLIP_MODE_PATH;
460 break;
461 }
462 }
464 CairoRenderContext::CairoClipMode
465 CairoRenderContext::getClipMode(void) const
466 {
467 return _clip_mode;
468 }
470 CairoRenderState*
471 CairoRenderContext::_createState(void)
472 {
473 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
474 g_assert( state != NULL );
476 state->has_filtereffect = FALSE;
477 state->merge_opacity = TRUE;
478 state->opacity = 1.0;
479 state->need_layer = FALSE;
480 state->has_overflow = FALSE;
481 state->parent_has_userspace = FALSE;
482 state->clip_path = NULL;
483 state->mask = NULL;
485 return state;
486 }
488 void
489 CairoRenderContext::pushLayer(void)
490 {
491 g_assert( _is_valid );
493 TRACE(("--pushLayer\n"));
494 cairo_push_group(_cr);
496 // clear buffer
497 if (!_vector_based_target) {
498 cairo_save(_cr);
499 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
500 cairo_paint(_cr);
501 cairo_restore(_cr);
502 }
503 }
505 void
506 CairoRenderContext::popLayer(void)
507 {
508 g_assert( _is_valid );
510 float opacity = _state->opacity;
511 TRACE(("--popLayer w/ %f\n", opacity));
513 // apply clipPath or mask if present
514 SPClipPath *clip_path = _state->clip_path;
515 SPMask *mask = _state->mask;
516 if (clip_path || mask) {
518 CairoRenderContext *clip_ctx = 0;
519 cairo_surface_t *clip_mask = 0;
521 if (clip_path) {
522 if (_render_mode == RENDER_MODE_CLIP)
523 mask = NULL; // disable mask when performing nested clipping
525 if (_vector_based_target) {
526 setClipMode(CLIP_MODE_PATH);
527 if (!mask) {
528 cairo_pop_group_to_source(_cr);
529 _renderer->applyClipPath(this, clip_path);
530 if (opacity == 1.0)
531 cairo_paint(_cr);
532 else
533 cairo_paint_with_alpha(_cr, opacity);
535 } else {
536 // the clipPath will be applied before masking
537 }
538 } else {
540 // setup a new rendering context
541 clip_ctx = _renderer->createContext();
542 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
543 clip_ctx->setClipMode(CLIP_MODE_MASK);
544 if (!clip_ctx->setupSurface(_width, _height)) {
545 TRACE(("setupSurface failed\n"));
546 _renderer->destroyContext(clip_ctx);
547 return;
548 }
550 // clear buffer
551 cairo_save(clip_ctx->_cr);
552 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
553 cairo_paint(clip_ctx->_cr);
554 cairo_restore(clip_ctx->_cr);
556 // if a mask won't be applied set opacity too
557 if (!mask)
558 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
559 else
560 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
562 // copy over the correct CTM
563 if (_state->parent_has_userspace)
564 clip_ctx->setTransform(&getParentState()->transform);
565 else
566 clip_ctx->setTransform(&_state->transform);
568 // apply the clip path
569 clip_ctx->pushState();
570 _renderer->applyClipPath(clip_ctx, clip_path);
571 clip_ctx->popState();
573 clip_mask = clip_ctx->getSurface();
574 TEST(clip_ctx->saveAsPng("clip_mask.png"));
576 if (!mask) {
577 cairo_pop_group_to_source(_cr);
578 cairo_mask_surface(_cr, clip_mask, 0, 0);
579 _renderer->destroyContext(clip_ctx);
580 }
581 }
582 }
584 if (mask) {
585 // create rendering context for mask
586 CairoRenderContext *mask_ctx = _renderer->createContext();
587 mask_ctx->setupSurface(_width, _height);
589 // set rendering mode to normal
590 setRenderMode(RENDER_MODE_NORMAL);
592 // copy the correct CTM to mask context
593 if (_state->parent_has_userspace)
594 mask_ctx->setTransform(&getParentState()->transform);
595 else
596 mask_ctx->setTransform(&_state->transform);
598 // render mask contents to mask_ctx
599 _renderer->applyMask(mask_ctx, mask);
601 TEST(mask_ctx->saveAsPng("mask.png"));
603 // composite with clip mask
604 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
605 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
606 _renderer->destroyContext(clip_ctx);
607 }
609 cairo_surface_t *mask_image = mask_ctx->getSurface();
610 int width = cairo_image_surface_get_width(mask_image);
611 int height = cairo_image_surface_get_height(mask_image);
612 int stride = cairo_image_surface_get_stride(mask_image);
613 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
615 // premultiply with opacity
616 if (_state->opacity != 1.0) {
617 TRACE(("premul w/ %f\n", opacity));
618 guint8 int_opacity = (guint8)(255 * opacity);
619 for (int row = 0 ; row < height; row++) {
620 unsigned char *row_data = pixels + (row * stride);
621 for (int i = 0 ; i < width; i++) {
622 guint32 *pixel = (guint32 *)row_data + i;
623 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
624 ((*pixel & 0x0000ff00) >> 8) * 46518 +
625 ((*pixel & 0x000000ff) ) * 4688) *
626 int_opacity);
627 }
628 }
629 }
631 cairo_pop_group_to_source(_cr);
632 if (_clip_mode == CLIP_MODE_PATH) {
633 // we have to do the clipping after cairo_pop_group_to_source
634 _renderer->applyClipPath(this, clip_path);
635 }
636 // apply the mask onto the layer
637 cairo_mask_surface(_cr, mask_image, 0, 0);
638 _renderer->destroyContext(mask_ctx);
639 }
640 } else {
641 cairo_pop_group_to_source(_cr);
642 if (opacity == 1.0)
643 cairo_paint(_cr);
644 else
645 cairo_paint_with_alpha(_cr, opacity);
646 }
647 }
649 void
650 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
651 {
652 g_assert( _is_valid );
654 // here it should be checked whether the current clip winding changed
655 // so we could switch back to masked clipping
656 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
657 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
658 } else {
659 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
660 }
661 addBpath(bp);
662 }
664 void
665 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
666 {
667 g_assert( _is_valid );
669 cairo_rectangle(_cr, x, y, width, height);
670 cairo_clip(_cr);
671 }
673 bool
674 CairoRenderContext::setupSurface(double width, double height)
675 {
676 // Is the surface already set up?
677 if (_is_valid)
678 return true;
680 if (_vector_based_target && _stream == NULL)
681 return false;
683 cairo_surface_t *surface = NULL;
684 switch (_target) {
685 case CAIRO_SURFACE_TYPE_IMAGE:
686 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
687 break;
688 #ifdef CAIRO_HAS_PDF_SURFACE
689 case CAIRO_SURFACE_TYPE_PDF:
690 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
691 break;
692 #endif
693 #ifdef CAIRO_HAS_PS_SURFACE
694 case CAIRO_SURFACE_TYPE_PS:
695 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
696 #if (CAIRO_VERSION >= 010502)
697 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
698 #endif
699 break;
700 #endif
701 default:
702 return false;
703 break;
704 }
706 return _finishSurfaceSetup (surface);
707 }
709 bool
710 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
711 {
712 if (_is_valid || !surface)
713 return false;
715 _vector_based_target = is_vector;
716 bool ret = _finishSurfaceSetup (surface);
717 if (ret)
718 cairo_surface_reference (surface);
719 return ret;
720 }
722 bool
723 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
724 {
725 _cr = cairo_create(surface);
726 _surface = surface;
728 if (_vector_based_target) {
729 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
730 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
731 // set background color on non-alpha surfaces
732 // TODO: bgcolor should be derived from SPDocument
733 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
734 cairo_rectangle(_cr, 0, 0, _width, _height);
735 cairo_fill(_cr);
736 }
738 _is_valid = TRUE;
740 return true;
741 }
743 bool
744 CairoRenderContext::finish(void)
745 {
746 g_assert( _is_valid );
748 if (_vector_based_target)
749 cairo_show_page(_cr);
751 cairo_destroy(_cr);
752 cairo_surface_destroy(_surface);
753 _cr = NULL;
754 _surface = NULL;
756 if (_layout)
757 g_object_unref(_layout);
759 _is_valid = FALSE;
761 if (_vector_based_target && _stream) {
762 /* Flush stream to be sure. */
763 (void) fflush(_stream);
765 fclose(_stream);
766 _stream = NULL;
767 }
769 return true;
770 }
772 void
773 CairoRenderContext::transform(NRMatrix const *transform)
774 {
775 g_assert( _is_valid );
777 cairo_matrix_t matrix;
778 _initCairoMatrix(&matrix, transform);
779 cairo_transform(_cr, &matrix);
781 // store new CTM
782 getTransform(&_state->transform);
783 }
785 void
786 CairoRenderContext::setTransform(NRMatrix const *transform)
787 {
788 g_assert( _is_valid );
790 cairo_matrix_t matrix;
791 _initCairoMatrix(&matrix, transform);
792 cairo_set_matrix(_cr, &matrix);
793 _state->transform = *transform;
794 }
796 void
797 CairoRenderContext::getTransform(NRMatrix *copy) const
798 {
799 g_assert( _is_valid );
801 cairo_matrix_t ctm;
802 cairo_get_matrix(_cr, &ctm);
803 (*copy)[0] = ctm.xx;
804 (*copy)[1] = ctm.yx;
805 (*copy)[2] = ctm.xy;
806 (*copy)[3] = ctm.yy;
807 (*copy)[4] = ctm.x0;
808 (*copy)[5] = ctm.y0;
809 }
811 void
812 CairoRenderContext::getParentTransform(NRMatrix *copy) const
813 {
814 g_assert( _is_valid );
816 CairoRenderState *parent_state = getParentState();
817 memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
818 }
820 void
821 CairoRenderContext::pushState(void)
822 {
823 g_assert( _is_valid );
825 cairo_save(_cr);
827 CairoRenderState *new_state = _createState();
828 // copy current state's transform
829 new_state->transform = _state->transform;
830 _state_stack = g_slist_prepend(_state_stack, new_state);
831 _state = new_state;
832 }
834 void
835 CairoRenderContext::popState(void)
836 {
837 g_assert( _is_valid );
839 cairo_restore(_cr);
841 g_free(_state_stack->data);
842 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
843 _state = (CairoRenderState*)_state_stack->data;
845 g_assert( g_slist_length(_state_stack) > 0 );
846 }
848 static bool pattern_hasItemChildren (SPPattern *pat)
849 {
850 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
851 if (SP_IS_ITEM (child)) {
852 return true;
853 }
854 }
855 return false;
856 }
858 cairo_pattern_t*
859 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
860 {
861 g_assert( SP_IS_PATTERN(paintserver) );
863 SPPattern *pat = SP_PATTERN (paintserver);
865 NRMatrix ps2user, pcs2dev;
866 nr_matrix_set_identity(&ps2user);
867 nr_matrix_set_identity(&pcs2dev);
869 double x = pattern_x(pat);
870 double y = pattern_y(pat);
871 double width = pattern_width(pat);
872 double height = pattern_height(pat);
873 double bbox_width_scaler;
874 double bbox_height_scaler;
876 TRACE(("%f x %f pattern\n", width, height));
878 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
879 //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
880 bbox_width_scaler = pbox->x1 - pbox->x0;
881 bbox_height_scaler = pbox->y1 - pbox->y0;
882 ps2user[4] = x * bbox_width_scaler + pbox->x0;
883 ps2user[5] = y * bbox_height_scaler + pbox->y0;
884 } else {
885 bbox_width_scaler = 1.0;
886 bbox_height_scaler = 1.0;
887 ps2user[4] = x;
888 ps2user[5] = y;
889 }
891 // apply pattern transformation
892 NRMatrix pattern_transform(pattern_patternTransform(pat));
893 nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
895 // create pattern contents coordinate system
896 if (pat->viewBox_set) {
897 NRRect *view_box = pattern_viewBox(pat);
899 double x, y, w, h;
900 double view_width, view_height;
901 x = 0;
902 y = 0;
903 w = width * bbox_width_scaler;
904 h = height * bbox_height_scaler;
906 view_width = view_box->x1 - view_box->x0;
907 view_height = view_box->y1 - view_box->y0;
909 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
910 pcs2dev[0] = w / view_width;
911 pcs2dev[3] = h / view_height;
912 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
913 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
914 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
915 pcs2dev[0] = pbox->x1 - pbox->x0;
916 pcs2dev[3] = pbox->y1 - pbox->y0;
917 }
919 // calculate the size of the surface which has to be created
920 // the scaling needs to be taken into account in the ctm after the pattern transformation
921 NRMatrix temp;
922 nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
923 double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
924 double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
926 if (_vector_based_target) {
927 // eliminate PT_PER_PX mul from these
928 width_scaler *= 1.25;
929 height_scaler *= 1.25;
930 }
931 double surface_width = ceil(bbox_width_scaler * width_scaler * width);
932 double surface_height = ceil(bbox_height_scaler * height_scaler * height);
933 TRACE(("surface size: %f x %f\n", surface_width, surface_height));
934 // create new rendering context
935 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
937 // adjust the size of the painted pattern to fit exactly the created surface
938 // this has to be done because of the rounding to obtain an integer pattern surface width/height
939 double scale_width = surface_width / (bbox_width_scaler * width);
940 double scale_height = surface_height / (bbox_height_scaler * height);
941 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
942 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
943 NRMatrix scale;
944 nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
945 nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
947 nr_matrix_set_scale(&scale, scale_width, scale_height);
948 nr_matrix_multiply(&ps2user, &ps2user, &scale);
949 }
951 pattern_ctx->setTransform(&pcs2dev);
952 pattern_ctx->pushState();
954 // create arena and group
955 NRArena *arena = NRArena::create();
956 unsigned dkey = sp_item_display_key_new(1);
958 // show items and render them
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_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
964 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
965 }
966 }
967 break; // do not go further up the chain if children are found
968 }
969 }
971 pattern_ctx->popState();
973 // setup a cairo_pattern_t
974 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
975 TEST(pattern_ctx->saveAsPng("pattern.png"));
976 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
977 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
979 // set pattern transformation
980 cairo_matrix_t pattern_matrix;
981 _initCairoMatrix(&pattern_matrix, &ps2user);
982 cairo_matrix_invert(&pattern_matrix);
983 cairo_pattern_set_matrix(result, &pattern_matrix);
985 delete pattern_ctx;
987 // hide all items
988 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
989 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
990 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
991 if (SP_IS_ITEM (child)) {
992 sp_item_invoke_hide (SP_ITEM (child), dkey);
993 }
994 }
995 break; // do not go further up the chain if children are found
996 }
997 }
999 return result;
1000 }
1002 cairo_pattern_t*
1003 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1004 NRRect const *pbox, float alpha)
1005 {
1006 cairo_pattern_t *pattern = NULL;
1007 bool apply_bbox2user = FALSE;
1009 if (SP_IS_LINEARGRADIENT (paintserver)) {
1011 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1013 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1015 NR::Point p1 (lg->x1.computed, lg->y1.computed);
1016 NR::Point p2 (lg->x2.computed, lg->y2.computed);
1017 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1018 // convert to userspace
1019 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1020 p1 *= bbox2user;
1021 p2 *= bbox2user;
1022 }
1024 // create linear gradient pattern
1025 pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1027 // add stops
1028 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1029 float rgb[3];
1030 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1031 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1032 }
1033 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1035 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1037 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1039 NR::Point c (rg->cx.computed, rg->cy.computed);
1040 NR::Point f (rg->fx.computed, rg->fy.computed);
1041 double r = rg->r.computed;
1042 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1043 apply_bbox2user = true;
1045 // create radial gradient pattern
1046 pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1048 // add stops
1049 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1050 float rgb[3];
1051 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1052 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1053 }
1054 } else if (SP_IS_PATTERN (paintserver)) {
1056 pattern = _createPatternPainter(paintserver, pbox);
1057 } else {
1058 return NULL;
1059 }
1061 if (pattern && SP_IS_GRADIENT (paintserver)) {
1062 SPGradient *g = SP_GRADIENT(paintserver);
1064 // set extend type
1065 SPGradientSpread spread = sp_gradient_get_spread(g);
1066 switch (spread) {
1067 case SP_GRADIENT_SPREAD_REPEAT: {
1068 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1069 break;
1070 }
1071 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1072 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1073 break;
1074 }
1075 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1076 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1077 break;
1078 }
1079 default: {
1080 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1081 break;
1082 }
1083 }
1085 cairo_matrix_t pattern_matrix;
1086 if (g->gradientTransform_set) {
1087 // apply gradient transformation
1088 cairo_matrix_init(&pattern_matrix,
1089 g->gradientTransform[0], g->gradientTransform[1],
1090 g->gradientTransform[2], g->gradientTransform[3],
1091 g->gradientTransform[4], g->gradientTransform[5]);
1092 } else {
1093 cairo_matrix_init_identity (&pattern_matrix);
1094 }
1096 if (apply_bbox2user) {
1097 // convert to userspace
1098 cairo_matrix_t bbox2user;
1099 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1100 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1101 }
1102 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1103 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1104 }
1106 return pattern;
1107 }
1109 void
1110 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1111 {
1112 g_return_if_fail( style->fill.isColor()
1113 || style->fill.isPaintserver() );
1115 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1116 if (_state->merge_opacity) {
1117 alpha *= _state->opacity;
1118 TRACE(("merged op=%f\n", alpha));
1119 }
1121 if (style->fill.isColor()) {
1122 float rgb[3];
1123 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1125 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1126 } else {
1127 g_assert( style->fill.isPaintserver()
1128 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1129 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1131 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1133 if (pattern) {
1134 cairo_set_source(_cr, pattern);
1135 cairo_pattern_destroy(pattern);
1136 }
1137 }
1138 }
1140 void
1141 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1142 {
1143 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1144 if (_state->merge_opacity)
1145 alpha *= _state->opacity;
1147 if (style->stroke.isColor()) {
1148 float rgb[3];
1149 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1151 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1152 } else {
1153 g_assert( style->fill.isPaintserver()
1154 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1155 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1157 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1159 if (pattern) {
1160 cairo_set_source(_cr, pattern);
1161 cairo_pattern_destroy(pattern);
1162 }
1163 }
1165 if (style->stroke_dash.n_dash &&
1166 style->stroke_dash.dash )
1167 {
1168 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1169 } else {
1170 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1171 }
1173 cairo_set_line_width(_cr, style->stroke_width.computed);
1175 // set line join type
1176 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1177 switch (style->stroke_linejoin.computed) {
1178 case SP_STROKE_LINEJOIN_MITER:
1179 join = CAIRO_LINE_JOIN_MITER;
1180 break;
1181 case SP_STROKE_LINEJOIN_ROUND:
1182 join = CAIRO_LINE_JOIN_ROUND;
1183 break;
1184 case SP_STROKE_LINEJOIN_BEVEL:
1185 join = CAIRO_LINE_JOIN_BEVEL;
1186 break;
1187 }
1188 cairo_set_line_join(_cr, join);
1190 // set line cap type
1191 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1192 switch (style->stroke_linecap.computed) {
1193 case SP_STROKE_LINECAP_BUTT:
1194 cap = CAIRO_LINE_CAP_BUTT;
1195 break;
1196 case SP_STROKE_LINECAP_ROUND:
1197 cap = CAIRO_LINE_CAP_ROUND;
1198 break;
1199 case SP_STROKE_LINECAP_SQUARE:
1200 cap = CAIRO_LINE_CAP_SQUARE;
1201 break;
1202 }
1203 cairo_set_line_cap(_cr, cap);
1204 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1205 }
1207 bool
1208 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1209 {
1210 g_assert( _is_valid );
1212 if (_render_mode == RENDER_MODE_CLIP) {
1213 if (_clip_mode == CLIP_MODE_PATH) {
1214 addClipPath(bpath->path, &style->fill_rule);
1215 } else {
1216 setBpath(bpath->path);
1217 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1218 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1219 } else {
1220 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1221 }
1222 cairo_fill(_cr);
1223 cairo_surface_write_to_png (_surface, "gtar2.png");
1224 }
1225 return true;
1226 }
1228 if (style->fill.isNone() && style->stroke.isNone())
1229 return true;
1231 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1232 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1234 if (!need_layer)
1235 cairo_save(_cr);
1236 else
1237 pushLayer();
1239 if (!style->fill.isNone()) {
1240 _setFillStyle(style, pbox);
1241 setBpath(bpath->path);
1243 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1244 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1245 } else {
1246 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1247 }
1249 if (style->stroke.isNone())
1250 cairo_fill(_cr);
1251 else
1252 cairo_fill_preserve(_cr);
1253 }
1255 if (!style->stroke.isNone()) {
1256 _setStrokeStyle(style, pbox);
1257 if (style->fill.isNone())
1258 setBpath(bpath->path);
1260 cairo_stroke(_cr);
1261 }
1263 if (need_layer)
1264 popLayer();
1265 else
1266 cairo_restore(_cr);
1268 return true;
1269 }
1271 bool
1272 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1273 NRMatrix const *image_transform, SPStyle const *style)
1274 {
1275 g_assert( _is_valid );
1277 if (_render_mode == RENDER_MODE_CLIP)
1278 return true;
1280 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1281 if (!px_rgba)
1282 return false;
1284 float opacity;
1285 if (_state->merge_opacity)
1286 opacity = _state->opacity;
1287 else
1288 opacity = 1.0;
1290 // make a copy of the original pixbuf with premultiplied alpha
1291 // if we pass the original pixbuf it will get messed up
1292 for (unsigned i = 0; i < h; i++) {
1293 for (unsigned j = 0; j < w; j++) {
1294 guchar const *src = px + i * rs + j * 4;
1295 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1296 guchar r, g, b, alpha_dst;
1298 // calculate opacity-modified alpha
1299 alpha_dst = src[3];
1300 if (opacity != 1.0 && _vector_based_target)
1301 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1303 // premul alpha (needed because this will be undone by cairo-pdf)
1304 r = src[0]*alpha_dst/255;
1305 g = src[1]*alpha_dst/255;
1306 b = src[2]*alpha_dst/255;
1308 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1309 }
1310 }
1312 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1313 if (cairo_surface_status(image_surface)) {
1314 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1315 return false;
1316 }
1318 // setup automatic freeing of the image data when destroying the surface
1319 static cairo_user_data_key_t key;
1320 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1322 cairo_save(_cr);
1324 // scaling by width & height is not needed because it will be done by Cairo
1325 if (image_transform)
1326 transform(image_transform);
1328 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1330 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1331 if (_vector_based_target) {
1332 cairo_new_path(_cr);
1333 cairo_rectangle(_cr, 0, 0, w, h);
1334 cairo_clip(_cr);
1335 }
1337 if (_vector_based_target)
1338 cairo_paint(_cr);
1339 else
1340 cairo_paint_with_alpha(_cr, opacity);
1342 cairo_restore(_cr);
1344 cairo_surface_destroy(image_surface);
1346 return true;
1347 }
1349 #define GLYPH_ARRAY_SIZE 64
1351 unsigned int
1352 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1353 {
1354 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1355 cairo_glyph_t *glyphs = glyph_array;
1356 unsigned int num_glyphs = glyphtext.size();
1357 if (num_glyphs > GLYPH_ARRAY_SIZE)
1358 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1360 unsigned int num_invalid_glyphs = 0;
1361 unsigned int i = 0;
1362 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1363 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1364 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1365 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1366 TRACE(("INVALID GLYPH found\n"));
1367 num_invalid_glyphs++;
1368 continue;
1369 }
1370 glyphs[i - num_invalid_glyphs].index = it_info->index;
1371 glyphs[i - num_invalid_glyphs].x = it_info->x;
1372 glyphs[i - num_invalid_glyphs].y = it_info->y;
1373 i++;
1374 }
1376 if (is_stroke || _is_texttopath)
1377 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1378 else
1379 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1381 if (num_glyphs > GLYPH_ARRAY_SIZE)
1382 g_free(glyphs);
1384 return num_glyphs - num_invalid_glyphs;
1385 }
1387 bool
1388 CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1389 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1390 {
1391 // create a cairo_font_face from PangoFont
1392 double size = style->font_size.computed;
1393 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1394 FcPattern *fc_pattern = fc_font->font_pattern;
1396 cairo_save(_cr);
1398 #ifndef PANGO_ENABLE_BACKEND
1399 cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1400 cairo_set_font_face(_cr, font_face);
1402 if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1403 size = 12.0;
1405 // set the given font matrix
1406 cairo_matrix_t matrix;
1407 _initCairoMatrix(&matrix, font_matrix);
1408 cairo_set_font_matrix(_cr, &matrix);
1410 if (_render_mode == RENDER_MODE_CLIP) {
1411 if (_clip_mode == CLIP_MODE_MASK) {
1412 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1413 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1414 } else {
1415 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1416 }
1417 cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
1418 cairo_rectangle (_cr, 0, 0, 30, 40);
1419 cairo_fill (_cr);
1420 _showGlyphs(_cr, font, glyphtext, FALSE);
1421 //cairo_fill(_cr);
1422 } else {
1423 // just add the glyph paths to the current context
1424 _showGlyphs(_cr, font, glyphtext, TRUE);
1425 }
1426 } else {
1428 if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
1429 // set fill style
1430 _setFillStyle(style, NULL);
1432 _showGlyphs(_cr, font, glyphtext, FALSE);
1433 }
1435 if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
1436 // set stroke style
1437 _setStrokeStyle(style, NULL);
1439 // paint stroke
1440 _showGlyphs(_cr, font, glyphtext, TRUE);
1441 cairo_stroke(_cr);
1442 }
1443 }
1445 cairo_restore(_cr);
1447 cairo_font_face_destroy(font_face);
1448 #else
1449 (void)size;
1450 (void)fc_pattern;
1451 #endif
1453 return true;
1454 }
1456 /* Helper functions */
1458 void
1459 CairoRenderContext::addBpath(NArtBpath const *bp)
1460 {
1461 bool closed = false;
1462 while (bp->code != NR_END) {
1463 switch (bp->code) {
1464 case NR_MOVETO:
1465 if (closed) {
1466 cairo_close_path(_cr);
1467 }
1468 closed = true;
1469 cairo_move_to(_cr, bp->x3, bp->y3);
1470 break;
1471 case NR_MOVETO_OPEN:
1472 if (closed) {
1473 cairo_close_path(_cr);
1474 }
1475 closed = false;
1476 cairo_move_to(_cr, bp->x3, bp->y3);
1477 break;
1478 case NR_LINETO:
1479 cairo_line_to(_cr, bp->x3, bp->y3);
1480 break;
1481 case NR_CURVETO:
1482 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1483 break;
1484 default:
1485 break;
1486 }
1487 bp += 1;
1488 }
1489 if (closed) {
1490 cairo_close_path(_cr);
1491 }
1492 }
1494 void
1495 CairoRenderContext::setBpath(NArtBpath const *bp)
1496 {
1497 cairo_new_path(_cr);
1498 if (bp)
1499 addBpath(bp);
1500 }
1502 void
1503 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1504 {
1505 cairo_matrix_t matrix;
1507 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1508 cairo_transform(cr, &matrix);
1509 }
1511 void
1512 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1513 {
1514 matrix->xx = (*transform)[0];
1515 matrix->yx = (*transform)[1];
1516 matrix->xy = (*transform)[2];
1517 matrix->yy = (*transform)[3];
1518 matrix->x0 = (*transform)[4];
1519 matrix->y0 = (*transform)[5];
1520 }
1522 void
1523 CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1524 {
1525 _concatTransform(cr, (*transform)[0], (*transform)[1],
1526 (*transform)[2], (*transform)[3],
1527 (*transform)[4], (*transform)[5]);
1528 }
1530 static cairo_status_t
1531 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1532 {
1533 size_t written;
1534 FILE *file = (FILE*)closure;
1536 written = fwrite (data, 1, length, file);
1538 if (written == length)
1539 return CAIRO_STATUS_SUCCESS;
1540 else
1541 return CAIRO_STATUS_WRITE_ERROR;
1542 }
1544 #include "clear-n_.h"
1546 } /* namespace Internal */
1547 } /* namespace Extension */
1548 } /* namespace Inkscape */
1550 #undef TRACE
1551 #undef TEST
1553 /* End of GNU GPL code */
1556 /*
1557 Local Variables:
1558 mode:c++
1559 c-file-style:"stroustrup"
1560 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1561 indent-tabs-mode:nil
1562 fill-column:99
1563 End:
1564 */
1565 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :