Code

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