Code

Fix some regressions in the snapping of the selector tool
[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>
37 #include <libnr/n-art-bpath-2geom.h>
38 #include <2geom/pathvector.h>
40 #include <glib/gmem.h>
42 #include <glibmm/i18n.h>
43 #include "display/nr-arena.h"
44 #include "display/nr-arena-item.h"
45 #include "display/nr-arena-group.h"
46 #include "display/curve.h"
47 #include "display/canvas-bpath.h"
48 #include "sp-item.h"
49 #include "sp-item-group.h"
50 #include "style.h"
51 #include "sp-linear-gradient.h"
52 #include "sp-radial-gradient.h"
53 #include "sp-pattern.h"
54 #include "sp-mask.h"
55 #include "sp-clippath.h"
56 #ifdef WIN32
57 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
58 #endif
60 #include <unit-constants.h>
62 #include "cairo-render-context.h"
63 #include "cairo-renderer.h"
64 #include "extension/system.h"
66 #include "io/sys.h"
68 #include <cairo.h>
70 // include support for only the compiled-in surface types
71 #ifdef CAIRO_HAS_PDF_SURFACE
72 #include <cairo-pdf.h>
73 #endif
74 #ifdef CAIRO_HAS_PS_SURFACE
75 #include <cairo-ps.h>
76 #endif
79 #ifdef CAIRO_HAS_FT_FONT
80 #include <cairo-ft.h>
81 #endif
82 #ifdef CAIRO_HAS_WIN32_FONT
83 #include <cairo-win32.h>
84 #include <pango/pangowin32.h>
85 #endif
87 #include <pango/pangofc-fontmap.h>
89 //#define TRACE(_args) g_printf _args
90 #define TRACE(_args)
91 //#define TEST(_args) _args
92 #define TEST(_args)
94 // FIXME: expose these from sp-clippath/mask.cpp
95 struct SPClipPathView {
96     SPClipPathView *next;
97     unsigned int key;
98     NRArenaItem *arenaitem;
99     NRRect bbox;
100 };
102 struct SPMaskView {
103     SPMaskView *next;
104     unsigned int key;
105     NRArenaItem *arenaitem;
106     NRRect bbox;
107 };
109 namespace Inkscape {
110 namespace Extension {
111 namespace Internal {
113 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
115 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
116     _dpi(72),
117     _pdf_level(0),
118     _ps_level(1),
119     _is_texttopath(FALSE),
120     _is_filtertobitmap(FALSE),
121     _bitmapresolution(72),
122     _stream(NULL),
123     _is_valid(FALSE),
124     _vector_based_target(FALSE),
125     _cr(NULL),
126     _surface(NULL),
127     _target(CAIRO_SURFACE_TYPE_IMAGE),
128     _target_format(CAIRO_FORMAT_ARGB32),
129     _layout(NULL),
130     _state(NULL),
131     _renderer(parent),
132     _render_mode(RENDER_MODE_NORMAL),
133     _clip_mode(CLIP_MODE_MASK)
134 {}
136 CairoRenderContext::~CairoRenderContext(void)
138     if (_cr) cairo_destroy(_cr);
139     if (_surface) cairo_surface_destroy(_surface);
140     if (_layout) g_object_unref(_layout);
143 CairoRenderer*
144 CairoRenderContext::getRenderer(void) const
146     return _renderer;
149 CairoRenderState*
150 CairoRenderContext::getCurrentState(void) const
152     return _state;
155 CairoRenderState*
156 CairoRenderContext::getParentState(void) const
158     // if this is the root node just return it
159     if (g_slist_length(_state_stack) == 1) {
160         return _state;
161     } else {
162         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
163     }
166 void
167 CairoRenderContext::setStateForStyle(SPStyle const *style)
169     // only opacity & overflow is stored for now
170     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
171     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
172     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
174     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
175         _state->merge_opacity = FALSE;
177     // disable rendering of opacity if there's a stroke on the fill
178     if (_state->merge_opacity
179         && !style->fill.isNone()
180         && !style->stroke.isNone())
181         _state->merge_opacity = FALSE;
184 /**
185  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
186  *
187  * \param width     width of the surface to be created
188  * \param height    height of the surface to be created
189  */
190 CairoRenderContext*
191 CairoRenderContext::cloneMe(double width, double height) const
193     g_assert( _is_valid );
194     g_assert( width > 0.0 && height > 0.0 );
196     CairoRenderContext *new_context = _renderer->createContext();
197     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
198                                                             (int)ceil(width), (int)ceil(height));
199     new_context->_cr = cairo_create(surface);
200     new_context->_surface = surface;
201     new_context->_is_valid = TRUE;
203     return new_context;
206 CairoRenderContext*
207 CairoRenderContext::cloneMe(void) const
209     g_assert( _is_valid );
211     return cloneMe(_width, _height);
214 bool
215 CairoRenderContext::setImageTarget(cairo_format_t format)
217     // format cannot be set on an already initialized surface
218     if (_is_valid)
219         return false;
221     switch (format) {
222         case CAIRO_FORMAT_ARGB32:
223         case CAIRO_FORMAT_RGB24:
224         case CAIRO_FORMAT_A8:
225         case CAIRO_FORMAT_A1:
226             _target_format = format;
227             _target = CAIRO_SURFACE_TYPE_IMAGE;
228             return true;
229             break;
230         default:
231             break;
232     }
234     return false;
237 bool
238 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
240 #ifndef CAIRO_HAS_PDF_SURFACE
241     return false;
242 #else
243     _target = CAIRO_SURFACE_TYPE_PDF;
244     _vector_based_target = TRUE;
245 #endif
247     FILE *osf = NULL;
248     FILE *osp = NULL;
250     gsize bytesRead = 0;
251     gsize bytesWritten = 0;
252     GError *error = NULL;
253     gchar *local_fn = g_filename_from_utf8(utf8_fn,
254                                            -1,  &bytesRead,  &bytesWritten, &error);
255     gchar const *fn = local_fn;
257     /* TODO: Replace the below fprintf's with something that does the right thing whether in
258     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
259     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
260     * return code.
261     */
262     if (fn != NULL) {
263         if (*fn == '|') {
264             fn += 1;
265             while (isspace(*fn)) fn += 1;
266 #ifndef WIN32
267             osp = popen(fn, "w");
268 #else
269             osp = _popen(fn, "w");
270 #endif
271             if (!osp) {
272                 fprintf(stderr, "inkscape: popen(%s): %s\n",
273                         fn, strerror(errno));
274                 return false;
275             }
276             _stream = osp;
277         } else if (*fn == '>') {
278             fn += 1;
279             while (isspace(*fn)) fn += 1;
280             Inkscape::IO::dump_fopen_call(fn, "K");
281             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
282             if (!osf) {
283                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
284                         fn, strerror(errno));
285                 return false;
286             }
287             _stream = osf;
288         } else {
289             /* put cwd stuff in here */
290             gchar *qn = ( *fn
291                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
292                 : g_strdup("lpr") );
293 #ifndef WIN32
294             osp = popen(qn, "w");
295 #else
296             osp = _popen(qn, "w");
297 #endif
298             if (!osp) {
299                 fprintf(stderr, "inkscape: popen(%s): %s\n",
300                         qn, strerror(errno));
301                 return false;
302             }
303             g_free(qn);
304             _stream = osp;
305         }
306     }
308     g_free(local_fn);
310     if (_stream) {
311         /* fixme: this is kinda icky */
312 #if !defined(_WIN32) && !defined(__WIN32__)
313         (void) signal(SIGPIPE, SIG_IGN);
314 #endif
315     }
317     return true;
320 bool
321 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
323 #ifndef CAIRO_HAS_PS_SURFACE
324     return false;
325 #else
326     _target = CAIRO_SURFACE_TYPE_PS;
327     _vector_based_target = TRUE;
328 #endif
330     FILE *osf = NULL;
331     FILE *osp = NULL;
333     gsize bytesRead = 0;
334     gsize bytesWritten = 0;
335     GError *error = NULL;
336     gchar *local_fn = g_filename_from_utf8(utf8_fn,
337                                            -1,  &bytesRead,  &bytesWritten, &error);
338     gchar const *fn = local_fn;
340     /* TODO: Replace the below fprintf's with something that does the right thing whether in
341     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
342     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
343     * return code.
344     */
345     if (fn != NULL) {
346         if (*fn == '|') {
347             fn += 1;
348             while (isspace(*fn)) fn += 1;
349 #ifndef WIN32
350             osp = popen(fn, "w");
351 #else
352             osp = _popen(fn, "w");
353 #endif
354             if (!osp) {
355                 fprintf(stderr, "inkscape: popen(%s): %s\n",
356                         fn, strerror(errno));
357                 return false;
358             }
359             _stream = osp;
360         } else if (*fn == '>') {
361             fn += 1;
362             while (isspace(*fn)) fn += 1;
363             Inkscape::IO::dump_fopen_call(fn, "K");
364             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
365             if (!osf) {
366                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
367                         fn, strerror(errno));
368                 return false;
369             }
370             _stream = osf;
371         } else {
372             /* put cwd stuff in here */
373             gchar *qn = ( *fn
374                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
375                 : g_strdup("lpr") );
376 #ifndef WIN32
377             osp = popen(qn, "w");
378 #else
379             osp = _popen(qn, "w");
380 #endif
381             if (!osp) {
382                 fprintf(stderr, "inkscape: popen(%s): %s\n",
383                         qn, strerror(errno));
384                 return false;
385             }
386             g_free(qn);
387             _stream = osp;
388         }
389     }
391     g_free(local_fn);
393     if (_stream) {
394         /* fixme: this is kinda icky */
395 #if !defined(_WIN32) && !defined(__WIN32__)
396         (void) signal(SIGPIPE, SIG_IGN);
397 #endif
398     }
400     return true;
403 void CairoRenderContext::setPSLevel(unsigned int level)
405     _ps_level = level;
408 unsigned int CairoRenderContext::getPSLevel(void)
410     return _ps_level;
413 void CairoRenderContext::setPDFLevel(unsigned int level)
415     _pdf_level = level;
418 void CairoRenderContext::setTextToPath(bool texttopath)
420     _is_texttopath = texttopath;
423 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
425     _is_filtertobitmap = filtertobitmap;
428 bool CairoRenderContext::getFilterToBitmap(void)
430     return _is_filtertobitmap;
433 void CairoRenderContext::setBitmapResolution(int resolution)
435     _bitmapresolution = resolution;
438 int CairoRenderContext::getBitmapResolution(void)
440     return _bitmapresolution;
443 cairo_surface_t*
444 CairoRenderContext::getSurface(void)
446     g_assert( _is_valid );
448     return _surface;
451 bool
452 CairoRenderContext::saveAsPng(const char *file_name)
454     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
455     if (status)
456         return false;
457     else
458         return true;
461 void
462 CairoRenderContext::setRenderMode(CairoRenderMode mode)
464     switch (mode) {
465         case RENDER_MODE_NORMAL:
466         case RENDER_MODE_CLIP:
467             _render_mode = mode;
468             break;
469         default:
470             _render_mode = RENDER_MODE_NORMAL;
471             break;
472     }
475 CairoRenderContext::CairoRenderMode
476 CairoRenderContext::getRenderMode(void) const
478     return _render_mode;
481 void
482 CairoRenderContext::setClipMode(CairoClipMode mode)
484     switch (mode) {
485         case CLIP_MODE_PATH:
486         case CLIP_MODE_MASK:
487             _clip_mode = mode;
488             break;
489         default:
490             _clip_mode = CLIP_MODE_PATH;
491             break;
492     }
495 CairoRenderContext::CairoClipMode
496 CairoRenderContext::getClipMode(void) const
498     return _clip_mode;
501 CairoRenderState*
502 CairoRenderContext::_createState(void)
504     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
505     g_assert( state != NULL );
507     state->has_filtereffect = FALSE;
508     state->merge_opacity = TRUE;
509     state->opacity = 1.0;
510     state->need_layer = FALSE;
511     state->has_overflow = FALSE;
512     state->parent_has_userspace = FALSE;
513     state->clip_path = NULL;
514     state->mask = NULL;
516     return state;
519 void
520 CairoRenderContext::pushLayer(void)
522     g_assert( _is_valid );
524     TRACE(("--pushLayer\n"));
525     cairo_push_group(_cr);
527     // clear buffer
528     if (!_vector_based_target) {
529         cairo_save(_cr);
530         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
531         cairo_paint(_cr);
532         cairo_restore(_cr);
533     }
536 void
537 CairoRenderContext::popLayer(void)
539     g_assert( _is_valid );
541     float opacity = _state->opacity;
542     TRACE(("--popLayer w/ %f\n", opacity));
544     // apply clipPath or mask if present
545     SPClipPath *clip_path = _state->clip_path;
546     SPMask *mask = _state->mask;
547     if (clip_path || mask) {
549         CairoRenderContext *clip_ctx = 0;
550         cairo_surface_t *clip_mask = 0;
552         if (clip_path) {
553             if (_render_mode == RENDER_MODE_CLIP)
554                 mask = NULL;    // disable mask when performing nested clipping
556             if (_vector_based_target) {
557                 setClipMode(CLIP_MODE_PATH);
558                 if (!mask) {
559                     cairo_pop_group_to_source(_cr);
560                     _renderer->applyClipPath(this, clip_path);
561                     if (opacity == 1.0)
562                         cairo_paint(_cr);
563                     else
564                         cairo_paint_with_alpha(_cr, opacity);
566                 } else {
567                     // the clipPath will be applied before masking
568                 }
569             } else {
571                 // setup a new rendering context
572                 clip_ctx = _renderer->createContext();
573                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
574                 clip_ctx->setClipMode(CLIP_MODE_MASK);
575                 if (!clip_ctx->setupSurface(_width, _height)) {
576                     TRACE(("setupSurface failed\n"));
577                     _renderer->destroyContext(clip_ctx);
578                     return;
579                 }
581                 // clear buffer
582                 cairo_save(clip_ctx->_cr);
583                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
584                 cairo_paint(clip_ctx->_cr);
585                 cairo_restore(clip_ctx->_cr);
587                 // if a mask won't be applied set opacity too
588                 if (!mask)
589                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
590                 else
591                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
593                 // copy over the correct CTM
594                 if (_state->parent_has_userspace)
595                     clip_ctx->setTransform(&getParentState()->transform);
596                 else
597                     clip_ctx->setTransform(&_state->transform);
599                 // apply the clip path
600                 clip_ctx->pushState();
601                 _renderer->applyClipPath(clip_ctx, clip_path);
602                 clip_ctx->popState();
604                 clip_mask = clip_ctx->getSurface();
605                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
607                 if (!mask) {
608                     cairo_pop_group_to_source(_cr);
609                     cairo_mask_surface(_cr, clip_mask, 0, 0);
610                     _renderer->destroyContext(clip_ctx);
611                 }
612             }
613         }
615         if (mask) {
616             // create rendering context for mask
617             CairoRenderContext *mask_ctx = _renderer->createContext();
618             mask_ctx->setupSurface(_width, _height);
620             // set rendering mode to normal
621             setRenderMode(RENDER_MODE_NORMAL);
623             // copy the correct CTM to mask context
624             if (_state->parent_has_userspace)
625                 mask_ctx->setTransform(&getParentState()->transform);
626             else
627                 mask_ctx->setTransform(&_state->transform);
629             // render mask contents to mask_ctx
630             _renderer->applyMask(mask_ctx, mask);
632             TEST(mask_ctx->saveAsPng("mask.png"));
634             // composite with clip mask
635             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
636                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
637                 _renderer->destroyContext(clip_ctx);
638             }
640             cairo_surface_t *mask_image = mask_ctx->getSurface();
641             int width = cairo_image_surface_get_width(mask_image);
642             int height = cairo_image_surface_get_height(mask_image);
643             int stride = cairo_image_surface_get_stride(mask_image);
644             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
646             // premultiply with opacity
647             if (_state->opacity != 1.0) {
648                 TRACE(("premul w/ %f\n", opacity));
649                 guint8 int_opacity = (guint8)(255 * opacity);
650                 for (int row = 0 ; row < height; row++) {
651                     unsigned char *row_data = pixels + (row * stride);
652                     for (int i = 0 ; i < width; i++) {
653                         guint32 *pixel = (guint32 *)row_data + i;
654                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
655                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
656                                 ((*pixel & 0x000000ff)      ) * 4688) *
657                                 int_opacity);
658                     }
659                 }
660             }
662             cairo_pop_group_to_source(_cr);
663             if (_clip_mode == CLIP_MODE_PATH) {
664                 // we have to do the clipping after cairo_pop_group_to_source
665                 _renderer->applyClipPath(this, clip_path);
666             }
667             // apply the mask onto the layer
668             cairo_mask_surface(_cr, mask_image, 0, 0);
669             _renderer->destroyContext(mask_ctx);
670         }
671     } else {
672         cairo_pop_group_to_source(_cr);
673         if (opacity == 1.0)
674             cairo_paint(_cr);
675         else
676             cairo_paint_with_alpha(_cr, opacity);
677     }
680 void
681 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
683     g_assert( _is_valid );
685     // here it should be checked whether the current clip winding changed
686     // so we could switch back to masked clipping
687     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
688         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
689     } else {
690         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
691     }
692     addBpath(bp);
695 void
696 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
698     g_assert( _is_valid );
700     cairo_rectangle(_cr, x, y, width, height);
701     cairo_clip(_cr);
704 bool
705 CairoRenderContext::setupSurface(double width, double height)
707     // Is the surface already set up?
708     if (_is_valid)
709         return true;
711     if (_vector_based_target && _stream == NULL)
712         return false;
714     cairo_surface_t *surface = NULL;
715     switch (_target) {
716         case CAIRO_SURFACE_TYPE_IMAGE:
717             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
718             break;
719 #ifdef CAIRO_HAS_PDF_SURFACE
720         case CAIRO_SURFACE_TYPE_PDF:
721             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
722             break;
723 #endif
724 #ifdef CAIRO_HAS_PS_SURFACE
725         case CAIRO_SURFACE_TYPE_PS:
726             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
727 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
728             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
729                 return FALSE;
730             }
731             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
732 #endif
733             break;
734 #endif
735         default:
736             return false;
737             break;
738     }
740     return _finishSurfaceSetup (surface);
743 bool
744 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
746     if (_is_valid || !surface)
747         return false;
749     _vector_based_target = is_vector;
750     bool ret = _finishSurfaceSetup (surface);
751     if (ret)
752         cairo_surface_reference (surface);
753     return ret;
756 bool
757 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
759     if(surface == NULL) {
760         return FALSE;
761     }
762     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
763         return FALSE;
764     }
766     _cr = cairo_create(surface);
767     _surface = surface;
769     if (_vector_based_target) {
770         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
771     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
772         // set background color on non-alpha surfaces
773         // TODO: bgcolor should be derived from SPDocument
774         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
775         cairo_rectangle(_cr, 0, 0, _width, _height);
776         cairo_fill(_cr);
777     }
779     _is_valid = TRUE;
781     return true;
784 bool
785 CairoRenderContext::finish(void)
787     g_assert( _is_valid );
789     if (_vector_based_target)
790         cairo_show_page(_cr);
792     cairo_destroy(_cr);
793     cairo_surface_finish(_surface);
794     cairo_status_t status = cairo_surface_status(_surface);
795     cairo_surface_destroy(_surface);
796     _cr = NULL;
797     _surface = NULL;
799     if (_layout)
800         g_object_unref(_layout);
802     _is_valid = FALSE;
804     if (_vector_based_target && _stream) {
805         /* Flush stream to be sure. */
806         (void) fflush(_stream);
808         fclose(_stream);
809         _stream = NULL;
810     }
812     if (status == CAIRO_STATUS_SUCCESS)
813         return true;
814     else
815         return false;
818 void
819 CairoRenderContext::transform(NR::Matrix const *transform)
821     g_assert( _is_valid );
823     cairo_matrix_t matrix;
824     _initCairoMatrix(&matrix, transform);
825     cairo_transform(_cr, &matrix);
827     // store new CTM
828     getTransform(&_state->transform);
831 void
832 CairoRenderContext::setTransform(NR::Matrix const *transform)
834     g_assert( _is_valid );
836     cairo_matrix_t matrix;
837     _initCairoMatrix(&matrix, transform);
838     cairo_set_matrix(_cr, &matrix);
839     _state->transform = *transform;
842 void
843 CairoRenderContext::getTransform(NR::Matrix *copy) const
845     g_assert( _is_valid );
847     cairo_matrix_t ctm;
848     cairo_get_matrix(_cr, &ctm);
849     (*copy)[0] = ctm.xx;
850     (*copy)[1] = ctm.yx;
851     (*copy)[2] = ctm.xy;
852     (*copy)[3] = ctm.yy;
853     (*copy)[4] = ctm.x0;
854     (*copy)[5] = ctm.y0;
857 void
858 CairoRenderContext::getParentTransform(NR::Matrix *copy) const
860     g_assert( _is_valid );
862     CairoRenderState *parent_state = getParentState();
863     memcpy(copy, &parent_state->transform, sizeof(NR::Matrix));
866 void
867 CairoRenderContext::pushState(void)
869     g_assert( _is_valid );
871     cairo_save(_cr);
873     CairoRenderState *new_state = _createState();
874     // copy current state's transform
875     new_state->transform = _state->transform;
876     _state_stack = g_slist_prepend(_state_stack, new_state);
877     _state = new_state;
880 void
881 CairoRenderContext::popState(void)
883     g_assert( _is_valid );
885     cairo_restore(_cr);
887     g_free(_state_stack->data);
888     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
889     _state = (CairoRenderState*)_state_stack->data;
891     g_assert( g_slist_length(_state_stack) > 0 );
894 static bool pattern_hasItemChildren (SPPattern *pat)
896     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
897         if (SP_IS_ITEM (child)) {
898             return true;
899         }
900     }
901     return false;
904 cairo_pattern_t*
905 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
907     g_assert( SP_IS_PATTERN(paintserver) );
909     SPPattern *pat = SP_PATTERN (paintserver);
911     NR::Matrix ps2user, pcs2dev;
912     ps2user.set_identity();
913     pcs2dev.set_identity();
915     double x = pattern_x(pat);
916     double y = pattern_y(pat);
917     double width = pattern_width(pat);
918     double height = pattern_height(pat);
919     double bbox_width_scaler;
920     double bbox_height_scaler;
922     TRACE(("%f x %f pattern\n", width, height));
924     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
925         //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
926         bbox_width_scaler = pbox->x1 - pbox->x0;
927         bbox_height_scaler = pbox->y1 - pbox->y0;
928         ps2user[4] = x * bbox_width_scaler + pbox->x0;
929         ps2user[5] = y * bbox_height_scaler + pbox->y0;
930     } else {
931         bbox_width_scaler = 1.0;
932         bbox_height_scaler = 1.0;
933         ps2user[4] = x;
934         ps2user[5] = y;
935     }
937     // apply pattern transformation
938     NR::Matrix pattern_transform(pattern_patternTransform(pat));
939     ps2user *= pattern_transform;
941     // create pattern contents coordinate system
942     if (pat->viewBox_set) {
943         NRRect *view_box = pattern_viewBox(pat);
945         double x, y, w, h;
946         double view_width, view_height;
947         x = 0;
948         y = 0;
949         w = width * bbox_width_scaler;
950         h = height * bbox_height_scaler;
952         view_width = view_box->x1 - view_box->x0;
953         view_height = view_box->y1 - view_box->y0;
955         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
956         pcs2dev[0] = w / view_width;
957         pcs2dev[3] = h / view_height;
958         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
959         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
960     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
961         pcs2dev[0] = pbox->x1 - pbox->x0;
962         pcs2dev[3] = pbox->y1 - pbox->y0;
963     }
965     // calculate the size of the surface which has to be created
966     // the scaling needs to be taken into account in the ctm after the pattern transformation
967     NR::Matrix temp;
968     temp = pattern_transform * _state->transform;
969     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
970     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
972     if (_vector_based_target) {
973         // eliminate PT_PER_PX mul from these
974         width_scaler *= 1.25;
975         height_scaler *= 1.25;
976     }
977     double surface_width = ceil(bbox_width_scaler * width_scaler * width);
978     double surface_height = ceil(bbox_height_scaler * height_scaler * height);
979     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
980     // create new rendering context
981     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
983     // adjust the size of the painted pattern to fit exactly the created surface
984     // this has to be done because of the rounding to obtain an integer pattern surface width/height
985     double scale_width = surface_width / (bbox_width_scaler * width);
986     double scale_height = surface_height / (bbox_height_scaler * height);
987     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
988         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
989         pcs2dev *= NR::scale(1.0 / scale_width, 1.0 / scale_height);
990         ps2user *= NR::scale(scale_width, scale_height);
991     }
993     pattern_ctx->setTransform(&pcs2dev);
994     pattern_ctx->pushState();
996     // create arena and group
997     NRArena *arena = NRArena::create();
998     unsigned dkey = sp_item_display_key_new(1);
1000     // show items and render them
1001     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1002         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1003             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1004                 if (SP_IS_ITEM (child)) {
1005                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1006                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1007                 }
1008             }
1009             break; // do not go further up the chain if children are found
1010         }
1011     }
1013     pattern_ctx->popState();
1015     // setup a cairo_pattern_t
1016     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1017     TEST(pattern_ctx->saveAsPng("pattern.png"));
1018     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1019     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1021     // set pattern transformation
1022     cairo_matrix_t pattern_matrix;
1023     _initCairoMatrix(&pattern_matrix, &ps2user);
1024     cairo_matrix_invert(&pattern_matrix);
1025     cairo_pattern_set_matrix(result, &pattern_matrix);
1027     delete pattern_ctx;
1029     // hide all items
1030     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1031         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1032             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1033                 if (SP_IS_ITEM (child)) {
1034                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1035                 }
1036             }
1037             break; // do not go further up the chain if children are found
1038         }
1039     }
1041     return result;
1044 cairo_pattern_t*
1045 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1046                                                  NRRect const *pbox, float alpha)
1048     cairo_pattern_t *pattern = NULL;
1049     bool apply_bbox2user = FALSE;
1051     if (SP_IS_LINEARGRADIENT (paintserver)) {
1053             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1055             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1057             NR::Point p1 (lg->x1.computed, lg->y1.computed);
1058             NR::Point p2 (lg->x2.computed, lg->y2.computed);
1059             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1060                 // convert to userspace
1061                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1062                 p1 *= bbox2user;
1063                 p2 *= bbox2user;
1064             }
1066             // create linear gradient pattern
1067             pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1069             // add stops
1070             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1071                 float rgb[3];
1072                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1073                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1074             }
1075     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1077         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1079         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1081         NR::Point c (rg->cx.computed, rg->cy.computed);
1082         NR::Point f (rg->fx.computed, rg->fy.computed);
1083         double r = rg->r.computed;
1084         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1085             apply_bbox2user = true;
1087         // create radial gradient pattern
1088         pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1090         // add stops
1091         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1092             float rgb[3];
1093             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1094             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1095         }
1096     } else if (SP_IS_PATTERN (paintserver)) {
1098         pattern = _createPatternPainter(paintserver, pbox);
1099     } else {
1100         return NULL;
1101     }
1103     if (pattern && SP_IS_GRADIENT (paintserver)) {
1104         SPGradient *g = SP_GRADIENT(paintserver);
1106         // set extend type
1107         SPGradientSpread spread = sp_gradient_get_spread(g);
1108         switch (spread) {
1109             case SP_GRADIENT_SPREAD_REPEAT: {
1110                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1111                 break;
1112             }
1113             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1114                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1115                 break;
1116             }
1117             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1118                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1119                 break;
1120             }
1121             default: {
1122                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1123                 break;
1124             }
1125         }
1127         cairo_matrix_t pattern_matrix;
1128         if (g->gradientTransform_set) {
1129             // apply gradient transformation
1130             cairo_matrix_init(&pattern_matrix,
1131                 g->gradientTransform[0], g->gradientTransform[1],
1132                 g->gradientTransform[2], g->gradientTransform[3],
1133                 g->gradientTransform[4], g->gradientTransform[5]);
1134         } else {
1135             cairo_matrix_init_identity (&pattern_matrix);
1136         }
1138         if (apply_bbox2user) {
1139             // convert to userspace
1140             cairo_matrix_t bbox2user;
1141             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1142             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1143         }
1144         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1145         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1146     }
1148     return pattern;
1151 void
1152 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1154     g_return_if_fail( style->fill.isColor()
1155                       || style->fill.isPaintserver() );
1157     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1158     if (_state->merge_opacity) {
1159         alpha *= _state->opacity;
1160         TRACE(("merged op=%f\n", alpha));
1161     }
1163     if (style->fill.isColor()) {
1164         float rgb[3];
1165         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1167         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1168     } else {
1169         g_assert( style->fill.isPaintserver()
1170                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1171                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1173         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1175         if (pattern) {
1176             cairo_set_source(_cr, pattern);
1177             cairo_pattern_destroy(pattern);
1178         }
1179     }
1182 void
1183 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1185     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1186     if (_state->merge_opacity)
1187         alpha *= _state->opacity;
1189     if (style->stroke.isColor()) {
1190         float rgb[3];
1191         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1193         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1194     } else {
1195         g_assert( style->fill.isPaintserver()
1196                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1197                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1199         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1201         if (pattern) {
1202             cairo_set_source(_cr, pattern);
1203             cairo_pattern_destroy(pattern);
1204         }
1205     }
1207     if (style->stroke_dash.n_dash   &&
1208         style->stroke_dash.dash       )
1209     {
1210         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1211     } else {
1212         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1213     }
1215     cairo_set_line_width(_cr, style->stroke_width.computed);
1217     // set line join type
1218     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1219     switch (style->stroke_linejoin.computed) {
1220         case SP_STROKE_LINEJOIN_MITER:
1221             join = CAIRO_LINE_JOIN_MITER;
1222             break;
1223         case SP_STROKE_LINEJOIN_ROUND:
1224             join = CAIRO_LINE_JOIN_ROUND;
1225             break;
1226         case SP_STROKE_LINEJOIN_BEVEL:
1227             join = CAIRO_LINE_JOIN_BEVEL;
1228             break;
1229     }
1230     cairo_set_line_join(_cr, join);
1232     // set line cap type
1233     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1234     switch (style->stroke_linecap.computed) {
1235         case SP_STROKE_LINECAP_BUTT:
1236             cap = CAIRO_LINE_CAP_BUTT;
1237             break;
1238         case SP_STROKE_LINECAP_ROUND:
1239             cap = CAIRO_LINE_CAP_ROUND;
1240             break;
1241         case SP_STROKE_LINECAP_SQUARE:
1242             cap = CAIRO_LINE_CAP_SQUARE;
1243             break;
1244     }
1245     cairo_set_line_cap(_cr, cap);
1246     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1249 bool
1250 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1252     NArtBpath * bpath = BPath_from_2GeomPath (pathv);
1253     const_NRBPath *bp;
1254     bp->path = bpath;
1255     bool retvalue = renderPath(bp, style, pbox);
1256     g_free(bpath);
1257     return retvalue;
1260 bool
1261 CairoRenderContext::renderPath(const_NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1263     g_assert( _is_valid );
1265     if (_render_mode == RENDER_MODE_CLIP) {
1266         if (_clip_mode == CLIP_MODE_PATH) {
1267             addClipPath(bpath->path, &style->fill_rule);
1268         } else {
1269             setBpath(bpath->path);
1270             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1271                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1272             } else {
1273                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1274             }
1275             cairo_fill(_cr);
1276             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1277         }
1278         return true;
1279     }
1281     if (style->fill.isNone() && style->stroke.isNone())
1282         return true;
1284     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1285                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1287     if (!need_layer)
1288         cairo_save(_cr);
1289     else
1290         pushLayer();
1292     if (!style->fill.isNone()) {
1293         _setFillStyle(style, pbox);
1294         setBpath(bpath->path);
1296         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1297             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1298         } else {
1299             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1300         }
1302         if (style->stroke.isNone())
1303             cairo_fill(_cr);
1304         else
1305             cairo_fill_preserve(_cr);
1306     }
1308     if (!style->stroke.isNone()) {
1309         _setStrokeStyle(style, pbox);
1310         if (style->fill.isNone())
1311             setBpath(bpath->path);
1313         cairo_stroke(_cr);
1314     }
1316     if (need_layer)
1317         popLayer();
1318     else
1319         cairo_restore(_cr);
1321     return true;
1324 bool
1325 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1326                                 NR::Matrix const *image_transform, SPStyle const *style)
1328     g_assert( _is_valid );
1330     if (_render_mode == RENDER_MODE_CLIP)
1331         return true;
1333     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1334     if (!px_rgba)
1335         return false;
1337     float opacity;
1338     if (_state->merge_opacity)
1339         opacity = _state->opacity;
1340     else
1341         opacity = 1.0;
1343     // make a copy of the original pixbuf with premultiplied alpha
1344     // if we pass the original pixbuf it will get messed up
1345     for (unsigned i = 0; i < h; i++) {
1346         for (unsigned j = 0; j < w; j++) {
1347             guchar const *src = px + i * rs + j * 4;
1348             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1349             guchar r, g, b, alpha_dst;
1351             // calculate opacity-modified alpha
1352             alpha_dst = src[3];
1353             if (opacity != 1.0 && _vector_based_target)
1354                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1356             // premul alpha (needed because this will be undone by cairo-pdf)
1357             r = src[0]*alpha_dst/255;
1358             g = src[1]*alpha_dst/255;
1359             b = src[2]*alpha_dst/255;
1361             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1362         }
1363     }
1365     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1366     if (cairo_surface_status(image_surface)) {
1367         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1368         return false;
1369     }
1371     // setup automatic freeing of the image data when destroying the surface
1372     static cairo_user_data_key_t key;
1373     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1375     cairo_save(_cr);
1377     // scaling by width & height is not needed because it will be done by Cairo
1378     if (image_transform)
1379         transform(image_transform);
1381     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1383     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1384     if (_vector_based_target) {
1385         cairo_new_path(_cr);
1386         cairo_rectangle(_cr, 0, 0, w, h);
1387         cairo_clip(_cr);
1388     }
1390     if (_vector_based_target)
1391         cairo_paint(_cr);
1392     else
1393         cairo_paint_with_alpha(_cr, opacity);
1395     cairo_restore(_cr);
1397     cairo_surface_destroy(image_surface);
1399     return true;
1402 #define GLYPH_ARRAY_SIZE 64
1404 unsigned int
1405 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1407     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1408     cairo_glyph_t *glyphs = glyph_array;
1409     unsigned int num_glyphs = glyphtext.size();
1410     if (num_glyphs > GLYPH_ARRAY_SIZE)
1411         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1413     unsigned int num_invalid_glyphs = 0;
1414     unsigned int i = 0;
1415     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1416         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1417         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1418         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1419             TRACE(("INVALID GLYPH found\n"));
1420             num_invalid_glyphs++;
1421             continue;
1422         }
1423         glyphs[i - num_invalid_glyphs].index = it_info->index;
1424         glyphs[i - num_invalid_glyphs].x = it_info->x;
1425         glyphs[i - num_invalid_glyphs].y = it_info->y;
1426         i++;
1427     }
1429     if (is_stroke || _is_texttopath)
1430         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1431     else
1432         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1434     if (num_glyphs > GLYPH_ARRAY_SIZE)
1435         g_free(glyphs);
1437     return num_glyphs - num_invalid_glyphs;
1440 bool
1441 CairoRenderContext::renderGlyphtext(PangoFont *font, NR::Matrix const *font_matrix,
1442                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1444     // create a cairo_font_face from PangoFont
1445     double size = style->font_size.computed;
1446     cairo_font_face_t *font_face = NULL;
1448     FcPattern *fc_pattern = NULL;
1449     
1450 #ifdef USE_PANGO_WIN32
1451 # ifdef CAIRO_HAS_WIN32_FONT
1452     LOGFONTA *lfa = pango_win32_font_logfont(font);
1453     LOGFONTW lfw;
1455     ZeroMemory(&lfw, sizeof(LOGFONTW));
1456     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1457     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1458     
1459     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1460 # endif
1461 #else
1462 # ifdef CAIRO_HAS_FT_FONT
1463     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1464     fc_pattern = fc_font->font_pattern;
1465     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1466 # endif
1467 #endif
1468     
1469     cairo_save(_cr);
1470     cairo_set_font_face(_cr, font_face);
1472     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1473         size = 12.0;
1475     // set the given font matrix
1476     cairo_matrix_t matrix;
1477     _initCairoMatrix(&matrix, font_matrix);
1478     cairo_set_font_matrix(_cr, &matrix);
1480     if (_render_mode == RENDER_MODE_CLIP) {
1481         if (_clip_mode == CLIP_MODE_MASK) {
1482             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1483                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1484             } else {
1485                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1486             }
1487             _showGlyphs(_cr, font, glyphtext, FALSE);
1488         } else {
1489             // just add the glyph paths to the current context
1490             _showGlyphs(_cr, font, glyphtext, TRUE);
1491         }
1492     } else {
1494         if (style->fill.isColor() || style->fill.isPaintserver()) {
1495             // set fill style
1496             _setFillStyle(style, NULL);
1498             _showGlyphs(_cr, font, glyphtext, FALSE);
1499         }
1501         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1502             // set stroke style
1503             _setStrokeStyle(style, NULL);
1505             // paint stroke
1506             _showGlyphs(_cr, font, glyphtext, TRUE);
1507             cairo_stroke(_cr);
1508         }
1509     }
1511     cairo_restore(_cr);
1513     if (font_face)
1514         cairo_font_face_destroy(font_face);
1516     return true;
1519 /* Helper functions */
1521 void
1522 CairoRenderContext::addBpath(NArtBpath const *bp)
1524     bool closed = false;
1525     while (bp->code != NR_END) {
1526         switch (bp->code) {
1527             case NR_MOVETO:
1528                 if (closed) {
1529                     cairo_close_path(_cr);
1530                 }
1531                 closed = true;
1532                 cairo_move_to(_cr, bp->x3, bp->y3);
1533                 break;
1534             case NR_MOVETO_OPEN:
1535                 if (closed) {
1536                     cairo_close_path(_cr);
1537                 }
1538                 closed = false;
1539                 cairo_move_to(_cr, bp->x3, bp->y3);
1540                 break;
1541             case NR_LINETO:
1542                 cairo_line_to(_cr, bp->x3, bp->y3);
1543                 break;
1544             case NR_CURVETO:
1545                 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1546                 break;
1547             default:
1548                 break;
1549         }
1550         bp += 1;
1551     }
1552     if (closed) {
1553         cairo_close_path(_cr);
1554     }
1557 void
1558 CairoRenderContext::setBpath(NArtBpath const *bp)
1560     cairo_new_path(_cr);
1561     if (bp)
1562         addBpath(bp);
1565 void
1566 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1568     cairo_matrix_t matrix;
1570     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1571     cairo_transform(cr, &matrix);
1574 void
1575 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NR::Matrix const *transform)
1577     matrix->xx = (*transform)[0];
1578     matrix->yx = (*transform)[1];
1579     matrix->xy = (*transform)[2];
1580     matrix->yy = (*transform)[3];
1581     matrix->x0 = (*transform)[4];
1582     matrix->y0 = (*transform)[5];
1585 void
1586 CairoRenderContext::_concatTransform(cairo_t *cr, NR::Matrix const *transform)
1588     _concatTransform(cr, (*transform)[0], (*transform)[1],
1589                      (*transform)[2], (*transform)[3],
1590                      (*transform)[4], (*transform)[5]);
1593 static cairo_status_t
1594 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1596     size_t written;
1597     FILE *file = (FILE*)closure;
1599     written = fwrite (data, 1, length, file);
1601     if (written == length)
1602         return CAIRO_STATUS_SUCCESS;
1603     else
1604         return CAIRO_STATUS_WRITE_ERROR;
1607 #include "clear-n_.h"
1609 }  /* namespace Internal */
1610 }  /* namespace Extension */
1611 }  /* namespace Inkscape */
1613 #undef TRACE
1614 #undef TEST
1616 /* End of GNU GPL code */
1619 /*
1620   Local Variables:
1621   mode:c++
1622   c-file-style:"stroustrup"
1623   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1624   indent-tabs-mode:nil
1625   fill-column:99
1626   End:
1627 */
1628 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :