Code

patch by Adib for 406470
[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->_width = width;
196     new_context->_height = height;
197     new_context->_is_valid = TRUE;
199     return new_context;
202 CairoRenderContext*
203 CairoRenderContext::cloneMe(void) const
205     g_assert( _is_valid );
207     return cloneMe(_width, _height);
210 bool
211 CairoRenderContext::setImageTarget(cairo_format_t format)
213     // format cannot be set on an already initialized surface
214     if (_is_valid)
215         return false;
217     switch (format) {
218         case CAIRO_FORMAT_ARGB32:
219         case CAIRO_FORMAT_RGB24:
220         case CAIRO_FORMAT_A8:
221         case CAIRO_FORMAT_A1:
222             _target_format = format;
223             _target = CAIRO_SURFACE_TYPE_IMAGE;
224             return true;
225             break;
226         default:
227             break;
228     }
230     return false;
233 bool
234 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
236 #ifndef CAIRO_HAS_PDF_SURFACE
237     return false;
238 #else
239     _target = CAIRO_SURFACE_TYPE_PDF;
240     _vector_based_target = TRUE;
241 #endif
243     FILE *osf = NULL;
244     FILE *osp = NULL;
246     gsize bytesRead = 0;
247     gsize bytesWritten = 0;
248     GError *error = NULL;
249     gchar *local_fn = g_filename_from_utf8(utf8_fn,
250                                            -1,  &bytesRead,  &bytesWritten, &error);
251     gchar const *fn = local_fn;
253     /* TODO: Replace the below fprintf's with something that does the right thing whether in
254     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
255     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
256     * return code.
257     */
258     if (fn != NULL) {
259         if (*fn == '|') {
260             fn += 1;
261             while (isspace(*fn)) fn += 1;
262 #ifndef WIN32
263             osp = popen(fn, "w");
264 #else
265             osp = _popen(fn, "w");
266 #endif
267             if (!osp) {
268                 fprintf(stderr, "inkscape: popen(%s): %s\n",
269                         fn, strerror(errno));
270                 return false;
271             }
272             _stream = osp;
273         } else if (*fn == '>') {
274             fn += 1;
275             while (isspace(*fn)) fn += 1;
276             Inkscape::IO::dump_fopen_call(fn, "K");
277             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
278             if (!osf) {
279                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
280                         fn, strerror(errno));
281                 return false;
282             }
283             _stream = osf;
284         } else {
285             /* put cwd stuff in here */
286             gchar *qn = ( *fn
287                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
288                 : g_strdup("lpr") );
289 #ifndef WIN32
290             osp = popen(qn, "w");
291 #else
292             osp = _popen(qn, "w");
293 #endif
294             if (!osp) {
295                 fprintf(stderr, "inkscape: popen(%s): %s\n",
296                         qn, strerror(errno));
297                 return false;
298             }
299             g_free(qn);
300             _stream = osp;
301         }
302     }
304     g_free(local_fn);
306     if (_stream) {
307         /* fixme: this is kinda icky */
308 #if !defined(_WIN32) && !defined(__WIN32__)
309         (void) signal(SIGPIPE, SIG_IGN);
310 #endif
311     }
313     return true;
316 bool
317 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
319 #ifndef CAIRO_HAS_PS_SURFACE
320     return false;
321 #else
322     _target = CAIRO_SURFACE_TYPE_PS;
323     _vector_based_target = TRUE;
324 #endif
326     FILE *osf = NULL;
327     FILE *osp = NULL;
329     gsize bytesRead = 0;
330     gsize bytesWritten = 0;
331     GError *error = NULL;
332     gchar *local_fn = g_filename_from_utf8(utf8_fn,
333                                            -1,  &bytesRead,  &bytesWritten, &error);
334     gchar const *fn = local_fn;
336     /* TODO: Replace the below fprintf's with something that does the right thing whether in
337     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
338     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
339     * return code.
340     */
341     if (fn != NULL) {
342         if (*fn == '|') {
343             fn += 1;
344             while (isspace(*fn)) fn += 1;
345 #ifndef WIN32
346             osp = popen(fn, "w");
347 #else
348             osp = _popen(fn, "w");
349 #endif
350             if (!osp) {
351                 fprintf(stderr, "inkscape: popen(%s): %s\n",
352                         fn, strerror(errno));
353                 return false;
354             }
355             _stream = osp;
356         } else if (*fn == '>') {
357             fn += 1;
358             while (isspace(*fn)) fn += 1;
359             Inkscape::IO::dump_fopen_call(fn, "K");
360             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
361             if (!osf) {
362                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
363                         fn, strerror(errno));
364                 return false;
365             }
366             _stream = osf;
367         } else {
368             /* put cwd stuff in here */
369             gchar *qn = ( *fn
370                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
371                 : g_strdup("lpr") );
372 #ifndef WIN32
373             osp = popen(qn, "w");
374 #else
375             osp = _popen(qn, "w");
376 #endif
377             if (!osp) {
378                 fprintf(stderr, "inkscape: popen(%s): %s\n",
379                         qn, strerror(errno));
380                 return false;
381             }
382             g_free(qn);
383             _stream = osp;
384         }
385     }
387     g_free(local_fn);
389     if (_stream) {
390         /* fixme: this is kinda icky */
391 #if !defined(_WIN32) && !defined(__WIN32__)
392         (void) signal(SIGPIPE, SIG_IGN);
393 #endif
394     }
396     return true;
399 void CairoRenderContext::setPSLevel(unsigned int level)
401     _ps_level = level;
404 void CairoRenderContext::setEPS(bool eps)
406     _eps = eps;
409 unsigned int CairoRenderContext::getPSLevel(void)
411     return _ps_level;
414 void CairoRenderContext::setPDFLevel(unsigned int level)
416     _pdf_level = level;
419 void CairoRenderContext::setTextToPath(bool texttopath)
421     _is_texttopath = texttopath;
424 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
426     _is_filtertobitmap = filtertobitmap;
429 bool CairoRenderContext::getFilterToBitmap(void)
431     return _is_filtertobitmap;
434 void CairoRenderContext::setBitmapResolution(int resolution)
436     _bitmapresolution = resolution;
439 int CairoRenderContext::getBitmapResolution(void)
441     return _bitmapresolution;
444 cairo_surface_t*
445 CairoRenderContext::getSurface(void)
447     g_assert( _is_valid );
449     return _surface;
452 bool
453 CairoRenderContext::saveAsPng(const char *file_name)
455     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
456     if (status)
457         return false;
458     else
459         return true;
462 void
463 CairoRenderContext::setRenderMode(CairoRenderMode mode)
465     switch (mode) {
466         case RENDER_MODE_NORMAL:
467         case RENDER_MODE_CLIP:
468             _render_mode = mode;
469             break;
470         default:
471             _render_mode = RENDER_MODE_NORMAL;
472             break;
473     }
476 CairoRenderContext::CairoRenderMode
477 CairoRenderContext::getRenderMode(void) const
479     return _render_mode;
482 void
483 CairoRenderContext::setClipMode(CairoClipMode mode)
485     switch (mode) {
486         case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
487         case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
488             _clip_mode = mode;
489             break;
490         default:
491             _clip_mode = CLIP_MODE_PATH;
492             break;
493     }
496 CairoRenderContext::CairoClipMode
497 CairoRenderContext::getClipMode(void) const
499     return _clip_mode;
502 CairoRenderState*
503 CairoRenderContext::_createState(void)
505     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
506     g_assert( state != NULL );
508     state->has_filtereffect = FALSE;
509     state->merge_opacity = TRUE;
510     state->opacity = 1.0;
511     state->need_layer = FALSE;
512     state->has_overflow = FALSE;
513     state->parent_has_userspace = FALSE;
514     state->clip_path = NULL;
515     state->mask = NULL;
517     return state;
520 void
521 CairoRenderContext::pushLayer(void)
523     g_assert( _is_valid );
525     TRACE(("--pushLayer\n"));
526     cairo_push_group(_cr);
528     // clear buffer
529     if (!_vector_based_target) {
530         cairo_save(_cr);
531         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
532         cairo_paint(_cr);
533         cairo_restore(_cr);
534     }
537 void
538 CairoRenderContext::popLayer(void)
540     g_assert( _is_valid );
542     float opacity = _state->opacity;
543     TRACE(("--popLayer w/ opacity %f\n", opacity));
545     /*
546      At this point, the Cairo source is ready. A Cairo mask must be created if required.
547      Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
548      masks independently of the objects they effect while in SVG the clip paths and masks
549      are defined relative to the objects they are attached to.
550      Notes:
551      1. An SVG object may have both a clip path and a mask!
552      2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
553      3. An SVG clipped or masked object may be first drawn off the page and then translated onto
554         the page (document). This is also not handled properly.
555      4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
556      5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
557         alpha. This is handled here by doing a pixel by pixel conversion.
558     */
560     SPClipPath *clip_path = _state->clip_path;
561     SPMask *mask = _state->mask;
562     if (clip_path || mask) {
564         CairoRenderContext *clip_ctx = 0;
565         cairo_surface_t *clip_mask = 0;
567         // Apply any clip path first
568         if (clip_path) {
569             TRACE(("  Applying clip\n"));
570             if (_render_mode == RENDER_MODE_CLIP)
571                 mask = NULL;    // disable mask when performing nested clipping
573             if (_vector_based_target) {
574                 setClipMode(CLIP_MODE_PATH); // Vector
575                 if (!mask) {
576                     cairo_pop_group_to_source(_cr);
577                     _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
578                     if (opacity == 1.0)
579                         cairo_paint(_cr);
580                     else
581                         cairo_paint_with_alpha(_cr, opacity);
583                 } else {
584                     // the clipPath will be applied before masking
585                 }
586             } else {
588                 // setup a new rendering context
589                 clip_ctx = _renderer->createContext();
590                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
591                 clip_ctx->setClipMode(CLIP_MODE_MASK);  // Raster
592                 // This code ties the clipping to the document coordinates. It doesn't allow
593                 // for a clipped object intially drawn off the page and then translated onto
594                 // the page.
595                 if (!clip_ctx->setupSurface(_width, _height)) {
596                     TRACE(("clip: setupSurface failed\n"));
597                     _renderer->destroyContext(clip_ctx);
598                     return;
599                 }
601                 // clear buffer
602                 cairo_save(clip_ctx->_cr);
603                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
604                 cairo_paint(clip_ctx->_cr);
605                 cairo_restore(clip_ctx->_cr);
607                 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
608                 if (!mask)
609                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
610                 else
611                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
613                 // copy over the correct CTM
614                 // It must be stored in item_transform of current state after pushState.
615                 Geom::Matrix item_transform; 
616                 if (_state->parent_has_userspace)
617                     item_transform = getParentState()->transform * _state->item_transform;
618                 else
619                     item_transform = _state->item_transform;
621                 // apply the clip path
622                 clip_ctx->pushState();
623                 clip_ctx->getCurrentState()->item_transform = item_transform;
624                 _renderer->applyClipPath(clip_ctx, clip_path);
625                 clip_ctx->popState();
627                 clip_mask = clip_ctx->getSurface();
628                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
630                 if (!mask) {
631                     cairo_pop_group_to_source(_cr);
632                     cairo_mask_surface(_cr, clip_mask, 0, 0);
633                     _renderer->destroyContext(clip_ctx);
634                 }
635             }
636         }
638         // Apply any mask second
639         if (mask) {
640             TRACE(("  Applying mask\n"));
641             // create rendering context for mask
642             CairoRenderContext *mask_ctx = _renderer->createContext();
644             // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
645             // Cairo surface is expecting the mask to be 90 dpi.
646             float surface_width = _width;
647             float surface_height = _height;
648             if( _vector_based_target ) {
649                 surface_width *= 1.25;
650                 surface_height *= 1.25;
651             }
652             mask_ctx->setupSurface( surface_width, surface_height );
653             TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
655             // set rendering mode to normal
656             setRenderMode(RENDER_MODE_NORMAL);
658             // copy the correct CTM to mask context
659             /*
660             if (_state->parent_has_userspace)
661                 mask_ctx->setTransform(&getParentState()->transform);
662             else
663                 mask_ctx->setTransform(&_state->transform);
664             */
665             // This is probably not correct... but it seems to do the trick.
666             mask_ctx->setTransform(&_state->item_transform);
668             // render mask contents to mask_ctx
669             _renderer->applyMask(mask_ctx, mask);
671             TEST(mask_ctx->saveAsPng("mask.png"));
673             // composite with clip mask
674             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
675                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
676                 _renderer->destroyContext(clip_ctx);
677             }
679             cairo_surface_t *mask_image = mask_ctx->getSurface();
680             int width = cairo_image_surface_get_width(mask_image);
681             int height = cairo_image_surface_get_height(mask_image);
682             int stride = cairo_image_surface_get_stride(mask_image);
683             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
685             // premultiply with opacity
686             // In SVG, the rgb channels as well as the alpha channel is used in masking.
687             // In Cairo, only the alpha channel is used thus requiring this conversion.
688             TRACE(("premul w/ %f\n", opacity));
689             guint8 int_opacity = (guint8)(255 * opacity);
690             for (int row = 0 ; row < height; row++) {
691                 unsigned char *row_data = pixels + (row * stride);
692                 for (int i = 0 ; i < width; i++) {
693                     guint32 *pixel = (guint32 *)row_data + i;
694                     *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
695                                ((*pixel & 0x0000ff00) >>  8) * 46518 +
696                                ((*pixel & 0x000000ff)      ) * 4688) *
697                               int_opacity);
698                 }
699             }
701             cairo_pop_group_to_source(_cr);
702             if (_clip_mode == CLIP_MODE_PATH) {
703                 // we have to do the clipping after cairo_pop_group_to_source
704                 _renderer->applyClipPath(this, clip_path);
705             }
706             // apply the mask onto the layer
707             cairo_mask_surface(_cr, mask_image, 0, 0);
708             _renderer->destroyContext(mask_ctx);
709         }
710     } else {
711         // No clip path or mask
712         cairo_pop_group_to_source(_cr);
713         if (opacity == 1.0)
714             cairo_paint(_cr);
715         else
716             cairo_paint_with_alpha(_cr, opacity);
717     }
720 void
721 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
723     g_assert( _is_valid );
725     // here it should be checked whether the current clip winding changed
726     // so we could switch back to masked clipping
727     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
728         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
729     } else {
730         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
731     }
732     addPathVector(pv);
735 void
736 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
738     g_assert( _is_valid );
740     cairo_rectangle(_cr, x, y, width, height);
741     cairo_clip(_cr);
744 bool
745 CairoRenderContext::setupSurface(double width, double height)
747     // Is the surface already set up?
748     if (_is_valid)
749         return true;
751     if (_vector_based_target && _stream == NULL)
752         return false;
754     _width = width;
755     _height = height;
757     cairo_surface_t *surface = NULL;
758     switch (_target) {
759         case CAIRO_SURFACE_TYPE_IMAGE:
760             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
761             break;
762 #ifdef CAIRO_HAS_PDF_SURFACE
763         case CAIRO_SURFACE_TYPE_PDF:
764             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
765             break;
766 #endif
767 #ifdef CAIRO_HAS_PS_SURFACE
768         case CAIRO_SURFACE_TYPE_PS:
769             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
770 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
771             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
772                 return FALSE;
773             }
774             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
775             cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
776 #endif
777             break;
778 #endif
779         default:
780             return false;
781             break;
782     }
784     return _finishSurfaceSetup (surface);
787 bool
788 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
790     if (_is_valid || !surface)
791         return false;
793     _vector_based_target = is_vector;
794     bool ret = _finishSurfaceSetup (surface, ctm);
795     if (ret)
796         cairo_surface_reference (surface);
797     return ret;
800 bool
801 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
803     if(surface == NULL) {
804         return false;
805     }
806     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
807         return false;
808     }
810     _cr = cairo_create(surface);
811     if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
812         return false;
813     }
814     if (ctm)
815         cairo_set_matrix(_cr, ctm);
816     _surface = surface;
818     if (_vector_based_target) {
819         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
820     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
821         // set background color on non-alpha surfaces
822         // TODO: bgcolor should be derived from SPDocument
823         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
824         cairo_rectangle(_cr, 0, 0, _width, _height);
825         cairo_fill(_cr);
826     }
828     _is_valid = TRUE;
830     return true;
833 bool
834 CairoRenderContext::finish(void)
836     g_assert( _is_valid );
838     if (_vector_based_target)
839         cairo_show_page(_cr);
841     cairo_destroy(_cr);
842     cairo_surface_finish(_surface);
843     cairo_status_t status = cairo_surface_status(_surface);
844     cairo_surface_destroy(_surface);
845     _cr = NULL;
846     _surface = NULL;
848     if (_layout)
849         g_object_unref(_layout);
851     _is_valid = FALSE;
853     if (_vector_based_target && _stream) {
854         /* Flush stream to be sure. */
855         (void) fflush(_stream);
857         fclose(_stream);
858         _stream = NULL;
859     }
861     if (status == CAIRO_STATUS_SUCCESS)
862         return true;
863     else
864         return false;
867 void
868 CairoRenderContext::transform(Geom::Matrix const *transform)
870     g_assert( _is_valid );
872     cairo_matrix_t matrix;
873     _initCairoMatrix(&matrix, transform);
874     cairo_transform(_cr, &matrix);
876     // store new CTM
877     getTransform(&_state->transform);
880 void
881 CairoRenderContext::setTransform(Geom::Matrix const *transform)
883     g_assert( _is_valid );
885     cairo_matrix_t matrix;
886     _initCairoMatrix(&matrix, transform);
887     cairo_set_matrix(_cr, &matrix);
888     _state->transform = *transform;
891 void
892 CairoRenderContext::getTransform(Geom::Matrix *copy) const
894     g_assert( _is_valid );
896     cairo_matrix_t ctm;
897     cairo_get_matrix(_cr, &ctm);
898     (*copy)[0] = ctm.xx;
899     (*copy)[1] = ctm.yx;
900     (*copy)[2] = ctm.xy;
901     (*copy)[3] = ctm.yy;
902     (*copy)[4] = ctm.x0;
903     (*copy)[5] = ctm.y0;
906 void
907 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
909     g_assert( _is_valid );
911     CairoRenderState *parent_state = getParentState();
912     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
915 void
916 CairoRenderContext::pushState(void)
918     g_assert( _is_valid );
920     cairo_save(_cr);
922     CairoRenderState *new_state = _createState();
923     // copy current state's transform
924     new_state->transform = _state->transform;
925     _state_stack = g_slist_prepend(_state_stack, new_state);
926     _state = new_state;
929 void
930 CairoRenderContext::popState(void)
932     g_assert( _is_valid );
934     cairo_restore(_cr);
936     g_free(_state_stack->data);
937     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
938     _state = (CairoRenderState*)_state_stack->data;
940     g_assert( g_slist_length(_state_stack) > 0 );
943 static bool pattern_hasItemChildren (SPPattern *pat)
945     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
946         if (SP_IS_ITEM (child)) {
947             return true;
948         }
949     }
950     return false;
953 cairo_pattern_t*
954 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
956     g_assert( SP_IS_PATTERN(paintserver) );
958     SPPattern *pat = SP_PATTERN (paintserver);
960     Geom::Matrix ps2user, pcs2dev;
961     ps2user = Geom::identity();
962     pcs2dev = Geom::identity();
964     double x = pattern_x(pat);
965     double y = pattern_y(pat);
966     double width = pattern_width(pat);
967     double height = pattern_height(pat);
968     double bbox_width_scaler;
969     double bbox_height_scaler;
971     TRACE(("%f x %f pattern\n", width, height));
973     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
974         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
975         bbox_width_scaler = pbox->x1 - pbox->x0;
976         bbox_height_scaler = pbox->y1 - pbox->y0;
977         ps2user[4] = x * bbox_width_scaler + pbox->x0;
978         ps2user[5] = y * bbox_height_scaler + pbox->y0;
979     } else {
980         bbox_width_scaler = 1.0;
981         bbox_height_scaler = 1.0;
982         ps2user[4] = x;
983         ps2user[5] = y;
984     }
986     // apply pattern transformation
987     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
988     ps2user *= pattern_transform;
989     Geom::Point ori (ps2user[4], ps2user[5]);
991     // create pattern contents coordinate system
992     if (pat->viewBox_set) {
993         NRRect *view_box = pattern_viewBox(pat);
995         double x, y, w, h;
996         double view_width, view_height;
997         x = 0;
998         y = 0;
999         w = width * bbox_width_scaler;
1000         h = height * bbox_height_scaler;
1002         view_width = view_box->x1 - view_box->x0;
1003         view_height = view_box->y1 - view_box->y0;
1005         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1006         pcs2dev[0] = w / view_width;
1007         pcs2dev[3] = h / view_height;
1008         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1009         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1010     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1011         pcs2dev[0] = pbox->x1 - pbox->x0;
1012         pcs2dev[3] = pbox->y1 - pbox->y0;
1014     }
1016     // Calculate the size of the surface which has to be created 
1017 #define SUBPIX_SCALE 100
1018     // Cairo requires an integer pattern surface width/height.
1019     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1020     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1021     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1022     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1023     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1024     // create new rendering context
1025     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1027     // adjust the size of the painted pattern to fit exactly the created surface
1028     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1029     double scale_width = surface_width / (bbox_width_scaler * width);
1030     double scale_height = surface_height / (bbox_height_scaler * height);
1031     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1032         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1033         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1034         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1035     }
1037     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1038     ps2user[4] = ori[Geom::X];
1039     ps2user[5] = ori[Geom::Y];
1041     pattern_ctx->setTransform(&pcs2dev);
1042     pattern_ctx->pushState();
1044     // create arena and group
1045     NRArena *arena = NRArena::create();
1046     unsigned dkey = sp_item_display_key_new(1);
1048     // show items and render them
1049     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1050         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1051             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1052                 if (SP_IS_ITEM (child)) {
1053                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1054                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1055                 }
1056             }
1057             break; // do not go further up the chain if children are found
1058         }
1059     }
1061     pattern_ctx->popState();
1063     // setup a cairo_pattern_t
1064     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1065     TEST(pattern_ctx->saveAsPng("pattern.png"));
1066     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1067     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1069     // set pattern transformation
1070     cairo_matrix_t pattern_matrix;
1071     _initCairoMatrix(&pattern_matrix, &ps2user);
1072     cairo_matrix_invert(&pattern_matrix);
1073     cairo_pattern_set_matrix(result, &pattern_matrix);
1075     delete pattern_ctx;
1077     // hide all items
1078     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1079         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1080             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1081                 if (SP_IS_ITEM (child)) {
1082                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1083                 }
1084             }
1085             break; // do not go further up the chain if children are found
1086         }
1087     }
1089     return result;
1092 cairo_pattern_t*
1093 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1094                                                  NRRect const *pbox, float alpha)
1096     cairo_pattern_t *pattern = NULL;
1097     bool apply_bbox2user = FALSE;
1099     if (SP_IS_LINEARGRADIENT (paintserver)) {
1101             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1103             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1105             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1106             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1107             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1108                 // convert to userspace
1109                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1110                 p1 *= bbox2user;
1111                 p2 *= bbox2user;
1112             }
1114             // create linear gradient pattern
1115             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1117             // add stops
1118             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1119                 float rgb[3];
1120                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1121                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1122             }
1123     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1125         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1127         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1129         Geom::Point c (rg->cx.computed, rg->cy.computed);
1130         Geom::Point f (rg->fx.computed, rg->fy.computed);
1131         double r = rg->r.computed;
1132         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1133             apply_bbox2user = true;
1135         // create radial gradient pattern
1136         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1138         // add stops
1139         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1140             float rgb[3];
1141             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1142             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1143         }
1144     } else if (SP_IS_PATTERN (paintserver)) {
1146         pattern = _createPatternPainter(paintserver, pbox);
1147     } else {
1148         return NULL;
1149     }
1151     if (pattern && SP_IS_GRADIENT (paintserver)) {
1152         SPGradient *g = SP_GRADIENT(paintserver);
1154         // set extend type
1155         SPGradientSpread spread = sp_gradient_get_spread(g);
1156         switch (spread) {
1157             case SP_GRADIENT_SPREAD_REPEAT: {
1158                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1159                 break;
1160             }
1161             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1162                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1163                 break;
1164             }
1165             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1166                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1167                 break;
1168             }
1169             default: {
1170                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1171                 break;
1172             }
1173         }
1175         cairo_matrix_t pattern_matrix;
1176         if (g->gradientTransform_set) {
1177             // apply gradient transformation
1178             cairo_matrix_init(&pattern_matrix,
1179                 g->gradientTransform[0], g->gradientTransform[1],
1180                 g->gradientTransform[2], g->gradientTransform[3],
1181                 g->gradientTransform[4], g->gradientTransform[5]);
1182         } else {
1183             cairo_matrix_init_identity (&pattern_matrix);
1184         }
1186         if (apply_bbox2user) {
1187             // convert to userspace
1188             cairo_matrix_t bbox2user;
1189             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1190             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1191         }
1192         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1193         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1194     }
1196     return pattern;
1199 void
1200 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1202     g_return_if_fail( !style->fill.set
1203                       || style->fill.isColor()
1204                       || style->fill.isPaintserver() );
1206     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1207     if (_state->merge_opacity) {
1208         alpha *= _state->opacity;
1209         TRACE(("merged op=%f\n", alpha));
1210     }
1212     if (style->fill.isColor()) {
1213         float rgb[3];
1214         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1216         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1218     } else if (!style->fill.set) { // unset fill is black
1219         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1221     } else {
1222         g_assert( style->fill.isPaintserver()
1223                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1224                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1226         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1228         if (pattern) {
1229             cairo_set_source(_cr, pattern);
1230             cairo_pattern_destroy(pattern);
1231         }
1232     }
1235 void
1236 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1238     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1239     if (_state->merge_opacity)
1240         alpha *= _state->opacity;
1242     if (style->stroke.isColor()) {
1243         float rgb[3];
1244         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1246         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1247     } else {
1248         g_assert( style->fill.isPaintserver()
1249                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1250                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1252         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1254         if (pattern) {
1255             cairo_set_source(_cr, pattern);
1256             cairo_pattern_destroy(pattern);
1257         }
1258     }
1260     if (style->stroke_dash.n_dash   &&
1261         style->stroke_dash.dash       )
1262     {
1263         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1264     } else {
1265         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1266     }
1268     cairo_set_line_width(_cr, style->stroke_width.computed);
1270     // set line join type
1271     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1272     switch (style->stroke_linejoin.computed) {
1273         case SP_STROKE_LINEJOIN_MITER:
1274             join = CAIRO_LINE_JOIN_MITER;
1275             break;
1276         case SP_STROKE_LINEJOIN_ROUND:
1277             join = CAIRO_LINE_JOIN_ROUND;
1278             break;
1279         case SP_STROKE_LINEJOIN_BEVEL:
1280             join = CAIRO_LINE_JOIN_BEVEL;
1281             break;
1282     }
1283     cairo_set_line_join(_cr, join);
1285     // set line cap type
1286     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1287     switch (style->stroke_linecap.computed) {
1288         case SP_STROKE_LINECAP_BUTT:
1289             cap = CAIRO_LINE_CAP_BUTT;
1290             break;
1291         case SP_STROKE_LINECAP_ROUND:
1292             cap = CAIRO_LINE_CAP_ROUND;
1293             break;
1294         case SP_STROKE_LINECAP_SQUARE:
1295             cap = CAIRO_LINE_CAP_SQUARE;
1296             break;
1297     }
1298     cairo_set_line_cap(_cr, cap);
1299     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1302 bool
1303 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1305     g_assert( _is_valid );
1307     if (_render_mode == RENDER_MODE_CLIP) {
1308         if (_clip_mode == CLIP_MODE_PATH) {
1309             addClipPath(pathv, &style->fill_rule);
1310         } else {
1311             setPathVector(pathv);
1312             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1313                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1314             } else {
1315                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1316             }
1317             cairo_fill(_cr);
1318             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1319         }
1320         return true;
1321     }
1323     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1324     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 || 
1325                     style->stroke_opacity.value == 0;
1327     if (no_fill && no_stroke)
1328         return true;
1330     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1331                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1333     if (!need_layer)
1334         cairo_save(_cr);
1335     else
1336         pushLayer();
1338     if (!no_fill) {
1339         _setFillStyle(style, pbox);
1340         setPathVector(pathv);
1342         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1343             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1344         } else {
1345             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1346         }
1348         if (no_stroke)
1349             cairo_fill(_cr);
1350         else
1351             cairo_fill_preserve(_cr);
1352     }
1354     if (!no_stroke) {
1355         _setStrokeStyle(style, pbox);
1356         if (no_fill)
1357             setPathVector(pathv);
1359         cairo_stroke(_cr);
1360     }
1362     if (need_layer)
1363         popLayer();
1364     else
1365         cairo_restore(_cr);
1367     return true;
1370 bool
1371 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1372                                 Geom::Matrix const *image_transform, SPStyle const *style)
1374     g_assert( _is_valid );
1376     if (_render_mode == RENDER_MODE_CLIP)
1377         return true;
1379     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1380     if (!px_rgba)
1381         return false;
1383     float opacity;
1384     if (_state->merge_opacity)
1385         opacity = _state->opacity;
1386     else
1387         opacity = 1.0;
1389     // make a copy of the original pixbuf with premultiplied alpha
1390     // if we pass the original pixbuf it will get messed up
1391     for (unsigned i = 0; i < h; i++) {
1392         for (unsigned j = 0; j < w; j++) {
1393             guchar const *src = px + i * rs + j * 4;
1394             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1395             guchar r, g, b, alpha_dst;
1397             // calculate opacity-modified alpha
1398             alpha_dst = src[3];
1399             if (opacity != 1.0 && _vector_based_target)
1400                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1402             // premul alpha (needed because this will be undone by cairo-pdf)
1403             r = src[0]*alpha_dst/255;
1404             g = src[1]*alpha_dst/255;
1405             b = src[2]*alpha_dst/255;
1407             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1408         }
1409     }
1411     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1412     if (cairo_surface_status(image_surface)) {
1413         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1414         return false;
1415     }
1417     // setup automatic freeing of the image data when destroying the surface
1418     static cairo_user_data_key_t key;
1419     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1421     cairo_save(_cr);
1423     // scaling by width & height is not needed because it will be done by Cairo
1424     if (image_transform)
1425         transform(image_transform);
1427     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1429     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1430     if (_vector_based_target) {
1431         cairo_new_path(_cr);
1432         cairo_rectangle(_cr, 0, 0, w, h);
1433         cairo_clip(_cr);
1434     }
1436     if (_vector_based_target)
1437         cairo_paint(_cr);
1438     else
1439         cairo_paint_with_alpha(_cr, opacity);
1441     cairo_restore(_cr);
1443     cairo_surface_destroy(image_surface);
1445     return true;
1448 #define GLYPH_ARRAY_SIZE 64
1450 unsigned int
1451 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1453     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1454     cairo_glyph_t *glyphs = glyph_array;
1455     unsigned int num_glyphs = glyphtext.size();
1456     if (num_glyphs > GLYPH_ARRAY_SIZE)
1457         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1459     unsigned int num_invalid_glyphs = 0;
1460     unsigned int i = 0;
1461     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1462         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1463         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1464         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1465             TRACE(("INVALID GLYPH found\n"));
1466             num_invalid_glyphs++;
1467             continue;
1468         }
1469         glyphs[i - num_invalid_glyphs].index = it_info->index;
1470         glyphs[i - num_invalid_glyphs].x = it_info->x;
1471         glyphs[i - num_invalid_glyphs].y = it_info->y;
1472         i++;
1473     }
1475     if (is_stroke) {
1476         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1477     } else {
1478         if (_is_texttopath) {
1479             cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1480             cairo_fill_preserve(cr);
1481         } else {
1482             cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1483         }
1484     }
1486     if (num_glyphs > GLYPH_ARRAY_SIZE)
1487         g_free(glyphs);
1489     return num_glyphs - num_invalid_glyphs;
1492 bool
1493 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1494                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1496     // create a cairo_font_face from PangoFont
1497     double size = style->font_size.computed;
1498     cairo_font_face_t *font_face = NULL;
1500     FcPattern *fc_pattern = NULL;
1501     
1502 #ifdef USE_PANGO_WIN32
1503 # ifdef CAIRO_HAS_WIN32_FONT
1504     LOGFONTA *lfa = pango_win32_font_logfont(font);
1505     LOGFONTW lfw;
1507     ZeroMemory(&lfw, sizeof(LOGFONTW));
1508     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1509     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1510     
1511     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1512 # endif
1513 #else
1514 # ifdef CAIRO_HAS_FT_FONT
1515     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1516     fc_pattern = fc_font->font_pattern;
1517     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1518 # endif
1519 #endif
1520     
1521     cairo_save(_cr);
1522     cairo_set_font_face(_cr, font_face);
1524     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1525         size = 12.0;
1527     // set the given font matrix
1528     cairo_matrix_t matrix;
1529     _initCairoMatrix(&matrix, font_matrix);
1530     cairo_set_font_matrix(_cr, &matrix);
1532     if (_render_mode == RENDER_MODE_CLIP) {
1533         if (_clip_mode == CLIP_MODE_MASK) {
1534             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1535                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1536             } else {
1537                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1538             }
1539             _showGlyphs(_cr, font, glyphtext, FALSE);
1540         } else {
1541             // just add the glyph paths to the current context
1542             _showGlyphs(_cr, font, glyphtext, TRUE);
1543         }
1544     } else {
1546         if (style->fill.isColor() || style->fill.isPaintserver()) {
1547             // set fill style
1548             _setFillStyle(style, NULL);
1550             _showGlyphs(_cr, font, glyphtext, FALSE);
1551         }
1553         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1554             // set stroke style
1555             _setStrokeStyle(style, NULL);
1557             // paint stroke
1558             _showGlyphs(_cr, font, glyphtext, TRUE);
1559             cairo_stroke(_cr);
1560         }
1561     }
1563     cairo_restore(_cr);
1565     if (font_face)
1566         cairo_font_face_destroy(font_face);
1568     return true;
1571 /* Helper functions */
1573 void
1574 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1576     cairo_new_path(_cr);
1577     addPathVector(pv);
1580 void
1581 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1583     feed_pathvector_to_cairo(_cr, pv);
1586 void
1587 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1589     cairo_matrix_t matrix;
1591     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1592     cairo_transform(cr, &matrix);
1595 void
1596 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1598     matrix->xx = (*transform)[0];
1599     matrix->yx = (*transform)[1];
1600     matrix->xy = (*transform)[2];
1601     matrix->yy = (*transform)[3];
1602     matrix->x0 = (*transform)[4];
1603     matrix->y0 = (*transform)[5];
1606 void
1607 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1609     _concatTransform(cr, (*transform)[0], (*transform)[1],
1610                      (*transform)[2], (*transform)[3],
1611                      (*transform)[4], (*transform)[5]);
1614 static cairo_status_t
1615 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1617     size_t written;
1618     FILE *file = (FILE*)closure;
1620     written = fwrite (data, 1, length, file);
1622     if (written == length)
1623         return CAIRO_STATUS_SUCCESS;
1624     else
1625         return CAIRO_STATUS_WRITE_ERROR;
1628 #include "clear-n_.h"
1630 }  /* namespace Internal */
1631 }  /* namespace Extension */
1632 }  /* namespace Inkscape */
1634 #undef TRACE
1635 #undef TEST
1637 /* End of GNU GPL code */
1640 /*
1641   Local Variables:
1642   mode:c++
1643   c-file-style:"stroustrup"
1644   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1645   indent-tabs-mode:nil
1646   fill-column:99
1647   End:
1648 */
1649 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :