Code

f2ee32c74c8274fcfe10c627aa2d10ab120846bd
[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>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 #define TRACE(_args)
84 //#define TEST(_args) _args
85 #define TEST(_args)
87 // FIXME: expose these from sp-clippath/mask.cpp
88 struct SPClipPathView {
89     SPClipPathView *next;
90     unsigned int key;
91     NRArenaItem *arenaitem;
92     NRRect bbox;
93 };
95 struct SPMaskView {
96     SPMaskView *next;
97     unsigned int key;
98     NRArenaItem *arenaitem;
99     NRRect bbox;
100 };
102 namespace Inkscape {
103 namespace Extension {
104 namespace Internal {
106 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
108 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
109     _dpi(72),
110     _pdf_level(0),
111     _ps_level(1),
112     _eps(false),
113     _is_texttopath(FALSE),
114     _is_filtertobitmap(FALSE),
115     _bitmapresolution(72),
116     _stream(NULL),
117     _is_valid(FALSE),
118     _vector_based_target(FALSE),
119     _cr(NULL),
120     _surface(NULL),
121     _target(CAIRO_SURFACE_TYPE_IMAGE),
122     _target_format(CAIRO_FORMAT_ARGB32),
123     _layout(NULL),
124     _state(NULL),
125     _renderer(parent),
126     _render_mode(RENDER_MODE_NORMAL),
127     _clip_mode(CLIP_MODE_MASK)
128 {}
130 CairoRenderContext::~CairoRenderContext(void)
132     if (_cr) cairo_destroy(_cr);
133     if (_surface) cairo_surface_destroy(_surface);
134     if (_layout) g_object_unref(_layout);
137 CairoRenderer*
138 CairoRenderContext::getRenderer(void) const
140     return _renderer;
143 CairoRenderState*
144 CairoRenderContext::getCurrentState(void) const
146     return _state;
149 CairoRenderState*
150 CairoRenderContext::getParentState(void) const
152     // if this is the root node just return it
153     if (g_slist_length(_state_stack) == 1) {
154         return _state;
155     } else {
156         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
157     }
160 void
161 CairoRenderContext::setStateForStyle(SPStyle const *style)
163     // only opacity & overflow is stored for now
164     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
165     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
166     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
168     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
169         _state->merge_opacity = FALSE;
171     // disable rendering of opacity if there's a stroke on the fill
172     if (_state->merge_opacity
173         && !style->fill.isNone()
174         && !style->stroke.isNone())
175         _state->merge_opacity = FALSE;
178 /**
179  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
180  *
181  * \param width     width of the surface to be created
182  * \param height    height of the surface to be created
183  */
184 CairoRenderContext*
185 CairoRenderContext::cloneMe(double width, double height) const
187     g_assert( _is_valid );
188     g_assert( width > 0.0 && height > 0.0 );
190     CairoRenderContext *new_context = _renderer->createContext();
191     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
192                                                             (int)ceil(width), (int)ceil(height));
193     new_context->_cr = cairo_create(surface);
194     new_context->_surface = surface;
195     new_context->_is_valid = TRUE;
197     return new_context;
200 CairoRenderContext*
201 CairoRenderContext::cloneMe(void) const
203     g_assert( _is_valid );
205     return cloneMe(_width, _height);
208 bool
209 CairoRenderContext::setImageTarget(cairo_format_t format)
211     // format cannot be set on an already initialized surface
212     if (_is_valid)
213         return false;
215     switch (format) {
216         case CAIRO_FORMAT_ARGB32:
217         case CAIRO_FORMAT_RGB24:
218         case CAIRO_FORMAT_A8:
219         case CAIRO_FORMAT_A1:
220             _target_format = format;
221             _target = CAIRO_SURFACE_TYPE_IMAGE;
222             return true;
223             break;
224         default:
225             break;
226     }
228     return false;
231 bool
232 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
234 #ifndef CAIRO_HAS_PDF_SURFACE
235     return false;
236 #else
237     _target = CAIRO_SURFACE_TYPE_PDF;
238     _vector_based_target = TRUE;
239 #endif
241     FILE *osf = NULL;
242     FILE *osp = NULL;
244     gsize bytesRead = 0;
245     gsize bytesWritten = 0;
246     GError *error = NULL;
247     gchar *local_fn = g_filename_from_utf8(utf8_fn,
248                                            -1,  &bytesRead,  &bytesWritten, &error);
249     gchar const *fn = local_fn;
251     /* TODO: Replace the below fprintf's with something that does the right thing whether in
252     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
253     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
254     * return code.
255     */
256     if (fn != NULL) {
257         if (*fn == '|') {
258             fn += 1;
259             while (isspace(*fn)) fn += 1;
260 #ifndef WIN32
261             osp = popen(fn, "w");
262 #else
263             osp = _popen(fn, "w");
264 #endif
265             if (!osp) {
266                 fprintf(stderr, "inkscape: popen(%s): %s\n",
267                         fn, strerror(errno));
268                 return false;
269             }
270             _stream = osp;
271         } else if (*fn == '>') {
272             fn += 1;
273             while (isspace(*fn)) fn += 1;
274             Inkscape::IO::dump_fopen_call(fn, "K");
275             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
276             if (!osf) {
277                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
278                         fn, strerror(errno));
279                 return false;
280             }
281             _stream = osf;
282         } else {
283             /* put cwd stuff in here */
284             gchar *qn = ( *fn
285                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
286                 : g_strdup("lpr") );
287 #ifndef WIN32
288             osp = popen(qn, "w");
289 #else
290             osp = _popen(qn, "w");
291 #endif
292             if (!osp) {
293                 fprintf(stderr, "inkscape: popen(%s): %s\n",
294                         qn, strerror(errno));
295                 return false;
296             }
297             g_free(qn);
298             _stream = osp;
299         }
300     }
302     g_free(local_fn);
304     if (_stream) {
305         /* fixme: this is kinda icky */
306 #if !defined(_WIN32) && !defined(__WIN32__)
307         (void) signal(SIGPIPE, SIG_IGN);
308 #endif
309     }
311     return true;
314 bool
315 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
317 #ifndef CAIRO_HAS_PS_SURFACE
318     return false;
319 #else
320     _target = CAIRO_SURFACE_TYPE_PS;
321     _vector_based_target = TRUE;
322 #endif
324     FILE *osf = NULL;
325     FILE *osp = NULL;
327     gsize bytesRead = 0;
328     gsize bytesWritten = 0;
329     GError *error = NULL;
330     gchar *local_fn = g_filename_from_utf8(utf8_fn,
331                                            -1,  &bytesRead,  &bytesWritten, &error);
332     gchar const *fn = local_fn;
334     /* TODO: Replace the below fprintf's with something that does the right thing whether in
335     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
336     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
337     * return code.
338     */
339     if (fn != NULL) {
340         if (*fn == '|') {
341             fn += 1;
342             while (isspace(*fn)) fn += 1;
343 #ifndef WIN32
344             osp = popen(fn, "w");
345 #else
346             osp = _popen(fn, "w");
347 #endif
348             if (!osp) {
349                 fprintf(stderr, "inkscape: popen(%s): %s\n",
350                         fn, strerror(errno));
351                 return false;
352             }
353             _stream = osp;
354         } else if (*fn == '>') {
355             fn += 1;
356             while (isspace(*fn)) fn += 1;
357             Inkscape::IO::dump_fopen_call(fn, "K");
358             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
359             if (!osf) {
360                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
361                         fn, strerror(errno));
362                 return false;
363             }
364             _stream = osf;
365         } else {
366             /* put cwd stuff in here */
367             gchar *qn = ( *fn
368                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
369                 : g_strdup("lpr") );
370 #ifndef WIN32
371             osp = popen(qn, "w");
372 #else
373             osp = _popen(qn, "w");
374 #endif
375             if (!osp) {
376                 fprintf(stderr, "inkscape: popen(%s): %s\n",
377                         qn, strerror(errno));
378                 return false;
379             }
380             g_free(qn);
381             _stream = osp;
382         }
383     }
385     g_free(local_fn);
387     if (_stream) {
388         /* fixme: this is kinda icky */
389 #if !defined(_WIN32) && !defined(__WIN32__)
390         (void) signal(SIGPIPE, SIG_IGN);
391 #endif
392     }
394     return true;
397 void CairoRenderContext::setPSLevel(unsigned int level)
399     _ps_level = level;
402 void CairoRenderContext::setEPS(bool eps)
404     _eps = eps;
407 unsigned int CairoRenderContext::getPSLevel(void)
409     return _ps_level;
412 void CairoRenderContext::setPDFLevel(unsigned int level)
414     _pdf_level = level;
417 void CairoRenderContext::setTextToPath(bool texttopath)
419     _is_texttopath = texttopath;
422 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
424     _is_filtertobitmap = filtertobitmap;
427 bool CairoRenderContext::getFilterToBitmap(void)
429     return _is_filtertobitmap;
432 void CairoRenderContext::setBitmapResolution(int resolution)
434     _bitmapresolution = resolution;
437 int CairoRenderContext::getBitmapResolution(void)
439     return _bitmapresolution;
442 cairo_surface_t*
443 CairoRenderContext::getSurface(void)
445     g_assert( _is_valid );
447     return _surface;
450 bool
451 CairoRenderContext::saveAsPng(const char *file_name)
453     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
454     if (status)
455         return false;
456     else
457         return true;
460 void
461 CairoRenderContext::setRenderMode(CairoRenderMode mode)
463     switch (mode) {
464         case RENDER_MODE_NORMAL:
465         case RENDER_MODE_CLIP:
466             _render_mode = mode;
467             break;
468         default:
469             _render_mode = RENDER_MODE_NORMAL;
470             break;
471     }
474 CairoRenderContext::CairoRenderMode
475 CairoRenderContext::getRenderMode(void) const
477     return _render_mode;
480 void
481 CairoRenderContext::setClipMode(CairoClipMode mode)
483     switch (mode) {
484         case CLIP_MODE_PATH:
485         case CLIP_MODE_MASK:
486             _clip_mode = mode;
487             break;
488         default:
489             _clip_mode = CLIP_MODE_PATH;
490             break;
491     }
494 CairoRenderContext::CairoClipMode
495 CairoRenderContext::getClipMode(void) const
497     return _clip_mode;
500 CairoRenderState*
501 CairoRenderContext::_createState(void)
503     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
504     g_assert( state != NULL );
506     state->has_filtereffect = FALSE;
507     state->merge_opacity = TRUE;
508     state->opacity = 1.0;
509     state->need_layer = FALSE;
510     state->has_overflow = FALSE;
511     state->parent_has_userspace = FALSE;
512     state->clip_path = NULL;
513     state->mask = NULL;
515     return state;
518 void
519 CairoRenderContext::pushLayer(void)
521     g_assert( _is_valid );
523     TRACE(("--pushLayer\n"));
524     cairo_push_group(_cr);
526     // clear buffer
527     if (!_vector_based_target) {
528         cairo_save(_cr);
529         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
530         cairo_paint(_cr);
531         cairo_restore(_cr);
532     }
535 void
536 CairoRenderContext::popLayer(void)
538     g_assert( _is_valid );
540     float opacity = _state->opacity;
541     TRACE(("--popLayer w/ %f\n", opacity));
543     // apply clipPath or mask if present
544     SPClipPath *clip_path = _state->clip_path;
545     SPMask *mask = _state->mask;
546     if (clip_path || mask) {
548         CairoRenderContext *clip_ctx = 0;
549         cairo_surface_t *clip_mask = 0;
551         if (clip_path) {
552             if (_render_mode == RENDER_MODE_CLIP)
553                 mask = NULL;    // disable mask when performing nested clipping
555             if (_vector_based_target) {
556                 setClipMode(CLIP_MODE_PATH);
557                 if (!mask) {
558                     cairo_pop_group_to_source(_cr);
559                     _renderer->applyClipPath(this, clip_path);
560                     if (opacity == 1.0)
561                         cairo_paint(_cr);
562                     else
563                         cairo_paint_with_alpha(_cr, opacity);
565                 } else {
566                     // the clipPath will be applied before masking
567                 }
568             } else {
570                 // setup a new rendering context
571                 clip_ctx = _renderer->createContext();
572                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
573                 clip_ctx->setClipMode(CLIP_MODE_MASK);
574                 if (!clip_ctx->setupSurface(_width, _height)) {
575                     TRACE(("setupSurface failed\n"));
576                     _renderer->destroyContext(clip_ctx);
577                     return;
578                 }
580                 // clear buffer
581                 cairo_save(clip_ctx->_cr);
582                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
583                 cairo_paint(clip_ctx->_cr);
584                 cairo_restore(clip_ctx->_cr);
586                 // if a mask won't be applied set opacity too
587                 if (!mask)
588                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
589                 else
590                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
592                 // copy over the correct CTM
593                 // It must be stored in item_transform of current state after pushState.
594                 Geom::Matrix item_transform; 
595                 if (_state->parent_has_userspace)
596                     item_transform = getParentState()->transform;
597                 else
598                     item_transform = _state->transform;
600                 // apply the clip path
601                 clip_ctx->pushState();
602                 clip_ctx->getCurrentState()->item_transform = item_transform;
603                 _renderer->applyClipPath(clip_ctx, clip_path);
604                 clip_ctx->popState();
606                 clip_mask = clip_ctx->getSurface();
607                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
609                 if (!mask) {
610                     cairo_pop_group_to_source(_cr);
611                     cairo_mask_surface(_cr, clip_mask, 0, 0);
612                     _renderer->destroyContext(clip_ctx);
613                 }
614             }
615         }
617         if (mask) {
618             // create rendering context for mask
619             CairoRenderContext *mask_ctx = _renderer->createContext();
620             mask_ctx->setupSurface(_width, _height);
622             // set rendering mode to normal
623             setRenderMode(RENDER_MODE_NORMAL);
625             // copy the correct CTM to mask context
626             if (_state->parent_has_userspace)
627                 mask_ctx->setTransform(&getParentState()->transform);
628             else
629                 mask_ctx->setTransform(&_state->transform);
631             // render mask contents to mask_ctx
632             _renderer->applyMask(mask_ctx, mask);
634             TEST(mask_ctx->saveAsPng("mask.png"));
636             // composite with clip mask
637             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
638                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
639                 _renderer->destroyContext(clip_ctx);
640             }
642             cairo_surface_t *mask_image = mask_ctx->getSurface();
643             int width = cairo_image_surface_get_width(mask_image);
644             int height = cairo_image_surface_get_height(mask_image);
645             int stride = cairo_image_surface_get_stride(mask_image);
646             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
648             // premultiply with opacity
649             if (_state->opacity != 1.0) {
650                 TRACE(("premul w/ %f\n", opacity));
651                 guint8 int_opacity = (guint8)(255 * opacity);
652                 for (int row = 0 ; row < height; row++) {
653                     unsigned char *row_data = pixels + (row * stride);
654                     for (int i = 0 ; i < width; i++) {
655                         guint32 *pixel = (guint32 *)row_data + i;
656                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
657                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
658                                 ((*pixel & 0x000000ff)      ) * 4688) *
659                                 int_opacity);
660                     }
661                 }
662             }
664             cairo_pop_group_to_source(_cr);
665             if (_clip_mode == CLIP_MODE_PATH) {
666                 // we have to do the clipping after cairo_pop_group_to_source
667                 _renderer->applyClipPath(this, clip_path);
668             }
669             // apply the mask onto the layer
670             cairo_mask_surface(_cr, mask_image, 0, 0);
671             _renderer->destroyContext(mask_ctx);
672         }
673     } else {
674         cairo_pop_group_to_source(_cr);
675         if (opacity == 1.0)
676             cairo_paint(_cr);
677         else
678             cairo_paint_with_alpha(_cr, opacity);
679     }
682 void
683 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
685     g_assert( _is_valid );
687     // here it should be checked whether the current clip winding changed
688     // so we could switch back to masked clipping
689     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
690         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
691     } else {
692         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
693     }
694     addPathVector(pv);
697 void
698 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
700     g_assert( _is_valid );
702     cairo_rectangle(_cr, x, y, width, height);
703     cairo_clip(_cr);
706 bool
707 CairoRenderContext::setupSurface(double width, double height)
709     // Is the surface already set up?
710     if (_is_valid)
711         return true;
713     if (_vector_based_target && _stream == NULL)
714         return false;
716     cairo_surface_t *surface = NULL;
717     switch (_target) {
718         case CAIRO_SURFACE_TYPE_IMAGE:
719             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
720             break;
721 #ifdef CAIRO_HAS_PDF_SURFACE
722         case CAIRO_SURFACE_TYPE_PDF:
723             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
724             break;
725 #endif
726 #ifdef CAIRO_HAS_PS_SURFACE
727         case CAIRO_SURFACE_TYPE_PS:
728             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
729 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
730             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
731                 return FALSE;
732             }
733             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
734             cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
735 #endif
736             break;
737 #endif
738         default:
739             return false;
740             break;
741     }
743     return _finishSurfaceSetup (surface);
746 bool
747 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
749     if (_is_valid || !surface)
750         return false;
752     _vector_based_target = is_vector;
753     bool ret = _finishSurfaceSetup (surface);
754     if (ret)
755         cairo_surface_reference (surface);
756     return ret;
759 bool
760 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
762     if(surface == NULL) {
763         return FALSE;
764     }
765     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
766         return FALSE;
767     }
769     _cr = cairo_create(surface);
770     _surface = surface;
772     if (_vector_based_target) {
773         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
774     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
775         // set background color on non-alpha surfaces
776         // TODO: bgcolor should be derived from SPDocument
777         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
778         cairo_rectangle(_cr, 0, 0, _width, _height);
779         cairo_fill(_cr);
780     }
782     _is_valid = TRUE;
784     return true;
787 bool
788 CairoRenderContext::finish(void)
790     g_assert( _is_valid );
792     if (_vector_based_target)
793         cairo_show_page(_cr);
795     cairo_destroy(_cr);
796     cairo_surface_finish(_surface);
797     cairo_status_t status = cairo_surface_status(_surface);
798     cairo_surface_destroy(_surface);
799     _cr = NULL;
800     _surface = NULL;
802     if (_layout)
803         g_object_unref(_layout);
805     _is_valid = FALSE;
807     if (_vector_based_target && _stream) {
808         /* Flush stream to be sure. */
809         (void) fflush(_stream);
811         fclose(_stream);
812         _stream = NULL;
813     }
815     if (status == CAIRO_STATUS_SUCCESS)
816         return true;
817     else
818         return false;
821 void
822 CairoRenderContext::transform(Geom::Matrix const *transform)
824     g_assert( _is_valid );
826     cairo_matrix_t matrix;
827     _initCairoMatrix(&matrix, transform);
828     cairo_transform(_cr, &matrix);
830     // store new CTM
831     getTransform(&_state->transform);
834 void
835 CairoRenderContext::setTransform(Geom::Matrix const *transform)
837     g_assert( _is_valid );
839     cairo_matrix_t matrix;
840     _initCairoMatrix(&matrix, transform);
841     cairo_set_matrix(_cr, &matrix);
842     _state->transform = *transform;
845 void
846 CairoRenderContext::getTransform(Geom::Matrix *copy) const
848     g_assert( _is_valid );
850     cairo_matrix_t ctm;
851     cairo_get_matrix(_cr, &ctm);
852     (*copy)[0] = ctm.xx;
853     (*copy)[1] = ctm.yx;
854     (*copy)[2] = ctm.xy;
855     (*copy)[3] = ctm.yy;
856     (*copy)[4] = ctm.x0;
857     (*copy)[5] = ctm.y0;
860 void
861 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
863     g_assert( _is_valid );
865     CairoRenderState *parent_state = getParentState();
866     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
869 void
870 CairoRenderContext::pushState(void)
872     g_assert( _is_valid );
874     cairo_save(_cr);
876     CairoRenderState *new_state = _createState();
877     // copy current state's transform
878     new_state->transform = _state->transform;
879     _state_stack = g_slist_prepend(_state_stack, new_state);
880     _state = new_state;
883 void
884 CairoRenderContext::popState(void)
886     g_assert( _is_valid );
888     cairo_restore(_cr);
890     g_free(_state_stack->data);
891     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
892     _state = (CairoRenderState*)_state_stack->data;
894     g_assert( g_slist_length(_state_stack) > 0 );
897 static bool pattern_hasItemChildren (SPPattern *pat)
899     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
900         if (SP_IS_ITEM (child)) {
901             return true;
902         }
903     }
904     return false;
907 cairo_pattern_t*
908 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
910     g_assert( SP_IS_PATTERN(paintserver) );
912     SPPattern *pat = SP_PATTERN (paintserver);
914     Geom::Matrix ps2user, pcs2dev;
915     ps2user = Geom::identity();
916     pcs2dev = Geom::identity();
918     double x = pattern_x(pat);
919     double y = pattern_y(pat);
920     double width = pattern_width(pat);
921     double height = pattern_height(pat);
922     double bbox_width_scaler;
923     double bbox_height_scaler;
925     TRACE(("%f x %f pattern\n", width, height));
927     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
928         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
929         bbox_width_scaler = pbox->x1 - pbox->x0;
930         bbox_height_scaler = pbox->y1 - pbox->y0;
931         ps2user[4] = x * bbox_width_scaler + pbox->x0;
932         ps2user[5] = y * bbox_height_scaler + pbox->y0;
933     } else {
934         bbox_width_scaler = 1.0;
935         bbox_height_scaler = 1.0;
936         ps2user[4] = x;
937         ps2user[5] = y;
938     }
940     // apply pattern transformation
941     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
942     ps2user *= pattern_transform;
943     Geom::Point ori (ps2user[4], ps2user[5]);
945     // create pattern contents coordinate system
946     if (pat->viewBox_set) {
947         NRRect *view_box = pattern_viewBox(pat);
949         double x, y, w, h;
950         double view_width, view_height;
951         x = 0;
952         y = 0;
953         w = width * bbox_width_scaler;
954         h = height * bbox_height_scaler;
956         view_width = view_box->x1 - view_box->x0;
957         view_height = view_box->y1 - view_box->y0;
959         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
960         pcs2dev[0] = w / view_width;
961         pcs2dev[3] = h / view_height;
962         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
963         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
964     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
965         pcs2dev[0] = pbox->x1 - pbox->x0;
966         pcs2dev[3] = pbox->y1 - pbox->y0;
968     }
970     // Calculate the size of the surface which has to be created 
971 #define SUBPIX_SCALE 100
972     // Cairo requires an integer pattern surface width/height.
973     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
974     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
975     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
976     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
977     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
978     // create new rendering context
979     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
981     // adjust the size of the painted pattern to fit exactly the created surface
982     // this has to be done because of the rounding to obtain an integer pattern surface width/height
983     double scale_width = surface_width / (bbox_width_scaler * width);
984     double scale_height = surface_height / (bbox_height_scaler * height);
985     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
986         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
987         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
988         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
989     }
991     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
992     ps2user[4] = ori[Geom::X];
993     ps2user[5] = ori[Geom::Y];
995     pattern_ctx->setTransform(&pcs2dev);
996     pattern_ctx->pushState();
998     // create arena and group
999     NRArena *arena = NRArena::create();
1000     unsigned dkey = sp_item_display_key_new(1);
1002     // show items and render them
1003     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1004         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1005             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1006                 if (SP_IS_ITEM (child)) {
1007                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1008                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1009                 }
1010             }
1011             break; // do not go further up the chain if children are found
1012         }
1013     }
1015     pattern_ctx->popState();
1017     // setup a cairo_pattern_t
1018     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1019     TEST(pattern_ctx->saveAsPng("pattern.png"));
1020     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1021     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1023     // set pattern transformation
1024     cairo_matrix_t pattern_matrix;
1025     _initCairoMatrix(&pattern_matrix, &ps2user);
1026     cairo_matrix_invert(&pattern_matrix);
1027     cairo_pattern_set_matrix(result, &pattern_matrix);
1029     delete pattern_ctx;
1031     // hide all items
1032     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1033         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1034             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1035                 if (SP_IS_ITEM (child)) {
1036                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1037                 }
1038             }
1039             break; // do not go further up the chain if children are found
1040         }
1041     }
1043     return result;
1046 cairo_pattern_t*
1047 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1048                                                  NRRect const *pbox, float alpha)
1050     cairo_pattern_t *pattern = NULL;
1051     bool apply_bbox2user = FALSE;
1053     if (SP_IS_LINEARGRADIENT (paintserver)) {
1055             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1057             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1059             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1060             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1061             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1062                 // convert to userspace
1063                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1064                 p1 *= bbox2user;
1065                 p2 *= bbox2user;
1066             }
1068             // create linear gradient pattern
1069             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1071             // add stops
1072             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1073                 float rgb[3];
1074                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1075                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1076             }
1077     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1079         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1081         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1083         Geom::Point c (rg->cx.computed, rg->cy.computed);
1084         Geom::Point f (rg->fx.computed, rg->fy.computed);
1085         double r = rg->r.computed;
1086         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1087             apply_bbox2user = true;
1089         // create radial gradient pattern
1090         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1092         // add stops
1093         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1094             float rgb[3];
1095             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1096             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1097         }
1098     } else if (SP_IS_PATTERN (paintserver)) {
1100         pattern = _createPatternPainter(paintserver, pbox);
1101     } else {
1102         return NULL;
1103     }
1105     if (pattern && SP_IS_GRADIENT (paintserver)) {
1106         SPGradient *g = SP_GRADIENT(paintserver);
1108         // set extend type
1109         SPGradientSpread spread = sp_gradient_get_spread(g);
1110         switch (spread) {
1111             case SP_GRADIENT_SPREAD_REPEAT: {
1112                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1113                 break;
1114             }
1115             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1116                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1117                 break;
1118             }
1119             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1120                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1121                 break;
1122             }
1123             default: {
1124                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1125                 break;
1126             }
1127         }
1129         cairo_matrix_t pattern_matrix;
1130         if (g->gradientTransform_set) {
1131             // apply gradient transformation
1132             cairo_matrix_init(&pattern_matrix,
1133                 g->gradientTransform[0], g->gradientTransform[1],
1134                 g->gradientTransform[2], g->gradientTransform[3],
1135                 g->gradientTransform[4], g->gradientTransform[5]);
1136         } else {
1137             cairo_matrix_init_identity (&pattern_matrix);
1138         }
1140         if (apply_bbox2user) {
1141             // convert to userspace
1142             cairo_matrix_t bbox2user;
1143             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1144             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1145         }
1146         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1147         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1148     }
1150     return pattern;
1153 void
1154 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1156     g_return_if_fail( style->fill.isColor()
1157                       || style->fill.isPaintserver() );
1159     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1160     if (_state->merge_opacity) {
1161         alpha *= _state->opacity;
1162         TRACE(("merged op=%f\n", alpha));
1163     }
1165     if (style->fill.isColor()) {
1166         float rgb[3];
1167         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1169         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1170     } else {
1171         g_assert( style->fill.isPaintserver()
1172                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1173                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1175         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1177         if (pattern) {
1178             cairo_set_source(_cr, pattern);
1179             cairo_pattern_destroy(pattern);
1180         }
1181     }
1184 void
1185 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1187     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1188     if (_state->merge_opacity)
1189         alpha *= _state->opacity;
1191     if (style->stroke.isColor()) {
1192         float rgb[3];
1193         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1195         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1196     } else {
1197         g_assert( style->fill.isPaintserver()
1198                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1199                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1201         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1203         if (pattern) {
1204             cairo_set_source(_cr, pattern);
1205             cairo_pattern_destroy(pattern);
1206         }
1207     }
1209     if (style->stroke_dash.n_dash   &&
1210         style->stroke_dash.dash       )
1211     {
1212         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1213     } else {
1214         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1215     }
1217     cairo_set_line_width(_cr, style->stroke_width.computed);
1219     // set line join type
1220     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1221     switch (style->stroke_linejoin.computed) {
1222         case SP_STROKE_LINEJOIN_MITER:
1223             join = CAIRO_LINE_JOIN_MITER;
1224             break;
1225         case SP_STROKE_LINEJOIN_ROUND:
1226             join = CAIRO_LINE_JOIN_ROUND;
1227             break;
1228         case SP_STROKE_LINEJOIN_BEVEL:
1229             join = CAIRO_LINE_JOIN_BEVEL;
1230             break;
1231     }
1232     cairo_set_line_join(_cr, join);
1234     // set line cap type
1235     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1236     switch (style->stroke_linecap.computed) {
1237         case SP_STROKE_LINECAP_BUTT:
1238             cap = CAIRO_LINE_CAP_BUTT;
1239             break;
1240         case SP_STROKE_LINECAP_ROUND:
1241             cap = CAIRO_LINE_CAP_ROUND;
1242             break;
1243         case SP_STROKE_LINECAP_SQUARE:
1244             cap = CAIRO_LINE_CAP_SQUARE;
1245             break;
1246     }
1247     cairo_set_line_cap(_cr, cap);
1248     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1251 bool
1252 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1254     g_assert( _is_valid );
1256     if (_render_mode == RENDER_MODE_CLIP) {
1257         if (_clip_mode == CLIP_MODE_PATH) {
1258             addClipPath(pathv, &style->fill_rule);
1259         } else {
1260             setPathVector(pathv);
1261             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1262                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1263             } else {
1264                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1265             }
1266             cairo_fill(_cr);
1267             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1268         }
1269         return true;
1270     }
1272     if (style->fill.isNone() && style->stroke.isNone())
1273         return true;
1275     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1276                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1278     if (!need_layer)
1279         cairo_save(_cr);
1280     else
1281         pushLayer();
1283     if (!style->fill.isNone()) {
1284         _setFillStyle(style, pbox);
1285         setPathVector(pathv);
1287         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1288             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1289         } else {
1290             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1291         }
1293         if (style->stroke.isNone())
1294             cairo_fill(_cr);
1295         else
1296             cairo_fill_preserve(_cr);
1297     }
1299     if (!style->stroke.isNone()) {
1300         _setStrokeStyle(style, pbox);
1301         if (style->fill.isNone())
1302             setPathVector(pathv);
1304         cairo_stroke(_cr);
1305     }
1307     if (need_layer)
1308         popLayer();
1309     else
1310         cairo_restore(_cr);
1312     return true;
1315 bool
1316 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1317                                 Geom::Matrix const *image_transform, SPStyle const *style)
1319     g_assert( _is_valid );
1321     if (_render_mode == RENDER_MODE_CLIP)
1322         return true;
1324     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1325     if (!px_rgba)
1326         return false;
1328     float opacity;
1329     if (_state->merge_opacity)
1330         opacity = _state->opacity;
1331     else
1332         opacity = 1.0;
1334     // make a copy of the original pixbuf with premultiplied alpha
1335     // if we pass the original pixbuf it will get messed up
1336     for (unsigned i = 0; i < h; i++) {
1337         for (unsigned j = 0; j < w; j++) {
1338             guchar const *src = px + i * rs + j * 4;
1339             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1340             guchar r, g, b, alpha_dst;
1342             // calculate opacity-modified alpha
1343             alpha_dst = src[3];
1344             if (opacity != 1.0 && _vector_based_target)
1345                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1347             // premul alpha (needed because this will be undone by cairo-pdf)
1348             r = src[0]*alpha_dst/255;
1349             g = src[1]*alpha_dst/255;
1350             b = src[2]*alpha_dst/255;
1352             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1353         }
1354     }
1356     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1357     if (cairo_surface_status(image_surface)) {
1358         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1359         return false;
1360     }
1362     // setup automatic freeing of the image data when destroying the surface
1363     static cairo_user_data_key_t key;
1364     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1366     cairo_save(_cr);
1368     // scaling by width & height is not needed because it will be done by Cairo
1369     if (image_transform)
1370         transform(image_transform);
1372     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1374     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1375     if (_vector_based_target) {
1376         cairo_new_path(_cr);
1377         cairo_rectangle(_cr, 0, 0, w, h);
1378         cairo_clip(_cr);
1379     }
1381     if (_vector_based_target)
1382         cairo_paint(_cr);
1383     else
1384         cairo_paint_with_alpha(_cr, opacity);
1386     cairo_restore(_cr);
1388     cairo_surface_destroy(image_surface);
1390     return true;
1393 #define GLYPH_ARRAY_SIZE 64
1395 unsigned int
1396 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1398     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1399     cairo_glyph_t *glyphs = glyph_array;
1400     unsigned int num_glyphs = glyphtext.size();
1401     if (num_glyphs > GLYPH_ARRAY_SIZE)
1402         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1404     unsigned int num_invalid_glyphs = 0;
1405     unsigned int i = 0;
1406     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1407         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1408         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1409         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1410             TRACE(("INVALID GLYPH found\n"));
1411             num_invalid_glyphs++;
1412             continue;
1413         }
1414         glyphs[i - num_invalid_glyphs].index = it_info->index;
1415         glyphs[i - num_invalid_glyphs].x = it_info->x;
1416         glyphs[i - num_invalid_glyphs].y = it_info->y;
1417         i++;
1418     }
1420     if (is_stroke || _is_texttopath)
1421         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1422     else
1423         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1425     if (num_glyphs > GLYPH_ARRAY_SIZE)
1426         g_free(glyphs);
1428     return num_glyphs - num_invalid_glyphs;
1431 bool
1432 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1433                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1435     // create a cairo_font_face from PangoFont
1436     double size = style->font_size.computed;
1437     cairo_font_face_t *font_face = NULL;
1439     FcPattern *fc_pattern = NULL;
1440     
1441 #ifdef USE_PANGO_WIN32
1442 # ifdef CAIRO_HAS_WIN32_FONT
1443     LOGFONTA *lfa = pango_win32_font_logfont(font);
1444     LOGFONTW lfw;
1446     ZeroMemory(&lfw, sizeof(LOGFONTW));
1447     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1448     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1449     
1450     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1451 # endif
1452 #else
1453 # ifdef CAIRO_HAS_FT_FONT
1454     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1455     fc_pattern = fc_font->font_pattern;
1456     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1457 # endif
1458 #endif
1459     
1460     cairo_save(_cr);
1461     cairo_set_font_face(_cr, font_face);
1463     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1464         size = 12.0;
1466     // set the given font matrix
1467     cairo_matrix_t matrix;
1468     _initCairoMatrix(&matrix, font_matrix);
1469     cairo_set_font_matrix(_cr, &matrix);
1471     if (_render_mode == RENDER_MODE_CLIP) {
1472         if (_clip_mode == CLIP_MODE_MASK) {
1473             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1474                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1475             } else {
1476                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1477             }
1478             _showGlyphs(_cr, font, glyphtext, FALSE);
1479         } else {
1480             // just add the glyph paths to the current context
1481             _showGlyphs(_cr, font, glyphtext, TRUE);
1482         }
1483     } else {
1485         if (style->fill.isColor() || style->fill.isPaintserver()) {
1486             // set fill style
1487             _setFillStyle(style, NULL);
1489             _showGlyphs(_cr, font, glyphtext, FALSE);
1490         }
1492         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1493             // set stroke style
1494             _setStrokeStyle(style, NULL);
1496             // paint stroke
1497             _showGlyphs(_cr, font, glyphtext, TRUE);
1498             cairo_stroke(_cr);
1499         }
1500     }
1502     cairo_restore(_cr);
1504     if (font_face)
1505         cairo_font_face_destroy(font_face);
1507     return true;
1510 /* Helper functions */
1512 void
1513 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1515     cairo_new_path(_cr);
1516     addPathVector(pv);
1519 void
1520 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1522     feed_pathvector_to_cairo(_cr, pv);
1525 void
1526 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1528     cairo_matrix_t matrix;
1530     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1531     cairo_transform(cr, &matrix);
1534 void
1535 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1537     matrix->xx = (*transform)[0];
1538     matrix->yx = (*transform)[1];
1539     matrix->xy = (*transform)[2];
1540     matrix->yy = (*transform)[3];
1541     matrix->x0 = (*transform)[4];
1542     matrix->y0 = (*transform)[5];
1545 void
1546 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1548     _concatTransform(cr, (*transform)[0], (*transform)[1],
1549                      (*transform)[2], (*transform)[3],
1550                      (*transform)[4], (*transform)[5]);
1553 static cairo_status_t
1554 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1556     size_t written;
1557     FILE *file = (FILE*)closure;
1559     written = fwrite (data, 1, length, file);
1561     if (written == length)
1562         return CAIRO_STATUS_SUCCESS;
1563     else
1564         return CAIRO_STATUS_WRITE_ERROR;
1567 #include "clear-n_.h"
1569 }  /* namespace Internal */
1570 }  /* namespace Extension */
1571 }  /* namespace Inkscape */
1573 #undef TRACE
1574 #undef TEST
1576 /* End of GNU GPL code */
1579 /*
1580   Local Variables:
1581   mode:c++
1582   c-file-style:"stroustrup"
1583   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1584   indent-tabs-mode:nil
1585   fill-column:99
1586   End:
1587 */
1588 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :