f308ce15753de2441e9e32106e5b085c3bd6332f
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 _eps(false),
113 _is_texttopath(FALSE),
114 _is_filtertobitmap(FALSE),
115 _bitmapresolution(72),
116 _stream(NULL),
117 _is_valid(FALSE),
118 _vector_based_target(FALSE),
119 _cr(NULL),
120 _surface(NULL),
121 _target(CAIRO_SURFACE_TYPE_IMAGE),
122 _target_format(CAIRO_FORMAT_ARGB32),
123 _layout(NULL),
124 _state(NULL),
125 _renderer(parent),
126 _render_mode(RENDER_MODE_NORMAL),
127 _clip_mode(CLIP_MODE_MASK)
128 {}
130 CairoRenderContext::~CairoRenderContext(void)
131 {
132 if (_cr) cairo_destroy(_cr);
133 if (_surface) cairo_surface_destroy(_surface);
134 if (_layout) g_object_unref(_layout);
135 }
137 CairoRenderer*
138 CairoRenderContext::getRenderer(void) const
139 {
140 return _renderer;
141 }
143 CairoRenderState*
144 CairoRenderContext::getCurrentState(void) const
145 {
146 return _state;
147 }
149 CairoRenderState*
150 CairoRenderContext::getParentState(void) const
151 {
152 // if this is the root node just return it
153 if (g_slist_length(_state_stack) == 1) {
154 return _state;
155 } else {
156 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
157 }
158 }
160 void
161 CairoRenderContext::setStateForStyle(SPStyle const *style)
162 {
163 // only opacity & overflow is stored for now
164 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
165 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
166 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
168 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
169 _state->merge_opacity = FALSE;
171 // disable rendering of opacity if there's a stroke on the fill
172 if (_state->merge_opacity
173 && !style->fill.isNone()
174 && !style->stroke.isNone())
175 _state->merge_opacity = FALSE;
176 }
178 /**
179 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
180 *
181 * \param width width of the surface to be created
182 * \param height height of the surface to be created
183 */
184 CairoRenderContext*
185 CairoRenderContext::cloneMe(double width, double height) const
186 {
187 g_assert( _is_valid );
188 g_assert( width > 0.0 && height > 0.0 );
190 CairoRenderContext *new_context = _renderer->createContext();
191 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
192 (int)ceil(width), (int)ceil(height));
193 new_context->_cr = cairo_create(surface);
194 new_context->_surface = surface;
195 new_context->_is_valid = TRUE;
197 return new_context;
198 }
200 CairoRenderContext*
201 CairoRenderContext::cloneMe(void) const
202 {
203 g_assert( _is_valid );
205 return cloneMe(_width, _height);
206 }
208 bool
209 CairoRenderContext::setImageTarget(cairo_format_t format)
210 {
211 // format cannot be set on an already initialized surface
212 if (_is_valid)
213 return false;
215 switch (format) {
216 case CAIRO_FORMAT_ARGB32:
217 case CAIRO_FORMAT_RGB24:
218 case CAIRO_FORMAT_A8:
219 case CAIRO_FORMAT_A1:
220 _target_format = format;
221 _target = CAIRO_SURFACE_TYPE_IMAGE;
222 return true;
223 break;
224 default:
225 break;
226 }
228 return false;
229 }
231 bool
232 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
233 {
234 #ifndef CAIRO_HAS_PDF_SURFACE
235 return false;
236 #else
237 _target = CAIRO_SURFACE_TYPE_PDF;
238 _vector_based_target = TRUE;
239 #endif
241 FILE *osf = NULL;
242 FILE *osp = NULL;
244 gsize bytesRead = 0;
245 gsize bytesWritten = 0;
246 GError *error = NULL;
247 gchar *local_fn = g_filename_from_utf8(utf8_fn,
248 -1, &bytesRead, &bytesWritten, &error);
249 gchar const *fn = local_fn;
251 /* TODO: Replace the below fprintf's with something that does the right thing whether in
252 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
253 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
254 * return code.
255 */
256 if (fn != NULL) {
257 if (*fn == '|') {
258 fn += 1;
259 while (isspace(*fn)) fn += 1;
260 #ifndef WIN32
261 osp = popen(fn, "w");
262 #else
263 osp = _popen(fn, "w");
264 #endif
265 if (!osp) {
266 fprintf(stderr, "inkscape: popen(%s): %s\n",
267 fn, strerror(errno));
268 return false;
269 }
270 _stream = osp;
271 } else if (*fn == '>') {
272 fn += 1;
273 while (isspace(*fn)) fn += 1;
274 Inkscape::IO::dump_fopen_call(fn, "K");
275 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
276 if (!osf) {
277 fprintf(stderr, "inkscape: fopen(%s): %s\n",
278 fn, strerror(errno));
279 return false;
280 }
281 _stream = osf;
282 } else {
283 /* put cwd stuff in here */
284 gchar *qn = ( *fn
285 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
286 : g_strdup("lpr") );
287 #ifndef WIN32
288 osp = popen(qn, "w");
289 #else
290 osp = _popen(qn, "w");
291 #endif
292 if (!osp) {
293 fprintf(stderr, "inkscape: popen(%s): %s\n",
294 qn, strerror(errno));
295 return false;
296 }
297 g_free(qn);
298 _stream = osp;
299 }
300 }
302 g_free(local_fn);
304 if (_stream) {
305 /* fixme: this is kinda icky */
306 #if !defined(_WIN32) && !defined(__WIN32__)
307 (void) signal(SIGPIPE, SIG_IGN);
308 #endif
309 }
311 return true;
312 }
314 bool
315 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
316 {
317 #ifndef CAIRO_HAS_PS_SURFACE
318 return false;
319 #else
320 _target = CAIRO_SURFACE_TYPE_PS;
321 _vector_based_target = TRUE;
322 #endif
324 FILE *osf = NULL;
325 FILE *osp = NULL;
327 gsize bytesRead = 0;
328 gsize bytesWritten = 0;
329 GError *error = NULL;
330 gchar *local_fn = g_filename_from_utf8(utf8_fn,
331 -1, &bytesRead, &bytesWritten, &error);
332 gchar const *fn = local_fn;
334 /* TODO: Replace the below fprintf's with something that does the right thing whether in
335 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
336 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
337 * return code.
338 */
339 if (fn != NULL) {
340 if (*fn == '|') {
341 fn += 1;
342 while (isspace(*fn)) fn += 1;
343 #ifndef WIN32
344 osp = popen(fn, "w");
345 #else
346 osp = _popen(fn, "w");
347 #endif
348 if (!osp) {
349 fprintf(stderr, "inkscape: popen(%s): %s\n",
350 fn, strerror(errno));
351 return false;
352 }
353 _stream = osp;
354 } else if (*fn == '>') {
355 fn += 1;
356 while (isspace(*fn)) fn += 1;
357 Inkscape::IO::dump_fopen_call(fn, "K");
358 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
359 if (!osf) {
360 fprintf(stderr, "inkscape: fopen(%s): %s\n",
361 fn, strerror(errno));
362 return false;
363 }
364 _stream = osf;
365 } else {
366 /* put cwd stuff in here */
367 gchar *qn = ( *fn
368 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
369 : g_strdup("lpr") );
370 #ifndef WIN32
371 osp = popen(qn, "w");
372 #else
373 osp = _popen(qn, "w");
374 #endif
375 if (!osp) {
376 fprintf(stderr, "inkscape: popen(%s): %s\n",
377 qn, strerror(errno));
378 return false;
379 }
380 g_free(qn);
381 _stream = osp;
382 }
383 }
385 g_free(local_fn);
387 if (_stream) {
388 /* fixme: this is kinda icky */
389 #if !defined(_WIN32) && !defined(__WIN32__)
390 (void) signal(SIGPIPE, SIG_IGN);
391 #endif
392 }
394 return true;
395 }
397 void CairoRenderContext::setPSLevel(unsigned int level)
398 {
399 _ps_level = level;
400 }
402 void CairoRenderContext::setEPS(bool eps)
403 {
404 _eps = eps;
405 }
407 unsigned int CairoRenderContext::getPSLevel(void)
408 {
409 return _ps_level;
410 }
412 void CairoRenderContext::setPDFLevel(unsigned int level)
413 {
414 _pdf_level = level;
415 }
417 void CairoRenderContext::setTextToPath(bool texttopath)
418 {
419 _is_texttopath = texttopath;
420 }
422 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
423 {
424 _is_filtertobitmap = filtertobitmap;
425 }
427 bool CairoRenderContext::getFilterToBitmap(void)
428 {
429 return _is_filtertobitmap;
430 }
432 void CairoRenderContext::setBitmapResolution(int resolution)
433 {
434 _bitmapresolution = resolution;
435 }
437 int CairoRenderContext::getBitmapResolution(void)
438 {
439 return _bitmapresolution;
440 }
442 cairo_surface_t*
443 CairoRenderContext::getSurface(void)
444 {
445 g_assert( _is_valid );
447 return _surface;
448 }
450 bool
451 CairoRenderContext::saveAsPng(const char *file_name)
452 {
453 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
454 if (status)
455 return false;
456 else
457 return true;
458 }
460 void
461 CairoRenderContext::setRenderMode(CairoRenderMode mode)
462 {
463 switch (mode) {
464 case RENDER_MODE_NORMAL:
465 case RENDER_MODE_CLIP:
466 _render_mode = mode;
467 break;
468 default:
469 _render_mode = RENDER_MODE_NORMAL;
470 break;
471 }
472 }
474 CairoRenderContext::CairoRenderMode
475 CairoRenderContext::getRenderMode(void) const
476 {
477 return _render_mode;
478 }
480 void
481 CairoRenderContext::setClipMode(CairoClipMode mode)
482 {
483 switch (mode) {
484 case CLIP_MODE_PATH:
485 case CLIP_MODE_MASK:
486 _clip_mode = mode;
487 break;
488 default:
489 _clip_mode = CLIP_MODE_PATH;
490 break;
491 }
492 }
494 CairoRenderContext::CairoClipMode
495 CairoRenderContext::getClipMode(void) const
496 {
497 return _clip_mode;
498 }
500 CairoRenderState*
501 CairoRenderContext::_createState(void)
502 {
503 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
504 g_assert( state != NULL );
506 state->has_filtereffect = FALSE;
507 state->merge_opacity = TRUE;
508 state->opacity = 1.0;
509 state->need_layer = FALSE;
510 state->has_overflow = FALSE;
511 state->parent_has_userspace = FALSE;
512 state->clip_path = NULL;
513 state->mask = NULL;
515 return state;
516 }
518 void
519 CairoRenderContext::pushLayer(void)
520 {
521 g_assert( _is_valid );
523 TRACE(("--pushLayer\n"));
524 cairo_push_group(_cr);
526 // clear buffer
527 if (!_vector_based_target) {
528 cairo_save(_cr);
529 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
530 cairo_paint(_cr);
531 cairo_restore(_cr);
532 }
533 }
535 void
536 CairoRenderContext::popLayer(void)
537 {
538 g_assert( _is_valid );
540 float opacity = _state->opacity;
541 TRACE(("--popLayer w/ %f\n", opacity));
543 // apply clipPath or mask if present
544 SPClipPath *clip_path = _state->clip_path;
545 SPMask *mask = _state->mask;
546 if (clip_path || mask) {
548 CairoRenderContext *clip_ctx = 0;
549 cairo_surface_t *clip_mask = 0;
551 if (clip_path) {
552 if (_render_mode == RENDER_MODE_CLIP)
553 mask = NULL; // disable mask when performing nested clipping
555 if (_vector_based_target) {
556 setClipMode(CLIP_MODE_PATH);
557 if (!mask) {
558 cairo_pop_group_to_source(_cr);
559 _renderer->applyClipPath(this, clip_path);
560 if (opacity == 1.0)
561 cairo_paint(_cr);
562 else
563 cairo_paint_with_alpha(_cr, opacity);
565 } else {
566 // the clipPath will be applied before masking
567 }
568 } else {
570 // setup a new rendering context
571 clip_ctx = _renderer->createContext();
572 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
573 clip_ctx->setClipMode(CLIP_MODE_MASK);
574 if (!clip_ctx->setupSurface(_width, _height)) {
575 TRACE(("clip: setupSurface failed\n"));
576 _renderer->destroyContext(clip_ctx);
577 return;
578 }
580 // clear buffer
581 cairo_save(clip_ctx->_cr);
582 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
583 cairo_paint(clip_ctx->_cr);
584 cairo_restore(clip_ctx->_cr);
586 // if a mask won't be applied set opacity too
587 if (!mask)
588 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
589 else
590 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
592 // copy over the correct CTM
593 // It must be stored in item_transform of current state after pushState.
594 Geom::Matrix item_transform;
595 if (_state->parent_has_userspace)
596 item_transform = getParentState()->transform * _state->item_transform;
597 else
598 item_transform = _state->item_transform;
600 // apply the clip path
601 clip_ctx->pushState();
602 clip_ctx->getCurrentState()->item_transform = item_transform;
603 _renderer->applyClipPath(clip_ctx, clip_path);
604 clip_ctx->popState();
606 clip_mask = clip_ctx->getSurface();
607 TEST(clip_ctx->saveAsPng("clip_mask.png"));
609 if (!mask) {
610 cairo_pop_group_to_source(_cr);
611 cairo_mask_surface(_cr, clip_mask, 0, 0);
612 _renderer->destroyContext(clip_ctx);
613 }
614 }
615 }
617 if (mask) {
618 // create rendering context for mask
619 CairoRenderContext *mask_ctx = _renderer->createContext();
621 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
622 // Cairo surface is expecting the mask to be 90 dpi.
623 float surface_width = _width;
624 float surface_height = _height;
625 if( _vector_based_target ) {
626 surface_width *= 1.25;
627 surface_height *= 1.25;
628 }
629 mask_ctx->setupSurface( surface_width, surface_height );
630 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
632 // set rendering mode to normal
633 setRenderMode(RENDER_MODE_NORMAL);
635 // copy the correct CTM to mask context
636 /*
637 if (_state->parent_has_userspace)
638 mask_ctx->setTransform(&getParentState()->transform);
639 else
640 mask_ctx->setTransform(&_state->transform);
641 */
642 // This is probably not correct... but it seems to do the trick.
643 mask_ctx->setTransform(&_state->item_transform);
645 // render mask contents to mask_ctx
646 _renderer->applyMask(mask_ctx, mask);
648 TEST(mask_ctx->saveAsPng("mask.png"));
650 // composite with clip mask
651 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
652 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
653 _renderer->destroyContext(clip_ctx);
654 }
656 cairo_surface_t *mask_image = mask_ctx->getSurface();
657 int width = cairo_image_surface_get_width(mask_image);
658 int height = cairo_image_surface_get_height(mask_image);
659 int stride = cairo_image_surface_get_stride(mask_image);
660 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
662 // premultiply with opacity
663 // In SVG, the rgb channels as well as the alpha channel is used in masking.
664 // In Cairo, only the alpha channel is used thus requiring this conversion.
665 TRACE(("premul w/ %f\n", opacity));
666 guint8 int_opacity = (guint8)(255 * opacity);
667 for (int row = 0 ; row < height; row++) {
668 unsigned char *row_data = pixels + (row * stride);
669 for (int i = 0 ; i < width; i++) {
670 guint32 *pixel = (guint32 *)row_data + i;
671 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
672 ((*pixel & 0x0000ff00) >> 8) * 46518 +
673 ((*pixel & 0x000000ff) ) * 4688) *
674 int_opacity);
675 }
676 }
678 cairo_pop_group_to_source(_cr);
679 if (_clip_mode == CLIP_MODE_PATH) {
680 // we have to do the clipping after cairo_pop_group_to_source
681 _renderer->applyClipPath(this, clip_path);
682 }
683 // apply the mask onto the layer
684 cairo_mask_surface(_cr, mask_image, 0, 0);
685 _renderer->destroyContext(mask_ctx);
686 }
687 } else {
688 cairo_pop_group_to_source(_cr);
689 if (opacity == 1.0)
690 cairo_paint(_cr);
691 else
692 cairo_paint_with_alpha(_cr, opacity);
693 }
694 }
696 void
697 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
698 {
699 g_assert( _is_valid );
701 // here it should be checked whether the current clip winding changed
702 // so we could switch back to masked clipping
703 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
704 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
705 } else {
706 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
707 }
708 addPathVector(pv);
709 }
711 void
712 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
713 {
714 g_assert( _is_valid );
716 cairo_rectangle(_cr, x, y, width, height);
717 cairo_clip(_cr);
718 }
720 bool
721 CairoRenderContext::setupSurface(double width, double height)
722 {
723 // Is the surface already set up?
724 if (_is_valid)
725 return true;
727 if (_vector_based_target && _stream == NULL)
728 return false;
730 cairo_surface_t *surface = NULL;
731 switch (_target) {
732 case CAIRO_SURFACE_TYPE_IMAGE:
733 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
734 break;
735 #ifdef CAIRO_HAS_PDF_SURFACE
736 case CAIRO_SURFACE_TYPE_PDF:
737 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
738 break;
739 #endif
740 #ifdef CAIRO_HAS_PS_SURFACE
741 case CAIRO_SURFACE_TYPE_PS:
742 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
743 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
744 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
745 return FALSE;
746 }
747 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
748 cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
749 #endif
750 break;
751 #endif
752 default:
753 return false;
754 break;
755 }
757 return _finishSurfaceSetup (surface);
758 }
760 bool
761 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
762 {
763 if (_is_valid || !surface)
764 return false;
766 _vector_based_target = is_vector;
767 bool ret = _finishSurfaceSetup (surface);
768 if (ret)
769 cairo_surface_reference (surface);
770 return ret;
771 }
773 bool
774 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
775 {
776 if(surface == NULL) {
777 return FALSE;
778 }
779 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
780 return FALSE;
781 }
783 _cr = cairo_create(surface);
784 _surface = surface;
786 if (_vector_based_target) {
787 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
788 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
789 // set background color on non-alpha surfaces
790 // TODO: bgcolor should be derived from SPDocument
791 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
792 cairo_rectangle(_cr, 0, 0, _width, _height);
793 cairo_fill(_cr);
794 }
796 _is_valid = TRUE;
798 return true;
799 }
801 bool
802 CairoRenderContext::finish(void)
803 {
804 g_assert( _is_valid );
806 if (_vector_based_target)
807 cairo_show_page(_cr);
809 cairo_destroy(_cr);
810 cairo_surface_finish(_surface);
811 cairo_status_t status = cairo_surface_status(_surface);
812 cairo_surface_destroy(_surface);
813 _cr = NULL;
814 _surface = NULL;
816 if (_layout)
817 g_object_unref(_layout);
819 _is_valid = FALSE;
821 if (_vector_based_target && _stream) {
822 /* Flush stream to be sure. */
823 (void) fflush(_stream);
825 fclose(_stream);
826 _stream = NULL;
827 }
829 if (status == CAIRO_STATUS_SUCCESS)
830 return true;
831 else
832 return false;
833 }
835 void
836 CairoRenderContext::transform(Geom::Matrix const *transform)
837 {
838 g_assert( _is_valid );
840 cairo_matrix_t matrix;
841 _initCairoMatrix(&matrix, transform);
842 cairo_transform(_cr, &matrix);
844 // store new CTM
845 getTransform(&_state->transform);
846 }
848 void
849 CairoRenderContext::setTransform(Geom::Matrix const *transform)
850 {
851 g_assert( _is_valid );
853 cairo_matrix_t matrix;
854 _initCairoMatrix(&matrix, transform);
855 cairo_set_matrix(_cr, &matrix);
856 _state->transform = *transform;
857 }
859 void
860 CairoRenderContext::getTransform(Geom::Matrix *copy) const
861 {
862 g_assert( _is_valid );
864 cairo_matrix_t ctm;
865 cairo_get_matrix(_cr, &ctm);
866 (*copy)[0] = ctm.xx;
867 (*copy)[1] = ctm.yx;
868 (*copy)[2] = ctm.xy;
869 (*copy)[3] = ctm.yy;
870 (*copy)[4] = ctm.x0;
871 (*copy)[5] = ctm.y0;
872 }
874 void
875 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
876 {
877 g_assert( _is_valid );
879 CairoRenderState *parent_state = getParentState();
880 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
881 }
883 void
884 CairoRenderContext::pushState(void)
885 {
886 g_assert( _is_valid );
888 cairo_save(_cr);
890 CairoRenderState *new_state = _createState();
891 // copy current state's transform
892 new_state->transform = _state->transform;
893 _state_stack = g_slist_prepend(_state_stack, new_state);
894 _state = new_state;
895 }
897 void
898 CairoRenderContext::popState(void)
899 {
900 g_assert( _is_valid );
902 cairo_restore(_cr);
904 g_free(_state_stack->data);
905 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
906 _state = (CairoRenderState*)_state_stack->data;
908 g_assert( g_slist_length(_state_stack) > 0 );
909 }
911 static bool pattern_hasItemChildren (SPPattern *pat)
912 {
913 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
914 if (SP_IS_ITEM (child)) {
915 return true;
916 }
917 }
918 return false;
919 }
921 cairo_pattern_t*
922 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
923 {
924 g_assert( SP_IS_PATTERN(paintserver) );
926 SPPattern *pat = SP_PATTERN (paintserver);
928 Geom::Matrix ps2user, pcs2dev;
929 ps2user = Geom::identity();
930 pcs2dev = Geom::identity();
932 double x = pattern_x(pat);
933 double y = pattern_y(pat);
934 double width = pattern_width(pat);
935 double height = pattern_height(pat);
936 double bbox_width_scaler;
937 double bbox_height_scaler;
939 TRACE(("%f x %f pattern\n", width, height));
941 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
942 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
943 bbox_width_scaler = pbox->x1 - pbox->x0;
944 bbox_height_scaler = pbox->y1 - pbox->y0;
945 ps2user[4] = x * bbox_width_scaler + pbox->x0;
946 ps2user[5] = y * bbox_height_scaler + pbox->y0;
947 } else {
948 bbox_width_scaler = 1.0;
949 bbox_height_scaler = 1.0;
950 ps2user[4] = x;
951 ps2user[5] = y;
952 }
954 // apply pattern transformation
955 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
956 ps2user *= pattern_transform;
957 Geom::Point ori (ps2user[4], ps2user[5]);
959 // create pattern contents coordinate system
960 if (pat->viewBox_set) {
961 NRRect *view_box = pattern_viewBox(pat);
963 double x, y, w, h;
964 double view_width, view_height;
965 x = 0;
966 y = 0;
967 w = width * bbox_width_scaler;
968 h = height * bbox_height_scaler;
970 view_width = view_box->x1 - view_box->x0;
971 view_height = view_box->y1 - view_box->y0;
973 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
974 pcs2dev[0] = w / view_width;
975 pcs2dev[3] = h / view_height;
976 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
977 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
978 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
979 pcs2dev[0] = pbox->x1 - pbox->x0;
980 pcs2dev[3] = pbox->y1 - pbox->y0;
982 }
984 // Calculate the size of the surface which has to be created
985 #define SUBPIX_SCALE 100
986 // Cairo requires an integer pattern surface width/height.
987 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
988 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
989 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
990 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
991 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
992 // create new rendering context
993 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
995 // adjust the size of the painted pattern to fit exactly the created surface
996 // this has to be done because of the rounding to obtain an integer pattern surface width/height
997 double scale_width = surface_width / (bbox_width_scaler * width);
998 double scale_height = surface_height / (bbox_height_scaler * height);
999 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1000 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1001 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1002 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1003 }
1005 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1006 ps2user[4] = ori[Geom::X];
1007 ps2user[5] = ori[Geom::Y];
1009 pattern_ctx->setTransform(&pcs2dev);
1010 pattern_ctx->pushState();
1012 // create arena and group
1013 NRArena *arena = NRArena::create();
1014 unsigned dkey = sp_item_display_key_new(1);
1016 // show items and render them
1017 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1018 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1019 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1020 if (SP_IS_ITEM (child)) {
1021 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1022 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1023 }
1024 }
1025 break; // do not go further up the chain if children are found
1026 }
1027 }
1029 pattern_ctx->popState();
1031 // setup a cairo_pattern_t
1032 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1033 TEST(pattern_ctx->saveAsPng("pattern.png"));
1034 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1035 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1037 // set pattern transformation
1038 cairo_matrix_t pattern_matrix;
1039 _initCairoMatrix(&pattern_matrix, &ps2user);
1040 cairo_matrix_invert(&pattern_matrix);
1041 cairo_pattern_set_matrix(result, &pattern_matrix);
1043 delete pattern_ctx;
1045 // hide all items
1046 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1047 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1048 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1049 if (SP_IS_ITEM (child)) {
1050 sp_item_invoke_hide (SP_ITEM (child), dkey);
1051 }
1052 }
1053 break; // do not go further up the chain if children are found
1054 }
1055 }
1057 return result;
1058 }
1060 cairo_pattern_t*
1061 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1062 NRRect const *pbox, float alpha)
1063 {
1064 cairo_pattern_t *pattern = NULL;
1065 bool apply_bbox2user = FALSE;
1067 if (SP_IS_LINEARGRADIENT (paintserver)) {
1069 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1071 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1073 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1074 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1075 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1076 // convert to userspace
1077 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1078 p1 *= bbox2user;
1079 p2 *= bbox2user;
1080 }
1082 // create linear gradient pattern
1083 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1085 // add stops
1086 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1087 float rgb[3];
1088 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1089 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1090 }
1091 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1093 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1095 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1097 Geom::Point c (rg->cx.computed, rg->cy.computed);
1098 Geom::Point f (rg->fx.computed, rg->fy.computed);
1099 double r = rg->r.computed;
1100 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1101 apply_bbox2user = true;
1103 // create radial gradient pattern
1104 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1106 // add stops
1107 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1108 float rgb[3];
1109 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1110 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1111 }
1112 } else if (SP_IS_PATTERN (paintserver)) {
1114 pattern = _createPatternPainter(paintserver, pbox);
1115 } else {
1116 return NULL;
1117 }
1119 if (pattern && SP_IS_GRADIENT (paintserver)) {
1120 SPGradient *g = SP_GRADIENT(paintserver);
1122 // set extend type
1123 SPGradientSpread spread = sp_gradient_get_spread(g);
1124 switch (spread) {
1125 case SP_GRADIENT_SPREAD_REPEAT: {
1126 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1127 break;
1128 }
1129 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1130 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1131 break;
1132 }
1133 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1134 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1135 break;
1136 }
1137 default: {
1138 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1139 break;
1140 }
1141 }
1143 cairo_matrix_t pattern_matrix;
1144 if (g->gradientTransform_set) {
1145 // apply gradient transformation
1146 cairo_matrix_init(&pattern_matrix,
1147 g->gradientTransform[0], g->gradientTransform[1],
1148 g->gradientTransform[2], g->gradientTransform[3],
1149 g->gradientTransform[4], g->gradientTransform[5]);
1150 } else {
1151 cairo_matrix_init_identity (&pattern_matrix);
1152 }
1154 if (apply_bbox2user) {
1155 // convert to userspace
1156 cairo_matrix_t bbox2user;
1157 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1158 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1159 }
1160 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1161 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1162 }
1164 return pattern;
1165 }
1167 void
1168 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1169 {
1170 g_return_if_fail( style->fill.isColor()
1171 || style->fill.isPaintserver() );
1173 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1174 if (_state->merge_opacity) {
1175 alpha *= _state->opacity;
1176 TRACE(("merged op=%f\n", alpha));
1177 }
1179 if (style->fill.isColor()) {
1180 float rgb[3];
1181 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1183 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1184 } else {
1185 g_assert( style->fill.isPaintserver()
1186 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1187 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1189 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1191 if (pattern) {
1192 cairo_set_source(_cr, pattern);
1193 cairo_pattern_destroy(pattern);
1194 }
1195 }
1196 }
1198 void
1199 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1200 {
1201 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1202 if (_state->merge_opacity)
1203 alpha *= _state->opacity;
1205 if (style->stroke.isColor()) {
1206 float rgb[3];
1207 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1209 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1210 } else {
1211 g_assert( style->fill.isPaintserver()
1212 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1213 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1215 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1217 if (pattern) {
1218 cairo_set_source(_cr, pattern);
1219 cairo_pattern_destroy(pattern);
1220 }
1221 }
1223 if (style->stroke_dash.n_dash &&
1224 style->stroke_dash.dash )
1225 {
1226 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1227 } else {
1228 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1229 }
1231 cairo_set_line_width(_cr, style->stroke_width.computed);
1233 // set line join type
1234 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1235 switch (style->stroke_linejoin.computed) {
1236 case SP_STROKE_LINEJOIN_MITER:
1237 join = CAIRO_LINE_JOIN_MITER;
1238 break;
1239 case SP_STROKE_LINEJOIN_ROUND:
1240 join = CAIRO_LINE_JOIN_ROUND;
1241 break;
1242 case SP_STROKE_LINEJOIN_BEVEL:
1243 join = CAIRO_LINE_JOIN_BEVEL;
1244 break;
1245 }
1246 cairo_set_line_join(_cr, join);
1248 // set line cap type
1249 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1250 switch (style->stroke_linecap.computed) {
1251 case SP_STROKE_LINECAP_BUTT:
1252 cap = CAIRO_LINE_CAP_BUTT;
1253 break;
1254 case SP_STROKE_LINECAP_ROUND:
1255 cap = CAIRO_LINE_CAP_ROUND;
1256 break;
1257 case SP_STROKE_LINECAP_SQUARE:
1258 cap = CAIRO_LINE_CAP_SQUARE;
1259 break;
1260 }
1261 cairo_set_line_cap(_cr, cap);
1262 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1263 }
1265 bool
1266 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1267 {
1268 g_assert( _is_valid );
1270 if (_render_mode == RENDER_MODE_CLIP) {
1271 if (_clip_mode == CLIP_MODE_PATH) {
1272 addClipPath(pathv, &style->fill_rule);
1273 } else {
1274 setPathVector(pathv);
1275 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1276 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1277 } else {
1278 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1279 }
1280 cairo_fill(_cr);
1281 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1282 }
1283 return true;
1284 }
1286 if (style->fill.isNone() && style->stroke.isNone())
1287 return true;
1289 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1290 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1292 if (!need_layer)
1293 cairo_save(_cr);
1294 else
1295 pushLayer();
1297 if (!style->fill.isNone()) {
1298 _setFillStyle(style, pbox);
1299 setPathVector(pathv);
1301 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1302 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1303 } else {
1304 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1305 }
1307 if (style->stroke.isNone())
1308 cairo_fill(_cr);
1309 else
1310 cairo_fill_preserve(_cr);
1311 }
1313 if (!style->stroke.isNone()) {
1314 _setStrokeStyle(style, pbox);
1315 if (style->fill.isNone())
1316 setPathVector(pathv);
1318 cairo_stroke(_cr);
1319 }
1321 if (need_layer)
1322 popLayer();
1323 else
1324 cairo_restore(_cr);
1326 return true;
1327 }
1329 bool
1330 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1331 Geom::Matrix const *image_transform, SPStyle const *style)
1332 {
1333 g_assert( _is_valid );
1335 if (_render_mode == RENDER_MODE_CLIP)
1336 return true;
1338 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1339 if (!px_rgba)
1340 return false;
1342 float opacity;
1343 if (_state->merge_opacity)
1344 opacity = _state->opacity;
1345 else
1346 opacity = 1.0;
1348 // make a copy of the original pixbuf with premultiplied alpha
1349 // if we pass the original pixbuf it will get messed up
1350 for (unsigned i = 0; i < h; i++) {
1351 for (unsigned j = 0; j < w; j++) {
1352 guchar const *src = px + i * rs + j * 4;
1353 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1354 guchar r, g, b, alpha_dst;
1356 // calculate opacity-modified alpha
1357 alpha_dst = src[3];
1358 if (opacity != 1.0 && _vector_based_target)
1359 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1361 // premul alpha (needed because this will be undone by cairo-pdf)
1362 r = src[0]*alpha_dst/255;
1363 g = src[1]*alpha_dst/255;
1364 b = src[2]*alpha_dst/255;
1366 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1367 }
1368 }
1370 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1371 if (cairo_surface_status(image_surface)) {
1372 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1373 return false;
1374 }
1376 // setup automatic freeing of the image data when destroying the surface
1377 static cairo_user_data_key_t key;
1378 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1380 cairo_save(_cr);
1382 // scaling by width & height is not needed because it will be done by Cairo
1383 if (image_transform)
1384 transform(image_transform);
1386 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1388 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1389 if (_vector_based_target) {
1390 cairo_new_path(_cr);
1391 cairo_rectangle(_cr, 0, 0, w, h);
1392 cairo_clip(_cr);
1393 }
1395 if (_vector_based_target)
1396 cairo_paint(_cr);
1397 else
1398 cairo_paint_with_alpha(_cr, opacity);
1400 cairo_restore(_cr);
1402 cairo_surface_destroy(image_surface);
1404 return true;
1405 }
1407 #define GLYPH_ARRAY_SIZE 64
1409 unsigned int
1410 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1411 {
1412 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1413 cairo_glyph_t *glyphs = glyph_array;
1414 unsigned int num_glyphs = glyphtext.size();
1415 if (num_glyphs > GLYPH_ARRAY_SIZE)
1416 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1418 unsigned int num_invalid_glyphs = 0;
1419 unsigned int i = 0;
1420 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1421 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1422 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1423 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1424 TRACE(("INVALID GLYPH found\n"));
1425 num_invalid_glyphs++;
1426 continue;
1427 }
1428 glyphs[i - num_invalid_glyphs].index = it_info->index;
1429 glyphs[i - num_invalid_glyphs].x = it_info->x;
1430 glyphs[i - num_invalid_glyphs].y = it_info->y;
1431 i++;
1432 }
1434 if (is_stroke || _is_texttopath)
1435 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1436 else
1437 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1439 if (num_glyphs > GLYPH_ARRAY_SIZE)
1440 g_free(glyphs);
1442 return num_glyphs - num_invalid_glyphs;
1443 }
1445 bool
1446 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1447 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1448 {
1449 // create a cairo_font_face from PangoFont
1450 double size = style->font_size.computed;
1451 cairo_font_face_t *font_face = NULL;
1453 FcPattern *fc_pattern = NULL;
1455 #ifdef USE_PANGO_WIN32
1456 # ifdef CAIRO_HAS_WIN32_FONT
1457 LOGFONTA *lfa = pango_win32_font_logfont(font);
1458 LOGFONTW lfw;
1460 ZeroMemory(&lfw, sizeof(LOGFONTW));
1461 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1462 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1464 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1465 # endif
1466 #else
1467 # ifdef CAIRO_HAS_FT_FONT
1468 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1469 fc_pattern = fc_font->font_pattern;
1470 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1471 # endif
1472 #endif
1474 cairo_save(_cr);
1475 cairo_set_font_face(_cr, font_face);
1477 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1478 size = 12.0;
1480 // set the given font matrix
1481 cairo_matrix_t matrix;
1482 _initCairoMatrix(&matrix, font_matrix);
1483 cairo_set_font_matrix(_cr, &matrix);
1485 if (_render_mode == RENDER_MODE_CLIP) {
1486 if (_clip_mode == CLIP_MODE_MASK) {
1487 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1488 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1489 } else {
1490 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1491 }
1492 _showGlyphs(_cr, font, glyphtext, FALSE);
1493 } else {
1494 // just add the glyph paths to the current context
1495 _showGlyphs(_cr, font, glyphtext, TRUE);
1496 }
1497 } else {
1499 if (style->fill.isColor() || style->fill.isPaintserver()) {
1500 // set fill style
1501 _setFillStyle(style, NULL);
1503 _showGlyphs(_cr, font, glyphtext, FALSE);
1504 }
1506 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1507 // set stroke style
1508 _setStrokeStyle(style, NULL);
1510 // paint stroke
1511 _showGlyphs(_cr, font, glyphtext, TRUE);
1512 cairo_stroke(_cr);
1513 }
1514 }
1516 cairo_restore(_cr);
1518 if (font_face)
1519 cairo_font_face_destroy(font_face);
1521 return true;
1522 }
1524 /* Helper functions */
1526 void
1527 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1528 {
1529 cairo_new_path(_cr);
1530 addPathVector(pv);
1531 }
1533 void
1534 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1535 {
1536 feed_pathvector_to_cairo(_cr, pv);
1537 }
1539 void
1540 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1541 {
1542 cairo_matrix_t matrix;
1544 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1545 cairo_transform(cr, &matrix);
1546 }
1548 void
1549 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1550 {
1551 matrix->xx = (*transform)[0];
1552 matrix->yx = (*transform)[1];
1553 matrix->xy = (*transform)[2];
1554 matrix->yy = (*transform)[3];
1555 matrix->x0 = (*transform)[4];
1556 matrix->y0 = (*transform)[5];
1557 }
1559 void
1560 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1561 {
1562 _concatTransform(cr, (*transform)[0], (*transform)[1],
1563 (*transform)[2], (*transform)[3],
1564 (*transform)[4], (*transform)[5]);
1565 }
1567 static cairo_status_t
1568 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1569 {
1570 size_t written;
1571 FILE *file = (FILE*)closure;
1573 written = fwrite (data, 1, length, file);
1575 if (written == length)
1576 return CAIRO_STATUS_SUCCESS;
1577 else
1578 return CAIRO_STATUS_WRITE_ERROR;
1579 }
1581 #include "clear-n_.h"
1583 } /* namespace Internal */
1584 } /* namespace Extension */
1585 } /* namespace Inkscape */
1587 #undef TRACE
1588 #undef TEST
1590 /* End of GNU GPL code */
1593 /*
1594 Local Variables:
1595 mode:c++
1596 c-file-style:"stroustrup"
1597 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1598 indent-tabs-mode:nil
1599 fill-column:99
1600 End:
1601 */
1602 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :