Code

8f106f55fe2db6ad087d9d5e5b2e92dcbbdbde0b
[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     _stream(NULL),
108     _is_valid(FALSE),
109     _vector_based_target(FALSE),
110     _cr(NULL),
111     _surface(NULL),
112     _target(CAIRO_SURFACE_TYPE_IMAGE),
113     _target_format(CAIRO_FORMAT_ARGB32),
114     _layout(NULL),
115     _state(NULL),
116     _renderer(parent),
117     _render_mode(RENDER_MODE_NORMAL),
118     _clip_mode(CLIP_MODE_MASK)
119 {}
121 CairoRenderContext::~CairoRenderContext(void)
123     if (_cr) cairo_destroy(_cr);
124     if (_surface) cairo_surface_destroy(_surface);
125     if (_layout) g_object_unref(_layout);
128 CairoRenderer*
129 CairoRenderContext::getRenderer(void) const
131     return _renderer;
134 CairoRenderState*
135 CairoRenderContext::getCurrentState(void) const
137     return _state;
140 CairoRenderState*
141 CairoRenderContext::getParentState(void) const
143     // if this is the root node just return it
144     if (g_slist_length(_state_stack) == 1) {
145         return _state;
146     } else {
147         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
148     }
151 void
152 CairoRenderContext::setStateForStyle(SPStyle const *style)
154     // only opacity & overflow is stored for now
155     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
156     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
158     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
159         _state->merge_opacity = FALSE;
161     // disable rendering of opacity if there's a stroke on the fill
162     if (_state->merge_opacity
163         && !style->fill.isNone()
164         && !style->stroke.isNone())
165         _state->merge_opacity = FALSE;
168 /**
169  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
170  *
171  * \param width     width of the surface to be created
172  * \param height    height of the surface to be created
173  */
174 CairoRenderContext*
175 CairoRenderContext::cloneMe(double width, double height) const
177     g_assert( _is_valid );
178     g_assert( width > 0.0 && height > 0.0 );
180     CairoRenderContext *new_context = _renderer->createContext();
181     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
182                                                             (int)ceil(width), (int)ceil(height));
183     new_context->_cr = cairo_create(surface);
184     new_context->_surface = surface;
185     new_context->_is_valid = TRUE;
187     return new_context;
190 CairoRenderContext*
191 CairoRenderContext::cloneMe(void) const
193     g_assert( _is_valid );
195     return cloneMe(_width, _height);
198 bool
199 CairoRenderContext::setImageTarget(cairo_format_t format)
201     // format cannot be set on an already initialized surface
202     if (_is_valid)
203         return false;
205     switch (format) {
206         case CAIRO_FORMAT_ARGB32:
207         case CAIRO_FORMAT_RGB24:
208         case CAIRO_FORMAT_A8:
209         case CAIRO_FORMAT_A1:
210             _target_format = format;
211             _target = CAIRO_SURFACE_TYPE_IMAGE;
212             return true;
213             break;
214         default:
215             break;
216     }
218     return false;
221 bool
222 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
224 #ifndef CAIRO_HAS_PDF_SURFACE
225     return false;
226 #else
227     _target = CAIRO_SURFACE_TYPE_PDF;
228     _vector_based_target = TRUE;
229 #endif
231     FILE *osf = NULL;
232     FILE *osp = NULL;
234     gsize bytesRead = 0;
235     gsize bytesWritten = 0;
236     GError *error = NULL;
237     gchar *local_fn = g_filename_from_utf8(utf8_fn,
238                                            -1,  &bytesRead,  &bytesWritten, &error);
239     gchar const *fn = local_fn;
241     /* TODO: Replace the below fprintf's with something that does the right thing whether in
242     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
243     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
244     * return code.
245     */
246     if (fn != NULL) {
247         if (*fn == '|') {
248             fn += 1;
249             while (isspace(*fn)) fn += 1;
250 #ifndef WIN32
251             osp = popen(fn, "w");
252 #else
253             osp = _popen(fn, "w");
254 #endif
255             if (!osp) {
256                 fprintf(stderr, "inkscape: popen(%s): %s\n",
257                         fn, strerror(errno));
258                 return false;
259             }
260             _stream = osp;
261         } else if (*fn == '>') {
262             fn += 1;
263             while (isspace(*fn)) fn += 1;
264             Inkscape::IO::dump_fopen_call(fn, "K");
265             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
266             if (!osf) {
267                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
268                         fn, strerror(errno));
269                 return false;
270             }
271             _stream = osf;
272         } else {
273             /* put cwd stuff in here */
274             gchar *qn = ( *fn
275                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
276                 : g_strdup("lpr") );
277 #ifndef WIN32
278             osp = popen(qn, "w");
279 #else
280             osp = _popen(qn, "w");
281 #endif
282             if (!osp) {
283                 fprintf(stderr, "inkscape: popen(%s): %s\n",
284                         qn, strerror(errno));
285                 return false;
286             }
287             g_free(qn);
288             _stream = osp;
289         }
290     }
292     g_free(local_fn);
294     if (_stream) {
295         /* fixme: this is kinda icky */
296 #if !defined(_WIN32) && !defined(__WIN32__)
297         (void) signal(SIGPIPE, SIG_IGN);
298 #endif
299     }
301     return true;
304 bool
305 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
307 #ifndef CAIRO_HAS_PS_SURFACE
308     return false;
309 #else
310     _target = CAIRO_SURFACE_TYPE_PS;
311     _vector_based_target = TRUE;
312 #endif
314     FILE *osf = NULL;
315     FILE *osp = NULL;
317     gsize bytesRead = 0;
318     gsize bytesWritten = 0;
319     GError *error = NULL;
320     gchar *local_fn = g_filename_from_utf8(utf8_fn,
321                                            -1,  &bytesRead,  &bytesWritten, &error);
322     gchar const *fn = local_fn;
324     /* TODO: Replace the below fprintf's with something that does the right thing whether in
325     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
326     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
327     * return code.
328     */
329     if (fn != NULL) {
330         if (*fn == '|') {
331             fn += 1;
332             while (isspace(*fn)) fn += 1;
333 #ifndef WIN32
334             osp = popen(fn, "w");
335 #else
336             osp = _popen(fn, "w");
337 #endif
338             if (!osp) {
339                 fprintf(stderr, "inkscape: popen(%s): %s\n",
340                         fn, strerror(errno));
341                 return false;
342             }
343             _stream = osp;
344         } else if (*fn == '>') {
345             fn += 1;
346             while (isspace(*fn)) fn += 1;
347             Inkscape::IO::dump_fopen_call(fn, "K");
348             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
349             if (!osf) {
350                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
351                         fn, strerror(errno));
352                 return false;
353             }
354             _stream = osf;
355         } else {
356             /* put cwd stuff in here */
357             gchar *qn = ( *fn
358                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
359                 : g_strdup("lpr") );
360 #ifndef WIN32
361             osp = popen(qn, "w");
362 #else
363             osp = _popen(qn, "w");
364 #endif
365             if (!osp) {
366                 fprintf(stderr, "inkscape: popen(%s): %s\n",
367                         qn, strerror(errno));
368                 return false;
369             }
370             g_free(qn);
371             _stream = osp;
372         }
373     }
375     g_free(local_fn);
377     if (_stream) {
378         /* fixme: this is kinda icky */
379 #if !defined(_WIN32) && !defined(__WIN32__)
380         (void) signal(SIGPIPE, SIG_IGN);
381 #endif
382     }
384     return true;
387 cairo_surface_t*
388 CairoRenderContext::getSurface(void)
390     g_assert( _is_valid );
392     return _surface;
395 bool
396 CairoRenderContext::saveAsPng(const char *file_name)
398     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
399     if (status)
400         return false;
401     else
402         return true;
405 void
406 CairoRenderContext::setRenderMode(CairoRenderMode mode)
408     switch (mode) {
409         case RENDER_MODE_NORMAL:
410         case RENDER_MODE_CLIP:
411             _render_mode = mode;
412             break;
413         default:
414             _render_mode = RENDER_MODE_NORMAL;
415             break;
416     }
419 CairoRenderContext::CairoRenderMode
420 CairoRenderContext::getRenderMode(void) const
422     return _render_mode;
425 void
426 CairoRenderContext::setClipMode(CairoClipMode mode)
428     switch (mode) {
429         case CLIP_MODE_PATH:
430         case CLIP_MODE_MASK:
431             _clip_mode = mode;
432             break;
433         default:
434             _clip_mode = CLIP_MODE_PATH;
435             break;
436     }
439 CairoRenderContext::CairoClipMode
440 CairoRenderContext::getClipMode(void) const
442     return _clip_mode;
445 CairoRenderState*
446 CairoRenderContext::_createState(void)
448     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
449     g_assert( state != NULL );
451     state->merge_opacity = TRUE;
452     state->opacity = 1.0;
453     state->need_layer = FALSE;
454     state->has_overflow = FALSE;
455     state->parent_has_userspace = FALSE;
456     state->clip_path = NULL;
457     state->mask = NULL;
459     return state;
462 void
463 CairoRenderContext::pushLayer(void)
465     g_assert( _is_valid );
467     TRACE(("--pushLayer\n"));
468     cairo_push_group(_cr);
470     // clear buffer
471     if (!_vector_based_target) {
472         cairo_save(_cr);
473         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
474         cairo_paint(_cr);
475         cairo_restore(_cr);
476     }
479 void
480 CairoRenderContext::popLayer(void)
482     g_assert( _is_valid );
484     float opacity = _state->opacity;
485     TRACE(("--popLayer w/ %f\n", opacity));
487     // apply clipPath or mask if present
488     SPClipPath *clip_path = _state->clip_path;
489     SPMask *mask = _state->mask;
490     if (clip_path || mask) {
492         CairoRenderContext *clip_ctx = 0;
493         cairo_surface_t *clip_mask = 0;
495         if (clip_path) {
496             if (_render_mode == RENDER_MODE_CLIP)
497                 mask = NULL;    // disable mask when performing nested clipping
499             if (_vector_based_target) {
500                 setClipMode(CLIP_MODE_PATH);
501                 if (!mask) {
502                     cairo_pop_group_to_source(_cr);
503                     _renderer->applyClipPath(this, clip_path);
504                     if (opacity == 1.0)
505                         cairo_paint(_cr);
506                     else
507                         cairo_paint_with_alpha(_cr, opacity);
509                 } else {
510                     // the clipPath will be applied before masking
511                 }
512             } else {
514                 // setup a new rendering context
515                 clip_ctx = _renderer->createContext();
516                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
517                 clip_ctx->setClipMode(CLIP_MODE_MASK);
518                 if (!clip_ctx->setupSurface(_width, _height)) {
519                     TRACE(("setupSurface failed\n"));
520                     _renderer->destroyContext(clip_ctx);
521                     return;
522                 }
524                 // clear buffer
525                 cairo_save(clip_ctx->_cr);
526                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
527                 cairo_paint(clip_ctx->_cr);
528                 cairo_restore(clip_ctx->_cr);
530                 // if a mask won't be applied set opacity too
531                 if (!mask)
532                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
533                 else
534                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
536                 // copy over the correct CTM
537                 if (_state->parent_has_userspace)
538                     clip_ctx->setTransform(&getParentState()->transform);
539                 else
540                     clip_ctx->setTransform(&_state->transform);
542                 // apply the clip path
543                 clip_ctx->pushState();
544                 _renderer->applyClipPath(clip_ctx, clip_path);
545                 clip_ctx->popState();
547                 clip_mask = clip_ctx->getSurface();
548                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
550                 if (!mask) {
551                     cairo_pop_group_to_source(_cr);
552                     cairo_mask_surface(_cr, clip_mask, 0, 0);
553                     _renderer->destroyContext(clip_ctx);
554                 }
555             }
556         }
558         if (mask) {
559             // create rendering context for mask
560             CairoRenderContext *mask_ctx = _renderer->createContext();
561             mask_ctx->setupSurface(_width, _height);
563             // set rendering mode to normal
564             setRenderMode(RENDER_MODE_NORMAL);
566             // copy the correct CTM to mask context
567             if (_state->parent_has_userspace)
568                 mask_ctx->setTransform(&getParentState()->transform);
569             else
570                 mask_ctx->setTransform(&_state->transform);
572             // render mask contents to mask_ctx
573             _renderer->applyMask(mask_ctx, mask);
575             TEST(mask_ctx->saveAsPng("mask.png"));
577             // composite with clip mask
578             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
579                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
580                 _renderer->destroyContext(clip_ctx);
581             }
583             cairo_surface_t *mask_image = mask_ctx->getSurface();
584             int width = cairo_image_surface_get_width(mask_image);
585             int height = cairo_image_surface_get_height(mask_image);
586             int stride = cairo_image_surface_get_stride(mask_image);
587             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
589             // premultiply with opacity
590             if (_state->opacity != 1.0) {
591                 TRACE(("premul w/ %f\n", opacity));
592                 guint8 int_opacity = (guint8)(255 * opacity);
593                 for (int row = 0 ; row < height; row++) {
594                     unsigned char *row_data = pixels + (row * stride);
595                     for (int i = 0 ; i < width; i++) {
596                         guint32 *pixel = (guint32 *)row_data + i;
597                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
598                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
599                                 ((*pixel & 0x000000ff)      ) * 4688) *
600                                 int_opacity);
601                     }
602                 }
603             }
605             cairo_pop_group_to_source(_cr);
606             if (_clip_mode == CLIP_MODE_PATH) {
607                 // we have to do the clipping after cairo_pop_group_to_source
608                 _renderer->applyClipPath(this, clip_path);
609             }
610             // apply the mask onto the layer
611             cairo_mask_surface(_cr, mask_image, 0, 0);
612             _renderer->destroyContext(mask_ctx);
613         }
614     } else {
615         cairo_pop_group_to_source(_cr);
616         if (opacity == 1.0)
617             cairo_paint(_cr);
618         else
619             cairo_paint_with_alpha(_cr, opacity);
620     }
623 void
624 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
626     g_assert( _is_valid );
628     // here it should be checked whether the current clip winding changed
629     // so we could switch back to masked clipping
630     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
631         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
632     } else {
633         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
634     }
635     addBpath(bp);
638 void
639 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
641     g_assert( _is_valid );
643     cairo_rectangle(_cr, x, y, width, height);
644     cairo_clip(_cr);
647 bool
648 CairoRenderContext::setupSurface(double width, double height)
650     if (_vector_based_target && _stream == NULL)
651         return false;
653     cairo_surface_t *surface = NULL;
654     switch (_target) {
655         case CAIRO_SURFACE_TYPE_IMAGE:
656             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
657             break;
658 #ifdef CAIRO_HAS_PDF_SURFACE
659         case CAIRO_SURFACE_TYPE_PDF:
660             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
661             break;
662 #endif
663 #ifdef CAIRO_HAS_PS_SURFACE
664         case CAIRO_SURFACE_TYPE_PS:
665             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
666             break;
667 #endif
668         default:
669             return false;
670             break;
671     }
673     return _finishSurfaceSetup (surface);
676 bool
677 CairoRenderContext::setSurface(cairo_surface_t *surface)
679     if (_is_valid || !surface)
680         return false;
682     bool ret = _finishSurfaceSetup (surface);
683     if (ret)
684         cairo_surface_reference (surface);
685     return ret;
688 bool
689 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
691     _cr = cairo_create(surface);
692     _surface = surface;
694     if (_vector_based_target) {
695         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
696     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
697         // set background color on non-alpha surfaces
698         // TODO: bgcolor should be derived from SPDocument
699         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
700         cairo_rectangle(_cr, 0, 0, _width, _height);
701         cairo_fill(_cr);
702     }
704     _is_valid = TRUE;
706     return true;
709 bool
710 CairoRenderContext::finish(void)
712     g_assert( _is_valid );
714     if (_vector_based_target)
715         cairo_show_page(_cr);
717     cairo_destroy(_cr);
718     cairo_surface_destroy(_surface);
719     _cr = NULL;
720     _surface = NULL;
722     if (_layout)
723         g_object_unref(_layout);
725     _is_valid = FALSE;
727     if (_vector_based_target) {
728         /* Flush stream to be sure. */
729         (void) fflush(_stream);
731         fclose(_stream);
732         _stream = NULL;
733     }
735     return true;
738 void
739 CairoRenderContext::transform(NRMatrix const *transform)
741     g_assert( _is_valid );
743     cairo_matrix_t matrix;
744     _initCairoMatrix(&matrix, transform);
745     cairo_transform(_cr, &matrix);
747     // store new CTM
748     getTransform(&_state->transform);
751 void
752 CairoRenderContext::setTransform(NRMatrix const *transform)
754     g_assert( _is_valid );
756     cairo_matrix_t matrix;
757     _initCairoMatrix(&matrix, transform);
758     cairo_set_matrix(_cr, &matrix);
759     _state->transform = *transform;
762 void
763 CairoRenderContext::getTransform(NRMatrix *copy) const
765     g_assert( _is_valid );
767     cairo_matrix_t ctm;
768     cairo_get_matrix(_cr, &ctm);
769     (*copy)[0] = ctm.xx;
770     (*copy)[1] = ctm.yx;
771     (*copy)[2] = ctm.xy;
772     (*copy)[3] = ctm.yy;
773     (*copy)[4] = ctm.x0;
774     (*copy)[5] = ctm.y0;
777 void
778 CairoRenderContext::getParentTransform(NRMatrix *copy) const
780     g_assert( _is_valid );
782     CairoRenderState *parent_state = getParentState();
783     memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
786 void
787 CairoRenderContext::pushState(void)
789     g_assert( _is_valid );
791     cairo_save(_cr);
793     CairoRenderState *new_state = _createState();
794     // copy current state's transform
795     new_state->transform = _state->transform;
796     _state_stack = g_slist_prepend(_state_stack, new_state);
797     _state = new_state;
800 void
801 CairoRenderContext::popState(void)
803     g_assert( _is_valid );
805     cairo_restore(_cr);
807     g_free(_state_stack->data);
808     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
809     _state = (CairoRenderState*)_state_stack->data;
811     g_assert( g_slist_length(_state_stack) > 0 );
814 static bool pattern_hasItemChildren (SPPattern *pat)
816     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
817         if (SP_IS_ITEM (child)) {
818             return true;
819         }
820     }
821     return false;
824 cairo_pattern_t*
825 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
827     g_assert( SP_IS_PATTERN(paintserver) );
829     SPPattern *pat = SP_PATTERN (paintserver);
831     NRMatrix ps2user, pcs2dev;
832     nr_matrix_set_identity(&ps2user);
833     nr_matrix_set_identity(&pcs2dev);
835     double x = pattern_x(pat);
836     double y = pattern_y(pat);
837     double width = pattern_width(pat);
838     double height = pattern_height(pat);
839     double bbox_width_scaler;
840     double bbox_height_scaler;
842     TRACE(("%f x %f pattern\n", width, height));
844     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
845         //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
846         bbox_width_scaler = pbox->x1 - pbox->x0;
847         bbox_height_scaler = pbox->y1 - pbox->y0;
848         ps2user[4] = x * bbox_width_scaler + pbox->x0;
849         ps2user[5] = y * bbox_height_scaler + pbox->y0;
850     } else {
851         bbox_width_scaler = 1.0;
852         bbox_height_scaler = 1.0;
853         ps2user[4] = x;
854         ps2user[5] = y;
855     }
857     // apply pattern transformation
858     NRMatrix pattern_transform(pattern_patternTransform(pat));
859     nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
861     // create pattern contents coordinate system
862     if (pat->viewBox_set) {
863         NRRect *view_box = pattern_viewBox(pat);
865         double x, y, w, h;
866         double view_width, view_height;
867         x = 0;
868         y = 0;
869         w = width * bbox_width_scaler;
870         h = height * bbox_height_scaler;
872         view_width = view_box->x1 - view_box->x0;
873         view_height = view_box->y1 - view_box->y0;
875         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
876         pcs2dev[0] = w / view_width;
877         pcs2dev[3] = h / view_height;
878         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
879         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
880     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
881         pcs2dev[0] = pbox->x1 - pbox->x0;
882         pcs2dev[3] = pbox->y1 - pbox->y0;
883     }
885     // calculate the size of the surface which has to be created
886     // the scaling needs to be taken into account in the ctm after the pattern transformation
887     NRMatrix temp;
888     nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
889     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
890     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
892     if (_vector_based_target) {
893         // eliminate PT_PER_PX mul from these
894         width_scaler *= 1.25;
895         height_scaler *= 1.25;
896     }
897     double surface_width = ceil(bbox_width_scaler * width_scaler * width);
898     double surface_height = ceil(bbox_height_scaler * height_scaler * height);
899     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
900     // create new rendering context
901     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
903     // adjust the size of the painted pattern to fit exactly the created surface
904     // this has to be done because of the rounding to obtain an integer pattern surface width/height
905     double scale_width = surface_width / (bbox_width_scaler * width);
906     double scale_height = surface_height / (bbox_height_scaler * height);
907     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
908         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
909         NRMatrix scale;
910         nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
911         nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
913         nr_matrix_set_scale(&scale, scale_width, scale_height);
914         nr_matrix_multiply(&ps2user, &ps2user, &scale);
915     }
917     pattern_ctx->setTransform(&pcs2dev);
918     pattern_ctx->pushState();
920     // create arena and group
921     NRArena *arena = NRArena::create();
922     unsigned dkey = sp_item_display_key_new(1);
924     // show items and render them
925     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
926         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
927             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
928                 if (SP_IS_ITEM (child)) {
929                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
930                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
931                 }
932             }
933             break; // do not go further up the chain if children are found
934         }
935     }
937     pattern_ctx->popState();
939     // setup a cairo_pattern_t
940     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
941     TEST(pattern_ctx->saveAsPng("pattern.png"));
942     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
943     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
945     // set pattern transformation
946     cairo_matrix_t pattern_matrix;
947     _initCairoMatrix(&pattern_matrix, &ps2user);
948     cairo_matrix_invert(&pattern_matrix);
949     cairo_pattern_set_matrix(result, &pattern_matrix);
951     delete pattern_ctx;
953     // hide all items
954     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
955         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
956             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
957                 if (SP_IS_ITEM (child)) {
958                     sp_item_invoke_hide (SP_ITEM (child), dkey);
959                 }
960             }
961             break; // do not go further up the chain if children are found
962         }
963     }
965     return result;
968 cairo_pattern_t*
969 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
970                                                  NRRect const *pbox, float alpha)
972     cairo_pattern_t *pattern = NULL;
973     bool apply_bbox2user = FALSE;
975     if (SP_IS_LINEARGRADIENT (paintserver)) {
977             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
979             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
981             NR::Point p1 (lg->x1.computed, lg->y1.computed);
982             NR::Point p2 (lg->x2.computed, lg->y2.computed);
983             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
984                 // convert to userspace
985                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
986                 p1 *= bbox2user;
987                 p2 *= bbox2user;
988             }
990             // create linear gradient pattern
991             pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
993             // add stops
994             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
995                 float rgb[3];
996                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
997                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
998             }
999     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1001         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1003         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1005         NR::Point c (rg->cx.computed, rg->cy.computed);
1006         NR::Point f (rg->fx.computed, rg->fy.computed);
1007         double r = rg->r.computed;
1008         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1009             apply_bbox2user = true;
1011         // create radial gradient pattern
1012         pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1014         // add stops
1015         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1016             float rgb[3];
1017             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1018             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1019         }
1020     } else if (SP_IS_PATTERN (paintserver)) {
1022         pattern = _createPatternPainter(paintserver, pbox);
1023     } else {
1024         return NULL;
1025     }
1027     if (pattern && SP_IS_GRADIENT (paintserver)) {
1028         SPGradient *g = SP_GRADIENT(paintserver);
1030         // set extend type
1031         SPGradientSpread spread = sp_gradient_get_spread(g);
1032         switch (spread) {
1033             case SP_GRADIENT_SPREAD_REPEAT: {
1034                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1035                 break;
1036             }
1037             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1038                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1039                 break;
1040             }
1041             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1042                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1043                 break;
1044             }
1045             default: {
1046                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1047                 break;
1048             }
1049         }
1051         cairo_matrix_t pattern_matrix;
1052         if (g->gradientTransform_set) {
1053             // apply gradient transformation
1054             cairo_matrix_init(&pattern_matrix,
1055                 g->gradientTransform[0], g->gradientTransform[1],
1056                 g->gradientTransform[2], g->gradientTransform[3],
1057                 g->gradientTransform[4], g->gradientTransform[5]);
1058         } else {
1059             cairo_matrix_init_identity (&pattern_matrix);
1060         }
1062         if (apply_bbox2user) {
1063             // convert to userspace
1064             cairo_matrix_t bbox2user;
1065             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1066             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1067         }
1068         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1069         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1070     }
1072     return pattern;
1075 void
1076 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1078     g_return_if_fail( style->fill.isColor()
1079                       || style->fill.isPaintserver() );
1081     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1082     if (_state->merge_opacity) {
1083         alpha *= _state->opacity;
1084         TRACE(("merged op=%f\n", alpha));
1085     }
1087     if (style->fill.isColor()) {
1088         float rgb[3];
1089         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1091         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1092     } else {
1093         g_assert( style->fill.isPaintserver()
1094                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1095                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1097         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1099         if (pattern) {
1100             cairo_set_source(_cr, pattern);
1101             cairo_pattern_destroy(pattern);
1102         }
1103     }
1106 void
1107 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1109     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1110     if (_state->merge_opacity)
1111         alpha *= _state->opacity;
1113     if (style->stroke.isColor()) {
1114         float rgb[3];
1115         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1117         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1118     } else {
1119         g_assert( style->fill.isPaintserver()
1120                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1121                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1123         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1125         if (pattern) {
1126             cairo_set_source(_cr, pattern);
1127             cairo_pattern_destroy(pattern);
1128         }
1129     }
1131     if (style->stroke_dash.n_dash   &&
1132         style->stroke_dash.dash       )
1133     {
1134         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1135     } else {
1136         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1137     }
1139     cairo_set_line_width(_cr, style->stroke_width.computed);
1141     // set line join type
1142     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1143     switch (style->stroke_linejoin.computed) {
1144         case SP_STROKE_LINEJOIN_MITER:
1145             join = CAIRO_LINE_JOIN_MITER;
1146             break;
1147         case SP_STROKE_LINEJOIN_ROUND:
1148             join = CAIRO_LINE_JOIN_ROUND;
1149             break;
1150         case SP_STROKE_LINEJOIN_BEVEL:
1151             join = CAIRO_LINE_JOIN_BEVEL;
1152             break;
1153     }
1154     cairo_set_line_join(_cr, join);
1156     // set line cap type
1157     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1158     switch (style->stroke_linecap.computed) {
1159         case SP_STROKE_LINECAP_BUTT:
1160             cap = CAIRO_LINE_CAP_BUTT;
1161             break;
1162         case SP_STROKE_LINECAP_ROUND:
1163             cap = CAIRO_LINE_CAP_ROUND;
1164             break;
1165         case SP_STROKE_LINECAP_SQUARE:
1166             cap = CAIRO_LINE_CAP_SQUARE;
1167             break;
1168     }
1169     cairo_set_line_cap(_cr, cap);
1170     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1173 bool
1174 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1176     g_assert( _is_valid );
1178     if (_render_mode == RENDER_MODE_CLIP) {
1179         if (_clip_mode == CLIP_MODE_PATH) {
1180             addClipPath(bpath->path, &style->fill_rule);
1181         } else {
1182             setBpath(bpath->path);
1183             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1184                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1185             } else {
1186                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1187             }
1188             cairo_fill(_cr);
1189             cairo_surface_write_to_png (_surface, "gtar2.png");
1190         }
1191         return true;
1192     }
1194     if (style->fill.isNone() && style->stroke.isNone())
1195         return true;
1197     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1198                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1200     if (!need_layer)
1201         cairo_save(_cr);
1202     else
1203         pushLayer();
1205     if (!style->fill.isNone()) {
1206         _setFillStyle(style, pbox);
1207         setBpath(bpath->path);
1209         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1210             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1211         } else {
1212             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1213         }
1215         if (style->stroke.isNone())
1216             cairo_fill(_cr);
1217         else
1218             cairo_fill_preserve(_cr);
1219     }
1221     if (!style->stroke.isNone()) {
1222         _setStrokeStyle(style, pbox);
1223         if (style->fill.isNone())
1224             setBpath(bpath->path);
1226         cairo_stroke(_cr);
1227     }
1229     if (need_layer)
1230         popLayer();
1231     else
1232         cairo_restore(_cr);
1234     return true;
1237 bool
1238 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1239                                 NRMatrix const *image_transform, SPStyle const *style)
1241     g_assert( _is_valid );
1243     if (_render_mode == RENDER_MODE_CLIP)
1244         return true;
1246     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1247     if (!px_rgba)
1248         return false;
1250     float opacity;
1251     if (_state->merge_opacity)
1252         opacity = _state->opacity;
1253     else
1254         opacity = 1.0;
1256     // make a copy of the original pixbuf with premultiplied alpha
1257     // if we pass the original pixbuf it will get messed up
1258     for (unsigned i = 0; i < h; i++) {
1259         for (unsigned j = 0; j < w; j++) {
1260             guchar const *src = px + i * rs + j * 4;
1261             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1262             guchar r, g, b, alpha_dst;
1264             // calculate opacity-modified alpha
1265             alpha_dst = src[3];
1266             if (opacity != 1.0 && _vector_based_target)
1267                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1269             // premul alpha (needed because this will be undone by cairo-pdf)
1270             r = src[0]*alpha_dst/255;
1271             g = src[1]*alpha_dst/255;
1272             b = src[2]*alpha_dst/255;
1274             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1275         }
1276     }
1278     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1279     if (cairo_surface_status(image_surface)) {
1280         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1281         return false;
1282     }
1284     // setup automatic freeing of the image data when destroying the surface
1285     static cairo_user_data_key_t key;
1286     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1288     cairo_save(_cr);
1290     // scaling by width & height is not needed because it will be done by Cairo
1291     if (image_transform)
1292         transform(image_transform);
1294     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1296     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1297     if (_vector_based_target) {
1298         cairo_new_path(_cr);
1299         cairo_rectangle(_cr, 0, 0, w, h);
1300         cairo_clip(_cr);
1301     }
1303     if (_vector_based_target)
1304         cairo_paint(_cr);
1305     else
1306         cairo_paint_with_alpha(_cr, opacity);
1308     cairo_restore(_cr);
1310     cairo_surface_destroy(image_surface);
1312     return true;
1315 #define GLYPH_ARRAY_SIZE 64
1317 unsigned int
1318 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1320     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1321     cairo_glyph_t *glyphs = glyph_array;
1322     unsigned int num_glyphs = glyphtext.size();
1323     if (num_glyphs > GLYPH_ARRAY_SIZE)
1324         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1326     unsigned int num_invalid_glyphs = 0;
1327     unsigned int i = 0;
1328     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1329         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1330         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1331         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1332             TRACE(("INVALID GLYPH found\n"));
1333             num_invalid_glyphs++;
1334             continue;
1335         }
1336         glyphs[i - num_invalid_glyphs].index = it_info->index;
1337         glyphs[i - num_invalid_glyphs].x = it_info->x;
1338         glyphs[i - num_invalid_glyphs].y = it_info->y;
1339         i++;
1340     }
1342     if (is_stroke)
1343         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1344     else
1345         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1347     if (num_glyphs > GLYPH_ARRAY_SIZE)
1348         g_free(glyphs);
1350     return num_glyphs - num_invalid_glyphs;
1353 bool
1354 CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1355                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1357     // create a cairo_font_face from PangoFont
1358     double size = style->font_size.computed;
1359     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1360     FcPattern *fc_pattern = fc_font->font_pattern;
1362     cairo_save(_cr);
1364 #ifndef PANGO_ENABLE_BACKEND
1365     cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1366     cairo_set_font_face(_cr, font_face);
1368     if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1369         size = 12.0;
1371     // set the given font matrix
1372     cairo_matrix_t matrix;
1373     _initCairoMatrix(&matrix, font_matrix);
1374     cairo_set_font_matrix(_cr, &matrix);
1376     if (_render_mode == RENDER_MODE_CLIP) {
1377         if (_clip_mode == CLIP_MODE_MASK) {
1378             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1379                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1380             } else {
1381                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1382             }
1383             cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
1384             cairo_rectangle (_cr, 0, 0, 30, 40);
1385             cairo_fill (_cr);
1386             _showGlyphs(_cr, font, glyphtext, FALSE);
1387             //cairo_fill(_cr);
1388         } else {
1389             // just add the glyph paths to the current context
1390             _showGlyphs(_cr, font, glyphtext, TRUE);
1391         }
1392     } else {
1394         if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
1395             // set fill style
1396             _setFillStyle(style, NULL);
1398             _showGlyphs(_cr, font, glyphtext, FALSE);
1399         }
1401         if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
1402             // set stroke style
1403             _setStrokeStyle(style, NULL);
1405             // paint stroke
1406             _showGlyphs(_cr, font, glyphtext, TRUE);
1407             cairo_stroke(_cr);
1408         }
1409     }
1411     cairo_restore(_cr);
1413     cairo_font_face_destroy(font_face);
1414 #else
1415     (void)size;
1416     (void)fc_pattern;
1417 #endif
1419     return true;
1422 /* Helper functions */
1424 void
1425 CairoRenderContext::addBpath(NArtBpath const *bp)
1427     bool closed = false;
1428     while (bp->code != NR_END) {
1429         switch (bp->code) {
1430             case NR_MOVETO:
1431                 if (closed) {
1432                     cairo_close_path(_cr);
1433                 }
1434                 closed = true;
1435                 cairo_move_to(_cr, bp->x3, bp->y3);
1436                 break;
1437             case NR_MOVETO_OPEN:
1438                 if (closed) {
1439                     cairo_close_path(_cr);
1440                 }
1441                 closed = false;
1442                 cairo_move_to(_cr, bp->x3, bp->y3);
1443                 break;
1444             case NR_LINETO:
1445                 cairo_line_to(_cr, bp->x3, bp->y3);
1446                 break;
1447             case NR_CURVETO:
1448                 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1449                 break;
1450             default:
1451                 break;
1452         }
1453         bp += 1;
1454     }
1455     if (closed) {
1456         cairo_close_path(_cr);
1457     }
1460 void
1461 CairoRenderContext::setBpath(NArtBpath const *bp)
1463     cairo_new_path(_cr);
1464     if (bp)
1465         addBpath(bp);
1468 void
1469 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1471     cairo_matrix_t matrix;
1473     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1474     cairo_transform(cr, &matrix);
1477 void
1478 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1480     matrix->xx = (*transform)[0];
1481     matrix->yx = (*transform)[1];
1482     matrix->xy = (*transform)[2];
1483     matrix->yy = (*transform)[3];
1484     matrix->x0 = (*transform)[4];
1485     matrix->y0 = (*transform)[5];
1488 void
1489 CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1491     _concatTransform(cr, (*transform)[0], (*transform)[1],
1492                      (*transform)[2], (*transform)[3],
1493                      (*transform)[4], (*transform)[5]);
1496 static cairo_status_t
1497 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1499     size_t written;
1500     FILE *file = (FILE*)closure;
1502     written = fwrite (data, 1, length, file);
1504     if (written == length)
1505         return CAIRO_STATUS_SUCCESS;
1506     else
1507         return CAIRO_STATUS_WRITE_ERROR;
1510 #include "clear-n_.h"
1512 }  /* namespace Internal */
1513 }  /* namespace Extension */
1514 }  /* namespace Inkscape */
1516 #undef TRACE
1517 #undef TEST
1519 /* End of GNU GPL code */
1522 /*
1523   Local Variables:
1524   mode:c++
1525   c-file-style:"stroustrup"
1526   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1527   indent-tabs-mode:nil
1528   fill-column:99
1529   End:
1530 */
1531 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :