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>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 #define TRACE(_args)
84 //#define TEST(_args) _args
85 #define TEST(_args)
87 // FIXME: expose these from sp-clippath/mask.cpp
88 struct SPClipPathView {
89 SPClipPathView *next;
90 unsigned int key;
91 NRArenaItem *arenaitem;
92 NRRect bbox;
93 };
95 struct SPMaskView {
96 SPMaskView *next;
97 unsigned int key;
98 NRArenaItem *arenaitem;
99 NRRect bbox;
100 };
102 namespace Inkscape {
103 namespace Extension {
104 namespace Internal {
106 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
108 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
109 _dpi(72),
110 _pdf_level(0),
111 _ps_level(1),
112 _is_texttopath(FALSE),
113 _is_filtertobitmap(FALSE),
114 _bitmapresolution(72),
115 _stream(NULL),
116 _is_valid(FALSE),
117 _vector_based_target(FALSE),
118 _cr(NULL),
119 _surface(NULL),
120 _target(CAIRO_SURFACE_TYPE_IMAGE),
121 _target_format(CAIRO_FORMAT_ARGB32),
122 _layout(NULL),
123 _state(NULL),
124 _renderer(parent),
125 _render_mode(RENDER_MODE_NORMAL),
126 _clip_mode(CLIP_MODE_MASK)
127 {}
129 CairoRenderContext::~CairoRenderContext(void)
130 {
131 if (_cr) cairo_destroy(_cr);
132 if (_surface) cairo_surface_destroy(_surface);
133 if (_layout) g_object_unref(_layout);
134 }
136 CairoRenderer*
137 CairoRenderContext::getRenderer(void) const
138 {
139 return _renderer;
140 }
142 CairoRenderState*
143 CairoRenderContext::getCurrentState(void) const
144 {
145 return _state;
146 }
148 CairoRenderState*
149 CairoRenderContext::getParentState(void) const
150 {
151 // if this is the root node just return it
152 if (g_slist_length(_state_stack) == 1) {
153 return _state;
154 } else {
155 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
156 }
157 }
159 void
160 CairoRenderContext::setStateForStyle(SPStyle const *style)
161 {
162 // only opacity & overflow is stored for now
163 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
164 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
165 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
167 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
168 _state->merge_opacity = FALSE;
170 // disable rendering of opacity if there's a stroke on the fill
171 if (_state->merge_opacity
172 && !style->fill.isNone()
173 && !style->stroke.isNone())
174 _state->merge_opacity = FALSE;
175 }
177 /**
178 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
179 *
180 * \param width width of the surface to be created
181 * \param height height of the surface to be created
182 */
183 CairoRenderContext*
184 CairoRenderContext::cloneMe(double width, double height) const
185 {
186 g_assert( _is_valid );
187 g_assert( width > 0.0 && height > 0.0 );
189 CairoRenderContext *new_context = _renderer->createContext();
190 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
191 (int)ceil(width), (int)ceil(height));
192 new_context->_cr = cairo_create(surface);
193 new_context->_surface = surface;
194 new_context->_is_valid = TRUE;
196 return new_context;
197 }
199 CairoRenderContext*
200 CairoRenderContext::cloneMe(void) const
201 {
202 g_assert( _is_valid );
204 return cloneMe(_width, _height);
205 }
207 bool
208 CairoRenderContext::setImageTarget(cairo_format_t format)
209 {
210 // format cannot be set on an already initialized surface
211 if (_is_valid)
212 return false;
214 switch (format) {
215 case CAIRO_FORMAT_ARGB32:
216 case CAIRO_FORMAT_RGB24:
217 case CAIRO_FORMAT_A8:
218 case CAIRO_FORMAT_A1:
219 _target_format = format;
220 _target = CAIRO_SURFACE_TYPE_IMAGE;
221 return true;
222 break;
223 default:
224 break;
225 }
227 return false;
228 }
230 bool
231 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
232 {
233 #ifndef CAIRO_HAS_PDF_SURFACE
234 return false;
235 #else
236 _target = CAIRO_SURFACE_TYPE_PDF;
237 _vector_based_target = TRUE;
238 #endif
240 FILE *osf = NULL;
241 FILE *osp = NULL;
243 gsize bytesRead = 0;
244 gsize bytesWritten = 0;
245 GError *error = NULL;
246 gchar *local_fn = g_filename_from_utf8(utf8_fn,
247 -1, &bytesRead, &bytesWritten, &error);
248 gchar const *fn = local_fn;
250 /* TODO: Replace the below fprintf's with something that does the right thing whether in
251 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
252 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
253 * return code.
254 */
255 if (fn != NULL) {
256 if (*fn == '|') {
257 fn += 1;
258 while (isspace(*fn)) fn += 1;
259 #ifndef WIN32
260 osp = popen(fn, "w");
261 #else
262 osp = _popen(fn, "w");
263 #endif
264 if (!osp) {
265 fprintf(stderr, "inkscape: popen(%s): %s\n",
266 fn, strerror(errno));
267 return false;
268 }
269 _stream = osp;
270 } else if (*fn == '>') {
271 fn += 1;
272 while (isspace(*fn)) fn += 1;
273 Inkscape::IO::dump_fopen_call(fn, "K");
274 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
275 if (!osf) {
276 fprintf(stderr, "inkscape: fopen(%s): %s\n",
277 fn, strerror(errno));
278 return false;
279 }
280 _stream = osf;
281 } else {
282 /* put cwd stuff in here */
283 gchar *qn = ( *fn
284 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
285 : g_strdup("lpr") );
286 #ifndef WIN32
287 osp = popen(qn, "w");
288 #else
289 osp = _popen(qn, "w");
290 #endif
291 if (!osp) {
292 fprintf(stderr, "inkscape: popen(%s): %s\n",
293 qn, strerror(errno));
294 return false;
295 }
296 g_free(qn);
297 _stream = osp;
298 }
299 }
301 g_free(local_fn);
303 if (_stream) {
304 /* fixme: this is kinda icky */
305 #if !defined(_WIN32) && !defined(__WIN32__)
306 (void) signal(SIGPIPE, SIG_IGN);
307 #endif
308 }
310 return true;
311 }
313 bool
314 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
315 {
316 #ifndef CAIRO_HAS_PS_SURFACE
317 return false;
318 #else
319 _target = CAIRO_SURFACE_TYPE_PS;
320 _vector_based_target = TRUE;
321 #endif
323 FILE *osf = NULL;
324 FILE *osp = NULL;
326 gsize bytesRead = 0;
327 gsize bytesWritten = 0;
328 GError *error = NULL;
329 gchar *local_fn = g_filename_from_utf8(utf8_fn,
330 -1, &bytesRead, &bytesWritten, &error);
331 gchar const *fn = local_fn;
333 /* TODO: Replace the below fprintf's with something that does the right thing whether in
334 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
335 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
336 * return code.
337 */
338 if (fn != NULL) {
339 if (*fn == '|') {
340 fn += 1;
341 while (isspace(*fn)) fn += 1;
342 #ifndef WIN32
343 osp = popen(fn, "w");
344 #else
345 osp = _popen(fn, "w");
346 #endif
347 if (!osp) {
348 fprintf(stderr, "inkscape: popen(%s): %s\n",
349 fn, strerror(errno));
350 return false;
351 }
352 _stream = osp;
353 } else if (*fn == '>') {
354 fn += 1;
355 while (isspace(*fn)) fn += 1;
356 Inkscape::IO::dump_fopen_call(fn, "K");
357 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
358 if (!osf) {
359 fprintf(stderr, "inkscape: fopen(%s): %s\n",
360 fn, strerror(errno));
361 return false;
362 }
363 _stream = osf;
364 } else {
365 /* put cwd stuff in here */
366 gchar *qn = ( *fn
367 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
368 : g_strdup("lpr") );
369 #ifndef WIN32
370 osp = popen(qn, "w");
371 #else
372 osp = _popen(qn, "w");
373 #endif
374 if (!osp) {
375 fprintf(stderr, "inkscape: popen(%s): %s\n",
376 qn, strerror(errno));
377 return false;
378 }
379 g_free(qn);
380 _stream = osp;
381 }
382 }
384 g_free(local_fn);
386 if (_stream) {
387 /* fixme: this is kinda icky */
388 #if !defined(_WIN32) && !defined(__WIN32__)
389 (void) signal(SIGPIPE, SIG_IGN);
390 #endif
391 }
393 return true;
394 }
396 void CairoRenderContext::setPSLevel(unsigned int level)
397 {
398 _ps_level = level;
399 }
401 unsigned int CairoRenderContext::getPSLevel(void)
402 {
403 return _ps_level;
404 }
406 void CairoRenderContext::setPDFLevel(unsigned int level)
407 {
408 _pdf_level = level;
409 }
411 void CairoRenderContext::setTextToPath(bool texttopath)
412 {
413 _is_texttopath = texttopath;
414 }
416 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
417 {
418 _is_filtertobitmap = filtertobitmap;
419 }
421 bool CairoRenderContext::getFilterToBitmap(void)
422 {
423 return _is_filtertobitmap;
424 }
426 void CairoRenderContext::setBitmapResolution(int resolution)
427 {
428 _bitmapresolution = resolution;
429 }
431 int CairoRenderContext::getBitmapResolution(void)
432 {
433 return _bitmapresolution;
434 }
436 cairo_surface_t*
437 CairoRenderContext::getSurface(void)
438 {
439 g_assert( _is_valid );
441 return _surface;
442 }
444 bool
445 CairoRenderContext::saveAsPng(const char *file_name)
446 {
447 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
448 if (status)
449 return false;
450 else
451 return true;
452 }
454 void
455 CairoRenderContext::setRenderMode(CairoRenderMode mode)
456 {
457 switch (mode) {
458 case RENDER_MODE_NORMAL:
459 case RENDER_MODE_CLIP:
460 _render_mode = mode;
461 break;
462 default:
463 _render_mode = RENDER_MODE_NORMAL;
464 break;
465 }
466 }
468 CairoRenderContext::CairoRenderMode
469 CairoRenderContext::getRenderMode(void) const
470 {
471 return _render_mode;
472 }
474 void
475 CairoRenderContext::setClipMode(CairoClipMode mode)
476 {
477 switch (mode) {
478 case CLIP_MODE_PATH:
479 case CLIP_MODE_MASK:
480 _clip_mode = mode;
481 break;
482 default:
483 _clip_mode = CLIP_MODE_PATH;
484 break;
485 }
486 }
488 CairoRenderContext::CairoClipMode
489 CairoRenderContext::getClipMode(void) const
490 {
491 return _clip_mode;
492 }
494 CairoRenderState*
495 CairoRenderContext::_createState(void)
496 {
497 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
498 g_assert( state != NULL );
500 state->has_filtereffect = FALSE;
501 state->merge_opacity = TRUE;
502 state->opacity = 1.0;
503 state->need_layer = FALSE;
504 state->has_overflow = FALSE;
505 state->parent_has_userspace = FALSE;
506 state->clip_path = NULL;
507 state->mask = NULL;
509 return state;
510 }
512 void
513 CairoRenderContext::pushLayer(void)
514 {
515 g_assert( _is_valid );
517 TRACE(("--pushLayer\n"));
518 cairo_push_group(_cr);
520 // clear buffer
521 if (!_vector_based_target) {
522 cairo_save(_cr);
523 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
524 cairo_paint(_cr);
525 cairo_restore(_cr);
526 }
527 }
529 void
530 CairoRenderContext::popLayer(void)
531 {
532 g_assert( _is_valid );
534 float opacity = _state->opacity;
535 TRACE(("--popLayer w/ %f\n", opacity));
537 // apply clipPath or mask if present
538 SPClipPath *clip_path = _state->clip_path;
539 SPMask *mask = _state->mask;
540 if (clip_path || mask) {
542 CairoRenderContext *clip_ctx = 0;
543 cairo_surface_t *clip_mask = 0;
545 if (clip_path) {
546 if (_render_mode == RENDER_MODE_CLIP)
547 mask = NULL; // disable mask when performing nested clipping
549 if (_vector_based_target) {
550 setClipMode(CLIP_MODE_PATH);
551 if (!mask) {
552 cairo_pop_group_to_source(_cr);
553 _renderer->applyClipPath(this, clip_path);
554 if (opacity == 1.0)
555 cairo_paint(_cr);
556 else
557 cairo_paint_with_alpha(_cr, opacity);
559 } else {
560 // the clipPath will be applied before masking
561 }
562 } else {
564 // setup a new rendering context
565 clip_ctx = _renderer->createContext();
566 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
567 clip_ctx->setClipMode(CLIP_MODE_MASK);
568 if (!clip_ctx->setupSurface(_width, _height)) {
569 TRACE(("setupSurface failed\n"));
570 _renderer->destroyContext(clip_ctx);
571 return;
572 }
574 // clear buffer
575 cairo_save(clip_ctx->_cr);
576 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
577 cairo_paint(clip_ctx->_cr);
578 cairo_restore(clip_ctx->_cr);
580 // if a mask won't be applied set opacity too
581 if (!mask)
582 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
583 else
584 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
586 // copy over the correct CTM
587 if (_state->parent_has_userspace)
588 clip_ctx->setTransform(&getParentState()->transform);
589 else
590 clip_ctx->setTransform(&_state->transform);
592 // apply the clip path
593 clip_ctx->pushState();
594 _renderer->applyClipPath(clip_ctx, clip_path);
595 clip_ctx->popState();
597 clip_mask = clip_ctx->getSurface();
598 TEST(clip_ctx->saveAsPng("clip_mask.png"));
600 if (!mask) {
601 cairo_pop_group_to_source(_cr);
602 cairo_mask_surface(_cr, clip_mask, 0, 0);
603 _renderer->destroyContext(clip_ctx);
604 }
605 }
606 }
608 if (mask) {
609 // create rendering context for mask
610 CairoRenderContext *mask_ctx = _renderer->createContext();
611 mask_ctx->setupSurface(_width, _height);
613 // set rendering mode to normal
614 setRenderMode(RENDER_MODE_NORMAL);
616 // copy the correct CTM to mask context
617 if (_state->parent_has_userspace)
618 mask_ctx->setTransform(&getParentState()->transform);
619 else
620 mask_ctx->setTransform(&_state->transform);
622 // render mask contents to mask_ctx
623 _renderer->applyMask(mask_ctx, mask);
625 TEST(mask_ctx->saveAsPng("mask.png"));
627 // composite with clip mask
628 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
629 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
630 _renderer->destroyContext(clip_ctx);
631 }
633 cairo_surface_t *mask_image = mask_ctx->getSurface();
634 int width = cairo_image_surface_get_width(mask_image);
635 int height = cairo_image_surface_get_height(mask_image);
636 int stride = cairo_image_surface_get_stride(mask_image);
637 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
639 // premultiply with opacity
640 if (_state->opacity != 1.0) {
641 TRACE(("premul w/ %f\n", opacity));
642 guint8 int_opacity = (guint8)(255 * opacity);
643 for (int row = 0 ; row < height; row++) {
644 unsigned char *row_data = pixels + (row * stride);
645 for (int i = 0 ; i < width; i++) {
646 guint32 *pixel = (guint32 *)row_data + i;
647 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
648 ((*pixel & 0x0000ff00) >> 8) * 46518 +
649 ((*pixel & 0x000000ff) ) * 4688) *
650 int_opacity);
651 }
652 }
653 }
655 cairo_pop_group_to_source(_cr);
656 if (_clip_mode == CLIP_MODE_PATH) {
657 // we have to do the clipping after cairo_pop_group_to_source
658 _renderer->applyClipPath(this, clip_path);
659 }
660 // apply the mask onto the layer
661 cairo_mask_surface(_cr, mask_image, 0, 0);
662 _renderer->destroyContext(mask_ctx);
663 }
664 } else {
665 cairo_pop_group_to_source(_cr);
666 if (opacity == 1.0)
667 cairo_paint(_cr);
668 else
669 cairo_paint_with_alpha(_cr, opacity);
670 }
671 }
673 void
674 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
675 {
676 g_assert( _is_valid );
678 // here it should be checked whether the current clip winding changed
679 // so we could switch back to masked clipping
680 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
681 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
682 } else {
683 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
684 }
685 addPathVector(pv);
686 }
688 void
689 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
690 {
691 g_assert( _is_valid );
693 cairo_rectangle(_cr, x, y, width, height);
694 cairo_clip(_cr);
695 }
697 bool
698 CairoRenderContext::setupSurface(double width, double height)
699 {
700 // Is the surface already set up?
701 if (_is_valid)
702 return true;
704 if (_vector_based_target && _stream == NULL)
705 return false;
707 cairo_surface_t *surface = NULL;
708 switch (_target) {
709 case CAIRO_SURFACE_TYPE_IMAGE:
710 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
711 break;
712 #ifdef CAIRO_HAS_PDF_SURFACE
713 case CAIRO_SURFACE_TYPE_PDF:
714 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
715 break;
716 #endif
717 #ifdef CAIRO_HAS_PS_SURFACE
718 case CAIRO_SURFACE_TYPE_PS:
719 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
720 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
721 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
722 return FALSE;
723 }
724 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
725 #endif
726 break;
727 #endif
728 default:
729 return false;
730 break;
731 }
733 return _finishSurfaceSetup (surface);
734 }
736 bool
737 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
738 {
739 if (_is_valid || !surface)
740 return false;
742 _vector_based_target = is_vector;
743 bool ret = _finishSurfaceSetup (surface);
744 if (ret)
745 cairo_surface_reference (surface);
746 return ret;
747 }
749 bool
750 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
751 {
752 if(surface == NULL) {
753 return FALSE;
754 }
755 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
756 return FALSE;
757 }
759 _cr = cairo_create(surface);
760 _surface = surface;
762 if (_vector_based_target) {
763 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
764 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
765 // set background color on non-alpha surfaces
766 // TODO: bgcolor should be derived from SPDocument
767 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
768 cairo_rectangle(_cr, 0, 0, _width, _height);
769 cairo_fill(_cr);
770 }
772 _is_valid = TRUE;
774 return true;
775 }
777 bool
778 CairoRenderContext::finish(void)
779 {
780 g_assert( _is_valid );
782 if (_vector_based_target)
783 cairo_show_page(_cr);
785 cairo_destroy(_cr);
786 cairo_surface_finish(_surface);
787 cairo_status_t status = cairo_surface_status(_surface);
788 cairo_surface_destroy(_surface);
789 _cr = NULL;
790 _surface = NULL;
792 if (_layout)
793 g_object_unref(_layout);
795 _is_valid = FALSE;
797 if (_vector_based_target && _stream) {
798 /* Flush stream to be sure. */
799 (void) fflush(_stream);
801 fclose(_stream);
802 _stream = NULL;
803 }
805 if (status == CAIRO_STATUS_SUCCESS)
806 return true;
807 else
808 return false;
809 }
811 void
812 CairoRenderContext::transform(Geom::Matrix const *transform)
813 {
814 g_assert( _is_valid );
816 cairo_matrix_t matrix;
817 _initCairoMatrix(&matrix, transform);
818 cairo_transform(_cr, &matrix);
820 // store new CTM
821 getTransform(&_state->transform);
822 }
824 void
825 CairoRenderContext::setTransform(Geom::Matrix const *transform)
826 {
827 g_assert( _is_valid );
829 cairo_matrix_t matrix;
830 _initCairoMatrix(&matrix, transform);
831 cairo_set_matrix(_cr, &matrix);
832 _state->transform = *transform;
833 }
835 void
836 CairoRenderContext::getTransform(Geom::Matrix *copy) const
837 {
838 g_assert( _is_valid );
840 cairo_matrix_t ctm;
841 cairo_get_matrix(_cr, &ctm);
842 (*copy)[0] = ctm.xx;
843 (*copy)[1] = ctm.yx;
844 (*copy)[2] = ctm.xy;
845 (*copy)[3] = ctm.yy;
846 (*copy)[4] = ctm.x0;
847 (*copy)[5] = ctm.y0;
848 }
850 void
851 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
852 {
853 g_assert( _is_valid );
855 CairoRenderState *parent_state = getParentState();
856 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
857 }
859 void
860 CairoRenderContext::pushState(void)
861 {
862 g_assert( _is_valid );
864 cairo_save(_cr);
866 CairoRenderState *new_state = _createState();
867 // copy current state's transform
868 new_state->transform = _state->transform;
869 _state_stack = g_slist_prepend(_state_stack, new_state);
870 _state = new_state;
871 }
873 void
874 CairoRenderContext::popState(void)
875 {
876 g_assert( _is_valid );
878 cairo_restore(_cr);
880 g_free(_state_stack->data);
881 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
882 _state = (CairoRenderState*)_state_stack->data;
884 g_assert( g_slist_length(_state_stack) > 0 );
885 }
887 static bool pattern_hasItemChildren (SPPattern *pat)
888 {
889 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
890 if (SP_IS_ITEM (child)) {
891 return true;
892 }
893 }
894 return false;
895 }
897 cairo_pattern_t*
898 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
899 {
900 g_assert( SP_IS_PATTERN(paintserver) );
902 SPPattern *pat = SP_PATTERN (paintserver);
904 Geom::Matrix ps2user, pcs2dev;
905 ps2user = Geom::identity();
906 pcs2dev = Geom::identity();
908 double x = pattern_x(pat);
909 double y = pattern_y(pat);
910 double width = pattern_width(pat);
911 double height = pattern_height(pat);
912 double bbox_width_scaler;
913 double bbox_height_scaler;
915 TRACE(("%f x %f pattern\n", width, height));
917 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
918 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
919 bbox_width_scaler = pbox->x1 - pbox->x0;
920 bbox_height_scaler = pbox->y1 - pbox->y0;
921 ps2user[4] = x * bbox_width_scaler + pbox->x0;
922 ps2user[5] = y * bbox_height_scaler + pbox->y0;
923 } else {
924 bbox_width_scaler = 1.0;
925 bbox_height_scaler = 1.0;
926 ps2user[4] = x;
927 ps2user[5] = y;
928 }
930 // apply pattern transformation
931 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
932 ps2user *= pattern_transform;
934 // create pattern contents coordinate system
935 if (pat->viewBox_set) {
936 NRRect *view_box = pattern_viewBox(pat);
938 double x, y, w, h;
939 double view_width, view_height;
940 x = 0;
941 y = 0;
942 w = width * bbox_width_scaler;
943 h = height * bbox_height_scaler;
945 view_width = view_box->x1 - view_box->x0;
946 view_height = view_box->y1 - view_box->y0;
948 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
949 pcs2dev[0] = w / view_width;
950 pcs2dev[3] = h / view_height;
951 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
952 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
953 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
954 pcs2dev[0] = pbox->x1 - pbox->x0;
955 pcs2dev[3] = pbox->y1 - pbox->y0;
956 }
958 // Calculate the size of the surface which has to be created so that the pattern resolution
959 // matches the output resolution (i.e., if the pattern is scaled up by a factor of two,
960 // the surface width should be scaled by a factor of two).
961 // The scaling needs to be taken into account in the ctm after the pattern transformation.
962 Geom::Matrix temp;
963 temp = pattern_transform * _state->transform;
964 double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
965 double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
967 if (_vector_based_target) {
968 // eliminate PT_PER_PX mul from these
969 width_scaler *= 1.25;
970 height_scaler *= 1.25;
971 }
972 // Cairo requires an integer pattern surface width/height.
973 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
974 double surface_width = ceil(bbox_width_scaler * width_scaler * width - 0.5);
975 double surface_height = ceil(bbox_height_scaler * height_scaler * height - 0.5);
976 TRACE(("surface size: %f x %f\n", surface_width, surface_height));
977 // create new rendering context
978 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
980 // adjust the size of the painted pattern to fit exactly the created surface
981 // this has to be done because of the rounding to obtain an integer pattern surface width/height
982 double scale_width = surface_width / (bbox_width_scaler * width);
983 double scale_height = surface_height / (bbox_height_scaler * height);
984 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
985 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
986 pcs2dev *= Geom::Scale(scale_width, scale_height);
987 ps2user *= Geom::Scale(1.0 / scale_width, 1.0 / scale_height);
988 }
990 pattern_ctx->setTransform(&pcs2dev);
991 pattern_ctx->pushState();
993 // create arena and group
994 NRArena *arena = NRArena::create();
995 unsigned dkey = sp_item_display_key_new(1);
997 // show items and render them
998 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
999 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1000 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1001 if (SP_IS_ITEM (child)) {
1002 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1003 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1004 }
1005 }
1006 break; // do not go further up the chain if children are found
1007 }
1008 }
1010 pattern_ctx->popState();
1012 // setup a cairo_pattern_t
1013 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1014 TEST(pattern_ctx->saveAsPng("pattern.png"));
1015 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1016 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1018 // set pattern transformation
1019 cairo_matrix_t pattern_matrix;
1020 _initCairoMatrix(&pattern_matrix, &ps2user);
1021 cairo_matrix_invert(&pattern_matrix);
1022 cairo_pattern_set_matrix(result, &pattern_matrix);
1024 delete pattern_ctx;
1026 // hide all items
1027 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1028 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1029 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1030 if (SP_IS_ITEM (child)) {
1031 sp_item_invoke_hide (SP_ITEM (child), dkey);
1032 }
1033 }
1034 break; // do not go further up the chain if children are found
1035 }
1036 }
1038 return result;
1039 }
1041 cairo_pattern_t*
1042 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1043 NRRect const *pbox, float alpha)
1044 {
1045 cairo_pattern_t *pattern = NULL;
1046 bool apply_bbox2user = FALSE;
1048 if (SP_IS_LINEARGRADIENT (paintserver)) {
1050 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1052 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1054 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1055 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1056 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1057 // convert to userspace
1058 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1059 p1 *= bbox2user;
1060 p2 *= bbox2user;
1061 }
1063 // create linear gradient pattern
1064 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1066 // add stops
1067 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1068 float rgb[3];
1069 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1070 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1071 }
1072 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1074 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1076 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1078 Geom::Point c (rg->cx.computed, rg->cy.computed);
1079 Geom::Point f (rg->fx.computed, rg->fy.computed);
1080 double r = rg->r.computed;
1081 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1082 apply_bbox2user = true;
1084 // create radial gradient pattern
1085 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1087 // add stops
1088 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1089 float rgb[3];
1090 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1091 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1092 }
1093 } else if (SP_IS_PATTERN (paintserver)) {
1095 pattern = _createPatternPainter(paintserver, pbox);
1096 } else {
1097 return NULL;
1098 }
1100 if (pattern && SP_IS_GRADIENT (paintserver)) {
1101 SPGradient *g = SP_GRADIENT(paintserver);
1103 // set extend type
1104 SPGradientSpread spread = sp_gradient_get_spread(g);
1105 switch (spread) {
1106 case SP_GRADIENT_SPREAD_REPEAT: {
1107 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1108 break;
1109 }
1110 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1111 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1112 break;
1113 }
1114 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1115 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1116 break;
1117 }
1118 default: {
1119 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1120 break;
1121 }
1122 }
1124 cairo_matrix_t pattern_matrix;
1125 if (g->gradientTransform_set) {
1126 // apply gradient transformation
1127 cairo_matrix_init(&pattern_matrix,
1128 g->gradientTransform[0], g->gradientTransform[1],
1129 g->gradientTransform[2], g->gradientTransform[3],
1130 g->gradientTransform[4], g->gradientTransform[5]);
1131 } else {
1132 cairo_matrix_init_identity (&pattern_matrix);
1133 }
1135 if (apply_bbox2user) {
1136 // convert to userspace
1137 cairo_matrix_t bbox2user;
1138 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1139 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1140 }
1141 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1142 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1143 }
1145 return pattern;
1146 }
1148 void
1149 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1150 {
1151 g_return_if_fail( style->fill.isColor()
1152 || style->fill.isPaintserver() );
1154 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1155 if (_state->merge_opacity) {
1156 alpha *= _state->opacity;
1157 TRACE(("merged op=%f\n", alpha));
1158 }
1160 if (style->fill.isColor()) {
1161 float rgb[3];
1162 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1164 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1165 } else {
1166 g_assert( style->fill.isPaintserver()
1167 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1168 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1170 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1172 if (pattern) {
1173 cairo_set_source(_cr, pattern);
1174 cairo_pattern_destroy(pattern);
1175 }
1176 }
1177 }
1179 void
1180 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1181 {
1182 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1183 if (_state->merge_opacity)
1184 alpha *= _state->opacity;
1186 if (style->stroke.isColor()) {
1187 float rgb[3];
1188 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1190 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1191 } else {
1192 g_assert( style->fill.isPaintserver()
1193 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1194 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1196 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1198 if (pattern) {
1199 cairo_set_source(_cr, pattern);
1200 cairo_pattern_destroy(pattern);
1201 }
1202 }
1204 if (style->stroke_dash.n_dash &&
1205 style->stroke_dash.dash )
1206 {
1207 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1208 } else {
1209 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1210 }
1212 cairo_set_line_width(_cr, style->stroke_width.computed);
1214 // set line join type
1215 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1216 switch (style->stroke_linejoin.computed) {
1217 case SP_STROKE_LINEJOIN_MITER:
1218 join = CAIRO_LINE_JOIN_MITER;
1219 break;
1220 case SP_STROKE_LINEJOIN_ROUND:
1221 join = CAIRO_LINE_JOIN_ROUND;
1222 break;
1223 case SP_STROKE_LINEJOIN_BEVEL:
1224 join = CAIRO_LINE_JOIN_BEVEL;
1225 break;
1226 }
1227 cairo_set_line_join(_cr, join);
1229 // set line cap type
1230 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1231 switch (style->stroke_linecap.computed) {
1232 case SP_STROKE_LINECAP_BUTT:
1233 cap = CAIRO_LINE_CAP_BUTT;
1234 break;
1235 case SP_STROKE_LINECAP_ROUND:
1236 cap = CAIRO_LINE_CAP_ROUND;
1237 break;
1238 case SP_STROKE_LINECAP_SQUARE:
1239 cap = CAIRO_LINE_CAP_SQUARE;
1240 break;
1241 }
1242 cairo_set_line_cap(_cr, cap);
1243 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1244 }
1246 bool
1247 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1248 {
1249 g_assert( _is_valid );
1251 if (_render_mode == RENDER_MODE_CLIP) {
1252 if (_clip_mode == CLIP_MODE_PATH) {
1253 addClipPath(pathv, &style->fill_rule);
1254 } else {
1255 setPathVector(pathv);
1256 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1257 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1258 } else {
1259 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1260 }
1261 cairo_fill(_cr);
1262 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1263 }
1264 return true;
1265 }
1267 if (style->fill.isNone() && style->stroke.isNone())
1268 return true;
1270 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1271 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1273 if (!need_layer)
1274 cairo_save(_cr);
1275 else
1276 pushLayer();
1278 if (!style->fill.isNone()) {
1279 _setFillStyle(style, pbox);
1280 setPathVector(pathv);
1282 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1283 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1284 } else {
1285 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1286 }
1288 if (style->stroke.isNone())
1289 cairo_fill(_cr);
1290 else
1291 cairo_fill_preserve(_cr);
1292 }
1294 if (!style->stroke.isNone()) {
1295 _setStrokeStyle(style, pbox);
1296 if (style->fill.isNone())
1297 setPathVector(pathv);
1299 cairo_stroke(_cr);
1300 }
1302 if (need_layer)
1303 popLayer();
1304 else
1305 cairo_restore(_cr);
1307 return true;
1308 }
1310 bool
1311 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1312 Geom::Matrix const *image_transform, SPStyle const *style)
1313 {
1314 g_assert( _is_valid );
1316 if (_render_mode == RENDER_MODE_CLIP)
1317 return true;
1319 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1320 if (!px_rgba)
1321 return false;
1323 float opacity;
1324 if (_state->merge_opacity)
1325 opacity = _state->opacity;
1326 else
1327 opacity = 1.0;
1329 // make a copy of the original pixbuf with premultiplied alpha
1330 // if we pass the original pixbuf it will get messed up
1331 for (unsigned i = 0; i < h; i++) {
1332 for (unsigned j = 0; j < w; j++) {
1333 guchar const *src = px + i * rs + j * 4;
1334 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1335 guchar r, g, b, alpha_dst;
1337 // calculate opacity-modified alpha
1338 alpha_dst = src[3];
1339 if (opacity != 1.0 && _vector_based_target)
1340 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1342 // premul alpha (needed because this will be undone by cairo-pdf)
1343 r = src[0]*alpha_dst/255;
1344 g = src[1]*alpha_dst/255;
1345 b = src[2]*alpha_dst/255;
1347 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1348 }
1349 }
1351 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1352 if (cairo_surface_status(image_surface)) {
1353 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1354 return false;
1355 }
1357 // setup automatic freeing of the image data when destroying the surface
1358 static cairo_user_data_key_t key;
1359 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1361 cairo_save(_cr);
1363 // scaling by width & height is not needed because it will be done by Cairo
1364 if (image_transform)
1365 transform(image_transform);
1367 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1369 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1370 if (_vector_based_target) {
1371 cairo_new_path(_cr);
1372 cairo_rectangle(_cr, 0, 0, w, h);
1373 cairo_clip(_cr);
1374 }
1376 if (_vector_based_target)
1377 cairo_paint(_cr);
1378 else
1379 cairo_paint_with_alpha(_cr, opacity);
1381 cairo_restore(_cr);
1383 cairo_surface_destroy(image_surface);
1385 return true;
1386 }
1388 #define GLYPH_ARRAY_SIZE 64
1390 unsigned int
1391 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1392 {
1393 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1394 cairo_glyph_t *glyphs = glyph_array;
1395 unsigned int num_glyphs = glyphtext.size();
1396 if (num_glyphs > GLYPH_ARRAY_SIZE)
1397 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1399 unsigned int num_invalid_glyphs = 0;
1400 unsigned int i = 0;
1401 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1402 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1403 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1404 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1405 TRACE(("INVALID GLYPH found\n"));
1406 num_invalid_glyphs++;
1407 continue;
1408 }
1409 glyphs[i - num_invalid_glyphs].index = it_info->index;
1410 glyphs[i - num_invalid_glyphs].x = it_info->x;
1411 glyphs[i - num_invalid_glyphs].y = it_info->y;
1412 i++;
1413 }
1415 if (is_stroke || _is_texttopath)
1416 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1417 else
1418 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1420 if (num_glyphs > GLYPH_ARRAY_SIZE)
1421 g_free(glyphs);
1423 return num_glyphs - num_invalid_glyphs;
1424 }
1426 bool
1427 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1428 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1429 {
1430 // create a cairo_font_face from PangoFont
1431 double size = style->font_size.computed;
1432 cairo_font_face_t *font_face = NULL;
1434 FcPattern *fc_pattern = NULL;
1436 #ifdef USE_PANGO_WIN32
1437 # ifdef CAIRO_HAS_WIN32_FONT
1438 LOGFONTA *lfa = pango_win32_font_logfont(font);
1439 LOGFONTW lfw;
1441 ZeroMemory(&lfw, sizeof(LOGFONTW));
1442 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1443 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1445 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1446 # endif
1447 #else
1448 # ifdef CAIRO_HAS_FT_FONT
1449 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1450 fc_pattern = fc_font->font_pattern;
1451 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1452 # endif
1453 #endif
1455 cairo_save(_cr);
1456 cairo_set_font_face(_cr, font_face);
1458 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1459 size = 12.0;
1461 // set the given font matrix
1462 cairo_matrix_t matrix;
1463 _initCairoMatrix(&matrix, font_matrix);
1464 cairo_set_font_matrix(_cr, &matrix);
1466 if (_render_mode == RENDER_MODE_CLIP) {
1467 if (_clip_mode == CLIP_MODE_MASK) {
1468 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1469 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1470 } else {
1471 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1472 }
1473 _showGlyphs(_cr, font, glyphtext, FALSE);
1474 } else {
1475 // just add the glyph paths to the current context
1476 _showGlyphs(_cr, font, glyphtext, TRUE);
1477 }
1478 } else {
1480 if (style->fill.isColor() || style->fill.isPaintserver()) {
1481 // set fill style
1482 _setFillStyle(style, NULL);
1484 _showGlyphs(_cr, font, glyphtext, FALSE);
1485 }
1487 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1488 // set stroke style
1489 _setStrokeStyle(style, NULL);
1491 // paint stroke
1492 _showGlyphs(_cr, font, glyphtext, TRUE);
1493 cairo_stroke(_cr);
1494 }
1495 }
1497 cairo_restore(_cr);
1499 if (font_face)
1500 cairo_font_face_destroy(font_face);
1502 return true;
1503 }
1505 /* Helper functions */
1507 void
1508 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1509 {
1510 cairo_new_path(_cr);
1511 addPathVector(pv);
1512 }
1514 void
1515 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1516 {
1517 feed_pathvector_to_cairo(_cr, pv);
1518 }
1520 void
1521 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1522 {
1523 cairo_matrix_t matrix;
1525 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1526 cairo_transform(cr, &matrix);
1527 }
1529 void
1530 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1531 {
1532 matrix->xx = (*transform)[0];
1533 matrix->yx = (*transform)[1];
1534 matrix->xy = (*transform)[2];
1535 matrix->yy = (*transform)[3];
1536 matrix->x0 = (*transform)[4];
1537 matrix->y0 = (*transform)[5];
1538 }
1540 void
1541 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1542 {
1543 _concatTransform(cr, (*transform)[0], (*transform)[1],
1544 (*transform)[2], (*transform)[3],
1545 (*transform)[4], (*transform)[5]);
1546 }
1548 static cairo_status_t
1549 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1550 {
1551 size_t written;
1552 FILE *file = (FILE*)closure;
1554 written = fwrite (data, 1, length, file);
1556 if (written == length)
1557 return CAIRO_STATUS_SUCCESS;
1558 else
1559 return CAIRO_STATUS_WRITE_ERROR;
1560 }
1562 #include "clear-n_.h"
1564 } /* namespace Internal */
1565 } /* namespace Extension */
1566 } /* namespace Inkscape */
1568 #undef TRACE
1569 #undef TEST
1571 /* End of GNU GPL code */
1574 /*
1575 Local Variables:
1576 mode:c++
1577 c-file-style:"stroustrup"
1578 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1579 indent-tabs-mode:nil
1580 fill-column:99
1581 End:
1582 */
1583 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :