1 #define __SP_CAIRO_RENDER_CONTEXT_C__
3 /** \file
4 * Rendering with Cairo.
5 */
6 /*
7 * Author:
8 * Miklos Erdelyi <erdelyim@gmail.com>
9 *
10 * Copyright (C) 2006 Miklos Erdelyi
11 *
12 * Licensed under GNU GPL
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #ifndef PANGO_ENABLE_BACKEND
20 #define PANGO_ENABLE_BACKEND
21 #endif
23 #ifndef PANGO_ENABLE_ENGINE
24 #define PANGO_ENABLE_ENGINE
25 #endif
28 #include <signal.h>
29 #include <errno.h>
31 #include <libnr/n-art-bpath.h>
32 #include <libnr/nr-matrix-ops.h>
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-scale-ops.h>
35 #include <libnr/nr-matrix-translate-ops.h>
36 #include <libnr/nr-scale-matrix-ops.h>
37 #include <libnr/n-art-bpath-2geom.h>
38 #include <2geom/pathvector.h>
40 #include <glib/gmem.h>
42 #include <glibmm/i18n.h>
43 #include "display/nr-arena.h"
44 #include "display/nr-arena-item.h"
45 #include "display/nr-arena-group.h"
46 #include "display/curve.h"
47 #include "display/canvas-bpath.h"
48 #include "sp-item.h"
49 #include "sp-item-group.h"
50 #include "style.h"
51 #include "sp-linear-gradient.h"
52 #include "sp-radial-gradient.h"
53 #include "sp-pattern.h"
54 #include "sp-mask.h"
55 #include "sp-clippath.h"
56 #ifdef WIN32
57 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
58 #endif
60 #include <unit-constants.h>
62 #include "cairo-render-context.h"
63 #include "cairo-renderer.h"
64 #include "extension/system.h"
66 #include "io/sys.h"
68 #include <cairo.h>
70 // include support for only the compiled-in surface types
71 #ifdef CAIRO_HAS_PDF_SURFACE
72 #include <cairo-pdf.h>
73 #endif
74 #ifdef CAIRO_HAS_PS_SURFACE
75 #include <cairo-ps.h>
76 #endif
79 #ifdef CAIRO_HAS_FT_FONT
80 #include <cairo-ft.h>
81 #endif
82 #ifdef CAIRO_HAS_WIN32_FONT
83 #include <cairo-win32.h>
84 #include <pango/pangowin32.h>
85 #endif
87 #include <pango/pangofc-fontmap.h>
89 //#define TRACE(_args) g_printf _args
90 #define TRACE(_args)
91 //#define TEST(_args) _args
92 #define TEST(_args)
94 // FIXME: expose these from sp-clippath/mask.cpp
95 struct SPClipPathView {
96 SPClipPathView *next;
97 unsigned int key;
98 NRArenaItem *arenaitem;
99 NRRect bbox;
100 };
102 struct SPMaskView {
103 SPMaskView *next;
104 unsigned int key;
105 NRArenaItem *arenaitem;
106 NRRect bbox;
107 };
109 namespace Inkscape {
110 namespace Extension {
111 namespace Internal {
113 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
115 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
116 _dpi(72),
117 _pdf_level(0),
118 _ps_level(1),
119 _is_texttopath(FALSE),
120 _is_filtertobitmap(FALSE),
121 _bitmapresolution(72),
122 _stream(NULL),
123 _is_valid(FALSE),
124 _vector_based_target(FALSE),
125 _cr(NULL),
126 _surface(NULL),
127 _target(CAIRO_SURFACE_TYPE_IMAGE),
128 _target_format(CAIRO_FORMAT_ARGB32),
129 _layout(NULL),
130 _state(NULL),
131 _renderer(parent),
132 _render_mode(RENDER_MODE_NORMAL),
133 _clip_mode(CLIP_MODE_MASK)
134 {}
136 CairoRenderContext::~CairoRenderContext(void)
137 {
138 if (_cr) cairo_destroy(_cr);
139 if (_surface) cairo_surface_destroy(_surface);
140 if (_layout) g_object_unref(_layout);
141 }
143 CairoRenderer*
144 CairoRenderContext::getRenderer(void) const
145 {
146 return _renderer;
147 }
149 CairoRenderState*
150 CairoRenderContext::getCurrentState(void) const
151 {
152 return _state;
153 }
155 CairoRenderState*
156 CairoRenderContext::getParentState(void) const
157 {
158 // if this is the root node just return it
159 if (g_slist_length(_state_stack) == 1) {
160 return _state;
161 } else {
162 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
163 }
164 }
166 void
167 CairoRenderContext::setStateForStyle(SPStyle const *style)
168 {
169 // only opacity & overflow is stored for now
170 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
171 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
172 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
174 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
175 _state->merge_opacity = FALSE;
177 // disable rendering of opacity if there's a stroke on the fill
178 if (_state->merge_opacity
179 && !style->fill.isNone()
180 && !style->stroke.isNone())
181 _state->merge_opacity = FALSE;
182 }
184 /**
185 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
186 *
187 * \param width width of the surface to be created
188 * \param height height of the surface to be created
189 */
190 CairoRenderContext*
191 CairoRenderContext::cloneMe(double width, double height) const
192 {
193 g_assert( _is_valid );
194 g_assert( width > 0.0 && height > 0.0 );
196 CairoRenderContext *new_context = _renderer->createContext();
197 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
198 (int)ceil(width), (int)ceil(height));
199 new_context->_cr = cairo_create(surface);
200 new_context->_surface = surface;
201 new_context->_is_valid = TRUE;
203 return new_context;
204 }
206 CairoRenderContext*
207 CairoRenderContext::cloneMe(void) const
208 {
209 g_assert( _is_valid );
211 return cloneMe(_width, _height);
212 }
214 bool
215 CairoRenderContext::setImageTarget(cairo_format_t format)
216 {
217 // format cannot be set on an already initialized surface
218 if (_is_valid)
219 return false;
221 switch (format) {
222 case CAIRO_FORMAT_ARGB32:
223 case CAIRO_FORMAT_RGB24:
224 case CAIRO_FORMAT_A8:
225 case CAIRO_FORMAT_A1:
226 _target_format = format;
227 _target = CAIRO_SURFACE_TYPE_IMAGE;
228 return true;
229 break;
230 default:
231 break;
232 }
234 return false;
235 }
237 bool
238 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
239 {
240 #ifndef CAIRO_HAS_PDF_SURFACE
241 return false;
242 #else
243 _target = CAIRO_SURFACE_TYPE_PDF;
244 _vector_based_target = TRUE;
245 #endif
247 FILE *osf = NULL;
248 FILE *osp = NULL;
250 gsize bytesRead = 0;
251 gsize bytesWritten = 0;
252 GError *error = NULL;
253 gchar *local_fn = g_filename_from_utf8(utf8_fn,
254 -1, &bytesRead, &bytesWritten, &error);
255 gchar const *fn = local_fn;
257 /* TODO: Replace the below fprintf's with something that does the right thing whether in
258 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
259 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
260 * return code.
261 */
262 if (fn != NULL) {
263 if (*fn == '|') {
264 fn += 1;
265 while (isspace(*fn)) fn += 1;
266 #ifndef WIN32
267 osp = popen(fn, "w");
268 #else
269 osp = _popen(fn, "w");
270 #endif
271 if (!osp) {
272 fprintf(stderr, "inkscape: popen(%s): %s\n",
273 fn, strerror(errno));
274 return false;
275 }
276 _stream = osp;
277 } else if (*fn == '>') {
278 fn += 1;
279 while (isspace(*fn)) fn += 1;
280 Inkscape::IO::dump_fopen_call(fn, "K");
281 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
282 if (!osf) {
283 fprintf(stderr, "inkscape: fopen(%s): %s\n",
284 fn, strerror(errno));
285 return false;
286 }
287 _stream = osf;
288 } else {
289 /* put cwd stuff in here */
290 gchar *qn = ( *fn
291 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
292 : g_strdup("lpr") );
293 #ifndef WIN32
294 osp = popen(qn, "w");
295 #else
296 osp = _popen(qn, "w");
297 #endif
298 if (!osp) {
299 fprintf(stderr, "inkscape: popen(%s): %s\n",
300 qn, strerror(errno));
301 return false;
302 }
303 g_free(qn);
304 _stream = osp;
305 }
306 }
308 g_free(local_fn);
310 if (_stream) {
311 /* fixme: this is kinda icky */
312 #if !defined(_WIN32) && !defined(__WIN32__)
313 (void) signal(SIGPIPE, SIG_IGN);
314 #endif
315 }
317 return true;
318 }
320 bool
321 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
322 {
323 #ifndef CAIRO_HAS_PS_SURFACE
324 return false;
325 #else
326 _target = CAIRO_SURFACE_TYPE_PS;
327 _vector_based_target = TRUE;
328 #endif
330 FILE *osf = NULL;
331 FILE *osp = NULL;
333 gsize bytesRead = 0;
334 gsize bytesWritten = 0;
335 GError *error = NULL;
336 gchar *local_fn = g_filename_from_utf8(utf8_fn,
337 -1, &bytesRead, &bytesWritten, &error);
338 gchar const *fn = local_fn;
340 /* TODO: Replace the below fprintf's with something that does the right thing whether in
341 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
342 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
343 * return code.
344 */
345 if (fn != NULL) {
346 if (*fn == '|') {
347 fn += 1;
348 while (isspace(*fn)) fn += 1;
349 #ifndef WIN32
350 osp = popen(fn, "w");
351 #else
352 osp = _popen(fn, "w");
353 #endif
354 if (!osp) {
355 fprintf(stderr, "inkscape: popen(%s): %s\n",
356 fn, strerror(errno));
357 return false;
358 }
359 _stream = osp;
360 } else if (*fn == '>') {
361 fn += 1;
362 while (isspace(*fn)) fn += 1;
363 Inkscape::IO::dump_fopen_call(fn, "K");
364 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
365 if (!osf) {
366 fprintf(stderr, "inkscape: fopen(%s): %s\n",
367 fn, strerror(errno));
368 return false;
369 }
370 _stream = osf;
371 } else {
372 /* put cwd stuff in here */
373 gchar *qn = ( *fn
374 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
375 : g_strdup("lpr") );
376 #ifndef WIN32
377 osp = popen(qn, "w");
378 #else
379 osp = _popen(qn, "w");
380 #endif
381 if (!osp) {
382 fprintf(stderr, "inkscape: popen(%s): %s\n",
383 qn, strerror(errno));
384 return false;
385 }
386 g_free(qn);
387 _stream = osp;
388 }
389 }
391 g_free(local_fn);
393 if (_stream) {
394 /* fixme: this is kinda icky */
395 #if !defined(_WIN32) && !defined(__WIN32__)
396 (void) signal(SIGPIPE, SIG_IGN);
397 #endif
398 }
400 return true;
401 }
403 void CairoRenderContext::setPSLevel(unsigned int level)
404 {
405 _ps_level = level;
406 }
408 unsigned int CairoRenderContext::getPSLevel(void)
409 {
410 return _ps_level;
411 }
413 void CairoRenderContext::setPDFLevel(unsigned int level)
414 {
415 _pdf_level = level;
416 }
418 void CairoRenderContext::setTextToPath(bool texttopath)
419 {
420 _is_texttopath = texttopath;
421 }
423 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
424 {
425 _is_filtertobitmap = filtertobitmap;
426 }
428 bool CairoRenderContext::getFilterToBitmap(void)
429 {
430 return _is_filtertobitmap;
431 }
433 void CairoRenderContext::setBitmapResolution(int resolution)
434 {
435 _bitmapresolution = resolution;
436 }
438 int CairoRenderContext::getBitmapResolution(void)
439 {
440 return _bitmapresolution;
441 }
443 cairo_surface_t*
444 CairoRenderContext::getSurface(void)
445 {
446 g_assert( _is_valid );
448 return _surface;
449 }
451 bool
452 CairoRenderContext::saveAsPng(const char *file_name)
453 {
454 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
455 if (status)
456 return false;
457 else
458 return true;
459 }
461 void
462 CairoRenderContext::setRenderMode(CairoRenderMode mode)
463 {
464 switch (mode) {
465 case RENDER_MODE_NORMAL:
466 case RENDER_MODE_CLIP:
467 _render_mode = mode;
468 break;
469 default:
470 _render_mode = RENDER_MODE_NORMAL;
471 break;
472 }
473 }
475 CairoRenderContext::CairoRenderMode
476 CairoRenderContext::getRenderMode(void) const
477 {
478 return _render_mode;
479 }
481 void
482 CairoRenderContext::setClipMode(CairoClipMode mode)
483 {
484 switch (mode) {
485 case CLIP_MODE_PATH:
486 case CLIP_MODE_MASK:
487 _clip_mode = mode;
488 break;
489 default:
490 _clip_mode = CLIP_MODE_PATH;
491 break;
492 }
493 }
495 CairoRenderContext::CairoClipMode
496 CairoRenderContext::getClipMode(void) const
497 {
498 return _clip_mode;
499 }
501 CairoRenderState*
502 CairoRenderContext::_createState(void)
503 {
504 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
505 g_assert( state != NULL );
507 state->has_filtereffect = FALSE;
508 state->merge_opacity = TRUE;
509 state->opacity = 1.0;
510 state->need_layer = FALSE;
511 state->has_overflow = FALSE;
512 state->parent_has_userspace = FALSE;
513 state->clip_path = NULL;
514 state->mask = NULL;
516 return state;
517 }
519 void
520 CairoRenderContext::pushLayer(void)
521 {
522 g_assert( _is_valid );
524 TRACE(("--pushLayer\n"));
525 cairo_push_group(_cr);
527 // clear buffer
528 if (!_vector_based_target) {
529 cairo_save(_cr);
530 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
531 cairo_paint(_cr);
532 cairo_restore(_cr);
533 }
534 }
536 void
537 CairoRenderContext::popLayer(void)
538 {
539 g_assert( _is_valid );
541 float opacity = _state->opacity;
542 TRACE(("--popLayer w/ %f\n", opacity));
544 // apply clipPath or mask if present
545 SPClipPath *clip_path = _state->clip_path;
546 SPMask *mask = _state->mask;
547 if (clip_path || mask) {
549 CairoRenderContext *clip_ctx = 0;
550 cairo_surface_t *clip_mask = 0;
552 if (clip_path) {
553 if (_render_mode == RENDER_MODE_CLIP)
554 mask = NULL; // disable mask when performing nested clipping
556 if (_vector_based_target) {
557 setClipMode(CLIP_MODE_PATH);
558 if (!mask) {
559 cairo_pop_group_to_source(_cr);
560 _renderer->applyClipPath(this, clip_path);
561 if (opacity == 1.0)
562 cairo_paint(_cr);
563 else
564 cairo_paint_with_alpha(_cr, opacity);
566 } else {
567 // the clipPath will be applied before masking
568 }
569 } else {
571 // setup a new rendering context
572 clip_ctx = _renderer->createContext();
573 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
574 clip_ctx->setClipMode(CLIP_MODE_MASK);
575 if (!clip_ctx->setupSurface(_width, _height)) {
576 TRACE(("setupSurface failed\n"));
577 _renderer->destroyContext(clip_ctx);
578 return;
579 }
581 // clear buffer
582 cairo_save(clip_ctx->_cr);
583 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
584 cairo_paint(clip_ctx->_cr);
585 cairo_restore(clip_ctx->_cr);
587 // if a mask won't be applied set opacity too
588 if (!mask)
589 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
590 else
591 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
593 // copy over the correct CTM
594 if (_state->parent_has_userspace)
595 clip_ctx->setTransform(&getParentState()->transform);
596 else
597 clip_ctx->setTransform(&_state->transform);
599 // apply the clip path
600 clip_ctx->pushState();
601 _renderer->applyClipPath(clip_ctx, clip_path);
602 clip_ctx->popState();
604 clip_mask = clip_ctx->getSurface();
605 TEST(clip_ctx->saveAsPng("clip_mask.png"));
607 if (!mask) {
608 cairo_pop_group_to_source(_cr);
609 cairo_mask_surface(_cr, clip_mask, 0, 0);
610 _renderer->destroyContext(clip_ctx);
611 }
612 }
613 }
615 if (mask) {
616 // create rendering context for mask
617 CairoRenderContext *mask_ctx = _renderer->createContext();
618 mask_ctx->setupSurface(_width, _height);
620 // set rendering mode to normal
621 setRenderMode(RENDER_MODE_NORMAL);
623 // copy the correct CTM to mask context
624 if (_state->parent_has_userspace)
625 mask_ctx->setTransform(&getParentState()->transform);
626 else
627 mask_ctx->setTransform(&_state->transform);
629 // render mask contents to mask_ctx
630 _renderer->applyMask(mask_ctx, mask);
632 TEST(mask_ctx->saveAsPng("mask.png"));
634 // composite with clip mask
635 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
636 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
637 _renderer->destroyContext(clip_ctx);
638 }
640 cairo_surface_t *mask_image = mask_ctx->getSurface();
641 int width = cairo_image_surface_get_width(mask_image);
642 int height = cairo_image_surface_get_height(mask_image);
643 int stride = cairo_image_surface_get_stride(mask_image);
644 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
646 // premultiply with opacity
647 if (_state->opacity != 1.0) {
648 TRACE(("premul w/ %f\n", opacity));
649 guint8 int_opacity = (guint8)(255 * opacity);
650 for (int row = 0 ; row < height; row++) {
651 unsigned char *row_data = pixels + (row * stride);
652 for (int i = 0 ; i < width; i++) {
653 guint32 *pixel = (guint32 *)row_data + i;
654 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
655 ((*pixel & 0x0000ff00) >> 8) * 46518 +
656 ((*pixel & 0x000000ff) ) * 4688) *
657 int_opacity);
658 }
659 }
660 }
662 cairo_pop_group_to_source(_cr);
663 if (_clip_mode == CLIP_MODE_PATH) {
664 // we have to do the clipping after cairo_pop_group_to_source
665 _renderer->applyClipPath(this, clip_path);
666 }
667 // apply the mask onto the layer
668 cairo_mask_surface(_cr, mask_image, 0, 0);
669 _renderer->destroyContext(mask_ctx);
670 }
671 } else {
672 cairo_pop_group_to_source(_cr);
673 if (opacity == 1.0)
674 cairo_paint(_cr);
675 else
676 cairo_paint_with_alpha(_cr, opacity);
677 }
678 }
680 void
681 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
682 {
683 g_assert( _is_valid );
685 // here it should be checked whether the current clip winding changed
686 // so we could switch back to masked clipping
687 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
688 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
689 } else {
690 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
691 }
692 addBpath(bp);
693 }
695 void
696 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
697 {
698 g_assert( _is_valid );
700 cairo_rectangle(_cr, x, y, width, height);
701 cairo_clip(_cr);
702 }
704 bool
705 CairoRenderContext::setupSurface(double width, double height)
706 {
707 // Is the surface already set up?
708 if (_is_valid)
709 return true;
711 if (_vector_based_target && _stream == NULL)
712 return false;
714 cairo_surface_t *surface = NULL;
715 switch (_target) {
716 case CAIRO_SURFACE_TYPE_IMAGE:
717 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
718 break;
719 #ifdef CAIRO_HAS_PDF_SURFACE
720 case CAIRO_SURFACE_TYPE_PDF:
721 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
722 break;
723 #endif
724 #ifdef CAIRO_HAS_PS_SURFACE
725 case CAIRO_SURFACE_TYPE_PS:
726 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
727 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
728 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
729 return FALSE;
730 }
731 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
732 #endif
733 break;
734 #endif
735 default:
736 return false;
737 break;
738 }
740 return _finishSurfaceSetup (surface);
741 }
743 bool
744 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
745 {
746 if (_is_valid || !surface)
747 return false;
749 _vector_based_target = is_vector;
750 bool ret = _finishSurfaceSetup (surface);
751 if (ret)
752 cairo_surface_reference (surface);
753 return ret;
754 }
756 bool
757 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
758 {
759 if(surface == NULL) {
760 return FALSE;
761 }
762 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
763 return FALSE;
764 }
766 _cr = cairo_create(surface);
767 _surface = surface;
769 if (_vector_based_target) {
770 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
771 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
772 // set background color on non-alpha surfaces
773 // TODO: bgcolor should be derived from SPDocument
774 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
775 cairo_rectangle(_cr, 0, 0, _width, _height);
776 cairo_fill(_cr);
777 }
779 _is_valid = TRUE;
781 return true;
782 }
784 bool
785 CairoRenderContext::finish(void)
786 {
787 g_assert( _is_valid );
789 if (_vector_based_target)
790 cairo_show_page(_cr);
792 cairo_destroy(_cr);
793 cairo_surface_finish(_surface);
794 cairo_status_t status = cairo_surface_status(_surface);
795 cairo_surface_destroy(_surface);
796 _cr = NULL;
797 _surface = NULL;
799 if (_layout)
800 g_object_unref(_layout);
802 _is_valid = FALSE;
804 if (_vector_based_target && _stream) {
805 /* Flush stream to be sure. */
806 (void) fflush(_stream);
808 fclose(_stream);
809 _stream = NULL;
810 }
812 if (status == CAIRO_STATUS_SUCCESS)
813 return true;
814 else
815 return false;
816 }
818 void
819 CairoRenderContext::transform(Geom::Matrix const *transform)
820 {
821 g_assert( _is_valid );
823 cairo_matrix_t matrix;
824 _initCairoMatrix(&matrix, transform);
825 cairo_transform(_cr, &matrix);
827 // store new CTM
828 getTransform(&_state->transform);
829 }
831 void
832 CairoRenderContext::setTransform(Geom::Matrix const *transform)
833 {
834 g_assert( _is_valid );
836 cairo_matrix_t matrix;
837 _initCairoMatrix(&matrix, transform);
838 cairo_set_matrix(_cr, &matrix);
839 _state->transform = *transform;
840 }
842 void
843 CairoRenderContext::getTransform(Geom::Matrix *copy) const
844 {
845 g_assert( _is_valid );
847 cairo_matrix_t ctm;
848 cairo_get_matrix(_cr, &ctm);
849 (*copy)[0] = ctm.xx;
850 (*copy)[1] = ctm.yx;
851 (*copy)[2] = ctm.xy;
852 (*copy)[3] = ctm.yy;
853 (*copy)[4] = ctm.x0;
854 (*copy)[5] = ctm.y0;
855 }
857 void
858 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
859 {
860 g_assert( _is_valid );
862 CairoRenderState *parent_state = getParentState();
863 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
864 }
866 void
867 CairoRenderContext::pushState(void)
868 {
869 g_assert( _is_valid );
871 cairo_save(_cr);
873 CairoRenderState *new_state = _createState();
874 // copy current state's transform
875 new_state->transform = _state->transform;
876 _state_stack = g_slist_prepend(_state_stack, new_state);
877 _state = new_state;
878 }
880 void
881 CairoRenderContext::popState(void)
882 {
883 g_assert( _is_valid );
885 cairo_restore(_cr);
887 g_free(_state_stack->data);
888 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
889 _state = (CairoRenderState*)_state_stack->data;
891 g_assert( g_slist_length(_state_stack) > 0 );
892 }
894 static bool pattern_hasItemChildren (SPPattern *pat)
895 {
896 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
897 if (SP_IS_ITEM (child)) {
898 return true;
899 }
900 }
901 return false;
902 }
904 cairo_pattern_t*
905 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
906 {
907 g_assert( SP_IS_PATTERN(paintserver) );
909 SPPattern *pat = SP_PATTERN (paintserver);
911 Geom::Matrix ps2user, pcs2dev;
912 ps2user = Geom::identity();
913 pcs2dev = Geom::identity();
915 double x = pattern_x(pat);
916 double y = pattern_y(pat);
917 double width = pattern_width(pat);
918 double height = pattern_height(pat);
919 double bbox_width_scaler;
920 double bbox_height_scaler;
922 TRACE(("%f x %f pattern\n", width, height));
924 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
925 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
926 bbox_width_scaler = pbox->x1 - pbox->x0;
927 bbox_height_scaler = pbox->y1 - pbox->y0;
928 ps2user[4] = x * bbox_width_scaler + pbox->x0;
929 ps2user[5] = y * bbox_height_scaler + pbox->y0;
930 } else {
931 bbox_width_scaler = 1.0;
932 bbox_height_scaler = 1.0;
933 ps2user[4] = x;
934 ps2user[5] = y;
935 }
937 // apply pattern transformation
938 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
939 ps2user *= pattern_transform;
941 // create pattern contents coordinate system
942 if (pat->viewBox_set) {
943 NRRect *view_box = pattern_viewBox(pat);
945 double x, y, w, h;
946 double view_width, view_height;
947 x = 0;
948 y = 0;
949 w = width * bbox_width_scaler;
950 h = height * bbox_height_scaler;
952 view_width = view_box->x1 - view_box->x0;
953 view_height = view_box->y1 - view_box->y0;
955 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
956 pcs2dev[0] = w / view_width;
957 pcs2dev[3] = h / view_height;
958 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
959 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
960 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
961 pcs2dev[0] = pbox->x1 - pbox->x0;
962 pcs2dev[3] = pbox->y1 - pbox->y0;
963 }
965 // Calculate the size of the surface which has to be created so that the pattern resolution
966 // matches the output resolution (i.e., if the pattern is scaled up by a factor of two,
967 // the surface width should be scaled by a factor of two).
968 // The scaling needs to be taken into account in the ctm after the pattern transformation.
969 Geom::Matrix temp;
970 temp = pattern_transform * _state->transform;
971 double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
972 double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
974 if (_vector_based_target) {
975 // eliminate PT_PER_PX mul from these
976 width_scaler *= 1.25;
977 height_scaler *= 1.25;
978 }
979 // Cairo requires an integer pattern surface width/height.
980 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
981 double surface_width = ceil(bbox_width_scaler * width_scaler * width - 0.5);
982 double surface_height = ceil(bbox_height_scaler * height_scaler * height - 0.5);
983 TRACE(("surface size: %f x %f\n", surface_width, surface_height));
984 // create new rendering context
985 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
987 // adjust the size of the painted pattern to fit exactly the created surface
988 // this has to be done because of the rounding to obtain an integer pattern surface width/height
989 double scale_width = surface_width / (bbox_width_scaler * width);
990 double scale_height = surface_height / (bbox_height_scaler * height);
991 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
992 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
993 pcs2dev *= Geom::Scale(scale_width, scale_height);
994 ps2user *= Geom::Scale(1.0 / scale_width, 1.0 / scale_height);
995 }
997 pattern_ctx->setTransform(&pcs2dev);
998 pattern_ctx->pushState();
1000 // create arena and group
1001 NRArena *arena = NRArena::create();
1002 unsigned dkey = sp_item_display_key_new(1);
1004 // show items and render them
1005 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1006 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1007 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1008 if (SP_IS_ITEM (child)) {
1009 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1010 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1011 }
1012 }
1013 break; // do not go further up the chain if children are found
1014 }
1015 }
1017 pattern_ctx->popState();
1019 // setup a cairo_pattern_t
1020 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1021 TEST(pattern_ctx->saveAsPng("pattern.png"));
1022 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1023 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1025 // set pattern transformation
1026 cairo_matrix_t pattern_matrix;
1027 _initCairoMatrix(&pattern_matrix, &ps2user);
1028 cairo_matrix_invert(&pattern_matrix);
1029 cairo_pattern_set_matrix(result, &pattern_matrix);
1031 delete pattern_ctx;
1033 // hide all items
1034 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1035 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1036 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1037 if (SP_IS_ITEM (child)) {
1038 sp_item_invoke_hide (SP_ITEM (child), dkey);
1039 }
1040 }
1041 break; // do not go further up the chain if children are found
1042 }
1043 }
1045 return result;
1046 }
1048 cairo_pattern_t*
1049 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1050 NRRect const *pbox, float alpha)
1051 {
1052 cairo_pattern_t *pattern = NULL;
1053 bool apply_bbox2user = FALSE;
1055 if (SP_IS_LINEARGRADIENT (paintserver)) {
1057 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1059 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1061 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1062 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1063 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1064 // convert to userspace
1065 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1066 p1 *= bbox2user;
1067 p2 *= bbox2user;
1068 }
1070 // create linear gradient pattern
1071 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1073 // add stops
1074 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1075 float rgb[3];
1076 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1077 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1078 }
1079 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1081 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1083 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1085 Geom::Point c (rg->cx.computed, rg->cy.computed);
1086 Geom::Point f (rg->fx.computed, rg->fy.computed);
1087 double r = rg->r.computed;
1088 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1089 apply_bbox2user = true;
1091 // create radial gradient pattern
1092 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1094 // add stops
1095 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1096 float rgb[3];
1097 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1098 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1099 }
1100 } else if (SP_IS_PATTERN (paintserver)) {
1102 pattern = _createPatternPainter(paintserver, pbox);
1103 } else {
1104 return NULL;
1105 }
1107 if (pattern && SP_IS_GRADIENT (paintserver)) {
1108 SPGradient *g = SP_GRADIENT(paintserver);
1110 // set extend type
1111 SPGradientSpread spread = sp_gradient_get_spread(g);
1112 switch (spread) {
1113 case SP_GRADIENT_SPREAD_REPEAT: {
1114 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1115 break;
1116 }
1117 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1118 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1119 break;
1120 }
1121 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1122 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1123 break;
1124 }
1125 default: {
1126 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1127 break;
1128 }
1129 }
1131 cairo_matrix_t pattern_matrix;
1132 if (g->gradientTransform_set) {
1133 // apply gradient transformation
1134 cairo_matrix_init(&pattern_matrix,
1135 g->gradientTransform[0], g->gradientTransform[1],
1136 g->gradientTransform[2], g->gradientTransform[3],
1137 g->gradientTransform[4], g->gradientTransform[5]);
1138 } else {
1139 cairo_matrix_init_identity (&pattern_matrix);
1140 }
1142 if (apply_bbox2user) {
1143 // convert to userspace
1144 cairo_matrix_t bbox2user;
1145 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1146 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1147 }
1148 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1149 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1150 }
1152 return pattern;
1153 }
1155 void
1156 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1157 {
1158 g_return_if_fail( style->fill.isColor()
1159 || style->fill.isPaintserver() );
1161 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1162 if (_state->merge_opacity) {
1163 alpha *= _state->opacity;
1164 TRACE(("merged op=%f\n", alpha));
1165 }
1167 if (style->fill.isColor()) {
1168 float rgb[3];
1169 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1171 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1172 } else {
1173 g_assert( style->fill.isPaintserver()
1174 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1175 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1177 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1179 if (pattern) {
1180 cairo_set_source(_cr, pattern);
1181 cairo_pattern_destroy(pattern);
1182 }
1183 }
1184 }
1186 void
1187 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1188 {
1189 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1190 if (_state->merge_opacity)
1191 alpha *= _state->opacity;
1193 if (style->stroke.isColor()) {
1194 float rgb[3];
1195 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1197 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1198 } else {
1199 g_assert( style->fill.isPaintserver()
1200 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1201 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1203 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1205 if (pattern) {
1206 cairo_set_source(_cr, pattern);
1207 cairo_pattern_destroy(pattern);
1208 }
1209 }
1211 if (style->stroke_dash.n_dash &&
1212 style->stroke_dash.dash )
1213 {
1214 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1215 } else {
1216 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1217 }
1219 cairo_set_line_width(_cr, style->stroke_width.computed);
1221 // set line join type
1222 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1223 switch (style->stroke_linejoin.computed) {
1224 case SP_STROKE_LINEJOIN_MITER:
1225 join = CAIRO_LINE_JOIN_MITER;
1226 break;
1227 case SP_STROKE_LINEJOIN_ROUND:
1228 join = CAIRO_LINE_JOIN_ROUND;
1229 break;
1230 case SP_STROKE_LINEJOIN_BEVEL:
1231 join = CAIRO_LINE_JOIN_BEVEL;
1232 break;
1233 }
1234 cairo_set_line_join(_cr, join);
1236 // set line cap type
1237 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1238 switch (style->stroke_linecap.computed) {
1239 case SP_STROKE_LINECAP_BUTT:
1240 cap = CAIRO_LINE_CAP_BUTT;
1241 break;
1242 case SP_STROKE_LINECAP_ROUND:
1243 cap = CAIRO_LINE_CAP_ROUND;
1244 break;
1245 case SP_STROKE_LINECAP_SQUARE:
1246 cap = CAIRO_LINE_CAP_SQUARE;
1247 break;
1248 }
1249 cairo_set_line_cap(_cr, cap);
1250 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1251 }
1253 bool
1254 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1255 {
1256 NArtBpath * bpath = BPath_from_2GeomPath (pathv);
1257 const_NRBPath bp;
1258 bp.path = bpath;
1259 bool retvalue = renderPath(&bp, style, pbox);
1260 g_free(bpath);
1261 return retvalue;
1262 }
1264 bool
1265 CairoRenderContext::renderPath(const_NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1266 {
1267 g_assert( _is_valid );
1269 if (_render_mode == RENDER_MODE_CLIP) {
1270 if (_clip_mode == CLIP_MODE_PATH) {
1271 addClipPath(bpath->path, &style->fill_rule);
1272 } else {
1273 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 }
1279 cairo_fill(_cr);
1280 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1281 }
1282 return true;
1283 }
1285 if (style->fill.isNone() && style->stroke.isNone())
1286 return true;
1288 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1289 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1291 if (!need_layer)
1292 cairo_save(_cr);
1293 else
1294 pushLayer();
1296 if (!style->fill.isNone()) {
1297 _setFillStyle(style, pbox);
1298 setBpath(bpath->path);
1300 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1301 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1302 } else {
1303 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1304 }
1306 if (style->stroke.isNone())
1307 cairo_fill(_cr);
1308 else
1309 cairo_fill_preserve(_cr);
1310 }
1312 if (!style->stroke.isNone()) {
1313 _setStrokeStyle(style, pbox);
1314 if (style->fill.isNone())
1315 setBpath(bpath->path);
1317 cairo_stroke(_cr);
1318 }
1320 if (need_layer)
1321 popLayer();
1322 else
1323 cairo_restore(_cr);
1325 return true;
1326 }
1328 bool
1329 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1330 Geom::Matrix const *image_transform, SPStyle const *style)
1331 {
1332 g_assert( _is_valid );
1334 if (_render_mode == RENDER_MODE_CLIP)
1335 return true;
1337 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1338 if (!px_rgba)
1339 return false;
1341 float opacity;
1342 if (_state->merge_opacity)
1343 opacity = _state->opacity;
1344 else
1345 opacity = 1.0;
1347 // make a copy of the original pixbuf with premultiplied alpha
1348 // if we pass the original pixbuf it will get messed up
1349 for (unsigned i = 0; i < h; i++) {
1350 for (unsigned j = 0; j < w; j++) {
1351 guchar const *src = px + i * rs + j * 4;
1352 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1353 guchar r, g, b, alpha_dst;
1355 // calculate opacity-modified alpha
1356 alpha_dst = src[3];
1357 if (opacity != 1.0 && _vector_based_target)
1358 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1360 // premul alpha (needed because this will be undone by cairo-pdf)
1361 r = src[0]*alpha_dst/255;
1362 g = src[1]*alpha_dst/255;
1363 b = src[2]*alpha_dst/255;
1365 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1366 }
1367 }
1369 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1370 if (cairo_surface_status(image_surface)) {
1371 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1372 return false;
1373 }
1375 // setup automatic freeing of the image data when destroying the surface
1376 static cairo_user_data_key_t key;
1377 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1379 cairo_save(_cr);
1381 // scaling by width & height is not needed because it will be done by Cairo
1382 if (image_transform)
1383 transform(image_transform);
1385 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1387 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1388 if (_vector_based_target) {
1389 cairo_new_path(_cr);
1390 cairo_rectangle(_cr, 0, 0, w, h);
1391 cairo_clip(_cr);
1392 }
1394 if (_vector_based_target)
1395 cairo_paint(_cr);
1396 else
1397 cairo_paint_with_alpha(_cr, opacity);
1399 cairo_restore(_cr);
1401 cairo_surface_destroy(image_surface);
1403 return true;
1404 }
1406 #define GLYPH_ARRAY_SIZE 64
1408 unsigned int
1409 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1410 {
1411 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1412 cairo_glyph_t *glyphs = glyph_array;
1413 unsigned int num_glyphs = glyphtext.size();
1414 if (num_glyphs > GLYPH_ARRAY_SIZE)
1415 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1417 unsigned int num_invalid_glyphs = 0;
1418 unsigned int i = 0;
1419 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1420 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1421 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1422 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1423 TRACE(("INVALID GLYPH found\n"));
1424 num_invalid_glyphs++;
1425 continue;
1426 }
1427 glyphs[i - num_invalid_glyphs].index = it_info->index;
1428 glyphs[i - num_invalid_glyphs].x = it_info->x;
1429 glyphs[i - num_invalid_glyphs].y = it_info->y;
1430 i++;
1431 }
1433 if (is_stroke || _is_texttopath)
1434 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1435 else
1436 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1438 if (num_glyphs > GLYPH_ARRAY_SIZE)
1439 g_free(glyphs);
1441 return num_glyphs - num_invalid_glyphs;
1442 }
1444 bool
1445 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1446 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1447 {
1448 // create a cairo_font_face from PangoFont
1449 double size = style->font_size.computed;
1450 cairo_font_face_t *font_face = NULL;
1452 FcPattern *fc_pattern = NULL;
1454 #ifdef USE_PANGO_WIN32
1455 # ifdef CAIRO_HAS_WIN32_FONT
1456 LOGFONTA *lfa = pango_win32_font_logfont(font);
1457 LOGFONTW lfw;
1459 ZeroMemory(&lfw, sizeof(LOGFONTW));
1460 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1461 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1463 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1464 # endif
1465 #else
1466 # ifdef CAIRO_HAS_FT_FONT
1467 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1468 fc_pattern = fc_font->font_pattern;
1469 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1470 # endif
1471 #endif
1473 cairo_save(_cr);
1474 cairo_set_font_face(_cr, font_face);
1476 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1477 size = 12.0;
1479 // set the given font matrix
1480 cairo_matrix_t matrix;
1481 _initCairoMatrix(&matrix, font_matrix);
1482 cairo_set_font_matrix(_cr, &matrix);
1484 if (_render_mode == RENDER_MODE_CLIP) {
1485 if (_clip_mode == CLIP_MODE_MASK) {
1486 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1487 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1488 } else {
1489 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1490 }
1491 _showGlyphs(_cr, font, glyphtext, FALSE);
1492 } else {
1493 // just add the glyph paths to the current context
1494 _showGlyphs(_cr, font, glyphtext, TRUE);
1495 }
1496 } else {
1498 if (style->fill.isColor() || style->fill.isPaintserver()) {
1499 // set fill style
1500 _setFillStyle(style, NULL);
1502 _showGlyphs(_cr, font, glyphtext, FALSE);
1503 }
1505 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1506 // set stroke style
1507 _setStrokeStyle(style, NULL);
1509 // paint stroke
1510 _showGlyphs(_cr, font, glyphtext, TRUE);
1511 cairo_stroke(_cr);
1512 }
1513 }
1515 cairo_restore(_cr);
1517 if (font_face)
1518 cairo_font_face_destroy(font_face);
1520 return true;
1521 }
1523 /* Helper functions */
1525 void
1526 CairoRenderContext::addBpath(NArtBpath const *bp)
1527 {
1528 bool closed = false;
1529 while (bp->code != NR_END) {
1530 switch (bp->code) {
1531 case NR_MOVETO:
1532 if (closed) {
1533 cairo_close_path(_cr);
1534 }
1535 closed = true;
1536 cairo_move_to(_cr, bp->x3, bp->y3);
1537 break;
1538 case NR_MOVETO_OPEN:
1539 if (closed) {
1540 cairo_close_path(_cr);
1541 }
1542 closed = false;
1543 cairo_move_to(_cr, bp->x3, bp->y3);
1544 break;
1545 case NR_LINETO:
1546 cairo_line_to(_cr, bp->x3, bp->y3);
1547 break;
1548 case NR_CURVETO:
1549 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1550 break;
1551 default:
1552 break;
1553 }
1554 bp += 1;
1555 }
1556 if (closed) {
1557 cairo_close_path(_cr);
1558 }
1559 }
1561 void
1562 CairoRenderContext::setBpath(NArtBpath const *bp)
1563 {
1564 cairo_new_path(_cr);
1565 if (bp)
1566 addBpath(bp);
1567 }
1569 void
1570 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1571 {
1572 cairo_matrix_t matrix;
1574 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1575 cairo_transform(cr, &matrix);
1576 }
1578 void
1579 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1580 {
1581 matrix->xx = (*transform)[0];
1582 matrix->yx = (*transform)[1];
1583 matrix->xy = (*transform)[2];
1584 matrix->yy = (*transform)[3];
1585 matrix->x0 = (*transform)[4];
1586 matrix->y0 = (*transform)[5];
1587 }
1589 void
1590 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1591 {
1592 _concatTransform(cr, (*transform)[0], (*transform)[1],
1593 (*transform)[2], (*transform)[3],
1594 (*transform)[4], (*transform)[5]);
1595 }
1597 static cairo_status_t
1598 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1599 {
1600 size_t written;
1601 FILE *file = (FILE*)closure;
1603 written = fwrite (data, 1, length, file);
1605 if (written == length)
1606 return CAIRO_STATUS_SUCCESS;
1607 else
1608 return CAIRO_STATUS_WRITE_ERROR;
1609 }
1611 #include "clear-n_.h"
1613 } /* namespace Internal */
1614 } /* namespace Extension */
1615 } /* namespace Inkscape */
1617 #undef TRACE
1618 #undef TEST
1620 /* End of GNU GPL code */
1623 /*
1624 Local Variables:
1625 mode:c++
1626 c-file-style:"stroustrup"
1627 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1628 indent-tabs-mode:nil
1629 fill-column:99
1630 End:
1631 */
1632 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :