a040d5c099c387a9f0bce9ccc2d59abc1f52675c
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-scale-ops.h>
35 #include <libnr/nr-matrix-translate-ops.h>
36 #include <libnr/nr-scale-matrix-ops.h>
38 #include <glib/gmem.h>
40 #include <glibmm/i18n.h>
41 #include "display/nr-arena.h"
42 #include "display/nr-arena-item.h"
43 #include "display/nr-arena-group.h"
44 #include "display/curve.h"
45 #include "display/canvas-bpath.h"
46 #include "sp-item.h"
47 #include "sp-item-group.h"
48 #include "style.h"
49 #include "sp-linear-gradient.h"
50 #include "sp-radial-gradient.h"
51 #include "sp-pattern.h"
52 #include "sp-mask.h"
53 #include "sp-clippath.h"
55 #include <unit-constants.h>
57 #include "cairo-render-context.h"
58 #include "cairo-renderer.h"
59 #include "extension/system.h"
61 #include "io/sys.h"
63 #include <cairo.h>
65 // include support for only the compiled-in surface types
66 #ifdef CAIRO_HAS_PDF_SURFACE
67 #include <cairo-pdf.h>
68 #endif
69 #ifdef CAIRO_HAS_PS_SURFACE
70 #include <cairo-ps.h>
71 #endif
74 #ifdef CAIRO_HAS_FT_FONT
75 #include <cairo-ft.h>
76 #endif
78 #include <pango/pangofc-fontmap.h>
80 //#define TRACE(_args) g_printf _args
81 #define TRACE(_args)
82 //#define TEST(_args) _args
83 #define TEST(_args)
85 // FIXME: expose these from sp-clippath/mask.cpp
86 struct SPClipPathView {
87 SPClipPathView *next;
88 unsigned int key;
89 NRArenaItem *arenaitem;
90 NRRect bbox;
91 };
93 struct SPMaskView {
94 SPMaskView *next;
95 unsigned int key;
96 NRArenaItem *arenaitem;
97 NRRect bbox;
98 };
100 namespace Inkscape {
101 namespace Extension {
102 namespace Internal {
104 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
106 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
107 _dpi(72),
108 _pdf_level(0),
109 _ps_level(1),
110 _is_texttopath(FALSE),
111 _is_filtertobitmap(FALSE),
112 _bitmapresolution(72),
113 _stream(NULL),
114 _is_valid(FALSE),
115 _vector_based_target(FALSE),
116 _cr(NULL),
117 _surface(NULL),
118 _target(CAIRO_SURFACE_TYPE_IMAGE),
119 _target_format(CAIRO_FORMAT_ARGB32),
120 _layout(NULL),
121 _state(NULL),
122 _renderer(parent),
123 _render_mode(RENDER_MODE_NORMAL),
124 _clip_mode(CLIP_MODE_MASK)
125 {}
127 CairoRenderContext::~CairoRenderContext(void)
128 {
129 if (_cr) cairo_destroy(_cr);
130 if (_surface) cairo_surface_destroy(_surface);
131 if (_layout) g_object_unref(_layout);
132 }
134 CairoRenderer*
135 CairoRenderContext::getRenderer(void) const
136 {
137 return _renderer;
138 }
140 CairoRenderState*
141 CairoRenderContext::getCurrentState(void) const
142 {
143 return _state;
144 }
146 CairoRenderState*
147 CairoRenderContext::getParentState(void) const
148 {
149 // if this is the root node just return it
150 if (g_slist_length(_state_stack) == 1) {
151 return _state;
152 } else {
153 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
154 }
155 }
157 void
158 CairoRenderContext::setStateForStyle(SPStyle const *style)
159 {
160 // only opacity & overflow is stored for now
161 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
162 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
163 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
165 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
166 _state->merge_opacity = FALSE;
168 // disable rendering of opacity if there's a stroke on the fill
169 if (_state->merge_opacity
170 && !style->fill.isNone()
171 && !style->stroke.isNone())
172 _state->merge_opacity = FALSE;
173 }
175 /**
176 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
177 *
178 * \param width width of the surface to be created
179 * \param height height of the surface to be created
180 */
181 CairoRenderContext*
182 CairoRenderContext::cloneMe(double width, double height) const
183 {
184 g_assert( _is_valid );
185 g_assert( width > 0.0 && height > 0.0 );
187 CairoRenderContext *new_context = _renderer->createContext();
188 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
189 (int)ceil(width), (int)ceil(height));
190 new_context->_cr = cairo_create(surface);
191 new_context->_surface = surface;
192 new_context->_is_valid = TRUE;
194 return new_context;
195 }
197 CairoRenderContext*
198 CairoRenderContext::cloneMe(void) const
199 {
200 g_assert( _is_valid );
202 return cloneMe(_width, _height);
203 }
205 bool
206 CairoRenderContext::setImageTarget(cairo_format_t format)
207 {
208 // format cannot be set on an already initialized surface
209 if (_is_valid)
210 return false;
212 switch (format) {
213 case CAIRO_FORMAT_ARGB32:
214 case CAIRO_FORMAT_RGB24:
215 case CAIRO_FORMAT_A8:
216 case CAIRO_FORMAT_A1:
217 _target_format = format;
218 _target = CAIRO_SURFACE_TYPE_IMAGE;
219 return true;
220 break;
221 default:
222 break;
223 }
225 return false;
226 }
228 bool
229 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
230 {
231 #ifndef CAIRO_HAS_PDF_SURFACE
232 return false;
233 #else
234 _target = CAIRO_SURFACE_TYPE_PDF;
235 _vector_based_target = TRUE;
236 #endif
238 FILE *osf = NULL;
239 FILE *osp = NULL;
241 gsize bytesRead = 0;
242 gsize bytesWritten = 0;
243 GError *error = NULL;
244 gchar *local_fn = g_filename_from_utf8(utf8_fn,
245 -1, &bytesRead, &bytesWritten, &error);
246 gchar const *fn = local_fn;
248 /* TODO: Replace the below fprintf's with something that does the right thing whether in
249 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
250 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
251 * return code.
252 */
253 if (fn != NULL) {
254 if (*fn == '|') {
255 fn += 1;
256 while (isspace(*fn)) fn += 1;
257 #ifndef WIN32
258 osp = popen(fn, "w");
259 #else
260 osp = _popen(fn, "w");
261 #endif
262 if (!osp) {
263 fprintf(stderr, "inkscape: popen(%s): %s\n",
264 fn, strerror(errno));
265 return false;
266 }
267 _stream = osp;
268 } else if (*fn == '>') {
269 fn += 1;
270 while (isspace(*fn)) fn += 1;
271 Inkscape::IO::dump_fopen_call(fn, "K");
272 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
273 if (!osf) {
274 fprintf(stderr, "inkscape: fopen(%s): %s\n",
275 fn, strerror(errno));
276 return false;
277 }
278 _stream = osf;
279 } else {
280 /* put cwd stuff in here */
281 gchar *qn = ( *fn
282 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
283 : g_strdup("lpr") );
284 #ifndef WIN32
285 osp = popen(qn, "w");
286 #else
287 osp = _popen(qn, "w");
288 #endif
289 if (!osp) {
290 fprintf(stderr, "inkscape: popen(%s): %s\n",
291 qn, strerror(errno));
292 return false;
293 }
294 g_free(qn);
295 _stream = osp;
296 }
297 }
299 g_free(local_fn);
301 if (_stream) {
302 /* fixme: this is kinda icky */
303 #if !defined(_WIN32) && !defined(__WIN32__)
304 (void) signal(SIGPIPE, SIG_IGN);
305 #endif
306 }
308 return true;
309 }
311 bool
312 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
313 {
314 #ifndef CAIRO_HAS_PS_SURFACE
315 return false;
316 #else
317 _target = CAIRO_SURFACE_TYPE_PS;
318 _vector_based_target = TRUE;
319 #endif
321 FILE *osf = NULL;
322 FILE *osp = NULL;
324 gsize bytesRead = 0;
325 gsize bytesWritten = 0;
326 GError *error = NULL;
327 gchar *local_fn = g_filename_from_utf8(utf8_fn,
328 -1, &bytesRead, &bytesWritten, &error);
329 gchar const *fn = local_fn;
331 /* TODO: Replace the below fprintf's with something that does the right thing whether in
332 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
333 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
334 * return code.
335 */
336 if (fn != NULL) {
337 if (*fn == '|') {
338 fn += 1;
339 while (isspace(*fn)) fn += 1;
340 #ifndef WIN32
341 osp = popen(fn, "w");
342 #else
343 osp = _popen(fn, "w");
344 #endif
345 if (!osp) {
346 fprintf(stderr, "inkscape: popen(%s): %s\n",
347 fn, strerror(errno));
348 return false;
349 }
350 _stream = osp;
351 } else if (*fn == '>') {
352 fn += 1;
353 while (isspace(*fn)) fn += 1;
354 Inkscape::IO::dump_fopen_call(fn, "K");
355 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
356 if (!osf) {
357 fprintf(stderr, "inkscape: fopen(%s): %s\n",
358 fn, strerror(errno));
359 return false;
360 }
361 _stream = osf;
362 } else {
363 /* put cwd stuff in here */
364 gchar *qn = ( *fn
365 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
366 : g_strdup("lpr") );
367 #ifndef WIN32
368 osp = popen(qn, "w");
369 #else
370 osp = _popen(qn, "w");
371 #endif
372 if (!osp) {
373 fprintf(stderr, "inkscape: popen(%s): %s\n",
374 qn, strerror(errno));
375 return false;
376 }
377 g_free(qn);
378 _stream = osp;
379 }
380 }
382 g_free(local_fn);
384 if (_stream) {
385 /* fixme: this is kinda icky */
386 #if !defined(_WIN32) && !defined(__WIN32__)
387 (void) signal(SIGPIPE, SIG_IGN);
388 #endif
389 }
391 return true;
392 }
394 void CairoRenderContext::setPSLevel(unsigned int level)
395 {
396 _ps_level = level;
397 }
399 unsigned int CairoRenderContext::getPSLevel(void)
400 {
401 return _ps_level;
402 }
404 void CairoRenderContext::setPDFLevel(unsigned int level)
405 {
406 _pdf_level = level;
407 }
409 void CairoRenderContext::setTextToPath(bool texttopath)
410 {
411 _is_texttopath = texttopath;
412 }
414 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
415 {
416 _is_filtertobitmap = filtertobitmap;
417 }
419 bool CairoRenderContext::getFilterToBitmap(void)
420 {
421 return _is_filtertobitmap;
422 }
424 void CairoRenderContext::setBitmapResolution(int resolution)
425 {
426 _bitmapresolution = resolution;
427 }
429 int CairoRenderContext::getBitmapResolution(void)
430 {
431 return _bitmapresolution;
432 }
434 cairo_surface_t*
435 CairoRenderContext::getSurface(void)
436 {
437 g_assert( _is_valid );
439 return _surface;
440 }
442 bool
443 CairoRenderContext::saveAsPng(const char *file_name)
444 {
445 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
446 if (status)
447 return false;
448 else
449 return true;
450 }
452 void
453 CairoRenderContext::setRenderMode(CairoRenderMode mode)
454 {
455 switch (mode) {
456 case RENDER_MODE_NORMAL:
457 case RENDER_MODE_CLIP:
458 _render_mode = mode;
459 break;
460 default:
461 _render_mode = RENDER_MODE_NORMAL;
462 break;
463 }
464 }
466 CairoRenderContext::CairoRenderMode
467 CairoRenderContext::getRenderMode(void) const
468 {
469 return _render_mode;
470 }
472 void
473 CairoRenderContext::setClipMode(CairoClipMode mode)
474 {
475 switch (mode) {
476 case CLIP_MODE_PATH:
477 case CLIP_MODE_MASK:
478 _clip_mode = mode;
479 break;
480 default:
481 _clip_mode = CLIP_MODE_PATH;
482 break;
483 }
484 }
486 CairoRenderContext::CairoClipMode
487 CairoRenderContext::getClipMode(void) const
488 {
489 return _clip_mode;
490 }
492 CairoRenderState*
493 CairoRenderContext::_createState(void)
494 {
495 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
496 g_assert( state != NULL );
498 state->has_filtereffect = FALSE;
499 state->merge_opacity = TRUE;
500 state->opacity = 1.0;
501 state->need_layer = FALSE;
502 state->has_overflow = FALSE;
503 state->parent_has_userspace = FALSE;
504 state->clip_path = NULL;
505 state->mask = NULL;
507 return state;
508 }
510 void
511 CairoRenderContext::pushLayer(void)
512 {
513 g_assert( _is_valid );
515 TRACE(("--pushLayer\n"));
516 cairo_push_group(_cr);
518 // clear buffer
519 if (!_vector_based_target) {
520 cairo_save(_cr);
521 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
522 cairo_paint(_cr);
523 cairo_restore(_cr);
524 }
525 }
527 void
528 CairoRenderContext::popLayer(void)
529 {
530 g_assert( _is_valid );
532 float opacity = _state->opacity;
533 TRACE(("--popLayer w/ %f\n", opacity));
535 // apply clipPath or mask if present
536 SPClipPath *clip_path = _state->clip_path;
537 SPMask *mask = _state->mask;
538 if (clip_path || mask) {
540 CairoRenderContext *clip_ctx = 0;
541 cairo_surface_t *clip_mask = 0;
543 if (clip_path) {
544 if (_render_mode == RENDER_MODE_CLIP)
545 mask = NULL; // disable mask when performing nested clipping
547 if (_vector_based_target) {
548 setClipMode(CLIP_MODE_PATH);
549 if (!mask) {
550 cairo_pop_group_to_source(_cr);
551 _renderer->applyClipPath(this, clip_path);
552 if (opacity == 1.0)
553 cairo_paint(_cr);
554 else
555 cairo_paint_with_alpha(_cr, opacity);
557 } else {
558 // the clipPath will be applied before masking
559 }
560 } else {
562 // setup a new rendering context
563 clip_ctx = _renderer->createContext();
564 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
565 clip_ctx->setClipMode(CLIP_MODE_MASK);
566 if (!clip_ctx->setupSurface(_width, _height)) {
567 TRACE(("setupSurface failed\n"));
568 _renderer->destroyContext(clip_ctx);
569 return;
570 }
572 // clear buffer
573 cairo_save(clip_ctx->_cr);
574 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
575 cairo_paint(clip_ctx->_cr);
576 cairo_restore(clip_ctx->_cr);
578 // if a mask won't be applied set opacity too
579 if (!mask)
580 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
581 else
582 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
584 // copy over the correct CTM
585 if (_state->parent_has_userspace)
586 clip_ctx->setTransform(&getParentState()->transform);
587 else
588 clip_ctx->setTransform(&_state->transform);
590 // apply the clip path
591 clip_ctx->pushState();
592 _renderer->applyClipPath(clip_ctx, clip_path);
593 clip_ctx->popState();
595 clip_mask = clip_ctx->getSurface();
596 TEST(clip_ctx->saveAsPng("clip_mask.png"));
598 if (!mask) {
599 cairo_pop_group_to_source(_cr);
600 cairo_mask_surface(_cr, clip_mask, 0, 0);
601 _renderer->destroyContext(clip_ctx);
602 }
603 }
604 }
606 if (mask) {
607 // create rendering context for mask
608 CairoRenderContext *mask_ctx = _renderer->createContext();
609 mask_ctx->setupSurface(_width, _height);
611 // set rendering mode to normal
612 setRenderMode(RENDER_MODE_NORMAL);
614 // copy the correct CTM to mask context
615 if (_state->parent_has_userspace)
616 mask_ctx->setTransform(&getParentState()->transform);
617 else
618 mask_ctx->setTransform(&_state->transform);
620 // render mask contents to mask_ctx
621 _renderer->applyMask(mask_ctx, mask);
623 TEST(mask_ctx->saveAsPng("mask.png"));
625 // composite with clip mask
626 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
627 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
628 _renderer->destroyContext(clip_ctx);
629 }
631 cairo_surface_t *mask_image = mask_ctx->getSurface();
632 int width = cairo_image_surface_get_width(mask_image);
633 int height = cairo_image_surface_get_height(mask_image);
634 int stride = cairo_image_surface_get_stride(mask_image);
635 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
637 // premultiply with opacity
638 if (_state->opacity != 1.0) {
639 TRACE(("premul w/ %f\n", opacity));
640 guint8 int_opacity = (guint8)(255 * opacity);
641 for (int row = 0 ; row < height; row++) {
642 unsigned char *row_data = pixels + (row * stride);
643 for (int i = 0 ; i < width; i++) {
644 guint32 *pixel = (guint32 *)row_data + i;
645 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
646 ((*pixel & 0x0000ff00) >> 8) * 46518 +
647 ((*pixel & 0x000000ff) ) * 4688) *
648 int_opacity);
649 }
650 }
651 }
653 cairo_pop_group_to_source(_cr);
654 if (_clip_mode == CLIP_MODE_PATH) {
655 // we have to do the clipping after cairo_pop_group_to_source
656 _renderer->applyClipPath(this, clip_path);
657 }
658 // apply the mask onto the layer
659 cairo_mask_surface(_cr, mask_image, 0, 0);
660 _renderer->destroyContext(mask_ctx);
661 }
662 } else {
663 cairo_pop_group_to_source(_cr);
664 if (opacity == 1.0)
665 cairo_paint(_cr);
666 else
667 cairo_paint_with_alpha(_cr, opacity);
668 }
669 }
671 void
672 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
673 {
674 g_assert( _is_valid );
676 // here it should be checked whether the current clip winding changed
677 // so we could switch back to masked clipping
678 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
679 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
680 } else {
681 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
682 }
683 addBpath(bp);
684 }
686 void
687 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
688 {
689 g_assert( _is_valid );
691 cairo_rectangle(_cr, x, y, width, height);
692 cairo_clip(_cr);
693 }
695 bool
696 CairoRenderContext::setupSurface(double width, double height)
697 {
698 // Is the surface already set up?
699 if (_is_valid)
700 return true;
702 if (_vector_based_target && _stream == NULL)
703 return false;
705 cairo_surface_t *surface = NULL;
706 switch (_target) {
707 case CAIRO_SURFACE_TYPE_IMAGE:
708 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
709 break;
710 #ifdef CAIRO_HAS_PDF_SURFACE
711 case CAIRO_SURFACE_TYPE_PDF:
712 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
713 break;
714 #endif
715 #ifdef CAIRO_HAS_PS_SURFACE
716 case CAIRO_SURFACE_TYPE_PS:
717 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
718 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
719 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
720 return FALSE;
721 }
722 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
723 #endif
724 break;
725 #endif
726 default:
727 return false;
728 break;
729 }
731 return _finishSurfaceSetup (surface);
732 }
734 bool
735 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
736 {
737 if (_is_valid || !surface)
738 return false;
740 _vector_based_target = is_vector;
741 bool ret = _finishSurfaceSetup (surface);
742 if (ret)
743 cairo_surface_reference (surface);
744 return ret;
745 }
747 bool
748 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
749 {
750 if(surface == NULL) {
751 return FALSE;
752 }
753 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
754 return FALSE;
755 }
757 _cr = cairo_create(surface);
758 _surface = surface;
760 if (_vector_based_target) {
761 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
762 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
763 // set background color on non-alpha surfaces
764 // TODO: bgcolor should be derived from SPDocument
765 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
766 cairo_rectangle(_cr, 0, 0, _width, _height);
767 cairo_fill(_cr);
768 }
770 _is_valid = TRUE;
772 return true;
773 }
775 bool
776 CairoRenderContext::finish(void)
777 {
778 g_assert( _is_valid );
780 if (_vector_based_target)
781 cairo_show_page(_cr);
783 cairo_destroy(_cr);
784 cairo_surface_destroy(_surface);
785 _cr = NULL;
786 _surface = NULL;
788 if (_layout)
789 g_object_unref(_layout);
791 _is_valid = FALSE;
793 if (_vector_based_target && _stream) {
794 /* Flush stream to be sure. */
795 (void) fflush(_stream);
797 fclose(_stream);
798 _stream = NULL;
799 }
801 return true;
802 }
804 void
805 CairoRenderContext::transform(NR::Matrix const *transform)
806 {
807 g_assert( _is_valid );
809 cairo_matrix_t matrix;
810 _initCairoMatrix(&matrix, transform);
811 cairo_transform(_cr, &matrix);
813 // store new CTM
814 getTransform(&_state->transform);
815 }
817 void
818 CairoRenderContext::setTransform(NR::Matrix const *transform)
819 {
820 g_assert( _is_valid );
822 cairo_matrix_t matrix;
823 _initCairoMatrix(&matrix, transform);
824 cairo_set_matrix(_cr, &matrix);
825 _state->transform = *transform;
826 }
828 void
829 CairoRenderContext::getTransform(NR::Matrix *copy) const
830 {
831 g_assert( _is_valid );
833 cairo_matrix_t ctm;
834 cairo_get_matrix(_cr, &ctm);
835 (*copy)[0] = ctm.xx;
836 (*copy)[1] = ctm.yx;
837 (*copy)[2] = ctm.xy;
838 (*copy)[3] = ctm.yy;
839 (*copy)[4] = ctm.x0;
840 (*copy)[5] = ctm.y0;
841 }
843 void
844 CairoRenderContext::getParentTransform(NR::Matrix *copy) const
845 {
846 g_assert( _is_valid );
848 CairoRenderState *parent_state = getParentState();
849 memcpy(copy, &parent_state->transform, sizeof(NR::Matrix));
850 }
852 void
853 CairoRenderContext::pushState(void)
854 {
855 g_assert( _is_valid );
857 cairo_save(_cr);
859 CairoRenderState *new_state = _createState();
860 // copy current state's transform
861 new_state->transform = _state->transform;
862 _state_stack = g_slist_prepend(_state_stack, new_state);
863 _state = new_state;
864 }
866 void
867 CairoRenderContext::popState(void)
868 {
869 g_assert( _is_valid );
871 cairo_restore(_cr);
873 g_free(_state_stack->data);
874 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
875 _state = (CairoRenderState*)_state_stack->data;
877 g_assert( g_slist_length(_state_stack) > 0 );
878 }
880 static bool pattern_hasItemChildren (SPPattern *pat)
881 {
882 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
883 if (SP_IS_ITEM (child)) {
884 return true;
885 }
886 }
887 return false;
888 }
890 cairo_pattern_t*
891 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
892 {
893 g_assert( SP_IS_PATTERN(paintserver) );
895 SPPattern *pat = SP_PATTERN (paintserver);
897 NR::Matrix ps2user, pcs2dev;
898 ps2user.set_identity();
899 pcs2dev.set_identity();
901 double x = pattern_x(pat);
902 double y = pattern_y(pat);
903 double width = pattern_width(pat);
904 double height = pattern_height(pat);
905 double bbox_width_scaler;
906 double bbox_height_scaler;
908 TRACE(("%f x %f pattern\n", width, height));
910 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
911 //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
912 bbox_width_scaler = pbox->x1 - pbox->x0;
913 bbox_height_scaler = pbox->y1 - pbox->y0;
914 ps2user[4] = x * bbox_width_scaler + pbox->x0;
915 ps2user[5] = y * bbox_height_scaler + pbox->y0;
916 } else {
917 bbox_width_scaler = 1.0;
918 bbox_height_scaler = 1.0;
919 ps2user[4] = x;
920 ps2user[5] = y;
921 }
923 // apply pattern transformation
924 NR::Matrix pattern_transform(pattern_patternTransform(pat));
925 ps2user *= pattern_transform;
927 // create pattern contents coordinate system
928 if (pat->viewBox_set) {
929 NRRect *view_box = pattern_viewBox(pat);
931 double x, y, w, h;
932 double view_width, view_height;
933 x = 0;
934 y = 0;
935 w = width * bbox_width_scaler;
936 h = height * bbox_height_scaler;
938 view_width = view_box->x1 - view_box->x0;
939 view_height = view_box->y1 - view_box->y0;
941 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
942 pcs2dev[0] = w / view_width;
943 pcs2dev[3] = h / view_height;
944 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
945 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
946 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
947 pcs2dev[0] = pbox->x1 - pbox->x0;
948 pcs2dev[3] = pbox->y1 - pbox->y0;
949 }
951 // calculate the size of the surface which has to be created
952 // the scaling needs to be taken into account in the ctm after the pattern transformation
953 NR::Matrix temp;
954 temp = pattern_transform * _state->transform;
955 double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
956 double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
958 if (_vector_based_target) {
959 // eliminate PT_PER_PX mul from these
960 width_scaler *= 1.25;
961 height_scaler *= 1.25;
962 }
963 double surface_width = ceil(bbox_width_scaler * width_scaler * width);
964 double surface_height = ceil(bbox_height_scaler * height_scaler * height);
965 TRACE(("surface size: %f x %f\n", surface_width, surface_height));
966 // create new rendering context
967 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
969 // adjust the size of the painted pattern to fit exactly the created surface
970 // this has to be done because of the rounding to obtain an integer pattern surface width/height
971 double scale_width = surface_width / (bbox_width_scaler * width);
972 double scale_height = surface_height / (bbox_height_scaler * height);
973 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
974 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
975 pcs2dev *= NR::scale(1.0 / scale_width, 1.0 / scale_height);
976 ps2user *= NR::scale(scale_width, scale_height);
977 }
979 pattern_ctx->setTransform(&pcs2dev);
980 pattern_ctx->pushState();
982 // create arena and group
983 NRArena *arena = NRArena::create();
984 unsigned dkey = sp_item_display_key_new(1);
986 // show items and render them
987 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
988 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
989 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
990 if (SP_IS_ITEM (child)) {
991 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
992 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
993 }
994 }
995 break; // do not go further up the chain if children are found
996 }
997 }
999 pattern_ctx->popState();
1001 // setup a cairo_pattern_t
1002 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1003 TEST(pattern_ctx->saveAsPng("pattern.png"));
1004 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1005 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1007 // set pattern transformation
1008 cairo_matrix_t pattern_matrix;
1009 _initCairoMatrix(&pattern_matrix, &ps2user);
1010 cairo_matrix_invert(&pattern_matrix);
1011 cairo_pattern_set_matrix(result, &pattern_matrix);
1013 delete pattern_ctx;
1015 // hide all items
1016 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1017 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1018 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1019 if (SP_IS_ITEM (child)) {
1020 sp_item_invoke_hide (SP_ITEM (child), dkey);
1021 }
1022 }
1023 break; // do not go further up the chain if children are found
1024 }
1025 }
1027 return result;
1028 }
1030 cairo_pattern_t*
1031 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1032 NRRect const *pbox, float alpha)
1033 {
1034 cairo_pattern_t *pattern = NULL;
1035 bool apply_bbox2user = FALSE;
1037 if (SP_IS_LINEARGRADIENT (paintserver)) {
1039 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1041 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1043 NR::Point p1 (lg->x1.computed, lg->y1.computed);
1044 NR::Point p2 (lg->x2.computed, lg->y2.computed);
1045 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1046 // convert to userspace
1047 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1048 p1 *= bbox2user;
1049 p2 *= bbox2user;
1050 }
1052 // create linear gradient pattern
1053 pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1055 // add stops
1056 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1057 float rgb[3];
1058 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1059 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1060 }
1061 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1063 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1065 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1067 NR::Point c (rg->cx.computed, rg->cy.computed);
1068 NR::Point f (rg->fx.computed, rg->fy.computed);
1069 double r = rg->r.computed;
1070 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1071 apply_bbox2user = true;
1073 // create radial gradient pattern
1074 pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1076 // add stops
1077 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1078 float rgb[3];
1079 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1080 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1081 }
1082 } else if (SP_IS_PATTERN (paintserver)) {
1084 pattern = _createPatternPainter(paintserver, pbox);
1085 } else {
1086 return NULL;
1087 }
1089 if (pattern && SP_IS_GRADIENT (paintserver)) {
1090 SPGradient *g = SP_GRADIENT(paintserver);
1092 // set extend type
1093 SPGradientSpread spread = sp_gradient_get_spread(g);
1094 switch (spread) {
1095 case SP_GRADIENT_SPREAD_REPEAT: {
1096 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1097 break;
1098 }
1099 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1100 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1101 break;
1102 }
1103 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1104 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1105 break;
1106 }
1107 default: {
1108 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1109 break;
1110 }
1111 }
1113 cairo_matrix_t pattern_matrix;
1114 if (g->gradientTransform_set) {
1115 // apply gradient transformation
1116 cairo_matrix_init(&pattern_matrix,
1117 g->gradientTransform[0], g->gradientTransform[1],
1118 g->gradientTransform[2], g->gradientTransform[3],
1119 g->gradientTransform[4], g->gradientTransform[5]);
1120 } else {
1121 cairo_matrix_init_identity (&pattern_matrix);
1122 }
1124 if (apply_bbox2user) {
1125 // convert to userspace
1126 cairo_matrix_t bbox2user;
1127 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1128 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1129 }
1130 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1131 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1132 }
1134 return pattern;
1135 }
1137 void
1138 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1139 {
1140 g_return_if_fail( style->fill.isColor()
1141 || style->fill.isPaintserver() );
1143 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1144 if (_state->merge_opacity) {
1145 alpha *= _state->opacity;
1146 TRACE(("merged op=%f\n", alpha));
1147 }
1149 if (style->fill.isColor()) {
1150 float rgb[3];
1151 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1153 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1154 } else {
1155 g_assert( style->fill.isPaintserver()
1156 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1157 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1159 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1161 if (pattern) {
1162 cairo_set_source(_cr, pattern);
1163 cairo_pattern_destroy(pattern);
1164 }
1165 }
1166 }
1168 void
1169 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1170 {
1171 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1172 if (_state->merge_opacity)
1173 alpha *= _state->opacity;
1175 if (style->stroke.isColor()) {
1176 float rgb[3];
1177 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1179 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1180 } else {
1181 g_assert( style->fill.isPaintserver()
1182 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1183 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1185 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1187 if (pattern) {
1188 cairo_set_source(_cr, pattern);
1189 cairo_pattern_destroy(pattern);
1190 }
1191 }
1193 if (style->stroke_dash.n_dash &&
1194 style->stroke_dash.dash )
1195 {
1196 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1197 } else {
1198 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1199 }
1201 cairo_set_line_width(_cr, style->stroke_width.computed);
1203 // set line join type
1204 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1205 switch (style->stroke_linejoin.computed) {
1206 case SP_STROKE_LINEJOIN_MITER:
1207 join = CAIRO_LINE_JOIN_MITER;
1208 break;
1209 case SP_STROKE_LINEJOIN_ROUND:
1210 join = CAIRO_LINE_JOIN_ROUND;
1211 break;
1212 case SP_STROKE_LINEJOIN_BEVEL:
1213 join = CAIRO_LINE_JOIN_BEVEL;
1214 break;
1215 }
1216 cairo_set_line_join(_cr, join);
1218 // set line cap type
1219 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1220 switch (style->stroke_linecap.computed) {
1221 case SP_STROKE_LINECAP_BUTT:
1222 cap = CAIRO_LINE_CAP_BUTT;
1223 break;
1224 case SP_STROKE_LINECAP_ROUND:
1225 cap = CAIRO_LINE_CAP_ROUND;
1226 break;
1227 case SP_STROKE_LINECAP_SQUARE:
1228 cap = CAIRO_LINE_CAP_SQUARE;
1229 break;
1230 }
1231 cairo_set_line_cap(_cr, cap);
1232 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1233 }
1235 bool
1236 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1237 {
1238 g_assert( _is_valid );
1240 if (_render_mode == RENDER_MODE_CLIP) {
1241 if (_clip_mode == CLIP_MODE_PATH) {
1242 addClipPath(bpath->path, &style->fill_rule);
1243 } else {
1244 setBpath(bpath->path);
1245 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1246 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1247 } else {
1248 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1249 }
1250 cairo_fill(_cr);
1251 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1252 }
1253 return true;
1254 }
1256 if (style->fill.isNone() && style->stroke.isNone())
1257 return true;
1259 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1260 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1262 if (!need_layer)
1263 cairo_save(_cr);
1264 else
1265 pushLayer();
1267 if (!style->fill.isNone()) {
1268 _setFillStyle(style, pbox);
1269 setBpath(bpath->path);
1271 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1272 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1273 } else {
1274 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1275 }
1277 if (style->stroke.isNone())
1278 cairo_fill(_cr);
1279 else
1280 cairo_fill_preserve(_cr);
1281 }
1283 if (!style->stroke.isNone()) {
1284 _setStrokeStyle(style, pbox);
1285 if (style->fill.isNone())
1286 setBpath(bpath->path);
1288 cairo_stroke(_cr);
1289 }
1291 if (need_layer)
1292 popLayer();
1293 else
1294 cairo_restore(_cr);
1296 return true;
1297 }
1299 bool
1300 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1301 NR::Matrix const *image_transform, SPStyle const *style)
1302 {
1303 g_assert( _is_valid );
1305 if (_render_mode == RENDER_MODE_CLIP)
1306 return true;
1308 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1309 if (!px_rgba)
1310 return false;
1312 float opacity;
1313 if (_state->merge_opacity)
1314 opacity = _state->opacity;
1315 else
1316 opacity = 1.0;
1318 // make a copy of the original pixbuf with premultiplied alpha
1319 // if we pass the original pixbuf it will get messed up
1320 for (unsigned i = 0; i < h; i++) {
1321 for (unsigned j = 0; j < w; j++) {
1322 guchar const *src = px + i * rs + j * 4;
1323 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1324 guchar r, g, b, alpha_dst;
1326 // calculate opacity-modified alpha
1327 alpha_dst = src[3];
1328 if (opacity != 1.0 && _vector_based_target)
1329 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1331 // premul alpha (needed because this will be undone by cairo-pdf)
1332 r = src[0]*alpha_dst/255;
1333 g = src[1]*alpha_dst/255;
1334 b = src[2]*alpha_dst/255;
1336 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1337 }
1338 }
1340 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1341 if (cairo_surface_status(image_surface)) {
1342 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1343 return false;
1344 }
1346 // setup automatic freeing of the image data when destroying the surface
1347 static cairo_user_data_key_t key;
1348 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1350 cairo_save(_cr);
1352 // scaling by width & height is not needed because it will be done by Cairo
1353 if (image_transform)
1354 transform(image_transform);
1356 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1358 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1359 if (_vector_based_target) {
1360 cairo_new_path(_cr);
1361 cairo_rectangle(_cr, 0, 0, w, h);
1362 cairo_clip(_cr);
1363 }
1365 if (_vector_based_target)
1366 cairo_paint(_cr);
1367 else
1368 cairo_paint_with_alpha(_cr, opacity);
1370 cairo_restore(_cr);
1372 cairo_surface_destroy(image_surface);
1374 return true;
1375 }
1377 #define GLYPH_ARRAY_SIZE 64
1379 unsigned int
1380 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1381 {
1382 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1383 cairo_glyph_t *glyphs = glyph_array;
1384 unsigned int num_glyphs = glyphtext.size();
1385 if (num_glyphs > GLYPH_ARRAY_SIZE)
1386 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1388 unsigned int num_invalid_glyphs = 0;
1389 unsigned int i = 0;
1390 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1391 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1392 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1393 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1394 TRACE(("INVALID GLYPH found\n"));
1395 num_invalid_glyphs++;
1396 continue;
1397 }
1398 glyphs[i - num_invalid_glyphs].index = it_info->index;
1399 glyphs[i - num_invalid_glyphs].x = it_info->x;
1400 glyphs[i - num_invalid_glyphs].y = it_info->y;
1401 i++;
1402 }
1404 if (is_stroke || _is_texttopath)
1405 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1406 else
1407 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1409 if (num_glyphs > GLYPH_ARRAY_SIZE)
1410 g_free(glyphs);
1412 return num_glyphs - num_invalid_glyphs;
1413 }
1415 bool
1416 CairoRenderContext::renderGlyphtext(PangoFont *font, NR::Matrix const *font_matrix,
1417 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1418 {
1419 // create a cairo_font_face from PangoFont
1420 double size = style->font_size.computed;
1421 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1422 FcPattern *fc_pattern = fc_font->font_pattern;
1424 cairo_save(_cr);
1426 #ifdef CAIRO_HAS_FT_FONT
1427 cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1428 cairo_set_font_face(_cr, font_face);
1430 if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1431 size = 12.0;
1433 // set the given font matrix
1434 cairo_matrix_t matrix;
1435 _initCairoMatrix(&matrix, font_matrix);
1436 cairo_set_font_matrix(_cr, &matrix);
1438 if (_render_mode == RENDER_MODE_CLIP) {
1439 if (_clip_mode == CLIP_MODE_MASK) {
1440 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1441 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1442 } else {
1443 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1444 }
1445 _showGlyphs(_cr, font, glyphtext, FALSE);
1446 } else {
1447 // just add the glyph paths to the current context
1448 _showGlyphs(_cr, font, glyphtext, TRUE);
1449 }
1450 } else {
1452 if (style->fill.isColor() || style->fill.isPaintserver()) {
1453 // set fill style
1454 _setFillStyle(style, NULL);
1456 _showGlyphs(_cr, font, glyphtext, FALSE);
1457 }
1459 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1460 // set stroke style
1461 _setStrokeStyle(style, NULL);
1463 // paint stroke
1464 _showGlyphs(_cr, font, glyphtext, TRUE);
1465 cairo_stroke(_cr);
1466 }
1467 }
1469 cairo_restore(_cr);
1471 cairo_font_face_destroy(font_face);
1472 #else
1473 (void)size;
1474 (void)fc_pattern;
1476 cairo_restore(_cr);
1477 #endif
1479 return true;
1480 }
1482 /* Helper functions */
1484 void
1485 CairoRenderContext::addBpath(NArtBpath const *bp)
1486 {
1487 bool closed = false;
1488 while (bp->code != NR_END) {
1489 switch (bp->code) {
1490 case NR_MOVETO:
1491 if (closed) {
1492 cairo_close_path(_cr);
1493 }
1494 closed = true;
1495 cairo_move_to(_cr, bp->x3, bp->y3);
1496 break;
1497 case NR_MOVETO_OPEN:
1498 if (closed) {
1499 cairo_close_path(_cr);
1500 }
1501 closed = false;
1502 cairo_move_to(_cr, bp->x3, bp->y3);
1503 break;
1504 case NR_LINETO:
1505 cairo_line_to(_cr, bp->x3, bp->y3);
1506 break;
1507 case NR_CURVETO:
1508 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1509 break;
1510 default:
1511 break;
1512 }
1513 bp += 1;
1514 }
1515 if (closed) {
1516 cairo_close_path(_cr);
1517 }
1518 }
1520 void
1521 CairoRenderContext::setBpath(NArtBpath const *bp)
1522 {
1523 cairo_new_path(_cr);
1524 if (bp)
1525 addBpath(bp);
1526 }
1528 void
1529 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1530 {
1531 cairo_matrix_t matrix;
1533 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1534 cairo_transform(cr, &matrix);
1535 }
1537 void
1538 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NR::Matrix const *transform)
1539 {
1540 matrix->xx = (*transform)[0];
1541 matrix->yx = (*transform)[1];
1542 matrix->xy = (*transform)[2];
1543 matrix->yy = (*transform)[3];
1544 matrix->x0 = (*transform)[4];
1545 matrix->y0 = (*transform)[5];
1546 }
1548 void
1549 CairoRenderContext::_concatTransform(cairo_t *cr, NR::Matrix const *transform)
1550 {
1551 _concatTransform(cr, (*transform)[0], (*transform)[1],
1552 (*transform)[2], (*transform)[3],
1553 (*transform)[4], (*transform)[5]);
1554 }
1556 static cairo_status_t
1557 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1558 {
1559 size_t written;
1560 FILE *file = (FILE*)closure;
1562 written = fwrite (data, 1, length, file);
1564 if (written == length)
1565 return CAIRO_STATUS_SUCCESS;
1566 else
1567 return CAIRO_STATUS_WRITE_ERROR;
1568 }
1570 #include "clear-n_.h"
1572 } /* namespace Internal */
1573 } /* namespace Extension */
1574 } /* namespace Inkscape */
1576 #undef TRACE
1577 #undef TEST
1579 /* End of GNU GPL code */
1582 /*
1583 Local Variables:
1584 mode:c++
1585 c-file-style:"stroustrup"
1586 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1587 indent-tabs-mode:nil
1588 fill-column:99
1589 End:
1590 */
1591 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :