Code

d7fbbc1aa90aac4939567c01612c4ace0a13290b
[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 #ifdef CAIRO_HAS_FT_FONT
74 #include <cairo-ft.h>
75 #endif
77 #include <pango/pangofc-fontmap.h>
79 //#define TRACE(_args) g_printf _args
80 #define TRACE(_args)
81 //#define TEST(_args) _args
82 #define TEST(_args)
84 // FIXME: expose these from sp-clippath/mask.cpp
85 struct SPClipPathView {
86     SPClipPathView *next;
87     unsigned int key;
88     NRArenaItem *arenaitem;
89     NRRect bbox;
90 };
92 struct SPMaskView {
93     SPMaskView *next;
94     unsigned int key;
95     NRArenaItem *arenaitem;
96     NRRect bbox;
97 };
99 namespace Inkscape {
100 namespace Extension {
101 namespace Internal {
103 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
105 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
106     _dpi(72),
107     _pdf_level(0),
108     _ps_level(1),
109     _is_texttopath(FALSE),
110     _is_filtertobitmap(FALSE),
111     _bitmapresolution(72),
112     _stream(NULL),
113     _is_valid(FALSE),
114     _vector_based_target(FALSE),
115     _cr(NULL),
116     _surface(NULL),
117     _target(CAIRO_SURFACE_TYPE_IMAGE),
118     _target_format(CAIRO_FORMAT_ARGB32),
119     _layout(NULL),
120     _state(NULL),
121     _renderer(parent),
122     _render_mode(RENDER_MODE_NORMAL),
123     _clip_mode(CLIP_MODE_MASK)
124 {}
126 CairoRenderContext::~CairoRenderContext(void)
128     if (_cr) cairo_destroy(_cr);
129     if (_surface) cairo_surface_destroy(_surface);
130     if (_layout) g_object_unref(_layout);
133 CairoRenderer*
134 CairoRenderContext::getRenderer(void) const
136     return _renderer;
139 CairoRenderState*
140 CairoRenderContext::getCurrentState(void) const
142     return _state;
145 CairoRenderState*
146 CairoRenderContext::getParentState(void) const
148     // if this is the root node just return it
149     if (g_slist_length(_state_stack) == 1) {
150         return _state;
151     } else {
152         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
153     }
156 void
157 CairoRenderContext::setStateForStyle(SPStyle const *style)
159     // only opacity & overflow is stored for now
160     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
161     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
162     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
164     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
165         _state->merge_opacity = FALSE;
167     // disable rendering of opacity if there's a stroke on the fill
168     if (_state->merge_opacity
169         && !style->fill.isNone()
170         && !style->stroke.isNone())
171         _state->merge_opacity = FALSE;
174 /**
175  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
176  *
177  * \param width     width of the surface to be created
178  * \param height    height of the surface to be created
179  */
180 CairoRenderContext*
181 CairoRenderContext::cloneMe(double width, double height) const
183     g_assert( _is_valid );
184     g_assert( width > 0.0 && height > 0.0 );
186     CairoRenderContext *new_context = _renderer->createContext();
187     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
188                                                             (int)ceil(width), (int)ceil(height));
189     new_context->_cr = cairo_create(surface);
190     new_context->_surface = surface;
191     new_context->_is_valid = TRUE;
193     return new_context;
196 CairoRenderContext*
197 CairoRenderContext::cloneMe(void) const
199     g_assert( _is_valid );
201     return cloneMe(_width, _height);
204 bool
205 CairoRenderContext::setImageTarget(cairo_format_t format)
207     // format cannot be set on an already initialized surface
208     if (_is_valid)
209         return false;
211     switch (format) {
212         case CAIRO_FORMAT_ARGB32:
213         case CAIRO_FORMAT_RGB24:
214         case CAIRO_FORMAT_A8:
215         case CAIRO_FORMAT_A1:
216             _target_format = format;
217             _target = CAIRO_SURFACE_TYPE_IMAGE;
218             return true;
219             break;
220         default:
221             break;
222     }
224     return false;
227 bool
228 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
230 #ifndef CAIRO_HAS_PDF_SURFACE
231     return false;
232 #else
233     _target = CAIRO_SURFACE_TYPE_PDF;
234     _vector_based_target = TRUE;
235 #endif
237     FILE *osf = NULL;
238     FILE *osp = NULL;
240     gsize bytesRead = 0;
241     gsize bytesWritten = 0;
242     GError *error = NULL;
243     gchar *local_fn = g_filename_from_utf8(utf8_fn,
244                                            -1,  &bytesRead,  &bytesWritten, &error);
245     gchar const *fn = local_fn;
247     /* TODO: Replace the below fprintf's with something that does the right thing whether in
248     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
249     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
250     * return code.
251     */
252     if (fn != NULL) {
253         if (*fn == '|') {
254             fn += 1;
255             while (isspace(*fn)) fn += 1;
256 #ifndef WIN32
257             osp = popen(fn, "w");
258 #else
259             osp = _popen(fn, "w");
260 #endif
261             if (!osp) {
262                 fprintf(stderr, "inkscape: popen(%s): %s\n",
263                         fn, strerror(errno));
264                 return false;
265             }
266             _stream = osp;
267         } else if (*fn == '>') {
268             fn += 1;
269             while (isspace(*fn)) fn += 1;
270             Inkscape::IO::dump_fopen_call(fn, "K");
271             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
272             if (!osf) {
273                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
274                         fn, strerror(errno));
275                 return false;
276             }
277             _stream = osf;
278         } else {
279             /* put cwd stuff in here */
280             gchar *qn = ( *fn
281                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
282                 : g_strdup("lpr") );
283 #ifndef WIN32
284             osp = popen(qn, "w");
285 #else
286             osp = _popen(qn, "w");
287 #endif
288             if (!osp) {
289                 fprintf(stderr, "inkscape: popen(%s): %s\n",
290                         qn, strerror(errno));
291                 return false;
292             }
293             g_free(qn);
294             _stream = osp;
295         }
296     }
298     g_free(local_fn);
300     if (_stream) {
301         /* fixme: this is kinda icky */
302 #if !defined(_WIN32) && !defined(__WIN32__)
303         (void) signal(SIGPIPE, SIG_IGN);
304 #endif
305     }
307     return true;
310 bool
311 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
313 #ifndef CAIRO_HAS_PS_SURFACE
314     return false;
315 #else
316     _target = CAIRO_SURFACE_TYPE_PS;
317     _vector_based_target = TRUE;
318 #endif
320     FILE *osf = NULL;
321     FILE *osp = NULL;
323     gsize bytesRead = 0;
324     gsize bytesWritten = 0;
325     GError *error = NULL;
326     gchar *local_fn = g_filename_from_utf8(utf8_fn,
327                                            -1,  &bytesRead,  &bytesWritten, &error);
328     gchar const *fn = local_fn;
330     /* TODO: Replace the below fprintf's with something that does the right thing whether in
331     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
332     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
333     * return code.
334     */
335     if (fn != NULL) {
336         if (*fn == '|') {
337             fn += 1;
338             while (isspace(*fn)) fn += 1;
339 #ifndef WIN32
340             osp = popen(fn, "w");
341 #else
342             osp = _popen(fn, "w");
343 #endif
344             if (!osp) {
345                 fprintf(stderr, "inkscape: popen(%s): %s\n",
346                         fn, strerror(errno));
347                 return false;
348             }
349             _stream = osp;
350         } else if (*fn == '>') {
351             fn += 1;
352             while (isspace(*fn)) fn += 1;
353             Inkscape::IO::dump_fopen_call(fn, "K");
354             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
355             if (!osf) {
356                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
357                         fn, strerror(errno));
358                 return false;
359             }
360             _stream = osf;
361         } else {
362             /* put cwd stuff in here */
363             gchar *qn = ( *fn
364                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
365                 : g_strdup("lpr") );
366 #ifndef WIN32
367             osp = popen(qn, "w");
368 #else
369             osp = _popen(qn, "w");
370 #endif
371             if (!osp) {
372                 fprintf(stderr, "inkscape: popen(%s): %s\n",
373                         qn, strerror(errno));
374                 return false;
375             }
376             g_free(qn);
377             _stream = osp;
378         }
379     }
381     g_free(local_fn);
383     if (_stream) {
384         /* fixme: this is kinda icky */
385 #if !defined(_WIN32) && !defined(__WIN32__)
386         (void) signal(SIGPIPE, SIG_IGN);
387 #endif
388     }
390     return true;
393 void CairoRenderContext::setPSLevel(unsigned int level)
395     _ps_level = level;
398 unsigned int CairoRenderContext::getPSLevel(void)
400     return _ps_level;
403 void CairoRenderContext::setPDFLevel(unsigned int level)
405     _pdf_level = level;
408 void CairoRenderContext::setTextToPath(bool texttopath)
410     _is_texttopath = texttopath;
413 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
415     _is_filtertobitmap = filtertobitmap;
418 bool CairoRenderContext::getFilterToBitmap(void)
420     return _is_filtertobitmap;
423 void CairoRenderContext::setBitmapResolution(int resolution)
425     _bitmapresolution = resolution;
428 int CairoRenderContext::getBitmapResolution(void)
430     return _bitmapresolution;
433 cairo_surface_t*
434 CairoRenderContext::getSurface(void)
436     g_assert( _is_valid );
438     return _surface;
441 bool
442 CairoRenderContext::saveAsPng(const char *file_name)
444     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
445     if (status)
446         return false;
447     else
448         return true;
451 void
452 CairoRenderContext::setRenderMode(CairoRenderMode mode)
454     switch (mode) {
455         case RENDER_MODE_NORMAL:
456         case RENDER_MODE_CLIP:
457             _render_mode = mode;
458             break;
459         default:
460             _render_mode = RENDER_MODE_NORMAL;
461             break;
462     }
465 CairoRenderContext::CairoRenderMode
466 CairoRenderContext::getRenderMode(void) const
468     return _render_mode;
471 void
472 CairoRenderContext::setClipMode(CairoClipMode mode)
474     switch (mode) {
475         case CLIP_MODE_PATH:
476         case CLIP_MODE_MASK:
477             _clip_mode = mode;
478             break;
479         default:
480             _clip_mode = CLIP_MODE_PATH;
481             break;
482     }
485 CairoRenderContext::CairoClipMode
486 CairoRenderContext::getClipMode(void) const
488     return _clip_mode;
491 CairoRenderState*
492 CairoRenderContext::_createState(void)
494     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
495     g_assert( state != NULL );
497     state->has_filtereffect = FALSE;
498     state->merge_opacity = TRUE;
499     state->opacity = 1.0;
500     state->need_layer = FALSE;
501     state->has_overflow = FALSE;
502     state->parent_has_userspace = FALSE;
503     state->clip_path = NULL;
504     state->mask = NULL;
506     return state;
509 void
510 CairoRenderContext::pushLayer(void)
512     g_assert( _is_valid );
514     TRACE(("--pushLayer\n"));
515     cairo_push_group(_cr);
517     // clear buffer
518     if (!_vector_based_target) {
519         cairo_save(_cr);
520         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
521         cairo_paint(_cr);
522         cairo_restore(_cr);
523     }
526 void
527 CairoRenderContext::popLayer(void)
529     g_assert( _is_valid );
531     float opacity = _state->opacity;
532     TRACE(("--popLayer w/ %f\n", opacity));
534     // apply clipPath or mask if present
535     SPClipPath *clip_path = _state->clip_path;
536     SPMask *mask = _state->mask;
537     if (clip_path || mask) {
539         CairoRenderContext *clip_ctx = 0;
540         cairo_surface_t *clip_mask = 0;
542         if (clip_path) {
543             if (_render_mode == RENDER_MODE_CLIP)
544                 mask = NULL;    // disable mask when performing nested clipping
546             if (_vector_based_target) {
547                 setClipMode(CLIP_MODE_PATH);
548                 if (!mask) {
549                     cairo_pop_group_to_source(_cr);
550                     _renderer->applyClipPath(this, clip_path);
551                     if (opacity == 1.0)
552                         cairo_paint(_cr);
553                     else
554                         cairo_paint_with_alpha(_cr, opacity);
556                 } else {
557                     // the clipPath will be applied before masking
558                 }
559             } else {
561                 // setup a new rendering context
562                 clip_ctx = _renderer->createContext();
563                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
564                 clip_ctx->setClipMode(CLIP_MODE_MASK);
565                 if (!clip_ctx->setupSurface(_width, _height)) {
566                     TRACE(("setupSurface failed\n"));
567                     _renderer->destroyContext(clip_ctx);
568                     return;
569                 }
571                 // clear buffer
572                 cairo_save(clip_ctx->_cr);
573                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
574                 cairo_paint(clip_ctx->_cr);
575                 cairo_restore(clip_ctx->_cr);
577                 // if a mask won't be applied set opacity too
578                 if (!mask)
579                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
580                 else
581                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
583                 // copy over the correct CTM
584                 if (_state->parent_has_userspace)
585                     clip_ctx->setTransform(&getParentState()->transform);
586                 else
587                     clip_ctx->setTransform(&_state->transform);
589                 // apply the clip path
590                 clip_ctx->pushState();
591                 _renderer->applyClipPath(clip_ctx, clip_path);
592                 clip_ctx->popState();
594                 clip_mask = clip_ctx->getSurface();
595                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
597                 if (!mask) {
598                     cairo_pop_group_to_source(_cr);
599                     cairo_mask_surface(_cr, clip_mask, 0, 0);
600                     _renderer->destroyContext(clip_ctx);
601                 }
602             }
603         }
605         if (mask) {
606             // create rendering context for mask
607             CairoRenderContext *mask_ctx = _renderer->createContext();
608             mask_ctx->setupSurface(_width, _height);
610             // set rendering mode to normal
611             setRenderMode(RENDER_MODE_NORMAL);
613             // copy the correct CTM to mask context
614             if (_state->parent_has_userspace)
615                 mask_ctx->setTransform(&getParentState()->transform);
616             else
617                 mask_ctx->setTransform(&_state->transform);
619             // render mask contents to mask_ctx
620             _renderer->applyMask(mask_ctx, mask);
622             TEST(mask_ctx->saveAsPng("mask.png"));
624             // composite with clip mask
625             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
626                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
627                 _renderer->destroyContext(clip_ctx);
628             }
630             cairo_surface_t *mask_image = mask_ctx->getSurface();
631             int width = cairo_image_surface_get_width(mask_image);
632             int height = cairo_image_surface_get_height(mask_image);
633             int stride = cairo_image_surface_get_stride(mask_image);
634             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
636             // premultiply with opacity
637             if (_state->opacity != 1.0) {
638                 TRACE(("premul w/ %f\n", opacity));
639                 guint8 int_opacity = (guint8)(255 * opacity);
640                 for (int row = 0 ; row < height; row++) {
641                     unsigned char *row_data = pixels + (row * stride);
642                     for (int i = 0 ; i < width; i++) {
643                         guint32 *pixel = (guint32 *)row_data + i;
644                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
645                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
646                                 ((*pixel & 0x000000ff)      ) * 4688) *
647                                 int_opacity);
648                     }
649                 }
650             }
652             cairo_pop_group_to_source(_cr);
653             if (_clip_mode == CLIP_MODE_PATH) {
654                 // we have to do the clipping after cairo_pop_group_to_source
655                 _renderer->applyClipPath(this, clip_path);
656             }
657             // apply the mask onto the layer
658             cairo_mask_surface(_cr, mask_image, 0, 0);
659             _renderer->destroyContext(mask_ctx);
660         }
661     } else {
662         cairo_pop_group_to_source(_cr);
663         if (opacity == 1.0)
664             cairo_paint(_cr);
665         else
666             cairo_paint_with_alpha(_cr, opacity);
667     }
670 void
671 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
673     g_assert( _is_valid );
675     // here it should be checked whether the current clip winding changed
676     // so we could switch back to masked clipping
677     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
678         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
679     } else {
680         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
681     }
682     addBpath(bp);
685 void
686 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
688     g_assert( _is_valid );
690     cairo_rectangle(_cr, x, y, width, height);
691     cairo_clip(_cr);
694 bool
695 CairoRenderContext::setupSurface(double width, double height)
697     // Is the surface already set up?
698     if (_is_valid)
699         return true;
701     if (_vector_based_target && _stream == NULL)
702         return false;
704     cairo_surface_t *surface = NULL;
705     switch (_target) {
706         case CAIRO_SURFACE_TYPE_IMAGE:
707             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
708             break;
709 #ifdef CAIRO_HAS_PDF_SURFACE
710         case CAIRO_SURFACE_TYPE_PDF:
711             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
712             break;
713 #endif
714 #ifdef CAIRO_HAS_PS_SURFACE
715         case CAIRO_SURFACE_TYPE_PS:
716             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
717 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
718             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
719                 return FALSE;
720             }
721             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
722 #endif
723             break;
724 #endif
725         default:
726             return false;
727             break;
728     }
730     return _finishSurfaceSetup (surface);
733 bool
734 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
736     if (_is_valid || !surface)
737         return false;
739     _vector_based_target = is_vector;
740     bool ret = _finishSurfaceSetup (surface);
741     if (ret)
742         cairo_surface_reference (surface);
743     return ret;
746 bool
747 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
749     if(surface == NULL) {
750         return FALSE;
751     }
752     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
753         return FALSE;
754     }
756     _cr = cairo_create(surface);
757     _surface = surface;
759     if (_vector_based_target) {
760         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
761     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
762         // set background color on non-alpha surfaces
763         // TODO: bgcolor should be derived from SPDocument
764         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
765         cairo_rectangle(_cr, 0, 0, _width, _height);
766         cairo_fill(_cr);
767     }
769     _is_valid = TRUE;
771     return true;
774 bool
775 CairoRenderContext::finish(void)
777     g_assert( _is_valid );
779     if (_vector_based_target)
780         cairo_show_page(_cr);
782     cairo_destroy(_cr);
783     cairo_surface_destroy(_surface);
784     _cr = NULL;
785     _surface = NULL;
787     if (_layout)
788         g_object_unref(_layout);
790     _is_valid = FALSE;
792     if (_vector_based_target && _stream) {
793         /* Flush stream to be sure. */
794         (void) fflush(_stream);
796         fclose(_stream);
797         _stream = NULL;
798     }
800     return true;
803 void
804 CairoRenderContext::transform(NRMatrix const *transform)
806     g_assert( _is_valid );
808     cairo_matrix_t matrix;
809     _initCairoMatrix(&matrix, transform);
810     cairo_transform(_cr, &matrix);
812     // store new CTM
813     getTransform(&_state->transform);
816 void
817 CairoRenderContext::setTransform(NRMatrix const *transform)
819     g_assert( _is_valid );
821     cairo_matrix_t matrix;
822     _initCairoMatrix(&matrix, transform);
823     cairo_set_matrix(_cr, &matrix);
824     _state->transform = *transform;
827 void
828 CairoRenderContext::getTransform(NRMatrix *copy) const
830     g_assert( _is_valid );
832     cairo_matrix_t ctm;
833     cairo_get_matrix(_cr, &ctm);
834     (*copy)[0] = ctm.xx;
835     (*copy)[1] = ctm.yx;
836     (*copy)[2] = ctm.xy;
837     (*copy)[3] = ctm.yy;
838     (*copy)[4] = ctm.x0;
839     (*copy)[5] = ctm.y0;
842 void
843 CairoRenderContext::getParentTransform(NRMatrix *copy) const
845     g_assert( _is_valid );
847     CairoRenderState *parent_state = getParentState();
848     memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
851 void
852 CairoRenderContext::pushState(void)
854     g_assert( _is_valid );
856     cairo_save(_cr);
858     CairoRenderState *new_state = _createState();
859     // copy current state's transform
860     new_state->transform = _state->transform;
861     _state_stack = g_slist_prepend(_state_stack, new_state);
862     _state = new_state;
865 void
866 CairoRenderContext::popState(void)
868     g_assert( _is_valid );
870     cairo_restore(_cr);
872     g_free(_state_stack->data);
873     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
874     _state = (CairoRenderState*)_state_stack->data;
876     g_assert( g_slist_length(_state_stack) > 0 );
879 static bool pattern_hasItemChildren (SPPattern *pat)
881     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
882         if (SP_IS_ITEM (child)) {
883             return true;
884         }
885     }
886     return false;
889 cairo_pattern_t*
890 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
892     g_assert( SP_IS_PATTERN(paintserver) );
894     SPPattern *pat = SP_PATTERN (paintserver);
896     NRMatrix ps2user, pcs2dev;
897     nr_matrix_set_identity(&ps2user);
898     nr_matrix_set_identity(&pcs2dev);
900     double x = pattern_x(pat);
901     double y = pattern_y(pat);
902     double width = pattern_width(pat);
903     double height = pattern_height(pat);
904     double bbox_width_scaler;
905     double bbox_height_scaler;
907     TRACE(("%f x %f pattern\n", width, height));
909     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
910         //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
911         bbox_width_scaler = pbox->x1 - pbox->x0;
912         bbox_height_scaler = pbox->y1 - pbox->y0;
913         ps2user[4] = x * bbox_width_scaler + pbox->x0;
914         ps2user[5] = y * bbox_height_scaler + pbox->y0;
915     } else {
916         bbox_width_scaler = 1.0;
917         bbox_height_scaler = 1.0;
918         ps2user[4] = x;
919         ps2user[5] = y;
920     }
922     // apply pattern transformation
923     NRMatrix pattern_transform(pattern_patternTransform(pat));
924     nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
926     // create pattern contents coordinate system
927     if (pat->viewBox_set) {
928         NRRect *view_box = pattern_viewBox(pat);
930         double x, y, w, h;
931         double view_width, view_height;
932         x = 0;
933         y = 0;
934         w = width * bbox_width_scaler;
935         h = height * bbox_height_scaler;
937         view_width = view_box->x1 - view_box->x0;
938         view_height = view_box->y1 - view_box->y0;
940         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
941         pcs2dev[0] = w / view_width;
942         pcs2dev[3] = h / view_height;
943         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
944         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
945     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
946         pcs2dev[0] = pbox->x1 - pbox->x0;
947         pcs2dev[3] = pbox->y1 - pbox->y0;
948     }
950     // calculate the size of the surface which has to be created
951     // the scaling needs to be taken into account in the ctm after the pattern transformation
952     NRMatrix temp;
953     nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
954     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
955     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
957     if (_vector_based_target) {
958         // eliminate PT_PER_PX mul from these
959         width_scaler *= 1.25;
960         height_scaler *= 1.25;
961     }
962     double surface_width = ceil(bbox_width_scaler * width_scaler * width);
963     double surface_height = ceil(bbox_height_scaler * height_scaler * height);
964     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
965     // create new rendering context
966     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
968     // adjust the size of the painted pattern to fit exactly the created surface
969     // this has to be done because of the rounding to obtain an integer pattern surface width/height
970     double scale_width = surface_width / (bbox_width_scaler * width);
971     double scale_height = surface_height / (bbox_height_scaler * height);
972     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
973         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
974         NRMatrix scale;
975         nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
976         nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
978         nr_matrix_set_scale(&scale, scale_width, scale_height);
979         nr_matrix_multiply(&ps2user, &ps2user, &scale);
980     }
982     pattern_ctx->setTransform(&pcs2dev);
983     pattern_ctx->pushState();
985     // create arena and group
986     NRArena *arena = NRArena::create();
987     unsigned dkey = sp_item_display_key_new(1);
989     // show items and render them
990     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
991         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
992             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
993                 if (SP_IS_ITEM (child)) {
994                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
995                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
996                 }
997             }
998             break; // do not go further up the chain if children are found
999         }
1000     }
1002     pattern_ctx->popState();
1004     // setup a cairo_pattern_t
1005     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1006     TEST(pattern_ctx->saveAsPng("pattern.png"));
1007     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1008     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1010     // set pattern transformation
1011     cairo_matrix_t pattern_matrix;
1012     _initCairoMatrix(&pattern_matrix, &ps2user);
1013     cairo_matrix_invert(&pattern_matrix);
1014     cairo_pattern_set_matrix(result, &pattern_matrix);
1016     delete pattern_ctx;
1018     // hide all items
1019     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1020         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1021             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1022                 if (SP_IS_ITEM (child)) {
1023                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1024                 }
1025             }
1026             break; // do not go further up the chain if children are found
1027         }
1028     }
1030     return result;
1033 cairo_pattern_t*
1034 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1035                                                  NRRect const *pbox, float alpha)
1037     cairo_pattern_t *pattern = NULL;
1038     bool apply_bbox2user = FALSE;
1040     if (SP_IS_LINEARGRADIENT (paintserver)) {
1042             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1044             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1046             NR::Point p1 (lg->x1.computed, lg->y1.computed);
1047             NR::Point p2 (lg->x2.computed, lg->y2.computed);
1048             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1049                 // convert to userspace
1050                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1051                 p1 *= bbox2user;
1052                 p2 *= bbox2user;
1053             }
1055             // create linear gradient pattern
1056             pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1058             // add stops
1059             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1060                 float rgb[3];
1061                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1062                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1063             }
1064     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1066         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1068         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1070         NR::Point c (rg->cx.computed, rg->cy.computed);
1071         NR::Point f (rg->fx.computed, rg->fy.computed);
1072         double r = rg->r.computed;
1073         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1074             apply_bbox2user = true;
1076         // create radial gradient pattern
1077         pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1079         // add stops
1080         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1081             float rgb[3];
1082             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1083             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1084         }
1085     } else if (SP_IS_PATTERN (paintserver)) {
1087         pattern = _createPatternPainter(paintserver, pbox);
1088     } else {
1089         return NULL;
1090     }
1092     if (pattern && SP_IS_GRADIENT (paintserver)) {
1093         SPGradient *g = SP_GRADIENT(paintserver);
1095         // set extend type
1096         SPGradientSpread spread = sp_gradient_get_spread(g);
1097         switch (spread) {
1098             case SP_GRADIENT_SPREAD_REPEAT: {
1099                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1100                 break;
1101             }
1102             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1103                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1104                 break;
1105             }
1106             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1107                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1108                 break;
1109             }
1110             default: {
1111                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1112                 break;
1113             }
1114         }
1116         cairo_matrix_t pattern_matrix;
1117         if (g->gradientTransform_set) {
1118             // apply gradient transformation
1119             cairo_matrix_init(&pattern_matrix,
1120                 g->gradientTransform[0], g->gradientTransform[1],
1121                 g->gradientTransform[2], g->gradientTransform[3],
1122                 g->gradientTransform[4], g->gradientTransform[5]);
1123         } else {
1124             cairo_matrix_init_identity (&pattern_matrix);
1125         }
1127         if (apply_bbox2user) {
1128             // convert to userspace
1129             cairo_matrix_t bbox2user;
1130             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1131             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1132         }
1133         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1134         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1135     }
1137     return pattern;
1140 void
1141 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1143     g_return_if_fail( style->fill.isColor()
1144                       || style->fill.isPaintserver() );
1146     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1147     if (_state->merge_opacity) {
1148         alpha *= _state->opacity;
1149         TRACE(("merged op=%f\n", alpha));
1150     }
1152     if (style->fill.isColor()) {
1153         float rgb[3];
1154         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1156         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1157     } else {
1158         g_assert( style->fill.isPaintserver()
1159                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1160                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1162         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1164         if (pattern) {
1165             cairo_set_source(_cr, pattern);
1166             cairo_pattern_destroy(pattern);
1167         }
1168     }
1171 void
1172 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1174     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1175     if (_state->merge_opacity)
1176         alpha *= _state->opacity;
1178     if (style->stroke.isColor()) {
1179         float rgb[3];
1180         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1182         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1183     } else {
1184         g_assert( style->fill.isPaintserver()
1185                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1186                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1188         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1190         if (pattern) {
1191             cairo_set_source(_cr, pattern);
1192             cairo_pattern_destroy(pattern);
1193         }
1194     }
1196     if (style->stroke_dash.n_dash   &&
1197         style->stroke_dash.dash       )
1198     {
1199         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1200     } else {
1201         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1202     }
1204     cairo_set_line_width(_cr, style->stroke_width.computed);
1206     // set line join type
1207     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1208     switch (style->stroke_linejoin.computed) {
1209         case SP_STROKE_LINEJOIN_MITER:
1210             join = CAIRO_LINE_JOIN_MITER;
1211             break;
1212         case SP_STROKE_LINEJOIN_ROUND:
1213             join = CAIRO_LINE_JOIN_ROUND;
1214             break;
1215         case SP_STROKE_LINEJOIN_BEVEL:
1216             join = CAIRO_LINE_JOIN_BEVEL;
1217             break;
1218     }
1219     cairo_set_line_join(_cr, join);
1221     // set line cap type
1222     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1223     switch (style->stroke_linecap.computed) {
1224         case SP_STROKE_LINECAP_BUTT:
1225             cap = CAIRO_LINE_CAP_BUTT;
1226             break;
1227         case SP_STROKE_LINECAP_ROUND:
1228             cap = CAIRO_LINE_CAP_ROUND;
1229             break;
1230         case SP_STROKE_LINECAP_SQUARE:
1231             cap = CAIRO_LINE_CAP_SQUARE;
1232             break;
1233     }
1234     cairo_set_line_cap(_cr, cap);
1235     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1238 bool
1239 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1241     g_assert( _is_valid );
1243     if (_render_mode == RENDER_MODE_CLIP) {
1244         if (_clip_mode == CLIP_MODE_PATH) {
1245             addClipPath(bpath->path, &style->fill_rule);
1246         } else {
1247             setBpath(bpath->path);
1248             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1249                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1250             } else {
1251                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1252             }
1253             cairo_fill(_cr);
1254             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1255         }
1256         return true;
1257     }
1259     if (style->fill.isNone() && style->stroke.isNone())
1260         return true;
1262     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1263                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1265     if (!need_layer)
1266         cairo_save(_cr);
1267     else
1268         pushLayer();
1270     if (!style->fill.isNone()) {
1271         _setFillStyle(style, pbox);
1272         setBpath(bpath->path);
1274         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1275             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1276         } else {
1277             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1278         }
1280         if (style->stroke.isNone())
1281             cairo_fill(_cr);
1282         else
1283             cairo_fill_preserve(_cr);
1284     }
1286     if (!style->stroke.isNone()) {
1287         _setStrokeStyle(style, pbox);
1288         if (style->fill.isNone())
1289             setBpath(bpath->path);
1291         cairo_stroke(_cr);
1292     }
1294     if (need_layer)
1295         popLayer();
1296     else
1297         cairo_restore(_cr);
1299     return true;
1302 bool
1303 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1304                                 NRMatrix const *image_transform, SPStyle const *style)
1306     g_assert( _is_valid );
1308     if (_render_mode == RENDER_MODE_CLIP)
1309         return true;
1311     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1312     if (!px_rgba)
1313         return false;
1315     float opacity;
1316     if (_state->merge_opacity)
1317         opacity = _state->opacity;
1318     else
1319         opacity = 1.0;
1321     // make a copy of the original pixbuf with premultiplied alpha
1322     // if we pass the original pixbuf it will get messed up
1323     for (unsigned i = 0; i < h; i++) {
1324         for (unsigned j = 0; j < w; j++) {
1325             guchar const *src = px + i * rs + j * 4;
1326             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1327             guchar r, g, b, alpha_dst;
1329             // calculate opacity-modified alpha
1330             alpha_dst = src[3];
1331             if (opacity != 1.0 && _vector_based_target)
1332                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1334             // premul alpha (needed because this will be undone by cairo-pdf)
1335             r = src[0]*alpha_dst/255;
1336             g = src[1]*alpha_dst/255;
1337             b = src[2]*alpha_dst/255;
1339             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1340         }
1341     }
1343     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1344     if (cairo_surface_status(image_surface)) {
1345         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1346         return false;
1347     }
1349     // setup automatic freeing of the image data when destroying the surface
1350     static cairo_user_data_key_t key;
1351     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1353     cairo_save(_cr);
1355     // scaling by width & height is not needed because it will be done by Cairo
1356     if (image_transform)
1357         transform(image_transform);
1359     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1361     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1362     if (_vector_based_target) {
1363         cairo_new_path(_cr);
1364         cairo_rectangle(_cr, 0, 0, w, h);
1365         cairo_clip(_cr);
1366     }
1368     if (_vector_based_target)
1369         cairo_paint(_cr);
1370     else
1371         cairo_paint_with_alpha(_cr, opacity);
1373     cairo_restore(_cr);
1375     cairo_surface_destroy(image_surface);
1377     return true;
1380 #define GLYPH_ARRAY_SIZE 64
1382 unsigned int
1383 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1385     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1386     cairo_glyph_t *glyphs = glyph_array;
1387     unsigned int num_glyphs = glyphtext.size();
1388     if (num_glyphs > GLYPH_ARRAY_SIZE)
1389         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1391     unsigned int num_invalid_glyphs = 0;
1392     unsigned int i = 0;
1393     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1394         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1395         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1396         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1397             TRACE(("INVALID GLYPH found\n"));
1398             num_invalid_glyphs++;
1399             continue;
1400         }
1401         glyphs[i - num_invalid_glyphs].index = it_info->index;
1402         glyphs[i - num_invalid_glyphs].x = it_info->x;
1403         glyphs[i - num_invalid_glyphs].y = it_info->y;
1404         i++;
1405     }
1407     if (is_stroke || _is_texttopath)
1408         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1409     else
1410         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1412     if (num_glyphs > GLYPH_ARRAY_SIZE)
1413         g_free(glyphs);
1415     return num_glyphs - num_invalid_glyphs;
1418 bool
1419 CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1420                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1422     // create a cairo_font_face from PangoFont
1423     double size = style->font_size.computed;
1424     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1425     FcPattern *fc_pattern = fc_font->font_pattern;
1427     cairo_save(_cr);
1429 #ifdef CAIRO_HAS_FT_FONT
1430     cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1431     cairo_set_font_face(_cr, font_face);
1433     if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1434         size = 12.0;
1436     // set the given font matrix
1437     cairo_matrix_t matrix;
1438     _initCairoMatrix(&matrix, font_matrix);
1439     cairo_set_font_matrix(_cr, &matrix);
1441     if (_render_mode == RENDER_MODE_CLIP) {
1442         if (_clip_mode == CLIP_MODE_MASK) {
1443             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1444                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1445             } else {
1446                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1447             }
1448             _showGlyphs(_cr, font, glyphtext, FALSE);
1449         } else {
1450             // just add the glyph paths to the current context
1451             _showGlyphs(_cr, font, glyphtext, TRUE);
1452         }
1453     } else {
1455         if (style->fill.isColor() || style->fill.isPaintserver()) {
1456             // set fill style
1457             _setFillStyle(style, NULL);
1459             _showGlyphs(_cr, font, glyphtext, FALSE);
1460         }
1462         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1463             // set stroke style
1464             _setStrokeStyle(style, NULL);
1466             // paint stroke
1467             _showGlyphs(_cr, font, glyphtext, TRUE);
1468             cairo_stroke(_cr);
1469         }
1470     }
1472     cairo_restore(_cr);
1474     cairo_font_face_destroy(font_face);
1475 #else
1476     (void)size;
1477     (void)fc_pattern;
1479     cairo_restore(_cr);
1480 #endif
1482     return true;
1485 /* Helper functions */
1487 void
1488 CairoRenderContext::addBpath(NArtBpath const *bp)
1490     bool closed = false;
1491     while (bp->code != NR_END) {
1492         switch (bp->code) {
1493             case NR_MOVETO:
1494                 if (closed) {
1495                     cairo_close_path(_cr);
1496                 }
1497                 closed = true;
1498                 cairo_move_to(_cr, bp->x3, bp->y3);
1499                 break;
1500             case NR_MOVETO_OPEN:
1501                 if (closed) {
1502                     cairo_close_path(_cr);
1503                 }
1504                 closed = false;
1505                 cairo_move_to(_cr, bp->x3, bp->y3);
1506                 break;
1507             case NR_LINETO:
1508                 cairo_line_to(_cr, bp->x3, bp->y3);
1509                 break;
1510             case NR_CURVETO:
1511                 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1512                 break;
1513             default:
1514                 break;
1515         }
1516         bp += 1;
1517     }
1518     if (closed) {
1519         cairo_close_path(_cr);
1520     }
1523 void
1524 CairoRenderContext::setBpath(NArtBpath const *bp)
1526     cairo_new_path(_cr);
1527     if (bp)
1528         addBpath(bp);
1531 void
1532 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1534     cairo_matrix_t matrix;
1536     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1537     cairo_transform(cr, &matrix);
1540 void
1541 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1543     matrix->xx = (*transform)[0];
1544     matrix->yx = (*transform)[1];
1545     matrix->xy = (*transform)[2];
1546     matrix->yy = (*transform)[3];
1547     matrix->x0 = (*transform)[4];
1548     matrix->y0 = (*transform)[5];
1551 void
1552 CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1554     _concatTransform(cr, (*transform)[0], (*transform)[1],
1555                      (*transform)[2], (*transform)[3],
1556                      (*transform)[4], (*transform)[5]);
1559 static cairo_status_t
1560 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1562     size_t written;
1563     FILE *file = (FILE*)closure;
1565     written = fwrite (data, 1, length, file);
1567     if (written == length)
1568         return CAIRO_STATUS_SUCCESS;
1569     else
1570         return CAIRO_STATUS_WRITE_ERROR;
1573 #include "clear-n_.h"
1575 }  /* namespace Internal */
1576 }  /* namespace Extension */
1577 }  /* namespace Inkscape */
1579 #undef TRACE
1580 #undef TEST
1582 /* End of GNU GPL code */
1585 /*
1586   Local Variables:
1587   mode:c++
1588   c-file-style:"stroustrup"
1589   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1590   indent-tabs-mode:nil
1591   fill-column:99
1592   End:
1593 */
1594 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :