Code

error checks on surface return values
[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>
31 #include <libnr/n-art-bpath.h>
32 #include <libnr/nr-matrix-ops.h>
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-translate-ops.h>
35 #include <libnr/nr-scale-matrix-ops.h>
37 #include <glib/gmem.h>
39 #include <glibmm/i18n.h>
40 #include "display/nr-arena.h"
41 #include "display/nr-arena-item.h"
42 #include "display/nr-arena-group.h"
43 #include "display/curve.h"
44 #include "display/canvas-bpath.h"
45 #include "sp-item.h"
46 #include "sp-item-group.h"
47 #include "style.h"
48 #include "sp-linear-gradient.h"
49 #include "sp-radial-gradient.h"
50 #include "sp-pattern.h"
51 #include "sp-mask.h"
52 #include "sp-clippath.h"
54 #include <unit-constants.h>
56 #include "cairo-render-context.h"
57 #include "cairo-renderer.h"
58 #include "extension/system.h"
60 #include "io/sys.h"
62 #include <cairo.h>
64 // include support for only the compiled-in surface types
65 #ifdef CAIRO_HAS_PDF_SURFACE
66 #include <cairo-pdf.h>
67 #endif
68 #ifdef CAIRO_HAS_PS_SURFACE
69 #include <cairo-ps.h>
70 #endif
73 #ifndef PANGO_ENABLE_BACKEND
74 #include <cairo-ft.h>
75 #endif
77 #include <pango/pangofc-fontmap.h>
79 //#define TRACE(_args) g_printf _args
80 #define TRACE(_args)
81 //#define TEST(_args) _args
82 #define TEST(_args)
84 // FIXME: expose these from sp-clippath/mask.cpp
85 struct SPClipPathView {
86     SPClipPathView *next;
87     unsigned int key;
88     NRArenaItem *arenaitem;
89     NRRect bbox;
90 };
92 struct SPMaskView {
93     SPMaskView *next;
94     unsigned int key;
95     NRArenaItem *arenaitem;
96     NRRect bbox;
97 };
99 namespace Inkscape {
100 namespace Extension {
101 namespace Internal {
103 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
105 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
106     _dpi(72),
107     _pdf_level(0),
108     _ps_level(1),
109     _is_texttopath(FALSE),
110     _is_filtertobitmap(FALSE),
111     _stream(NULL),
112     _is_valid(FALSE),
113     _vector_based_target(FALSE),
114     _cr(NULL),
115     _surface(NULL),
116     _target(CAIRO_SURFACE_TYPE_IMAGE),
117     _target_format(CAIRO_FORMAT_ARGB32),
118     _layout(NULL),
119     _state(NULL),
120     _renderer(parent),
121     _render_mode(RENDER_MODE_NORMAL),
122     _clip_mode(CLIP_MODE_MASK)
123 {}
125 CairoRenderContext::~CairoRenderContext(void)
127     if (_cr) cairo_destroy(_cr);
128     if (_surface) cairo_surface_destroy(_surface);
129     if (_layout) g_object_unref(_layout);
132 CairoRenderer*
133 CairoRenderContext::getRenderer(void) const
135     return _renderer;
138 CairoRenderState*
139 CairoRenderContext::getCurrentState(void) const
141     return _state;
144 CairoRenderState*
145 CairoRenderContext::getParentState(void) const
147     // if this is the root node just return it
148     if (g_slist_length(_state_stack) == 1) {
149         return _state;
150     } else {
151         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
152     }
155 void
156 CairoRenderContext::setStateForStyle(SPStyle const *style)
158     // only opacity & overflow is stored for now
159     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
160     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
161     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
163     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
164         _state->merge_opacity = FALSE;
166     // disable rendering of opacity if there's a stroke on the fill
167     if (_state->merge_opacity
168         && !style->fill.isNone()
169         && !style->stroke.isNone())
170         _state->merge_opacity = FALSE;
173 /**
174  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
175  *
176  * \param width     width of the surface to be created
177  * \param height    height of the surface to be created
178  */
179 CairoRenderContext*
180 CairoRenderContext::cloneMe(double width, double height) const
182     g_assert( _is_valid );
183     g_assert( width > 0.0 && height > 0.0 );
185     CairoRenderContext *new_context = _renderer->createContext();
186     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
187                                                             (int)ceil(width), (int)ceil(height));
188     new_context->_cr = cairo_create(surface);
189     new_context->_surface = surface;
190     new_context->_is_valid = TRUE;
192     return new_context;
195 CairoRenderContext*
196 CairoRenderContext::cloneMe(void) const
198     g_assert( _is_valid );
200     return cloneMe(_width, _height);
203 bool
204 CairoRenderContext::setImageTarget(cairo_format_t format)
206     // format cannot be set on an already initialized surface
207     if (_is_valid)
208         return false;
210     switch (format) {
211         case CAIRO_FORMAT_ARGB32:
212         case CAIRO_FORMAT_RGB24:
213         case CAIRO_FORMAT_A8:
214         case CAIRO_FORMAT_A1:
215             _target_format = format;
216             _target = CAIRO_SURFACE_TYPE_IMAGE;
217             return true;
218             break;
219         default:
220             break;
221     }
223     return false;
226 bool
227 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
229 #ifndef CAIRO_HAS_PDF_SURFACE
230     return false;
231 #else
232     _target = CAIRO_SURFACE_TYPE_PDF;
233     _vector_based_target = TRUE;
234 #endif
236     FILE *osf = NULL;
237     FILE *osp = NULL;
239     gsize bytesRead = 0;
240     gsize bytesWritten = 0;
241     GError *error = NULL;
242     gchar *local_fn = g_filename_from_utf8(utf8_fn,
243                                            -1,  &bytesRead,  &bytesWritten, &error);
244     gchar const *fn = local_fn;
246     /* TODO: Replace the below fprintf's with something that does the right thing whether in
247     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
248     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
249     * return code.
250     */
251     if (fn != NULL) {
252         if (*fn == '|') {
253             fn += 1;
254             while (isspace(*fn)) fn += 1;
255 #ifndef WIN32
256             osp = popen(fn, "w");
257 #else
258             osp = _popen(fn, "w");
259 #endif
260             if (!osp) {
261                 fprintf(stderr, "inkscape: popen(%s): %s\n",
262                         fn, strerror(errno));
263                 return false;
264             }
265             _stream = osp;
266         } else if (*fn == '>') {
267             fn += 1;
268             while (isspace(*fn)) fn += 1;
269             Inkscape::IO::dump_fopen_call(fn, "K");
270             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
271             if (!osf) {
272                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
273                         fn, strerror(errno));
274                 return false;
275             }
276             _stream = osf;
277         } else {
278             /* put cwd stuff in here */
279             gchar *qn = ( *fn
280                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
281                 : g_strdup("lpr") );
282 #ifndef WIN32
283             osp = popen(qn, "w");
284 #else
285             osp = _popen(qn, "w");
286 #endif
287             if (!osp) {
288                 fprintf(stderr, "inkscape: popen(%s): %s\n",
289                         qn, strerror(errno));
290                 return false;
291             }
292             g_free(qn);
293             _stream = osp;
294         }
295     }
297     g_free(local_fn);
299     if (_stream) {
300         /* fixme: this is kinda icky */
301 #if !defined(_WIN32) && !defined(__WIN32__)
302         (void) signal(SIGPIPE, SIG_IGN);
303 #endif
304     }
306     return true;
309 bool
310 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
312 #ifndef CAIRO_HAS_PS_SURFACE
313     return false;
314 #else
315     _target = CAIRO_SURFACE_TYPE_PS;
316     _vector_based_target = TRUE;
317 #endif
319     FILE *osf = NULL;
320     FILE *osp = NULL;
322     gsize bytesRead = 0;
323     gsize bytesWritten = 0;
324     GError *error = NULL;
325     gchar *local_fn = g_filename_from_utf8(utf8_fn,
326                                            -1,  &bytesRead,  &bytesWritten, &error);
327     gchar const *fn = local_fn;
329     /* TODO: Replace the below fprintf's with something that does the right thing whether in
330     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
331     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
332     * return code.
333     */
334     if (fn != NULL) {
335         if (*fn == '|') {
336             fn += 1;
337             while (isspace(*fn)) fn += 1;
338 #ifndef WIN32
339             osp = popen(fn, "w");
340 #else
341             osp = _popen(fn, "w");
342 #endif
343             if (!osp) {
344                 fprintf(stderr, "inkscape: popen(%s): %s\n",
345                         fn, strerror(errno));
346                 return false;
347             }
348             _stream = osp;
349         } else if (*fn == '>') {
350             fn += 1;
351             while (isspace(*fn)) fn += 1;
352             Inkscape::IO::dump_fopen_call(fn, "K");
353             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
354             if (!osf) {
355                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
356                         fn, strerror(errno));
357                 return false;
358             }
359             _stream = osf;
360         } else {
361             /* put cwd stuff in here */
362             gchar *qn = ( *fn
363                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
364                 : g_strdup("lpr") );
365 #ifndef WIN32
366             osp = popen(qn, "w");
367 #else
368             osp = _popen(qn, "w");
369 #endif
370             if (!osp) {
371                 fprintf(stderr, "inkscape: popen(%s): %s\n",
372                         qn, strerror(errno));
373                 return false;
374             }
375             g_free(qn);
376             _stream = osp;
377         }
378     }
380     g_free(local_fn);
382     if (_stream) {
383         /* fixme: this is kinda icky */
384 #if !defined(_WIN32) && !defined(__WIN32__)
385         (void) signal(SIGPIPE, SIG_IGN);
386 #endif
387     }
389     return true;
392 void CairoRenderContext::setPSLevel(unsigned int level)
394     _ps_level = level;
397 void CairoRenderContext::setPDFLevel(unsigned int level)
399     _pdf_level = level;
402 void CairoRenderContext::setTextToPath(bool texttopath)
404     _is_texttopath = texttopath;
407 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
409     _is_filtertobitmap = filtertobitmap;
412 cairo_surface_t*
413 CairoRenderContext::getSurface(void)
415     g_assert( _is_valid );
417     return _surface;
420 bool
421 CairoRenderContext::saveAsPng(const char *file_name)
423     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
424     if (status)
425         return false;
426     else
427         return true;
430 void
431 CairoRenderContext::setRenderMode(CairoRenderMode mode)
433     switch (mode) {
434         case RENDER_MODE_NORMAL:
435         case RENDER_MODE_CLIP:
436             _render_mode = mode;
437             break;
438         default:
439             _render_mode = RENDER_MODE_NORMAL;
440             break;
441     }
444 CairoRenderContext::CairoRenderMode
445 CairoRenderContext::getRenderMode(void) const
447     return _render_mode;
450 void
451 CairoRenderContext::setClipMode(CairoClipMode mode)
453     switch (mode) {
454         case CLIP_MODE_PATH:
455         case CLIP_MODE_MASK:
456             _clip_mode = mode;
457             break;
458         default:
459             _clip_mode = CLIP_MODE_PATH;
460             break;
461     }
464 CairoRenderContext::CairoClipMode
465 CairoRenderContext::getClipMode(void) const
467     return _clip_mode;
470 CairoRenderState*
471 CairoRenderContext::_createState(void)
473     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
474     g_assert( state != NULL );
476     state->has_filtereffect = FALSE;
477     state->merge_opacity = TRUE;
478     state->opacity = 1.0;
479     state->need_layer = FALSE;
480     state->has_overflow = FALSE;
481     state->parent_has_userspace = FALSE;
482     state->clip_path = NULL;
483     state->mask = NULL;
485     return state;
488 void
489 CairoRenderContext::pushLayer(void)
491     g_assert( _is_valid );
493     TRACE(("--pushLayer\n"));
494     cairo_push_group(_cr);
496     // clear buffer
497     if (!_vector_based_target) {
498         cairo_save(_cr);
499         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
500         cairo_paint(_cr);
501         cairo_restore(_cr);
502     }
505 void
506 CairoRenderContext::popLayer(void)
508     g_assert( _is_valid );
510     float opacity = _state->opacity;
511     TRACE(("--popLayer w/ %f\n", opacity));
513     // apply clipPath or mask if present
514     SPClipPath *clip_path = _state->clip_path;
515     SPMask *mask = _state->mask;
516     if (clip_path || mask) {
518         CairoRenderContext *clip_ctx = 0;
519         cairo_surface_t *clip_mask = 0;
521         if (clip_path) {
522             if (_render_mode == RENDER_MODE_CLIP)
523                 mask = NULL;    // disable mask when performing nested clipping
525             if (_vector_based_target) {
526                 setClipMode(CLIP_MODE_PATH);
527                 if (!mask) {
528                     cairo_pop_group_to_source(_cr);
529                     _renderer->applyClipPath(this, clip_path);
530                     if (opacity == 1.0)
531                         cairo_paint(_cr);
532                     else
533                         cairo_paint_with_alpha(_cr, opacity);
535                 } else {
536                     // the clipPath will be applied before masking
537                 }
538             } else {
540                 // setup a new rendering context
541                 clip_ctx = _renderer->createContext();
542                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
543                 clip_ctx->setClipMode(CLIP_MODE_MASK);
544                 if (!clip_ctx->setupSurface(_width, _height)) {
545                     TRACE(("setupSurface failed\n"));
546                     _renderer->destroyContext(clip_ctx);
547                     return;
548                 }
550                 // clear buffer
551                 cairo_save(clip_ctx->_cr);
552                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
553                 cairo_paint(clip_ctx->_cr);
554                 cairo_restore(clip_ctx->_cr);
556                 // if a mask won't be applied set opacity too
557                 if (!mask)
558                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
559                 else
560                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
562                 // copy over the correct CTM
563                 if (_state->parent_has_userspace)
564                     clip_ctx->setTransform(&getParentState()->transform);
565                 else
566                     clip_ctx->setTransform(&_state->transform);
568                 // apply the clip path
569                 clip_ctx->pushState();
570                 _renderer->applyClipPath(clip_ctx, clip_path);
571                 clip_ctx->popState();
573                 clip_mask = clip_ctx->getSurface();
574                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
576                 if (!mask) {
577                     cairo_pop_group_to_source(_cr);
578                     cairo_mask_surface(_cr, clip_mask, 0, 0);
579                     _renderer->destroyContext(clip_ctx);
580                 }
581             }
582         }
584         if (mask) {
585             // create rendering context for mask
586             CairoRenderContext *mask_ctx = _renderer->createContext();
587             mask_ctx->setupSurface(_width, _height);
589             // set rendering mode to normal
590             setRenderMode(RENDER_MODE_NORMAL);
592             // copy the correct CTM to mask context
593             if (_state->parent_has_userspace)
594                 mask_ctx->setTransform(&getParentState()->transform);
595             else
596                 mask_ctx->setTransform(&_state->transform);
598             // render mask contents to mask_ctx
599             _renderer->applyMask(mask_ctx, mask);
601             TEST(mask_ctx->saveAsPng("mask.png"));
603             // composite with clip mask
604             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
605                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
606                 _renderer->destroyContext(clip_ctx);
607             }
609             cairo_surface_t *mask_image = mask_ctx->getSurface();
610             int width = cairo_image_surface_get_width(mask_image);
611             int height = cairo_image_surface_get_height(mask_image);
612             int stride = cairo_image_surface_get_stride(mask_image);
613             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
615             // premultiply with opacity
616             if (_state->opacity != 1.0) {
617                 TRACE(("premul w/ %f\n", opacity));
618                 guint8 int_opacity = (guint8)(255 * opacity);
619                 for (int row = 0 ; row < height; row++) {
620                     unsigned char *row_data = pixels + (row * stride);
621                     for (int i = 0 ; i < width; i++) {
622                         guint32 *pixel = (guint32 *)row_data + i;
623                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
624                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
625                                 ((*pixel & 0x000000ff)      ) * 4688) *
626                                 int_opacity);
627                     }
628                 }
629             }
631             cairo_pop_group_to_source(_cr);
632             if (_clip_mode == CLIP_MODE_PATH) {
633                 // we have to do the clipping after cairo_pop_group_to_source
634                 _renderer->applyClipPath(this, clip_path);
635             }
636             // apply the mask onto the layer
637             cairo_mask_surface(_cr, mask_image, 0, 0);
638             _renderer->destroyContext(mask_ctx);
639         }
640     } else {
641         cairo_pop_group_to_source(_cr);
642         if (opacity == 1.0)
643             cairo_paint(_cr);
644         else
645             cairo_paint_with_alpha(_cr, opacity);
646     }
649 void
650 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
652     g_assert( _is_valid );
654     // here it should be checked whether the current clip winding changed
655     // so we could switch back to masked clipping
656     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
657         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
658     } else {
659         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
660     }
661     addBpath(bp);
664 void
665 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
667     g_assert( _is_valid );
669     cairo_rectangle(_cr, x, y, width, height);
670     cairo_clip(_cr);
673 bool
674 CairoRenderContext::setupSurface(double width, double height)
676     // Is the surface already set up?
677     if (_is_valid)
678         return true;
680     if (_vector_based_target && _stream == NULL)
681         return false;
683     cairo_surface_t *surface = NULL;
684     switch (_target) {
685         case CAIRO_SURFACE_TYPE_IMAGE:
686             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
687             break;
688 #ifdef CAIRO_HAS_PDF_SURFACE
689         case CAIRO_SURFACE_TYPE_PDF:
690             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
691             break;
692 #endif
693 #ifdef CAIRO_HAS_PS_SURFACE
694         case CAIRO_SURFACE_TYPE_PS:
695             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
696 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
697             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
698                 return FALSE;
699             }
700             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
701 #endif
702             break;
703 #endif
704         default:
705             return false;
706             break;
707     }
709     return _finishSurfaceSetup (surface);
712 bool
713 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
715     if (_is_valid || !surface)
716         return false;
718     _vector_based_target = is_vector;
719     bool ret = _finishSurfaceSetup (surface);
720     if (ret)
721         cairo_surface_reference (surface);
722     return ret;
725 bool
726 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
728     if(surface == NULL) {
729         return FALSE;
730     }
731     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
732         return FALSE;
733     }
735     _cr = cairo_create(surface);
736     _surface = surface;
738     if (_vector_based_target) {
739         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
740     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
741         // set background color on non-alpha surfaces
742         // TODO: bgcolor should be derived from SPDocument
743         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
744         cairo_rectangle(_cr, 0, 0, _width, _height);
745         cairo_fill(_cr);
746     }
748     _is_valid = TRUE;
750     return true;
753 bool
754 CairoRenderContext::finish(void)
756     g_assert( _is_valid );
758     if (_vector_based_target)
759         cairo_show_page(_cr);
761     cairo_destroy(_cr);
762     cairo_surface_destroy(_surface);
763     _cr = NULL;
764     _surface = NULL;
766     if (_layout)
767         g_object_unref(_layout);
769     _is_valid = FALSE;
771     if (_vector_based_target && _stream) {
772         /* Flush stream to be sure. */
773         (void) fflush(_stream);
775         fclose(_stream);
776         _stream = NULL;
777     }
779     return true;
782 void
783 CairoRenderContext::transform(NRMatrix const *transform)
785     g_assert( _is_valid );
787     cairo_matrix_t matrix;
788     _initCairoMatrix(&matrix, transform);
789     cairo_transform(_cr, &matrix);
791     // store new CTM
792     getTransform(&_state->transform);
795 void
796 CairoRenderContext::setTransform(NRMatrix const *transform)
798     g_assert( _is_valid );
800     cairo_matrix_t matrix;
801     _initCairoMatrix(&matrix, transform);
802     cairo_set_matrix(_cr, &matrix);
803     _state->transform = *transform;
806 void
807 CairoRenderContext::getTransform(NRMatrix *copy) const
809     g_assert( _is_valid );
811     cairo_matrix_t ctm;
812     cairo_get_matrix(_cr, &ctm);
813     (*copy)[0] = ctm.xx;
814     (*copy)[1] = ctm.yx;
815     (*copy)[2] = ctm.xy;
816     (*copy)[3] = ctm.yy;
817     (*copy)[4] = ctm.x0;
818     (*copy)[5] = ctm.y0;
821 void
822 CairoRenderContext::getParentTransform(NRMatrix *copy) const
824     g_assert( _is_valid );
826     CairoRenderState *parent_state = getParentState();
827     memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
830 void
831 CairoRenderContext::pushState(void)
833     g_assert( _is_valid );
835     cairo_save(_cr);
837     CairoRenderState *new_state = _createState();
838     // copy current state's transform
839     new_state->transform = _state->transform;
840     _state_stack = g_slist_prepend(_state_stack, new_state);
841     _state = new_state;
844 void
845 CairoRenderContext::popState(void)
847     g_assert( _is_valid );
849     cairo_restore(_cr);
851     g_free(_state_stack->data);
852     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
853     _state = (CairoRenderState*)_state_stack->data;
855     g_assert( g_slist_length(_state_stack) > 0 );
858 static bool pattern_hasItemChildren (SPPattern *pat)
860     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
861         if (SP_IS_ITEM (child)) {
862             return true;
863         }
864     }
865     return false;
868 cairo_pattern_t*
869 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
871     g_assert( SP_IS_PATTERN(paintserver) );
873     SPPattern *pat = SP_PATTERN (paintserver);
875     NRMatrix ps2user, pcs2dev;
876     nr_matrix_set_identity(&ps2user);
877     nr_matrix_set_identity(&pcs2dev);
879     double x = pattern_x(pat);
880     double y = pattern_y(pat);
881     double width = pattern_width(pat);
882     double height = pattern_height(pat);
883     double bbox_width_scaler;
884     double bbox_height_scaler;
886     TRACE(("%f x %f pattern\n", width, height));
888     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
889         //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
890         bbox_width_scaler = pbox->x1 - pbox->x0;
891         bbox_height_scaler = pbox->y1 - pbox->y0;
892         ps2user[4] = x * bbox_width_scaler + pbox->x0;
893         ps2user[5] = y * bbox_height_scaler + pbox->y0;
894     } else {
895         bbox_width_scaler = 1.0;
896         bbox_height_scaler = 1.0;
897         ps2user[4] = x;
898         ps2user[5] = y;
899     }
901     // apply pattern transformation
902     NRMatrix pattern_transform(pattern_patternTransform(pat));
903     nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
905     // create pattern contents coordinate system
906     if (pat->viewBox_set) {
907         NRRect *view_box = pattern_viewBox(pat);
909         double x, y, w, h;
910         double view_width, view_height;
911         x = 0;
912         y = 0;
913         w = width * bbox_width_scaler;
914         h = height * bbox_height_scaler;
916         view_width = view_box->x1 - view_box->x0;
917         view_height = view_box->y1 - view_box->y0;
919         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
920         pcs2dev[0] = w / view_width;
921         pcs2dev[3] = h / view_height;
922         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
923         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
924     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
925         pcs2dev[0] = pbox->x1 - pbox->x0;
926         pcs2dev[3] = pbox->y1 - pbox->y0;
927     }
929     // calculate the size of the surface which has to be created
930     // the scaling needs to be taken into account in the ctm after the pattern transformation
931     NRMatrix temp;
932     nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
933     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
934     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
936     if (_vector_based_target) {
937         // eliminate PT_PER_PX mul from these
938         width_scaler *= 1.25;
939         height_scaler *= 1.25;
940     }
941     double surface_width = ceil(bbox_width_scaler * width_scaler * width);
942     double surface_height = ceil(bbox_height_scaler * height_scaler * height);
943     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
944     // create new rendering context
945     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
947     // adjust the size of the painted pattern to fit exactly the created surface
948     // this has to be done because of the rounding to obtain an integer pattern surface width/height
949     double scale_width = surface_width / (bbox_width_scaler * width);
950     double scale_height = surface_height / (bbox_height_scaler * height);
951     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
952         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
953         NRMatrix scale;
954         nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
955         nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
957         nr_matrix_set_scale(&scale, scale_width, scale_height);
958         nr_matrix_multiply(&ps2user, &ps2user, &scale);
959     }
961     pattern_ctx->setTransform(&pcs2dev);
962     pattern_ctx->pushState();
964     // create arena and group
965     NRArena *arena = NRArena::create();
966     unsigned dkey = sp_item_display_key_new(1);
968     // show items and render them
969     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
970         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
971             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
972                 if (SP_IS_ITEM (child)) {
973                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
974                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
975                 }
976             }
977             break; // do not go further up the chain if children are found
978         }
979     }
981     pattern_ctx->popState();
983     // setup a cairo_pattern_t
984     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
985     TEST(pattern_ctx->saveAsPng("pattern.png"));
986     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
987     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
989     // set pattern transformation
990     cairo_matrix_t pattern_matrix;
991     _initCairoMatrix(&pattern_matrix, &ps2user);
992     cairo_matrix_invert(&pattern_matrix);
993     cairo_pattern_set_matrix(result, &pattern_matrix);
995     delete pattern_ctx;
997     // hide all items
998     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
999         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1000             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1001                 if (SP_IS_ITEM (child)) {
1002                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1003                 }
1004             }
1005             break; // do not go further up the chain if children are found
1006         }
1007     }
1009     return result;
1012 cairo_pattern_t*
1013 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1014                                                  NRRect const *pbox, float alpha)
1016     cairo_pattern_t *pattern = NULL;
1017     bool apply_bbox2user = FALSE;
1019     if (SP_IS_LINEARGRADIENT (paintserver)) {
1021             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1023             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1025             NR::Point p1 (lg->x1.computed, lg->y1.computed);
1026             NR::Point p2 (lg->x2.computed, lg->y2.computed);
1027             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1028                 // convert to userspace
1029                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1030                 p1 *= bbox2user;
1031                 p2 *= bbox2user;
1032             }
1034             // create linear gradient pattern
1035             pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1037             // add stops
1038             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1039                 float rgb[3];
1040                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1041                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1042             }
1043     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1045         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1047         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1049         NR::Point c (rg->cx.computed, rg->cy.computed);
1050         NR::Point f (rg->fx.computed, rg->fy.computed);
1051         double r = rg->r.computed;
1052         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1053             apply_bbox2user = true;
1055         // create radial gradient pattern
1056         pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1058         // add stops
1059         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1060             float rgb[3];
1061             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1062             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1063         }
1064     } else if (SP_IS_PATTERN (paintserver)) {
1066         pattern = _createPatternPainter(paintserver, pbox);
1067     } else {
1068         return NULL;
1069     }
1071     if (pattern && SP_IS_GRADIENT (paintserver)) {
1072         SPGradient *g = SP_GRADIENT(paintserver);
1074         // set extend type
1075         SPGradientSpread spread = sp_gradient_get_spread(g);
1076         switch (spread) {
1077             case SP_GRADIENT_SPREAD_REPEAT: {
1078                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1079                 break;
1080             }
1081             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1082                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1083                 break;
1084             }
1085             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1086                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1087                 break;
1088             }
1089             default: {
1090                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1091                 break;
1092             }
1093         }
1095         cairo_matrix_t pattern_matrix;
1096         if (g->gradientTransform_set) {
1097             // apply gradient transformation
1098             cairo_matrix_init(&pattern_matrix,
1099                 g->gradientTransform[0], g->gradientTransform[1],
1100                 g->gradientTransform[2], g->gradientTransform[3],
1101                 g->gradientTransform[4], g->gradientTransform[5]);
1102         } else {
1103             cairo_matrix_init_identity (&pattern_matrix);
1104         }
1106         if (apply_bbox2user) {
1107             // convert to userspace
1108             cairo_matrix_t bbox2user;
1109             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1110             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1111         }
1112         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1113         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1114     }
1116     return pattern;
1119 void
1120 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1122     g_return_if_fail( style->fill.isColor()
1123                       || style->fill.isPaintserver() );
1125     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1126     if (_state->merge_opacity) {
1127         alpha *= _state->opacity;
1128         TRACE(("merged op=%f\n", alpha));
1129     }
1131     if (style->fill.isColor()) {
1132         float rgb[3];
1133         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1135         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1136     } else {
1137         g_assert( style->fill.isPaintserver()
1138                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1139                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1141         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1143         if (pattern) {
1144             cairo_set_source(_cr, pattern);
1145             cairo_pattern_destroy(pattern);
1146         }
1147     }
1150 void
1151 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1153     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1154     if (_state->merge_opacity)
1155         alpha *= _state->opacity;
1157     if (style->stroke.isColor()) {
1158         float rgb[3];
1159         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1161         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1162     } else {
1163         g_assert( style->fill.isPaintserver()
1164                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1165                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1167         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1169         if (pattern) {
1170             cairo_set_source(_cr, pattern);
1171             cairo_pattern_destroy(pattern);
1172         }
1173     }
1175     if (style->stroke_dash.n_dash   &&
1176         style->stroke_dash.dash       )
1177     {
1178         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1179     } else {
1180         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1181     }
1183     cairo_set_line_width(_cr, style->stroke_width.computed);
1185     // set line join type
1186     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1187     switch (style->stroke_linejoin.computed) {
1188         case SP_STROKE_LINEJOIN_MITER:
1189             join = CAIRO_LINE_JOIN_MITER;
1190             break;
1191         case SP_STROKE_LINEJOIN_ROUND:
1192             join = CAIRO_LINE_JOIN_ROUND;
1193             break;
1194         case SP_STROKE_LINEJOIN_BEVEL:
1195             join = CAIRO_LINE_JOIN_BEVEL;
1196             break;
1197     }
1198     cairo_set_line_join(_cr, join);
1200     // set line cap type
1201     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1202     switch (style->stroke_linecap.computed) {
1203         case SP_STROKE_LINECAP_BUTT:
1204             cap = CAIRO_LINE_CAP_BUTT;
1205             break;
1206         case SP_STROKE_LINECAP_ROUND:
1207             cap = CAIRO_LINE_CAP_ROUND;
1208             break;
1209         case SP_STROKE_LINECAP_SQUARE:
1210             cap = CAIRO_LINE_CAP_SQUARE;
1211             break;
1212     }
1213     cairo_set_line_cap(_cr, cap);
1214     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1217 bool
1218 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1220     g_assert( _is_valid );
1222     if (_render_mode == RENDER_MODE_CLIP) {
1223         if (_clip_mode == CLIP_MODE_PATH) {
1224             addClipPath(bpath->path, &style->fill_rule);
1225         } else {
1226             setBpath(bpath->path);
1227             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1228                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1229             } else {
1230                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1231             }
1232             cairo_fill(_cr);
1233             cairo_surface_write_to_png (_surface, "gtar2.png");
1234         }
1235         return true;
1236     }
1238     if (style->fill.isNone() && style->stroke.isNone())
1239         return true;
1241     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1242                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1244     if (!need_layer)
1245         cairo_save(_cr);
1246     else
1247         pushLayer();
1249     if (!style->fill.isNone()) {
1250         _setFillStyle(style, pbox);
1251         setBpath(bpath->path);
1253         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1254             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1255         } else {
1256             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1257         }
1259         if (style->stroke.isNone())
1260             cairo_fill(_cr);
1261         else
1262             cairo_fill_preserve(_cr);
1263     }
1265     if (!style->stroke.isNone()) {
1266         _setStrokeStyle(style, pbox);
1267         if (style->fill.isNone())
1268             setBpath(bpath->path);
1270         cairo_stroke(_cr);
1271     }
1273     if (need_layer)
1274         popLayer();
1275     else
1276         cairo_restore(_cr);
1278     return true;
1281 bool
1282 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1283                                 NRMatrix const *image_transform, SPStyle const *style)
1285     g_assert( _is_valid );
1287     if (_render_mode == RENDER_MODE_CLIP)
1288         return true;
1290     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1291     if (!px_rgba)
1292         return false;
1294     float opacity;
1295     if (_state->merge_opacity)
1296         opacity = _state->opacity;
1297     else
1298         opacity = 1.0;
1300     // make a copy of the original pixbuf with premultiplied alpha
1301     // if we pass the original pixbuf it will get messed up
1302     for (unsigned i = 0; i < h; i++) {
1303         for (unsigned j = 0; j < w; j++) {
1304             guchar const *src = px + i * rs + j * 4;
1305             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1306             guchar r, g, b, alpha_dst;
1308             // calculate opacity-modified alpha
1309             alpha_dst = src[3];
1310             if (opacity != 1.0 && _vector_based_target)
1311                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1313             // premul alpha (needed because this will be undone by cairo-pdf)
1314             r = src[0]*alpha_dst/255;
1315             g = src[1]*alpha_dst/255;
1316             b = src[2]*alpha_dst/255;
1318             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1319         }
1320     }
1322     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1323     if (cairo_surface_status(image_surface)) {
1324         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1325         return false;
1326     }
1328     // setup automatic freeing of the image data when destroying the surface
1329     static cairo_user_data_key_t key;
1330     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1332     cairo_save(_cr);
1334     // scaling by width & height is not needed because it will be done by Cairo
1335     if (image_transform)
1336         transform(image_transform);
1338     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1340     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1341     if (_vector_based_target) {
1342         cairo_new_path(_cr);
1343         cairo_rectangle(_cr, 0, 0, w, h);
1344         cairo_clip(_cr);
1345     }
1347     if (_vector_based_target)
1348         cairo_paint(_cr);
1349     else
1350         cairo_paint_with_alpha(_cr, opacity);
1352     cairo_restore(_cr);
1354     cairo_surface_destroy(image_surface);
1356     return true;
1359 #define GLYPH_ARRAY_SIZE 64
1361 unsigned int
1362 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1364     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1365     cairo_glyph_t *glyphs = glyph_array;
1366     unsigned int num_glyphs = glyphtext.size();
1367     if (num_glyphs > GLYPH_ARRAY_SIZE)
1368         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1370     unsigned int num_invalid_glyphs = 0;
1371     unsigned int i = 0;
1372     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1373         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1374         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1375         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1376             TRACE(("INVALID GLYPH found\n"));
1377             num_invalid_glyphs++;
1378             continue;
1379         }
1380         glyphs[i - num_invalid_glyphs].index = it_info->index;
1381         glyphs[i - num_invalid_glyphs].x = it_info->x;
1382         glyphs[i - num_invalid_glyphs].y = it_info->y;
1383         i++;
1384     }
1386     if (is_stroke || _is_texttopath)
1387         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1388     else
1389         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1391     if (num_glyphs > GLYPH_ARRAY_SIZE)
1392         g_free(glyphs);
1394     return num_glyphs - num_invalid_glyphs;
1397 bool
1398 CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1399                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1401     // create a cairo_font_face from PangoFont
1402     double size = style->font_size.computed;
1403     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1404     FcPattern *fc_pattern = fc_font->font_pattern;
1406     cairo_save(_cr);
1408 #ifndef PANGO_ENABLE_BACKEND
1409     cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1410     cairo_set_font_face(_cr, font_face);
1412     if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1413         size = 12.0;
1415     // set the given font matrix
1416     cairo_matrix_t matrix;
1417     _initCairoMatrix(&matrix, font_matrix);
1418     cairo_set_font_matrix(_cr, &matrix);
1420     if (_render_mode == RENDER_MODE_CLIP) {
1421         if (_clip_mode == CLIP_MODE_MASK) {
1422             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1423                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1424             } else {
1425                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1426             }
1427             cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
1428             cairo_rectangle (_cr, 0, 0, 30, 40);
1429             cairo_fill (_cr);
1430             _showGlyphs(_cr, font, glyphtext, FALSE);
1431             //cairo_fill(_cr);
1432         } else {
1433             // just add the glyph paths to the current context
1434             _showGlyphs(_cr, font, glyphtext, TRUE);
1435         }
1436     } else {
1438         if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
1439             // set fill style
1440             _setFillStyle(style, NULL);
1442             _showGlyphs(_cr, font, glyphtext, FALSE);
1443         }
1445         if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
1446             // set stroke style
1447             _setStrokeStyle(style, NULL);
1449             // paint stroke
1450             _showGlyphs(_cr, font, glyphtext, TRUE);
1451             cairo_stroke(_cr);
1452         }
1453     }
1455     cairo_restore(_cr);
1457     cairo_font_face_destroy(font_face);
1458 #else
1459     (void)size;
1460     (void)fc_pattern;
1461 #endif
1463     return true;
1466 /* Helper functions */
1468 void
1469 CairoRenderContext::addBpath(NArtBpath const *bp)
1471     bool closed = false;
1472     while (bp->code != NR_END) {
1473         switch (bp->code) {
1474             case NR_MOVETO:
1475                 if (closed) {
1476                     cairo_close_path(_cr);
1477                 }
1478                 closed = true;
1479                 cairo_move_to(_cr, bp->x3, bp->y3);
1480                 break;
1481             case NR_MOVETO_OPEN:
1482                 if (closed) {
1483                     cairo_close_path(_cr);
1484                 }
1485                 closed = false;
1486                 cairo_move_to(_cr, bp->x3, bp->y3);
1487                 break;
1488             case NR_LINETO:
1489                 cairo_line_to(_cr, bp->x3, bp->y3);
1490                 break;
1491             case NR_CURVETO:
1492                 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1493                 break;
1494             default:
1495                 break;
1496         }
1497         bp += 1;
1498     }
1499     if (closed) {
1500         cairo_close_path(_cr);
1501     }
1504 void
1505 CairoRenderContext::setBpath(NArtBpath const *bp)
1507     cairo_new_path(_cr);
1508     if (bp)
1509         addBpath(bp);
1512 void
1513 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1515     cairo_matrix_t matrix;
1517     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1518     cairo_transform(cr, &matrix);
1521 void
1522 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1524     matrix->xx = (*transform)[0];
1525     matrix->yx = (*transform)[1];
1526     matrix->xy = (*transform)[2];
1527     matrix->yy = (*transform)[3];
1528     matrix->x0 = (*transform)[4];
1529     matrix->y0 = (*transform)[5];
1532 void
1533 CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1535     _concatTransform(cr, (*transform)[0], (*transform)[1],
1536                      (*transform)[2], (*transform)[3],
1537                      (*transform)[4], (*transform)[5]);
1540 static cairo_status_t
1541 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1543     size_t written;
1544     FILE *file = (FILE*)closure;
1546     written = fwrite (data, 1, length, file);
1548     if (written == length)
1549         return CAIRO_STATUS_SUCCESS;
1550     else
1551         return CAIRO_STATUS_WRITE_ERROR;
1554 #include "clear-n_.h"
1556 }  /* namespace Internal */
1557 }  /* namespace Extension */
1558 }  /* namespace Inkscape */
1560 #undef TRACE
1561 #undef TEST
1563 /* End of GNU GPL code */
1566 /*
1567   Local Variables:
1568   mode:c++
1569   c-file-style:"stroustrup"
1570   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1571   indent-tabs-mode:nil
1572   fill-column:99
1573   End:
1574 */
1575 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :