Code

Fixing as per discussion in LP # 179988
[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-scale-ops.h>
35 #include <libnr/nr-matrix-translate-ops.h>
36 #include <libnr/nr-scale-matrix-ops.h>
38 #include <glib/gmem.h>
40 #include <glibmm/i18n.h>
41 #include "display/nr-arena.h"
42 #include "display/nr-arena-item.h"
43 #include "display/nr-arena-group.h"
44 #include "display/curve.h"
45 #include "display/canvas-bpath.h"
46 #include "sp-item.h"
47 #include "sp-item-group.h"
48 #include "style.h"
49 #include "sp-linear-gradient.h"
50 #include "sp-radial-gradient.h"
51 #include "sp-pattern.h"
52 #include "sp-mask.h"
53 #include "sp-clippath.h"
54 #ifdef WIN32
55 #include "FontFactory.h" // USE_PANGO_WIN32
56 ##endif
58 #include <unit-constants.h>
60 #include "cairo-render-context.h"
61 #include "cairo-renderer.h"
62 #include "extension/system.h"
64 #include "io/sys.h"
66 #include <cairo.h>
68 // include support for only the compiled-in surface types
69 #ifdef CAIRO_HAS_PDF_SURFACE
70 #include <cairo-pdf.h>
71 #endif
72 #ifdef CAIRO_HAS_PS_SURFACE
73 #include <cairo-ps.h>
74 #endif
77 #ifdef CAIRO_HAS_FT_FONT
78 #include <cairo-ft.h>
79 #endif
80 #ifdef CAIRO_HAS_WIN32_FONT
81 #include <cairo-win32.h>
82 #include <pango/pangowin32.h>
83 #endif
85 #include <pango/pangofc-fontmap.h>
87 //#define TRACE(_args) g_printf _args
88 #define TRACE(_args)
89 //#define TEST(_args) _args
90 #define TEST(_args)
92 // FIXME: expose these from sp-clippath/mask.cpp
93 struct SPClipPathView {
94     SPClipPathView *next;
95     unsigned int key;
96     NRArenaItem *arenaitem;
97     NRRect bbox;
98 };
100 struct SPMaskView {
101     SPMaskView *next;
102     unsigned int key;
103     NRArenaItem *arenaitem;
104     NRRect bbox;
105 };
107 namespace Inkscape {
108 namespace Extension {
109 namespace Internal {
111 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
113 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
114     _dpi(72),
115     _pdf_level(0),
116     _ps_level(1),
117     _is_texttopath(FALSE),
118     _is_filtertobitmap(FALSE),
119     _bitmapresolution(72),
120     _stream(NULL),
121     _is_valid(FALSE),
122     _vector_based_target(FALSE),
123     _cr(NULL),
124     _surface(NULL),
125     _target(CAIRO_SURFACE_TYPE_IMAGE),
126     _target_format(CAIRO_FORMAT_ARGB32),
127     _layout(NULL),
128     _state(NULL),
129     _renderer(parent),
130     _render_mode(RENDER_MODE_NORMAL),
131     _clip_mode(CLIP_MODE_MASK)
132 {}
134 CairoRenderContext::~CairoRenderContext(void)
136     if (_cr) cairo_destroy(_cr);
137     if (_surface) cairo_surface_destroy(_surface);
138     if (_layout) g_object_unref(_layout);
141 CairoRenderer*
142 CairoRenderContext::getRenderer(void) const
144     return _renderer;
147 CairoRenderState*
148 CairoRenderContext::getCurrentState(void) const
150     return _state;
153 CairoRenderState*
154 CairoRenderContext::getParentState(void) const
156     // if this is the root node just return it
157     if (g_slist_length(_state_stack) == 1) {
158         return _state;
159     } else {
160         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
161     }
164 void
165 CairoRenderContext::setStateForStyle(SPStyle const *style)
167     // only opacity & overflow is stored for now
168     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
169     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
170     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
172     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
173         _state->merge_opacity = FALSE;
175     // disable rendering of opacity if there's a stroke on the fill
176     if (_state->merge_opacity
177         && !style->fill.isNone()
178         && !style->stroke.isNone())
179         _state->merge_opacity = FALSE;
182 /**
183  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
184  *
185  * \param width     width of the surface to be created
186  * \param height    height of the surface to be created
187  */
188 CairoRenderContext*
189 CairoRenderContext::cloneMe(double width, double height) const
191     g_assert( _is_valid );
192     g_assert( width > 0.0 && height > 0.0 );
194     CairoRenderContext *new_context = _renderer->createContext();
195     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
196                                                             (int)ceil(width), (int)ceil(height));
197     new_context->_cr = cairo_create(surface);
198     new_context->_surface = surface;
199     new_context->_is_valid = TRUE;
201     return new_context;
204 CairoRenderContext*
205 CairoRenderContext::cloneMe(void) const
207     g_assert( _is_valid );
209     return cloneMe(_width, _height);
212 bool
213 CairoRenderContext::setImageTarget(cairo_format_t format)
215     // format cannot be set on an already initialized surface
216     if (_is_valid)
217         return false;
219     switch (format) {
220         case CAIRO_FORMAT_ARGB32:
221         case CAIRO_FORMAT_RGB24:
222         case CAIRO_FORMAT_A8:
223         case CAIRO_FORMAT_A1:
224             _target_format = format;
225             _target = CAIRO_SURFACE_TYPE_IMAGE;
226             return true;
227             break;
228         default:
229             break;
230     }
232     return false;
235 bool
236 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
238 #ifndef CAIRO_HAS_PDF_SURFACE
239     return false;
240 #else
241     _target = CAIRO_SURFACE_TYPE_PDF;
242     _vector_based_target = TRUE;
243 #endif
245     FILE *osf = NULL;
246     FILE *osp = NULL;
248     gsize bytesRead = 0;
249     gsize bytesWritten = 0;
250     GError *error = NULL;
251     gchar *local_fn = g_filename_from_utf8(utf8_fn,
252                                            -1,  &bytesRead,  &bytesWritten, &error);
253     gchar const *fn = local_fn;
255     /* TODO: Replace the below fprintf's with something that does the right thing whether in
256     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
257     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
258     * return code.
259     */
260     if (fn != NULL) {
261         if (*fn == '|') {
262             fn += 1;
263             while (isspace(*fn)) fn += 1;
264 #ifndef WIN32
265             osp = popen(fn, "w");
266 #else
267             osp = _popen(fn, "w");
268 #endif
269             if (!osp) {
270                 fprintf(stderr, "inkscape: popen(%s): %s\n",
271                         fn, strerror(errno));
272                 return false;
273             }
274             _stream = osp;
275         } else if (*fn == '>') {
276             fn += 1;
277             while (isspace(*fn)) fn += 1;
278             Inkscape::IO::dump_fopen_call(fn, "K");
279             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
280             if (!osf) {
281                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
282                         fn, strerror(errno));
283                 return false;
284             }
285             _stream = osf;
286         } else {
287             /* put cwd stuff in here */
288             gchar *qn = ( *fn
289                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
290                 : g_strdup("lpr") );
291 #ifndef WIN32
292             osp = popen(qn, "w");
293 #else
294             osp = _popen(qn, "w");
295 #endif
296             if (!osp) {
297                 fprintf(stderr, "inkscape: popen(%s): %s\n",
298                         qn, strerror(errno));
299                 return false;
300             }
301             g_free(qn);
302             _stream = osp;
303         }
304     }
306     g_free(local_fn);
308     if (_stream) {
309         /* fixme: this is kinda icky */
310 #if !defined(_WIN32) && !defined(__WIN32__)
311         (void) signal(SIGPIPE, SIG_IGN);
312 #endif
313     }
315     return true;
318 bool
319 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
321 #ifndef CAIRO_HAS_PS_SURFACE
322     return false;
323 #else
324     _target = CAIRO_SURFACE_TYPE_PS;
325     _vector_based_target = TRUE;
326 #endif
328     FILE *osf = NULL;
329     FILE *osp = NULL;
331     gsize bytesRead = 0;
332     gsize bytesWritten = 0;
333     GError *error = NULL;
334     gchar *local_fn = g_filename_from_utf8(utf8_fn,
335                                            -1,  &bytesRead,  &bytesWritten, &error);
336     gchar const *fn = local_fn;
338     /* TODO: Replace the below fprintf's with something that does the right thing whether in
339     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
340     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
341     * return code.
342     */
343     if (fn != NULL) {
344         if (*fn == '|') {
345             fn += 1;
346             while (isspace(*fn)) fn += 1;
347 #ifndef WIN32
348             osp = popen(fn, "w");
349 #else
350             osp = _popen(fn, "w");
351 #endif
352             if (!osp) {
353                 fprintf(stderr, "inkscape: popen(%s): %s\n",
354                         fn, strerror(errno));
355                 return false;
356             }
357             _stream = osp;
358         } else if (*fn == '>') {
359             fn += 1;
360             while (isspace(*fn)) fn += 1;
361             Inkscape::IO::dump_fopen_call(fn, "K");
362             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
363             if (!osf) {
364                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
365                         fn, strerror(errno));
366                 return false;
367             }
368             _stream = osf;
369         } else {
370             /* put cwd stuff in here */
371             gchar *qn = ( *fn
372                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
373                 : g_strdup("lpr") );
374 #ifndef WIN32
375             osp = popen(qn, "w");
376 #else
377             osp = _popen(qn, "w");
378 #endif
379             if (!osp) {
380                 fprintf(stderr, "inkscape: popen(%s): %s\n",
381                         qn, strerror(errno));
382                 return false;
383             }
384             g_free(qn);
385             _stream = osp;
386         }
387     }
389     g_free(local_fn);
391     if (_stream) {
392         /* fixme: this is kinda icky */
393 #if !defined(_WIN32) && !defined(__WIN32__)
394         (void) signal(SIGPIPE, SIG_IGN);
395 #endif
396     }
398     return true;
401 void CairoRenderContext::setPSLevel(unsigned int level)
403     _ps_level = level;
406 unsigned int CairoRenderContext::getPSLevel(void)
408     return _ps_level;
411 void CairoRenderContext::setPDFLevel(unsigned int level)
413     _pdf_level = level;
416 void CairoRenderContext::setTextToPath(bool texttopath)
418     _is_texttopath = texttopath;
421 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
423     _is_filtertobitmap = filtertobitmap;
426 bool CairoRenderContext::getFilterToBitmap(void)
428     return _is_filtertobitmap;
431 void CairoRenderContext::setBitmapResolution(int resolution)
433     _bitmapresolution = resolution;
436 int CairoRenderContext::getBitmapResolution(void)
438     return _bitmapresolution;
441 cairo_surface_t*
442 CairoRenderContext::getSurface(void)
444     g_assert( _is_valid );
446     return _surface;
449 bool
450 CairoRenderContext::saveAsPng(const char *file_name)
452     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
453     if (status)
454         return false;
455     else
456         return true;
459 void
460 CairoRenderContext::setRenderMode(CairoRenderMode mode)
462     switch (mode) {
463         case RENDER_MODE_NORMAL:
464         case RENDER_MODE_CLIP:
465             _render_mode = mode;
466             break;
467         default:
468             _render_mode = RENDER_MODE_NORMAL;
469             break;
470     }
473 CairoRenderContext::CairoRenderMode
474 CairoRenderContext::getRenderMode(void) const
476     return _render_mode;
479 void
480 CairoRenderContext::setClipMode(CairoClipMode mode)
482     switch (mode) {
483         case CLIP_MODE_PATH:
484         case CLIP_MODE_MASK:
485             _clip_mode = mode;
486             break;
487         default:
488             _clip_mode = CLIP_MODE_PATH;
489             break;
490     }
493 CairoRenderContext::CairoClipMode
494 CairoRenderContext::getClipMode(void) const
496     return _clip_mode;
499 CairoRenderState*
500 CairoRenderContext::_createState(void)
502     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
503     g_assert( state != NULL );
505     state->has_filtereffect = FALSE;
506     state->merge_opacity = TRUE;
507     state->opacity = 1.0;
508     state->need_layer = FALSE;
509     state->has_overflow = FALSE;
510     state->parent_has_userspace = FALSE;
511     state->clip_path = NULL;
512     state->mask = NULL;
514     return state;
517 void
518 CairoRenderContext::pushLayer(void)
520     g_assert( _is_valid );
522     TRACE(("--pushLayer\n"));
523     cairo_push_group(_cr);
525     // clear buffer
526     if (!_vector_based_target) {
527         cairo_save(_cr);
528         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
529         cairo_paint(_cr);
530         cairo_restore(_cr);
531     }
534 void
535 CairoRenderContext::popLayer(void)
537     g_assert( _is_valid );
539     float opacity = _state->opacity;
540     TRACE(("--popLayer w/ %f\n", opacity));
542     // apply clipPath or mask if present
543     SPClipPath *clip_path = _state->clip_path;
544     SPMask *mask = _state->mask;
545     if (clip_path || mask) {
547         CairoRenderContext *clip_ctx = 0;
548         cairo_surface_t *clip_mask = 0;
550         if (clip_path) {
551             if (_render_mode == RENDER_MODE_CLIP)
552                 mask = NULL;    // disable mask when performing nested clipping
554             if (_vector_based_target) {
555                 setClipMode(CLIP_MODE_PATH);
556                 if (!mask) {
557                     cairo_pop_group_to_source(_cr);
558                     _renderer->applyClipPath(this, clip_path);
559                     if (opacity == 1.0)
560                         cairo_paint(_cr);
561                     else
562                         cairo_paint_with_alpha(_cr, opacity);
564                 } else {
565                     // the clipPath will be applied before masking
566                 }
567             } else {
569                 // setup a new rendering context
570                 clip_ctx = _renderer->createContext();
571                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
572                 clip_ctx->setClipMode(CLIP_MODE_MASK);
573                 if (!clip_ctx->setupSurface(_width, _height)) {
574                     TRACE(("setupSurface failed\n"));
575                     _renderer->destroyContext(clip_ctx);
576                     return;
577                 }
579                 // clear buffer
580                 cairo_save(clip_ctx->_cr);
581                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
582                 cairo_paint(clip_ctx->_cr);
583                 cairo_restore(clip_ctx->_cr);
585                 // if a mask won't be applied set opacity too
586                 if (!mask)
587                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
588                 else
589                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
591                 // copy over the correct CTM
592                 if (_state->parent_has_userspace)
593                     clip_ctx->setTransform(&getParentState()->transform);
594                 else
595                     clip_ctx->setTransform(&_state->transform);
597                 // apply the clip path
598                 clip_ctx->pushState();
599                 _renderer->applyClipPath(clip_ctx, clip_path);
600                 clip_ctx->popState();
602                 clip_mask = clip_ctx->getSurface();
603                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
605                 if (!mask) {
606                     cairo_pop_group_to_source(_cr);
607                     cairo_mask_surface(_cr, clip_mask, 0, 0);
608                     _renderer->destroyContext(clip_ctx);
609                 }
610             }
611         }
613         if (mask) {
614             // create rendering context for mask
615             CairoRenderContext *mask_ctx = _renderer->createContext();
616             mask_ctx->setupSurface(_width, _height);
618             // set rendering mode to normal
619             setRenderMode(RENDER_MODE_NORMAL);
621             // copy the correct CTM to mask context
622             if (_state->parent_has_userspace)
623                 mask_ctx->setTransform(&getParentState()->transform);
624             else
625                 mask_ctx->setTransform(&_state->transform);
627             // render mask contents to mask_ctx
628             _renderer->applyMask(mask_ctx, mask);
630             TEST(mask_ctx->saveAsPng("mask.png"));
632             // composite with clip mask
633             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
634                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
635                 _renderer->destroyContext(clip_ctx);
636             }
638             cairo_surface_t *mask_image = mask_ctx->getSurface();
639             int width = cairo_image_surface_get_width(mask_image);
640             int height = cairo_image_surface_get_height(mask_image);
641             int stride = cairo_image_surface_get_stride(mask_image);
642             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
644             // premultiply with opacity
645             if (_state->opacity != 1.0) {
646                 TRACE(("premul w/ %f\n", opacity));
647                 guint8 int_opacity = (guint8)(255 * opacity);
648                 for (int row = 0 ; row < height; row++) {
649                     unsigned char *row_data = pixels + (row * stride);
650                     for (int i = 0 ; i < width; i++) {
651                         guint32 *pixel = (guint32 *)row_data + i;
652                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
653                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
654                                 ((*pixel & 0x000000ff)      ) * 4688) *
655                                 int_opacity);
656                     }
657                 }
658             }
660             cairo_pop_group_to_source(_cr);
661             if (_clip_mode == CLIP_MODE_PATH) {
662                 // we have to do the clipping after cairo_pop_group_to_source
663                 _renderer->applyClipPath(this, clip_path);
664             }
665             // apply the mask onto the layer
666             cairo_mask_surface(_cr, mask_image, 0, 0);
667             _renderer->destroyContext(mask_ctx);
668         }
669     } else {
670         cairo_pop_group_to_source(_cr);
671         if (opacity == 1.0)
672             cairo_paint(_cr);
673         else
674             cairo_paint_with_alpha(_cr, opacity);
675     }
678 void
679 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
681     g_assert( _is_valid );
683     // here it should be checked whether the current clip winding changed
684     // so we could switch back to masked clipping
685     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
686         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
687     } else {
688         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
689     }
690     addBpath(bp);
693 void
694 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
696     g_assert( _is_valid );
698     cairo_rectangle(_cr, x, y, width, height);
699     cairo_clip(_cr);
702 bool
703 CairoRenderContext::setupSurface(double width, double height)
705     // Is the surface already set up?
706     if (_is_valid)
707         return true;
709     if (_vector_based_target && _stream == NULL)
710         return false;
712     cairo_surface_t *surface = NULL;
713     switch (_target) {
714         case CAIRO_SURFACE_TYPE_IMAGE:
715             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
716             break;
717 #ifdef CAIRO_HAS_PDF_SURFACE
718         case CAIRO_SURFACE_TYPE_PDF:
719             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
720             break;
721 #endif
722 #ifdef CAIRO_HAS_PS_SURFACE
723         case CAIRO_SURFACE_TYPE_PS:
724             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
725 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
726             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
727                 return FALSE;
728             }
729             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
730 #endif
731             break;
732 #endif
733         default:
734             return false;
735             break;
736     }
738     return _finishSurfaceSetup (surface);
741 bool
742 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
744     if (_is_valid || !surface)
745         return false;
747     _vector_based_target = is_vector;
748     bool ret = _finishSurfaceSetup (surface);
749     if (ret)
750         cairo_surface_reference (surface);
751     return ret;
754 bool
755 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
757     if(surface == NULL) {
758         return FALSE;
759     }
760     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
761         return FALSE;
762     }
764     _cr = cairo_create(surface);
765     _surface = surface;
767     if (_vector_based_target) {
768         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
769     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
770         // set background color on non-alpha surfaces
771         // TODO: bgcolor should be derived from SPDocument
772         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
773         cairo_rectangle(_cr, 0, 0, _width, _height);
774         cairo_fill(_cr);
775     }
777     _is_valid = TRUE;
779     return true;
782 bool
783 CairoRenderContext::finish(void)
785     g_assert( _is_valid );
787     if (_vector_based_target)
788         cairo_show_page(_cr);
790     cairo_destroy(_cr);
791     cairo_surface_destroy(_surface);
792     _cr = NULL;
793     _surface = NULL;
795     if (_layout)
796         g_object_unref(_layout);
798     _is_valid = FALSE;
800     if (_vector_based_target && _stream) {
801         /* Flush stream to be sure. */
802         (void) fflush(_stream);
804         fclose(_stream);
805         _stream = NULL;
806     }
808     return true;
811 void
812 CairoRenderContext::transform(NR::Matrix const *transform)
814     g_assert( _is_valid );
816     cairo_matrix_t matrix;
817     _initCairoMatrix(&matrix, transform);
818     cairo_transform(_cr, &matrix);
820     // store new CTM
821     getTransform(&_state->transform);
824 void
825 CairoRenderContext::setTransform(NR::Matrix const *transform)
827     g_assert( _is_valid );
829     cairo_matrix_t matrix;
830     _initCairoMatrix(&matrix, transform);
831     cairo_set_matrix(_cr, &matrix);
832     _state->transform = *transform;
835 void
836 CairoRenderContext::getTransform(NR::Matrix *copy) const
838     g_assert( _is_valid );
840     cairo_matrix_t ctm;
841     cairo_get_matrix(_cr, &ctm);
842     (*copy)[0] = ctm.xx;
843     (*copy)[1] = ctm.yx;
844     (*copy)[2] = ctm.xy;
845     (*copy)[3] = ctm.yy;
846     (*copy)[4] = ctm.x0;
847     (*copy)[5] = ctm.y0;
850 void
851 CairoRenderContext::getParentTransform(NR::Matrix *copy) const
853     g_assert( _is_valid );
855     CairoRenderState *parent_state = getParentState();
856     memcpy(copy, &parent_state->transform, sizeof(NR::Matrix));
859 void
860 CairoRenderContext::pushState(void)
862     g_assert( _is_valid );
864     cairo_save(_cr);
866     CairoRenderState *new_state = _createState();
867     // copy current state's transform
868     new_state->transform = _state->transform;
869     _state_stack = g_slist_prepend(_state_stack, new_state);
870     _state = new_state;
873 void
874 CairoRenderContext::popState(void)
876     g_assert( _is_valid );
878     cairo_restore(_cr);
880     g_free(_state_stack->data);
881     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
882     _state = (CairoRenderState*)_state_stack->data;
884     g_assert( g_slist_length(_state_stack) > 0 );
887 static bool pattern_hasItemChildren (SPPattern *pat)
889     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
890         if (SP_IS_ITEM (child)) {
891             return true;
892         }
893     }
894     return false;
897 cairo_pattern_t*
898 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
900     g_assert( SP_IS_PATTERN(paintserver) );
902     SPPattern *pat = SP_PATTERN (paintserver);
904     NR::Matrix ps2user, pcs2dev;
905     ps2user.set_identity();
906     pcs2dev.set_identity();
908     double x = pattern_x(pat);
909     double y = pattern_y(pat);
910     double width = pattern_width(pat);
911     double height = pattern_height(pat);
912     double bbox_width_scaler;
913     double bbox_height_scaler;
915     TRACE(("%f x %f pattern\n", width, height));
917     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
918         //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
919         bbox_width_scaler = pbox->x1 - pbox->x0;
920         bbox_height_scaler = pbox->y1 - pbox->y0;
921         ps2user[4] = x * bbox_width_scaler + pbox->x0;
922         ps2user[5] = y * bbox_height_scaler + pbox->y0;
923     } else {
924         bbox_width_scaler = 1.0;
925         bbox_height_scaler = 1.0;
926         ps2user[4] = x;
927         ps2user[5] = y;
928     }
930     // apply pattern transformation
931     NR::Matrix pattern_transform(pattern_patternTransform(pat));
932     ps2user *= pattern_transform;
934     // create pattern contents coordinate system
935     if (pat->viewBox_set) {
936         NRRect *view_box = pattern_viewBox(pat);
938         double x, y, w, h;
939         double view_width, view_height;
940         x = 0;
941         y = 0;
942         w = width * bbox_width_scaler;
943         h = height * bbox_height_scaler;
945         view_width = view_box->x1 - view_box->x0;
946         view_height = view_box->y1 - view_box->y0;
948         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
949         pcs2dev[0] = w / view_width;
950         pcs2dev[3] = h / view_height;
951         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
952         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
953     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
954         pcs2dev[0] = pbox->x1 - pbox->x0;
955         pcs2dev[3] = pbox->y1 - pbox->y0;
956     }
958     // calculate the size of the surface which has to be created
959     // the scaling needs to be taken into account in the ctm after the pattern transformation
960     NR::Matrix temp;
961     temp = pattern_transform * _state->transform;
962     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
963     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
965     if (_vector_based_target) {
966         // eliminate PT_PER_PX mul from these
967         width_scaler *= 1.25;
968         height_scaler *= 1.25;
969     }
970     double surface_width = ceil(bbox_width_scaler * width_scaler * width);
971     double surface_height = ceil(bbox_height_scaler * height_scaler * height);
972     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
973     // create new rendering context
974     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
976     // adjust the size of the painted pattern to fit exactly the created surface
977     // this has to be done because of the rounding to obtain an integer pattern surface width/height
978     double scale_width = surface_width / (bbox_width_scaler * width);
979     double scale_height = surface_height / (bbox_height_scaler * height);
980     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
981         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
982         pcs2dev *= NR::scale(1.0 / scale_width, 1.0 / scale_height);
983         ps2user *= NR::scale(scale_width, scale_height);
984     }
986     pattern_ctx->setTransform(&pcs2dev);
987     pattern_ctx->pushState();
989     // create arena and group
990     NRArena *arena = NRArena::create();
991     unsigned dkey = sp_item_display_key_new(1);
993     // show items and render them
994     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
995         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
996             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
997                 if (SP_IS_ITEM (child)) {
998                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
999                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1000                 }
1001             }
1002             break; // do not go further up the chain if children are found
1003         }
1004     }
1006     pattern_ctx->popState();
1008     // setup a cairo_pattern_t
1009     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1010     TEST(pattern_ctx->saveAsPng("pattern.png"));
1011     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1012     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1014     // set pattern transformation
1015     cairo_matrix_t pattern_matrix;
1016     _initCairoMatrix(&pattern_matrix, &ps2user);
1017     cairo_matrix_invert(&pattern_matrix);
1018     cairo_pattern_set_matrix(result, &pattern_matrix);
1020     delete pattern_ctx;
1022     // hide all items
1023     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1024         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1025             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1026                 if (SP_IS_ITEM (child)) {
1027                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1028                 }
1029             }
1030             break; // do not go further up the chain if children are found
1031         }
1032     }
1034     return result;
1037 cairo_pattern_t*
1038 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1039                                                  NRRect const *pbox, float alpha)
1041     cairo_pattern_t *pattern = NULL;
1042     bool apply_bbox2user = FALSE;
1044     if (SP_IS_LINEARGRADIENT (paintserver)) {
1046             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1048             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1050             NR::Point p1 (lg->x1.computed, lg->y1.computed);
1051             NR::Point p2 (lg->x2.computed, lg->y2.computed);
1052             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1053                 // convert to userspace
1054                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1055                 p1 *= bbox2user;
1056                 p2 *= bbox2user;
1057             }
1059             // create linear gradient pattern
1060             pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1062             // add stops
1063             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1064                 float rgb[3];
1065                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1066                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1067             }
1068     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1070         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1072         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1074         NR::Point c (rg->cx.computed, rg->cy.computed);
1075         NR::Point f (rg->fx.computed, rg->fy.computed);
1076         double r = rg->r.computed;
1077         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1078             apply_bbox2user = true;
1080         // create radial gradient pattern
1081         pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1083         // add stops
1084         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1085             float rgb[3];
1086             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1087             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1088         }
1089     } else if (SP_IS_PATTERN (paintserver)) {
1091         pattern = _createPatternPainter(paintserver, pbox);
1092     } else {
1093         return NULL;
1094     }
1096     if (pattern && SP_IS_GRADIENT (paintserver)) {
1097         SPGradient *g = SP_GRADIENT(paintserver);
1099         // set extend type
1100         SPGradientSpread spread = sp_gradient_get_spread(g);
1101         switch (spread) {
1102             case SP_GRADIENT_SPREAD_REPEAT: {
1103                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1104                 break;
1105             }
1106             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1107                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1108                 break;
1109             }
1110             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1111                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1112                 break;
1113             }
1114             default: {
1115                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1116                 break;
1117             }
1118         }
1120         cairo_matrix_t pattern_matrix;
1121         if (g->gradientTransform_set) {
1122             // apply gradient transformation
1123             cairo_matrix_init(&pattern_matrix,
1124                 g->gradientTransform[0], g->gradientTransform[1],
1125                 g->gradientTransform[2], g->gradientTransform[3],
1126                 g->gradientTransform[4], g->gradientTransform[5]);
1127         } else {
1128             cairo_matrix_init_identity (&pattern_matrix);
1129         }
1131         if (apply_bbox2user) {
1132             // convert to userspace
1133             cairo_matrix_t bbox2user;
1134             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1135             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1136         }
1137         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1138         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1139     }
1141     return pattern;
1144 void
1145 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1147     g_return_if_fail( style->fill.isColor()
1148                       || style->fill.isPaintserver() );
1150     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1151     if (_state->merge_opacity) {
1152         alpha *= _state->opacity;
1153         TRACE(("merged op=%f\n", alpha));
1154     }
1156     if (style->fill.isColor()) {
1157         float rgb[3];
1158         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1160         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1161     } else {
1162         g_assert( style->fill.isPaintserver()
1163                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1164                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1166         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1168         if (pattern) {
1169             cairo_set_source(_cr, pattern);
1170             cairo_pattern_destroy(pattern);
1171         }
1172     }
1175 void
1176 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1178     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1179     if (_state->merge_opacity)
1180         alpha *= _state->opacity;
1182     if (style->stroke.isColor()) {
1183         float rgb[3];
1184         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1186         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1187     } else {
1188         g_assert( style->fill.isPaintserver()
1189                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1190                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1192         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1194         if (pattern) {
1195             cairo_set_source(_cr, pattern);
1196             cairo_pattern_destroy(pattern);
1197         }
1198     }
1200     if (style->stroke_dash.n_dash   &&
1201         style->stroke_dash.dash       )
1202     {
1203         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1204     } else {
1205         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1206     }
1208     cairo_set_line_width(_cr, style->stroke_width.computed);
1210     // set line join type
1211     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1212     switch (style->stroke_linejoin.computed) {
1213         case SP_STROKE_LINEJOIN_MITER:
1214             join = CAIRO_LINE_JOIN_MITER;
1215             break;
1216         case SP_STROKE_LINEJOIN_ROUND:
1217             join = CAIRO_LINE_JOIN_ROUND;
1218             break;
1219         case SP_STROKE_LINEJOIN_BEVEL:
1220             join = CAIRO_LINE_JOIN_BEVEL;
1221             break;
1222     }
1223     cairo_set_line_join(_cr, join);
1225     // set line cap type
1226     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1227     switch (style->stroke_linecap.computed) {
1228         case SP_STROKE_LINECAP_BUTT:
1229             cap = CAIRO_LINE_CAP_BUTT;
1230             break;
1231         case SP_STROKE_LINECAP_ROUND:
1232             cap = CAIRO_LINE_CAP_ROUND;
1233             break;
1234         case SP_STROKE_LINECAP_SQUARE:
1235             cap = CAIRO_LINE_CAP_SQUARE;
1236             break;
1237     }
1238     cairo_set_line_cap(_cr, cap);
1239     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1242 bool
1243 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1245     g_assert( _is_valid );
1247     if (_render_mode == RENDER_MODE_CLIP) {
1248         if (_clip_mode == CLIP_MODE_PATH) {
1249             addClipPath(bpath->path, &style->fill_rule);
1250         } else {
1251             setBpath(bpath->path);
1252             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1253                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1254             } else {
1255                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1256             }
1257             cairo_fill(_cr);
1258             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1259         }
1260         return true;
1261     }
1263     if (style->fill.isNone() && style->stroke.isNone())
1264         return true;
1266     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1267                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1269     if (!need_layer)
1270         cairo_save(_cr);
1271     else
1272         pushLayer();
1274     if (!style->fill.isNone()) {
1275         _setFillStyle(style, pbox);
1276         setBpath(bpath->path);
1278         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1279             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1280         } else {
1281             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1282         }
1284         if (style->stroke.isNone())
1285             cairo_fill(_cr);
1286         else
1287             cairo_fill_preserve(_cr);
1288     }
1290     if (!style->stroke.isNone()) {
1291         _setStrokeStyle(style, pbox);
1292         if (style->fill.isNone())
1293             setBpath(bpath->path);
1295         cairo_stroke(_cr);
1296     }
1298     if (need_layer)
1299         popLayer();
1300     else
1301         cairo_restore(_cr);
1303     return true;
1306 bool
1307 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1308                                 NR::Matrix const *image_transform, SPStyle const *style)
1310     g_assert( _is_valid );
1312     if (_render_mode == RENDER_MODE_CLIP)
1313         return true;
1315     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1316     if (!px_rgba)
1317         return false;
1319     float opacity;
1320     if (_state->merge_opacity)
1321         opacity = _state->opacity;
1322     else
1323         opacity = 1.0;
1325     // make a copy of the original pixbuf with premultiplied alpha
1326     // if we pass the original pixbuf it will get messed up
1327     for (unsigned i = 0; i < h; i++) {
1328         for (unsigned j = 0; j < w; j++) {
1329             guchar const *src = px + i * rs + j * 4;
1330             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1331             guchar r, g, b, alpha_dst;
1333             // calculate opacity-modified alpha
1334             alpha_dst = src[3];
1335             if (opacity != 1.0 && _vector_based_target)
1336                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1338             // premul alpha (needed because this will be undone by cairo-pdf)
1339             r = src[0]*alpha_dst/255;
1340             g = src[1]*alpha_dst/255;
1341             b = src[2]*alpha_dst/255;
1343             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1344         }
1345     }
1347     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1348     if (cairo_surface_status(image_surface)) {
1349         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1350         return false;
1351     }
1353     // setup automatic freeing of the image data when destroying the surface
1354     static cairo_user_data_key_t key;
1355     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1357     cairo_save(_cr);
1359     // scaling by width & height is not needed because it will be done by Cairo
1360     if (image_transform)
1361         transform(image_transform);
1363     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1365     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1366     if (_vector_based_target) {
1367         cairo_new_path(_cr);
1368         cairo_rectangle(_cr, 0, 0, w, h);
1369         cairo_clip(_cr);
1370     }
1372     if (_vector_based_target)
1373         cairo_paint(_cr);
1374     else
1375         cairo_paint_with_alpha(_cr, opacity);
1377     cairo_restore(_cr);
1379     cairo_surface_destroy(image_surface);
1381     return true;
1384 #define GLYPH_ARRAY_SIZE 64
1386 unsigned int
1387 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1389     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1390     cairo_glyph_t *glyphs = glyph_array;
1391     unsigned int num_glyphs = glyphtext.size();
1392     if (num_glyphs > GLYPH_ARRAY_SIZE)
1393         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1395     unsigned int num_invalid_glyphs = 0;
1396     unsigned int i = 0;
1397     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1398         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1399         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1400         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1401             TRACE(("INVALID GLYPH found\n"));
1402             num_invalid_glyphs++;
1403             continue;
1404         }
1405         glyphs[i - num_invalid_glyphs].index = it_info->index;
1406         glyphs[i - num_invalid_glyphs].x = it_info->x;
1407         glyphs[i - num_invalid_glyphs].y = it_info->y;
1408         i++;
1409     }
1411     if (is_stroke || _is_texttopath)
1412         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1413     else
1414         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1416     if (num_glyphs > GLYPH_ARRAY_SIZE)
1417         g_free(glyphs);
1419     return num_glyphs - num_invalid_glyphs;
1422 bool
1423 CairoRenderContext::renderGlyphtext(PangoFont *font, NR::Matrix const *font_matrix,
1424                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1426     // create a cairo_font_face from PangoFont
1427     double size = style->font_size.computed;
1428     cairo_font_face_t *font_face = NULL;
1430     FcPattern *fc_pattern = NULL;
1431     
1432 #ifdef USE_PANGO_WIN32
1433 # ifdef CAIRO_HAS_WIN32_FONT
1434     LOGFONTA *lfa = pango_win32_font_logfont(font);
1435     LOGFONTW lfw;
1437     ZeroMemory(&lfw, sizeof(LOGFONTW));
1438     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1439     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1440     
1441     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1442 # endif
1443 #else
1444 # ifdef CAIRO_HAS_FT_FONT
1445     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1446     fc_pattern = fc_font->font_pattern;
1447     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1448 # endif
1449 #endif
1450     
1451     cairo_save(_cr);
1452     cairo_set_font_face(_cr, font_face);
1454     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1455         size = 12.0;
1457     // set the given font matrix
1458     cairo_matrix_t matrix;
1459     _initCairoMatrix(&matrix, font_matrix);
1460     cairo_set_font_matrix(_cr, &matrix);
1462     if (_render_mode == RENDER_MODE_CLIP) {
1463         if (_clip_mode == CLIP_MODE_MASK) {
1464             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1465                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1466             } else {
1467                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1468             }
1469             _showGlyphs(_cr, font, glyphtext, FALSE);
1470         } else {
1471             // just add the glyph paths to the current context
1472             _showGlyphs(_cr, font, glyphtext, TRUE);
1473         }
1474     } else {
1476         if (style->fill.isColor() || style->fill.isPaintserver()) {
1477             // set fill style
1478             _setFillStyle(style, NULL);
1480             _showGlyphs(_cr, font, glyphtext, FALSE);
1481         }
1483         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1484             // set stroke style
1485             _setStrokeStyle(style, NULL);
1487             // paint stroke
1488             _showGlyphs(_cr, font, glyphtext, TRUE);
1489             cairo_stroke(_cr);
1490         }
1491     }
1493     cairo_restore(_cr);
1495     if (font_face)
1496         cairo_font_face_destroy(font_face);
1498     return true;
1501 /* Helper functions */
1503 void
1504 CairoRenderContext::addBpath(NArtBpath const *bp)
1506     bool closed = false;
1507     while (bp->code != NR_END) {
1508         switch (bp->code) {
1509             case NR_MOVETO:
1510                 if (closed) {
1511                     cairo_close_path(_cr);
1512                 }
1513                 closed = true;
1514                 cairo_move_to(_cr, bp->x3, bp->y3);
1515                 break;
1516             case NR_MOVETO_OPEN:
1517                 if (closed) {
1518                     cairo_close_path(_cr);
1519                 }
1520                 closed = false;
1521                 cairo_move_to(_cr, bp->x3, bp->y3);
1522                 break;
1523             case NR_LINETO:
1524                 cairo_line_to(_cr, bp->x3, bp->y3);
1525                 break;
1526             case NR_CURVETO:
1527                 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1528                 break;
1529             default:
1530                 break;
1531         }
1532         bp += 1;
1533     }
1534     if (closed) {
1535         cairo_close_path(_cr);
1536     }
1539 void
1540 CairoRenderContext::setBpath(NArtBpath const *bp)
1542     cairo_new_path(_cr);
1543     if (bp)
1544         addBpath(bp);
1547 void
1548 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1550     cairo_matrix_t matrix;
1552     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1553     cairo_transform(cr, &matrix);
1556 void
1557 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NR::Matrix const *transform)
1559     matrix->xx = (*transform)[0];
1560     matrix->yx = (*transform)[1];
1561     matrix->xy = (*transform)[2];
1562     matrix->yy = (*transform)[3];
1563     matrix->x0 = (*transform)[4];
1564     matrix->y0 = (*transform)[5];
1567 void
1568 CairoRenderContext::_concatTransform(cairo_t *cr, NR::Matrix const *transform)
1570     _concatTransform(cr, (*transform)[0], (*transform)[1],
1571                      (*transform)[2], (*transform)[3],
1572                      (*transform)[4], (*transform)[5]);
1575 static cairo_status_t
1576 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1578     size_t written;
1579     FILE *file = (FILE*)closure;
1581     written = fwrite (data, 1, length, file);
1583     if (written == length)
1584         return CAIRO_STATUS_SUCCESS;
1585     else
1586         return CAIRO_STATUS_WRITE_ERROR;
1589 #include "clear-n_.h"
1591 }  /* namespace Internal */
1592 }  /* namespace Extension */
1593 }  /* namespace Inkscape */
1595 #undef TRACE
1596 #undef TEST
1598 /* End of GNU GPL code */
1601 /*
1602   Local Variables:
1603   mode:c++
1604   c-file-style:"stroustrup"
1605   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1606   indent-tabs-mode:nil
1607   fill-column:99
1608   End:
1609 */
1610 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :