Code

Add code to fix bug #380501 in the future...
[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             // set rendering mode to normal
674             setRenderMode(RENDER_MODE_NORMAL);
676             // copy the correct CTM to mask context
677             /*
678             if (_state->parent_has_userspace)
679                 mask_ctx->setTransform(&getParentState()->transform);
680             else
681                 mask_ctx->setTransform(&_state->transform);
682             */
683             // This is probably not correct... but it seems to do the trick.
684             mask_ctx->setTransform(&_state->item_transform);
686             // render mask contents to mask_ctx
687             _renderer->applyMask(mask_ctx, mask);
689             TEST(mask_ctx->saveAsPng("mask.png"));
691             // composite with clip mask
692             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
693                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
694                 _renderer->destroyContext(clip_ctx);
695             }
697             cairo_surface_t *mask_image = mask_ctx->getSurface();
698             int width = cairo_image_surface_get_width(mask_image);
699             int height = cairo_image_surface_get_height(mask_image);
700             int stride = cairo_image_surface_get_stride(mask_image);
701             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
703             // premultiply with opacity
704             // In SVG, the rgb channels as well as the alpha channel is used in masking.
705             // In Cairo, only the alpha channel is used thus requiring this conversion.
706             TRACE(("premul w/ %f\n", opacity));
707             guint8 int_opacity = (guint8)(255 * opacity);
708             for (int row = 0 ; row < height; row++) {
709                 unsigned char *row_data = pixels + (row * stride);
710                 for (int i = 0 ; i < width; i++) {
711                     guint32 *pixel = (guint32 *)row_data + i;
712                     *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
713                                ((*pixel & 0x0000ff00) >>  8) * 46518 +
714                                ((*pixel & 0x000000ff)      ) * 4688) *
715                               int_opacity);
716                 }
717             }
719             cairo_pop_group_to_source(_cr);
720             if (_clip_mode == CLIP_MODE_PATH) {
721                 // we have to do the clipping after cairo_pop_group_to_source
722                 _renderer->applyClipPath(this, clip_path);
723             }
724             // apply the mask onto the layer
725             cairo_mask_surface(_cr, mask_image, 0, 0);
726             _renderer->destroyContext(mask_ctx);
727         }
728     } else {
729         // No clip path or mask
730         cairo_pop_group_to_source(_cr);
731         if (opacity == 1.0)
732             cairo_paint(_cr);
733         else
734             cairo_paint_with_alpha(_cr, opacity);
735     }
738 void
739 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
741     g_assert( _is_valid );
743     // here it should be checked whether the current clip winding changed
744     // so we could switch back to masked clipping
745     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
746         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
747     } else {
748         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
749     }
750     addPathVector(pv);
753 void
754 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
756     g_assert( _is_valid );
758     cairo_rectangle(_cr, x, y, width, height);
759     cairo_clip(_cr);
762 bool
763 CairoRenderContext::setupSurface(double width, double height)
765     // Is the surface already set up?
766     if (_is_valid)
767         return true;
769     if (_vector_based_target && _stream == NULL)
770         return false;
772     _width = width;
773     _height = height;
775     cairo_surface_t *surface = NULL;
776     cairo_matrix_t ctm;
777     cairo_matrix_init_identity (&ctm);
778     switch (_target) {
779         case CAIRO_SURFACE_TYPE_IMAGE:
780             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
781             break;
782 #ifdef CAIRO_HAS_PDF_SURFACE
783         case CAIRO_SURFACE_TYPE_PDF:
784             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
785 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
786             cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);    
787 #endif
788             break;
789 #endif
790 #ifdef CAIRO_HAS_PS_SURFACE
791         case CAIRO_SURFACE_TYPE_PS:
792             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
793             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
794                 return FALSE;
795             }
796 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
797             cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
798             cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
799 #endif
800             // Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
801 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
802             if (override_bbox) {
803                 cairo_ps_dsc_comment(surface, "%%BoundingBox: 100 100 200 200");
804                 cairo_ps_dsc_comment(surface, "%%PageBoundingBox: 100 100 200 200");
805             }
806 #endif
807             break;
808 #endif
809         default:
810             return false;
811             break;
812     }
814     return _finishSurfaceSetup (surface, &ctm);
817 bool
818 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
820     if (_is_valid || !surface)
821         return false;
823     _vector_based_target = is_vector;
824     bool ret = _finishSurfaceSetup (surface, ctm);
825     if (ret)
826         cairo_surface_reference (surface);
827     return ret;
830 bool
831 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
833     if(surface == NULL) {
834         return false;
835     }
836     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
837         return false;
838     }
840     _cr = cairo_create(surface);
841     if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
842         return false;
843     }
844     if (ctm)
845         cairo_set_matrix(_cr, ctm);
846     _surface = surface;
848     if (_vector_based_target) {
849         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
850     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
851         // set background color on non-alpha surfaces
852         // TODO: bgcolor should be derived from SPDocument
853         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
854         cairo_rectangle(_cr, 0, 0, _width, _height);
855         cairo_fill(_cr);
856     }
858     _is_valid = TRUE;
860     return true;
863 bool
864 CairoRenderContext::finish(void)
866     g_assert( _is_valid );
868     if (_vector_based_target)
869         cairo_show_page(_cr);
871     cairo_destroy(_cr);
872     cairo_surface_finish(_surface);
873     cairo_status_t status = cairo_surface_status(_surface);
874     cairo_surface_destroy(_surface);
875     _cr = NULL;
876     _surface = NULL;
878     if (_layout)
879         g_object_unref(_layout);
881     _is_valid = FALSE;
883     if (_vector_based_target && _stream) {
884         /* Flush stream to be sure. */
885         (void) fflush(_stream);
887         fclose(_stream);
888         _stream = NULL;
889     }
891     if (status == CAIRO_STATUS_SUCCESS)
892         return true;
893     else
894         return false;
897 void
898 CairoRenderContext::transform(Geom::Matrix const *transform)
900     g_assert( _is_valid );
902     cairo_matrix_t matrix;
903     _initCairoMatrix(&matrix, transform);
904     cairo_transform(_cr, &matrix);
906     // store new CTM
907     getTransform(&_state->transform);
910 void
911 CairoRenderContext::setTransform(Geom::Matrix const *transform)
913     g_assert( _is_valid );
915     cairo_matrix_t matrix;
916     _initCairoMatrix(&matrix, transform);
917     cairo_set_matrix(_cr, &matrix);
918     _state->transform = *transform;
921 void
922 CairoRenderContext::getTransform(Geom::Matrix *copy) const
924     g_assert( _is_valid );
926     cairo_matrix_t ctm;
927     cairo_get_matrix(_cr, &ctm);
928     (*copy)[0] = ctm.xx;
929     (*copy)[1] = ctm.yx;
930     (*copy)[2] = ctm.xy;
931     (*copy)[3] = ctm.yy;
932     (*copy)[4] = ctm.x0;
933     (*copy)[5] = ctm.y0;
936 void
937 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
939     g_assert( _is_valid );
941     CairoRenderState *parent_state = getParentState();
942     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
945 void
946 CairoRenderContext::pushState(void)
948     g_assert( _is_valid );
950     cairo_save(_cr);
952     CairoRenderState *new_state = _createState();
953     // copy current state's transform
954     new_state->transform = _state->transform;
955     _state_stack = g_slist_prepend(_state_stack, new_state);
956     _state = new_state;
959 void
960 CairoRenderContext::popState(void)
962     g_assert( _is_valid );
964     cairo_restore(_cr);
966     g_free(_state_stack->data);
967     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
968     _state = (CairoRenderState*)_state_stack->data;
970     g_assert( g_slist_length(_state_stack) > 0 );
973 static bool pattern_hasItemChildren (SPPattern *pat)
975     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
976         if (SP_IS_ITEM (child)) {
977             return true;
978         }
979     }
980     return false;
983 cairo_pattern_t*
984 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
986     g_assert( SP_IS_PATTERN(paintserver) );
988     SPPattern *pat = SP_PATTERN (paintserver);
990     Geom::Matrix ps2user, pcs2dev;
991     ps2user = Geom::identity();
992     pcs2dev = Geom::identity();
994     double x = pattern_x(pat);
995     double y = pattern_y(pat);
996     double width = pattern_width(pat);
997     double height = pattern_height(pat);
998     double bbox_width_scaler;
999     double bbox_height_scaler;
1001     TRACE(("%f x %f pattern\n", width, height));
1003     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1004         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1005         bbox_width_scaler = pbox->x1 - pbox->x0;
1006         bbox_height_scaler = pbox->y1 - pbox->y0;
1007         ps2user[4] = x * bbox_width_scaler + pbox->x0;
1008         ps2user[5] = y * bbox_height_scaler + pbox->y0;
1009     } else {
1010         bbox_width_scaler = 1.0;
1011         bbox_height_scaler = 1.0;
1012         ps2user[4] = x;
1013         ps2user[5] = y;
1014     }
1016     // apply pattern transformation
1017     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1018     ps2user *= pattern_transform;
1019     Geom::Point ori (ps2user[4], ps2user[5]);
1021     // create pattern contents coordinate system
1022     if (pat->viewBox_set) {
1023         NRRect *view_box = pattern_viewBox(pat);
1025         double x, y, w, h;
1026         double view_width, view_height;
1027         x = 0;
1028         y = 0;
1029         w = width * bbox_width_scaler;
1030         h = height * bbox_height_scaler;
1032         view_width = view_box->x1 - view_box->x0;
1033         view_height = view_box->y1 - view_box->y0;
1035         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1036         pcs2dev[0] = w / view_width;
1037         pcs2dev[3] = h / view_height;
1038         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1039         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1040     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1041         pcs2dev[0] = pbox->x1 - pbox->x0;
1042         pcs2dev[3] = pbox->y1 - pbox->y0;
1044     }
1046     // Calculate the size of the surface which has to be created
1047 #define SUBPIX_SCALE 100
1048     // Cairo requires an integer pattern surface width/height.
1049     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1050     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1051     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1052     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1053     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1054     // create new rendering context
1055     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1057     // adjust the size of the painted pattern to fit exactly the created surface
1058     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1059     double scale_width = surface_width / (bbox_width_scaler * width);
1060     double scale_height = surface_height / (bbox_height_scaler * height);
1061     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1062         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1063         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1064         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1065     }
1067     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1068     ps2user[4] = ori[Geom::X];
1069     ps2user[5] = ori[Geom::Y];
1071     pattern_ctx->setTransform(&pcs2dev);
1072     pattern_ctx->pushState();
1074     // create arena and group
1075     NRArena *arena = NRArena::create();
1076     unsigned dkey = sp_item_display_key_new(1);
1078     // show items and render them
1079     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1080         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1081             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1082                 if (SP_IS_ITEM (child)) {
1083                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1084                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1085                 }
1086             }
1087             break; // do not go further up the chain if children are found
1088         }
1089     }
1091     pattern_ctx->popState();
1093     // setup a cairo_pattern_t
1094     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1095     TEST(pattern_ctx->saveAsPng("pattern.png"));
1096     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1097     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1099     // set pattern transformation
1100     cairo_matrix_t pattern_matrix;
1101     _initCairoMatrix(&pattern_matrix, &ps2user);
1102     cairo_matrix_invert(&pattern_matrix);
1103     cairo_pattern_set_matrix(result, &pattern_matrix);
1105     delete pattern_ctx;
1107     // hide all items
1108     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1109         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1110             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1111                 if (SP_IS_ITEM (child)) {
1112                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1113                 }
1114             }
1115             break; // do not go further up the chain if children are found
1116         }
1117     }
1119     return result;
1122 cairo_pattern_t*
1123 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1124                                                  NRRect const *pbox, float alpha)
1126     cairo_pattern_t *pattern = NULL;
1127     bool apply_bbox2user = FALSE;
1129     if (SP_IS_LINEARGRADIENT (paintserver)) {
1131             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1133             SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1135             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1136             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1137             if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1138                 // convert to userspace
1139                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1140                 p1 *= bbox2user;
1141                 p2 *= bbox2user;
1142             }
1144             // create linear gradient pattern
1145             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1147             // add stops
1148             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1149                 float rgb[3];
1150                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1151                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1152             }
1153     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1155         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1157         SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1159         Geom::Point c (rg->cx.computed, rg->cy.computed);
1160         Geom::Point f (rg->fx.computed, rg->fy.computed);
1161         double r = rg->r.computed;
1162         if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1163             apply_bbox2user = true;
1165         // create radial gradient pattern
1166         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1168         // add stops
1169         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1170             float rgb[3];
1171             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1172             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1173         }
1174     } else if (SP_IS_PATTERN (paintserver)) {
1176         pattern = _createPatternPainter(paintserver, pbox);
1177     } else {
1178         return NULL;
1179     }
1181     if (pattern && SP_IS_GRADIENT (paintserver)) {
1182         SPGradient *g = SP_GRADIENT(paintserver);
1184         // set extend type
1185         SPGradientSpread spread = g->fetchSpread();
1186         switch (spread) {
1187             case SP_GRADIENT_SPREAD_REPEAT: {
1188                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1189                 break;
1190             }
1191             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1192                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1193                 break;
1194             }
1195             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1196                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1197                 break;
1198             }
1199             default: {
1200                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1201                 break;
1202             }
1203         }
1205         cairo_matrix_t pattern_matrix;
1206         if (g->gradientTransform_set) {
1207             // apply gradient transformation
1208             cairo_matrix_init(&pattern_matrix,
1209                 g->gradientTransform[0], g->gradientTransform[1],
1210                 g->gradientTransform[2], g->gradientTransform[3],
1211                 g->gradientTransform[4], g->gradientTransform[5]);
1212         } else {
1213             cairo_matrix_init_identity (&pattern_matrix);
1214         }
1216         if (apply_bbox2user) {
1217             // convert to userspace
1218             cairo_matrix_t bbox2user;
1219             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1220             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1221         }
1222         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1223         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1224     }
1226     return pattern;
1229 void
1230 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1232     g_return_if_fail( !style->fill.set
1233                       || style->fill.isColor()
1234                       || style->fill.isPaintserver() );
1236     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1237     if (_state->merge_opacity) {
1238         alpha *= _state->opacity;
1239         TRACE(("merged op=%f\n", alpha));
1240     }
1242     if (style->fill.isColor()) {
1243         float rgb[3];
1244         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1246         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1248     } else if (!style->fill.set) { // unset fill is black
1249         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1251     } else {
1252         g_assert( style->fill.isPaintserver()
1253                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1254                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1256         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1258         if (pattern) {
1259             cairo_set_source(_cr, pattern);
1260             cairo_pattern_destroy(pattern);
1261         }
1262     }
1265 void
1266 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1268     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1269     if (_state->merge_opacity)
1270         alpha *= _state->opacity;
1272     if (style->stroke.isColor()) {
1273         float rgb[3];
1274         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1276         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1277     } else {
1278         g_assert( style->stroke.isPaintserver()
1279                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1280                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1282         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1284         if (pattern) {
1285             cairo_set_source(_cr, pattern);
1286             cairo_pattern_destroy(pattern);
1287         }
1288     }
1290     if (style->stroke_dash.n_dash   &&
1291         style->stroke_dash.dash       )
1292     {
1293         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1294     } else {
1295         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1296     }
1298     cairo_set_line_width(_cr, style->stroke_width.computed);
1300     // set line join type
1301     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1302     switch (style->stroke_linejoin.computed) {
1303         case SP_STROKE_LINEJOIN_MITER:
1304             join = CAIRO_LINE_JOIN_MITER;
1305             break;
1306         case SP_STROKE_LINEJOIN_ROUND:
1307             join = CAIRO_LINE_JOIN_ROUND;
1308             break;
1309         case SP_STROKE_LINEJOIN_BEVEL:
1310             join = CAIRO_LINE_JOIN_BEVEL;
1311             break;
1312     }
1313     cairo_set_line_join(_cr, join);
1315     // set line cap type
1316     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1317     switch (style->stroke_linecap.computed) {
1318         case SP_STROKE_LINECAP_BUTT:
1319             cap = CAIRO_LINE_CAP_BUTT;
1320             break;
1321         case SP_STROKE_LINECAP_ROUND:
1322             cap = CAIRO_LINE_CAP_ROUND;
1323             break;
1324         case SP_STROKE_LINECAP_SQUARE:
1325             cap = CAIRO_LINE_CAP_SQUARE;
1326             break;
1327     }
1328     cairo_set_line_cap(_cr, cap);
1329     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1332 bool
1333 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1335     g_assert( _is_valid );
1337     if (_render_mode == RENDER_MODE_CLIP) {
1338         if (_clip_mode == CLIP_MODE_PATH) {
1339             addClipPath(pathv, &style->fill_rule);
1340         } else {
1341             setPathVector(pathv);
1342             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1343                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1344             } else {
1345                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1346             }
1347             cairo_fill(_cr);
1348             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1349         }
1350         return true;
1351     }
1353     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1354     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1355                     style->stroke_opacity.value == 0;
1357     if (no_fill && no_stroke)
1358         return true;
1360     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1361                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1363     if (!need_layer)
1364         cairo_save(_cr);
1365     else
1366         pushLayer();
1368     if (!no_fill) {
1369         _setFillStyle(style, pbox);
1370         setPathVector(pathv);
1372         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1373             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1374         } else {
1375             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1376         }
1378         if (no_stroke)
1379             cairo_fill(_cr);
1380         else
1381             cairo_fill_preserve(_cr);
1382     }
1384     if (!no_stroke) {
1385         _setStrokeStyle(style, pbox);
1386         if (no_fill)
1387             setPathVector(pathv);
1389         cairo_stroke(_cr);
1390     }
1392     if (need_layer)
1393         popLayer();
1394     else
1395         cairo_restore(_cr);
1397     return true;
1400 bool
1401 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1402                                 Geom::Matrix const *image_transform, SPStyle const *style)
1404     g_assert( _is_valid );
1406     if (_render_mode == RENDER_MODE_CLIP)
1407         return true;
1409     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1410     if (!px_rgba)
1411         return false;
1413     float opacity;
1414     if (_state->merge_opacity)
1415         opacity = _state->opacity;
1416     else
1417         opacity = 1.0;
1419     // make a copy of the original pixbuf with premultiplied alpha
1420     // if we pass the original pixbuf it will get messed up
1421     for (unsigned i = 0; i < h; i++) {
1422         for (unsigned j = 0; j < w; j++) {
1423             guchar const *src = px + i * rs + j * 4;
1424             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1425             guchar r, g, b, alpha_dst;
1427             // calculate opacity-modified alpha
1428             alpha_dst = src[3];
1429             if (opacity != 1.0 && _vector_based_target)
1430                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1432             // premul alpha (needed because this will be undone by cairo-pdf)
1433             r = src[0]*alpha_dst/255;
1434             g = src[1]*alpha_dst/255;
1435             b = src[2]*alpha_dst/255;
1437             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1438         }
1439     }
1441     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1442     if (cairo_surface_status(image_surface)) {
1443         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1444         return false;
1445     }
1447     // setup automatic freeing of the image data when destroying the surface
1448     static cairo_user_data_key_t key;
1449     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1451     cairo_save(_cr);
1453     // scaling by width & height is not needed because it will be done by Cairo
1454     if (image_transform)
1455         transform(image_transform);
1457     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1459     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1460     if (_vector_based_target) {
1461         cairo_new_path(_cr);
1462         cairo_rectangle(_cr, 0, 0, w, h);
1463         cairo_clip(_cr);
1464     }
1466     if (_vector_based_target)
1467         cairo_paint(_cr);
1468     else
1469         cairo_paint_with_alpha(_cr, opacity);
1471     cairo_restore(_cr);
1473     cairo_surface_destroy(image_surface);
1475     return true;
1478 #define GLYPH_ARRAY_SIZE 64
1480 unsigned int
1481 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1483     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1484     cairo_glyph_t *glyphs = glyph_array;
1485     unsigned int num_glyphs = glyphtext.size();
1486     if (num_glyphs > GLYPH_ARRAY_SIZE)
1487         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1489     unsigned int num_invalid_glyphs = 0;
1490     unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1491     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1492         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1493         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1494         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1495             TRACE(("INVALID GLYPH found\n"));
1496             g_message("Invalid glyph found, continuing...");
1497             num_invalid_glyphs++;
1498             continue;
1499         }
1500         glyphs[i].index = it_info->index;
1501         glyphs[i].x     = it_info->x;
1502         glyphs[i].y     = it_info->y;
1503         i++;
1504     }
1506     if (path) {
1507         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1508     } else {
1509         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1510     }
1512     if (num_glyphs > GLYPH_ARRAY_SIZE)
1513         g_free(glyphs);
1515     return num_glyphs - num_invalid_glyphs;
1518 bool
1519 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1520                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1522     // create a cairo_font_face from PangoFont
1523     double size = style->font_size.computed;
1524     gpointer fonthash = (gpointer)font;
1525     cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1527     FcPattern *fc_pattern = NULL;
1529 #ifdef USE_PANGO_WIN32
1530 # ifdef CAIRO_HAS_WIN32_FONT
1531     LOGFONTA *lfa = pango_win32_font_logfont(font);
1532     LOGFONTW lfw;
1534     ZeroMemory(&lfw, sizeof(LOGFONTW));
1535     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1536     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1538     if(font_face == NULL) {
1539         font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1540         g_hash_table_insert(font_table, fonthash, font_face);
1541     }
1542 # endif
1543 #else
1544 # ifdef CAIRO_HAS_FT_FONT
1545     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1546     fc_pattern = fc_font->font_pattern;
1547     if(font_face == NULL) {
1548         font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1549         g_hash_table_insert(font_table, fonthash, font_face);
1550     }
1551 # endif
1552 #endif
1554     cairo_save(_cr);
1555     cairo_set_font_face(_cr, font_face);
1557     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1558         size = 12.0;
1560     // set the given font matrix
1561     cairo_matrix_t matrix;
1562     _initCairoMatrix(&matrix, font_matrix);
1563     cairo_set_font_matrix(_cr, &matrix);
1565     if (_render_mode == RENDER_MODE_CLIP) {
1566         if (_clip_mode == CLIP_MODE_MASK) {
1567             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1568                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1569             } else {
1570                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1571             }
1572             _showGlyphs(_cr, font, glyphtext, FALSE);
1573         } else {
1574             // just add the glyph paths to the current context
1575             _showGlyphs(_cr, font, glyphtext, TRUE);
1576         }
1577     } else {
1578         bool fill = false, stroke = false, have_path = false;
1579         if (style->fill.isColor() || style->fill.isPaintserver()) {
1580             fill = true;
1581         }
1583         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1584             stroke = true;
1585         }
1586         if (fill) {
1587             _setFillStyle(style, NULL);
1588             if (_is_texttopath) {
1589                 _showGlyphs(_cr, font, glyphtext, true);
1590                 have_path = true;
1591                 if (stroke) cairo_fill_preserve(_cr);
1592                 else cairo_fill(_cr);
1593             } else {
1594                 _showGlyphs(_cr, font, glyphtext, false);
1595             }
1596         }
1597         if (stroke) {
1598             _setStrokeStyle(style, NULL);
1599             if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1600             cairo_stroke(_cr);
1601         }
1602     }
1604     cairo_restore(_cr);
1606 //    if (font_face)
1607 //        cairo_font_face_destroy(font_face);
1609     return true;
1612 /* Helper functions */
1614 void
1615 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1617     cairo_new_path(_cr);
1618     addPathVector(pv);
1621 void
1622 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1624     feed_pathvector_to_cairo(_cr, pv);
1627 void
1628 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1630     cairo_matrix_t matrix;
1632     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1633     cairo_transform(cr, &matrix);
1636 void
1637 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1639     matrix->xx = (*transform)[0];
1640     matrix->yx = (*transform)[1];
1641     matrix->xy = (*transform)[2];
1642     matrix->yy = (*transform)[3];
1643     matrix->x0 = (*transform)[4];
1644     matrix->y0 = (*transform)[5];
1647 void
1648 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1650     _concatTransform(cr, (*transform)[0], (*transform)[1],
1651                      (*transform)[2], (*transform)[3],
1652                      (*transform)[4], (*transform)[5]);
1655 static cairo_status_t
1656 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1658     size_t written;
1659     FILE *file = (FILE*)closure;
1661     written = fwrite (data, 1, length, file);
1663     if (written == length)
1664         return CAIRO_STATUS_SUCCESS;
1665     else
1666         return CAIRO_STATUS_WRITE_ERROR;
1669 #include "clear-n_.h"
1671 }  /* namespace Internal */
1672 }  /* namespace Extension */
1673 }  /* namespace Inkscape */
1675 #undef TRACE
1676 #undef TEST
1678 /* End of GNU GPL code */
1681 /*
1682   Local Variables:
1683   mode:c++
1684   c-file-style:"stroustrup"
1685   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1686   indent-tabs-mode:nil
1687   fill-column:99
1688   End:
1689 */
1690 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :