1 #define __SP_CAIRO_RENDER_CONTEXT_C__
3 /** \file
4 * Rendering with Cairo.
5 */
6 /*
7 * Author:
8 * Miklos Erdelyi <erdelyim@gmail.com>
9 *
10 * Copyright (C) 2006 Miklos Erdelyi
11 *
12 * Licensed under GNU GPL
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #ifndef PANGO_ENABLE_BACKEND
20 #define PANGO_ENABLE_BACKEND
21 #endif
23 #ifndef PANGO_ENABLE_ENGINE
24 #define PANGO_ENABLE_ENGINE
25 #endif
28 #include <signal.h>
29 #include <errno.h>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 #define TRACE(_args)
84 //#define TEST(_args) _args
85 #define TEST(_args)
87 // FIXME: expose these from sp-clippath/mask.cpp
88 struct SPClipPathView {
89 SPClipPathView *next;
90 unsigned int key;
91 NRArenaItem *arenaitem;
92 NRRect bbox;
93 };
95 struct SPMaskView {
96 SPMaskView *next;
97 unsigned int key;
98 NRArenaItem *arenaitem;
99 NRRect bbox;
100 };
102 namespace Inkscape {
103 namespace Extension {
104 namespace Internal {
106 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
108 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
109 _dpi(72),
110 _pdf_level(0),
111 _ps_level(1),
112 _eps(false),
113 _is_texttopath(FALSE),
114 _is_filtertobitmap(FALSE),
115 _bitmapresolution(72),
116 _stream(NULL),
117 _is_valid(FALSE),
118 _vector_based_target(FALSE),
119 _cr(NULL), // Cairo context
120 _surface(NULL),
121 _target(CAIRO_SURFACE_TYPE_IMAGE),
122 _target_format(CAIRO_FORMAT_ARGB32),
123 _layout(NULL),
124 _state(NULL),
125 _renderer(parent),
126 _render_mode(RENDER_MODE_NORMAL),
127 _clip_mode(CLIP_MODE_MASK)
128 {
129 font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
130 }
132 CairoRenderContext::~CairoRenderContext(void)
133 {
134 if(font_table != NULL) {
135 g_hash_table_remove_all(font_table);
136 }
138 if (_cr) cairo_destroy(_cr);
139 if (_surface) cairo_surface_destroy(_surface);
140 if (_layout) g_object_unref(_layout);
141 }
142 void CairoRenderContext::font_data_free(gpointer data)
143 {
144 cairo_font_face_t *font_face = (cairo_font_face_t *)data;
145 if (font_face) {
146 cairo_font_face_destroy(font_face);
147 }
148 }
150 CairoRenderer*
151 CairoRenderContext::getRenderer(void) const
152 {
153 return _renderer;
154 }
156 CairoRenderState*
157 CairoRenderContext::getCurrentState(void) const
158 {
159 return _state;
160 }
162 CairoRenderState*
163 CairoRenderContext::getParentState(void) const
164 {
165 // if this is the root node just return it
166 if (g_slist_length(_state_stack) == 1) {
167 return _state;
168 } else {
169 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
170 }
171 }
173 void
174 CairoRenderContext::setStateForStyle(SPStyle const *style)
175 {
176 // only opacity & overflow is stored for now
177 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
178 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
179 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
181 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
182 _state->merge_opacity = FALSE;
184 // disable rendering of opacity if there's a stroke on the fill
185 if (_state->merge_opacity
186 && !style->fill.isNone()
187 && !style->stroke.isNone())
188 _state->merge_opacity = FALSE;
189 }
191 /**
192 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
193 *
194 * \param width width of the surface to be created
195 * \param height height of the surface to be created
196 */
197 CairoRenderContext*
198 CairoRenderContext::cloneMe(double width, double height) const
199 {
200 g_assert( _is_valid );
201 g_assert( width > 0.0 && height > 0.0 );
203 CairoRenderContext *new_context = _renderer->createContext();
204 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
205 (int)ceil(width), (int)ceil(height));
206 new_context->_cr = cairo_create(surface);
207 new_context->_surface = surface;
208 new_context->_width = width;
209 new_context->_height = height;
210 new_context->_is_valid = TRUE;
212 return new_context;
213 }
215 CairoRenderContext*
216 CairoRenderContext::cloneMe(void) const
217 {
218 g_assert( _is_valid );
220 return cloneMe(_width, _height);
221 }
223 bool
224 CairoRenderContext::setImageTarget(cairo_format_t format)
225 {
226 // format cannot be set on an already initialized surface
227 if (_is_valid)
228 return false;
230 switch (format) {
231 case CAIRO_FORMAT_ARGB32:
232 case CAIRO_FORMAT_RGB24:
233 case CAIRO_FORMAT_A8:
234 case CAIRO_FORMAT_A1:
235 _target_format = format;
236 _target = CAIRO_SURFACE_TYPE_IMAGE;
237 return true;
238 break;
239 default:
240 break;
241 }
243 return false;
244 }
246 bool
247 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
248 {
249 #ifndef CAIRO_HAS_PDF_SURFACE
250 return false;
251 #else
252 _target = CAIRO_SURFACE_TYPE_PDF;
253 _vector_based_target = TRUE;
254 #endif
256 FILE *osf = NULL;
257 FILE *osp = NULL;
259 gsize bytesRead = 0;
260 gsize bytesWritten = 0;
261 GError *error = NULL;
262 gchar *local_fn = g_filename_from_utf8(utf8_fn,
263 -1, &bytesRead, &bytesWritten, &error);
264 gchar const *fn = local_fn;
266 /* TODO: Replace the below fprintf's with something that does the right thing whether in
267 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
268 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
269 * return code.
270 */
271 if (fn != NULL) {
272 if (*fn == '|') {
273 fn += 1;
274 while (isspace(*fn)) fn += 1;
275 #ifndef WIN32
276 osp = popen(fn, "w");
277 #else
278 osp = _popen(fn, "w");
279 #endif
280 if (!osp) {
281 fprintf(stderr, "inkscape: popen(%s): %s\n",
282 fn, strerror(errno));
283 return false;
284 }
285 _stream = osp;
286 } else if (*fn == '>') {
287 fn += 1;
288 while (isspace(*fn)) fn += 1;
289 Inkscape::IO::dump_fopen_call(fn, "K");
290 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
291 if (!osf) {
292 fprintf(stderr, "inkscape: fopen(%s): %s\n",
293 fn, strerror(errno));
294 return false;
295 }
296 _stream = osf;
297 } else {
298 /* put cwd stuff in here */
299 gchar *qn = ( *fn
300 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
301 : g_strdup("lpr") );
302 #ifndef WIN32
303 osp = popen(qn, "w");
304 #else
305 osp = _popen(qn, "w");
306 #endif
307 if (!osp) {
308 fprintf(stderr, "inkscape: popen(%s): %s\n",
309 qn, strerror(errno));
310 return false;
311 }
312 g_free(qn);
313 _stream = osp;
314 }
315 }
317 g_free(local_fn);
319 if (_stream) {
320 /* fixme: this is kinda icky */
321 #if !defined(_WIN32) && !defined(__WIN32__)
322 (void) signal(SIGPIPE, SIG_IGN);
323 #endif
324 }
326 return true;
327 }
329 bool
330 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
331 {
332 #ifndef CAIRO_HAS_PS_SURFACE
333 return false;
334 #else
335 _target = CAIRO_SURFACE_TYPE_PS;
336 _vector_based_target = TRUE;
337 #endif
339 FILE *osf = NULL;
340 FILE *osp = NULL;
342 gsize bytesRead = 0;
343 gsize bytesWritten = 0;
344 GError *error = NULL;
345 gchar *local_fn = g_filename_from_utf8(utf8_fn,
346 -1, &bytesRead, &bytesWritten, &error);
347 gchar const *fn = local_fn;
349 /* TODO: Replace the below fprintf's with something that does the right thing whether in
350 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
351 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
352 * return code.
353 */
354 if (fn != NULL) {
355 if (*fn == '|') {
356 fn += 1;
357 while (isspace(*fn)) fn += 1;
358 #ifndef WIN32
359 osp = popen(fn, "w");
360 #else
361 osp = _popen(fn, "w");
362 #endif
363 if (!osp) {
364 fprintf(stderr, "inkscape: popen(%s): %s\n",
365 fn, strerror(errno));
366 return false;
367 }
368 _stream = osp;
369 } else if (*fn == '>') {
370 fn += 1;
371 while (isspace(*fn)) fn += 1;
372 Inkscape::IO::dump_fopen_call(fn, "K");
373 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
374 if (!osf) {
375 fprintf(stderr, "inkscape: fopen(%s): %s\n",
376 fn, strerror(errno));
377 return false;
378 }
379 _stream = osf;
380 } else {
381 /* put cwd stuff in here */
382 gchar *qn = ( *fn
383 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
384 : g_strdup("lpr") );
385 #ifndef WIN32
386 osp = popen(qn, "w");
387 #else
388 osp = _popen(qn, "w");
389 #endif
390 if (!osp) {
391 fprintf(stderr, "inkscape: popen(%s): %s\n",
392 qn, strerror(errno));
393 return false;
394 }
395 g_free(qn);
396 _stream = osp;
397 }
398 }
400 g_free(local_fn);
402 if (_stream) {
403 /* fixme: this is kinda icky */
404 #if !defined(_WIN32) && !defined(__WIN32__)
405 (void) signal(SIGPIPE, SIG_IGN);
406 #endif
407 }
409 return true;
410 }
412 void CairoRenderContext::setPSLevel(unsigned int level)
413 {
414 _ps_level = level;
415 }
417 void CairoRenderContext::setEPS(bool eps)
418 {
419 _eps = eps;
420 }
422 unsigned int CairoRenderContext::getPSLevel(void)
423 {
424 return _ps_level;
425 }
427 void CairoRenderContext::setPDFLevel(unsigned int level)
428 {
429 _pdf_level = level;
430 }
432 void CairoRenderContext::setTextToPath(bool texttopath)
433 {
434 _is_texttopath = texttopath;
435 }
437 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
438 {
439 _is_filtertobitmap = filtertobitmap;
440 }
442 bool CairoRenderContext::getFilterToBitmap(void)
443 {
444 return _is_filtertobitmap;
445 }
447 void CairoRenderContext::setBitmapResolution(int resolution)
448 {
449 _bitmapresolution = resolution;
450 }
452 int CairoRenderContext::getBitmapResolution(void)
453 {
454 return _bitmapresolution;
455 }
457 cairo_surface_t*
458 CairoRenderContext::getSurface(void)
459 {
460 g_assert( _is_valid );
462 return _surface;
463 }
465 bool
466 CairoRenderContext::saveAsPng(const char *file_name)
467 {
468 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
469 if (status)
470 return false;
471 else
472 return true;
473 }
475 void
476 CairoRenderContext::setRenderMode(CairoRenderMode mode)
477 {
478 switch (mode) {
479 case RENDER_MODE_NORMAL:
480 case RENDER_MODE_CLIP:
481 _render_mode = mode;
482 break;
483 default:
484 _render_mode = RENDER_MODE_NORMAL;
485 break;
486 }
487 }
489 CairoRenderContext::CairoRenderMode
490 CairoRenderContext::getRenderMode(void) const
491 {
492 return _render_mode;
493 }
495 void
496 CairoRenderContext::setClipMode(CairoClipMode mode)
497 {
498 switch (mode) {
499 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
500 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
501 _clip_mode = mode;
502 break;
503 default:
504 _clip_mode = CLIP_MODE_PATH;
505 break;
506 }
507 }
509 CairoRenderContext::CairoClipMode
510 CairoRenderContext::getClipMode(void) const
511 {
512 return _clip_mode;
513 }
515 CairoRenderState*
516 CairoRenderContext::_createState(void)
517 {
518 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
519 g_assert( state != NULL );
521 state->has_filtereffect = FALSE;
522 state->merge_opacity = TRUE;
523 state->opacity = 1.0;
524 state->need_layer = FALSE;
525 state->has_overflow = FALSE;
526 state->parent_has_userspace = FALSE;
527 state->clip_path = NULL;
528 state->mask = NULL;
530 return state;
531 }
533 void
534 CairoRenderContext::pushLayer(void)
535 {
536 g_assert( _is_valid );
538 TRACE(("--pushLayer\n"));
539 cairo_push_group(_cr);
541 // clear buffer
542 if (!_vector_based_target) {
543 cairo_save(_cr);
544 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
545 cairo_paint(_cr);
546 cairo_restore(_cr);
547 }
548 }
550 void
551 CairoRenderContext::popLayer(void)
552 {
553 g_assert( _is_valid );
555 float opacity = _state->opacity;
556 TRACE(("--popLayer w/ opacity %f\n", opacity));
558 /*
559 At this point, the Cairo source is ready. A Cairo mask must be created if required.
560 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
561 masks independently of the objects they effect while in SVG the clip paths and masks
562 are defined relative to the objects they are attached to.
563 Notes:
564 1. An SVG object may have both a clip path and a mask!
565 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
566 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
567 the page (document). This is also not handled properly.
568 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
569 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
570 alpha. This is handled here by doing a pixel by pixel conversion.
571 */
573 SPClipPath *clip_path = _state->clip_path;
574 SPMask *mask = _state->mask;
575 if (clip_path || mask) {
577 CairoRenderContext *clip_ctx = 0;
578 cairo_surface_t *clip_mask = 0;
580 // Apply any clip path first
581 if (clip_path) {
582 TRACE((" Applying clip\n"));
583 if (_render_mode == RENDER_MODE_CLIP)
584 mask = NULL; // disable mask when performing nested clipping
586 if (_vector_based_target) {
587 setClipMode(CLIP_MODE_PATH); // Vector
588 if (!mask) {
589 cairo_pop_group_to_source(_cr);
590 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
591 if (opacity == 1.0)
592 cairo_paint(_cr);
593 else
594 cairo_paint_with_alpha(_cr, opacity);
596 } else {
597 // the clipPath will be applied before masking
598 }
599 } else {
601 // setup a new rendering context
602 clip_ctx = _renderer->createContext();
603 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
604 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
605 // This code ties the clipping to the document coordinates. It doesn't allow
606 // for a clipped object intially drawn off the page and then translated onto
607 // the page.
608 if (!clip_ctx->setupSurface(_width, _height)) {
609 TRACE(("clip: setupSurface failed\n"));
610 _renderer->destroyContext(clip_ctx);
611 return;
612 }
614 // clear buffer
615 cairo_save(clip_ctx->_cr);
616 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
617 cairo_paint(clip_ctx->_cr);
618 cairo_restore(clip_ctx->_cr);
620 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
621 if (!mask)
622 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
623 else
624 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
626 // copy over the correct CTM
627 // It must be stored in item_transform of current state after pushState.
628 Geom::Matrix item_transform;
629 if (_state->parent_has_userspace)
630 item_transform = getParentState()->transform * _state->item_transform;
631 else
632 item_transform = _state->item_transform;
634 // apply the clip path
635 clip_ctx->pushState();
636 clip_ctx->getCurrentState()->item_transform = item_transform;
637 _renderer->applyClipPath(clip_ctx, clip_path);
638 clip_ctx->popState();
640 clip_mask = clip_ctx->getSurface();
641 TEST(clip_ctx->saveAsPng("clip_mask.png"));
643 if (!mask) {
644 cairo_pop_group_to_source(_cr);
645 cairo_mask_surface(_cr, clip_mask, 0, 0);
646 _renderer->destroyContext(clip_ctx);
647 }
648 }
649 }
651 // Apply any mask second
652 if (mask) {
653 TRACE((" Applying mask\n"));
654 // create rendering context for mask
655 CairoRenderContext *mask_ctx = _renderer->createContext();
657 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
658 // Cairo surface is expecting the mask to be 90 dpi.
659 float surface_width = _width;
660 float surface_height = _height;
661 if( _vector_based_target ) {
662 surface_width *= 1.25;
663 surface_height *= 1.25;
664 }
665 mask_ctx->setupSurface( surface_width, surface_height );
666 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
668 // set rendering mode to normal
669 setRenderMode(RENDER_MODE_NORMAL);
671 // copy the correct CTM to mask context
672 /*
673 if (_state->parent_has_userspace)
674 mask_ctx->setTransform(&getParentState()->transform);
675 else
676 mask_ctx->setTransform(&_state->transform);
677 */
678 // This is probably not correct... but it seems to do the trick.
679 mask_ctx->setTransform(&_state->item_transform);
681 // render mask contents to mask_ctx
682 _renderer->applyMask(mask_ctx, mask);
684 TEST(mask_ctx->saveAsPng("mask.png"));
686 // composite with clip mask
687 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
688 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
689 _renderer->destroyContext(clip_ctx);
690 }
692 cairo_surface_t *mask_image = mask_ctx->getSurface();
693 int width = cairo_image_surface_get_width(mask_image);
694 int height = cairo_image_surface_get_height(mask_image);
695 int stride = cairo_image_surface_get_stride(mask_image);
696 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
698 // premultiply with opacity
699 // In SVG, the rgb channels as well as the alpha channel is used in masking.
700 // In Cairo, only the alpha channel is used thus requiring this conversion.
701 TRACE(("premul w/ %f\n", opacity));
702 guint8 int_opacity = (guint8)(255 * opacity);
703 for (int row = 0 ; row < height; row++) {
704 unsigned char *row_data = pixels + (row * stride);
705 for (int i = 0 ; i < width; i++) {
706 guint32 *pixel = (guint32 *)row_data + i;
707 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
708 ((*pixel & 0x0000ff00) >> 8) * 46518 +
709 ((*pixel & 0x000000ff) ) * 4688) *
710 int_opacity);
711 }
712 }
714 cairo_pop_group_to_source(_cr);
715 if (_clip_mode == CLIP_MODE_PATH) {
716 // we have to do the clipping after cairo_pop_group_to_source
717 _renderer->applyClipPath(this, clip_path);
718 }
719 // apply the mask onto the layer
720 cairo_mask_surface(_cr, mask_image, 0, 0);
721 _renderer->destroyContext(mask_ctx);
722 }
723 } else {
724 // No clip path or mask
725 cairo_pop_group_to_source(_cr);
726 if (opacity == 1.0)
727 cairo_paint(_cr);
728 else
729 cairo_paint_with_alpha(_cr, opacity);
730 }
731 }
733 void
734 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
735 {
736 g_assert( _is_valid );
738 // here it should be checked whether the current clip winding changed
739 // so we could switch back to masked clipping
740 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
741 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
742 } else {
743 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
744 }
745 addPathVector(pv);
746 }
748 void
749 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
750 {
751 g_assert( _is_valid );
753 cairo_rectangle(_cr, x, y, width, height);
754 cairo_clip(_cr);
755 }
757 bool
758 CairoRenderContext::setupSurface(double width, double height)
759 {
760 // Is the surface already set up?
761 if (_is_valid)
762 return true;
764 if (_vector_based_target && _stream == NULL)
765 return false;
767 _width = width;
768 _height = height;
770 cairo_surface_t *surface = NULL;
771 cairo_matrix_t ctm;
772 cairo_matrix_init_identity (&ctm);
773 switch (_target) {
774 case CAIRO_SURFACE_TYPE_IMAGE:
775 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
776 break;
777 #ifdef CAIRO_HAS_PDF_SURFACE
778 case CAIRO_SURFACE_TYPE_PDF:
779 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
780 break;
781 #endif
782 #ifdef CAIRO_HAS_PS_SURFACE
783 case CAIRO_SURFACE_TYPE_PS:
784 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
785 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
786 return FALSE;
787 }
788 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
789 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
790 cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
791 #endif
792 break;
793 #endif
794 default:
795 return false;
796 break;
797 }
799 return _finishSurfaceSetup (surface, &ctm);
800 }
802 bool
803 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
804 {
805 if (_is_valid || !surface)
806 return false;
808 _vector_based_target = is_vector;
809 bool ret = _finishSurfaceSetup (surface, ctm);
810 if (ret)
811 cairo_surface_reference (surface);
812 return ret;
813 }
815 bool
816 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
817 {
818 if(surface == NULL) {
819 return false;
820 }
821 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
822 return false;
823 }
825 _cr = cairo_create(surface);
826 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
827 return false;
828 }
829 if (ctm)
830 cairo_set_matrix(_cr, ctm);
831 _surface = surface;
833 if (_vector_based_target) {
834 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
835 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
836 // set background color on non-alpha surfaces
837 // TODO: bgcolor should be derived from SPDocument
838 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
839 cairo_rectangle(_cr, 0, 0, _width, _height);
840 cairo_fill(_cr);
841 }
843 _is_valid = TRUE;
845 return true;
846 }
848 bool
849 CairoRenderContext::finish(void)
850 {
851 g_assert( _is_valid );
853 if (_vector_based_target)
854 cairo_show_page(_cr);
856 cairo_destroy(_cr);
857 cairo_surface_finish(_surface);
858 cairo_status_t status = cairo_surface_status(_surface);
859 cairo_surface_destroy(_surface);
860 _cr = NULL;
861 _surface = NULL;
863 if (_layout)
864 g_object_unref(_layout);
866 _is_valid = FALSE;
868 if (_vector_based_target && _stream) {
869 /* Flush stream to be sure. */
870 (void) fflush(_stream);
872 fclose(_stream);
873 _stream = NULL;
874 }
876 if (status == CAIRO_STATUS_SUCCESS)
877 return true;
878 else
879 return false;
880 }
882 void
883 CairoRenderContext::transform(Geom::Matrix const *transform)
884 {
885 g_assert( _is_valid );
887 cairo_matrix_t matrix;
888 _initCairoMatrix(&matrix, transform);
889 cairo_transform(_cr, &matrix);
891 // store new CTM
892 getTransform(&_state->transform);
893 }
895 void
896 CairoRenderContext::setTransform(Geom::Matrix const *transform)
897 {
898 g_assert( _is_valid );
900 cairo_matrix_t matrix;
901 _initCairoMatrix(&matrix, transform);
902 cairo_set_matrix(_cr, &matrix);
903 _state->transform = *transform;
904 }
906 void
907 CairoRenderContext::getTransform(Geom::Matrix *copy) const
908 {
909 g_assert( _is_valid );
911 cairo_matrix_t ctm;
912 cairo_get_matrix(_cr, &ctm);
913 (*copy)[0] = ctm.xx;
914 (*copy)[1] = ctm.yx;
915 (*copy)[2] = ctm.xy;
916 (*copy)[3] = ctm.yy;
917 (*copy)[4] = ctm.x0;
918 (*copy)[5] = ctm.y0;
919 }
921 void
922 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
923 {
924 g_assert( _is_valid );
926 CairoRenderState *parent_state = getParentState();
927 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
928 }
930 void
931 CairoRenderContext::pushState(void)
932 {
933 g_assert( _is_valid );
935 cairo_save(_cr);
937 CairoRenderState *new_state = _createState();
938 // copy current state's transform
939 new_state->transform = _state->transform;
940 _state_stack = g_slist_prepend(_state_stack, new_state);
941 _state = new_state;
942 }
944 void
945 CairoRenderContext::popState(void)
946 {
947 g_assert( _is_valid );
949 cairo_restore(_cr);
951 g_free(_state_stack->data);
952 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
953 _state = (CairoRenderState*)_state_stack->data;
955 g_assert( g_slist_length(_state_stack) > 0 );
956 }
958 static bool pattern_hasItemChildren (SPPattern *pat)
959 {
960 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
961 if (SP_IS_ITEM (child)) {
962 return true;
963 }
964 }
965 return false;
966 }
968 cairo_pattern_t*
969 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
970 {
971 g_assert( SP_IS_PATTERN(paintserver) );
973 SPPattern *pat = SP_PATTERN (paintserver);
975 Geom::Matrix ps2user, pcs2dev;
976 ps2user = Geom::identity();
977 pcs2dev = Geom::identity();
979 double x = pattern_x(pat);
980 double y = pattern_y(pat);
981 double width = pattern_width(pat);
982 double height = pattern_height(pat);
983 double bbox_width_scaler;
984 double bbox_height_scaler;
986 TRACE(("%f x %f pattern\n", width, height));
988 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
989 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
990 bbox_width_scaler = pbox->x1 - pbox->x0;
991 bbox_height_scaler = pbox->y1 - pbox->y0;
992 ps2user[4] = x * bbox_width_scaler + pbox->x0;
993 ps2user[5] = y * bbox_height_scaler + pbox->y0;
994 } else {
995 bbox_width_scaler = 1.0;
996 bbox_height_scaler = 1.0;
997 ps2user[4] = x;
998 ps2user[5] = y;
999 }
1001 // apply pattern transformation
1002 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1003 ps2user *= pattern_transform;
1004 Geom::Point ori (ps2user[4], ps2user[5]);
1006 // create pattern contents coordinate system
1007 if (pat->viewBox_set) {
1008 NRRect *view_box = pattern_viewBox(pat);
1010 double x, y, w, h;
1011 double view_width, view_height;
1012 x = 0;
1013 y = 0;
1014 w = width * bbox_width_scaler;
1015 h = height * bbox_height_scaler;
1017 view_width = view_box->x1 - view_box->x0;
1018 view_height = view_box->y1 - view_box->y0;
1020 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1021 pcs2dev[0] = w / view_width;
1022 pcs2dev[3] = h / view_height;
1023 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1024 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1025 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1026 pcs2dev[0] = pbox->x1 - pbox->x0;
1027 pcs2dev[3] = pbox->y1 - pbox->y0;
1029 }
1031 // Calculate the size of the surface which has to be created
1032 #define SUBPIX_SCALE 100
1033 // Cairo requires an integer pattern surface width/height.
1034 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1035 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1036 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1037 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1038 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1039 // create new rendering context
1040 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1042 // adjust the size of the painted pattern to fit exactly the created surface
1043 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1044 double scale_width = surface_width / (bbox_width_scaler * width);
1045 double scale_height = surface_height / (bbox_height_scaler * height);
1046 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1047 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1048 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1049 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1050 }
1052 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1053 ps2user[4] = ori[Geom::X];
1054 ps2user[5] = ori[Geom::Y];
1056 pattern_ctx->setTransform(&pcs2dev);
1057 pattern_ctx->pushState();
1059 // create arena and group
1060 NRArena *arena = NRArena::create();
1061 unsigned dkey = sp_item_display_key_new(1);
1063 // show items and render them
1064 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1065 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1066 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1067 if (SP_IS_ITEM (child)) {
1068 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1069 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1070 }
1071 }
1072 break; // do not go further up the chain if children are found
1073 }
1074 }
1076 pattern_ctx->popState();
1078 // setup a cairo_pattern_t
1079 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1080 TEST(pattern_ctx->saveAsPng("pattern.png"));
1081 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1082 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1084 // set pattern transformation
1085 cairo_matrix_t pattern_matrix;
1086 _initCairoMatrix(&pattern_matrix, &ps2user);
1087 cairo_matrix_invert(&pattern_matrix);
1088 cairo_pattern_set_matrix(result, &pattern_matrix);
1090 delete pattern_ctx;
1092 // hide all items
1093 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1094 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1095 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1096 if (SP_IS_ITEM (child)) {
1097 sp_item_invoke_hide (SP_ITEM (child), dkey);
1098 }
1099 }
1100 break; // do not go further up the chain if children are found
1101 }
1102 }
1104 return result;
1105 }
1107 cairo_pattern_t*
1108 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1109 NRRect const *pbox, float alpha)
1110 {
1111 cairo_pattern_t *pattern = NULL;
1112 bool apply_bbox2user = FALSE;
1114 if (SP_IS_LINEARGRADIENT (paintserver)) {
1116 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1118 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1120 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1121 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1122 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1123 // convert to userspace
1124 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1125 p1 *= bbox2user;
1126 p2 *= bbox2user;
1127 }
1129 // create linear gradient pattern
1130 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1132 // add stops
1133 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1134 float rgb[3];
1135 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1136 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1137 }
1138 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1140 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1142 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1144 Geom::Point c (rg->cx.computed, rg->cy.computed);
1145 Geom::Point f (rg->fx.computed, rg->fy.computed);
1146 double r = rg->r.computed;
1147 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1148 apply_bbox2user = true;
1150 // create radial gradient pattern
1151 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1153 // add stops
1154 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1155 float rgb[3];
1156 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1157 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1158 }
1159 } else if (SP_IS_PATTERN (paintserver)) {
1161 pattern = _createPatternPainter(paintserver, pbox);
1162 } else {
1163 return NULL;
1164 }
1166 if (pattern && SP_IS_GRADIENT (paintserver)) {
1167 SPGradient *g = SP_GRADIENT(paintserver);
1169 // set extend type
1170 SPGradientSpread spread = sp_gradient_get_spread(g);
1171 switch (spread) {
1172 case SP_GRADIENT_SPREAD_REPEAT: {
1173 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1174 break;
1175 }
1176 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1177 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1178 break;
1179 }
1180 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1181 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1182 break;
1183 }
1184 default: {
1185 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1186 break;
1187 }
1188 }
1190 cairo_matrix_t pattern_matrix;
1191 if (g->gradientTransform_set) {
1192 // apply gradient transformation
1193 cairo_matrix_init(&pattern_matrix,
1194 g->gradientTransform[0], g->gradientTransform[1],
1195 g->gradientTransform[2], g->gradientTransform[3],
1196 g->gradientTransform[4], g->gradientTransform[5]);
1197 } else {
1198 cairo_matrix_init_identity (&pattern_matrix);
1199 }
1201 if (apply_bbox2user) {
1202 // convert to userspace
1203 cairo_matrix_t bbox2user;
1204 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1205 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1206 }
1207 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1208 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1209 }
1211 return pattern;
1212 }
1214 void
1215 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1216 {
1217 g_return_if_fail( !style->fill.set
1218 || style->fill.isColor()
1219 || style->fill.isPaintserver() );
1221 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1222 if (_state->merge_opacity) {
1223 alpha *= _state->opacity;
1224 TRACE(("merged op=%f\n", alpha));
1225 }
1227 if (style->fill.isColor()) {
1228 float rgb[3];
1229 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1231 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1233 } else if (!style->fill.set) { // unset fill is black
1234 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1236 } else {
1237 g_assert( style->fill.isPaintserver()
1238 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1239 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1241 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1243 if (pattern) {
1244 cairo_set_source(_cr, pattern);
1245 cairo_pattern_destroy(pattern);
1246 }
1247 }
1248 }
1250 void
1251 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1252 {
1253 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1254 if (_state->merge_opacity)
1255 alpha *= _state->opacity;
1257 if (style->stroke.isColor()) {
1258 float rgb[3];
1259 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1261 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1262 } else {
1263 g_assert( style->fill.isPaintserver()
1264 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1265 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1267 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1269 if (pattern) {
1270 cairo_set_source(_cr, pattern);
1271 cairo_pattern_destroy(pattern);
1272 }
1273 }
1275 if (style->stroke_dash.n_dash &&
1276 style->stroke_dash.dash )
1277 {
1278 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1279 } else {
1280 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1281 }
1283 cairo_set_line_width(_cr, style->stroke_width.computed);
1285 // set line join type
1286 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1287 switch (style->stroke_linejoin.computed) {
1288 case SP_STROKE_LINEJOIN_MITER:
1289 join = CAIRO_LINE_JOIN_MITER;
1290 break;
1291 case SP_STROKE_LINEJOIN_ROUND:
1292 join = CAIRO_LINE_JOIN_ROUND;
1293 break;
1294 case SP_STROKE_LINEJOIN_BEVEL:
1295 join = CAIRO_LINE_JOIN_BEVEL;
1296 break;
1297 }
1298 cairo_set_line_join(_cr, join);
1300 // set line cap type
1301 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1302 switch (style->stroke_linecap.computed) {
1303 case SP_STROKE_LINECAP_BUTT:
1304 cap = CAIRO_LINE_CAP_BUTT;
1305 break;
1306 case SP_STROKE_LINECAP_ROUND:
1307 cap = CAIRO_LINE_CAP_ROUND;
1308 break;
1309 case SP_STROKE_LINECAP_SQUARE:
1310 cap = CAIRO_LINE_CAP_SQUARE;
1311 break;
1312 }
1313 cairo_set_line_cap(_cr, cap);
1314 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1315 }
1317 bool
1318 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1319 {
1320 g_assert( _is_valid );
1322 if (_render_mode == RENDER_MODE_CLIP) {
1323 if (_clip_mode == CLIP_MODE_PATH) {
1324 addClipPath(pathv, &style->fill_rule);
1325 } else {
1326 setPathVector(pathv);
1327 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1328 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1329 } else {
1330 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1331 }
1332 cairo_fill(_cr);
1333 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1334 }
1335 return true;
1336 }
1338 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1339 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1340 style->stroke_opacity.value == 0;
1342 if (no_fill && no_stroke)
1343 return true;
1345 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1346 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1348 if (!need_layer)
1349 cairo_save(_cr);
1350 else
1351 pushLayer();
1353 if (!no_fill) {
1354 _setFillStyle(style, pbox);
1355 setPathVector(pathv);
1357 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1358 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1359 } else {
1360 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1361 }
1363 if (no_stroke)
1364 cairo_fill(_cr);
1365 else
1366 cairo_fill_preserve(_cr);
1367 }
1369 if (!no_stroke) {
1370 _setStrokeStyle(style, pbox);
1371 if (no_fill)
1372 setPathVector(pathv);
1374 cairo_stroke(_cr);
1375 }
1377 if (need_layer)
1378 popLayer();
1379 else
1380 cairo_restore(_cr);
1382 return true;
1383 }
1385 bool
1386 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1387 Geom::Matrix const *image_transform, SPStyle const *style)
1388 {
1389 g_assert( _is_valid );
1391 if (_render_mode == RENDER_MODE_CLIP)
1392 return true;
1394 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1395 if (!px_rgba)
1396 return false;
1398 float opacity;
1399 if (_state->merge_opacity)
1400 opacity = _state->opacity;
1401 else
1402 opacity = 1.0;
1404 // make a copy of the original pixbuf with premultiplied alpha
1405 // if we pass the original pixbuf it will get messed up
1406 for (unsigned i = 0; i < h; i++) {
1407 for (unsigned j = 0; j < w; j++) {
1408 guchar const *src = px + i * rs + j * 4;
1409 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1410 guchar r, g, b, alpha_dst;
1412 // calculate opacity-modified alpha
1413 alpha_dst = src[3];
1414 if (opacity != 1.0 && _vector_based_target)
1415 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1417 // premul alpha (needed because this will be undone by cairo-pdf)
1418 r = src[0]*alpha_dst/255;
1419 g = src[1]*alpha_dst/255;
1420 b = src[2]*alpha_dst/255;
1422 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1423 }
1424 }
1426 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1427 if (cairo_surface_status(image_surface)) {
1428 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1429 return false;
1430 }
1432 // setup automatic freeing of the image data when destroying the surface
1433 static cairo_user_data_key_t key;
1434 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1436 cairo_save(_cr);
1438 // scaling by width & height is not needed because it will be done by Cairo
1439 if (image_transform)
1440 transform(image_transform);
1442 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1444 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1445 if (_vector_based_target) {
1446 cairo_new_path(_cr);
1447 cairo_rectangle(_cr, 0, 0, w, h);
1448 cairo_clip(_cr);
1449 }
1451 if (_vector_based_target)
1452 cairo_paint(_cr);
1453 else
1454 cairo_paint_with_alpha(_cr, opacity);
1456 cairo_restore(_cr);
1458 cairo_surface_destroy(image_surface);
1460 return true;
1461 }
1463 #define GLYPH_ARRAY_SIZE 64
1465 unsigned int
1466 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1467 {
1468 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1469 cairo_glyph_t *glyphs = glyph_array;
1470 unsigned int num_glyphs = glyphtext.size();
1471 if (num_glyphs > GLYPH_ARRAY_SIZE)
1472 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1474 unsigned int num_invalid_glyphs = 0;
1475 unsigned int i = 0;
1476 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1477 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1478 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1479 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1480 TRACE(("INVALID GLYPH found\n"));
1481 num_invalid_glyphs++;
1482 continue;
1483 }
1484 glyphs[i - num_invalid_glyphs].index = it_info->index;
1485 glyphs[i - num_invalid_glyphs].x = it_info->x;
1486 glyphs[i - num_invalid_glyphs].y = it_info->y;
1487 i++;
1488 }
1490 if (path) {
1491 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1492 } else {
1493 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1494 }
1496 if (num_glyphs > GLYPH_ARRAY_SIZE)
1497 g_free(glyphs);
1499 return num_glyphs - num_invalid_glyphs;
1500 }
1502 bool
1503 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1504 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1505 {
1506 // create a cairo_font_face from PangoFont
1507 double size = style->font_size.computed;
1508 gpointer fonthash = (gpointer)font;
1509 cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1511 FcPattern *fc_pattern = NULL;
1513 #ifdef USE_PANGO_WIN32
1514 # ifdef CAIRO_HAS_WIN32_FONT
1515 LOGFONTA *lfa = pango_win32_font_logfont(font);
1516 LOGFONTW lfw;
1518 ZeroMemory(&lfw, sizeof(LOGFONTW));
1519 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1520 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1522 if(font_face == NULL) {
1523 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1524 g_hash_table_insert(font_table, fonthash, font_face);
1525 }
1526 # endif
1527 #else
1528 # ifdef CAIRO_HAS_FT_FONT
1529 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1530 fc_pattern = fc_font->font_pattern;
1531 if(font_face == NULL) {
1532 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1533 g_hash_table_insert(font_table, fonthash, font_face);
1534 }
1535 # endif
1536 #endif
1538 cairo_save(_cr);
1539 cairo_set_font_face(_cr, font_face);
1541 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1542 size = 12.0;
1544 // set the given font matrix
1545 cairo_matrix_t matrix;
1546 _initCairoMatrix(&matrix, font_matrix);
1547 cairo_set_font_matrix(_cr, &matrix);
1549 if (_render_mode == RENDER_MODE_CLIP) {
1550 if (_clip_mode == CLIP_MODE_MASK) {
1551 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1552 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1553 } else {
1554 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1555 }
1556 _showGlyphs(_cr, font, glyphtext, FALSE);
1557 } else {
1558 // just add the glyph paths to the current context
1559 _showGlyphs(_cr, font, glyphtext, TRUE);
1560 }
1561 } else {
1562 bool fill = false, stroke = false, have_path = false;
1563 if (style->fill.isColor() || style->fill.isPaintserver()) {
1564 fill = true;
1565 }
1567 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1568 stroke = true;
1569 }
1570 if (fill) {
1571 _setFillStyle(style, NULL);
1572 if (_is_texttopath) {
1573 _showGlyphs(_cr, font, glyphtext, true);
1574 have_path = true;
1575 if (stroke) cairo_fill_preserve(_cr);
1576 else cairo_fill(_cr);
1577 } else {
1578 _showGlyphs(_cr, font, glyphtext, false);
1579 }
1580 }
1581 if (stroke) {
1582 _setStrokeStyle(style, NULL);
1583 if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1584 cairo_stroke(_cr);
1585 }
1586 }
1588 cairo_restore(_cr);
1590 // if (font_face)
1591 // cairo_font_face_destroy(font_face);
1593 return true;
1594 }
1596 /* Helper functions */
1598 void
1599 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1600 {
1601 cairo_new_path(_cr);
1602 addPathVector(pv);
1603 }
1605 void
1606 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1607 {
1608 feed_pathvector_to_cairo(_cr, pv);
1609 }
1611 void
1612 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1613 {
1614 cairo_matrix_t matrix;
1616 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1617 cairo_transform(cr, &matrix);
1618 }
1620 void
1621 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1622 {
1623 matrix->xx = (*transform)[0];
1624 matrix->yx = (*transform)[1];
1625 matrix->xy = (*transform)[2];
1626 matrix->yy = (*transform)[3];
1627 matrix->x0 = (*transform)[4];
1628 matrix->y0 = (*transform)[5];
1629 }
1631 void
1632 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1633 {
1634 _concatTransform(cr, (*transform)[0], (*transform)[1],
1635 (*transform)[2], (*transform)[3],
1636 (*transform)[4], (*transform)[5]);
1637 }
1639 static cairo_status_t
1640 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1641 {
1642 size_t written;
1643 FILE *file = (FILE*)closure;
1645 written = fwrite (data, 1, length, file);
1647 if (written == length)
1648 return CAIRO_STATUS_SUCCESS;
1649 else
1650 return CAIRO_STATUS_WRITE_ERROR;
1651 }
1653 #include "clear-n_.h"
1655 } /* namespace Internal */
1656 } /* namespace Extension */
1657 } /* namespace Inkscape */
1659 #undef TRACE
1660 #undef TEST
1662 /* End of GNU GPL code */
1665 /*
1666 Local Variables:
1667 mode:c++
1668 c-file-style:"stroustrup"
1669 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1670 indent-tabs-mode:nil
1671 fill-column:99
1672 End:
1673 */
1674 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :