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