Code

Merge from trunk
[inkscape.git] / src / extension / internal / cairo-render-context.cpp
1 #define __SP_CAIRO_RENDER_CONTEXT_C__
3 /** \file
4  * Rendering with Cairo.
5  */
6 /*
7  * Author:
8  *   Miklos Erdelyi <erdelyim@gmail.com>
9  *
10  * Copyright (C) 2006 Miklos Erdelyi
11  *
12  * Licensed under GNU GPL
13  */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #ifndef PANGO_ENABLE_BACKEND
20 #define PANGO_ENABLE_BACKEND
21 #endif
23 #ifndef PANGO_ENABLE_ENGINE
24 #define PANGO_ENABLE_ENGINE
25 #endif
28 #include <signal.h>
29 #include <errno.h>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 #define TRACE(_args) g_message _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)
129     font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
132 CairoRenderContext::~CairoRenderContext(void)
134     if(font_table != NULL) {
135         g_hash_table_remove_all(font_table);
136     }
138     if (_cr) cairo_destroy(_cr);
139     if (_surface) cairo_surface_destroy(_surface);
140     if (_layout) g_object_unref(_layout);
142 void CairoRenderContext::font_data_free(gpointer data)
144     cairo_font_face_t *font_face = (cairo_font_face_t *)data;
145     if (font_face) {
146         cairo_font_face_destroy(font_face);
147     }
150 CairoRenderer*
151 CairoRenderContext::getRenderer(void) const
153     return _renderer;
156 CairoRenderState*
157 CairoRenderContext::getCurrentState(void) const
159     return _state;
162 CairoRenderState*
163 CairoRenderContext::getParentState(void) const
165     // if this is the root node just return it
166     if (g_slist_length(_state_stack) == 1) {
167         return _state;
168     } else {
169         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
170     }
173 void
174 CairoRenderContext::setStateForStyle(SPStyle const *style)
176     // only opacity & overflow is stored for now
177     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
178     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
179     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
181     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
182         _state->merge_opacity = FALSE;
184     // disable rendering of opacity if there's a stroke on the fill
185     if (_state->merge_opacity
186         && !style->fill.isNone()
187         && !style->stroke.isNone())
188         _state->merge_opacity = FALSE;
191 /**
192  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
193  *
194  * \param width     width of the surface to be created
195  * \param height    height of the surface to be created
196  */
197 CairoRenderContext*
198 CairoRenderContext::cloneMe(double width, double height) const
200     g_assert( _is_valid );
201     g_assert( width > 0.0 && height > 0.0 );
203     CairoRenderContext *new_context = _renderer->createContext();
204     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
205                                                             (int)ceil(width), (int)ceil(height));
206     new_context->_cr = cairo_create(surface);
207     new_context->_surface = surface;
208     new_context->_width = width;
209     new_context->_height = height;
210     new_context->_is_valid = TRUE;
212     return new_context;
215 CairoRenderContext*
216 CairoRenderContext::cloneMe(void) const
218     g_assert( _is_valid );
220     return cloneMe(_width, _height);
223 bool
224 CairoRenderContext::setImageTarget(cairo_format_t format)
226     // format cannot be set on an already initialized surface
227     if (_is_valid)
228         return false;
230     switch (format) {
231         case CAIRO_FORMAT_ARGB32:
232         case CAIRO_FORMAT_RGB24:
233         case CAIRO_FORMAT_A8:
234         case CAIRO_FORMAT_A1:
235             _target_format = format;
236             _target = CAIRO_SURFACE_TYPE_IMAGE;
237             return true;
238             break;
239         default:
240             break;
241     }
243     return false;
246 bool
247 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
249 #ifndef CAIRO_HAS_PDF_SURFACE
250     return false;
251 #else
252     _target = CAIRO_SURFACE_TYPE_PDF;
253     _vector_based_target = TRUE;
254 #endif
256     FILE *osf = NULL;
257     FILE *osp = NULL;
259     gsize bytesRead = 0;
260     gsize bytesWritten = 0;
261     GError *error = NULL;
262     gchar *local_fn = g_filename_from_utf8(utf8_fn,
263                                            -1,  &bytesRead,  &bytesWritten, &error);
264     gchar const *fn = local_fn;
266     /* TODO: Replace the below fprintf's with something that does the right thing whether in
267     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
268     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
269     * return code.
270     */
271     if (fn != NULL) {
272         if (*fn == '|') {
273             fn += 1;
274             while (isspace(*fn)) fn += 1;
275 #ifndef WIN32
276             osp = popen(fn, "w");
277 #else
278             osp = _popen(fn, "w");
279 #endif
280             if (!osp) {
281                 fprintf(stderr, "inkscape: popen(%s): %s\n",
282                         fn, strerror(errno));
283                 return false;
284             }
285             _stream = osp;
286         } else if (*fn == '>') {
287             fn += 1;
288             while (isspace(*fn)) fn += 1;
289             Inkscape::IO::dump_fopen_call(fn, "K");
290             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
291             if (!osf) {
292                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
293                         fn, strerror(errno));
294                 return false;
295             }
296             _stream = osf;
297         } else {
298             /* put cwd stuff in here */
299             gchar *qn = ( *fn
300                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
301                 : g_strdup("lpr") );
302 #ifndef WIN32
303             osp = popen(qn, "w");
304 #else
305             osp = _popen(qn, "w");
306 #endif
307             if (!osp) {
308                 fprintf(stderr, "inkscape: popen(%s): %s\n",
309                         qn, strerror(errno));
310                 return false;
311             }
312             g_free(qn);
313             _stream = osp;
314         }
315     }
317     g_free(local_fn);
319     if (_stream) {
320         /* fixme: this is kinda icky */
321 #if !defined(_WIN32) && !defined(__WIN32__)
322         (void) signal(SIGPIPE, SIG_IGN);
323 #endif
324     }
326     return true;
329 bool
330 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
332 #ifndef CAIRO_HAS_PS_SURFACE
333     return false;
334 #else
335     _target = CAIRO_SURFACE_TYPE_PS;
336     _vector_based_target = TRUE;
337 #endif
339     FILE *osf = NULL;
340     FILE *osp = NULL;
342     gsize bytesRead = 0;
343     gsize bytesWritten = 0;
344     GError *error = NULL;
345     gchar *local_fn = g_filename_from_utf8(utf8_fn,
346                                            -1,  &bytesRead,  &bytesWritten, &error);
347     gchar const *fn = local_fn;
349     /* TODO: Replace the below fprintf's with something that does the right thing whether in
350     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
351     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
352     * return code.
353     */
354     if (fn != NULL) {
355         if (*fn == '|') {
356             fn += 1;
357             while (isspace(*fn)) fn += 1;
358 #ifndef WIN32
359             osp = popen(fn, "w");
360 #else
361             osp = _popen(fn, "w");
362 #endif
363             if (!osp) {
364                 fprintf(stderr, "inkscape: popen(%s): %s\n",
365                         fn, strerror(errno));
366                 return false;
367             }
368             _stream = osp;
369         } else if (*fn == '>') {
370             fn += 1;
371             while (isspace(*fn)) fn += 1;
372             Inkscape::IO::dump_fopen_call(fn, "K");
373             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
374             if (!osf) {
375                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
376                         fn, strerror(errno));
377                 return false;
378             }
379             _stream = osf;
380         } else {
381             /* put cwd stuff in here */
382             gchar *qn = ( *fn
383                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
384                 : g_strdup("lpr") );
385 #ifndef WIN32
386             osp = popen(qn, "w");
387 #else
388             osp = _popen(qn, "w");
389 #endif
390             if (!osp) {
391                 fprintf(stderr, "inkscape: popen(%s): %s\n",
392                         qn, strerror(errno));
393                 return false;
394             }
395             g_free(qn);
396             _stream = osp;
397         }
398     }
400     g_free(local_fn);
402     if (_stream) {
403         /* fixme: this is kinda icky */
404 #if !defined(_WIN32) && !defined(__WIN32__)
405         (void) signal(SIGPIPE, SIG_IGN);
406 #endif
407     }
409     return true;
412 void CairoRenderContext::setPSLevel(unsigned int level)
414     _ps_level = level;
417 void CairoRenderContext::setEPS(bool eps)
419     _eps = eps;
422 unsigned int CairoRenderContext::getPSLevel(void)
424     return _ps_level;
427 void CairoRenderContext::setPDFLevel(unsigned int level)
429     _pdf_level = level;
432 void CairoRenderContext::setTextToPath(bool texttopath)
434     _is_texttopath = texttopath;
437 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
439     _is_filtertobitmap = filtertobitmap;
442 bool CairoRenderContext::getFilterToBitmap(void)
444     return _is_filtertobitmap;
447 void CairoRenderContext::setBitmapResolution(int resolution)
449     _bitmapresolution = resolution;
452 int CairoRenderContext::getBitmapResolution(void)
454     return _bitmapresolution;
457 cairo_surface_t*
458 CairoRenderContext::getSurface(void)
460     g_assert( _is_valid );
462     return _surface;
465 bool
466 CairoRenderContext::saveAsPng(const char *file_name)
468     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
469     if (status)
470         return false;
471     else
472         return true;
475 void
476 CairoRenderContext::setRenderMode(CairoRenderMode mode)
478     switch (mode) {
479         case RENDER_MODE_NORMAL:
480         case RENDER_MODE_CLIP:
481             _render_mode = mode;
482             break;
483         default:
484             _render_mode = RENDER_MODE_NORMAL;
485             break;
486     }
489 CairoRenderContext::CairoRenderMode
490 CairoRenderContext::getRenderMode(void) const
492     return _render_mode;
495 void
496 CairoRenderContext::setClipMode(CairoClipMode mode)
498     switch (mode) {
499         case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
500         case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
501             _clip_mode = mode;
502             break;
503         default:
504             _clip_mode = CLIP_MODE_PATH;
505             break;
506     }
509 CairoRenderContext::CairoClipMode
510 CairoRenderContext::getClipMode(void) const
512     return _clip_mode;
515 CairoRenderState*
516 CairoRenderContext::_createState(void)
518     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
519     g_assert( state != NULL );
521     state->has_filtereffect = FALSE;
522     state->merge_opacity = TRUE;
523     state->opacity = 1.0;
524     state->need_layer = FALSE;
525     state->has_overflow = FALSE;
526     state->parent_has_userspace = FALSE;
527     state->clip_path = NULL;
528     state->mask = NULL;
530     return state;
533 void
534 CairoRenderContext::pushLayer(void)
536     g_assert( _is_valid );
538     TRACE(("--pushLayer\n"));
539     cairo_push_group(_cr);
541     // clear buffer
542     if (!_vector_based_target) {
543         cairo_save(_cr);
544         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
545         cairo_paint(_cr);
546         cairo_restore(_cr);
547     }
550 void
551 CairoRenderContext::popLayer(void)
553     g_assert( _is_valid );
555     float opacity = _state->opacity;
556     TRACE(("--popLayer w/ opacity %f\n", opacity));
558     /*
559      At this point, the Cairo source is ready. A Cairo mask must be created if required.
560      Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
561      masks independently of the objects they effect while in SVG the clip paths and masks
562      are defined relative to the objects they are attached to.
563      Notes:
564      1. An SVG object may have both a clip path and a mask!
565      2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
566      3. An SVG clipped or masked object may be first drawn off the page and then translated onto
567         the page (document). This is also not handled properly.
568      4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
569      5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
570         alpha. This is handled here by doing a pixel by pixel conversion.
571     */
573     SPClipPath *clip_path = _state->clip_path;
574     SPMask *mask = _state->mask;
575     if (clip_path || mask) {
577         CairoRenderContext *clip_ctx = 0;
578         cairo_surface_t *clip_mask = 0;
580         // Apply any clip path first
581         if (clip_path) {
582             TRACE(("  Applying clip\n"));
583             if (_render_mode == RENDER_MODE_CLIP)
584                 mask = NULL;    // disable mask when performing nested clipping
586             if (_vector_based_target) {
587                 setClipMode(CLIP_MODE_PATH); // Vector
588                 if (!mask) {
589                     cairo_pop_group_to_source(_cr);
590                     _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
591                     if (opacity == 1.0)
592                         cairo_paint(_cr);
593                     else
594                         cairo_paint_with_alpha(_cr, opacity);
596                 } else {
597                     // the clipPath will be applied before masking
598                 }
599             } else {
601                 // setup a new rendering context
602                 clip_ctx = _renderer->createContext();
603                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
604                 clip_ctx->setClipMode(CLIP_MODE_MASK);  // Raster
605                 // This code ties the clipping to the document coordinates. It doesn't allow
606                 // for a clipped object intially drawn off the page and then translated onto
607                 // the page.
608                 if (!clip_ctx->setupSurface(_width, _height)) {
609                     TRACE(("clip: setupSurface failed\n"));
610                     _renderer->destroyContext(clip_ctx);
611                     return;
612                 }
614                 // clear buffer
615                 cairo_save(clip_ctx->_cr);
616                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
617                 cairo_paint(clip_ctx->_cr);
618                 cairo_restore(clip_ctx->_cr);
620                 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
621                 if (!mask)
622                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
623                 else
624                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
626                 // copy over the correct CTM
627                 // It must be stored in item_transform of current state after pushState.
628                 Geom::Matrix item_transform;
629                 if (_state->parent_has_userspace)
630                     item_transform = getParentState()->transform * _state->item_transform;
631                 else
632                     item_transform = _state->item_transform;
634                 // apply the clip path
635                 clip_ctx->pushState();
636                 clip_ctx->getCurrentState()->item_transform = item_transform;
637                 _renderer->applyClipPath(clip_ctx, clip_path);
638                 clip_ctx->popState();
640                 clip_mask = clip_ctx->getSurface();
641                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
643                 if (!mask) {
644                     cairo_pop_group_to_source(_cr);
645                     cairo_mask_surface(_cr, clip_mask, 0, 0);
646                     _renderer->destroyContext(clip_ctx);
647                 }
648             }
649         }
651         // Apply any mask second
652         if (mask) {
653             TRACE(("  Applying mask\n"));
654             // create rendering context for mask
655             CairoRenderContext *mask_ctx = _renderer->createContext();
657             // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
658             // Cairo surface is expecting the mask to be 90 dpi.
659             float surface_width = _width;
660             float surface_height = _height;
661             if( _vector_based_target ) {
662                 surface_width *= 1.25;
663                 surface_height *= 1.25;
664             }
665             if (!mask_ctx->setupSurface( surface_width, surface_height )) {
666                 TRACE(("mask: setupSurface failed\n"));
667                 _renderer->destroyContext(mask_ctx);
668                 return;
669             }
670             TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
672             // set rendering mode to normal
673             setRenderMode(RENDER_MODE_NORMAL);
675             // copy the correct CTM to mask context
676             /*
677             if (_state->parent_has_userspace)
678                 mask_ctx->setTransform(&getParentState()->transform);
679             else
680                 mask_ctx->setTransform(&_state->transform);
681             */
682             // This is probably not correct... but it seems to do the trick.
683             mask_ctx->setTransform(&_state->item_transform);
685             // render mask contents to mask_ctx
686             _renderer->applyMask(mask_ctx, mask);
688             TEST(mask_ctx->saveAsPng("mask.png"));
690             // composite with clip mask
691             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
692                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
693                 _renderer->destroyContext(clip_ctx);
694             }
696             cairo_surface_t *mask_image = mask_ctx->getSurface();
697             int width = cairo_image_surface_get_width(mask_image);
698             int height = cairo_image_surface_get_height(mask_image);
699             int stride = cairo_image_surface_get_stride(mask_image);
700             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
702             // premultiply with opacity
703             // In SVG, the rgb channels as well as the alpha channel is used in masking.
704             // In Cairo, only the alpha channel is used thus requiring this conversion.
705             TRACE(("premul w/ %f\n", opacity));
706             guint8 int_opacity = (guint8)(255 * opacity);
707             for (int row = 0 ; row < height; row++) {
708                 unsigned char *row_data = pixels + (row * stride);
709                 for (int i = 0 ; i < width; i++) {
710                     guint32 *pixel = (guint32 *)row_data + i;
711                     *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
712                                ((*pixel & 0x0000ff00) >>  8) * 46518 +
713                                ((*pixel & 0x000000ff)      ) * 4688) *
714                               int_opacity);
715                 }
716             }
718             cairo_pop_group_to_source(_cr);
719             if (_clip_mode == CLIP_MODE_PATH) {
720                 // we have to do the clipping after cairo_pop_group_to_source
721                 _renderer->applyClipPath(this, clip_path);
722             }
723             // apply the mask onto the layer
724             cairo_mask_surface(_cr, mask_image, 0, 0);
725             _renderer->destroyContext(mask_ctx);
726         }
727     } else {
728         // No clip path or mask
729         cairo_pop_group_to_source(_cr);
730         if (opacity == 1.0)
731             cairo_paint(_cr);
732         else
733             cairo_paint_with_alpha(_cr, opacity);
734     }
737 void
738 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
740     g_assert( _is_valid );
742     // here it should be checked whether the current clip winding changed
743     // so we could switch back to masked clipping
744     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
745         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
746     } else {
747         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
748     }
749     addPathVector(pv);
752 void
753 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
755     g_assert( _is_valid );
757     cairo_rectangle(_cr, x, y, width, height);
758     cairo_clip(_cr);
761 bool
762 CairoRenderContext::setupSurface(double width, double height)
764     // Is the surface already set up?
765     if (_is_valid)
766         return true;
768     if (_vector_based_target && _stream == NULL)
769         return false;
771     _width = width;
772     _height = height;
774     cairo_surface_t *surface = NULL;
775     cairo_matrix_t ctm;
776     cairo_matrix_init_identity (&ctm);
777     switch (_target) {
778         case CAIRO_SURFACE_TYPE_IMAGE:
779             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
780             break;
781 #ifdef CAIRO_HAS_PDF_SURFACE
782         case CAIRO_SURFACE_TYPE_PDF:
783             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
784             break;
785 #endif
786 #ifdef CAIRO_HAS_PS_SURFACE
787         case CAIRO_SURFACE_TYPE_PS:
788             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
789             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
790                 return FALSE;
791             }
792 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
793             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
794             cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
795 #endif
796             break;
797 #endif
798         default:
799             return false;
800             break;
801     }
803     return _finishSurfaceSetup (surface, &ctm);
806 bool
807 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
809     if (_is_valid || !surface)
810         return false;
812     _vector_based_target = is_vector;
813     bool ret = _finishSurfaceSetup (surface, ctm);
814     if (ret)
815         cairo_surface_reference (surface);
816     return ret;
819 bool
820 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
822 g_message("enter");
823     if(surface == NULL) {
824         return false;
825     }
826     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
827         return false;
828     }
830     _cr = cairo_create(surface);
831     if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
832         return false;
833     }
834     if (ctm)
835         cairo_set_matrix(_cr, ctm);
836     _surface = surface;
838     if (_vector_based_target) {
839         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
840     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
841         // set background color on non-alpha surfaces
842         // TODO: bgcolor should be derived from SPDocument
843         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
844         cairo_rectangle(_cr, 0, 0, _width, _height);
845         cairo_fill(_cr);
846     }
848     _is_valid = TRUE;
849 g_message("leave");
850     return true;
853 bool
854 CairoRenderContext::finish(void)
856     g_assert( _is_valid );
858     if (_vector_based_target)
859         cairo_show_page(_cr);
861     cairo_destroy(_cr);
862     cairo_surface_finish(_surface);
863     cairo_status_t status = cairo_surface_status(_surface);
864     cairo_surface_destroy(_surface);
865     _cr = NULL;
866     _surface = NULL;
868     if (_layout)
869         g_object_unref(_layout);
871     _is_valid = FALSE;
873     if (_vector_based_target && _stream) {
874         /* Flush stream to be sure. */
875         (void) fflush(_stream);
877         fclose(_stream);
878         _stream = NULL;
879     }
881     if (status == CAIRO_STATUS_SUCCESS)
882         return true;
883     else
884         return false;
887 void
888 CairoRenderContext::transform(Geom::Matrix const *transform)
890     g_assert( _is_valid );
892     cairo_matrix_t matrix;
893     _initCairoMatrix(&matrix, transform);
894     cairo_transform(_cr, &matrix);
896     // store new CTM
897     getTransform(&_state->transform);
900 void
901 CairoRenderContext::setTransform(Geom::Matrix const *transform)
903     g_assert( _is_valid );
905     cairo_matrix_t matrix;
906     _initCairoMatrix(&matrix, transform);
907     cairo_set_matrix(_cr, &matrix);
908     _state->transform = *transform;
911 void
912 CairoRenderContext::getTransform(Geom::Matrix *copy) const
914     g_assert( _is_valid );
916     cairo_matrix_t ctm;
917     cairo_get_matrix(_cr, &ctm);
918     (*copy)[0] = ctm.xx;
919     (*copy)[1] = ctm.yx;
920     (*copy)[2] = ctm.xy;
921     (*copy)[3] = ctm.yy;
922     (*copy)[4] = ctm.x0;
923     (*copy)[5] = ctm.y0;
926 void
927 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
929     g_assert( _is_valid );
931     CairoRenderState *parent_state = getParentState();
932     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
935 void
936 CairoRenderContext::pushState(void)
938     g_assert( _is_valid );
940     cairo_save(_cr);
942     CairoRenderState *new_state = _createState();
943     // copy current state's transform
944     new_state->transform = _state->transform;
945     _state_stack = g_slist_prepend(_state_stack, new_state);
946     _state = new_state;
949 void
950 CairoRenderContext::popState(void)
952     g_assert( _is_valid );
954     cairo_restore(_cr);
956     g_free(_state_stack->data);
957     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
958     _state = (CairoRenderState*)_state_stack->data;
960     g_assert( g_slist_length(_state_stack) > 0 );
963 static bool pattern_hasItemChildren (SPPattern *pat)
965     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
966         if (SP_IS_ITEM (child)) {
967             return true;
968         }
969     }
970     return false;
973 cairo_pattern_t*
974 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
976     g_assert( SP_IS_PATTERN(paintserver) );
978     SPPattern *pat = SP_PATTERN (paintserver);
980     Geom::Matrix ps2user, pcs2dev;
981     ps2user = Geom::identity();
982     pcs2dev = Geom::identity();
984     double x = pattern_x(pat);
985     double y = pattern_y(pat);
986     double width = pattern_width(pat);
987     double height = pattern_height(pat);
988     double bbox_width_scaler;
989     double bbox_height_scaler;
991     TRACE(("%f x %f pattern\n", width, height));
993     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
994         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
995         bbox_width_scaler = pbox->x1 - pbox->x0;
996         bbox_height_scaler = pbox->y1 - pbox->y0;
997         ps2user[4] = x * bbox_width_scaler + pbox->x0;
998         ps2user[5] = y * bbox_height_scaler + pbox->y0;
999     } else {
1000         bbox_width_scaler = 1.0;
1001         bbox_height_scaler = 1.0;
1002         ps2user[4] = x;
1003         ps2user[5] = y;
1004     }
1006     // apply pattern transformation
1007     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1008     ps2user *= pattern_transform;
1009     Geom::Point ori (ps2user[4], ps2user[5]);
1011     // create pattern contents coordinate system
1012     if (pat->viewBox_set) {
1013         NRRect *view_box = pattern_viewBox(pat);
1015         double x, y, w, h;
1016         double view_width, view_height;
1017         x = 0;
1018         y = 0;
1019         w = width * bbox_width_scaler;
1020         h = height * bbox_height_scaler;
1022         view_width = view_box->x1 - view_box->x0;
1023         view_height = view_box->y1 - view_box->y0;
1025         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1026         pcs2dev[0] = w / view_width;
1027         pcs2dev[3] = h / view_height;
1028         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1029         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1030     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1031         pcs2dev[0] = pbox->x1 - pbox->x0;
1032         pcs2dev[3] = pbox->y1 - pbox->y0;
1034     }
1036     // Calculate the size of the surface which has to be created
1037 #define SUBPIX_SCALE 100
1038     // Cairo requires an integer pattern surface width/height.
1039     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1040     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1041     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1042     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1043     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1044     // create new rendering context
1045     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1047     // adjust the size of the painted pattern to fit exactly the created surface
1048     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1049     double scale_width = surface_width / (bbox_width_scaler * width);
1050     double scale_height = surface_height / (bbox_height_scaler * height);
1051     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1052         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1053         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1054         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1055     }
1057     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1058     ps2user[4] = ori[Geom::X];
1059     ps2user[5] = ori[Geom::Y];
1061     pattern_ctx->setTransform(&pcs2dev);
1062     pattern_ctx->pushState();
1064     // create arena and group
1065     NRArena *arena = NRArena::create();
1066     unsigned dkey = sp_item_display_key_new(1);
1068     // show items and render them
1069     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1070         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1071             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1072                 if (SP_IS_ITEM (child)) {
1073                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1074                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1075                 }
1076             }
1077             break; // do not go further up the chain if children are found
1078         }
1079     }
1081     pattern_ctx->popState();
1083     // setup a cairo_pattern_t
1084     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1085     TEST(pattern_ctx->saveAsPng("pattern.png"));
1086     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1087     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1089     // set pattern transformation
1090     cairo_matrix_t pattern_matrix;
1091     _initCairoMatrix(&pattern_matrix, &ps2user);
1092     cairo_matrix_invert(&pattern_matrix);
1093     cairo_pattern_set_matrix(result, &pattern_matrix);
1095     delete pattern_ctx;
1097     // hide all items
1098     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1099         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1100             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1101                 if (SP_IS_ITEM (child)) {
1102                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1103                 }
1104             }
1105             break; // do not go further up the chain if children are found
1106         }
1107     }
1109     return result;
1112 cairo_pattern_t*
1113 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1114                                                  NRRect const *pbox, float alpha)
1116     cairo_pattern_t *pattern = NULL;
1117     bool apply_bbox2user = FALSE;
1119     if (SP_IS_LINEARGRADIENT (paintserver)) {
1121             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1123             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1125             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1126             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1127             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1128                 // convert to userspace
1129                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1130                 p1 *= bbox2user;
1131                 p2 *= bbox2user;
1132             }
1134             // create linear gradient pattern
1135             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1137             // add stops
1138             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1139                 float rgb[3];
1140                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1141                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1142             }
1143     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1145         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1147         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1149         Geom::Point c (rg->cx.computed, rg->cy.computed);
1150         Geom::Point f (rg->fx.computed, rg->fy.computed);
1151         double r = rg->r.computed;
1152         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1153             apply_bbox2user = true;
1155         // create radial gradient pattern
1156         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1158         // add stops
1159         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1160             float rgb[3];
1161             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1162             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1163         }
1164     } else if (SP_IS_PATTERN (paintserver)) {
1166         pattern = _createPatternPainter(paintserver, pbox);
1167     } else {
1168         return NULL;
1169     }
1171     if (pattern && SP_IS_GRADIENT (paintserver)) {
1172         SPGradient *g = SP_GRADIENT(paintserver);
1174         // set extend type
1175         SPGradientSpread spread = sp_gradient_get_spread(g);
1176         switch (spread) {
1177             case SP_GRADIENT_SPREAD_REPEAT: {
1178                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1179                 break;
1180             }
1181             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1182                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1183                 break;
1184             }
1185             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1186                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1187                 break;
1188             }
1189             default: {
1190                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1191                 break;
1192             }
1193         }
1195         cairo_matrix_t pattern_matrix;
1196         if (g->gradientTransform_set) {
1197             // apply gradient transformation
1198             cairo_matrix_init(&pattern_matrix,
1199                 g->gradientTransform[0], g->gradientTransform[1],
1200                 g->gradientTransform[2], g->gradientTransform[3],
1201                 g->gradientTransform[4], g->gradientTransform[5]);
1202         } else {
1203             cairo_matrix_init_identity (&pattern_matrix);
1204         }
1206         if (apply_bbox2user) {
1207             // convert to userspace
1208             cairo_matrix_t bbox2user;
1209             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1210             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1211         }
1212         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1213         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1214     }
1216     return pattern;
1219 void
1220 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1222     g_return_if_fail( !style->fill.set
1223                       || style->fill.isColor()
1224                       || style->fill.isPaintserver() );
1226     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1227     if (_state->merge_opacity) {
1228         alpha *= _state->opacity;
1229         TRACE(("merged op=%f\n", alpha));
1230     }
1232     if (style->fill.isColor()) {
1233         float rgb[3];
1234         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1236         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1238     } else if (!style->fill.set) { // unset fill is black
1239         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1241     } else {
1242         g_assert( style->fill.isPaintserver()
1243                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1244                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1246         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1248         if (pattern) {
1249             cairo_set_source(_cr, pattern);
1250             cairo_pattern_destroy(pattern);
1251         }
1252     }
1255 void
1256 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1258     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1259     if (_state->merge_opacity)
1260         alpha *= _state->opacity;
1262     if (style->stroke.isColor()) {
1263         float rgb[3];
1264         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1266         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1267     } else {
1268         g_assert( style->fill.isPaintserver()
1269                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1270                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1272         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1274         if (pattern) {
1275             cairo_set_source(_cr, pattern);
1276             cairo_pattern_destroy(pattern);
1277         }
1278     }
1280     if (style->stroke_dash.n_dash   &&
1281         style->stroke_dash.dash       )
1282     {
1283         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1284     } else {
1285         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1286     }
1288     cairo_set_line_width(_cr, style->stroke_width.computed);
1290     // set line join type
1291     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1292     switch (style->stroke_linejoin.computed) {
1293         case SP_STROKE_LINEJOIN_MITER:
1294             join = CAIRO_LINE_JOIN_MITER;
1295             break;
1296         case SP_STROKE_LINEJOIN_ROUND:
1297             join = CAIRO_LINE_JOIN_ROUND;
1298             break;
1299         case SP_STROKE_LINEJOIN_BEVEL:
1300             join = CAIRO_LINE_JOIN_BEVEL;
1301             break;
1302     }
1303     cairo_set_line_join(_cr, join);
1305     // set line cap type
1306     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1307     switch (style->stroke_linecap.computed) {
1308         case SP_STROKE_LINECAP_BUTT:
1309             cap = CAIRO_LINE_CAP_BUTT;
1310             break;
1311         case SP_STROKE_LINECAP_ROUND:
1312             cap = CAIRO_LINE_CAP_ROUND;
1313             break;
1314         case SP_STROKE_LINECAP_SQUARE:
1315             cap = CAIRO_LINE_CAP_SQUARE;
1316             break;
1317     }
1318     cairo_set_line_cap(_cr, cap);
1319     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1322 bool
1323 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1325     g_assert( _is_valid );
1327     if (_render_mode == RENDER_MODE_CLIP) {
1328         if (_clip_mode == CLIP_MODE_PATH) {
1329             addClipPath(pathv, &style->fill_rule);
1330         } else {
1331             setPathVector(pathv);
1332             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1333                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1334             } else {
1335                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1336             }
1337             cairo_fill(_cr);
1338             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1339         }
1340         return true;
1341     }
1343     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1344     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1345                     style->stroke_opacity.value == 0;
1347     if (no_fill && no_stroke)
1348         return true;
1350     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1351                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1353     if (!need_layer)
1354         cairo_save(_cr);
1355     else
1356         pushLayer();
1358     if (!no_fill) {
1359         _setFillStyle(style, pbox);
1360         setPathVector(pathv);
1362         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1363             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1364         } else {
1365             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1366         }
1368         if (no_stroke)
1369             cairo_fill(_cr);
1370         else
1371             cairo_fill_preserve(_cr);
1372     }
1374     if (!no_stroke) {
1375         _setStrokeStyle(style, pbox);
1376         if (no_fill)
1377             setPathVector(pathv);
1379         cairo_stroke(_cr);
1380     }
1382     if (need_layer)
1383         popLayer();
1384     else
1385         cairo_restore(_cr);
1387     return true;
1390 bool
1391 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1392                                 Geom::Matrix const *image_transform, SPStyle const *style)
1394     g_assert( _is_valid );
1396     if (_render_mode == RENDER_MODE_CLIP)
1397         return true;
1399     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1400     if (!px_rgba)
1401         return false;
1403     float opacity;
1404     if (_state->merge_opacity)
1405         opacity = _state->opacity;
1406     else
1407         opacity = 1.0;
1409     // make a copy of the original pixbuf with premultiplied alpha
1410     // if we pass the original pixbuf it will get messed up
1411     for (unsigned i = 0; i < h; i++) {
1412         for (unsigned j = 0; j < w; j++) {
1413             guchar const *src = px + i * rs + j * 4;
1414             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1415             guchar r, g, b, alpha_dst;
1417             // calculate opacity-modified alpha
1418             alpha_dst = src[3];
1419             if (opacity != 1.0 && _vector_based_target)
1420                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1422             // premul alpha (needed because this will be undone by cairo-pdf)
1423             r = src[0]*alpha_dst/255;
1424             g = src[1]*alpha_dst/255;
1425             b = src[2]*alpha_dst/255;
1427             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1428         }
1429     }
1431     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1432     if (cairo_surface_status(image_surface)) {
1433         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1434         return false;
1435     }
1437     // setup automatic freeing of the image data when destroying the surface
1438     static cairo_user_data_key_t key;
1439     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1441     cairo_save(_cr);
1443     // scaling by width & height is not needed because it will be done by Cairo
1444     if (image_transform)
1445         transform(image_transform);
1447     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1449     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1450     if (_vector_based_target) {
1451         cairo_new_path(_cr);
1452         cairo_rectangle(_cr, 0, 0, w, h);
1453         cairo_clip(_cr);
1454     }
1456     if (_vector_based_target)
1457         cairo_paint(_cr);
1458     else
1459         cairo_paint_with_alpha(_cr, opacity);
1461     cairo_restore(_cr);
1463     cairo_surface_destroy(image_surface);
1465     return true;
1468 #define GLYPH_ARRAY_SIZE 64
1470 unsigned int
1471 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1473     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1474     cairo_glyph_t *glyphs = glyph_array;
1475     unsigned int num_glyphs = glyphtext.size();
1476     if (num_glyphs > GLYPH_ARRAY_SIZE)
1477         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1479     unsigned int num_invalid_glyphs = 0;
1480     unsigned int i = 0;
1481     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1482         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1483         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1484         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1485             TRACE(("INVALID GLYPH found\n"));
1486             num_invalid_glyphs++;
1487             continue;
1488         }
1489         glyphs[i - num_invalid_glyphs].index = it_info->index;
1490         glyphs[i - num_invalid_glyphs].x = it_info->x;
1491         glyphs[i - num_invalid_glyphs].y = it_info->y;
1492         i++;
1493     }
1495     if (path) {
1496         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1497     } else {
1498         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1499     }
1501     if (num_glyphs > GLYPH_ARRAY_SIZE)
1502         g_free(glyphs);
1504     return num_glyphs - num_invalid_glyphs;
1507 bool
1508 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1509                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1511     // create a cairo_font_face from PangoFont
1512     double size = style->font_size.computed;
1513     gpointer fonthash = (gpointer)font;
1514     cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1516     FcPattern *fc_pattern = NULL;
1518 #ifdef USE_PANGO_WIN32
1519 # ifdef CAIRO_HAS_WIN32_FONT
1520     LOGFONTA *lfa = pango_win32_font_logfont(font);
1521     LOGFONTW lfw;
1523     ZeroMemory(&lfw, sizeof(LOGFONTW));
1524     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1525     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1527     if(font_face == NULL) {
1528         font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1529         g_hash_table_insert(font_table, fonthash, font_face);
1530     }
1531 # endif
1532 #else
1533 # ifdef CAIRO_HAS_FT_FONT
1534     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1535     fc_pattern = fc_font->font_pattern;
1536     if(font_face == NULL) {
1537         font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1538         g_hash_table_insert(font_table, fonthash, font_face);
1539     }
1540 # endif
1541 #endif
1543     cairo_save(_cr);
1544     cairo_set_font_face(_cr, font_face);
1546     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1547         size = 12.0;
1549     // set the given font matrix
1550     cairo_matrix_t matrix;
1551     _initCairoMatrix(&matrix, font_matrix);
1552     cairo_set_font_matrix(_cr, &matrix);
1554     if (_render_mode == RENDER_MODE_CLIP) {
1555         if (_clip_mode == CLIP_MODE_MASK) {
1556             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1557                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1558             } else {
1559                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1560             }
1561             _showGlyphs(_cr, font, glyphtext, FALSE);
1562         } else {
1563             // just add the glyph paths to the current context
1564             _showGlyphs(_cr, font, glyphtext, TRUE);
1565         }
1566     } else {
1567         bool fill = false, stroke = false, have_path = false;
1568         if (style->fill.isColor() || style->fill.isPaintserver()) {
1569             fill = true;
1570         }
1572         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1573             stroke = true;
1574         }
1575         if (fill) {
1576             _setFillStyle(style, NULL);
1577             if (_is_texttopath) {
1578                 _showGlyphs(_cr, font, glyphtext, true);
1579                 have_path = true;
1580                 if (stroke) cairo_fill_preserve(_cr);
1581                 else cairo_fill(_cr);
1582             } else {
1583                 _showGlyphs(_cr, font, glyphtext, false);
1584             }
1585         }
1586         if (stroke) {
1587             _setStrokeStyle(style, NULL);
1588             if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1589             cairo_stroke(_cr);
1590         }
1591     }
1593     cairo_restore(_cr);
1595 //    if (font_face)
1596 //        cairo_font_face_destroy(font_face);
1598     return true;
1601 /* Helper functions */
1603 void
1604 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1606     cairo_new_path(_cr);
1607     addPathVector(pv);
1610 void
1611 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1613     feed_pathvector_to_cairo(_cr, pv);
1616 void
1617 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1619     cairo_matrix_t matrix;
1621     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1622     cairo_transform(cr, &matrix);
1625 void
1626 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1628     matrix->xx = (*transform)[0];
1629     matrix->yx = (*transform)[1];
1630     matrix->xy = (*transform)[2];
1631     matrix->yy = (*transform)[3];
1632     matrix->x0 = (*transform)[4];
1633     matrix->y0 = (*transform)[5];
1636 void
1637 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1639     _concatTransform(cr, (*transform)[0], (*transform)[1],
1640                      (*transform)[2], (*transform)[3],
1641                      (*transform)[4], (*transform)[5]);
1644 static cairo_status_t
1645 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1647     size_t written;
1648     FILE *file = (FILE*)closure;
1650     written = fwrite (data, 1, length, file);
1652     if (written == length)
1653         return CAIRO_STATUS_SUCCESS;
1654     else
1655         return CAIRO_STATUS_WRITE_ERROR;
1658 #include "clear-n_.h"
1660 }  /* namespace Internal */
1661 }  /* namespace Extension */
1662 }  /* namespace Inkscape */
1664 #undef TRACE
1665 #undef TEST
1667 /* End of GNU GPL code */
1670 /*
1671   Local Variables:
1672   mode:c++
1673   c-file-style:"stroustrup"
1674   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1675   indent-tabs-mode:nil
1676   fill-column:99
1677   End:
1678 */
1679 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :