066324ebf96a2ee1145702625fdc0c81c4b99793
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 break;
801 #endif
802 default:
803 return false;
804 break;
805 }
807 return _finishSurfaceSetup (surface, &ctm);
808 }
810 bool
811 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
812 {
813 if (_is_valid || !surface)
814 return false;
816 _vector_based_target = is_vector;
817 bool ret = _finishSurfaceSetup (surface, ctm);
818 if (ret)
819 cairo_surface_reference (surface);
820 return ret;
821 }
823 bool
824 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
825 {
826 if(surface == NULL) {
827 return false;
828 }
829 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
830 return false;
831 }
833 _cr = cairo_create(surface);
834 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
835 return false;
836 }
837 if (ctm)
838 cairo_set_matrix(_cr, ctm);
839 _surface = surface;
841 if (_vector_based_target) {
842 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
843 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
844 // set background color on non-alpha surfaces
845 // TODO: bgcolor should be derived from SPDocument
846 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
847 cairo_rectangle(_cr, 0, 0, _width, _height);
848 cairo_fill(_cr);
849 }
851 _is_valid = TRUE;
853 return true;
854 }
856 bool
857 CairoRenderContext::finish(void)
858 {
859 g_assert( _is_valid );
861 if (_vector_based_target)
862 cairo_show_page(_cr);
864 cairo_destroy(_cr);
865 cairo_surface_finish(_surface);
866 cairo_status_t status = cairo_surface_status(_surface);
867 cairo_surface_destroy(_surface);
868 _cr = NULL;
869 _surface = NULL;
871 if (_layout)
872 g_object_unref(_layout);
874 _is_valid = FALSE;
876 if (_vector_based_target && _stream) {
877 /* Flush stream to be sure. */
878 (void) fflush(_stream);
880 fclose(_stream);
881 _stream = NULL;
882 }
884 if (status == CAIRO_STATUS_SUCCESS)
885 return true;
886 else
887 return false;
888 }
890 void
891 CairoRenderContext::transform(Geom::Matrix const *transform)
892 {
893 g_assert( _is_valid );
895 cairo_matrix_t matrix;
896 _initCairoMatrix(&matrix, transform);
897 cairo_transform(_cr, &matrix);
899 // store new CTM
900 getTransform(&_state->transform);
901 }
903 void
904 CairoRenderContext::setTransform(Geom::Matrix const *transform)
905 {
906 g_assert( _is_valid );
908 cairo_matrix_t matrix;
909 _initCairoMatrix(&matrix, transform);
910 cairo_set_matrix(_cr, &matrix);
911 _state->transform = *transform;
912 }
914 void
915 CairoRenderContext::getTransform(Geom::Matrix *copy) const
916 {
917 g_assert( _is_valid );
919 cairo_matrix_t ctm;
920 cairo_get_matrix(_cr, &ctm);
921 (*copy)[0] = ctm.xx;
922 (*copy)[1] = ctm.yx;
923 (*copy)[2] = ctm.xy;
924 (*copy)[3] = ctm.yy;
925 (*copy)[4] = ctm.x0;
926 (*copy)[5] = ctm.y0;
927 }
929 void
930 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
931 {
932 g_assert( _is_valid );
934 CairoRenderState *parent_state = getParentState();
935 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
936 }
938 void
939 CairoRenderContext::pushState(void)
940 {
941 g_assert( _is_valid );
943 cairo_save(_cr);
945 CairoRenderState *new_state = _createState();
946 // copy current state's transform
947 new_state->transform = _state->transform;
948 _state_stack = g_slist_prepend(_state_stack, new_state);
949 _state = new_state;
950 }
952 void
953 CairoRenderContext::popState(void)
954 {
955 g_assert( _is_valid );
957 cairo_restore(_cr);
959 g_free(_state_stack->data);
960 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
961 _state = (CairoRenderState*)_state_stack->data;
963 g_assert( g_slist_length(_state_stack) > 0 );
964 }
966 static bool pattern_hasItemChildren (SPPattern *pat)
967 {
968 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
969 if (SP_IS_ITEM (child)) {
970 return true;
971 }
972 }
973 return false;
974 }
976 cairo_pattern_t*
977 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
978 {
979 g_assert( SP_IS_PATTERN(paintserver) );
981 SPPattern *pat = SP_PATTERN (paintserver);
983 Geom::Matrix ps2user, pcs2dev;
984 ps2user = Geom::identity();
985 pcs2dev = Geom::identity();
987 double x = pattern_x(pat);
988 double y = pattern_y(pat);
989 double width = pattern_width(pat);
990 double height = pattern_height(pat);
991 double bbox_width_scaler;
992 double bbox_height_scaler;
994 TRACE(("%f x %f pattern\n", width, height));
996 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
997 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
998 bbox_width_scaler = pbox->x1 - pbox->x0;
999 bbox_height_scaler = pbox->y1 - pbox->y0;
1000 ps2user[4] = x * bbox_width_scaler + pbox->x0;
1001 ps2user[5] = y * bbox_height_scaler + pbox->y0;
1002 } else {
1003 bbox_width_scaler = 1.0;
1004 bbox_height_scaler = 1.0;
1005 ps2user[4] = x;
1006 ps2user[5] = y;
1007 }
1009 // apply pattern transformation
1010 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1011 ps2user *= pattern_transform;
1012 Geom::Point ori (ps2user[4], ps2user[5]);
1014 // create pattern contents coordinate system
1015 if (pat->viewBox_set) {
1016 NRRect *view_box = pattern_viewBox(pat);
1018 double x, y, w, h;
1019 double view_width, view_height;
1020 x = 0;
1021 y = 0;
1022 w = width * bbox_width_scaler;
1023 h = height * bbox_height_scaler;
1025 view_width = view_box->x1 - view_box->x0;
1026 view_height = view_box->y1 - view_box->y0;
1028 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1029 pcs2dev[0] = w / view_width;
1030 pcs2dev[3] = h / view_height;
1031 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1032 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1033 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1034 pcs2dev[0] = pbox->x1 - pbox->x0;
1035 pcs2dev[3] = pbox->y1 - pbox->y0;
1037 }
1039 // Calculate the size of the surface which has to be created
1040 #define SUBPIX_SCALE 100
1041 // Cairo requires an integer pattern surface width/height.
1042 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1043 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1044 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1045 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1046 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1047 // create new rendering context
1048 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1050 // adjust the size of the painted pattern to fit exactly the created surface
1051 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1052 double scale_width = surface_width / (bbox_width_scaler * width);
1053 double scale_height = surface_height / (bbox_height_scaler * height);
1054 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1055 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1056 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1057 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1058 }
1060 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1061 ps2user[4] = ori[Geom::X];
1062 ps2user[5] = ori[Geom::Y];
1064 pattern_ctx->setTransform(&pcs2dev);
1065 pattern_ctx->pushState();
1067 // create arena and group
1068 NRArena *arena = NRArena::create();
1069 unsigned dkey = sp_item_display_key_new(1);
1071 // show items and render them
1072 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1073 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1074 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1075 if (SP_IS_ITEM (child)) {
1076 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1077 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1078 }
1079 }
1080 break; // do not go further up the chain if children are found
1081 }
1082 }
1084 pattern_ctx->popState();
1086 // setup a cairo_pattern_t
1087 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1088 TEST(pattern_ctx->saveAsPng("pattern.png"));
1089 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1090 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1092 // set pattern transformation
1093 cairo_matrix_t pattern_matrix;
1094 _initCairoMatrix(&pattern_matrix, &ps2user);
1095 cairo_matrix_invert(&pattern_matrix);
1096 cairo_pattern_set_matrix(result, &pattern_matrix);
1098 delete pattern_ctx;
1100 // hide all items
1101 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1102 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1103 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1104 if (SP_IS_ITEM (child)) {
1105 sp_item_invoke_hide (SP_ITEM (child), dkey);
1106 }
1107 }
1108 break; // do not go further up the chain if children are found
1109 }
1110 }
1112 return result;
1113 }
1115 cairo_pattern_t*
1116 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1117 NRRect const *pbox, float alpha)
1118 {
1119 cairo_pattern_t *pattern = NULL;
1120 bool apply_bbox2user = FALSE;
1122 if (SP_IS_LINEARGRADIENT (paintserver)) {
1124 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1126 SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1128 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1129 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1130 if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1131 // convert to userspace
1132 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1133 p1 *= bbox2user;
1134 p2 *= bbox2user;
1135 }
1137 // create linear gradient pattern
1138 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1140 // add stops
1141 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1142 float rgb[3];
1143 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1144 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1145 }
1146 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1148 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1150 SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1152 Geom::Point c (rg->cx.computed, rg->cy.computed);
1153 Geom::Point f (rg->fx.computed, rg->fy.computed);
1154 double r = rg->r.computed;
1155 if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1156 apply_bbox2user = true;
1158 // create radial gradient pattern
1159 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1161 // add stops
1162 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1163 float rgb[3];
1164 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1165 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1166 }
1167 } else if (SP_IS_PATTERN (paintserver)) {
1169 pattern = _createPatternPainter(paintserver, pbox);
1170 } else {
1171 return NULL;
1172 }
1174 if (pattern && SP_IS_GRADIENT (paintserver)) {
1175 SPGradient *g = SP_GRADIENT(paintserver);
1177 // set extend type
1178 SPGradientSpread spread = g->fetchSpread();
1179 switch (spread) {
1180 case SP_GRADIENT_SPREAD_REPEAT: {
1181 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1182 break;
1183 }
1184 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1185 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1186 break;
1187 }
1188 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1189 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1190 break;
1191 }
1192 default: {
1193 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1194 break;
1195 }
1196 }
1198 cairo_matrix_t pattern_matrix;
1199 if (g->gradientTransform_set) {
1200 // apply gradient transformation
1201 cairo_matrix_init(&pattern_matrix,
1202 g->gradientTransform[0], g->gradientTransform[1],
1203 g->gradientTransform[2], g->gradientTransform[3],
1204 g->gradientTransform[4], g->gradientTransform[5]);
1205 } else {
1206 cairo_matrix_init_identity (&pattern_matrix);
1207 }
1209 if (apply_bbox2user) {
1210 // convert to userspace
1211 cairo_matrix_t bbox2user;
1212 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1213 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1214 }
1215 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1216 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1217 }
1219 return pattern;
1220 }
1222 void
1223 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1224 {
1225 g_return_if_fail( !style->fill.set
1226 || style->fill.isColor()
1227 || style->fill.isPaintserver() );
1229 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1230 if (_state->merge_opacity) {
1231 alpha *= _state->opacity;
1232 TRACE(("merged op=%f\n", alpha));
1233 }
1235 if (style->fill.isColor()) {
1236 float rgb[3];
1237 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1239 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1241 } else if (!style->fill.set) { // unset fill is black
1242 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1244 } else {
1245 g_assert( style->fill.isPaintserver()
1246 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1247 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1249 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1251 if (pattern) {
1252 cairo_set_source(_cr, pattern);
1253 cairo_pattern_destroy(pattern);
1254 }
1255 }
1256 }
1258 void
1259 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1260 {
1261 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1262 if (_state->merge_opacity)
1263 alpha *= _state->opacity;
1265 if (style->stroke.isColor()) {
1266 float rgb[3];
1267 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1269 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1270 } else {
1271 g_assert( style->stroke.isPaintserver()
1272 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1273 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1275 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1277 if (pattern) {
1278 cairo_set_source(_cr, pattern);
1279 cairo_pattern_destroy(pattern);
1280 }
1281 }
1283 if (style->stroke_dash.n_dash &&
1284 style->stroke_dash.dash )
1285 {
1286 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1287 } else {
1288 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1289 }
1291 cairo_set_line_width(_cr, style->stroke_width.computed);
1293 // set line join type
1294 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1295 switch (style->stroke_linejoin.computed) {
1296 case SP_STROKE_LINEJOIN_MITER:
1297 join = CAIRO_LINE_JOIN_MITER;
1298 break;
1299 case SP_STROKE_LINEJOIN_ROUND:
1300 join = CAIRO_LINE_JOIN_ROUND;
1301 break;
1302 case SP_STROKE_LINEJOIN_BEVEL:
1303 join = CAIRO_LINE_JOIN_BEVEL;
1304 break;
1305 }
1306 cairo_set_line_join(_cr, join);
1308 // set line cap type
1309 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1310 switch (style->stroke_linecap.computed) {
1311 case SP_STROKE_LINECAP_BUTT:
1312 cap = CAIRO_LINE_CAP_BUTT;
1313 break;
1314 case SP_STROKE_LINECAP_ROUND:
1315 cap = CAIRO_LINE_CAP_ROUND;
1316 break;
1317 case SP_STROKE_LINECAP_SQUARE:
1318 cap = CAIRO_LINE_CAP_SQUARE;
1319 break;
1320 }
1321 cairo_set_line_cap(_cr, cap);
1322 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1323 }
1325 bool
1326 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1327 {
1328 g_assert( _is_valid );
1330 if (_render_mode == RENDER_MODE_CLIP) {
1331 if (_clip_mode == CLIP_MODE_PATH) {
1332 addClipPath(pathv, &style->fill_rule);
1333 } else {
1334 setPathVector(pathv);
1335 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1336 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1337 } else {
1338 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1339 }
1340 cairo_fill(_cr);
1341 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1342 }
1343 return true;
1344 }
1346 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1347 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1348 style->stroke_opacity.value == 0;
1350 if (no_fill && no_stroke)
1351 return true;
1353 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1354 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1356 if (!need_layer)
1357 cairo_save(_cr);
1358 else
1359 pushLayer();
1361 if (!no_fill) {
1362 _setFillStyle(style, pbox);
1363 setPathVector(pathv);
1365 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1366 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1367 } else {
1368 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1369 }
1371 if (no_stroke)
1372 cairo_fill(_cr);
1373 else
1374 cairo_fill_preserve(_cr);
1375 }
1377 if (!no_stroke) {
1378 _setStrokeStyle(style, pbox);
1379 if (no_fill)
1380 setPathVector(pathv);
1382 cairo_stroke(_cr);
1383 }
1385 if (need_layer)
1386 popLayer();
1387 else
1388 cairo_restore(_cr);
1390 return true;
1391 }
1393 bool
1394 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1395 Geom::Matrix const *image_transform, SPStyle const *style)
1396 {
1397 g_assert( _is_valid );
1399 if (_render_mode == RENDER_MODE_CLIP)
1400 return true;
1402 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1403 if (!px_rgba)
1404 return false;
1406 float opacity;
1407 if (_state->merge_opacity)
1408 opacity = _state->opacity;
1409 else
1410 opacity = 1.0;
1412 // make a copy of the original pixbuf with premultiplied alpha
1413 // if we pass the original pixbuf it will get messed up
1414 for (unsigned i = 0; i < h; i++) {
1415 for (unsigned j = 0; j < w; j++) {
1416 guchar const *src = px + i * rs + j * 4;
1417 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1418 guchar r, g, b, alpha_dst;
1420 // calculate opacity-modified alpha
1421 alpha_dst = src[3];
1422 if (opacity != 1.0 && _vector_based_target)
1423 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1425 // premul alpha (needed because this will be undone by cairo-pdf)
1426 r = src[0]*alpha_dst/255;
1427 g = src[1]*alpha_dst/255;
1428 b = src[2]*alpha_dst/255;
1430 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1431 }
1432 }
1434 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1435 if (cairo_surface_status(image_surface)) {
1436 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1437 return false;
1438 }
1440 // setup automatic freeing of the image data when destroying the surface
1441 static cairo_user_data_key_t key;
1442 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1444 cairo_save(_cr);
1446 // scaling by width & height is not needed because it will be done by Cairo
1447 if (image_transform)
1448 transform(image_transform);
1450 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1452 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1453 if (_vector_based_target) {
1454 cairo_new_path(_cr);
1455 cairo_rectangle(_cr, 0, 0, w, h);
1456 cairo_clip(_cr);
1457 }
1459 if (_vector_based_target)
1460 cairo_paint(_cr);
1461 else
1462 cairo_paint_with_alpha(_cr, opacity);
1464 cairo_restore(_cr);
1466 cairo_surface_destroy(image_surface);
1468 return true;
1469 }
1471 #define GLYPH_ARRAY_SIZE 64
1473 unsigned int
1474 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1475 {
1476 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1477 cairo_glyph_t *glyphs = glyph_array;
1478 unsigned int num_glyphs = glyphtext.size();
1479 if (num_glyphs > GLYPH_ARRAY_SIZE)
1480 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1482 unsigned int num_invalid_glyphs = 0;
1483 unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1484 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1485 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1486 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1487 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1488 TRACE(("INVALID GLYPH found\n"));
1489 g_message("Invalid glyph found, continuing...");
1490 num_invalid_glyphs++;
1491 continue;
1492 }
1493 glyphs[i].index = it_info->index;
1494 glyphs[i].x = it_info->x;
1495 glyphs[i].y = it_info->y;
1496 i++;
1497 }
1499 if (path) {
1500 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1501 } else {
1502 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1503 }
1505 if (num_glyphs > GLYPH_ARRAY_SIZE)
1506 g_free(glyphs);
1508 return num_glyphs - num_invalid_glyphs;
1509 }
1511 bool
1512 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1513 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1514 {
1515 // create a cairo_font_face from PangoFont
1516 double size = style->font_size.computed;
1517 gpointer fonthash = (gpointer)font;
1518 cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1520 FcPattern *fc_pattern = NULL;
1522 #ifdef USE_PANGO_WIN32
1523 # ifdef CAIRO_HAS_WIN32_FONT
1524 LOGFONTA *lfa = pango_win32_font_logfont(font);
1525 LOGFONTW lfw;
1527 ZeroMemory(&lfw, sizeof(LOGFONTW));
1528 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1529 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1531 if(font_face == NULL) {
1532 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1533 g_hash_table_insert(font_table, fonthash, font_face);
1534 }
1535 # endif
1536 #else
1537 # ifdef CAIRO_HAS_FT_FONT
1538 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1539 fc_pattern = fc_font->font_pattern;
1540 if(font_face == NULL) {
1541 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1542 g_hash_table_insert(font_table, fonthash, font_face);
1543 }
1544 # endif
1545 #endif
1547 cairo_save(_cr);
1548 cairo_set_font_face(_cr, font_face);
1550 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1551 size = 12.0;
1553 // set the given font matrix
1554 cairo_matrix_t matrix;
1555 _initCairoMatrix(&matrix, font_matrix);
1556 cairo_set_font_matrix(_cr, &matrix);
1558 if (_render_mode == RENDER_MODE_CLIP) {
1559 if (_clip_mode == CLIP_MODE_MASK) {
1560 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1561 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1562 } else {
1563 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1564 }
1565 _showGlyphs(_cr, font, glyphtext, FALSE);
1566 } else {
1567 // just add the glyph paths to the current context
1568 _showGlyphs(_cr, font, glyphtext, TRUE);
1569 }
1570 } else {
1571 bool fill = false, stroke = false, have_path = false;
1572 if (style->fill.isColor() || style->fill.isPaintserver()) {
1573 fill = true;
1574 }
1576 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1577 stroke = true;
1578 }
1579 if (fill) {
1580 _setFillStyle(style, NULL);
1581 if (_is_texttopath) {
1582 _showGlyphs(_cr, font, glyphtext, true);
1583 have_path = true;
1584 if (stroke) cairo_fill_preserve(_cr);
1585 else cairo_fill(_cr);
1586 } else {
1587 _showGlyphs(_cr, font, glyphtext, false);
1588 }
1589 }
1590 if (stroke) {
1591 _setStrokeStyle(style, NULL);
1592 if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1593 cairo_stroke(_cr);
1594 }
1595 }
1597 cairo_restore(_cr);
1599 // if (font_face)
1600 // cairo_font_face_destroy(font_face);
1602 return true;
1603 }
1605 /* Helper functions */
1607 void
1608 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1609 {
1610 cairo_new_path(_cr);
1611 addPathVector(pv);
1612 }
1614 void
1615 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1616 {
1617 feed_pathvector_to_cairo(_cr, pv);
1618 }
1620 void
1621 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1622 {
1623 cairo_matrix_t matrix;
1625 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1626 cairo_transform(cr, &matrix);
1627 }
1629 void
1630 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1631 {
1632 matrix->xx = (*transform)[0];
1633 matrix->yx = (*transform)[1];
1634 matrix->xy = (*transform)[2];
1635 matrix->yy = (*transform)[3];
1636 matrix->x0 = (*transform)[4];
1637 matrix->y0 = (*transform)[5];
1638 }
1640 void
1641 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1642 {
1643 _concatTransform(cr, (*transform)[0], (*transform)[1],
1644 (*transform)[2], (*transform)[3],
1645 (*transform)[4], (*transform)[5]);
1646 }
1648 static cairo_status_t
1649 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1650 {
1651 size_t written;
1652 FILE *file = (FILE*)closure;
1654 written = fwrite (data, 1, length, file);
1656 if (written == length)
1657 return CAIRO_STATUS_SUCCESS;
1658 else
1659 return CAIRO_STATUS_WRITE_ERROR;
1660 }
1662 #include "clear-n_.h"
1664 } /* namespace Internal */
1665 } /* namespace Extension */
1666 } /* namespace Inkscape */
1668 #undef TRACE
1669 #undef TEST
1671 /* End of GNU GPL code */
1674 /*
1675 Local Variables:
1676 mode:c++
1677 c-file-style:"stroustrup"
1678 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1679 indent-tabs-mode:nil
1680 fill-column:99
1681 End:
1682 */
1683 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :