Code

cairo-render-context extension implementation converted to 2geom
[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     _is_texttopath(FALSE),
113     _is_filtertobitmap(FALSE),
114     _bitmapresolution(72),
115     _stream(NULL),
116     _is_valid(FALSE),
117     _vector_based_target(FALSE),
118     _cr(NULL),
119     _surface(NULL),
120     _target(CAIRO_SURFACE_TYPE_IMAGE),
121     _target_format(CAIRO_FORMAT_ARGB32),
122     _layout(NULL),
123     _state(NULL),
124     _renderer(parent),
125     _render_mode(RENDER_MODE_NORMAL),
126     _clip_mode(CLIP_MODE_MASK)
127 {}
129 CairoRenderContext::~CairoRenderContext(void)
131     if (_cr) cairo_destroy(_cr);
132     if (_surface) cairo_surface_destroy(_surface);
133     if (_layout) g_object_unref(_layout);
136 CairoRenderer*
137 CairoRenderContext::getRenderer(void) const
139     return _renderer;
142 CairoRenderState*
143 CairoRenderContext::getCurrentState(void) const
145     return _state;
148 CairoRenderState*
149 CairoRenderContext::getParentState(void) const
151     // if this is the root node just return it
152     if (g_slist_length(_state_stack) == 1) {
153         return _state;
154     } else {
155         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
156     }
159 void
160 CairoRenderContext::setStateForStyle(SPStyle const *style)
162     // only opacity & overflow is stored for now
163     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
164     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
165     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
167     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
168         _state->merge_opacity = FALSE;
170     // disable rendering of opacity if there's a stroke on the fill
171     if (_state->merge_opacity
172         && !style->fill.isNone()
173         && !style->stroke.isNone())
174         _state->merge_opacity = FALSE;
177 /**
178  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
179  *
180  * \param width     width of the surface to be created
181  * \param height    height of the surface to be created
182  */
183 CairoRenderContext*
184 CairoRenderContext::cloneMe(double width, double height) const
186     g_assert( _is_valid );
187     g_assert( width > 0.0 && height > 0.0 );
189     CairoRenderContext *new_context = _renderer->createContext();
190     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
191                                                             (int)ceil(width), (int)ceil(height));
192     new_context->_cr = cairo_create(surface);
193     new_context->_surface = surface;
194     new_context->_is_valid = TRUE;
196     return new_context;
199 CairoRenderContext*
200 CairoRenderContext::cloneMe(void) const
202     g_assert( _is_valid );
204     return cloneMe(_width, _height);
207 bool
208 CairoRenderContext::setImageTarget(cairo_format_t format)
210     // format cannot be set on an already initialized surface
211     if (_is_valid)
212         return false;
214     switch (format) {
215         case CAIRO_FORMAT_ARGB32:
216         case CAIRO_FORMAT_RGB24:
217         case CAIRO_FORMAT_A8:
218         case CAIRO_FORMAT_A1:
219             _target_format = format;
220             _target = CAIRO_SURFACE_TYPE_IMAGE;
221             return true;
222             break;
223         default:
224             break;
225     }
227     return false;
230 bool
231 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
233 #ifndef CAIRO_HAS_PDF_SURFACE
234     return false;
235 #else
236     _target = CAIRO_SURFACE_TYPE_PDF;
237     _vector_based_target = TRUE;
238 #endif
240     FILE *osf = NULL;
241     FILE *osp = NULL;
243     gsize bytesRead = 0;
244     gsize bytesWritten = 0;
245     GError *error = NULL;
246     gchar *local_fn = g_filename_from_utf8(utf8_fn,
247                                            -1,  &bytesRead,  &bytesWritten, &error);
248     gchar const *fn = local_fn;
250     /* TODO: Replace the below fprintf's with something that does the right thing whether in
251     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
252     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
253     * return code.
254     */
255     if (fn != NULL) {
256         if (*fn == '|') {
257             fn += 1;
258             while (isspace(*fn)) fn += 1;
259 #ifndef WIN32
260             osp = popen(fn, "w");
261 #else
262             osp = _popen(fn, "w");
263 #endif
264             if (!osp) {
265                 fprintf(stderr, "inkscape: popen(%s): %s\n",
266                         fn, strerror(errno));
267                 return false;
268             }
269             _stream = osp;
270         } else if (*fn == '>') {
271             fn += 1;
272             while (isspace(*fn)) fn += 1;
273             Inkscape::IO::dump_fopen_call(fn, "K");
274             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
275             if (!osf) {
276                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
277                         fn, strerror(errno));
278                 return false;
279             }
280             _stream = osf;
281         } else {
282             /* put cwd stuff in here */
283             gchar *qn = ( *fn
284                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
285                 : g_strdup("lpr") );
286 #ifndef WIN32
287             osp = popen(qn, "w");
288 #else
289             osp = _popen(qn, "w");
290 #endif
291             if (!osp) {
292                 fprintf(stderr, "inkscape: popen(%s): %s\n",
293                         qn, strerror(errno));
294                 return false;
295             }
296             g_free(qn);
297             _stream = osp;
298         }
299     }
301     g_free(local_fn);
303     if (_stream) {
304         /* fixme: this is kinda icky */
305 #if !defined(_WIN32) && !defined(__WIN32__)
306         (void) signal(SIGPIPE, SIG_IGN);
307 #endif
308     }
310     return true;
313 bool
314 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
316 #ifndef CAIRO_HAS_PS_SURFACE
317     return false;
318 #else
319     _target = CAIRO_SURFACE_TYPE_PS;
320     _vector_based_target = TRUE;
321 #endif
323     FILE *osf = NULL;
324     FILE *osp = NULL;
326     gsize bytesRead = 0;
327     gsize bytesWritten = 0;
328     GError *error = NULL;
329     gchar *local_fn = g_filename_from_utf8(utf8_fn,
330                                            -1,  &bytesRead,  &bytesWritten, &error);
331     gchar const *fn = local_fn;
333     /* TODO: Replace the below fprintf's with something that does the right thing whether in
334     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
335     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
336     * return code.
337     */
338     if (fn != NULL) {
339         if (*fn == '|') {
340             fn += 1;
341             while (isspace(*fn)) fn += 1;
342 #ifndef WIN32
343             osp = popen(fn, "w");
344 #else
345             osp = _popen(fn, "w");
346 #endif
347             if (!osp) {
348                 fprintf(stderr, "inkscape: popen(%s): %s\n",
349                         fn, strerror(errno));
350                 return false;
351             }
352             _stream = osp;
353         } else if (*fn == '>') {
354             fn += 1;
355             while (isspace(*fn)) fn += 1;
356             Inkscape::IO::dump_fopen_call(fn, "K");
357             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
358             if (!osf) {
359                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
360                         fn, strerror(errno));
361                 return false;
362             }
363             _stream = osf;
364         } else {
365             /* put cwd stuff in here */
366             gchar *qn = ( *fn
367                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
368                 : g_strdup("lpr") );
369 #ifndef WIN32
370             osp = popen(qn, "w");
371 #else
372             osp = _popen(qn, "w");
373 #endif
374             if (!osp) {
375                 fprintf(stderr, "inkscape: popen(%s): %s\n",
376                         qn, strerror(errno));
377                 return false;
378             }
379             g_free(qn);
380             _stream = osp;
381         }
382     }
384     g_free(local_fn);
386     if (_stream) {
387         /* fixme: this is kinda icky */
388 #if !defined(_WIN32) && !defined(__WIN32__)
389         (void) signal(SIGPIPE, SIG_IGN);
390 #endif
391     }
393     return true;
396 void CairoRenderContext::setPSLevel(unsigned int level)
398     _ps_level = level;
401 unsigned int CairoRenderContext::getPSLevel(void)
403     return _ps_level;
406 void CairoRenderContext::setPDFLevel(unsigned int level)
408     _pdf_level = level;
411 void CairoRenderContext::setTextToPath(bool texttopath)
413     _is_texttopath = texttopath;
416 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
418     _is_filtertobitmap = filtertobitmap;
421 bool CairoRenderContext::getFilterToBitmap(void)
423     return _is_filtertobitmap;
426 void CairoRenderContext::setBitmapResolution(int resolution)
428     _bitmapresolution = resolution;
431 int CairoRenderContext::getBitmapResolution(void)
433     return _bitmapresolution;
436 cairo_surface_t*
437 CairoRenderContext::getSurface(void)
439     g_assert( _is_valid );
441     return _surface;
444 bool
445 CairoRenderContext::saveAsPng(const char *file_name)
447     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
448     if (status)
449         return false;
450     else
451         return true;
454 void
455 CairoRenderContext::setRenderMode(CairoRenderMode mode)
457     switch (mode) {
458         case RENDER_MODE_NORMAL:
459         case RENDER_MODE_CLIP:
460             _render_mode = mode;
461             break;
462         default:
463             _render_mode = RENDER_MODE_NORMAL;
464             break;
465     }
468 CairoRenderContext::CairoRenderMode
469 CairoRenderContext::getRenderMode(void) const
471     return _render_mode;
474 void
475 CairoRenderContext::setClipMode(CairoClipMode mode)
477     switch (mode) {
478         case CLIP_MODE_PATH:
479         case CLIP_MODE_MASK:
480             _clip_mode = mode;
481             break;
482         default:
483             _clip_mode = CLIP_MODE_PATH;
484             break;
485     }
488 CairoRenderContext::CairoClipMode
489 CairoRenderContext::getClipMode(void) const
491     return _clip_mode;
494 CairoRenderState*
495 CairoRenderContext::_createState(void)
497     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
498     g_assert( state != NULL );
500     state->has_filtereffect = FALSE;
501     state->merge_opacity = TRUE;
502     state->opacity = 1.0;
503     state->need_layer = FALSE;
504     state->has_overflow = FALSE;
505     state->parent_has_userspace = FALSE;
506     state->clip_path = NULL;
507     state->mask = NULL;
509     return state;
512 void
513 CairoRenderContext::pushLayer(void)
515     g_assert( _is_valid );
517     TRACE(("--pushLayer\n"));
518     cairo_push_group(_cr);
520     // clear buffer
521     if (!_vector_based_target) {
522         cairo_save(_cr);
523         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
524         cairo_paint(_cr);
525         cairo_restore(_cr);
526     }
529 void
530 CairoRenderContext::popLayer(void)
532     g_assert( _is_valid );
534     float opacity = _state->opacity;
535     TRACE(("--popLayer w/ %f\n", opacity));
537     // apply clipPath or mask if present
538     SPClipPath *clip_path = _state->clip_path;
539     SPMask *mask = _state->mask;
540     if (clip_path || mask) {
542         CairoRenderContext *clip_ctx = 0;
543         cairo_surface_t *clip_mask = 0;
545         if (clip_path) {
546             if (_render_mode == RENDER_MODE_CLIP)
547                 mask = NULL;    // disable mask when performing nested clipping
549             if (_vector_based_target) {
550                 setClipMode(CLIP_MODE_PATH);
551                 if (!mask) {
552                     cairo_pop_group_to_source(_cr);
553                     _renderer->applyClipPath(this, clip_path);
554                     if (opacity == 1.0)
555                         cairo_paint(_cr);
556                     else
557                         cairo_paint_with_alpha(_cr, opacity);
559                 } else {
560                     // the clipPath will be applied before masking
561                 }
562             } else {
564                 // setup a new rendering context
565                 clip_ctx = _renderer->createContext();
566                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
567                 clip_ctx->setClipMode(CLIP_MODE_MASK);
568                 if (!clip_ctx->setupSurface(_width, _height)) {
569                     TRACE(("setupSurface failed\n"));
570                     _renderer->destroyContext(clip_ctx);
571                     return;
572                 }
574                 // clear buffer
575                 cairo_save(clip_ctx->_cr);
576                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
577                 cairo_paint(clip_ctx->_cr);
578                 cairo_restore(clip_ctx->_cr);
580                 // if a mask won't be applied set opacity too
581                 if (!mask)
582                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
583                 else
584                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
586                 // copy over the correct CTM
587                 if (_state->parent_has_userspace)
588                     clip_ctx->setTransform(&getParentState()->transform);
589                 else
590                     clip_ctx->setTransform(&_state->transform);
592                 // apply the clip path
593                 clip_ctx->pushState();
594                 _renderer->applyClipPath(clip_ctx, clip_path);
595                 clip_ctx->popState();
597                 clip_mask = clip_ctx->getSurface();
598                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
600                 if (!mask) {
601                     cairo_pop_group_to_source(_cr);
602                     cairo_mask_surface(_cr, clip_mask, 0, 0);
603                     _renderer->destroyContext(clip_ctx);
604                 }
605             }
606         }
608         if (mask) {
609             // create rendering context for mask
610             CairoRenderContext *mask_ctx = _renderer->createContext();
611             mask_ctx->setupSurface(_width, _height);
613             // set rendering mode to normal
614             setRenderMode(RENDER_MODE_NORMAL);
616             // copy the correct CTM to mask context
617             if (_state->parent_has_userspace)
618                 mask_ctx->setTransform(&getParentState()->transform);
619             else
620                 mask_ctx->setTransform(&_state->transform);
622             // render mask contents to mask_ctx
623             _renderer->applyMask(mask_ctx, mask);
625             TEST(mask_ctx->saveAsPng("mask.png"));
627             // composite with clip mask
628             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
629                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
630                 _renderer->destroyContext(clip_ctx);
631             }
633             cairo_surface_t *mask_image = mask_ctx->getSurface();
634             int width = cairo_image_surface_get_width(mask_image);
635             int height = cairo_image_surface_get_height(mask_image);
636             int stride = cairo_image_surface_get_stride(mask_image);
637             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
639             // premultiply with opacity
640             if (_state->opacity != 1.0) {
641                 TRACE(("premul w/ %f\n", opacity));
642                 guint8 int_opacity = (guint8)(255 * opacity);
643                 for (int row = 0 ; row < height; row++) {
644                     unsigned char *row_data = pixels + (row * stride);
645                     for (int i = 0 ; i < width; i++) {
646                         guint32 *pixel = (guint32 *)row_data + i;
647                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
648                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
649                                 ((*pixel & 0x000000ff)      ) * 4688) *
650                                 int_opacity);
651                     }
652                 }
653             }
655             cairo_pop_group_to_source(_cr);
656             if (_clip_mode == CLIP_MODE_PATH) {
657                 // we have to do the clipping after cairo_pop_group_to_source
658                 _renderer->applyClipPath(this, clip_path);
659             }
660             // apply the mask onto the layer
661             cairo_mask_surface(_cr, mask_image, 0, 0);
662             _renderer->destroyContext(mask_ctx);
663         }
664     } else {
665         cairo_pop_group_to_source(_cr);
666         if (opacity == 1.0)
667             cairo_paint(_cr);
668         else
669             cairo_paint_with_alpha(_cr, opacity);
670     }
673 void
674 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
676     g_assert( _is_valid );
678     // here it should be checked whether the current clip winding changed
679     // so we could switch back to masked clipping
680     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
681         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
682     } else {
683         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
684     }
685     addPathVector(pv);
688 void
689 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
691     g_assert( _is_valid );
693     cairo_rectangle(_cr, x, y, width, height);
694     cairo_clip(_cr);
697 bool
698 CairoRenderContext::setupSurface(double width, double height)
700     // Is the surface already set up?
701     if (_is_valid)
702         return true;
704     if (_vector_based_target && _stream == NULL)
705         return false;
707     cairo_surface_t *surface = NULL;
708     switch (_target) {
709         case CAIRO_SURFACE_TYPE_IMAGE:
710             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
711             break;
712 #ifdef CAIRO_HAS_PDF_SURFACE
713         case CAIRO_SURFACE_TYPE_PDF:
714             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
715             break;
716 #endif
717 #ifdef CAIRO_HAS_PS_SURFACE
718         case CAIRO_SURFACE_TYPE_PS:
719             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
720 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
721             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
722                 return FALSE;
723             }
724             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
725 #endif
726             break;
727 #endif
728         default:
729             return false;
730             break;
731     }
733     return _finishSurfaceSetup (surface);
736 bool
737 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
739     if (_is_valid || !surface)
740         return false;
742     _vector_based_target = is_vector;
743     bool ret = _finishSurfaceSetup (surface);
744     if (ret)
745         cairo_surface_reference (surface);
746     return ret;
749 bool
750 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
752     if(surface == NULL) {
753         return FALSE;
754     }
755     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
756         return FALSE;
757     }
759     _cr = cairo_create(surface);
760     _surface = surface;
762     if (_vector_based_target) {
763         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
764     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
765         // set background color on non-alpha surfaces
766         // TODO: bgcolor should be derived from SPDocument
767         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
768         cairo_rectangle(_cr, 0, 0, _width, _height);
769         cairo_fill(_cr);
770     }
772     _is_valid = TRUE;
774     return true;
777 bool
778 CairoRenderContext::finish(void)
780     g_assert( _is_valid );
782     if (_vector_based_target)
783         cairo_show_page(_cr);
785     cairo_destroy(_cr);
786     cairo_surface_finish(_surface);
787     cairo_status_t status = cairo_surface_status(_surface);
788     cairo_surface_destroy(_surface);
789     _cr = NULL;
790     _surface = NULL;
792     if (_layout)
793         g_object_unref(_layout);
795     _is_valid = FALSE;
797     if (_vector_based_target && _stream) {
798         /* Flush stream to be sure. */
799         (void) fflush(_stream);
801         fclose(_stream);
802         _stream = NULL;
803     }
805     if (status == CAIRO_STATUS_SUCCESS)
806         return true;
807     else
808         return false;
811 void
812 CairoRenderContext::transform(Geom::Matrix const *transform)
814     g_assert( _is_valid );
816     cairo_matrix_t matrix;
817     _initCairoMatrix(&matrix, transform);
818     cairo_transform(_cr, &matrix);
820     // store new CTM
821     getTransform(&_state->transform);
824 void
825 CairoRenderContext::setTransform(Geom::Matrix const *transform)
827     g_assert( _is_valid );
829     cairo_matrix_t matrix;
830     _initCairoMatrix(&matrix, transform);
831     cairo_set_matrix(_cr, &matrix);
832     _state->transform = *transform;
835 void
836 CairoRenderContext::getTransform(Geom::Matrix *copy) const
838     g_assert( _is_valid );
840     cairo_matrix_t ctm;
841     cairo_get_matrix(_cr, &ctm);
842     (*copy)[0] = ctm.xx;
843     (*copy)[1] = ctm.yx;
844     (*copy)[2] = ctm.xy;
845     (*copy)[3] = ctm.yy;
846     (*copy)[4] = ctm.x0;
847     (*copy)[5] = ctm.y0;
850 void
851 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
853     g_assert( _is_valid );
855     CairoRenderState *parent_state = getParentState();
856     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
859 void
860 CairoRenderContext::pushState(void)
862     g_assert( _is_valid );
864     cairo_save(_cr);
866     CairoRenderState *new_state = _createState();
867     // copy current state's transform
868     new_state->transform = _state->transform;
869     _state_stack = g_slist_prepend(_state_stack, new_state);
870     _state = new_state;
873 void
874 CairoRenderContext::popState(void)
876     g_assert( _is_valid );
878     cairo_restore(_cr);
880     g_free(_state_stack->data);
881     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
882     _state = (CairoRenderState*)_state_stack->data;
884     g_assert( g_slist_length(_state_stack) > 0 );
887 static bool pattern_hasItemChildren (SPPattern *pat)
889     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
890         if (SP_IS_ITEM (child)) {
891             return true;
892         }
893     }
894     return false;
897 cairo_pattern_t*
898 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
900     g_assert( SP_IS_PATTERN(paintserver) );
902     SPPattern *pat = SP_PATTERN (paintserver);
904     Geom::Matrix ps2user, pcs2dev;
905     ps2user = Geom::identity();
906     pcs2dev = Geom::identity();
908     double x = pattern_x(pat);
909     double y = pattern_y(pat);
910     double width = pattern_width(pat);
911     double height = pattern_height(pat);
912     double bbox_width_scaler;
913     double bbox_height_scaler;
915     TRACE(("%f x %f pattern\n", width, height));
917     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
918         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
919         bbox_width_scaler = pbox->x1 - pbox->x0;
920         bbox_height_scaler = pbox->y1 - pbox->y0;
921         ps2user[4] = x * bbox_width_scaler + pbox->x0;
922         ps2user[5] = y * bbox_height_scaler + pbox->y0;
923     } else {
924         bbox_width_scaler = 1.0;
925         bbox_height_scaler = 1.0;
926         ps2user[4] = x;
927         ps2user[5] = y;
928     }
930     // apply pattern transformation
931     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
932     ps2user *= pattern_transform;
934     // create pattern contents coordinate system
935     if (pat->viewBox_set) {
936         NRRect *view_box = pattern_viewBox(pat);
938         double x, y, w, h;
939         double view_width, view_height;
940         x = 0;
941         y = 0;
942         w = width * bbox_width_scaler;
943         h = height * bbox_height_scaler;
945         view_width = view_box->x1 - view_box->x0;
946         view_height = view_box->y1 - view_box->y0;
948         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
949         pcs2dev[0] = w / view_width;
950         pcs2dev[3] = h / view_height;
951         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
952         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
953     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
954         pcs2dev[0] = pbox->x1 - pbox->x0;
955         pcs2dev[3] = pbox->y1 - pbox->y0;
956     }
958     // Calculate the size of the surface which has to be created so that the pattern resolution
959     // matches the output resolution (i.e., if the pattern is scaled up by a factor of two,
960     // the surface width should be scaled by a factor of two).
961     // The scaling needs to be taken into account in the ctm after the pattern transformation.
962     Geom::Matrix temp;
963     temp = pattern_transform * _state->transform;
964     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
965     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
967     if (_vector_based_target) {
968         // eliminate PT_PER_PX mul from these
969         width_scaler *= 1.25;
970         height_scaler *= 1.25;
971     }
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     double surface_width = ceil(bbox_width_scaler * width_scaler * width - 0.5);
975     double surface_height = ceil(bbox_height_scaler * height_scaler * height - 0.5);
976     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
977     // create new rendering context
978     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
980     // adjust the size of the painted pattern to fit exactly the created surface
981     // this has to be done because of the rounding to obtain an integer pattern surface width/height
982     double scale_width = surface_width / (bbox_width_scaler * width);
983     double scale_height = surface_height / (bbox_height_scaler * height);
984     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
985         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
986         pcs2dev *= Geom::Scale(scale_width, scale_height);
987         ps2user *= Geom::Scale(1.0 / scale_width, 1.0 / scale_height);
988     }
990     pattern_ctx->setTransform(&pcs2dev);
991     pattern_ctx->pushState();
993     // create arena and group
994     NRArena *arena = NRArena::create();
995     unsigned dkey = sp_item_display_key_new(1);
997     // show items and render them
998     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
999         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1000             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1001                 if (SP_IS_ITEM (child)) {
1002                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1003                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1004                 }
1005             }
1006             break; // do not go further up the chain if children are found
1007         }
1008     }
1010     pattern_ctx->popState();
1012     // setup a cairo_pattern_t
1013     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1014     TEST(pattern_ctx->saveAsPng("pattern.png"));
1015     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1016     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1018     // set pattern transformation
1019     cairo_matrix_t pattern_matrix;
1020     _initCairoMatrix(&pattern_matrix, &ps2user);
1021     cairo_matrix_invert(&pattern_matrix);
1022     cairo_pattern_set_matrix(result, &pattern_matrix);
1024     delete pattern_ctx;
1026     // hide all items
1027     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1028         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1029             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1030                 if (SP_IS_ITEM (child)) {
1031                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1032                 }
1033             }
1034             break; // do not go further up the chain if children are found
1035         }
1036     }
1038     return result;
1041 cairo_pattern_t*
1042 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1043                                                  NRRect const *pbox, float alpha)
1045     cairo_pattern_t *pattern = NULL;
1046     bool apply_bbox2user = FALSE;
1048     if (SP_IS_LINEARGRADIENT (paintserver)) {
1050             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1052             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1054             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1055             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1056             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1057                 // convert to userspace
1058                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1059                 p1 *= bbox2user;
1060                 p2 *= bbox2user;
1061             }
1063             // create linear gradient pattern
1064             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1066             // add stops
1067             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1068                 float rgb[3];
1069                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1070                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1071             }
1072     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1074         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1076         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1078         Geom::Point c (rg->cx.computed, rg->cy.computed);
1079         Geom::Point f (rg->fx.computed, rg->fy.computed);
1080         double r = rg->r.computed;
1081         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1082             apply_bbox2user = true;
1084         // create radial gradient pattern
1085         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1087         // add stops
1088         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1089             float rgb[3];
1090             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1091             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1092         }
1093     } else if (SP_IS_PATTERN (paintserver)) {
1095         pattern = _createPatternPainter(paintserver, pbox);
1096     } else {
1097         return NULL;
1098     }
1100     if (pattern && SP_IS_GRADIENT (paintserver)) {
1101         SPGradient *g = SP_GRADIENT(paintserver);
1103         // set extend type
1104         SPGradientSpread spread = sp_gradient_get_spread(g);
1105         switch (spread) {
1106             case SP_GRADIENT_SPREAD_REPEAT: {
1107                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1108                 break;
1109             }
1110             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1111                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1112                 break;
1113             }
1114             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1115                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1116                 break;
1117             }
1118             default: {
1119                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1120                 break;
1121             }
1122         }
1124         cairo_matrix_t pattern_matrix;
1125         if (g->gradientTransform_set) {
1126             // apply gradient transformation
1127             cairo_matrix_init(&pattern_matrix,
1128                 g->gradientTransform[0], g->gradientTransform[1],
1129                 g->gradientTransform[2], g->gradientTransform[3],
1130                 g->gradientTransform[4], g->gradientTransform[5]);
1131         } else {
1132             cairo_matrix_init_identity (&pattern_matrix);
1133         }
1135         if (apply_bbox2user) {
1136             // convert to userspace
1137             cairo_matrix_t bbox2user;
1138             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1139             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1140         }
1141         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1142         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1143     }
1145     return pattern;
1148 void
1149 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1151     g_return_if_fail( style->fill.isColor()
1152                       || style->fill.isPaintserver() );
1154     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1155     if (_state->merge_opacity) {
1156         alpha *= _state->opacity;
1157         TRACE(("merged op=%f\n", alpha));
1158     }
1160     if (style->fill.isColor()) {
1161         float rgb[3];
1162         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1164         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1165     } else {
1166         g_assert( style->fill.isPaintserver()
1167                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1168                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1170         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1172         if (pattern) {
1173             cairo_set_source(_cr, pattern);
1174             cairo_pattern_destroy(pattern);
1175         }
1176     }
1179 void
1180 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1182     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1183     if (_state->merge_opacity)
1184         alpha *= _state->opacity;
1186     if (style->stroke.isColor()) {
1187         float rgb[3];
1188         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1190         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1191     } else {
1192         g_assert( style->fill.isPaintserver()
1193                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1194                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1196         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1198         if (pattern) {
1199             cairo_set_source(_cr, pattern);
1200             cairo_pattern_destroy(pattern);
1201         }
1202     }
1204     if (style->stroke_dash.n_dash   &&
1205         style->stroke_dash.dash       )
1206     {
1207         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1208     } else {
1209         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1210     }
1212     cairo_set_line_width(_cr, style->stroke_width.computed);
1214     // set line join type
1215     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1216     switch (style->stroke_linejoin.computed) {
1217         case SP_STROKE_LINEJOIN_MITER:
1218             join = CAIRO_LINE_JOIN_MITER;
1219             break;
1220         case SP_STROKE_LINEJOIN_ROUND:
1221             join = CAIRO_LINE_JOIN_ROUND;
1222             break;
1223         case SP_STROKE_LINEJOIN_BEVEL:
1224             join = CAIRO_LINE_JOIN_BEVEL;
1225             break;
1226     }
1227     cairo_set_line_join(_cr, join);
1229     // set line cap type
1230     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1231     switch (style->stroke_linecap.computed) {
1232         case SP_STROKE_LINECAP_BUTT:
1233             cap = CAIRO_LINE_CAP_BUTT;
1234             break;
1235         case SP_STROKE_LINECAP_ROUND:
1236             cap = CAIRO_LINE_CAP_ROUND;
1237             break;
1238         case SP_STROKE_LINECAP_SQUARE:
1239             cap = CAIRO_LINE_CAP_SQUARE;
1240             break;
1241     }
1242     cairo_set_line_cap(_cr, cap);
1243     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1246 bool
1247 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1249     g_assert( _is_valid );
1251     if (_render_mode == RENDER_MODE_CLIP) {
1252         if (_clip_mode == CLIP_MODE_PATH) {
1253             addClipPath(pathv, &style->fill_rule);
1254         } else {
1255             setPathVector(pathv);
1256             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1257                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1258             } else {
1259                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1260             }
1261             cairo_fill(_cr);
1262             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1263         }
1264         return true;
1265     }
1267     if (style->fill.isNone() && style->stroke.isNone())
1268         return true;
1270     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1271                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1273     if (!need_layer)
1274         cairo_save(_cr);
1275     else
1276         pushLayer();
1278     if (!style->fill.isNone()) {
1279         _setFillStyle(style, pbox);
1280         setPathVector(pathv);
1282         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1283             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1284         } else {
1285             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1286         }
1288         if (style->stroke.isNone())
1289             cairo_fill(_cr);
1290         else
1291             cairo_fill_preserve(_cr);
1292     }
1294     if (!style->stroke.isNone()) {
1295         _setStrokeStyle(style, pbox);
1296         if (style->fill.isNone())
1297             setPathVector(pathv);
1299         cairo_stroke(_cr);
1300     }
1302     if (need_layer)
1303         popLayer();
1304     else
1305         cairo_restore(_cr);
1307     return true;
1310 bool
1311 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1312                                 Geom::Matrix const *image_transform, SPStyle const *style)
1314     g_assert( _is_valid );
1316     if (_render_mode == RENDER_MODE_CLIP)
1317         return true;
1319     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1320     if (!px_rgba)
1321         return false;
1323     float opacity;
1324     if (_state->merge_opacity)
1325         opacity = _state->opacity;
1326     else
1327         opacity = 1.0;
1329     // make a copy of the original pixbuf with premultiplied alpha
1330     // if we pass the original pixbuf it will get messed up
1331     for (unsigned i = 0; i < h; i++) {
1332         for (unsigned j = 0; j < w; j++) {
1333             guchar const *src = px + i * rs + j * 4;
1334             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1335             guchar r, g, b, alpha_dst;
1337             // calculate opacity-modified alpha
1338             alpha_dst = src[3];
1339             if (opacity != 1.0 && _vector_based_target)
1340                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1342             // premul alpha (needed because this will be undone by cairo-pdf)
1343             r = src[0]*alpha_dst/255;
1344             g = src[1]*alpha_dst/255;
1345             b = src[2]*alpha_dst/255;
1347             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1348         }
1349     }
1351     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1352     if (cairo_surface_status(image_surface)) {
1353         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1354         return false;
1355     }
1357     // setup automatic freeing of the image data when destroying the surface
1358     static cairo_user_data_key_t key;
1359     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1361     cairo_save(_cr);
1363     // scaling by width & height is not needed because it will be done by Cairo
1364     if (image_transform)
1365         transform(image_transform);
1367     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1369     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1370     if (_vector_based_target) {
1371         cairo_new_path(_cr);
1372         cairo_rectangle(_cr, 0, 0, w, h);
1373         cairo_clip(_cr);
1374     }
1376     if (_vector_based_target)
1377         cairo_paint(_cr);
1378     else
1379         cairo_paint_with_alpha(_cr, opacity);
1381     cairo_restore(_cr);
1383     cairo_surface_destroy(image_surface);
1385     return true;
1388 #define GLYPH_ARRAY_SIZE 64
1390 unsigned int
1391 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1393     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1394     cairo_glyph_t *glyphs = glyph_array;
1395     unsigned int num_glyphs = glyphtext.size();
1396     if (num_glyphs > GLYPH_ARRAY_SIZE)
1397         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1399     unsigned int num_invalid_glyphs = 0;
1400     unsigned int i = 0;
1401     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1402         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1403         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1404         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1405             TRACE(("INVALID GLYPH found\n"));
1406             num_invalid_glyphs++;
1407             continue;
1408         }
1409         glyphs[i - num_invalid_glyphs].index = it_info->index;
1410         glyphs[i - num_invalid_glyphs].x = it_info->x;
1411         glyphs[i - num_invalid_glyphs].y = it_info->y;
1412         i++;
1413     }
1415     if (is_stroke || _is_texttopath)
1416         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1417     else
1418         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1420     if (num_glyphs > GLYPH_ARRAY_SIZE)
1421         g_free(glyphs);
1423     return num_glyphs - num_invalid_glyphs;
1426 bool
1427 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1428                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1430     // create a cairo_font_face from PangoFont
1431     double size = style->font_size.computed;
1432     cairo_font_face_t *font_face = NULL;
1434     FcPattern *fc_pattern = NULL;
1435     
1436 #ifdef USE_PANGO_WIN32
1437 # ifdef CAIRO_HAS_WIN32_FONT
1438     LOGFONTA *lfa = pango_win32_font_logfont(font);
1439     LOGFONTW lfw;
1441     ZeroMemory(&lfw, sizeof(LOGFONTW));
1442     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1443     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1444     
1445     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1446 # endif
1447 #else
1448 # ifdef CAIRO_HAS_FT_FONT
1449     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1450     fc_pattern = fc_font->font_pattern;
1451     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1452 # endif
1453 #endif
1454     
1455     cairo_save(_cr);
1456     cairo_set_font_face(_cr, font_face);
1458     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1459         size = 12.0;
1461     // set the given font matrix
1462     cairo_matrix_t matrix;
1463     _initCairoMatrix(&matrix, font_matrix);
1464     cairo_set_font_matrix(_cr, &matrix);
1466     if (_render_mode == RENDER_MODE_CLIP) {
1467         if (_clip_mode == CLIP_MODE_MASK) {
1468             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1469                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1470             } else {
1471                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1472             }
1473             _showGlyphs(_cr, font, glyphtext, FALSE);
1474         } else {
1475             // just add the glyph paths to the current context
1476             _showGlyphs(_cr, font, glyphtext, TRUE);
1477         }
1478     } else {
1480         if (style->fill.isColor() || style->fill.isPaintserver()) {
1481             // set fill style
1482             _setFillStyle(style, NULL);
1484             _showGlyphs(_cr, font, glyphtext, FALSE);
1485         }
1487         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1488             // set stroke style
1489             _setStrokeStyle(style, NULL);
1491             // paint stroke
1492             _showGlyphs(_cr, font, glyphtext, TRUE);
1493             cairo_stroke(_cr);
1494         }
1495     }
1497     cairo_restore(_cr);
1499     if (font_face)
1500         cairo_font_face_destroy(font_face);
1502     return true;
1505 /* Helper functions */
1507 void
1508 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1510     cairo_new_path(_cr);
1511     addPathVector(pv);
1514 void
1515 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1517     feed_pathvector_to_cairo(_cr, pv);
1520 void
1521 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1523     cairo_matrix_t matrix;
1525     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1526     cairo_transform(cr, &matrix);
1529 void
1530 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1532     matrix->xx = (*transform)[0];
1533     matrix->yx = (*transform)[1];
1534     matrix->xy = (*transform)[2];
1535     matrix->yy = (*transform)[3];
1536     matrix->x0 = (*transform)[4];
1537     matrix->y0 = (*transform)[5];
1540 void
1541 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1543     _concatTransform(cr, (*transform)[0], (*transform)[1],
1544                      (*transform)[2], (*transform)[3],
1545                      (*transform)[4], (*transform)[5]);
1548 static cairo_status_t
1549 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1551     size_t written;
1552     FILE *file = (FILE*)closure;
1554     written = fwrite (data, 1, length, file);
1556     if (written == length)
1557         return CAIRO_STATUS_SUCCESS;
1558     else
1559         return CAIRO_STATUS_WRITE_ERROR;
1562 #include "clear-n_.h"
1564 }  /* namespace Internal */
1565 }  /* namespace Extension */
1566 }  /* namespace Inkscape */
1568 #undef TRACE
1569 #undef TEST
1571 /* End of GNU GPL code */
1574 /*
1575   Local Variables:
1576   mode:c++
1577   c-file-style:"stroustrup"
1578   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1579   indent-tabs-mode:nil
1580   fill-column:99
1581   End:
1582 */
1583 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :