Code

Merged from Poppler's Gfx.cc; Unset the font if it doesn't exist or we can not load...
[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 #ifdef HAVE_CAIRO_PDF
21 #ifndef PANGO_ENABLE_BACKEND
22 #define PANGO_ENABLE_BACKEND
23 #endif
25 #ifndef PANGO_ENABLE_ENGINE
26 #define PANGO_ENABLE_ENGINE
27 #endif
30 #include <signal.h>
31 #include <errno.h>
33 #include <libnr/n-art-bpath.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-matrix-fns.h>
36 #include <libnr/nr-matrix-translate-ops.h>
37 #include <libnr/nr-scale-matrix-ops.h>
39 #include <glib/gmem.h>
41 #include <glibmm/i18n.h>
42 #include "display/nr-arena.h"
43 #include "display/nr-arena-item.h"
44 #include "display/nr-arena-group.h"
45 #include "display/curve.h"
46 #include "display/canvas-bpath.h"
47 #include "sp-item.h"
48 #include "sp-item-group.h"
49 #include "style.h"
50 #include "sp-linear-gradient.h"
51 #include "sp-radial-gradient.h"
52 #include "sp-pattern.h"
53 #include "sp-mask.h"
54 #include "sp-clippath.h"
56 #include <unit-constants.h>
58 #include "cairo-render-context.h"
59 #include "cairo-renderer.h"
60 #include "extension/system.h"
62 #include "io/sys.h"
64 #include <cairo.h>
66 // include support for only the compiled-in surface types
67 #ifdef CAIRO_HAS_PDF_SURFACE
68 #include <cairo-pdf.h>
69 #endif
70 #ifdef CAIRO_HAS_PS_SURFACE
71 #include <cairo-ps.h>
72 #endif
75 #ifndef PANGO_ENABLE_BACKEND
76 #include <cairo-ft.h>
77 #endif
79 #include <pango/pangofc-fontmap.h>
81 //#define TRACE(_args) g_printf _args
82 #define TRACE(_args)
83 //#define TEST(_args) _args
84 #define TEST(_args)
86 // FIXME: expose these from sp-clippath/mask.cpp
87 struct SPClipPathView {
88     SPClipPathView *next;
89     unsigned int key;
90     NRArenaItem *arenaitem;
91     NRRect bbox;
92 };
94 struct SPMaskView {
95     SPMaskView *next;
96     unsigned int key;
97     NRArenaItem *arenaitem;
98     NRRect bbox;
99 };
101 namespace Inkscape {
102 namespace Extension {
103 namespace Internal {
105 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
107 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
108     _dpi(72),
109     _stream(NULL),
110     _is_valid(FALSE),
111     _vector_based_target(FALSE),
112     _cr(NULL),
113     _surface(NULL),
114     _target(CAIRO_SURFACE_TYPE_IMAGE),
115     _target_format(CAIRO_FORMAT_ARGB32),
116     _layout(NULL),
117     _state(NULL),
118     _renderer(parent),
119     _render_mode(RENDER_MODE_NORMAL),
120     _clip_mode(CLIP_MODE_MASK)
121 {}
123 CairoRenderContext::~CairoRenderContext(void)
125     if (_cr) cairo_destroy(_cr);
126     if (_surface) cairo_surface_destroy(_surface);
127     if (_layout) g_object_unref(_layout);
130 CairoRenderer*
131 CairoRenderContext::getRenderer(void) const
133     return _renderer;
136 CairoRenderState*
137 CairoRenderContext::getCurrentState(void) const
139     return _state;
142 CairoRenderState*
143 CairoRenderContext::getParentState(void) const
145     // if this is the root node just return it
146     if (g_slist_length(_state_stack) == 1) {
147         return _state;
148     } else {
149         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
150     }
153 void
154 CairoRenderContext::setStateForStyle(SPStyle const *style)
156     // only opacity & overflow is stored for now
157     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
158     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
160     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
161         _state->merge_opacity = FALSE;
163     // disable rendering of opacity if there's a stroke on the fill
164     if (_state->merge_opacity
165         && !style->fill.isNone()
166         && !style->stroke.isNone())
167         _state->merge_opacity = FALSE;
170 /**
171  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
172  *
173  * \param width     width of the surface to be created
174  * \param height    height of the surface to be created
175  */
176 CairoRenderContext*
177 CairoRenderContext::cloneMe(double width, double height) const
179     g_assert( _is_valid );
180     g_assert( width > 0.0 && height > 0.0 );
182     CairoRenderContext *new_context = _renderer->createContext();
183     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
184                                                             (int)ceil(width), (int)ceil(height));
185     new_context->_cr = cairo_create(surface);
186     new_context->_surface = surface;
187     new_context->_is_valid = TRUE;
189     return new_context;
192 CairoRenderContext*
193 CairoRenderContext::cloneMe(void) const
195     g_assert( _is_valid );
197     return cloneMe(_width, _height);
200 bool
201 CairoRenderContext::setImageTarget(cairo_format_t format)
203     // format cannot be set on an already initialized surface
204     if (_is_valid)
205         return false;
207     switch (format) {
208         case CAIRO_FORMAT_ARGB32:
209         case CAIRO_FORMAT_RGB24:
210         case CAIRO_FORMAT_A8:
211         case CAIRO_FORMAT_A1:
212             _target_format = format;
213             _target = CAIRO_SURFACE_TYPE_IMAGE;
214             return true;
215             break;
216         default:
217             break;
218     }
220     return false;
223 bool
224 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
226 #ifndef CAIRO_HAS_PDF_SURFACE
227     return false;
228 #else
229     _target = CAIRO_SURFACE_TYPE_PDF;
230     _vector_based_target = TRUE;
231 #endif
233     FILE *osf = NULL;
234     FILE *osp = NULL;
236     gsize bytesRead = 0;
237     gsize bytesWritten = 0;
238     GError *error = NULL;
239     gchar *local_fn = g_filename_from_utf8(utf8_fn,
240                                            -1,  &bytesRead,  &bytesWritten, &error);
241     gchar const *fn = local_fn;
243     /* TODO: Replace the below fprintf's with something that does the right thing whether in
244     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
245     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
246     * return code.
247     */
248     if (fn != NULL) {
249         if (*fn == '|') {
250             fn += 1;
251             while (isspace(*fn)) fn += 1;
252 #ifndef WIN32
253             osp = popen(fn, "w");
254 #else
255             osp = _popen(fn, "w");
256 #endif
257             if (!osp) {
258                 fprintf(stderr, "inkscape: popen(%s): %s\n",
259                         fn, strerror(errno));
260                 return false;
261             }
262             _stream = osp;
263         } else if (*fn == '>') {
264             fn += 1;
265             while (isspace(*fn)) fn += 1;
266             Inkscape::IO::dump_fopen_call(fn, "K");
267             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
268             if (!osf) {
269                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
270                         fn, strerror(errno));
271                 return false;
272             }
273             _stream = osf;
274         } else {
275             /* put cwd stuff in here */
276             gchar *qn = ( *fn
277                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
278                 : g_strdup("lpr") );
279 #ifndef WIN32
280             osp = popen(qn, "w");
281 #else
282             osp = _popen(qn, "w");
283 #endif
284             if (!osp) {
285                 fprintf(stderr, "inkscape: popen(%s): %s\n",
286                         qn, strerror(errno));
287                 return false;
288             }
289             g_free(qn);
290             _stream = osp;
291         }
292     }
294     g_free(local_fn);
296     if (_stream) {
297         /* fixme: this is kinda icky */
298 #if !defined(_WIN32) && !defined(__WIN32__)
299         (void) signal(SIGPIPE, SIG_IGN);
300 #endif
301     }
303     return true;
306 bool
307 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
309 #ifndef CAIRO_HAS_PS_SURFACE
310     return false;
311 #else
312     _target = CAIRO_SURFACE_TYPE_PS;
313     _vector_based_target = TRUE;
314 #endif
316     FILE *osf = NULL;
317     FILE *osp = NULL;
319     gsize bytesRead = 0;
320     gsize bytesWritten = 0;
321     GError *error = NULL;
322     gchar *local_fn = g_filename_from_utf8(utf8_fn,
323                                            -1,  &bytesRead,  &bytesWritten, &error);
324     gchar const *fn = local_fn;
326     /* TODO: Replace the below fprintf's with something that does the right thing whether in
327     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
328     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
329     * return code.
330     */
331     if (fn != NULL) {
332         if (*fn == '|') {
333             fn += 1;
334             while (isspace(*fn)) fn += 1;
335 #ifndef WIN32
336             osp = popen(fn, "w");
337 #else
338             osp = _popen(fn, "w");
339 #endif
340             if (!osp) {
341                 fprintf(stderr, "inkscape: popen(%s): %s\n",
342                         fn, strerror(errno));
343                 return false;
344             }
345             _stream = osp;
346         } else if (*fn == '>') {
347             fn += 1;
348             while (isspace(*fn)) fn += 1;
349             Inkscape::IO::dump_fopen_call(fn, "K");
350             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
351             if (!osf) {
352                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
353                         fn, strerror(errno));
354                 return false;
355             }
356             _stream = osf;
357         } else {
358             /* put cwd stuff in here */
359             gchar *qn = ( *fn
360                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
361                 : g_strdup("lpr") );
362 #ifndef WIN32
363             osp = popen(qn, "w");
364 #else
365             osp = _popen(qn, "w");
366 #endif
367             if (!osp) {
368                 fprintf(stderr, "inkscape: popen(%s): %s\n",
369                         qn, strerror(errno));
370                 return false;
371             }
372             g_free(qn);
373             _stream = osp;
374         }
375     }
377     g_free(local_fn);
379     if (_stream) {
380         /* fixme: this is kinda icky */
381 #if !defined(_WIN32) && !defined(__WIN32__)
382         (void) signal(SIGPIPE, SIG_IGN);
383 #endif
384     }
386     return true;
389 cairo_surface_t*
390 CairoRenderContext::getSurface(void)
392     g_assert( _is_valid );
394     return _surface;
397 bool
398 CairoRenderContext::saveAsPng(const char *file_name)
400     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
401     if (status)
402         return false;
403     else
404         return true;
407 void
408 CairoRenderContext::setRenderMode(CairoRenderMode mode)
410     switch (mode) {
411         case RENDER_MODE_NORMAL:
412         case RENDER_MODE_CLIP:
413             _render_mode = mode;
414             break;
415         default:
416             _render_mode = RENDER_MODE_NORMAL;
417             break;
418     }
421 CairoRenderContext::CairoRenderMode
422 CairoRenderContext::getRenderMode(void) const
424     return _render_mode;
427 void
428 CairoRenderContext::setClipMode(CairoClipMode mode)
430     switch (mode) {
431         case CLIP_MODE_PATH:
432         case CLIP_MODE_MASK:
433             _clip_mode = mode;
434             break;
435         default:
436             _clip_mode = CLIP_MODE_PATH;
437             break;
438     }
441 CairoRenderContext::CairoClipMode
442 CairoRenderContext::getClipMode(void) const
444     return _clip_mode;
447 CairoRenderState*
448 CairoRenderContext::_createState(void)
450     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
451     g_assert( state != NULL );
453     state->merge_opacity = TRUE;
454     state->opacity = 1.0;
455     state->need_layer = FALSE;
456     state->has_overflow = FALSE;
457     state->parent_has_userspace = FALSE;
458     state->clip_path = NULL;
459     state->mask = NULL;
461     return state;
464 void
465 CairoRenderContext::pushLayer(void)
467     g_assert( _is_valid );
469     TRACE(("--pushLayer\n"));
470     cairo_push_group(_cr);
472     // clear buffer
473     if (!_vector_based_target) {
474         cairo_save(_cr);
475         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
476         cairo_paint(_cr);
477         cairo_restore(_cr);
478     }
481 void
482 CairoRenderContext::popLayer(void)
484     g_assert( _is_valid );
486     float opacity = _state->opacity;
487     TRACE(("--popLayer w/ %f\n", opacity));
489     // apply clipPath or mask if present
490     SPClipPath *clip_path = _state->clip_path;
491     SPMask *mask = _state->mask;
492     if (clip_path || mask) {
494         CairoRenderContext *clip_ctx = 0;
495         cairo_surface_t *clip_mask = 0;
497         if (clip_path) {
498             if (_render_mode == RENDER_MODE_CLIP)
499                 mask = NULL;    // disable mask when performing nested clipping
501             if (_vector_based_target) {
502                 setClipMode(CLIP_MODE_PATH);
503                 if (!mask) {
504                     cairo_pop_group_to_source(_cr);
505                     _renderer->applyClipPath(this, clip_path);
506                     if (opacity == 1.0)
507                         cairo_paint(_cr);
508                     else
509                         cairo_paint_with_alpha(_cr, opacity);
511                 } else {
512                     // the clipPath will be applied before masking
513                 }
514             } else {
516                 // setup a new rendering context
517                 clip_ctx = _renderer->createContext();
518                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
519                 clip_ctx->setClipMode(CLIP_MODE_MASK);
520                 if (!clip_ctx->setupSurface(_width, _height)) {
521                     TRACE(("setupSurface failed\n"));
522                     _renderer->destroyContext(clip_ctx);
523                     return;
524                 }
526                 // clear buffer
527                 cairo_save(clip_ctx->_cr);
528                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
529                 cairo_paint(clip_ctx->_cr);
530                 cairo_restore(clip_ctx->_cr);
532                 // if a mask won't be applied set opacity too
533                 if (!mask)
534                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
535                 else
536                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
538                 // copy over the correct CTM
539                 if (_state->parent_has_userspace)
540                     clip_ctx->setTransform(&getParentState()->transform);
541                 else
542                     clip_ctx->setTransform(&_state->transform);
544                 // apply the clip path
545                 clip_ctx->pushState();
546                 _renderer->applyClipPath(clip_ctx, clip_path);
547                 clip_ctx->popState();
549                 clip_mask = clip_ctx->getSurface();
550                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
552                 if (!mask) {
553                     cairo_pop_group_to_source(_cr);
554                     cairo_mask_surface(_cr, clip_mask, 0, 0);
555                     _renderer->destroyContext(clip_ctx);
556                 }
557             }
558         }
560         if (mask) {
561             // create rendering context for mask
562             CairoRenderContext *mask_ctx = _renderer->createContext();
563             mask_ctx->setupSurface(_width, _height);
565             // set rendering mode to normal
566             setRenderMode(RENDER_MODE_NORMAL);
568             // copy the correct CTM to mask context
569             if (_state->parent_has_userspace)
570                 mask_ctx->setTransform(&getParentState()->transform);
571             else
572                 mask_ctx->setTransform(&_state->transform);
574             // render mask contents to mask_ctx
575             _renderer->applyMask(mask_ctx, mask);
577             TEST(mask_ctx->saveAsPng("mask.png"));
579             // composite with clip mask
580             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
581                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
582                 _renderer->destroyContext(clip_ctx);
583             }
585             cairo_surface_t *mask_image = mask_ctx->getSurface();
586             int width = cairo_image_surface_get_width(mask_image);
587             int height = cairo_image_surface_get_height(mask_image);
588             int stride = cairo_image_surface_get_stride(mask_image);
589             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
591             // premultiply with opacity
592             if (_state->opacity != 1.0) {
593                 TRACE(("premul w/ %f\n", opacity));
594                 guint8 int_opacity = (guint8)(255 * opacity);
595                 for (int row = 0 ; row < height; row++) {
596                     unsigned char *row_data = pixels + (row * stride);
597                     for (int i = 0 ; i < width; i++) {
598                         guint32 *pixel = (guint32 *)row_data + i;
599                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
600                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
601                                 ((*pixel & 0x000000ff)      ) * 4688) *
602                                 int_opacity);
603                     }
604                 }
605             }
607             cairo_pop_group_to_source(_cr);
608             if (_clip_mode == CLIP_MODE_PATH) {
609                 // we have to do the clipping after cairo_pop_group_to_source
610                 _renderer->applyClipPath(this, clip_path);
611             }
612             // apply the mask onto the layer
613             cairo_mask_surface(_cr, mask_image, 0, 0);
614             _renderer->destroyContext(mask_ctx);
615         }
616     } else {
617         cairo_pop_group_to_source(_cr);
618         if (opacity == 1.0)
619             cairo_paint(_cr);
620         else
621             cairo_paint_with_alpha(_cr, opacity);
622     }
625 void
626 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
628     g_assert( _is_valid );
630     // here it should be checked whether the current clip winding changed
631     // so we could switch back to masked clipping
632     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
633         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
634     } else {
635         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
636     }
637     addBpath(bp);
640 void
641 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
643     g_assert( _is_valid );
645     cairo_rectangle(_cr, x, y, width, height);
646     cairo_clip(_cr);
649 bool
650 CairoRenderContext::setupSurface(double width, double height)
652     if (_vector_based_target && _stream == NULL)
653         return false;
655     cairo_surface_t *surface = NULL;
656     switch (_target) {
657         case CAIRO_SURFACE_TYPE_IMAGE:
658             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
659             break;
660 #ifdef CAIRO_HAS_PDF_SURFACE
661         case CAIRO_SURFACE_TYPE_PDF:
662             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
663             break;
664 #endif
665 #ifdef CAIRO_HAS_PS_SURFACE
666         case CAIRO_SURFACE_TYPE_PS:
667             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
668             break;
669 #endif
670         default:
671             return false;
672             break;
673     }
675     _cr = cairo_create(surface);
676     _surface = surface;
678     if (_vector_based_target) {
679         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
680     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
681         // set background color on non-alpha surfaces
682         // TODO: bgcolor should be derived from SPDocument
683         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
684         cairo_rectangle(_cr, 0, 0, _width, _height);
685         cairo_fill(_cr);
686     }
688     _is_valid = TRUE;
690     return true;
693 bool
694 CairoRenderContext::finish(void)
696     g_assert( _is_valid );
698     if (_vector_based_target)
699         cairo_show_page(_cr);
701     cairo_destroy(_cr);
702     cairo_surface_destroy(_surface);
703     _cr = NULL;
704     _surface = NULL;
706     if (_layout)
707         g_object_unref(_layout);
709     _is_valid = FALSE;
711     if (_vector_based_target) {
712         /* Flush stream to be sure. */
713         (void) fflush(_stream);
715         fclose(_stream);
716         _stream = NULL;
717     }
719     return true;
722 void
723 CairoRenderContext::transform(NRMatrix const *transform)
725     g_assert( _is_valid );
727     cairo_matrix_t matrix;
728     _initCairoMatrix(&matrix, transform);
729     cairo_transform(_cr, &matrix);
731     // store new CTM
732     getTransform(&_state->transform);
735 void
736 CairoRenderContext::setTransform(NRMatrix const *transform)
738     g_assert( _is_valid );
740     cairo_matrix_t matrix;
741     _initCairoMatrix(&matrix, transform);
742     cairo_set_matrix(_cr, &matrix);
743     _state->transform = *transform;
746 void
747 CairoRenderContext::getTransform(NRMatrix *copy) const
749     g_assert( _is_valid );
751     cairo_matrix_t ctm;
752     cairo_get_matrix(_cr, &ctm);
753     (*copy)[0] = ctm.xx;
754     (*copy)[1] = ctm.yx;
755     (*copy)[2] = ctm.xy;
756     (*copy)[3] = ctm.yy;
757     (*copy)[4] = ctm.x0;
758     (*copy)[5] = ctm.y0;
761 void
762 CairoRenderContext::getParentTransform(NRMatrix *copy) const
764     g_assert( _is_valid );
766     CairoRenderState *parent_state = getParentState();
767     memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
770 void
771 CairoRenderContext::pushState(void)
773     g_assert( _is_valid );
775     cairo_save(_cr);
777     CairoRenderState *new_state = _createState();
778     // copy current state's transform
779     new_state->transform = _state->transform;
780     _state_stack = g_slist_prepend(_state_stack, new_state);
781     _state = new_state;
784 void
785 CairoRenderContext::popState(void)
787     g_assert( _is_valid );
789     cairo_restore(_cr);
791     g_free(_state_stack->data);
792     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
793     _state = (CairoRenderState*)_state_stack->data;
795     g_assert( g_slist_length(_state_stack) > 0 );
798 static bool pattern_hasItemChildren (SPPattern *pat)
800     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
801         if (SP_IS_ITEM (child)) {
802             return true;
803         }
804     }
805     return false;
808 cairo_pattern_t*
809 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
811     g_assert( SP_IS_PATTERN(paintserver) );
813     SPPattern *pat = SP_PATTERN (paintserver);
815     NRMatrix ps2user, pcs2dev;
816     nr_matrix_set_identity(&ps2user);
817     nr_matrix_set_identity(&pcs2dev);
819     double x = pattern_x(pat);
820     double y = pattern_y(pat);
821     double width = pattern_width(pat);
822     double height = pattern_height(pat);
823     double bbox_width_scaler;
824     double bbox_height_scaler;
826     TRACE(("%f x %f pattern\n", width, height));
828     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
829         //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
830         bbox_width_scaler = pbox->x1 - pbox->x0;
831         bbox_height_scaler = pbox->y1 - pbox->y0;
832         ps2user[4] = x * bbox_width_scaler + pbox->x0;
833         ps2user[5] = y * bbox_height_scaler + pbox->y0;
834     } else {
835         bbox_width_scaler = 1.0;
836         bbox_height_scaler = 1.0;
837         ps2user[4] = x;
838         ps2user[5] = y;
839     }
841     // apply pattern transformation
842     NRMatrix pattern_transform(pattern_patternTransform(pat));
843     nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
845     // create pattern contents coordinate system
846     if (pat->viewBox_set) {
847         NRRect *view_box = pattern_viewBox(pat);
849         double x, y, w, h;
850         double view_width, view_height;
851         x = 0;
852         y = 0;
853         w = width * bbox_width_scaler;
854         h = height * bbox_height_scaler;
856         view_width = view_box->x1 - view_box->x0;
857         view_height = view_box->y1 - view_box->y0;
859         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
860         pcs2dev[0] = w / view_width;
861         pcs2dev[3] = h / view_height;
862         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
863         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
864     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
865         pcs2dev[0] = pbox->x1 - pbox->x0;
866         pcs2dev[3] = pbox->y1 - pbox->y0;
867     }
869     // calculate the size of the surface which has to be created
870     // the scaling needs to be taken into account in the ctm after the pattern transformation
871     NRMatrix temp;
872     nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
873     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
874     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
876     if (_vector_based_target) {
877         // eliminate PT_PER_PX mul from these
878         width_scaler *= 1.25;
879         height_scaler *= 1.25;
880     }
881     double surface_width = ceil(bbox_width_scaler * width_scaler * width);
882     double surface_height = ceil(bbox_height_scaler * height_scaler * height);
883     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
884     // create new rendering context
885     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
887     // adjust the size of the painted pattern to fit exactly the created surface
888     // this has to be done because of the rounding to obtain an integer pattern surface width/height
889     double scale_width = surface_width / (bbox_width_scaler * width);
890     double scale_height = surface_height / (bbox_height_scaler * height);
891     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
892         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
893         NRMatrix scale;
894         nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
895         nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
897         nr_matrix_set_scale(&scale, scale_width, scale_height);
898         nr_matrix_multiply(&ps2user, &ps2user, &scale);
899     }
901     pattern_ctx->setTransform(&pcs2dev);
902     pattern_ctx->pushState();
904     // create arena and group
905     NRArena *arena = NRArena::create();
906     unsigned dkey = sp_item_display_key_new(1);
908     // show items and render them
909     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
910         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
911             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
912                 if (SP_IS_ITEM (child)) {
913                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
914                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
915                 }
916             }
917             break; // do not go further up the chain if children are found
918         }
919     }
921     pattern_ctx->popState();
923     // setup a cairo_pattern_t
924     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
925     TEST(pattern_ctx->saveAsPng("pattern.png"));
926     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
927     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
929     // set pattern transformation
930     cairo_matrix_t pattern_matrix;
931     _initCairoMatrix(&pattern_matrix, &ps2user);
932     cairo_matrix_invert(&pattern_matrix);
933     cairo_pattern_set_matrix(result, &pattern_matrix);
935     delete pattern_ctx;
937     // hide all items
938     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
939         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
940             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
941                 if (SP_IS_ITEM (child)) {
942                     sp_item_invoke_hide (SP_ITEM (child), dkey);
943                 }
944             }
945             break; // do not go further up the chain if children are found
946         }
947     }
949     return result;
952 cairo_pattern_t*
953 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
954                                                  NRRect const *pbox, float alpha)
956     cairo_pattern_t *pattern = NULL;
957     bool apply_bbox2user = FALSE;
959     if (SP_IS_LINEARGRADIENT (paintserver)) {
961             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
963             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
965             NR::Point p1 (lg->x1.computed, lg->y1.computed);
966             NR::Point p2 (lg->x2.computed, lg->y2.computed);
967             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
968                 // convert to userspace
969                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
970                 p1 *= bbox2user;
971                 p2 *= bbox2user;
972             }
974             // create linear gradient pattern
975             pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
977             // add stops
978             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
979                 float rgb[3];
980                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
981                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
982             }
983     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
985         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
987         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
989         NR::Point c (rg->cx.computed, rg->cy.computed);
990         NR::Point f (rg->fx.computed, rg->fy.computed);
991         double r = rg->r.computed;
992         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
993             apply_bbox2user = true;
995         // create radial gradient pattern
996         pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
998         // add stops
999         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1000             float rgb[3];
1001             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1002             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1003         }
1004     } else if (SP_IS_PATTERN (paintserver)) {
1006         pattern = _createPatternPainter(paintserver, pbox);
1007     } else {
1008         return NULL;
1009     }
1011     if (pattern && SP_IS_GRADIENT (paintserver)) {
1012         SPGradient *g = SP_GRADIENT(paintserver);
1014         // set extend type
1015         SPGradientSpread spread = sp_gradient_get_spread(g);
1016         switch (spread) {
1017             case SP_GRADIENT_SPREAD_REPEAT: {
1018                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1019                 break;
1020             }
1021             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1022                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1023                 break;
1024             }
1025             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1026                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1027                 break;
1028             }
1029             default: {
1030                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1031                 break;
1032             }
1033         }
1035         cairo_matrix_t pattern_matrix;
1036         if (g->gradientTransform_set) {
1037             // apply gradient transformation
1038             cairo_matrix_init(&pattern_matrix,
1039                 g->gradientTransform[0], g->gradientTransform[1],
1040                 g->gradientTransform[2], g->gradientTransform[3],
1041                 g->gradientTransform[4], g->gradientTransform[5]);
1042         } else {
1043             cairo_matrix_init_identity (&pattern_matrix);
1044         }
1046         if (apply_bbox2user) {
1047             // convert to userspace
1048             cairo_matrix_t bbox2user;
1049             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1050             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1051         }
1052         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1053         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1054     }
1056     return pattern;
1059 void
1060 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1062     g_return_if_fail( style->fill.isColor()
1063                       || style->fill.isPaintserver() );
1065     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1066     if (_state->merge_opacity) {
1067         alpha *= _state->opacity;
1068         TRACE(("merged op=%f\n", alpha));
1069     }
1071     if (style->fill.isColor()) {
1072         float rgb[3];
1073         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1075         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1076     } else {
1077         g_assert( style->fill.isPaintserver()
1078                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1079                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1081         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1083         if (pattern) {
1084             cairo_set_source(_cr, pattern);
1085             cairo_pattern_destroy(pattern);
1086         }
1087     }
1090 void
1091 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1093     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1094     if (_state->merge_opacity)
1095         alpha *= _state->opacity;
1097     if (style->stroke.isColor()) {
1098         float rgb[3];
1099         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1101         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1102     } else {
1103         g_assert( style->fill.isPaintserver()
1104                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1105                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1107         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1109         if (pattern) {
1110             cairo_set_source(_cr, pattern);
1111             cairo_pattern_destroy(pattern);
1112         }
1113     }
1115     if (style->stroke_dash.n_dash   &&
1116         style->stroke_dash.dash       )
1117     {
1118         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1119     } else {
1120         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1121     }
1123     cairo_set_line_width(_cr, style->stroke_width.computed);
1125     // set line join type
1126     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1127     switch (style->stroke_linejoin.computed) {
1128         case SP_STROKE_LINEJOIN_MITER:
1129             join = CAIRO_LINE_JOIN_MITER;
1130             break;
1131         case SP_STROKE_LINEJOIN_ROUND:
1132             join = CAIRO_LINE_JOIN_ROUND;
1133             break;
1134         case SP_STROKE_LINEJOIN_BEVEL:
1135             join = CAIRO_LINE_JOIN_BEVEL;
1136             break;
1137     }
1138     cairo_set_line_join(_cr, join);
1140     // set line cap type
1141     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1142     switch (style->stroke_linecap.computed) {
1143         case SP_STROKE_LINECAP_BUTT:
1144             cap = CAIRO_LINE_CAP_BUTT;
1145             break;
1146         case SP_STROKE_LINECAP_ROUND:
1147             cap = CAIRO_LINE_CAP_ROUND;
1148             break;
1149         case SP_STROKE_LINECAP_SQUARE:
1150             cap = CAIRO_LINE_CAP_SQUARE;
1151             break;
1152     }
1153     cairo_set_line_cap(_cr, cap);
1154     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1157 bool
1158 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1160     g_assert( _is_valid );
1162     if (_render_mode == RENDER_MODE_CLIP) {
1163         if (_clip_mode == CLIP_MODE_PATH) {
1164             addClipPath(bpath->path, &style->fill_rule);
1165         } else {
1166             setBpath(bpath->path);
1167             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1168                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1169             } else {
1170                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1171             }
1172             cairo_fill(_cr);
1173             cairo_surface_write_to_png (_surface, "gtar2.png");
1174         }
1175         return true;
1176     }
1178     if (style->fill.isNone() && style->stroke.isNone())
1179         return true;
1181     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1182                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1184     if (!need_layer)
1185         cairo_save(_cr);
1186     else
1187         pushLayer();
1189     if (!style->fill.isNone()) {
1190         _setFillStyle(style, pbox);
1191         setBpath(bpath->path);
1193         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1194             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1195         } else {
1196             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1197         }
1199         if (style->stroke.isNone())
1200             cairo_fill(_cr);
1201         else
1202             cairo_fill_preserve(_cr);
1203     }
1205     if (!style->stroke.isNone()) {
1206         _setStrokeStyle(style, pbox);
1207         if (style->fill.isNone())
1208             setBpath(bpath->path);
1210         cairo_stroke(_cr);
1211     }
1213     if (need_layer)
1214         popLayer();
1215     else
1216         cairo_restore(_cr);
1218     return true;
1221 bool
1222 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1223                                 NRMatrix const *image_transform, SPStyle const *style)
1225     g_assert( _is_valid );
1227     if (_render_mode == RENDER_MODE_CLIP)
1228         return true;
1230     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1231     if (!px_rgba)
1232         return false;
1234     float opacity;
1235     if (_state->merge_opacity)
1236         opacity = _state->opacity;
1237     else
1238         opacity = 1.0;
1240     // make a copy of the original pixbuf with premultiplied alpha
1241     // if we pass the original pixbuf it will get messed up
1242     for (unsigned i = 0; i < h; i++) {
1243         for (unsigned j = 0; j < w; j++) {
1244             guchar const *src = px + i * rs + j * 4;
1245             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1246             guchar r, g, b, alpha_dst;
1248             // calculate opacity-modified alpha
1249             alpha_dst = src[3];
1250             if (opacity != 1.0 && _vector_based_target)
1251                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1253             // premul alpha (needed because this will be undone by cairo-pdf)
1254             r = src[0]*alpha_dst/255;
1255             g = src[1]*alpha_dst/255;
1256             b = src[2]*alpha_dst/255;
1258             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1259         }
1260     }
1262     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1263     if (cairo_surface_status(image_surface)) {
1264         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1265         return false;
1266     }
1268     // setup automatic freeing of the image data when destroying the surface
1269     static cairo_user_data_key_t key;
1270     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1272     cairo_save(_cr);
1274     // scaling by width & height is not needed because it will be done by Cairo
1275     if (image_transform)
1276         transform(image_transform);
1278     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1280     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1281     if (_vector_based_target) {
1282         cairo_new_path(_cr);
1283         cairo_rectangle(_cr, 0, 0, w, h);
1284         cairo_clip(_cr);
1285     }
1287     if (_vector_based_target)
1288         cairo_paint(_cr);
1289     else
1290         cairo_paint_with_alpha(_cr, opacity);
1292     cairo_restore(_cr);
1294     cairo_surface_destroy(image_surface);
1296     return true;
1299 #define GLYPH_ARRAY_SIZE 64
1301 unsigned int
1302 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1304     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1305     cairo_glyph_t *glyphs = glyph_array;
1306     unsigned int num_glyphs = glyphtext.size();
1307     if (num_glyphs > GLYPH_ARRAY_SIZE)
1308         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1310     unsigned int num_invalid_glyphs = 0;
1311     unsigned int i = 0;
1312     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1313         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1314         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1315         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1316             TRACE(("INVALID GLYPH found\n"));
1317             num_invalid_glyphs++;
1318             continue;
1319         }
1320         glyphs[i - num_invalid_glyphs].index = it_info->index;
1321         glyphs[i - num_invalid_glyphs].x = it_info->x;
1322         glyphs[i - num_invalid_glyphs].y = it_info->y;
1323         i++;
1324     }
1326     if (is_stroke)
1327         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1328     else
1329         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1331     if (num_glyphs > GLYPH_ARRAY_SIZE)
1332         g_free(glyphs);
1334     return num_glyphs - num_invalid_glyphs;
1337 bool
1338 CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1339                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1341     // create a cairo_font_face from PangoFont
1342     double size = style->font_size.computed;
1343     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1344     FcPattern *fc_pattern = fc_font->font_pattern;
1346     cairo_save(_cr);
1348 #ifndef PANGO_ENABLE_BACKEND
1349     cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1350     cairo_set_font_face(_cr, font_face);
1352     if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1353         size = 12.0;
1355     // set the given font matrix
1356     cairo_matrix_t matrix;
1357     _initCairoMatrix(&matrix, font_matrix);
1358     cairo_set_font_matrix(_cr, &matrix);
1360     if (_render_mode == RENDER_MODE_CLIP) {
1361         if (_clip_mode == CLIP_MODE_MASK) {
1362             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1363                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1364             } else {
1365                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1366             }
1367             cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
1368             cairo_rectangle (_cr, 0, 0, 30, 40);
1369             cairo_fill (_cr);
1370             _showGlyphs(_cr, font, glyphtext, FALSE);
1371             //cairo_fill(_cr);
1372         } else {
1373             // just add the glyph paths to the current context
1374             _showGlyphs(_cr, font, glyphtext, TRUE);
1375         }
1376     } else {
1378         if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
1379             // set fill style
1380             _setFillStyle(style, NULL);
1382             _showGlyphs(_cr, font, glyphtext, FALSE);
1383         }
1385         if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
1386             // set stroke style
1387             _setStrokeStyle(style, NULL);
1389             // paint stroke
1390             _showGlyphs(_cr, font, glyphtext, TRUE);
1391             cairo_stroke(_cr);
1392         }
1393     }
1395     cairo_restore(_cr);
1397     cairo_font_face_destroy(font_face);
1398 #else
1399     (void)size;
1400     (void)fc_pattern;
1401 #endif
1403     return true;
1406 /* Helper functions */
1408 void
1409 CairoRenderContext::addBpath(NArtBpath const *bp)
1411     bool closed = false;
1412     while (bp->code != NR_END) {
1413         switch (bp->code) {
1414             case NR_MOVETO:
1415                 if (closed) {
1416                     cairo_close_path(_cr);
1417                 }
1418                 closed = true;
1419                 cairo_move_to(_cr, bp->x3, bp->y3);
1420                 break;
1421             case NR_MOVETO_OPEN:
1422                 if (closed) {
1423                     cairo_close_path(_cr);
1424                 }
1425                 closed = false;
1426                 cairo_move_to(_cr, bp->x3, bp->y3);
1427                 break;
1428             case NR_LINETO:
1429                 cairo_line_to(_cr, bp->x3, bp->y3);
1430                 break;
1431             case NR_CURVETO:
1432                 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1433                 break;
1434             default:
1435                 break;
1436         }
1437         bp += 1;
1438     }
1439     if (closed) {
1440         cairo_close_path(_cr);
1441     }
1444 void
1445 CairoRenderContext::setBpath(NArtBpath const *bp)
1447     cairo_new_path(_cr);
1448     if (bp)
1449         addBpath(bp);
1452 void
1453 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1455     cairo_matrix_t matrix;
1457     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1458     cairo_transform(cr, &matrix);
1461 void
1462 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1464     matrix->xx = (*transform)[0];
1465     matrix->yx = (*transform)[1];
1466     matrix->xy = (*transform)[2];
1467     matrix->yy = (*transform)[3];
1468     matrix->x0 = (*transform)[4];
1469     matrix->y0 = (*transform)[5];
1472 void
1473 CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1475     _concatTransform(cr, (*transform)[0], (*transform)[1],
1476                      (*transform)[2], (*transform)[3],
1477                      (*transform)[4], (*transform)[5]);
1480 static cairo_status_t
1481 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1483     size_t written;
1484     FILE *file = (FILE*)closure;
1486     written = fwrite (data, 1, length, file);
1488     if (written == length)
1489         return CAIRO_STATUS_SUCCESS;
1490     else
1491         return CAIRO_STATUS_WRITE_ERROR;
1494 #include "clear-n_.h"
1496 }  /* namespace Internal */
1497 }  /* namespace Extension */
1498 }  /* namespace Inkscape */
1500 #undef TRACE
1501 #undef TEST
1503 /* End of GNU GPL code */
1505 #endif /* HAVE_CAIRO_PDF */
1508 /*
1509   Local Variables:
1510   mode:c++
1511   c-file-style:"stroustrup"
1512   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1513   indent-tabs-mode:nil
1514   fill-column:99
1515   End:
1516 */
1517 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :