Code

fix obvious typo, patch 340311
[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, cairo_matrix_t *ctm)
785     if (_is_valid || !surface)
786         return false;
788     _vector_based_target = is_vector;
789     bool ret = _finishSurfaceSetup (surface, ctm);
790     if (ret)
791         cairo_surface_reference (surface);
792     return ret;
795 bool
796 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
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     if (ctm)
807         cairo_set_matrix(_cr, ctm);
808     _surface = surface;
810     if (_vector_based_target) {
811         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
812     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
813         // set background color on non-alpha surfaces
814         // TODO: bgcolor should be derived from SPDocument
815         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
816         cairo_rectangle(_cr, 0, 0, _width, _height);
817         cairo_fill(_cr);
818     }
820     _is_valid = TRUE;
822     return true;
825 bool
826 CairoRenderContext::finish(void)
828     g_assert( _is_valid );
830     if (_vector_based_target)
831         cairo_show_page(_cr);
833     cairo_destroy(_cr);
834     cairo_surface_finish(_surface);
835     cairo_status_t status = cairo_surface_status(_surface);
836     cairo_surface_destroy(_surface);
837     _cr = NULL;
838     _surface = NULL;
840     if (_layout)
841         g_object_unref(_layout);
843     _is_valid = FALSE;
845     if (_vector_based_target && _stream) {
846         /* Flush stream to be sure. */
847         (void) fflush(_stream);
849         fclose(_stream);
850         _stream = NULL;
851     }
853     if (status == CAIRO_STATUS_SUCCESS)
854         return true;
855     else
856         return false;
859 void
860 CairoRenderContext::transform(Geom::Matrix const *transform)
862     g_assert( _is_valid );
864     cairo_matrix_t matrix;
865     _initCairoMatrix(&matrix, transform);
866     cairo_transform(_cr, &matrix);
868     // store new CTM
869     getTransform(&_state->transform);
872 void
873 CairoRenderContext::setTransform(Geom::Matrix const *transform)
875     g_assert( _is_valid );
877     cairo_matrix_t matrix;
878     _initCairoMatrix(&matrix, transform);
879     cairo_set_matrix(_cr, &matrix);
880     _state->transform = *transform;
883 void
884 CairoRenderContext::getTransform(Geom::Matrix *copy) const
886     g_assert( _is_valid );
888     cairo_matrix_t ctm;
889     cairo_get_matrix(_cr, &ctm);
890     (*copy)[0] = ctm.xx;
891     (*copy)[1] = ctm.yx;
892     (*copy)[2] = ctm.xy;
893     (*copy)[3] = ctm.yy;
894     (*copy)[4] = ctm.x0;
895     (*copy)[5] = ctm.y0;
898 void
899 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
901     g_assert( _is_valid );
903     CairoRenderState *parent_state = getParentState();
904     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
907 void
908 CairoRenderContext::pushState(void)
910     g_assert( _is_valid );
912     cairo_save(_cr);
914     CairoRenderState *new_state = _createState();
915     // copy current state's transform
916     new_state->transform = _state->transform;
917     _state_stack = g_slist_prepend(_state_stack, new_state);
918     _state = new_state;
921 void
922 CairoRenderContext::popState(void)
924     g_assert( _is_valid );
926     cairo_restore(_cr);
928     g_free(_state_stack->data);
929     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
930     _state = (CairoRenderState*)_state_stack->data;
932     g_assert( g_slist_length(_state_stack) > 0 );
935 static bool pattern_hasItemChildren (SPPattern *pat)
937     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
938         if (SP_IS_ITEM (child)) {
939             return true;
940         }
941     }
942     return false;
945 cairo_pattern_t*
946 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
948     g_assert( SP_IS_PATTERN(paintserver) );
950     SPPattern *pat = SP_PATTERN (paintserver);
952     Geom::Matrix ps2user, pcs2dev;
953     ps2user = Geom::identity();
954     pcs2dev = Geom::identity();
956     double x = pattern_x(pat);
957     double y = pattern_y(pat);
958     double width = pattern_width(pat);
959     double height = pattern_height(pat);
960     double bbox_width_scaler;
961     double bbox_height_scaler;
963     TRACE(("%f x %f pattern\n", width, height));
965     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
966         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
967         bbox_width_scaler = pbox->x1 - pbox->x0;
968         bbox_height_scaler = pbox->y1 - pbox->y0;
969         ps2user[4] = x * bbox_width_scaler + pbox->x0;
970         ps2user[5] = y * bbox_height_scaler + pbox->y0;
971     } else {
972         bbox_width_scaler = 1.0;
973         bbox_height_scaler = 1.0;
974         ps2user[4] = x;
975         ps2user[5] = y;
976     }
978     // apply pattern transformation
979     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
980     ps2user *= pattern_transform;
981     Geom::Point ori (ps2user[4], ps2user[5]);
983     // create pattern contents coordinate system
984     if (pat->viewBox_set) {
985         NRRect *view_box = pattern_viewBox(pat);
987         double x, y, w, h;
988         double view_width, view_height;
989         x = 0;
990         y = 0;
991         w = width * bbox_width_scaler;
992         h = height * bbox_height_scaler;
994         view_width = view_box->x1 - view_box->x0;
995         view_height = view_box->y1 - view_box->y0;
997         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
998         pcs2dev[0] = w / view_width;
999         pcs2dev[3] = h / view_height;
1000         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1001         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1002     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1003         pcs2dev[0] = pbox->x1 - pbox->x0;
1004         pcs2dev[3] = pbox->y1 - pbox->y0;
1006     }
1008     // Calculate the size of the surface which has to be created 
1009 #define SUBPIX_SCALE 100
1010     // Cairo requires an integer pattern surface width/height.
1011     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1012     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1013     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1014     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1015     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1016     // create new rendering context
1017     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1019     // adjust the size of the painted pattern to fit exactly the created surface
1020     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1021     double scale_width = surface_width / (bbox_width_scaler * width);
1022     double scale_height = surface_height / (bbox_height_scaler * height);
1023     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1024         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1025         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1026         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1027     }
1029     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1030     ps2user[4] = ori[Geom::X];
1031     ps2user[5] = ori[Geom::Y];
1033     pattern_ctx->setTransform(&pcs2dev);
1034     pattern_ctx->pushState();
1036     // create arena and group
1037     NRArena *arena = NRArena::create();
1038     unsigned dkey = sp_item_display_key_new(1);
1040     // show items and render them
1041     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1042         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1043             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1044                 if (SP_IS_ITEM (child)) {
1045                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1046                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1047                 }
1048             }
1049             break; // do not go further up the chain if children are found
1050         }
1051     }
1053     pattern_ctx->popState();
1055     // setup a cairo_pattern_t
1056     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1057     TEST(pattern_ctx->saveAsPng("pattern.png"));
1058     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1059     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1061     // set pattern transformation
1062     cairo_matrix_t pattern_matrix;
1063     _initCairoMatrix(&pattern_matrix, &ps2user);
1064     cairo_matrix_invert(&pattern_matrix);
1065     cairo_pattern_set_matrix(result, &pattern_matrix);
1067     delete pattern_ctx;
1069     // hide all items
1070     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1071         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1072             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1073                 if (SP_IS_ITEM (child)) {
1074                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1075                 }
1076             }
1077             break; // do not go further up the chain if children are found
1078         }
1079     }
1081     return result;
1084 cairo_pattern_t*
1085 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1086                                                  NRRect const *pbox, float alpha)
1088     cairo_pattern_t *pattern = NULL;
1089     bool apply_bbox2user = FALSE;
1091     if (SP_IS_LINEARGRADIENT (paintserver)) {
1093             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1095             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1097             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1098             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1099             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1100                 // convert to userspace
1101                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1102                 p1 *= bbox2user;
1103                 p2 *= bbox2user;
1104             }
1106             // create linear gradient pattern
1107             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1109             // add stops
1110             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1111                 float rgb[3];
1112                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1113                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1114             }
1115     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1117         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1119         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1121         Geom::Point c (rg->cx.computed, rg->cy.computed);
1122         Geom::Point f (rg->fx.computed, rg->fy.computed);
1123         double r = rg->r.computed;
1124         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1125             apply_bbox2user = true;
1127         // create radial gradient pattern
1128         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1130         // add stops
1131         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1132             float rgb[3];
1133             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1134             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1135         }
1136     } else if (SP_IS_PATTERN (paintserver)) {
1138         pattern = _createPatternPainter(paintserver, pbox);
1139     } else {
1140         return NULL;
1141     }
1143     if (pattern && SP_IS_GRADIENT (paintserver)) {
1144         SPGradient *g = SP_GRADIENT(paintserver);
1146         // set extend type
1147         SPGradientSpread spread = sp_gradient_get_spread(g);
1148         switch (spread) {
1149             case SP_GRADIENT_SPREAD_REPEAT: {
1150                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1151                 break;
1152             }
1153             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1154                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1155                 break;
1156             }
1157             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1158                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1159                 break;
1160             }
1161             default: {
1162                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1163                 break;
1164             }
1165         }
1167         cairo_matrix_t pattern_matrix;
1168         if (g->gradientTransform_set) {
1169             // apply gradient transformation
1170             cairo_matrix_init(&pattern_matrix,
1171                 g->gradientTransform[0], g->gradientTransform[1],
1172                 g->gradientTransform[2], g->gradientTransform[3],
1173                 g->gradientTransform[4], g->gradientTransform[5]);
1174         } else {
1175             cairo_matrix_init_identity (&pattern_matrix);
1176         }
1178         if (apply_bbox2user) {
1179             // convert to userspace
1180             cairo_matrix_t bbox2user;
1181             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1182             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1183         }
1184         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1185         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1186     }
1188     return pattern;
1191 void
1192 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1194     g_return_if_fail( !style->fill.set
1195                       || style->fill.isColor()
1196                       || style->fill.isPaintserver() );
1198     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1199     if (_state->merge_opacity) {
1200         alpha *= _state->opacity;
1201         TRACE(("merged op=%f\n", alpha));
1202     }
1204     if (style->fill.isColor()) {
1205         float rgb[3];
1206         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1208         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1210     } else if (!style->fill.set) { // unset fill is black
1211         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1213     } else {
1214         g_assert( style->fill.isPaintserver()
1215                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1216                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1218         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1220         if (pattern) {
1221             cairo_set_source(_cr, pattern);
1222             cairo_pattern_destroy(pattern);
1223         }
1224     }
1227 void
1228 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1230     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1231     if (_state->merge_opacity)
1232         alpha *= _state->opacity;
1234     if (style->stroke.isColor()) {
1235         float rgb[3];
1236         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1238         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1239     } else {
1240         g_assert( style->fill.isPaintserver()
1241                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1242                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1244         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1246         if (pattern) {
1247             cairo_set_source(_cr, pattern);
1248             cairo_pattern_destroy(pattern);
1249         }
1250     }
1252     if (style->stroke_dash.n_dash   &&
1253         style->stroke_dash.dash       )
1254     {
1255         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1256     } else {
1257         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1258     }
1260     cairo_set_line_width(_cr, style->stroke_width.computed);
1262     // set line join type
1263     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1264     switch (style->stroke_linejoin.computed) {
1265         case SP_STROKE_LINEJOIN_MITER:
1266             join = CAIRO_LINE_JOIN_MITER;
1267             break;
1268         case SP_STROKE_LINEJOIN_ROUND:
1269             join = CAIRO_LINE_JOIN_ROUND;
1270             break;
1271         case SP_STROKE_LINEJOIN_BEVEL:
1272             join = CAIRO_LINE_JOIN_BEVEL;
1273             break;
1274     }
1275     cairo_set_line_join(_cr, join);
1277     // set line cap type
1278     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1279     switch (style->stroke_linecap.computed) {
1280         case SP_STROKE_LINECAP_BUTT:
1281             cap = CAIRO_LINE_CAP_BUTT;
1282             break;
1283         case SP_STROKE_LINECAP_ROUND:
1284             cap = CAIRO_LINE_CAP_ROUND;
1285             break;
1286         case SP_STROKE_LINECAP_SQUARE:
1287             cap = CAIRO_LINE_CAP_SQUARE;
1288             break;
1289     }
1290     cairo_set_line_cap(_cr, cap);
1291     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1294 bool
1295 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1297     g_assert( _is_valid );
1299     if (_render_mode == RENDER_MODE_CLIP) {
1300         if (_clip_mode == CLIP_MODE_PATH) {
1301             addClipPath(pathv, &style->fill_rule);
1302         } else {
1303             setPathVector(pathv);
1304             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1305                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1306             } else {
1307                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1308             }
1309             cairo_fill(_cr);
1310             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1311         }
1312         return true;
1313     }
1315     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1316     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 || 
1317                     style->stroke_opacity.value == 0;
1319     if (no_fill && no_stroke)
1320         return true;
1322     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1323                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1325     if (!need_layer)
1326         cairo_save(_cr);
1327     else
1328         pushLayer();
1330     if (!no_fill) {
1331         _setFillStyle(style, pbox);
1332         setPathVector(pathv);
1334         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1335             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1336         } else {
1337             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1338         }
1340         if (no_stroke)
1341             cairo_fill(_cr);
1342         else
1343             cairo_fill_preserve(_cr);
1344     }
1346     if (!no_stroke) {
1347         _setStrokeStyle(style, pbox);
1348         if (no_fill)
1349             setPathVector(pathv);
1351         cairo_stroke(_cr);
1352     }
1354     if (need_layer)
1355         popLayer();
1356     else
1357         cairo_restore(_cr);
1359     return true;
1362 bool
1363 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1364                                 Geom::Matrix const *image_transform, SPStyle const *style)
1366     g_assert( _is_valid );
1368     if (_render_mode == RENDER_MODE_CLIP)
1369         return true;
1371     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1372     if (!px_rgba)
1373         return false;
1375     float opacity;
1376     if (_state->merge_opacity)
1377         opacity = _state->opacity;
1378     else
1379         opacity = 1.0;
1381     // make a copy of the original pixbuf with premultiplied alpha
1382     // if we pass the original pixbuf it will get messed up
1383     for (unsigned i = 0; i < h; i++) {
1384         for (unsigned j = 0; j < w; j++) {
1385             guchar const *src = px + i * rs + j * 4;
1386             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1387             guchar r, g, b, alpha_dst;
1389             // calculate opacity-modified alpha
1390             alpha_dst = src[3];
1391             if (opacity != 1.0 && _vector_based_target)
1392                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1394             // premul alpha (needed because this will be undone by cairo-pdf)
1395             r = src[0]*alpha_dst/255;
1396             g = src[1]*alpha_dst/255;
1397             b = src[2]*alpha_dst/255;
1399             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1400         }
1401     }
1403     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1404     if (cairo_surface_status(image_surface)) {
1405         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1406         return false;
1407     }
1409     // setup automatic freeing of the image data when destroying the surface
1410     static cairo_user_data_key_t key;
1411     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1413     cairo_save(_cr);
1415     // scaling by width & height is not needed because it will be done by Cairo
1416     if (image_transform)
1417         transform(image_transform);
1419     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1421     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1422     if (_vector_based_target) {
1423         cairo_new_path(_cr);
1424         cairo_rectangle(_cr, 0, 0, w, h);
1425         cairo_clip(_cr);
1426     }
1428     if (_vector_based_target)
1429         cairo_paint(_cr);
1430     else
1431         cairo_paint_with_alpha(_cr, opacity);
1433     cairo_restore(_cr);
1435     cairo_surface_destroy(image_surface);
1437     return true;
1440 #define GLYPH_ARRAY_SIZE 64
1442 unsigned int
1443 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1445     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1446     cairo_glyph_t *glyphs = glyph_array;
1447     unsigned int num_glyphs = glyphtext.size();
1448     if (num_glyphs > GLYPH_ARRAY_SIZE)
1449         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1451     unsigned int num_invalid_glyphs = 0;
1452     unsigned int i = 0;
1453     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1454         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1455         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1456         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1457             TRACE(("INVALID GLYPH found\n"));
1458             num_invalid_glyphs++;
1459             continue;
1460         }
1461         glyphs[i - num_invalid_glyphs].index = it_info->index;
1462         glyphs[i - num_invalid_glyphs].x = it_info->x;
1463         glyphs[i - num_invalid_glyphs].y = it_info->y;
1464         i++;
1465     }
1467     if (is_stroke) {
1468         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1469     } else {
1470         if (_is_texttopath) {
1471             cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1472             cairo_fill_preserve(cr);
1473         } else {
1474             cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1475         }
1476     }
1478     if (num_glyphs > GLYPH_ARRAY_SIZE)
1479         g_free(glyphs);
1481     return num_glyphs - num_invalid_glyphs;
1484 bool
1485 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1486                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1488     // create a cairo_font_face from PangoFont
1489     double size = style->font_size.computed;
1490     cairo_font_face_t *font_face = NULL;
1492     FcPattern *fc_pattern = NULL;
1493     
1494 #ifdef USE_PANGO_WIN32
1495 # ifdef CAIRO_HAS_WIN32_FONT
1496     LOGFONTA *lfa = pango_win32_font_logfont(font);
1497     LOGFONTW lfw;
1499     ZeroMemory(&lfw, sizeof(LOGFONTW));
1500     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1501     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1502     
1503     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1504 # endif
1505 #else
1506 # ifdef CAIRO_HAS_FT_FONT
1507     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1508     fc_pattern = fc_font->font_pattern;
1509     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1510 # endif
1511 #endif
1512     
1513     cairo_save(_cr);
1514     cairo_set_font_face(_cr, font_face);
1516     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1517         size = 12.0;
1519     // set the given font matrix
1520     cairo_matrix_t matrix;
1521     _initCairoMatrix(&matrix, font_matrix);
1522     cairo_set_font_matrix(_cr, &matrix);
1524     if (_render_mode == RENDER_MODE_CLIP) {
1525         if (_clip_mode == CLIP_MODE_MASK) {
1526             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1527                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1528             } else {
1529                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1530             }
1531             _showGlyphs(_cr, font, glyphtext, FALSE);
1532         } else {
1533             // just add the glyph paths to the current context
1534             _showGlyphs(_cr, font, glyphtext, TRUE);
1535         }
1536     } else {
1538         if (style->fill.isColor() || style->fill.isPaintserver()) {
1539             // set fill style
1540             _setFillStyle(style, NULL);
1542             _showGlyphs(_cr, font, glyphtext, FALSE);
1543         }
1545         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1546             // set stroke style
1547             _setStrokeStyle(style, NULL);
1549             // paint stroke
1550             _showGlyphs(_cr, font, glyphtext, TRUE);
1551             cairo_stroke(_cr);
1552         }
1553     }
1555     cairo_restore(_cr);
1557     if (font_face)
1558         cairo_font_face_destroy(font_face);
1560     return true;
1563 /* Helper functions */
1565 void
1566 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1568     cairo_new_path(_cr);
1569     addPathVector(pv);
1572 void
1573 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1575     feed_pathvector_to_cairo(_cr, pv);
1578 void
1579 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1581     cairo_matrix_t matrix;
1583     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1584     cairo_transform(cr, &matrix);
1587 void
1588 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1590     matrix->xx = (*transform)[0];
1591     matrix->yx = (*transform)[1];
1592     matrix->xy = (*transform)[2];
1593     matrix->yy = (*transform)[3];
1594     matrix->x0 = (*transform)[4];
1595     matrix->y0 = (*transform)[5];
1598 void
1599 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1601     _concatTransform(cr, (*transform)[0], (*transform)[1],
1602                      (*transform)[2], (*transform)[3],
1603                      (*transform)[4], (*transform)[5]);
1606 static cairo_status_t
1607 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1609     size_t written;
1610     FILE *file = (FILE*)closure;
1612     written = fwrite (data, 1, length, file);
1614     if (written == length)
1615         return CAIRO_STATUS_SUCCESS;
1616     else
1617         return CAIRO_STATUS_WRITE_ERROR;
1620 #include "clear-n_.h"
1622 }  /* namespace Internal */
1623 }  /* namespace Extension */
1624 }  /* namespace Inkscape */
1626 #undef TRACE
1627 #undef TEST
1629 /* End of GNU GPL code */
1632 /*
1633   Local Variables:
1634   mode:c++
1635   c-file-style:"stroustrup"
1636   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1637   indent-tabs-mode:nil
1638   fill-column:99
1639   End:
1640 */
1641 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :