Code

A simple layout document as to what, why and how is cppification.
[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(0),
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             break;
786 #endif
787 #ifdef CAIRO_HAS_PS_SURFACE
788         case CAIRO_SURFACE_TYPE_PS:
789             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
790             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
791                 return FALSE;
792             }
793 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
794             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
795             cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
796 #endif
797             break;
798 #endif
799         default:
800             return false;
801             break;
802     }
804     return _finishSurfaceSetup (surface, &ctm);
807 bool
808 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
810     if (_is_valid || !surface)
811         return false;
813     _vector_based_target = is_vector;
814     bool ret = _finishSurfaceSetup (surface, ctm);
815     if (ret)
816         cairo_surface_reference (surface);
817     return ret;
820 bool
821 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
823     if(surface == NULL) {
824         return false;
825     }
826     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
827         return false;
828     }
830     _cr = cairo_create(surface);
831     if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
832         return false;
833     }
834     if (ctm)
835         cairo_set_matrix(_cr, ctm);
836     _surface = surface;
838     if (_vector_based_target) {
839         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
840     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
841         // set background color on non-alpha surfaces
842         // TODO: bgcolor should be derived from SPDocument
843         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
844         cairo_rectangle(_cr, 0, 0, _width, _height);
845         cairo_fill(_cr);
846     }
848     _is_valid = TRUE;
850     return true;
853 bool
854 CairoRenderContext::finish(void)
856     g_assert( _is_valid );
858     if (_vector_based_target)
859         cairo_show_page(_cr);
861     cairo_destroy(_cr);
862     cairo_surface_finish(_surface);
863     cairo_status_t status = cairo_surface_status(_surface);
864     cairo_surface_destroy(_surface);
865     _cr = NULL;
866     _surface = NULL;
868     if (_layout)
869         g_object_unref(_layout);
871     _is_valid = FALSE;
873     if (_vector_based_target && _stream) {
874         /* Flush stream to be sure. */
875         (void) fflush(_stream);
877         fclose(_stream);
878         _stream = NULL;
879     }
881     if (status == CAIRO_STATUS_SUCCESS)
882         return true;
883     else
884         return false;
887 void
888 CairoRenderContext::transform(Geom::Matrix const *transform)
890     g_assert( _is_valid );
892     cairo_matrix_t matrix;
893     _initCairoMatrix(&matrix, transform);
894     cairo_transform(_cr, &matrix);
896     // store new CTM
897     getTransform(&_state->transform);
900 void
901 CairoRenderContext::setTransform(Geom::Matrix const *transform)
903     g_assert( _is_valid );
905     cairo_matrix_t matrix;
906     _initCairoMatrix(&matrix, transform);
907     cairo_set_matrix(_cr, &matrix);
908     _state->transform = *transform;
911 void
912 CairoRenderContext::getTransform(Geom::Matrix *copy) const
914     g_assert( _is_valid );
916     cairo_matrix_t ctm;
917     cairo_get_matrix(_cr, &ctm);
918     (*copy)[0] = ctm.xx;
919     (*copy)[1] = ctm.yx;
920     (*copy)[2] = ctm.xy;
921     (*copy)[3] = ctm.yy;
922     (*copy)[4] = ctm.x0;
923     (*copy)[5] = ctm.y0;
926 void
927 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
929     g_assert( _is_valid );
931     CairoRenderState *parent_state = getParentState();
932     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
935 void
936 CairoRenderContext::pushState(void)
938     g_assert( _is_valid );
940     cairo_save(_cr);
942     CairoRenderState *new_state = _createState();
943     // copy current state's transform
944     new_state->transform = _state->transform;
945     _state_stack = g_slist_prepend(_state_stack, new_state);
946     _state = new_state;
949 void
950 CairoRenderContext::popState(void)
952     g_assert( _is_valid );
954     cairo_restore(_cr);
956     g_free(_state_stack->data);
957     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
958     _state = (CairoRenderState*)_state_stack->data;
960     g_assert( g_slist_length(_state_stack) > 0 );
963 static bool pattern_hasItemChildren (SPPattern *pat)
965     for (SPObject *child = SP_OBJECT(pat)->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
966         if (SP_IS_ITEM (child)) {
967             return true;
968         }
969     }
970     return false;
973 cairo_pattern_t*
974 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
976     g_assert( SP_IS_PATTERN(paintserver) );
978     SPPattern *pat = SP_PATTERN (paintserver);
980     Geom::Matrix ps2user, pcs2dev;
981     ps2user = Geom::identity();
982     pcs2dev = Geom::identity();
984     double x = pattern_x(pat);
985     double y = pattern_y(pat);
986     double width = pattern_width(pat);
987     double height = pattern_height(pat);
988     double bbox_width_scaler;
989     double bbox_height_scaler;
991     TRACE(("%f x %f pattern\n", width, height));
993     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
994         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
995         bbox_width_scaler = pbox->x1 - pbox->x0;
996         bbox_height_scaler = pbox->y1 - pbox->y0;
997         ps2user[4] = x * bbox_width_scaler + pbox->x0;
998         ps2user[5] = y * bbox_height_scaler + pbox->y0;
999     } else {
1000         bbox_width_scaler = 1.0;
1001         bbox_height_scaler = 1.0;
1002         ps2user[4] = x;
1003         ps2user[5] = y;
1004     }
1006     // apply pattern transformation
1007     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1008     ps2user *= pattern_transform;
1009     Geom::Point ori (ps2user[4], ps2user[5]);
1011     // create pattern contents coordinate system
1012     if (pat->viewBox_set) {
1013         NRRect *view_box = pattern_viewBox(pat);
1015         double x, y, w, h;
1016         double view_width, view_height;
1017         x = 0;
1018         y = 0;
1019         w = width * bbox_width_scaler;
1020         h = height * bbox_height_scaler;
1022         view_width = view_box->x1 - view_box->x0;
1023         view_height = view_box->y1 - view_box->y0;
1025         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1026         pcs2dev[0] = w / view_width;
1027         pcs2dev[3] = h / view_height;
1028         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1029         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1030     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1031         pcs2dev[0] = pbox->x1 - pbox->x0;
1032         pcs2dev[3] = pbox->y1 - pbox->y0;
1034     }
1036     // Calculate the size of the surface which has to be created
1037 #define SUBPIX_SCALE 100
1038     // Cairo requires an integer pattern surface width/height.
1039     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1040     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1041     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1042     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1043     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1044     // create new rendering context
1045     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1047     // adjust the size of the painted pattern to fit exactly the created surface
1048     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1049     double scale_width = surface_width / (bbox_width_scaler * width);
1050     double scale_height = surface_height / (bbox_height_scaler * height);
1051     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1052         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1053         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1054         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1055     }
1057     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1058     ps2user[4] = ori[Geom::X];
1059     ps2user[5] = ori[Geom::Y];
1061     pattern_ctx->setTransform(&pcs2dev);
1062     pattern_ctx->pushState();
1064     // create arena and group
1065     NRArena *arena = NRArena::create();
1066     unsigned dkey = SPItem::display_key_new(1);
1068     // show items and render them
1069     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1070         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1071             for (SPObject *child = SP_OBJECT(pat_i)->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1072                 if (SP_IS_ITEM (child)) {
1073                     SP_ITEM (child)->invoke_show (arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1074                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1075                 }
1076             }
1077             break; // do not go further up the chain if children are found
1078         }
1079     }
1081     pattern_ctx->popState();
1083     // setup a cairo_pattern_t
1084     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1085     TEST(pattern_ctx->saveAsPng("pattern.png"));
1086     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1087     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1089     // set pattern transformation
1090     cairo_matrix_t pattern_matrix;
1091     _initCairoMatrix(&pattern_matrix, &ps2user);
1092     cairo_matrix_invert(&pattern_matrix);
1093     cairo_pattern_set_matrix(result, &pattern_matrix);
1095     delete pattern_ctx;
1097     // hide all items
1098     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1099         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1100             for (SPObject *child = SP_OBJECT(pat_i)->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1101                 if (SP_IS_ITEM (child)) {
1102                     SP_ITEM (child)->invoke_hide (dkey);
1103                 }
1104             }
1105             break; // do not go further up the chain if children are found
1106         }
1107     }
1109     return result;
1112 cairo_pattern_t*
1113 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1114                                                  NRRect const *pbox, float alpha)
1116     cairo_pattern_t *pattern = NULL;
1117     bool apply_bbox2user = FALSE;
1119     if (SP_IS_LINEARGRADIENT (paintserver)) {
1121             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1123             SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1125             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1126             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1127             if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1128                 // convert to userspace
1129                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1130                 p1 *= bbox2user;
1131                 p2 *= bbox2user;
1132             }
1134             // create linear gradient pattern
1135             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1137             // add stops
1138             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1139                 float rgb[3];
1140                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1141                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1142             }
1143     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1145         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1147         SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1149         Geom::Point c (rg->cx.computed, rg->cy.computed);
1150         Geom::Point f (rg->fx.computed, rg->fy.computed);
1151         double r = rg->r.computed;
1152         if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1153             apply_bbox2user = true;
1155         // create radial gradient pattern
1156         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1158         // add stops
1159         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1160             float rgb[3];
1161             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1162             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1163         }
1164     } else if (SP_IS_PATTERN (paintserver)) {
1166         pattern = _createPatternPainter(paintserver, pbox);
1167     } else {
1168         return NULL;
1169     }
1171     if (pattern && SP_IS_GRADIENT (paintserver)) {
1172         SPGradient *g = SP_GRADIENT(paintserver);
1174         // set extend type
1175         SPGradientSpread spread = g->fetchSpread();
1176         switch (spread) {
1177             case SP_GRADIENT_SPREAD_REPEAT: {
1178                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1179                 break;
1180             }
1181             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1182                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1183                 break;
1184             }
1185             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1186                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1187                 break;
1188             }
1189             default: {
1190                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1191                 break;
1192             }
1193         }
1195         cairo_matrix_t pattern_matrix;
1196         if (g->gradientTransform_set) {
1197             // apply gradient transformation
1198             cairo_matrix_init(&pattern_matrix,
1199                 g->gradientTransform[0], g->gradientTransform[1],
1200                 g->gradientTransform[2], g->gradientTransform[3],
1201                 g->gradientTransform[4], g->gradientTransform[5]);
1202         } else {
1203             cairo_matrix_init_identity (&pattern_matrix);
1204         }
1206         if (apply_bbox2user) {
1207             // convert to userspace
1208             cairo_matrix_t bbox2user;
1209             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1210             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1211         }
1212         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1213         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1214     }
1216     return pattern;
1219 void
1220 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1222     g_return_if_fail( !style->fill.set
1223                       || style->fill.isColor()
1224                       || style->fill.isPaintserver() );
1226     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1227     if (_state->merge_opacity) {
1228         alpha *= _state->opacity;
1229         TRACE(("merged op=%f\n", alpha));
1230     }
1232     if (style->fill.isColor()) {
1233         float rgb[3];
1234         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1236         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1238     } else if (!style->fill.set) { // unset fill is black
1239         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1241     } else {
1242         g_assert( style->fill.isPaintserver()
1243                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1244                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1246         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1248         if (pattern) {
1249             cairo_set_source(_cr, pattern);
1250             cairo_pattern_destroy(pattern);
1251         }
1252     }
1255 void
1256 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1258     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1259     if (_state->merge_opacity)
1260         alpha *= _state->opacity;
1262     if (style->stroke.isColor()) {
1263         float rgb[3];
1264         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1266         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1267     } else {
1268         g_assert( style->fill.isPaintserver()
1269                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1270                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1272         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1274         if (pattern) {
1275             cairo_set_source(_cr, pattern);
1276             cairo_pattern_destroy(pattern);
1277         }
1278     }
1280     if (style->stroke_dash.n_dash   &&
1281         style->stroke_dash.dash       )
1282     {
1283         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1284     } else {
1285         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1286     }
1288     cairo_set_line_width(_cr, style->stroke_width.computed);
1290     // set line join type
1291     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1292     switch (style->stroke_linejoin.computed) {
1293         case SP_STROKE_LINEJOIN_MITER:
1294             join = CAIRO_LINE_JOIN_MITER;
1295             break;
1296         case SP_STROKE_LINEJOIN_ROUND:
1297             join = CAIRO_LINE_JOIN_ROUND;
1298             break;
1299         case SP_STROKE_LINEJOIN_BEVEL:
1300             join = CAIRO_LINE_JOIN_BEVEL;
1301             break;
1302     }
1303     cairo_set_line_join(_cr, join);
1305     // set line cap type
1306     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1307     switch (style->stroke_linecap.computed) {
1308         case SP_STROKE_LINECAP_BUTT:
1309             cap = CAIRO_LINE_CAP_BUTT;
1310             break;
1311         case SP_STROKE_LINECAP_ROUND:
1312             cap = CAIRO_LINE_CAP_ROUND;
1313             break;
1314         case SP_STROKE_LINECAP_SQUARE:
1315             cap = CAIRO_LINE_CAP_SQUARE;
1316             break;
1317     }
1318     cairo_set_line_cap(_cr, cap);
1319     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1322 bool
1323 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1325     g_assert( _is_valid );
1327     if (_render_mode == RENDER_MODE_CLIP) {
1328         if (_clip_mode == CLIP_MODE_PATH) {
1329             addClipPath(pathv, &style->fill_rule);
1330         } else {
1331             setPathVector(pathv);
1332             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1333                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1334             } else {
1335                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1336             }
1337             cairo_fill(_cr);
1338             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1339         }
1340         return true;
1341     }
1343     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1344     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1345                     style->stroke_opacity.value == 0;
1347     if (no_fill && no_stroke)
1348         return true;
1350     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1351                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1353     if (!need_layer)
1354         cairo_save(_cr);
1355     else
1356         pushLayer();
1358     if (!no_fill) {
1359         _setFillStyle(style, pbox);
1360         setPathVector(pathv);
1362         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1363             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1364         } else {
1365             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1366         }
1368         if (no_stroke)
1369             cairo_fill(_cr);
1370         else
1371             cairo_fill_preserve(_cr);
1372     }
1374     if (!no_stroke) {
1375         _setStrokeStyle(style, pbox);
1376         if (no_fill)
1377             setPathVector(pathv);
1379         cairo_stroke(_cr);
1380     }
1382     if (need_layer)
1383         popLayer();
1384     else
1385         cairo_restore(_cr);
1387     return true;
1390 bool
1391 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1392                                 Geom::Matrix const *image_transform, SPStyle const *style)
1394     g_assert( _is_valid );
1396     if (_render_mode == RENDER_MODE_CLIP)
1397         return true;
1399     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1400     if (!px_rgba)
1401         return false;
1403     float opacity;
1404     if (_state->merge_opacity)
1405         opacity = _state->opacity;
1406     else
1407         opacity = 1.0;
1409     // make a copy of the original pixbuf with premultiplied alpha
1410     // if we pass the original pixbuf it will get messed up
1411     for (unsigned i = 0; i < h; i++) {
1412         for (unsigned j = 0; j < w; j++) {
1413             guchar const *src = px + i * rs + j * 4;
1414             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1415             guchar r, g, b, alpha_dst;
1417             // calculate opacity-modified alpha
1418             alpha_dst = src[3];
1419             if (opacity != 1.0 && _vector_based_target)
1420                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1422             // premul alpha (needed because this will be undone by cairo-pdf)
1423             r = src[0]*alpha_dst/255;
1424             g = src[1]*alpha_dst/255;
1425             b = src[2]*alpha_dst/255;
1427             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1428         }
1429     }
1431     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1432     if (cairo_surface_status(image_surface)) {
1433         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1434         return false;
1435     }
1437     // setup automatic freeing of the image data when destroying the surface
1438     static cairo_user_data_key_t key;
1439     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1441     cairo_save(_cr);
1443     // scaling by width & height is not needed because it will be done by Cairo
1444     if (image_transform)
1445         transform(image_transform);
1447     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1449     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1450     if (_vector_based_target) {
1451         cairo_new_path(_cr);
1452         cairo_rectangle(_cr, 0, 0, w, h);
1453         cairo_clip(_cr);
1454     }
1456     if (_vector_based_target)
1457         cairo_paint(_cr);
1458     else
1459         cairo_paint_with_alpha(_cr, opacity);
1461     cairo_restore(_cr);
1463     cairo_surface_destroy(image_surface);
1465     return true;
1468 #define GLYPH_ARRAY_SIZE 64
1470 unsigned int
1471 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1473     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1474     cairo_glyph_t *glyphs = glyph_array;
1475     unsigned int num_glyphs = glyphtext.size();
1476     if (num_glyphs > GLYPH_ARRAY_SIZE)
1477         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1479     unsigned int num_invalid_glyphs = 0;
1480     unsigned int i = 0;
1481     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1482         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1483         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1484         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1485             TRACE(("INVALID GLYPH found\n"));
1486             num_invalid_glyphs++;
1487             continue;
1488         }
1489         glyphs[i - num_invalid_glyphs].index = it_info->index;
1490         glyphs[i - num_invalid_glyphs].x = it_info->x;
1491         glyphs[i - num_invalid_glyphs].y = it_info->y;
1492         i++;
1493     }
1495     if (path) {
1496         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1497     } else {
1498         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1499     }
1501     if (num_glyphs > GLYPH_ARRAY_SIZE)
1502         g_free(glyphs);
1504     return num_glyphs - num_invalid_glyphs;
1507 bool
1508 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1509                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1511     // create a cairo_font_face from PangoFont
1512     double size = style->font_size.computed;
1513     gpointer fonthash = (gpointer)font;
1514     cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1516     FcPattern *fc_pattern = NULL;
1518 #ifdef USE_PANGO_WIN32
1519 # ifdef CAIRO_HAS_WIN32_FONT
1520     LOGFONTA *lfa = pango_win32_font_logfont(font);
1521     LOGFONTW lfw;
1523     ZeroMemory(&lfw, sizeof(LOGFONTW));
1524     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1525     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1527     if(font_face == NULL) {
1528         font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1529         g_hash_table_insert(font_table, fonthash, font_face);
1530     }
1531 # endif
1532 #else
1533 # ifdef CAIRO_HAS_FT_FONT
1534     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1535     fc_pattern = fc_font->font_pattern;
1536     if(font_face == NULL) {
1537         font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1538         g_hash_table_insert(font_table, fonthash, font_face);
1539     }
1540 # endif
1541 #endif
1543     cairo_save(_cr);
1544     cairo_set_font_face(_cr, font_face);
1546     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1547         size = 12.0;
1549     // set the given font matrix
1550     cairo_matrix_t matrix;
1551     _initCairoMatrix(&matrix, font_matrix);
1552     cairo_set_font_matrix(_cr, &matrix);
1554     if (_render_mode == RENDER_MODE_CLIP) {
1555         if (_clip_mode == CLIP_MODE_MASK) {
1556             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1557                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1558             } else {
1559                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1560             }
1561             _showGlyphs(_cr, font, glyphtext, FALSE);
1562         } else {
1563             // just add the glyph paths to the current context
1564             _showGlyphs(_cr, font, glyphtext, TRUE);
1565         }
1566     } else {
1567         bool fill = false, stroke = false, have_path = false;
1568         if (style->fill.isColor() || style->fill.isPaintserver()) {
1569             fill = true;
1570         }
1572         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1573             stroke = true;
1574         }
1575         if (fill) {
1576             _setFillStyle(style, NULL);
1577             if (_is_texttopath) {
1578                 _showGlyphs(_cr, font, glyphtext, true);
1579                 have_path = true;
1580                 if (stroke) cairo_fill_preserve(_cr);
1581                 else cairo_fill(_cr);
1582             } else {
1583                 _showGlyphs(_cr, font, glyphtext, false);
1584             }
1585         }
1586         if (stroke) {
1587             _setStrokeStyle(style, NULL);
1588             if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1589             cairo_stroke(_cr);
1590         }
1591     }
1593     cairo_restore(_cr);
1595 //    if (font_face)
1596 //        cairo_font_face_destroy(font_face);
1598     return true;
1601 /* Helper functions */
1603 void
1604 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1606     cairo_new_path(_cr);
1607     addPathVector(pv);
1610 void
1611 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1613     feed_pathvector_to_cairo(_cr, pv);
1616 void
1617 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1619     cairo_matrix_t matrix;
1621     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1622     cairo_transform(cr, &matrix);
1625 void
1626 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1628     matrix->xx = (*transform)[0];
1629     matrix->yx = (*transform)[1];
1630     matrix->xy = (*transform)[2];
1631     matrix->yy = (*transform)[3];
1632     matrix->x0 = (*transform)[4];
1633     matrix->y0 = (*transform)[5];
1636 void
1637 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1639     _concatTransform(cr, (*transform)[0], (*transform)[1],
1640                      (*transform)[2], (*transform)[3],
1641                      (*transform)[4], (*transform)[5]);
1644 static cairo_status_t
1645 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1647     size_t written;
1648     FILE *file = (FILE*)closure;
1650     written = fwrite (data, 1, length, file);
1652     if (written == length)
1653         return CAIRO_STATUS_SUCCESS;
1654     else
1655         return CAIRO_STATUS_WRITE_ERROR;
1658 #include "clear-n_.h"
1660 }  /* namespace Internal */
1661 }  /* namespace Extension */
1662 }  /* namespace Inkscape */
1664 #undef TRACE
1665 #undef TEST
1667 /* End of GNU GPL code */
1670 /*
1671   Local Variables:
1672   mode:c++
1673   c-file-style:"stroustrup"
1674   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1675   indent-tabs-mode:nil
1676   fill-column:99
1677   End:
1678 */
1679 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :