Code

apply printing w32 patch https://bugs.launchpad.net/inkscape/+bug/179988/comments/174
[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.isColor()
1195                       || style->fill.isPaintserver() );
1197     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1198     if (_state->merge_opacity) {
1199         alpha *= _state->opacity;
1200         TRACE(("merged op=%f\n", alpha));
1201     }
1203     if (style->fill.isColor()) {
1204         float rgb[3];
1205         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1207         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1208     } else {
1209         g_assert( style->fill.isPaintserver()
1210                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1211                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1213         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1215         if (pattern) {
1216             cairo_set_source(_cr, pattern);
1217             cairo_pattern_destroy(pattern);
1218         }
1219     }
1222 void
1223 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1225     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1226     if (_state->merge_opacity)
1227         alpha *= _state->opacity;
1229     if (style->stroke.isColor()) {
1230         float rgb[3];
1231         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1233         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1234     } else {
1235         g_assert( style->fill.isPaintserver()
1236                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1237                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1239         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1241         if (pattern) {
1242             cairo_set_source(_cr, pattern);
1243             cairo_pattern_destroy(pattern);
1244         }
1245     }
1247     if (style->stroke_dash.n_dash   &&
1248         style->stroke_dash.dash       )
1249     {
1250         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1251     } else {
1252         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1253     }
1255     cairo_set_line_width(_cr, style->stroke_width.computed);
1257     // set line join type
1258     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1259     switch (style->stroke_linejoin.computed) {
1260         case SP_STROKE_LINEJOIN_MITER:
1261             join = CAIRO_LINE_JOIN_MITER;
1262             break;
1263         case SP_STROKE_LINEJOIN_ROUND:
1264             join = CAIRO_LINE_JOIN_ROUND;
1265             break;
1266         case SP_STROKE_LINEJOIN_BEVEL:
1267             join = CAIRO_LINE_JOIN_BEVEL;
1268             break;
1269     }
1270     cairo_set_line_join(_cr, join);
1272     // set line cap type
1273     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1274     switch (style->stroke_linecap.computed) {
1275         case SP_STROKE_LINECAP_BUTT:
1276             cap = CAIRO_LINE_CAP_BUTT;
1277             break;
1278         case SP_STROKE_LINECAP_ROUND:
1279             cap = CAIRO_LINE_CAP_ROUND;
1280             break;
1281         case SP_STROKE_LINECAP_SQUARE:
1282             cap = CAIRO_LINE_CAP_SQUARE;
1283             break;
1284     }
1285     cairo_set_line_cap(_cr, cap);
1286     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1289 bool
1290 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1292     g_assert( _is_valid );
1294     if (_render_mode == RENDER_MODE_CLIP) {
1295         if (_clip_mode == CLIP_MODE_PATH) {
1296             addClipPath(pathv, &style->fill_rule);
1297         } else {
1298             setPathVector(pathv);
1299             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1300                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1301             } else {
1302                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1303             }
1304             cairo_fill(_cr);
1305             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1306         }
1307         return true;
1308     }
1310     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1311     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 || 
1312                     style->fill_opacity.value == 0;
1314     if (no_fill && no_stroke)
1315         return true;
1317     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1318                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1320     if (!need_layer)
1321         cairo_save(_cr);
1322     else
1323         pushLayer();
1325     if (!no_fill) {
1326         _setFillStyle(style, pbox);
1327         setPathVector(pathv);
1329         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1330             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1331         } else {
1332             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1333         }
1335         if (no_stroke)
1336             cairo_fill(_cr);
1337         else
1338             cairo_fill_preserve(_cr);
1339     }
1341     if (!no_stroke) {
1342         _setStrokeStyle(style, pbox);
1343         if (no_fill)
1344             setPathVector(pathv);
1346         cairo_stroke(_cr);
1347     }
1349     if (need_layer)
1350         popLayer();
1351     else
1352         cairo_restore(_cr);
1354     return true;
1357 bool
1358 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1359                                 Geom::Matrix const *image_transform, SPStyle const *style)
1361     g_assert( _is_valid );
1363     if (_render_mode == RENDER_MODE_CLIP)
1364         return true;
1366     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1367     if (!px_rgba)
1368         return false;
1370     float opacity;
1371     if (_state->merge_opacity)
1372         opacity = _state->opacity;
1373     else
1374         opacity = 1.0;
1376     // make a copy of the original pixbuf with premultiplied alpha
1377     // if we pass the original pixbuf it will get messed up
1378     for (unsigned i = 0; i < h; i++) {
1379         for (unsigned j = 0; j < w; j++) {
1380             guchar const *src = px + i * rs + j * 4;
1381             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1382             guchar r, g, b, alpha_dst;
1384             // calculate opacity-modified alpha
1385             alpha_dst = src[3];
1386             if (opacity != 1.0 && _vector_based_target)
1387                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1389             // premul alpha (needed because this will be undone by cairo-pdf)
1390             r = src[0]*alpha_dst/255;
1391             g = src[1]*alpha_dst/255;
1392             b = src[2]*alpha_dst/255;
1394             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1395         }
1396     }
1398     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1399     if (cairo_surface_status(image_surface)) {
1400         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1401         return false;
1402     }
1404     // setup automatic freeing of the image data when destroying the surface
1405     static cairo_user_data_key_t key;
1406     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1408     cairo_save(_cr);
1410     // scaling by width & height is not needed because it will be done by Cairo
1411     if (image_transform)
1412         transform(image_transform);
1414     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1416     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1417     if (_vector_based_target) {
1418         cairo_new_path(_cr);
1419         cairo_rectangle(_cr, 0, 0, w, h);
1420         cairo_clip(_cr);
1421     }
1423     if (_vector_based_target)
1424         cairo_paint(_cr);
1425     else
1426         cairo_paint_with_alpha(_cr, opacity);
1428     cairo_restore(_cr);
1430     cairo_surface_destroy(image_surface);
1432     return true;
1435 #define GLYPH_ARRAY_SIZE 64
1437 unsigned int
1438 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1440     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1441     cairo_glyph_t *glyphs = glyph_array;
1442     unsigned int num_glyphs = glyphtext.size();
1443     if (num_glyphs > GLYPH_ARRAY_SIZE)
1444         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1446     unsigned int num_invalid_glyphs = 0;
1447     unsigned int i = 0;
1448     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1449         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1450         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1451         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1452             TRACE(("INVALID GLYPH found\n"));
1453             num_invalid_glyphs++;
1454             continue;
1455         }
1456         glyphs[i - num_invalid_glyphs].index = it_info->index;
1457         glyphs[i - num_invalid_glyphs].x = it_info->x;
1458         glyphs[i - num_invalid_glyphs].y = it_info->y;
1459         i++;
1460     }
1462     if (is_stroke || _is_texttopath)
1463         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1464     else
1465         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1467     if (num_glyphs > GLYPH_ARRAY_SIZE)
1468         g_free(glyphs);
1470     return num_glyphs - num_invalid_glyphs;
1473 bool
1474 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1475                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1477     // create a cairo_font_face from PangoFont
1478     double size = style->font_size.computed;
1479     cairo_font_face_t *font_face = NULL;
1481     FcPattern *fc_pattern = NULL;
1482     
1483 #ifdef USE_PANGO_WIN32
1484 # ifdef CAIRO_HAS_WIN32_FONT
1485     LOGFONTA *lfa = pango_win32_font_logfont(font);
1486     LOGFONTW lfw;
1488     ZeroMemory(&lfw, sizeof(LOGFONTW));
1489     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1490     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1491     
1492     font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1493 # endif
1494 #else
1495 # ifdef CAIRO_HAS_FT_FONT
1496     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1497     fc_pattern = fc_font->font_pattern;
1498     font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1499 # endif
1500 #endif
1501     
1502     cairo_save(_cr);
1503     cairo_set_font_face(_cr, font_face);
1505     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1506         size = 12.0;
1508     // set the given font matrix
1509     cairo_matrix_t matrix;
1510     _initCairoMatrix(&matrix, font_matrix);
1511     cairo_set_font_matrix(_cr, &matrix);
1513     if (_render_mode == RENDER_MODE_CLIP) {
1514         if (_clip_mode == CLIP_MODE_MASK) {
1515             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1516                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1517             } else {
1518                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1519             }
1520             _showGlyphs(_cr, font, glyphtext, FALSE);
1521         } else {
1522             // just add the glyph paths to the current context
1523             _showGlyphs(_cr, font, glyphtext, TRUE);
1524         }
1525     } else {
1527         if (style->fill.isColor() || style->fill.isPaintserver()) {
1528             // set fill style
1529             _setFillStyle(style, NULL);
1531             _showGlyphs(_cr, font, glyphtext, FALSE);
1532         }
1534         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1535             // set stroke style
1536             _setStrokeStyle(style, NULL);
1538             // paint stroke
1539             _showGlyphs(_cr, font, glyphtext, TRUE);
1540             cairo_stroke(_cr);
1541         }
1542     }
1544     cairo_restore(_cr);
1546     if (font_face)
1547         cairo_font_face_destroy(font_face);
1549     return true;
1552 /* Helper functions */
1554 void
1555 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1557     cairo_new_path(_cr);
1558     addPathVector(pv);
1561 void
1562 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1564     feed_pathvector_to_cairo(_cr, pv);
1567 void
1568 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1570     cairo_matrix_t matrix;
1572     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1573     cairo_transform(cr, &matrix);
1576 void
1577 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1579     matrix->xx = (*transform)[0];
1580     matrix->yx = (*transform)[1];
1581     matrix->xy = (*transform)[2];
1582     matrix->yy = (*transform)[3];
1583     matrix->x0 = (*transform)[4];
1584     matrix->y0 = (*transform)[5];
1587 void
1588 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1590     _concatTransform(cr, (*transform)[0], (*transform)[1],
1591                      (*transform)[2], (*transform)[3],
1592                      (*transform)[4], (*transform)[5]);
1595 static cairo_status_t
1596 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1598     size_t written;
1599     FILE *file = (FILE*)closure;
1601     written = fwrite (data, 1, length, file);
1603     if (written == length)
1604         return CAIRO_STATUS_SUCCESS;
1605     else
1606         return CAIRO_STATUS_WRITE_ERROR;
1609 #include "clear-n_.h"
1611 }  /* namespace Internal */
1612 }  /* namespace Extension */
1613 }  /* namespace Inkscape */
1615 #undef TRACE
1616 #undef TEST
1618 /* End of GNU GPL code */
1621 /*
1622   Local Variables:
1623   mode:c++
1624   c-file-style:"stroustrup"
1625   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1626   indent-tabs-mode:nil
1627   fill-column:99
1628   End:
1629 */
1630 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :