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) g_message _args
84 #define TRACE(_args)
85 //#define TEST(_args) _args
86 #define TEST(_args)
88 // FIXME: expose these from sp-clippath/mask.cpp
89 struct SPClipPathView {
90 SPClipPathView *next;
91 unsigned int key;
92 NRArenaItem *arenaitem;
93 NRRect bbox;
94 };
96 struct SPMaskView {
97 SPMaskView *next;
98 unsigned int key;
99 NRArenaItem *arenaitem;
100 NRRect bbox;
101 };
103 namespace Inkscape {
104 namespace Extension {
105 namespace Internal {
107 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
109 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
110 _dpi(72),
111 _pdf_level(1),
112 _ps_level(1),
113 _eps(false),
114 _is_texttopath(FALSE),
115 _is_filtertobitmap(FALSE),
116 _bitmapresolution(72),
117 _stream(NULL),
118 _is_valid(FALSE),
119 _vector_based_target(FALSE),
120 _cr(NULL), // Cairo context
121 _surface(NULL),
122 _target(CAIRO_SURFACE_TYPE_IMAGE),
123 _target_format(CAIRO_FORMAT_ARGB32),
124 _layout(NULL),
125 _state(NULL),
126 _renderer(parent),
127 _render_mode(RENDER_MODE_NORMAL),
128 _clip_mode(CLIP_MODE_MASK)
129 {
130 font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
131 }
133 CairoRenderContext::~CairoRenderContext(void)
134 {
135 if(font_table != NULL) {
136 g_hash_table_remove_all(font_table);
137 }
139 if (_cr) cairo_destroy(_cr);
140 if (_surface) cairo_surface_destroy(_surface);
141 if (_layout) g_object_unref(_layout);
142 }
143 void CairoRenderContext::font_data_free(gpointer data)
144 {
145 cairo_font_face_t *font_face = (cairo_font_face_t *)data;
146 if (font_face) {
147 cairo_font_face_destroy(font_face);
148 }
149 }
151 CairoRenderer*
152 CairoRenderContext::getRenderer(void) const
153 {
154 return _renderer;
155 }
157 CairoRenderState*
158 CairoRenderContext::getCurrentState(void) const
159 {
160 return _state;
161 }
163 CairoRenderState*
164 CairoRenderContext::getParentState(void) const
165 {
166 // if this is the root node just return it
167 if (g_slist_length(_state_stack) == 1) {
168 return _state;
169 } else {
170 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
171 }
172 }
174 void
175 CairoRenderContext::setStateForStyle(SPStyle const *style)
176 {
177 // only opacity & overflow is stored for now
178 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
179 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
180 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
182 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
183 _state->merge_opacity = FALSE;
185 // disable rendering of opacity if there's a stroke on the fill
186 if (_state->merge_opacity
187 && !style->fill.isNone()
188 && !style->stroke.isNone())
189 _state->merge_opacity = FALSE;
190 }
192 /**
193 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
194 *
195 * \param width width of the surface to be created
196 * \param height height of the surface to be created
197 */
198 CairoRenderContext*
199 CairoRenderContext::cloneMe(double width, double height) const
200 {
201 g_assert( _is_valid );
202 g_assert( width > 0.0 && height > 0.0 );
204 CairoRenderContext *new_context = _renderer->createContext();
205 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
206 (int)ceil(width), (int)ceil(height));
207 new_context->_cr = cairo_create(surface);
208 new_context->_surface = surface;
209 new_context->_width = width;
210 new_context->_height = height;
211 new_context->_is_valid = TRUE;
213 return new_context;
214 }
216 CairoRenderContext*
217 CairoRenderContext::cloneMe(void) const
218 {
219 g_assert( _is_valid );
221 return cloneMe(_width, _height);
222 }
224 bool
225 CairoRenderContext::setImageTarget(cairo_format_t format)
226 {
227 // format cannot be set on an already initialized surface
228 if (_is_valid)
229 return false;
231 switch (format) {
232 case CAIRO_FORMAT_ARGB32:
233 case CAIRO_FORMAT_RGB24:
234 case CAIRO_FORMAT_A8:
235 case CAIRO_FORMAT_A1:
236 _target_format = format;
237 _target = CAIRO_SURFACE_TYPE_IMAGE;
238 return true;
239 break;
240 default:
241 break;
242 }
244 return false;
245 }
247 bool
248 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
249 {
250 #ifndef CAIRO_HAS_PDF_SURFACE
251 return false;
252 #else
253 _target = CAIRO_SURFACE_TYPE_PDF;
254 _vector_based_target = TRUE;
255 #endif
257 FILE *osf = NULL;
258 FILE *osp = NULL;
260 gsize bytesRead = 0;
261 gsize bytesWritten = 0;
262 GError *error = NULL;
263 gchar *local_fn = g_filename_from_utf8(utf8_fn,
264 -1, &bytesRead, &bytesWritten, &error);
265 gchar const *fn = local_fn;
267 /* TODO: Replace the below fprintf's with something that does the right thing whether in
268 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
269 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
270 * return code.
271 */
272 if (fn != NULL) {
273 if (*fn == '|') {
274 fn += 1;
275 while (isspace(*fn)) fn += 1;
276 #ifndef WIN32
277 osp = popen(fn, "w");
278 #else
279 osp = _popen(fn, "w");
280 #endif
281 if (!osp) {
282 fprintf(stderr, "inkscape: popen(%s): %s\n",
283 fn, strerror(errno));
284 return false;
285 }
286 _stream = osp;
287 } else if (*fn == '>') {
288 fn += 1;
289 while (isspace(*fn)) fn += 1;
290 Inkscape::IO::dump_fopen_call(fn, "K");
291 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
292 if (!osf) {
293 fprintf(stderr, "inkscape: fopen(%s): %s\n",
294 fn, strerror(errno));
295 return false;
296 }
297 _stream = osf;
298 } else {
299 /* put cwd stuff in here */
300 gchar *qn = ( *fn
301 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
302 : g_strdup("lpr") );
303 #ifndef WIN32
304 osp = popen(qn, "w");
305 #else
306 osp = _popen(qn, "w");
307 #endif
308 if (!osp) {
309 fprintf(stderr, "inkscape: popen(%s): %s\n",
310 qn, strerror(errno));
311 return false;
312 }
313 g_free(qn);
314 _stream = osp;
315 }
316 }
318 g_free(local_fn);
320 if (_stream) {
321 /* fixme: this is kinda icky */
322 #if !defined(_WIN32) && !defined(__WIN32__)
323 (void) signal(SIGPIPE, SIG_IGN);
324 #endif
325 }
327 return true;
328 }
330 bool
331 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
332 {
333 #ifndef CAIRO_HAS_PS_SURFACE
334 return false;
335 #else
336 _target = CAIRO_SURFACE_TYPE_PS;
337 _vector_based_target = TRUE;
338 #endif
340 FILE *osf = NULL;
341 FILE *osp = NULL;
343 gsize bytesRead = 0;
344 gsize bytesWritten = 0;
345 GError *error = NULL;
346 gchar *local_fn = g_filename_from_utf8(utf8_fn,
347 -1, &bytesRead, &bytesWritten, &error);
348 gchar const *fn = local_fn;
350 /* TODO: Replace the below fprintf's with something that does the right thing whether in
351 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
352 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
353 * return code.
354 */
355 if (fn != NULL) {
356 if (*fn == '|') {
357 fn += 1;
358 while (isspace(*fn)) fn += 1;
359 #ifndef WIN32
360 osp = popen(fn, "w");
361 #else
362 osp = _popen(fn, "w");
363 #endif
364 if (!osp) {
365 fprintf(stderr, "inkscape: popen(%s): %s\n",
366 fn, strerror(errno));
367 return false;
368 }
369 _stream = osp;
370 } else if (*fn == '>') {
371 fn += 1;
372 while (isspace(*fn)) fn += 1;
373 Inkscape::IO::dump_fopen_call(fn, "K");
374 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
375 if (!osf) {
376 fprintf(stderr, "inkscape: fopen(%s): %s\n",
377 fn, strerror(errno));
378 return false;
379 }
380 _stream = osf;
381 } else {
382 /* put cwd stuff in here */
383 gchar *qn = ( *fn
384 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
385 : g_strdup("lpr") );
386 #ifndef WIN32
387 osp = popen(qn, "w");
388 #else
389 osp = _popen(qn, "w");
390 #endif
391 if (!osp) {
392 fprintf(stderr, "inkscape: popen(%s): %s\n",
393 qn, strerror(errno));
394 return false;
395 }
396 g_free(qn);
397 _stream = osp;
398 }
399 }
401 g_free(local_fn);
403 if (_stream) {
404 /* fixme: this is kinda icky */
405 #if !defined(_WIN32) && !defined(__WIN32__)
406 (void) signal(SIGPIPE, SIG_IGN);
407 #endif
408 }
410 return true;
411 }
413 void CairoRenderContext::setPSLevel(unsigned int level)
414 {
415 _ps_level = level;
416 }
418 void CairoRenderContext::setEPS(bool eps)
419 {
420 _eps = eps;
421 }
423 unsigned int CairoRenderContext::getPSLevel(void)
424 {
425 return _ps_level;
426 }
428 void CairoRenderContext::setPDFLevel(unsigned int level)
429 {
430 _pdf_level = level;
431 }
433 void CairoRenderContext::setTextToPath(bool texttopath)
434 {
435 _is_texttopath = texttopath;
436 }
438 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
439 {
440 _is_filtertobitmap = filtertobitmap;
441 }
443 bool CairoRenderContext::getFilterToBitmap(void)
444 {
445 return _is_filtertobitmap;
446 }
448 void CairoRenderContext::setBitmapResolution(int resolution)
449 {
450 _bitmapresolution = resolution;
451 }
453 int CairoRenderContext::getBitmapResolution(void)
454 {
455 return _bitmapresolution;
456 }
458 cairo_surface_t*
459 CairoRenderContext::getSurface(void)
460 {
461 g_assert( _is_valid );
463 return _surface;
464 }
466 bool
467 CairoRenderContext::saveAsPng(const char *file_name)
468 {
469 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
470 if (status)
471 return false;
472 else
473 return true;
474 }
476 void
477 CairoRenderContext::setRenderMode(CairoRenderMode mode)
478 {
479 switch (mode) {
480 case RENDER_MODE_NORMAL:
481 case RENDER_MODE_CLIP:
482 _render_mode = mode;
483 break;
484 default:
485 _render_mode = RENDER_MODE_NORMAL;
486 break;
487 }
488 }
490 CairoRenderContext::CairoRenderMode
491 CairoRenderContext::getRenderMode(void) const
492 {
493 return _render_mode;
494 }
496 void
497 CairoRenderContext::setClipMode(CairoClipMode mode)
498 {
499 switch (mode) {
500 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
501 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
502 _clip_mode = mode;
503 break;
504 default:
505 _clip_mode = CLIP_MODE_PATH;
506 break;
507 }
508 }
510 CairoRenderContext::CairoClipMode
511 CairoRenderContext::getClipMode(void) const
512 {
513 return _clip_mode;
514 }
516 CairoRenderState*
517 CairoRenderContext::_createState(void)
518 {
519 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
520 g_assert( state != NULL );
522 state->has_filtereffect = FALSE;
523 state->merge_opacity = TRUE;
524 state->opacity = 1.0;
525 state->need_layer = FALSE;
526 state->has_overflow = FALSE;
527 state->parent_has_userspace = FALSE;
528 state->clip_path = NULL;
529 state->mask = NULL;
531 return state;
532 }
534 void
535 CairoRenderContext::pushLayer(void)
536 {
537 g_assert( _is_valid );
539 TRACE(("--pushLayer\n"));
540 cairo_push_group(_cr);
542 // clear buffer
543 if (!_vector_based_target) {
544 cairo_save(_cr);
545 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
546 cairo_paint(_cr);
547 cairo_restore(_cr);
548 }
549 }
551 void
552 CairoRenderContext::popLayer(void)
553 {
554 g_assert( _is_valid );
556 float opacity = _state->opacity;
557 TRACE(("--popLayer w/ opacity %f\n", opacity));
559 /*
560 At this point, the Cairo source is ready. A Cairo mask must be created if required.
561 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
562 masks independently of the objects they effect while in SVG the clip paths and masks
563 are defined relative to the objects they are attached to.
564 Notes:
565 1. An SVG object may have both a clip path and a mask!
566 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
567 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
568 the page (document). This is also not handled properly.
569 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
570 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
571 alpha. This is handled here by doing a pixel by pixel conversion.
572 */
574 SPClipPath *clip_path = _state->clip_path;
575 SPMask *mask = _state->mask;
576 if (clip_path || mask) {
578 CairoRenderContext *clip_ctx = 0;
579 cairo_surface_t *clip_mask = 0;
581 // Apply any clip path first
582 if (clip_path) {
583 TRACE((" Applying clip\n"));
584 if (_render_mode == RENDER_MODE_CLIP)
585 mask = NULL; // disable mask when performing nested clipping
587 if (_vector_based_target) {
588 setClipMode(CLIP_MODE_PATH); // Vector
589 if (!mask) {
590 cairo_pop_group_to_source(_cr);
591 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
592 if (opacity == 1.0)
593 cairo_paint(_cr);
594 else
595 cairo_paint_with_alpha(_cr, opacity);
597 } else {
598 // the clipPath will be applied before masking
599 }
600 } else {
602 // setup a new rendering context
603 clip_ctx = _renderer->createContext();
604 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
605 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
606 // This code ties the clipping to the document coordinates. It doesn't allow
607 // for a clipped object intially drawn off the page and then translated onto
608 // the page.
609 if (!clip_ctx->setupSurface(_width, _height)) {
610 TRACE(("clip: setupSurface failed\n"));
611 _renderer->destroyContext(clip_ctx);
612 return;
613 }
615 // clear buffer
616 cairo_save(clip_ctx->_cr);
617 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
618 cairo_paint(clip_ctx->_cr);
619 cairo_restore(clip_ctx->_cr);
621 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
622 if (!mask)
623 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
624 else
625 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
627 // copy over the correct CTM
628 // It must be stored in item_transform of current state after pushState.
629 Geom::Matrix item_transform;
630 if (_state->parent_has_userspace)
631 item_transform = getParentState()->transform * _state->item_transform;
632 else
633 item_transform = _state->item_transform;
635 // apply the clip path
636 clip_ctx->pushState();
637 clip_ctx->getCurrentState()->item_transform = item_transform;
638 _renderer->applyClipPath(clip_ctx, clip_path);
639 clip_ctx->popState();
641 clip_mask = clip_ctx->getSurface();
642 TEST(clip_ctx->saveAsPng("clip_mask.png"));
644 if (!mask) {
645 cairo_pop_group_to_source(_cr);
646 cairo_mask_surface(_cr, clip_mask, 0, 0);
647 _renderer->destroyContext(clip_ctx);
648 }
649 }
650 }
652 // Apply any mask second
653 if (mask) {
654 TRACE((" Applying mask\n"));
655 // create rendering context for mask
656 CairoRenderContext *mask_ctx = _renderer->createContext();
658 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
659 // Cairo surface is expecting the mask to be 90 dpi.
660 float surface_width = _width;
661 float surface_height = _height;
662 if( _vector_based_target ) {
663 surface_width *= 1.25;
664 surface_height *= 1.25;
665 }
666 if (!mask_ctx->setupSurface( surface_width, surface_height )) {
667 TRACE(("mask: setupSurface failed\n"));
668 _renderer->destroyContext(mask_ctx);
669 return;
670 }
671 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
673 // set rendering mode to normal
674 setRenderMode(RENDER_MODE_NORMAL);
676 // copy the correct CTM to mask context
677 /*
678 if (_state->parent_has_userspace)
679 mask_ctx->setTransform(&getParentState()->transform);
680 else
681 mask_ctx->setTransform(&_state->transform);
682 */
683 // This is probably not correct... but it seems to do the trick.
684 mask_ctx->setTransform(&_state->item_transform);
686 // render mask contents to mask_ctx
687 _renderer->applyMask(mask_ctx, mask);
689 TEST(mask_ctx->saveAsPng("mask.png"));
691 // composite with clip mask
692 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
693 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
694 _renderer->destroyContext(clip_ctx);
695 }
697 cairo_surface_t *mask_image = mask_ctx->getSurface();
698 int width = cairo_image_surface_get_width(mask_image);
699 int height = cairo_image_surface_get_height(mask_image);
700 int stride = cairo_image_surface_get_stride(mask_image);
701 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
703 // premultiply with opacity
704 // In SVG, the rgb channels as well as the alpha channel is used in masking.
705 // In Cairo, only the alpha channel is used thus requiring this conversion.
706 TRACE(("premul w/ %f\n", opacity));
707 guint8 int_opacity = (guint8)(255 * opacity);
708 for (int row = 0 ; row < height; row++) {
709 unsigned char *row_data = pixels + (row * stride);
710 for (int i = 0 ; i < width; i++) {
711 guint32 *pixel = (guint32 *)row_data + i;
712 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
713 ((*pixel & 0x0000ff00) >> 8) * 46518 +
714 ((*pixel & 0x000000ff) ) * 4688) *
715 int_opacity);
716 }
717 }
719 cairo_pop_group_to_source(_cr);
720 if (_clip_mode == CLIP_MODE_PATH) {
721 // we have to do the clipping after cairo_pop_group_to_source
722 _renderer->applyClipPath(this, clip_path);
723 }
724 // apply the mask onto the layer
725 cairo_mask_surface(_cr, mask_image, 0, 0);
726 _renderer->destroyContext(mask_ctx);
727 }
728 } else {
729 // No clip path or mask
730 cairo_pop_group_to_source(_cr);
731 if (opacity == 1.0)
732 cairo_paint(_cr);
733 else
734 cairo_paint_with_alpha(_cr, opacity);
735 }
736 }
738 void
739 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
740 {
741 g_assert( _is_valid );
743 // here it should be checked whether the current clip winding changed
744 // so we could switch back to masked clipping
745 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
746 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
747 } else {
748 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
749 }
750 addPathVector(pv);
751 }
753 void
754 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
755 {
756 g_assert( _is_valid );
758 cairo_rectangle(_cr, x, y, width, height);
759 cairo_clip(_cr);
760 }
762 bool
763 CairoRenderContext::setupSurface(double width, double height)
764 {
765 // Is the surface already set up?
766 if (_is_valid)
767 return true;
769 if (_vector_based_target && _stream == NULL)
770 return false;
772 _width = width;
773 _height = height;
775 cairo_surface_t *surface = NULL;
776 cairo_matrix_t ctm;
777 cairo_matrix_init_identity (&ctm);
778 switch (_target) {
779 case CAIRO_SURFACE_TYPE_IMAGE:
780 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
781 break;
782 #ifdef CAIRO_HAS_PDF_SURFACE
783 case CAIRO_SURFACE_TYPE_PDF:
784 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
785 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
786 cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
787 #endif
788 break;
789 #endif
790 #ifdef CAIRO_HAS_PS_SURFACE
791 case CAIRO_SURFACE_TYPE_PS:
792 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
793 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
794 return FALSE;
795 }
796 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
797 cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
798 cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
799 #endif
800 // Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
801 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
802 if (override_bbox) {
803 cairo_ps_dsc_comment(surface, "%%BoundingBox: 100 100 200 200");
804 cairo_ps_dsc_comment(surface, "%%PageBoundingBox: 100 100 200 200");
805 }
806 #endif
807 break;
808 #endif
809 default:
810 return false;
811 break;
812 }
814 return _finishSurfaceSetup (surface, &ctm);
815 }
817 bool
818 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
819 {
820 if (_is_valid || !surface)
821 return false;
823 _vector_based_target = is_vector;
824 bool ret = _finishSurfaceSetup (surface, ctm);
825 if (ret)
826 cairo_surface_reference (surface);
827 return ret;
828 }
830 bool
831 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
832 {
833 if(surface == NULL) {
834 return false;
835 }
836 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
837 return false;
838 }
840 _cr = cairo_create(surface);
841 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
842 return false;
843 }
844 if (ctm)
845 cairo_set_matrix(_cr, ctm);
846 _surface = surface;
848 if (_vector_based_target) {
849 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
850 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
851 // set background color on non-alpha surfaces
852 // TODO: bgcolor should be derived from SPDocument
853 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
854 cairo_rectangle(_cr, 0, 0, _width, _height);
855 cairo_fill(_cr);
856 }
858 _is_valid = TRUE;
860 return true;
861 }
863 bool
864 CairoRenderContext::finish(void)
865 {
866 g_assert( _is_valid );
868 if (_vector_based_target)
869 cairo_show_page(_cr);
871 cairo_destroy(_cr);
872 cairo_surface_finish(_surface);
873 cairo_status_t status = cairo_surface_status(_surface);
874 cairo_surface_destroy(_surface);
875 _cr = NULL;
876 _surface = NULL;
878 if (_layout)
879 g_object_unref(_layout);
881 _is_valid = FALSE;
883 if (_vector_based_target && _stream) {
884 /* Flush stream to be sure. */
885 (void) fflush(_stream);
887 fclose(_stream);
888 _stream = NULL;
889 }
891 if (status == CAIRO_STATUS_SUCCESS)
892 return true;
893 else
894 return false;
895 }
897 void
898 CairoRenderContext::transform(Geom::Matrix const *transform)
899 {
900 g_assert( _is_valid );
902 cairo_matrix_t matrix;
903 _initCairoMatrix(&matrix, transform);
904 cairo_transform(_cr, &matrix);
906 // store new CTM
907 getTransform(&_state->transform);
908 }
910 void
911 CairoRenderContext::setTransform(Geom::Matrix const *transform)
912 {
913 g_assert( _is_valid );
915 cairo_matrix_t matrix;
916 _initCairoMatrix(&matrix, transform);
917 cairo_set_matrix(_cr, &matrix);
918 _state->transform = *transform;
919 }
921 void
922 CairoRenderContext::getTransform(Geom::Matrix *copy) const
923 {
924 g_assert( _is_valid );
926 cairo_matrix_t ctm;
927 cairo_get_matrix(_cr, &ctm);
928 (*copy)[0] = ctm.xx;
929 (*copy)[1] = ctm.yx;
930 (*copy)[2] = ctm.xy;
931 (*copy)[3] = ctm.yy;
932 (*copy)[4] = ctm.x0;
933 (*copy)[5] = ctm.y0;
934 }
936 void
937 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
938 {
939 g_assert( _is_valid );
941 CairoRenderState *parent_state = getParentState();
942 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
943 }
945 void
946 CairoRenderContext::pushState(void)
947 {
948 g_assert( _is_valid );
950 cairo_save(_cr);
952 CairoRenderState *new_state = _createState();
953 // copy current state's transform
954 new_state->transform = _state->transform;
955 _state_stack = g_slist_prepend(_state_stack, new_state);
956 _state = new_state;
957 }
959 void
960 CairoRenderContext::popState(void)
961 {
962 g_assert( _is_valid );
964 cairo_restore(_cr);
966 g_free(_state_stack->data);
967 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
968 _state = (CairoRenderState*)_state_stack->data;
970 g_assert( g_slist_length(_state_stack) > 0 );
971 }
973 static bool pattern_hasItemChildren (SPPattern *pat)
974 {
975 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
976 if (SP_IS_ITEM (child)) {
977 return true;
978 }
979 }
980 return false;
981 }
983 cairo_pattern_t*
984 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
985 {
986 g_assert( SP_IS_PATTERN(paintserver) );
988 SPPattern *pat = SP_PATTERN (paintserver);
990 Geom::Matrix ps2user, pcs2dev;
991 ps2user = Geom::identity();
992 pcs2dev = Geom::identity();
994 double x = pattern_x(pat);
995 double y = pattern_y(pat);
996 double width = pattern_width(pat);
997 double height = pattern_height(pat);
998 double bbox_width_scaler;
999 double bbox_height_scaler;
1001 TRACE(("%f x %f pattern\n", width, height));
1003 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1004 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1005 bbox_width_scaler = pbox->x1 - pbox->x0;
1006 bbox_height_scaler = pbox->y1 - pbox->y0;
1007 ps2user[4] = x * bbox_width_scaler + pbox->x0;
1008 ps2user[5] = y * bbox_height_scaler + pbox->y0;
1009 } else {
1010 bbox_width_scaler = 1.0;
1011 bbox_height_scaler = 1.0;
1012 ps2user[4] = x;
1013 ps2user[5] = y;
1014 }
1016 // apply pattern transformation
1017 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1018 ps2user *= pattern_transform;
1019 Geom::Point ori (ps2user[4], ps2user[5]);
1021 // create pattern contents coordinate system
1022 if (pat->viewBox_set) {
1023 NRRect *view_box = pattern_viewBox(pat);
1025 double x, y, w, h;
1026 double view_width, view_height;
1027 x = 0;
1028 y = 0;
1029 w = width * bbox_width_scaler;
1030 h = height * bbox_height_scaler;
1032 view_width = view_box->x1 - view_box->x0;
1033 view_height = view_box->y1 - view_box->y0;
1035 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1036 pcs2dev[0] = w / view_width;
1037 pcs2dev[3] = h / view_height;
1038 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1039 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1040 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1041 pcs2dev[0] = pbox->x1 - pbox->x0;
1042 pcs2dev[3] = pbox->y1 - pbox->y0;
1044 }
1046 // Calculate the size of the surface which has to be created
1047 #define SUBPIX_SCALE 100
1048 // Cairo requires an integer pattern surface width/height.
1049 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1050 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1051 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1052 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1053 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1054 // create new rendering context
1055 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1057 // adjust the size of the painted pattern to fit exactly the created surface
1058 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1059 double scale_width = surface_width / (bbox_width_scaler * width);
1060 double scale_height = surface_height / (bbox_height_scaler * height);
1061 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1062 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1063 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1064 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1065 }
1067 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1068 ps2user[4] = ori[Geom::X];
1069 ps2user[5] = ori[Geom::Y];
1071 pattern_ctx->setTransform(&pcs2dev);
1072 pattern_ctx->pushState();
1074 // create arena and group
1075 NRArena *arena = NRArena::create();
1076 unsigned dkey = sp_item_display_key_new(1);
1078 // show items and render them
1079 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1080 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1081 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1082 if (SP_IS_ITEM (child)) {
1083 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1084 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1085 }
1086 }
1087 break; // do not go further up the chain if children are found
1088 }
1089 }
1091 pattern_ctx->popState();
1093 // setup a cairo_pattern_t
1094 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1095 TEST(pattern_ctx->saveAsPng("pattern.png"));
1096 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1097 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1099 // set pattern transformation
1100 cairo_matrix_t pattern_matrix;
1101 _initCairoMatrix(&pattern_matrix, &ps2user);
1102 cairo_matrix_invert(&pattern_matrix);
1103 cairo_pattern_set_matrix(result, &pattern_matrix);
1105 delete pattern_ctx;
1107 // hide all items
1108 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1109 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1110 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1111 if (SP_IS_ITEM (child)) {
1112 sp_item_invoke_hide (SP_ITEM (child), dkey);
1113 }
1114 }
1115 break; // do not go further up the chain if children are found
1116 }
1117 }
1119 return result;
1120 }
1122 cairo_pattern_t*
1123 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1124 NRRect const *pbox, float alpha)
1125 {
1126 cairo_pattern_t *pattern = NULL;
1127 bool apply_bbox2user = FALSE;
1129 if (SP_IS_LINEARGRADIENT (paintserver)) {
1131 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1133 SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1135 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1136 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1137 if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1138 // convert to userspace
1139 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1140 p1 *= bbox2user;
1141 p2 *= bbox2user;
1142 }
1144 // create linear gradient pattern
1145 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1147 // add stops
1148 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1149 float rgb[3];
1150 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1151 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1152 }
1153 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1155 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1157 SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1159 Geom::Point c (rg->cx.computed, rg->cy.computed);
1160 Geom::Point f (rg->fx.computed, rg->fy.computed);
1161 double r = rg->r.computed;
1162 if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1163 apply_bbox2user = true;
1165 // create radial gradient pattern
1166 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1168 // add stops
1169 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1170 float rgb[3];
1171 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1172 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1173 }
1174 } else if (SP_IS_PATTERN (paintserver)) {
1176 pattern = _createPatternPainter(paintserver, pbox);
1177 } else {
1178 return NULL;
1179 }
1181 if (pattern && SP_IS_GRADIENT (paintserver)) {
1182 SPGradient *g = SP_GRADIENT(paintserver);
1184 // set extend type
1185 SPGradientSpread spread = g->fetchSpread();
1186 switch (spread) {
1187 case SP_GRADIENT_SPREAD_REPEAT: {
1188 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1189 break;
1190 }
1191 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1192 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1193 break;
1194 }
1195 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1196 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1197 break;
1198 }
1199 default: {
1200 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1201 break;
1202 }
1203 }
1205 cairo_matrix_t pattern_matrix;
1206 if (g->gradientTransform_set) {
1207 // apply gradient transformation
1208 cairo_matrix_init(&pattern_matrix,
1209 g->gradientTransform[0], g->gradientTransform[1],
1210 g->gradientTransform[2], g->gradientTransform[3],
1211 g->gradientTransform[4], g->gradientTransform[5]);
1212 } else {
1213 cairo_matrix_init_identity (&pattern_matrix);
1214 }
1216 if (apply_bbox2user) {
1217 // convert to userspace
1218 cairo_matrix_t bbox2user;
1219 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1220 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1221 }
1222 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1223 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1224 }
1226 return pattern;
1227 }
1229 void
1230 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1231 {
1232 g_return_if_fail( !style->fill.set
1233 || style->fill.isColor()
1234 || style->fill.isPaintserver() );
1236 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1237 if (_state->merge_opacity) {
1238 alpha *= _state->opacity;
1239 TRACE(("merged op=%f\n", alpha));
1240 }
1242 if (style->fill.isColor()) {
1243 float rgb[3];
1244 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1246 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1248 } else if (!style->fill.set) { // unset fill is black
1249 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1251 } else {
1252 g_assert( style->fill.isPaintserver()
1253 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1254 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1256 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1258 if (pattern) {
1259 cairo_set_source(_cr, pattern);
1260 cairo_pattern_destroy(pattern);
1261 }
1262 }
1263 }
1265 void
1266 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1267 {
1268 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1269 if (_state->merge_opacity)
1270 alpha *= _state->opacity;
1272 if (style->stroke.isColor()) {
1273 float rgb[3];
1274 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1276 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1277 } else {
1278 g_assert( style->stroke.isPaintserver()
1279 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1280 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1282 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1284 if (pattern) {
1285 cairo_set_source(_cr, pattern);
1286 cairo_pattern_destroy(pattern);
1287 }
1288 }
1290 if (style->stroke_dash.n_dash &&
1291 style->stroke_dash.dash )
1292 {
1293 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1294 } else {
1295 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1296 }
1298 cairo_set_line_width(_cr, style->stroke_width.computed);
1300 // set line join type
1301 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1302 switch (style->stroke_linejoin.computed) {
1303 case SP_STROKE_LINEJOIN_MITER:
1304 join = CAIRO_LINE_JOIN_MITER;
1305 break;
1306 case SP_STROKE_LINEJOIN_ROUND:
1307 join = CAIRO_LINE_JOIN_ROUND;
1308 break;
1309 case SP_STROKE_LINEJOIN_BEVEL:
1310 join = CAIRO_LINE_JOIN_BEVEL;
1311 break;
1312 }
1313 cairo_set_line_join(_cr, join);
1315 // set line cap type
1316 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1317 switch (style->stroke_linecap.computed) {
1318 case SP_STROKE_LINECAP_BUTT:
1319 cap = CAIRO_LINE_CAP_BUTT;
1320 break;
1321 case SP_STROKE_LINECAP_ROUND:
1322 cap = CAIRO_LINE_CAP_ROUND;
1323 break;
1324 case SP_STROKE_LINECAP_SQUARE:
1325 cap = CAIRO_LINE_CAP_SQUARE;
1326 break;
1327 }
1328 cairo_set_line_cap(_cr, cap);
1329 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1330 }
1332 bool
1333 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1334 {
1335 g_assert( _is_valid );
1337 if (_render_mode == RENDER_MODE_CLIP) {
1338 if (_clip_mode == CLIP_MODE_PATH) {
1339 addClipPath(pathv, &style->fill_rule);
1340 } else {
1341 setPathVector(pathv);
1342 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1343 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1344 } else {
1345 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1346 }
1347 cairo_fill(_cr);
1348 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1349 }
1350 return true;
1351 }
1353 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1354 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1355 style->stroke_opacity.value == 0;
1357 if (no_fill && no_stroke)
1358 return true;
1360 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1361 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1363 if (!need_layer)
1364 cairo_save(_cr);
1365 else
1366 pushLayer();
1368 if (!no_fill) {
1369 _setFillStyle(style, pbox);
1370 setPathVector(pathv);
1372 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1373 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1374 } else {
1375 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1376 }
1378 if (no_stroke)
1379 cairo_fill(_cr);
1380 else
1381 cairo_fill_preserve(_cr);
1382 }
1384 if (!no_stroke) {
1385 _setStrokeStyle(style, pbox);
1386 if (no_fill)
1387 setPathVector(pathv);
1389 cairo_stroke(_cr);
1390 }
1392 if (need_layer)
1393 popLayer();
1394 else
1395 cairo_restore(_cr);
1397 return true;
1398 }
1400 bool
1401 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1402 Geom::Matrix const *image_transform, SPStyle const *style)
1403 {
1404 g_assert( _is_valid );
1406 if (_render_mode == RENDER_MODE_CLIP)
1407 return true;
1409 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1410 if (!px_rgba)
1411 return false;
1413 float opacity;
1414 if (_state->merge_opacity)
1415 opacity = _state->opacity;
1416 else
1417 opacity = 1.0;
1419 // make a copy of the original pixbuf with premultiplied alpha
1420 // if we pass the original pixbuf it will get messed up
1421 for (unsigned i = 0; i < h; i++) {
1422 for (unsigned j = 0; j < w; j++) {
1423 guchar const *src = px + i * rs + j * 4;
1424 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1425 guchar r, g, b, alpha_dst;
1427 // calculate opacity-modified alpha
1428 alpha_dst = src[3];
1429 if (opacity != 1.0 && _vector_based_target)
1430 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1432 // premul alpha (needed because this will be undone by cairo-pdf)
1433 r = src[0]*alpha_dst/255;
1434 g = src[1]*alpha_dst/255;
1435 b = src[2]*alpha_dst/255;
1437 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1438 }
1439 }
1441 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1442 if (cairo_surface_status(image_surface)) {
1443 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1444 return false;
1445 }
1447 // setup automatic freeing of the image data when destroying the surface
1448 static cairo_user_data_key_t key;
1449 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1451 cairo_save(_cr);
1453 // scaling by width & height is not needed because it will be done by Cairo
1454 if (image_transform)
1455 transform(image_transform);
1457 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1459 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1460 if (_vector_based_target) {
1461 cairo_new_path(_cr);
1462 cairo_rectangle(_cr, 0, 0, w, h);
1463 cairo_clip(_cr);
1464 }
1466 if (_vector_based_target)
1467 cairo_paint(_cr);
1468 else
1469 cairo_paint_with_alpha(_cr, opacity);
1471 cairo_restore(_cr);
1473 cairo_surface_destroy(image_surface);
1475 return true;
1476 }
1478 #define GLYPH_ARRAY_SIZE 64
1480 unsigned int
1481 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1482 {
1483 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1484 cairo_glyph_t *glyphs = glyph_array;
1485 unsigned int num_glyphs = glyphtext.size();
1486 if (num_glyphs > GLYPH_ARRAY_SIZE)
1487 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1489 unsigned int num_invalid_glyphs = 0;
1490 unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1491 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1492 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1493 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1494 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1495 TRACE(("INVALID GLYPH found\n"));
1496 g_message("Invalid glyph found, continuing...");
1497 num_invalid_glyphs++;
1498 continue;
1499 }
1500 glyphs[i].index = it_info->index;
1501 glyphs[i].x = it_info->x;
1502 glyphs[i].y = it_info->y;
1503 i++;
1504 }
1506 if (path) {
1507 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1508 } else {
1509 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1510 }
1512 if (num_glyphs > GLYPH_ARRAY_SIZE)
1513 g_free(glyphs);
1515 return num_glyphs - num_invalid_glyphs;
1516 }
1518 bool
1519 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1520 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1521 {
1522 // create a cairo_font_face from PangoFont
1523 double size = style->font_size.computed;
1524 gpointer fonthash = (gpointer)font;
1525 cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1527 FcPattern *fc_pattern = NULL;
1529 #ifdef USE_PANGO_WIN32
1530 # ifdef CAIRO_HAS_WIN32_FONT
1531 LOGFONTA *lfa = pango_win32_font_logfont(font);
1532 LOGFONTW lfw;
1534 ZeroMemory(&lfw, sizeof(LOGFONTW));
1535 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1536 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1538 if(font_face == NULL) {
1539 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1540 g_hash_table_insert(font_table, fonthash, font_face);
1541 }
1542 # endif
1543 #else
1544 # ifdef CAIRO_HAS_FT_FONT
1545 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1546 fc_pattern = fc_font->font_pattern;
1547 if(font_face == NULL) {
1548 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1549 g_hash_table_insert(font_table, fonthash, font_face);
1550 }
1551 # endif
1552 #endif
1554 cairo_save(_cr);
1555 cairo_set_font_face(_cr, font_face);
1557 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1558 size = 12.0;
1560 // set the given font matrix
1561 cairo_matrix_t matrix;
1562 _initCairoMatrix(&matrix, font_matrix);
1563 cairo_set_font_matrix(_cr, &matrix);
1565 if (_render_mode == RENDER_MODE_CLIP) {
1566 if (_clip_mode == CLIP_MODE_MASK) {
1567 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1568 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1569 } else {
1570 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1571 }
1572 _showGlyphs(_cr, font, glyphtext, FALSE);
1573 } else {
1574 // just add the glyph paths to the current context
1575 _showGlyphs(_cr, font, glyphtext, TRUE);
1576 }
1577 } else {
1578 bool fill = false, stroke = false, have_path = false;
1579 if (style->fill.isColor() || style->fill.isPaintserver()) {
1580 fill = true;
1581 }
1583 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1584 stroke = true;
1585 }
1586 if (fill) {
1587 _setFillStyle(style, NULL);
1588 if (_is_texttopath) {
1589 _showGlyphs(_cr, font, glyphtext, true);
1590 have_path = true;
1591 if (stroke) cairo_fill_preserve(_cr);
1592 else cairo_fill(_cr);
1593 } else {
1594 _showGlyphs(_cr, font, glyphtext, false);
1595 }
1596 }
1597 if (stroke) {
1598 _setStrokeStyle(style, NULL);
1599 if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1600 cairo_stroke(_cr);
1601 }
1602 }
1604 cairo_restore(_cr);
1606 // if (font_face)
1607 // cairo_font_face_destroy(font_face);
1609 return true;
1610 }
1612 /* Helper functions */
1614 void
1615 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1616 {
1617 cairo_new_path(_cr);
1618 addPathVector(pv);
1619 }
1621 void
1622 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1623 {
1624 feed_pathvector_to_cairo(_cr, pv);
1625 }
1627 void
1628 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1629 {
1630 cairo_matrix_t matrix;
1632 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1633 cairo_transform(cr, &matrix);
1634 }
1636 void
1637 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1638 {
1639 matrix->xx = (*transform)[0];
1640 matrix->yx = (*transform)[1];
1641 matrix->xy = (*transform)[2];
1642 matrix->yy = (*transform)[3];
1643 matrix->x0 = (*transform)[4];
1644 matrix->y0 = (*transform)[5];
1645 }
1647 void
1648 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1649 {
1650 _concatTransform(cr, (*transform)[0], (*transform)[1],
1651 (*transform)[2], (*transform)[3],
1652 (*transform)[4], (*transform)[5]);
1653 }
1655 static cairo_status_t
1656 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1657 {
1658 size_t written;
1659 FILE *file = (FILE*)closure;
1661 written = fwrite (data, 1, length, file);
1663 if (written == length)
1664 return CAIRO_STATUS_SUCCESS;
1665 else
1666 return CAIRO_STATUS_WRITE_ERROR;
1667 }
1669 #include "clear-n_.h"
1671 } /* namespace Internal */
1672 } /* namespace Extension */
1673 } /* namespace Inkscape */
1675 #undef TRACE
1676 #undef TEST
1678 /* End of GNU GPL code */
1681 /*
1682 Local Variables:
1683 mode:c++
1684 c-file-style:"stroustrup"
1685 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1686 indent-tabs-mode:nil
1687 fill-column:99
1688 End:
1689 */
1690 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :