Code

656cd4a08956b40d5e4498060e2f6a2cdad5c126
[inkscape.git] / src / extension / internal / cairo-render-context.cpp
1 #define __SP_CAIRO_RENDER_CONTEXT_C__
3 /** \file
4  * Rendering with Cairo.
5  */
6 /*
7  * Author:
8  *   Miklos Erdelyi <erdelyim@gmail.com>
9  *
10  * Copyright (C) 2006 Miklos Erdelyi
11  *
12  * Licensed under GNU GPL
13  */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #ifndef PANGO_ENABLE_BACKEND
20 #define PANGO_ENABLE_BACKEND
21 #endif
23 #ifndef PANGO_ENABLE_ENGINE
24 #define PANGO_ENABLE_ENGINE
25 #endif
28 #include <signal.h>
29 #include <errno.h>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 #define TRACE(_args)
84 //#define TEST(_args) _args
85 #define TEST(_args)
87 // FIXME: expose these from sp-clippath/mask.cpp
88 struct SPClipPathView {
89     SPClipPathView *next;
90     unsigned int key;
91     NRArenaItem *arenaitem;
92     NRRect bbox;
93 };
95 struct SPMaskView {
96     SPMaskView *next;
97     unsigned int key;
98     NRArenaItem *arenaitem;
99     NRRect bbox;
100 };
102 namespace Inkscape {
103 namespace Extension {
104 namespace Internal {
106 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
108 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
109     _dpi(72),
110     _pdf_level(0),
111     _ps_level(1),
112     _eps(false),
113     _is_texttopath(FALSE),
114     _is_filtertobitmap(FALSE),
115     _bitmapresolution(72),
116     _stream(NULL),
117     _is_valid(FALSE),
118     _vector_based_target(FALSE),
119     _cr(NULL), // Cairo context
120     _surface(NULL),
121     _target(CAIRO_SURFACE_TYPE_IMAGE),
122     _target_format(CAIRO_FORMAT_ARGB32),
123     _layout(NULL),
124     _state(NULL),
125     _renderer(parent),
126     _render_mode(RENDER_MODE_NORMAL),
127     _clip_mode(CLIP_MODE_MASK)
128 {}
130 CairoRenderContext::~CairoRenderContext(void)
132     if (_cr) cairo_destroy(_cr);
133     if (_surface) cairo_surface_destroy(_surface);
134     if (_layout) g_object_unref(_layout);
137 CairoRenderer*
138 CairoRenderContext::getRenderer(void) const
140     return _renderer;
143 CairoRenderState*
144 CairoRenderContext::getCurrentState(void) const
146     return _state;
149 CairoRenderState*
150 CairoRenderContext::getParentState(void) const
152     // if this is the root node just return it
153     if (g_slist_length(_state_stack) == 1) {
154         return _state;
155     } else {
156         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
157     }
160 void
161 CairoRenderContext::setStateForStyle(SPStyle const *style)
163     // only opacity & overflow is stored for now
164     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
165     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
166     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
168     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
169         _state->merge_opacity = FALSE;
171     // disable rendering of opacity if there's a stroke on the fill
172     if (_state->merge_opacity
173         && !style->fill.isNone()
174         && !style->stroke.isNone())
175         _state->merge_opacity = FALSE;
178 /**
179  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
180  *
181  * \param width     width of the surface to be created
182  * \param height    height of the surface to be created
183  */
184 CairoRenderContext*
185 CairoRenderContext::cloneMe(double width, double height) const
187     g_assert( _is_valid );
188     g_assert( width > 0.0 && height > 0.0 );
190     CairoRenderContext *new_context = _renderer->createContext();
191     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
192                                                             (int)ceil(width), (int)ceil(height));
193     new_context->_cr = cairo_create(surface);
194     new_context->_surface = surface;
195     new_context->_is_valid = TRUE;
197     return new_context;
200 CairoRenderContext*
201 CairoRenderContext::cloneMe(void) const
203     g_assert( _is_valid );
205     return cloneMe(_width, _height);
208 bool
209 CairoRenderContext::setImageTarget(cairo_format_t format)
211     // format cannot be set on an already initialized surface
212     if (_is_valid)
213         return false;
215     switch (format) {
216         case CAIRO_FORMAT_ARGB32:
217         case CAIRO_FORMAT_RGB24:
218         case CAIRO_FORMAT_A8:
219         case CAIRO_FORMAT_A1:
220             _target_format = format;
221             _target = CAIRO_SURFACE_TYPE_IMAGE;
222             return true;
223             break;
224         default:
225             break;
226     }
228     return false;
231 bool
232 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
234 #ifndef CAIRO_HAS_PDF_SURFACE
235     return false;
236 #else
237     _target = CAIRO_SURFACE_TYPE_PDF;
238     _vector_based_target = TRUE;
239 #endif
241     FILE *osf = NULL;
242     FILE *osp = NULL;
244     gsize bytesRead = 0;
245     gsize bytesWritten = 0;
246     GError *error = NULL;
247     gchar *local_fn = g_filename_from_utf8(utf8_fn,
248                                            -1,  &bytesRead,  &bytesWritten, &error);
249     gchar const *fn = local_fn;
251     /* TODO: Replace the below fprintf's with something that does the right thing whether in
252     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
253     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
254     * return code.
255     */
256     if (fn != NULL) {
257         if (*fn == '|') {
258             fn += 1;
259             while (isspace(*fn)) fn += 1;
260 #ifndef WIN32
261             osp = popen(fn, "w");
262 #else
263             osp = _popen(fn, "w");
264 #endif
265             if (!osp) {
266                 fprintf(stderr, "inkscape: popen(%s): %s\n",
267                         fn, strerror(errno));
268                 return false;
269             }
270             _stream = osp;
271         } else if (*fn == '>') {
272             fn += 1;
273             while (isspace(*fn)) fn += 1;
274             Inkscape::IO::dump_fopen_call(fn, "K");
275             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
276             if (!osf) {
277                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
278                         fn, strerror(errno));
279                 return false;
280             }
281             _stream = osf;
282         } else {
283             /* put cwd stuff in here */
284             gchar *qn = ( *fn
285                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
286                 : g_strdup("lpr") );
287 #ifndef WIN32
288             osp = popen(qn, "w");
289 #else
290             osp = _popen(qn, "w");
291 #endif
292             if (!osp) {
293                 fprintf(stderr, "inkscape: popen(%s): %s\n",
294                         qn, strerror(errno));
295                 return false;
296             }
297             g_free(qn);
298             _stream = osp;
299         }
300     }
302     g_free(local_fn);
304     if (_stream) {
305         /* fixme: this is kinda icky */
306 #if !defined(_WIN32) && !defined(__WIN32__)
307         (void) signal(SIGPIPE, SIG_IGN);
308 #endif
309     }
311     return true;
314 bool
315 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
317 #ifndef CAIRO_HAS_PS_SURFACE
318     return false;
319 #else
320     _target = CAIRO_SURFACE_TYPE_PS;
321     _vector_based_target = TRUE;
322 #endif
324     FILE *osf = NULL;
325     FILE *osp = NULL;
327     gsize bytesRead = 0;
328     gsize bytesWritten = 0;
329     GError *error = NULL;
330     gchar *local_fn = g_filename_from_utf8(utf8_fn,
331                                            -1,  &bytesRead,  &bytesWritten, &error);
332     gchar const *fn = local_fn;
334     /* TODO: Replace the below fprintf's with something that does the right thing whether in
335     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
336     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
337     * return code.
338     */
339     if (fn != NULL) {
340         if (*fn == '|') {
341             fn += 1;
342             while (isspace(*fn)) fn += 1;
343 #ifndef WIN32
344             osp = popen(fn, "w");
345 #else
346             osp = _popen(fn, "w");
347 #endif
348             if (!osp) {
349                 fprintf(stderr, "inkscape: popen(%s): %s\n",
350                         fn, strerror(errno));
351                 return false;
352             }
353             _stream = osp;
354         } else if (*fn == '>') {
355             fn += 1;
356             while (isspace(*fn)) fn += 1;
357             Inkscape::IO::dump_fopen_call(fn, "K");
358             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
359             if (!osf) {
360                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
361                         fn, strerror(errno));
362                 return false;
363             }
364             _stream = osf;
365         } else {
366             /* put cwd stuff in here */
367             gchar *qn = ( *fn
368                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
369                 : g_strdup("lpr") );
370 #ifndef WIN32
371             osp = popen(qn, "w");
372 #else
373             osp = _popen(qn, "w");
374 #endif
375             if (!osp) {
376                 fprintf(stderr, "inkscape: popen(%s): %s\n",
377                         qn, strerror(errno));
378                 return false;
379             }
380             g_free(qn);
381             _stream = osp;
382         }
383     }
385     g_free(local_fn);
387     if (_stream) {
388         /* fixme: this is kinda icky */
389 #if !defined(_WIN32) && !defined(__WIN32__)
390         (void) signal(SIGPIPE, SIG_IGN);
391 #endif
392     }
394     return true;
397 void CairoRenderContext::setPSLevel(unsigned int level)
399     _ps_level = level;
402 void CairoRenderContext::setEPS(bool eps)
404     _eps = eps;
407 unsigned int CairoRenderContext::getPSLevel(void)
409     return _ps_level;
412 void CairoRenderContext::setPDFLevel(unsigned int level)
414     _pdf_level = level;
417 void CairoRenderContext::setTextToPath(bool texttopath)
419     _is_texttopath = texttopath;
422 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
424     _is_filtertobitmap = filtertobitmap;
427 bool CairoRenderContext::getFilterToBitmap(void)
429     return _is_filtertobitmap;
432 void CairoRenderContext::setBitmapResolution(int resolution)
434     _bitmapresolution = resolution;
437 int CairoRenderContext::getBitmapResolution(void)
439     return _bitmapresolution;
442 cairo_surface_t*
443 CairoRenderContext::getSurface(void)
445     g_assert( _is_valid );
447     return _surface;
450 bool
451 CairoRenderContext::saveAsPng(const char *file_name)
453     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
454     if (status)
455         return false;
456     else
457         return true;
460 void
461 CairoRenderContext::setRenderMode(CairoRenderMode mode)
463     switch (mode) {
464         case RENDER_MODE_NORMAL:
465         case RENDER_MODE_CLIP:
466             _render_mode = mode;
467             break;
468         default:
469             _render_mode = RENDER_MODE_NORMAL;
470             break;
471     }
474 CairoRenderContext::CairoRenderMode
475 CairoRenderContext::getRenderMode(void) const
477     return _render_mode;
480 void
481 CairoRenderContext::setClipMode(CairoClipMode mode)
483     switch (mode) {
484         case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
485         case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
486             _clip_mode = mode;
487             break;
488         default:
489             _clip_mode = CLIP_MODE_PATH;
490             break;
491     }
494 CairoRenderContext::CairoClipMode
495 CairoRenderContext::getClipMode(void) const
497     return _clip_mode;
500 CairoRenderState*
501 CairoRenderContext::_createState(void)
503     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
504     g_assert( state != NULL );
506     state->has_filtereffect = FALSE;
507     state->merge_opacity = TRUE;
508     state->opacity = 1.0;
509     state->need_layer = FALSE;
510     state->has_overflow = FALSE;
511     state->parent_has_userspace = FALSE;
512     state->clip_path = NULL;
513     state->mask = NULL;
515     return state;
518 void
519 CairoRenderContext::pushLayer(void)
521     g_assert( _is_valid );
523     TRACE(("--pushLayer\n"));
524     cairo_push_group(_cr);
526     // clear buffer
527     if (!_vector_based_target) {
528         cairo_save(_cr);
529         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
530         cairo_paint(_cr);
531         cairo_restore(_cr);
532     }
535 void
536 CairoRenderContext::popLayer(void)
538     g_assert( _is_valid );
540     float opacity = _state->opacity;
541     TRACE(("--popLayer w/ opacity %f\n", opacity));
543     /*
544      At this point, the Cairo source is ready. A Cairo mask must be created if required.
545      Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
546      masks independently of the objects they effect while in SVG the clip paths and masks
547      are defined relative to the objects they are attached to.
548      Notes:
549      1. An SVG object may have both a clip path and a mask!
550      2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
551      3. An SVG clipped or masked object may be first drawn off the page and then translated onto
552         the page (document). This is also not handled properly.
553      4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
554      5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
555         alpha. This is handled here by doing a pixel by pixel conversion.
556     */
558     SPClipPath *clip_path = _state->clip_path;
559     SPMask *mask = _state->mask;
560     if (clip_path || mask) {
562         CairoRenderContext *clip_ctx = 0;
563         cairo_surface_t *clip_mask = 0;
565         // Apply any clip path first
566         if (clip_path) {
567             TRACE(("  Applying clip\n"));
568             if (_render_mode == RENDER_MODE_CLIP)
569                 mask = NULL;    // disable mask when performing nested clipping
571             if (_vector_based_target) {
572                 setClipMode(CLIP_MODE_PATH); // Vector
573                 if (!mask) {
574                     cairo_pop_group_to_source(_cr);
575                     _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
576                     if (opacity == 1.0)
577                         cairo_paint(_cr);
578                     else
579                         cairo_paint_with_alpha(_cr, opacity);
581                 } else {
582                     // the clipPath will be applied before masking
583                 }
584             } else {
586                 // setup a new rendering context
587                 clip_ctx = _renderer->createContext();
588                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
589                 clip_ctx->setClipMode(CLIP_MODE_MASK);  // Raster
590                 // This code ties the clipping to the document coordinates. It doesn't allow
591                 // for a clipped object intially drawn off the page and then translated onto
592                 // the page.
593                 if (!clip_ctx->setupSurface(_width, _height)) {
594                     TRACE(("clip: setupSurface failed\n"));
595                     _renderer->destroyContext(clip_ctx);
596                     return;
597                 }
599                 // clear buffer
600                 cairo_save(clip_ctx->_cr);
601                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
602                 cairo_paint(clip_ctx->_cr);
603                 cairo_restore(clip_ctx->_cr);
605                 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
606                 if (!mask)
607                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
608                 else
609                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
611                 // copy over the correct CTM
612                 // It must be stored in item_transform of current state after pushState.
613                 Geom::Matrix item_transform; 
614                 if (_state->parent_has_userspace)
615                     item_transform = getParentState()->transform * _state->item_transform;
616                 else
617                     item_transform = _state->item_transform;
619                 // apply the clip path
620                 clip_ctx->pushState();
621                 clip_ctx->getCurrentState()->item_transform = item_transform;
622                 _renderer->applyClipPath(clip_ctx, clip_path);
623                 clip_ctx->popState();
625                 clip_mask = clip_ctx->getSurface();
626                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
628                 if (!mask) {
629                     cairo_pop_group_to_source(_cr);
630                     cairo_mask_surface(_cr, clip_mask, 0, 0);
631                     _renderer->destroyContext(clip_ctx);
632                 }
633             }
634         }
636         // Apply any mask second
637         if (mask) {
638             TRACE(("  Applying mask\n"));
639             // create rendering context for mask
640             CairoRenderContext *mask_ctx = _renderer->createContext();
642             // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
643             // Cairo surface is expecting the mask to be 90 dpi.
644             float surface_width = _width;
645             float surface_height = _height;
646             if( _vector_based_target ) {
647                 surface_width *= 1.25;
648                 surface_height *= 1.25;
649             }
650             mask_ctx->setupSurface( surface_width, surface_height );
651             TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
653             // set rendering mode to normal
654             setRenderMode(RENDER_MODE_NORMAL);
656             // copy the correct CTM to mask context
657             /*
658             if (_state->parent_has_userspace)
659                 mask_ctx->setTransform(&getParentState()->transform);
660             else
661                 mask_ctx->setTransform(&_state->transform);
662             */
663             // This is probably not correct... but it seems to do the trick.
664             mask_ctx->setTransform(&_state->item_transform);
666             // render mask contents to mask_ctx
667             _renderer->applyMask(mask_ctx, mask);
669             TEST(mask_ctx->saveAsPng("mask.png"));
671             // composite with clip mask
672             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
673                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
674                 _renderer->destroyContext(clip_ctx);
675             }
677             cairo_surface_t *mask_image = mask_ctx->getSurface();
678             int width = cairo_image_surface_get_width(mask_image);
679             int height = cairo_image_surface_get_height(mask_image);
680             int stride = cairo_image_surface_get_stride(mask_image);
681             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
683             // premultiply with opacity
684             // In SVG, the rgb channels as well as the alpha channel is used in masking.
685             // In Cairo, only the alpha channel is used thus requiring this conversion.
686             TRACE(("premul w/ %f\n", opacity));
687             guint8 int_opacity = (guint8)(255 * opacity);
688             for (int row = 0 ; row < height; row++) {
689                 unsigned char *row_data = pixels + (row * stride);
690                 for (int i = 0 ; i < width; i++) {
691                     guint32 *pixel = (guint32 *)row_data + i;
692                     *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
693                                ((*pixel & 0x0000ff00) >>  8) * 46518 +
694                                ((*pixel & 0x000000ff)      ) * 4688) *
695                               int_opacity);
696                 }
697             }
699             cairo_pop_group_to_source(_cr);
700             if (_clip_mode == CLIP_MODE_PATH) {
701                 // we have to do the clipping after cairo_pop_group_to_source
702                 _renderer->applyClipPath(this, clip_path);
703             }
704             // apply the mask onto the layer
705             cairo_mask_surface(_cr, mask_image, 0, 0);
706             _renderer->destroyContext(mask_ctx);
707         }
708     } else {
709         // No clip path or mask
710         cairo_pop_group_to_source(_cr);
711         if (opacity == 1.0)
712             cairo_paint(_cr);
713         else
714             cairo_paint_with_alpha(_cr, opacity);
715     }
718 void
719 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
721     g_assert( _is_valid );
723     // here it should be checked whether the current clip winding changed
724     // so we could switch back to masked clipping
725     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
726         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
727     } else {
728         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
729     }
730     addPathVector(pv);
733 void
734 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
736     g_assert( _is_valid );
738     cairo_rectangle(_cr, x, y, width, height);
739     cairo_clip(_cr);
742 bool
743 CairoRenderContext::setupSurface(double width, double height)
745     // Is the surface already set up?
746     if (_is_valid)
747         return true;
749     if (_vector_based_target && _stream == NULL)
750         return false;
752     cairo_surface_t *surface = NULL;
753     switch (_target) {
754         case CAIRO_SURFACE_TYPE_IMAGE:
755             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
756             break;
757 #ifdef CAIRO_HAS_PDF_SURFACE
758         case CAIRO_SURFACE_TYPE_PDF:
759             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
760             break;
761 #endif
762 #ifdef CAIRO_HAS_PS_SURFACE
763         case CAIRO_SURFACE_TYPE_PS:
764             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
765 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
766             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
767                 return FALSE;
768             }
769             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
770             cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
771 #endif
772             break;
773 #endif
774         default:
775             return false;
776             break;
777     }
779     return _finishSurfaceSetup (surface);
782 bool
783 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
785     if (_is_valid || !surface)
786         return false;
788     _vector_based_target = is_vector;
789     bool ret = _finishSurfaceSetup (surface);
790     if (ret)
791         cairo_surface_reference (surface);
792     return ret;
795 bool
796 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
798     if(surface == NULL) {
799         return FALSE;
800     }
801     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
802         return FALSE;
803     }
805     _cr = cairo_create(surface);
806     _surface = surface;
808     if (_vector_based_target) {
809         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
810     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
811         // set background color on non-alpha surfaces
812         // TODO: bgcolor should be derived from SPDocument
813         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
814         cairo_rectangle(_cr, 0, 0, _width, _height);
815         cairo_fill(_cr);
816     }
818     _is_valid = TRUE;
820     return true;
823 bool
824 CairoRenderContext::finish(void)
826     g_assert( _is_valid );
828     if (_vector_based_target)
829         cairo_show_page(_cr);
831     cairo_destroy(_cr);
832     cairo_surface_finish(_surface);
833     cairo_status_t status = cairo_surface_status(_surface);
834     cairo_surface_destroy(_surface);
835     _cr = NULL;
836     _surface = NULL;
838     if (_layout)
839         g_object_unref(_layout);
841     _is_valid = FALSE;
843     if (_vector_based_target && _stream) {
844         /* Flush stream to be sure. */
845         (void) fflush(_stream);
847         fclose(_stream);
848         _stream = NULL;
849     }
851     if (status == CAIRO_STATUS_SUCCESS)
852         return true;
853     else
854         return false;
857 void
858 CairoRenderContext::transform(Geom::Matrix const *transform)
860     g_assert( _is_valid );
862     cairo_matrix_t matrix;
863     _initCairoMatrix(&matrix, transform);
864     cairo_transform(_cr, &matrix);
866     // store new CTM
867     getTransform(&_state->transform);
870 void
871 CairoRenderContext::setTransform(Geom::Matrix const *transform)
873     g_assert( _is_valid );
875     cairo_matrix_t matrix;
876     _initCairoMatrix(&matrix, transform);
877     cairo_set_matrix(_cr, &matrix);
878     _state->transform = *transform;
881 void
882 CairoRenderContext::getTransform(Geom::Matrix *copy) const
884     g_assert( _is_valid );
886     cairo_matrix_t ctm;
887     cairo_get_matrix(_cr, &ctm);
888     (*copy)[0] = ctm.xx;
889     (*copy)[1] = ctm.yx;
890     (*copy)[2] = ctm.xy;
891     (*copy)[3] = ctm.yy;
892     (*copy)[4] = ctm.x0;
893     (*copy)[5] = ctm.y0;
896 void
897 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
899     g_assert( _is_valid );
901     CairoRenderState *parent_state = getParentState();
902     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
905 void
906 CairoRenderContext::pushState(void)
908     g_assert( _is_valid );
910     cairo_save(_cr);
912     CairoRenderState *new_state = _createState();
913     // copy current state's transform
914     new_state->transform = _state->transform;
915     _state_stack = g_slist_prepend(_state_stack, new_state);
916     _state = new_state;
919 void
920 CairoRenderContext::popState(void)
922     g_assert( _is_valid );
924     cairo_restore(_cr);
926     g_free(_state_stack->data);
927     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
928     _state = (CairoRenderState*)_state_stack->data;
930     g_assert( g_slist_length(_state_stack) > 0 );
933 static bool pattern_hasItemChildren (SPPattern *pat)
935     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
936         if (SP_IS_ITEM (child)) {
937             return true;
938         }
939     }
940     return false;
943 cairo_pattern_t*
944 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
946     g_assert( SP_IS_PATTERN(paintserver) );
948     SPPattern *pat = SP_PATTERN (paintserver);
950     Geom::Matrix ps2user, pcs2dev;
951     ps2user = Geom::identity();
952     pcs2dev = Geom::identity();
954     double x = pattern_x(pat);
955     double y = pattern_y(pat);
956     double width = pattern_width(pat);
957     double height = pattern_height(pat);
958     double bbox_width_scaler;
959     double bbox_height_scaler;
961     TRACE(("%f x %f pattern\n", width, height));
963     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
964         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
965         bbox_width_scaler = pbox->x1 - pbox->x0;
966         bbox_height_scaler = pbox->y1 - pbox->y0;
967         ps2user[4] = x * bbox_width_scaler + pbox->x0;
968         ps2user[5] = y * bbox_height_scaler + pbox->y0;
969     } else {
970         bbox_width_scaler = 1.0;
971         bbox_height_scaler = 1.0;
972         ps2user[4] = x;
973         ps2user[5] = y;
974     }
976     // apply pattern transformation
977     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
978     ps2user *= pattern_transform;
979     Geom::Point ori (ps2user[4], ps2user[5]);
981     // create pattern contents coordinate system
982     if (pat->viewBox_set) {
983         NRRect *view_box = pattern_viewBox(pat);
985         double x, y, w, h;
986         double view_width, view_height;
987         x = 0;
988         y = 0;
989         w = width * bbox_width_scaler;
990         h = height * bbox_height_scaler;
992         view_width = view_box->x1 - view_box->x0;
993         view_height = view_box->y1 - view_box->y0;
995         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
996         pcs2dev[0] = w / view_width;
997         pcs2dev[3] = h / view_height;
998         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
999         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1000     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1001         pcs2dev[0] = pbox->x1 - pbox->x0;
1002         pcs2dev[3] = pbox->y1 - pbox->y0;
1004     }
1006     // Calculate the size of the surface which has to be created 
1007 #define SUBPIX_SCALE 100
1008     // Cairo requires an integer pattern surface width/height.
1009     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1010     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1011     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1012     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1013     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1014     // create new rendering context
1015     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1017     // adjust the size of the painted pattern to fit exactly the created surface
1018     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1019     double scale_width = surface_width / (bbox_width_scaler * width);
1020     double scale_height = surface_height / (bbox_height_scaler * height);
1021     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1022         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1023         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1024         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1025     }
1027     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1028     ps2user[4] = ori[Geom::X];
1029     ps2user[5] = ori[Geom::Y];
1031     pattern_ctx->setTransform(&pcs2dev);
1032     pattern_ctx->pushState();
1034     // create arena and group
1035     NRArena *arena = NRArena::create();
1036     unsigned dkey = sp_item_display_key_new(1);
1038     // show items and render them
1039     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1040         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1041             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1042                 if (SP_IS_ITEM (child)) {
1043                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1044                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1045                 }
1046             }
1047             break; // do not go further up the chain if children are found
1048         }
1049     }
1051     pattern_ctx->popState();
1053     // setup a cairo_pattern_t
1054     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1055     TEST(pattern_ctx->saveAsPng("pattern.png"));
1056     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1057     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1059     // set pattern transformation
1060     cairo_matrix_t pattern_matrix;
1061     _initCairoMatrix(&pattern_matrix, &ps2user);
1062     cairo_matrix_invert(&pattern_matrix);
1063     cairo_pattern_set_matrix(result, &pattern_matrix);
1065     delete pattern_ctx;
1067     // hide all items
1068     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1069         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1070             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1071                 if (SP_IS_ITEM (child)) {
1072                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1073                 }
1074             }
1075             break; // do not go further up the chain if children are found
1076         }
1077     }
1079     return result;
1082 cairo_pattern_t*
1083 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1084                                                  NRRect const *pbox, float alpha)
1086     cairo_pattern_t *pattern = NULL;
1087     bool apply_bbox2user = FALSE;
1089     if (SP_IS_LINEARGRADIENT (paintserver)) {
1091             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1093             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1095             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1096             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1097             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1098                 // convert to userspace
1099                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1100                 p1 *= bbox2user;
1101                 p2 *= bbox2user;
1102             }
1104             // create linear gradient pattern
1105             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1107             // add stops
1108             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1109                 float rgb[3];
1110                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1111                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1112             }
1113     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1115         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1117         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1119         Geom::Point c (rg->cx.computed, rg->cy.computed);
1120         Geom::Point f (rg->fx.computed, rg->fy.computed);
1121         double r = rg->r.computed;
1122         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1123             apply_bbox2user = true;
1125         // create radial gradient pattern
1126         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1128         // add stops
1129         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1130             float rgb[3];
1131             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1132             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1133         }
1134     } else if (SP_IS_PATTERN (paintserver)) {
1136         pattern = _createPatternPainter(paintserver, pbox);
1137     } else {
1138         return NULL;
1139     }
1141     if (pattern && SP_IS_GRADIENT (paintserver)) {
1142         SPGradient *g = SP_GRADIENT(paintserver);
1144         // set extend type
1145         SPGradientSpread spread = sp_gradient_get_spread(g);
1146         switch (spread) {
1147             case SP_GRADIENT_SPREAD_REPEAT: {
1148                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1149                 break;
1150             }
1151             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1152                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1153                 break;
1154             }
1155             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1156                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1157                 break;
1158             }
1159             default: {
1160                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1161                 break;
1162             }
1163         }
1165         cairo_matrix_t pattern_matrix;
1166         if (g->gradientTransform_set) {
1167             // apply gradient transformation
1168             cairo_matrix_init(&pattern_matrix,
1169                 g->gradientTransform[0], g->gradientTransform[1],
1170                 g->gradientTransform[2], g->gradientTransform[3],
1171                 g->gradientTransform[4], g->gradientTransform[5]);
1172         } else {
1173             cairo_matrix_init_identity (&pattern_matrix);
1174         }
1176         if (apply_bbox2user) {
1177             // convert to userspace
1178             cairo_matrix_t bbox2user;
1179             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1180             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1181         }
1182         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1183         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1184     }
1186     return pattern;
1189 void
1190 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1192     g_return_if_fail( style->fill.isColor()
1193                       || style->fill.isPaintserver() );
1195     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1196     if (_state->merge_opacity) {
1197         alpha *= _state->opacity;
1198         TRACE(("merged op=%f\n", alpha));
1199     }
1201     if (style->fill.isColor()) {
1202         float rgb[3];
1203         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1205         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1206     } else {
1207         g_assert( style->fill.isPaintserver()
1208                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1209                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1211         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1213         if (pattern) {
1214             cairo_set_source(_cr, pattern);
1215             cairo_pattern_destroy(pattern);
1216         }
1217     }
1220 void
1221 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1223     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1224     if (_state->merge_opacity)
1225         alpha *= _state->opacity;
1227     if (style->stroke.isColor()) {
1228         float rgb[3];
1229         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1231         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1232     } else {
1233         g_assert( style->fill.isPaintserver()
1234                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1235                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1237         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1239         if (pattern) {
1240             cairo_set_source(_cr, pattern);
1241             cairo_pattern_destroy(pattern);
1242         }
1243     }
1245     if (style->stroke_dash.n_dash   &&
1246         style->stroke_dash.dash       )
1247     {
1248         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1249     } else {
1250         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1251     }
1253     cairo_set_line_width(_cr, style->stroke_width.computed);
1255     // set line join type
1256     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1257     switch (style->stroke_linejoin.computed) {
1258         case SP_STROKE_LINEJOIN_MITER:
1259             join = CAIRO_LINE_JOIN_MITER;
1260             break;
1261         case SP_STROKE_LINEJOIN_ROUND:
1262             join = CAIRO_LINE_JOIN_ROUND;
1263             break;
1264         case SP_STROKE_LINEJOIN_BEVEL:
1265             join = CAIRO_LINE_JOIN_BEVEL;
1266             break;
1267     }
1268     cairo_set_line_join(_cr, join);
1270     // set line cap type
1271     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1272     switch (style->stroke_linecap.computed) {
1273         case SP_STROKE_LINECAP_BUTT:
1274             cap = CAIRO_LINE_CAP_BUTT;
1275             break;
1276         case SP_STROKE_LINECAP_ROUND:
1277             cap = CAIRO_LINE_CAP_ROUND;
1278             break;
1279         case SP_STROKE_LINECAP_SQUARE:
1280             cap = CAIRO_LINE_CAP_SQUARE;
1281             break;
1282     }
1283     cairo_set_line_cap(_cr, cap);
1284     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1287 bool
1288 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1290     g_assert( _is_valid );
1292     if (_render_mode == RENDER_MODE_CLIP) {
1293         if (_clip_mode == CLIP_MODE_PATH) {
1294             addClipPath(pathv, &style->fill_rule);
1295         } else {
1296             setPathVector(pathv);
1297             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1298                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1299             } else {
1300                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1301             }
1302             cairo_fill(_cr);
1303             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1304         }
1305         return true;
1306     }
1308     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1309     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 || 
1310                     style->fill_opacity.value == 0;
1312     if (no_fill && no_stroke)
1313         return true;
1315     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1316                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1318     if (!need_layer)
1319         cairo_save(_cr);
1320     else
1321         pushLayer();
1323     if (!no_fill) {
1324         _setFillStyle(style, pbox);
1325         setPathVector(pathv);
1327         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1328             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1329         } else {
1330             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1331         }
1333         if (no_stroke)
1334             cairo_fill(_cr);
1335         else
1336             cairo_fill_preserve(_cr);
1337     }
1339     if (!no_stroke) {
1340         _setStrokeStyle(style, pbox);
1341         if (no_fill)
1342             setPathVector(pathv);
1344         cairo_stroke(_cr);
1345     }
1347     if (need_layer)
1348         popLayer();
1349     else
1350         cairo_restore(_cr);
1352     return true;
1355 bool
1356 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1357                                 Geom::Matrix const *image_transform, SPStyle const *style)
1359     g_assert( _is_valid );
1361     if (_render_mode == RENDER_MODE_CLIP)
1362         return true;
1364     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1365     if (!px_rgba)
1366         return false;
1368     float opacity;
1369     if (_state->merge_opacity)
1370         opacity = _state->opacity;
1371     else
1372         opacity = 1.0;
1374     // make a copy of the original pixbuf with premultiplied alpha
1375     // if we pass the original pixbuf it will get messed up
1376     for (unsigned i = 0; i < h; i++) {
1377         for (unsigned j = 0; j < w; j++) {
1378             guchar const *src = px + i * rs + j * 4;
1379             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1380             guchar r, g, b, alpha_dst;
1382             // calculate opacity-modified alpha
1383             alpha_dst = src[3];
1384             if (opacity != 1.0 && _vector_based_target)
1385                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1387             // premul alpha (needed because this will be undone by cairo-pdf)
1388             r = src[0]*alpha_dst/255;
1389             g = src[1]*alpha_dst/255;
1390             b = src[2]*alpha_dst/255;
1392             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1393         }
1394     }
1396     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1397     if (cairo_surface_status(image_surface)) {
1398         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1399         return false;
1400     }
1402     // setup automatic freeing of the image data when destroying the surface
1403     static cairo_user_data_key_t key;
1404     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1406     cairo_save(_cr);
1408     // scaling by width & height is not needed because it will be done by Cairo
1409     if (image_transform)
1410         transform(image_transform);
1412     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1414     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1415     if (_vector_based_target) {
1416         cairo_new_path(_cr);
1417         cairo_rectangle(_cr, 0, 0, w, h);
1418         cairo_clip(_cr);
1419     }
1421     if (_vector_based_target)
1422         cairo_paint(_cr);
1423     else
1424         cairo_paint_with_alpha(_cr, opacity);
1426     cairo_restore(_cr);
1428     cairo_surface_destroy(image_surface);
1430     return true;
1433 #define GLYPH_ARRAY_SIZE 64
1435 unsigned int
1436 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1438     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1439     cairo_glyph_t *glyphs = glyph_array;
1440     unsigned int num_glyphs = glyphtext.size();
1441     if (num_glyphs > GLYPH_ARRAY_SIZE)
1442         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1444     unsigned int num_invalid_glyphs = 0;
1445     unsigned int i = 0;
1446     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1447         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1448         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1449         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1450             TRACE(("INVALID GLYPH found\n"));
1451             num_invalid_glyphs++;
1452             continue;
1453         }
1454         glyphs[i - num_invalid_glyphs].index = it_info->index;
1455         glyphs[i - num_invalid_glyphs].x = it_info->x;
1456         glyphs[i - num_invalid_glyphs].y = it_info->y;
1457         i++;
1458     }
1460     if (is_stroke || _is_texttopath)
1461         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1462     else
1463         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1465     if (num_glyphs > GLYPH_ARRAY_SIZE)
1466         g_free(glyphs);
1468     return num_glyphs - num_invalid_glyphs;
1471 bool
1472 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1473                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1475     // create a cairo_font_face from PangoFont
1476     double size = style->font_size.computed;
1477     cairo_font_face_t *font_face = NULL;
1479     FcPattern *fc_pattern = NULL;
1480     
1481 #ifdef USE_PANGO_WIN32
1482 # ifdef CAIRO_HAS_WIN32_FONT
1483     LOGFONTA *lfa = pango_win32_font_logfont(font);
1484     LOGFONTW lfw;
1486     ZeroMemory(&lfw, sizeof(LOGFONTW));
1487     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1488     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1489     
1490     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1491 # endif
1492 #else
1493 # ifdef CAIRO_HAS_FT_FONT
1494     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1495     fc_pattern = fc_font->font_pattern;
1496     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1497 # endif
1498 #endif
1499     
1500     cairo_save(_cr);
1501     cairo_set_font_face(_cr, font_face);
1503     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1504         size = 12.0;
1506     // set the given font matrix
1507     cairo_matrix_t matrix;
1508     _initCairoMatrix(&matrix, font_matrix);
1509     cairo_set_font_matrix(_cr, &matrix);
1511     if (_render_mode == RENDER_MODE_CLIP) {
1512         if (_clip_mode == CLIP_MODE_MASK) {
1513             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1514                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1515             } else {
1516                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1517             }
1518             _showGlyphs(_cr, font, glyphtext, FALSE);
1519         } else {
1520             // just add the glyph paths to the current context
1521             _showGlyphs(_cr, font, glyphtext, TRUE);
1522         }
1523     } else {
1525         if (style->fill.isColor() || style->fill.isPaintserver()) {
1526             // set fill style
1527             _setFillStyle(style, NULL);
1529             _showGlyphs(_cr, font, glyphtext, FALSE);
1530         }
1532         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1533             // set stroke style
1534             _setStrokeStyle(style, NULL);
1536             // paint stroke
1537             _showGlyphs(_cr, font, glyphtext, TRUE);
1538             cairo_stroke(_cr);
1539         }
1540     }
1542     cairo_restore(_cr);
1544     if (font_face)
1545         cairo_font_face_destroy(font_face);
1547     return true;
1550 /* Helper functions */
1552 void
1553 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1555     cairo_new_path(_cr);
1556     addPathVector(pv);
1559 void
1560 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1562     feed_pathvector_to_cairo(_cr, pv);
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, Geom::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, Geom::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 :