Code

Pot and Dutch translation update
[inkscape.git] / src / extension / internal / cairo-render-context.cpp
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)
130     font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
133 CairoRenderContext::~CairoRenderContext(void)
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);
143 void CairoRenderContext::font_data_free(gpointer data)
145     cairo_font_face_t *font_face = (cairo_font_face_t *)data;
146     if (font_face) {
147         cairo_font_face_destroy(font_face);
148     }
151 CairoRenderer*
152 CairoRenderContext::getRenderer(void) const
154     return _renderer;
157 CairoRenderState*
158 CairoRenderContext::getCurrentState(void) const
160     return _state;
163 CairoRenderState*
164 CairoRenderContext::getParentState(void) const
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     }
174 void
175 CairoRenderContext::setStateForStyle(SPStyle const *style)
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;
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
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;
216 CairoRenderContext*
217 CairoRenderContext::cloneMe(void) const
219     g_assert( _is_valid );
221     return cloneMe(_width, _height);
224 bool
225 CairoRenderContext::setImageTarget(cairo_format_t format)
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;
247 bool
248 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
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;
330 bool
331 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
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;
413 void CairoRenderContext::setPSLevel(unsigned int level)
415     _ps_level = level;
418 void CairoRenderContext::setEPS(bool eps)
420     _eps = eps;
423 unsigned int CairoRenderContext::getPSLevel(void)
425     return _ps_level;
428 void CairoRenderContext::setPDFLevel(unsigned int level)
430     _pdf_level = level;
433 void CairoRenderContext::setTextToPath(bool texttopath)
435     _is_texttopath = texttopath;
438 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
440     _is_filtertobitmap = filtertobitmap;
443 bool CairoRenderContext::getFilterToBitmap(void)
445     return _is_filtertobitmap;
448 void CairoRenderContext::setBitmapResolution(int resolution)
450     _bitmapresolution = resolution;
453 int CairoRenderContext::getBitmapResolution(void)
455     return _bitmapresolution;
458 cairo_surface_t*
459 CairoRenderContext::getSurface(void)
461     g_assert( _is_valid );
463     return _surface;
466 bool
467 CairoRenderContext::saveAsPng(const char *file_name)
469     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
470     if (status)
471         return false;
472     else
473         return true;
476 void
477 CairoRenderContext::setRenderMode(CairoRenderMode mode)
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     }
490 CairoRenderContext::CairoRenderMode
491 CairoRenderContext::getRenderMode(void) const
493     return _render_mode;
496 void
497 CairoRenderContext::setClipMode(CairoClipMode mode)
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     }
510 CairoRenderContext::CairoClipMode
511 CairoRenderContext::getClipMode(void) const
513     return _clip_mode;
516 CairoRenderState*
517 CairoRenderContext::_createState(void)
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;
534 void
535 CairoRenderContext::pushLayer(void)
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     }
551 void
552 CairoRenderContext::popLayer(void)
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             // Mask should start black, but it is created white.
674             cairo_set_source_rgba(mask_ctx->_cr, 0.0, 0.0, 0.0, 1.0);
675             cairo_rectangle(mask_ctx->_cr, 0, 0, surface_width, surface_height);
676             cairo_fill(mask_ctx->_cr);
678             // set rendering mode to normal
679             setRenderMode(RENDER_MODE_NORMAL);
681             // copy the correct CTM to mask context
682             /*
683             if (_state->parent_has_userspace)
684                 mask_ctx->setTransform(&getParentState()->transform);
685             else
686                 mask_ctx->setTransform(&_state->transform);
687             */
688             // This is probably not correct... but it seems to do the trick.
689             mask_ctx->setTransform(&_state->item_transform);
691             // render mask contents to mask_ctx
692             _renderer->applyMask(mask_ctx, mask);
694             TEST(mask_ctx->saveAsPng("mask.png"));
696             // composite with clip mask
697             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
698                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
699                 _renderer->destroyContext(clip_ctx);
700             }
702             cairo_surface_t *mask_image = mask_ctx->getSurface();
703             int width = cairo_image_surface_get_width(mask_image);
704             int height = cairo_image_surface_get_height(mask_image);
705             int stride = cairo_image_surface_get_stride(mask_image);
706             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
708             // In SVG, the rgb channels as well as the alpha channel is used in masking.
709             // In Cairo, only the alpha channel is used thus requiring this conversion.
710             // SVG specifies that RGB be converted to alpha using luminance-to-alpha.
711             // Notes: This calculation assumes linear RGB values. VERIFY COLOR SPACE!
712             // The incoming pixel values already include alpha, fill-opacity, etc.,
713             // however, opacity must still be applied.
714             TRACE(("premul w/ %f\n", opacity));
715             const float coeff_r = 0.2125 / 255.0;
716             const float coeff_g = 0.7154 / 255.0;
717             const float coeff_b = 0.0721 / 255.0;
718             for (int row = 0 ; row < height; row++) {
719                 unsigned char *row_data = pixels + (row * stride);
720                 for (int i = 0 ; i < width; i++) {
721                     guint32 *pixel = (guint32 *)row_data + i;
722                     float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
723                                        ((*pixel & 0x0000ff00) >>  8) * coeff_g +
724                                        ((*pixel & 0x000000ff)      ) * coeff_b );
725                     // lum_alpha can be slightly greater than 1 due to rounding errors...
726                     // but this should be OK since it doesn't matter what the lower
727                     // six hexadecimal numbers of *pixel are.
728                     *pixel = (guint32)(0xff000000 * lum_alpha * opacity);
729                 }
730             }
732             cairo_pop_group_to_source(_cr);
733             if (_clip_mode == CLIP_MODE_PATH) {
734                 // we have to do the clipping after cairo_pop_group_to_source
735                 _renderer->applyClipPath(this, clip_path);
736             }
737             // apply the mask onto the layer
738             cairo_mask_surface(_cr, mask_image, 0, 0);
739             _renderer->destroyContext(mask_ctx);
740         }
741     } else {
742         // No clip path or mask
743         cairo_pop_group_to_source(_cr);
744         if (opacity == 1.0)
745             cairo_paint(_cr);
746         else
747             cairo_paint_with_alpha(_cr, opacity);
748     }
751 void
752 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
754     g_assert( _is_valid );
756     // here it should be checked whether the current clip winding changed
757     // so we could switch back to masked clipping
758     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
759         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
760     } else {
761         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
762     }
763     addPathVector(pv);
766 void
767 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
769     g_assert( _is_valid );
771     cairo_rectangle(_cr, x, y, width, height);
772     cairo_clip(_cr);
775 bool
776 CairoRenderContext::setupSurface(double width, double height)
778     // Is the surface already set up?
779     if (_is_valid)
780         return true;
782     if (_vector_based_target && _stream == NULL)
783         return false;
785     _width = width;
786     _height = height;
788     cairo_surface_t *surface = NULL;
789     cairo_matrix_t ctm;
790     cairo_matrix_init_identity (&ctm);
791     switch (_target) {
792         case CAIRO_SURFACE_TYPE_IMAGE:
793             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
794             break;
795 #ifdef CAIRO_HAS_PDF_SURFACE
796         case CAIRO_SURFACE_TYPE_PDF:
797             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
798 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
799             cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);    
800 #endif
801             break;
802 #endif
803 #ifdef CAIRO_HAS_PS_SURFACE
804         case CAIRO_SURFACE_TYPE_PS:
805             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
806             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
807                 return FALSE;
808             }
809 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
810             cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
811             cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
812 #endif
813             // Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
814 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
815             if (override_bbox) {
816                 cairo_ps_dsc_comment(surface, "%%BoundingBox: 100 100 200 200");
817                 cairo_ps_dsc_comment(surface, "%%PageBoundingBox: 100 100 200 200");
818             }
819 #endif
820             break;
821 #endif
822         default:
823             return false;
824             break;
825     }
827     return _finishSurfaceSetup (surface, &ctm);
830 bool
831 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
833     if (_is_valid || !surface)
834         return false;
836     _vector_based_target = is_vector;
837     bool ret = _finishSurfaceSetup (surface, ctm);
838     if (ret)
839         cairo_surface_reference (surface);
840     return ret;
843 bool
844 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
846     if(surface == NULL) {
847         return false;
848     }
849     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
850         return false;
851     }
853     _cr = cairo_create(surface);
854     if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
855         return false;
856     }
857     if (ctm)
858         cairo_set_matrix(_cr, ctm);
859     _surface = surface;
861     if (_vector_based_target) {
862         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
863     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
864         // set background color on non-alpha surfaces
865         // TODO: bgcolor should be derived from SPDocument
866         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
867         cairo_rectangle(_cr, 0, 0, _width, _height);
868         cairo_fill(_cr);
869     }
871     _is_valid = TRUE;
873     return true;
876 bool
877 CairoRenderContext::finish(void)
879     g_assert( _is_valid );
881     if (_vector_based_target)
882         cairo_show_page(_cr);
884     cairo_destroy(_cr);
885     cairo_surface_finish(_surface);
886     cairo_status_t status = cairo_surface_status(_surface);
887     cairo_surface_destroy(_surface);
888     _cr = NULL;
889     _surface = NULL;
891     if (_layout)
892         g_object_unref(_layout);
894     _is_valid = FALSE;
896     if (_vector_based_target && _stream) {
897         /* Flush stream to be sure. */
898         (void) fflush(_stream);
900         fclose(_stream);
901         _stream = NULL;
902     }
904     if (status == CAIRO_STATUS_SUCCESS)
905         return true;
906     else
907         return false;
910 void
911 CairoRenderContext::transform(Geom::Matrix const *transform)
913     g_assert( _is_valid );
915     cairo_matrix_t matrix;
916     _initCairoMatrix(&matrix, transform);
917     cairo_transform(_cr, &matrix);
919     // store new CTM
920     getTransform(&_state->transform);
923 void
924 CairoRenderContext::setTransform(Geom::Matrix const *transform)
926     g_assert( _is_valid );
928     cairo_matrix_t matrix;
929     _initCairoMatrix(&matrix, transform);
930     cairo_set_matrix(_cr, &matrix);
931     _state->transform = *transform;
934 void
935 CairoRenderContext::getTransform(Geom::Matrix *copy) const
937     g_assert( _is_valid );
939     cairo_matrix_t ctm;
940     cairo_get_matrix(_cr, &ctm);
941     (*copy)[0] = ctm.xx;
942     (*copy)[1] = ctm.yx;
943     (*copy)[2] = ctm.xy;
944     (*copy)[3] = ctm.yy;
945     (*copy)[4] = ctm.x0;
946     (*copy)[5] = ctm.y0;
949 void
950 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
952     g_assert( _is_valid );
954     CairoRenderState *parent_state = getParentState();
955     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
958 void
959 CairoRenderContext::pushState(void)
961     g_assert( _is_valid );
963     cairo_save(_cr);
965     CairoRenderState *new_state = _createState();
966     // copy current state's transform
967     new_state->transform = _state->transform;
968     _state_stack = g_slist_prepend(_state_stack, new_state);
969     _state = new_state;
972 void
973 CairoRenderContext::popState(void)
975     g_assert( _is_valid );
977     cairo_restore(_cr);
979     g_free(_state_stack->data);
980     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
981     _state = (CairoRenderState*)_state_stack->data;
983     g_assert( g_slist_length(_state_stack) > 0 );
986 static bool pattern_hasItemChildren (SPPattern *pat)
988     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
989         if (SP_IS_ITEM (child)) {
990             return true;
991         }
992     }
993     return false;
996 cairo_pattern_t*
997 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
999     g_assert( SP_IS_PATTERN(paintserver) );
1001     SPPattern *pat = SP_PATTERN (paintserver);
1003     Geom::Matrix ps2user, pcs2dev;
1004     ps2user = Geom::identity();
1005     pcs2dev = Geom::identity();
1007     double x = pattern_x(pat);
1008     double y = pattern_y(pat);
1009     double width = pattern_width(pat);
1010     double height = pattern_height(pat);
1011     double bbox_width_scaler;
1012     double bbox_height_scaler;
1014     TRACE(("%f x %f pattern\n", width, height));
1016     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1017         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1018         bbox_width_scaler = pbox->x1 - pbox->x0;
1019         bbox_height_scaler = pbox->y1 - pbox->y0;
1020         ps2user[4] = x * bbox_width_scaler + pbox->x0;
1021         ps2user[5] = y * bbox_height_scaler + pbox->y0;
1022     } else {
1023         bbox_width_scaler = 1.0;
1024         bbox_height_scaler = 1.0;
1025         ps2user[4] = x;
1026         ps2user[5] = y;
1027     }
1029     // apply pattern transformation
1030     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1031     ps2user *= pattern_transform;
1032     Geom::Point ori (ps2user[4], ps2user[5]);
1034     // create pattern contents coordinate system
1035     if (pat->viewBox_set) {
1036         NRRect *view_box = pattern_viewBox(pat);
1038         double x, y, w, h;
1039         double view_width, view_height;
1040         x = 0;
1041         y = 0;
1042         w = width * bbox_width_scaler;
1043         h = height * bbox_height_scaler;
1045         view_width = view_box->x1 - view_box->x0;
1046         view_height = view_box->y1 - view_box->y0;
1048         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1049         pcs2dev[0] = w / view_width;
1050         pcs2dev[3] = h / view_height;
1051         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1052         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1053     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1054         pcs2dev[0] = pbox->x1 - pbox->x0;
1055         pcs2dev[3] = pbox->y1 - pbox->y0;
1057     }
1059     // Calculate the size of the surface which has to be created
1060 #define SUBPIX_SCALE 100
1061     // Cairo requires an integer pattern surface width/height.
1062     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1063     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1064     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1065     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1066     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1067     // create new rendering context
1068     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1070     // adjust the size of the painted pattern to fit exactly the created surface
1071     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1072     double scale_width = surface_width / (bbox_width_scaler * width);
1073     double scale_height = surface_height / (bbox_height_scaler * height);
1074     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1075         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1076         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1077         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1078     }
1080     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1081     ps2user[4] = ori[Geom::X];
1082     ps2user[5] = ori[Geom::Y];
1084     pattern_ctx->setTransform(&pcs2dev);
1085     pattern_ctx->pushState();
1087     // create arena and group
1088     NRArena *arena = NRArena::create();
1089     unsigned dkey = sp_item_display_key_new(1);
1091     // show items and render them
1092     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1093         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1094             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1095                 if (SP_IS_ITEM (child)) {
1096                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1097                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1098                 }
1099             }
1100             break; // do not go further up the chain if children are found
1101         }
1102     }
1104     pattern_ctx->popState();
1106     // setup a cairo_pattern_t
1107     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1108     TEST(pattern_ctx->saveAsPng("pattern.png"));
1109     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1110     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1112     // set pattern transformation
1113     cairo_matrix_t pattern_matrix;
1114     _initCairoMatrix(&pattern_matrix, &ps2user);
1115     cairo_matrix_invert(&pattern_matrix);
1116     cairo_pattern_set_matrix(result, &pattern_matrix);
1118     delete pattern_ctx;
1120     // hide all items
1121     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1122         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1123             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1124                 if (SP_IS_ITEM (child)) {
1125                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1126                 }
1127             }
1128             break; // do not go further up the chain if children are found
1129         }
1130     }
1132     return result;
1135 cairo_pattern_t*
1136 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1137                                                  NRRect const *pbox, float alpha)
1139     cairo_pattern_t *pattern = NULL;
1140     bool apply_bbox2user = FALSE;
1142     if (SP_IS_LINEARGRADIENT (paintserver)) {
1144             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1146             SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1148             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1149             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1150             if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1151                 // convert to userspace
1152                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1153                 p1 *= bbox2user;
1154                 p2 *= bbox2user;
1155             }
1157             // create linear gradient pattern
1158             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1160             // add stops
1161             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1162                 float rgb[3];
1163                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1164                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1165             }
1166     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1168         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1170         SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1172         Geom::Point c (rg->cx.computed, rg->cy.computed);
1173         Geom::Point f (rg->fx.computed, rg->fy.computed);
1174         double r = rg->r.computed;
1175         if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1176             apply_bbox2user = true;
1178         // create radial gradient pattern
1179         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1181         // add stops
1182         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1183             float rgb[3];
1184             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1185             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1186         }
1187     } else if (SP_IS_PATTERN (paintserver)) {
1189         pattern = _createPatternPainter(paintserver, pbox);
1190     } else {
1191         return NULL;
1192     }
1194     if (pattern && SP_IS_GRADIENT (paintserver)) {
1195         SPGradient *g = SP_GRADIENT(paintserver);
1197         // set extend type
1198         SPGradientSpread spread = g->fetchSpread();
1199         switch (spread) {
1200             case SP_GRADIENT_SPREAD_REPEAT: {
1201                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1202                 break;
1203             }
1204             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1205                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1206                 break;
1207             }
1208             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1209                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1210                 break;
1211             }
1212             default: {
1213                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1214                 break;
1215             }
1216         }
1218         cairo_matrix_t pattern_matrix;
1219         if (g->gradientTransform_set) {
1220             // apply gradient transformation
1221             cairo_matrix_init(&pattern_matrix,
1222                 g->gradientTransform[0], g->gradientTransform[1],
1223                 g->gradientTransform[2], g->gradientTransform[3],
1224                 g->gradientTransform[4], g->gradientTransform[5]);
1225         } else {
1226             cairo_matrix_init_identity (&pattern_matrix);
1227         }
1229         if (apply_bbox2user) {
1230             // convert to userspace
1231             cairo_matrix_t bbox2user;
1232             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1233             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1234         }
1235         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1236         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1237     }
1239     return pattern;
1242 void
1243 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1245     g_return_if_fail( !style->fill.set
1246                       || style->fill.isColor()
1247                       || style->fill.isPaintserver() );
1249     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1250     if (_state->merge_opacity) {
1251         alpha *= _state->opacity;
1252         TRACE(("merged op=%f\n", alpha));
1253     }
1255     if (style->fill.isColor()) {
1256         float rgb[3];
1257         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1259         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1261     } else if (!style->fill.set) { // unset fill is black
1262         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1264     } else {
1265         g_assert( style->fill.isPaintserver()
1266                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1267                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1269         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1271         if (pattern) {
1272             cairo_set_source(_cr, pattern);
1273             cairo_pattern_destroy(pattern);
1274         }
1275     }
1278 void
1279 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1281     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1282     if (_state->merge_opacity)
1283         alpha *= _state->opacity;
1285     if (style->stroke.isColor()) {
1286         float rgb[3];
1287         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1289         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1290     } else {
1291         g_assert( style->stroke.isPaintserver()
1292                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1293                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1295         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1297         if (pattern) {
1298             cairo_set_source(_cr, pattern);
1299             cairo_pattern_destroy(pattern);
1300         }
1301     }
1303     if (style->stroke_dash.n_dash   &&
1304         style->stroke_dash.dash       )
1305     {
1306         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1307     } else {
1308         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1309     }
1311     cairo_set_line_width(_cr, style->stroke_width.computed);
1313     // set line join type
1314     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1315     switch (style->stroke_linejoin.computed) {
1316         case SP_STROKE_LINEJOIN_MITER:
1317             join = CAIRO_LINE_JOIN_MITER;
1318             break;
1319         case SP_STROKE_LINEJOIN_ROUND:
1320             join = CAIRO_LINE_JOIN_ROUND;
1321             break;
1322         case SP_STROKE_LINEJOIN_BEVEL:
1323             join = CAIRO_LINE_JOIN_BEVEL;
1324             break;
1325     }
1326     cairo_set_line_join(_cr, join);
1328     // set line cap type
1329     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1330     switch (style->stroke_linecap.computed) {
1331         case SP_STROKE_LINECAP_BUTT:
1332             cap = CAIRO_LINE_CAP_BUTT;
1333             break;
1334         case SP_STROKE_LINECAP_ROUND:
1335             cap = CAIRO_LINE_CAP_ROUND;
1336             break;
1337         case SP_STROKE_LINECAP_SQUARE:
1338             cap = CAIRO_LINE_CAP_SQUARE;
1339             break;
1340     }
1341     cairo_set_line_cap(_cr, cap);
1342     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1345 bool
1346 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1348     g_assert( _is_valid );
1350     if (_render_mode == RENDER_MODE_CLIP) {
1351         if (_clip_mode == CLIP_MODE_PATH) {
1352             addClipPath(pathv, &style->fill_rule);
1353         } else {
1354             setPathVector(pathv);
1355             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1356                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1357             } else {
1358                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1359             }
1360             cairo_fill(_cr);
1361             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1362         }
1363         return true;
1364     }
1366     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1367     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1368                     style->stroke_opacity.value == 0;
1370     if (no_fill && no_stroke)
1371         return true;
1373     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1374                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1376     if (!need_layer)
1377         cairo_save(_cr);
1378     else
1379         pushLayer();
1381     if (!no_fill) {
1382         _setFillStyle(style, pbox);
1383         setPathVector(pathv);
1385         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1386             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1387         } else {
1388             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1389         }
1391         if (no_stroke)
1392             cairo_fill(_cr);
1393         else
1394             cairo_fill_preserve(_cr);
1395     }
1397     if (!no_stroke) {
1398         _setStrokeStyle(style, pbox);
1399         if (no_fill)
1400             setPathVector(pathv);
1402         cairo_stroke(_cr);
1403     }
1405     if (need_layer)
1406         popLayer();
1407     else
1408         cairo_restore(_cr);
1410     return true;
1413 bool
1414 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1415                                 Geom::Matrix const *image_transform, SPStyle const *style)
1417     g_assert( _is_valid );
1419     if (_render_mode == RENDER_MODE_CLIP)
1420         return true;
1422     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1423     if (!px_rgba)
1424         return false;
1426     float opacity;
1427     if (_state->merge_opacity)
1428         opacity = _state->opacity;
1429     else
1430         opacity = 1.0;
1432     // make a copy of the original pixbuf with premultiplied alpha
1433     // if we pass the original pixbuf it will get messed up
1434     for (unsigned i = 0; i < h; i++) {
1435         for (unsigned j = 0; j < w; j++) {
1436             guchar const *src = px + i * rs + j * 4;
1437             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1438             guchar r, g, b, alpha_dst;
1440             // calculate opacity-modified alpha
1441             alpha_dst = src[3];
1442             if (opacity != 1.0 && _vector_based_target)
1443                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1445             // premul alpha (needed because this will be undone by cairo-pdf)
1446             r = src[0]*alpha_dst/255;
1447             g = src[1]*alpha_dst/255;
1448             b = src[2]*alpha_dst/255;
1450             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1451         }
1452     }
1454     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1455     if (cairo_surface_status(image_surface)) {
1456         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1457         return false;
1458     }
1460     // setup automatic freeing of the image data when destroying the surface
1461     static cairo_user_data_key_t key;
1462     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1464     cairo_save(_cr);
1466     // scaling by width & height is not needed because it will be done by Cairo
1467     if (image_transform)
1468         transform(image_transform);
1470     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1472     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1473     if (_vector_based_target) {
1474         cairo_new_path(_cr);
1475         cairo_rectangle(_cr, 0, 0, w, h);
1476         cairo_clip(_cr);
1477     }
1479     if (_vector_based_target)
1480         cairo_paint(_cr);
1481     else
1482         cairo_paint_with_alpha(_cr, opacity);
1484     cairo_restore(_cr);
1486     cairo_surface_destroy(image_surface);
1488     return true;
1491 #define GLYPH_ARRAY_SIZE 64
1493 unsigned int
1494 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1496     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1497     cairo_glyph_t *glyphs = glyph_array;
1498     unsigned int num_glyphs = glyphtext.size();
1499     if (num_glyphs > GLYPH_ARRAY_SIZE)
1500         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1502     unsigned int num_invalid_glyphs = 0;
1503     unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1504     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1505         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1506         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1507         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1508             TRACE(("INVALID GLYPH found\n"));
1509             g_message("Invalid glyph found, continuing...");
1510             num_invalid_glyphs++;
1511             continue;
1512         }
1513         glyphs[i].index = it_info->index;
1514         glyphs[i].x     = it_info->x;
1515         glyphs[i].y     = it_info->y;
1516         i++;
1517     }
1519     if (path) {
1520         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1521     } else {
1522         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1523     }
1525     if (num_glyphs > GLYPH_ARRAY_SIZE)
1526         g_free(glyphs);
1528     return num_glyphs - num_invalid_glyphs;
1531 bool
1532 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1533                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1535     // create a cairo_font_face from PangoFont
1536     double size = style->font_size.computed;
1537     gpointer fonthash = (gpointer)font;
1538     cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1540     FcPattern *fc_pattern = NULL;
1542 #ifdef USE_PANGO_WIN32
1543 # ifdef CAIRO_HAS_WIN32_FONT
1544     LOGFONTA *lfa = pango_win32_font_logfont(font);
1545     LOGFONTW lfw;
1547     ZeroMemory(&lfw, sizeof(LOGFONTW));
1548     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1549     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1551     if(font_face == NULL) {
1552         font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1553         g_hash_table_insert(font_table, fonthash, font_face);
1554     }
1555 # endif
1556 #else
1557 # ifdef CAIRO_HAS_FT_FONT
1558     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1559     fc_pattern = fc_font->font_pattern;
1560     if(font_face == NULL) {
1561         font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1562         g_hash_table_insert(font_table, fonthash, font_face);
1563     }
1564 # endif
1565 #endif
1567     cairo_save(_cr);
1568     cairo_set_font_face(_cr, font_face);
1570     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1571         size = 12.0;
1573     // set the given font matrix
1574     cairo_matrix_t matrix;
1575     _initCairoMatrix(&matrix, font_matrix);
1576     cairo_set_font_matrix(_cr, &matrix);
1578     if (_render_mode == RENDER_MODE_CLIP) {
1579         if (_clip_mode == CLIP_MODE_MASK) {
1580             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1581                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1582             } else {
1583                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1584             }
1585             _showGlyphs(_cr, font, glyphtext, FALSE);
1586         } else {
1587             // just add the glyph paths to the current context
1588             _showGlyphs(_cr, font, glyphtext, TRUE);
1589         }
1590     } else {
1591         bool fill = false, stroke = false, have_path = false;
1592         if (style->fill.isColor() || style->fill.isPaintserver()) {
1593             fill = true;
1594         }
1596         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1597             stroke = true;
1598         }
1599         if (fill) {
1600             _setFillStyle(style, NULL);
1601             if (_is_texttopath) {
1602                 _showGlyphs(_cr, font, glyphtext, true);
1603                 have_path = true;
1604                 if (stroke) cairo_fill_preserve(_cr);
1605                 else cairo_fill(_cr);
1606             } else {
1607                 _showGlyphs(_cr, font, glyphtext, false);
1608             }
1609         }
1610         if (stroke) {
1611             _setStrokeStyle(style, NULL);
1612             if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1613             cairo_stroke(_cr);
1614         }
1615     }
1617     cairo_restore(_cr);
1619 //    if (font_face)
1620 //        cairo_font_face_destroy(font_face);
1622     return true;
1625 /* Helper functions */
1627 void
1628 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1630     cairo_new_path(_cr);
1631     addPathVector(pv);
1634 void
1635 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1637     feed_pathvector_to_cairo(_cr, pv);
1640 void
1641 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1643     cairo_matrix_t matrix;
1645     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1646     cairo_transform(cr, &matrix);
1649 void
1650 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1652     matrix->xx = (*transform)[0];
1653     matrix->yx = (*transform)[1];
1654     matrix->xy = (*transform)[2];
1655     matrix->yy = (*transform)[3];
1656     matrix->x0 = (*transform)[4];
1657     matrix->y0 = (*transform)[5];
1660 void
1661 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1663     _concatTransform(cr, (*transform)[0], (*transform)[1],
1664                      (*transform)[2], (*transform)[3],
1665                      (*transform)[4], (*transform)[5]);
1668 static cairo_status_t
1669 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1671     size_t written;
1672     FILE *file = (FILE*)closure;
1674     written = fwrite (data, 1, length, file);
1676     if (written == length)
1677         return CAIRO_STATUS_SUCCESS;
1678     else
1679         return CAIRO_STATUS_WRITE_ERROR;
1682 #include "clear-n_.h"
1684 }  /* namespace Internal */
1685 }  /* namespace Extension */
1686 }  /* namespace Inkscape */
1688 #undef TRACE
1689 #undef TEST
1691 /* End of GNU GPL code */
1694 /*
1695   Local Variables:
1696   mode:c++
1697   c-file-style:"stroustrup"
1698   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1699   indent-tabs-mode:nil
1700   fill-column:99
1701   End:
1702 */
1703 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :