Code

Merging from trunk
[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     if (style->fill.isNone() && style->stroke.isNone())
1309         return true;
1311     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1312                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1314     if (!need_layer)
1315         cairo_save(_cr);
1316     else
1317         pushLayer();
1319     if (!style->fill.isNone()) {
1320         _setFillStyle(style, pbox);
1321         setPathVector(pathv);
1323         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1324             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1325         } else {
1326             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1327         }
1329         if (style->stroke.isNone())
1330             cairo_fill(_cr);
1331         else
1332             cairo_fill_preserve(_cr);
1333     }
1335     if (!style->stroke.isNone()) {
1336         _setStrokeStyle(style, pbox);
1337         if (style->fill.isNone())
1338             setPathVector(pathv);
1340         cairo_stroke(_cr);
1341     }
1343     if (need_layer)
1344         popLayer();
1345     else
1346         cairo_restore(_cr);
1348     return true;
1351 bool
1352 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1353                                 Geom::Matrix const *image_transform, SPStyle const *style)
1355     g_assert( _is_valid );
1357     if (_render_mode == RENDER_MODE_CLIP)
1358         return true;
1360     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1361     if (!px_rgba)
1362         return false;
1364     float opacity;
1365     if (_state->merge_opacity)
1366         opacity = _state->opacity;
1367     else
1368         opacity = 1.0;
1370     // make a copy of the original pixbuf with premultiplied alpha
1371     // if we pass the original pixbuf it will get messed up
1372     for (unsigned i = 0; i < h; i++) {
1373         for (unsigned j = 0; j < w; j++) {
1374             guchar const *src = px + i * rs + j * 4;
1375             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1376             guchar r, g, b, alpha_dst;
1378             // calculate opacity-modified alpha
1379             alpha_dst = src[3];
1380             if (opacity != 1.0 && _vector_based_target)
1381                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1383             // premul alpha (needed because this will be undone by cairo-pdf)
1384             r = src[0]*alpha_dst/255;
1385             g = src[1]*alpha_dst/255;
1386             b = src[2]*alpha_dst/255;
1388             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1389         }
1390     }
1392     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1393     if (cairo_surface_status(image_surface)) {
1394         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1395         return false;
1396     }
1398     // setup automatic freeing of the image data when destroying the surface
1399     static cairo_user_data_key_t key;
1400     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1402     cairo_save(_cr);
1404     // scaling by width & height is not needed because it will be done by Cairo
1405     if (image_transform)
1406         transform(image_transform);
1408     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1410     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1411     if (_vector_based_target) {
1412         cairo_new_path(_cr);
1413         cairo_rectangle(_cr, 0, 0, w, h);
1414         cairo_clip(_cr);
1415     }
1417     if (_vector_based_target)
1418         cairo_paint(_cr);
1419     else
1420         cairo_paint_with_alpha(_cr, opacity);
1422     cairo_restore(_cr);
1424     cairo_surface_destroy(image_surface);
1426     return true;
1429 #define GLYPH_ARRAY_SIZE 64
1431 unsigned int
1432 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1434     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1435     cairo_glyph_t *glyphs = glyph_array;
1436     unsigned int num_glyphs = glyphtext.size();
1437     if (num_glyphs > GLYPH_ARRAY_SIZE)
1438         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1440     unsigned int num_invalid_glyphs = 0;
1441     unsigned int i = 0;
1442     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1443         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1444         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1445         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1446             TRACE(("INVALID GLYPH found\n"));
1447             num_invalid_glyphs++;
1448             continue;
1449         }
1450         glyphs[i - num_invalid_glyphs].index = it_info->index;
1451         glyphs[i - num_invalid_glyphs].x = it_info->x;
1452         glyphs[i - num_invalid_glyphs].y = it_info->y;
1453         i++;
1454     }
1456     if (is_stroke || _is_texttopath)
1457         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1458     else
1459         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1461     if (num_glyphs > GLYPH_ARRAY_SIZE)
1462         g_free(glyphs);
1464     return num_glyphs - num_invalid_glyphs;
1467 bool
1468 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1469                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1471     // create a cairo_font_face from PangoFont
1472     double size = style->font_size.computed;
1473     cairo_font_face_t *font_face = NULL;
1475     FcPattern *fc_pattern = NULL;
1476     
1477 #ifdef USE_PANGO_WIN32
1478 # ifdef CAIRO_HAS_WIN32_FONT
1479     LOGFONTA *lfa = pango_win32_font_logfont(font);
1480     LOGFONTW lfw;
1482     ZeroMemory(&lfw, sizeof(LOGFONTW));
1483     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1484     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1485     
1486     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1487 # endif
1488 #else
1489 # ifdef CAIRO_HAS_FT_FONT
1490     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1491     fc_pattern = fc_font->font_pattern;
1492     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1493 # endif
1494 #endif
1495     
1496     cairo_save(_cr);
1497     cairo_set_font_face(_cr, font_face);
1499     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1500         size = 12.0;
1502     // set the given font matrix
1503     cairo_matrix_t matrix;
1504     _initCairoMatrix(&matrix, font_matrix);
1505     cairo_set_font_matrix(_cr, &matrix);
1507     if (_render_mode == RENDER_MODE_CLIP) {
1508         if (_clip_mode == CLIP_MODE_MASK) {
1509             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1510                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1511             } else {
1512                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1513             }
1514             _showGlyphs(_cr, font, glyphtext, FALSE);
1515         } else {
1516             // just add the glyph paths to the current context
1517             _showGlyphs(_cr, font, glyphtext, TRUE);
1518         }
1519     } else {
1521         if (style->fill.isColor() || style->fill.isPaintserver()) {
1522             // set fill style
1523             _setFillStyle(style, NULL);
1525             _showGlyphs(_cr, font, glyphtext, FALSE);
1526         }
1528         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1529             // set stroke style
1530             _setStrokeStyle(style, NULL);
1532             // paint stroke
1533             _showGlyphs(_cr, font, glyphtext, TRUE);
1534             cairo_stroke(_cr);
1535         }
1536     }
1538     cairo_restore(_cr);
1540     if (font_face)
1541         cairo_font_face_destroy(font_face);
1543     return true;
1546 /* Helper functions */
1548 void
1549 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1551     cairo_new_path(_cr);
1552     addPathVector(pv);
1555 void
1556 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1558     feed_pathvector_to_cairo(_cr, pv);
1561 void
1562 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1564     cairo_matrix_t matrix;
1566     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1567     cairo_transform(cr, &matrix);
1570 void
1571 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1573     matrix->xx = (*transform)[0];
1574     matrix->yx = (*transform)[1];
1575     matrix->xy = (*transform)[2];
1576     matrix->yy = (*transform)[3];
1577     matrix->x0 = (*transform)[4];
1578     matrix->y0 = (*transform)[5];
1581 void
1582 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1584     _concatTransform(cr, (*transform)[0], (*transform)[1],
1585                      (*transform)[2], (*transform)[3],
1586                      (*transform)[4], (*transform)[5]);
1589 static cairo_status_t
1590 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1592     size_t written;
1593     FILE *file = (FILE*)closure;
1595     written = fwrite (data, 1, length, file);
1597     if (written == length)
1598         return CAIRO_STATUS_SUCCESS;
1599     else
1600         return CAIRO_STATUS_WRITE_ERROR;
1603 #include "clear-n_.h"
1605 }  /* namespace Internal */
1606 }  /* namespace Extension */
1607 }  /* namespace Inkscape */
1609 #undef TRACE
1610 #undef TEST
1612 /* End of GNU GPL code */
1615 /*
1616   Local Variables:
1617   mode:c++
1618   c-file-style:"stroustrup"
1619   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1620   indent-tabs-mode:nil
1621   fill-column:99
1622   End:
1623 */
1624 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :