Code

PNG output for Cairo renderer
[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 #ifdef HAVE_CAIRO_PDF
21 #ifndef PANGO_ENABLE_BACKEND
22 #define PANGO_ENABLE_BACKEND
23 #endif
25 #ifndef PANGO_ENABLE_ENGINE
26 #define PANGO_ENABLE_ENGINE
27 #endif
30 #include <signal.h>
31 #include <errno.h>
33 #include <libnr/n-art-bpath.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-matrix-fns.h>
36 #include <libnr/nr-matrix-translate-ops.h>
37 #include <libnr/nr-scale-matrix-ops.h>
39 #include <glib/gmem.h>
41 #include <glibmm/i18n.h>
42 #include "display/nr-arena.h"
43 #include "display/nr-arena-item.h"
44 #include "display/nr-arena-group.h"
45 #include "display/curve.h"
46 #include "display/canvas-bpath.h"
47 #include "sp-item.h"
48 #include "sp-item-group.h"
49 #include "style.h"
50 #include "sp-linear-gradient.h"
51 #include "sp-radial-gradient.h"
52 #include "sp-pattern.h"
53 #include "sp-mask.h"
54 #include "sp-clippath.h"
56 #include <unit-constants.h>
58 #include "cairo-render-context.h"
59 #include "cairo-renderer.h"
60 #include "extension/system.h"
62 #include "io/sys.h"
64 #include <cairo.h>
66 // include support for only the compiled-in surface types
67 #ifdef CAIRO_HAS_PDF_SURFACE
68 #include <cairo-pdf.h>
69 #endif
70 #ifdef CAIRO_HAS_PS_SURFACE
71 #include <cairo-ps.h>
72 #endif
74 #include <cairo-ft.h>
76 #include <pango/pangofc-fontmap.h>
78 //#define TRACE(_args) g_printf _args
79 #define TRACE
80 //#define TEST(_args) _args
81 #define TEST
83 // FIXME: expose these from sp-clippath/mask.cpp
84 struct SPClipPathView {
85     SPClipPathView *next;
86     unsigned int key;
87     NRArenaItem *arenaitem;
88     NRRect bbox;
89 };
91 struct SPMaskView {
92     SPMaskView *next;
93     unsigned int key;
94     NRArenaItem *arenaitem;
95     NRRect bbox;
96 };
98 namespace Inkscape {
99 namespace Extension {
100 namespace Internal {
102 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
104 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
105     _renderer(parent),
106     _is_valid(FALSE),
107     _vector_based_target(FALSE),
108     _target(CAIRO_SURFACE_TYPE_IMAGE),
109     _target_format(CAIRO_FORMAT_ARGB32),
110     _clip_mode(CLIP_MODE_MASK),
111     _render_mode(RENDER_MODE_NORMAL),
112     _dpi(72),
113     _stream(NULL),
114     _cr(NULL),
115     _surface(NULL),
116     _layout(NULL),
117     _state(NULL)
118 {}
120 CairoRenderContext::~CairoRenderContext(void)
122     if (_cr) cairo_destroy(_cr);
123     if (_surface) cairo_surface_destroy(_surface);
124     if (_layout) g_object_unref(_layout);
127 CairoRenderer*
128 CairoRenderContext::getRenderer(void) const
130     return _renderer;
133 CairoRenderState*
134 CairoRenderContext::getCurrentState(void) const
136     return _state;
139 CairoRenderState*
140 CairoRenderContext::getParentState(void) const
142     // if this is the root node just return it
143     if (g_slist_length(_state_stack) == 1) {
144         return _state;
145     } else {
146         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
147     }
150 void
151 CairoRenderContext::setStateForStyle(SPStyle const *style)
153     // only opacity & overflow is stored for now
154     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
155     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
157     if (style->fill.type == SP_PAINT_TYPE_PAINTSERVER || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)
158         _state->merge_opacity = FALSE;
160     // disable rendering of opacity if there's a stroke on the fill
161     if (_state->merge_opacity
162         && style->fill.type != SP_PAINT_TYPE_NONE
163         && style->stroke.type != SP_PAINT_TYPE_NONE)
164         _state->merge_opacity = FALSE;
167 /**
168  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
169  * 
170  * \param width     width of the surface to be created
171  * \param height    height of the surface to be created
172  */
173 CairoRenderContext*
174 CairoRenderContext::cloneMe(double width, double height) const
176     g_assert( _is_valid );
177     g_assert( width > 0.0 && height > 0.0 );
179     CairoRenderContext *new_context = _renderer->createContext();
180     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
181                                                             (int)ceil(width), (int)ceil(height));
182     new_context->_cr = cairo_create(surface);
183     new_context->_surface = surface;
184     new_context->_is_valid = TRUE;
186     return new_context;
189 CairoRenderContext*
190 CairoRenderContext::cloneMe(void) const
192     g_assert( _is_valid );
194     return cloneMe(_width, _height);
197 bool
198 CairoRenderContext::setImageTarget(cairo_format_t format)
200     // format cannot be set on an already initialized surface
201     if (_is_valid)
202         return false;
204     switch (format) {
205         case CAIRO_FORMAT_ARGB32:
206         case CAIRO_FORMAT_RGB24:
207         case CAIRO_FORMAT_A8:
208         case CAIRO_FORMAT_A1:
209             _target_format = format;
210             _target = CAIRO_SURFACE_TYPE_IMAGE;
211             return true;
212             break;
213         default:
214             break;
215     }
217     return false;
220 bool
221 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
223 #ifndef CAIRO_HAS_PDF_SURFACE
224     return false;
225 #else
226     _target = CAIRO_SURFACE_TYPE_PDF;
227     _vector_based_target = TRUE;
228 #endif
230     FILE *osf = NULL;
231     FILE *osp = NULL;
233     gsize bytesRead = 0;
234     gsize bytesWritten = 0;
235     GError *error = NULL;
236     gchar *local_fn = g_filename_from_utf8(utf8_fn,
237                                            -1,  &bytesRead,  &bytesWritten, &error);
238     gchar const *fn = local_fn;
239     
240     /* TODO: Replace the below fprintf's with something that does the right thing whether in
241     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
242     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
243     * return code.
244     */
245     if (fn != NULL) {
246         if (*fn == '|') {
247             fn += 1;
248             while (isspace(*fn)) fn += 1;
249 #ifndef WIN32
250             osp = popen(fn, "w");
251 #else
252             osp = _popen(fn, "w");
253 #endif
254             if (!osp) {
255                 fprintf(stderr, "inkscape: popen(%s): %s\n",
256                         fn, strerror(errno));
257                 return false;
258             }
259             _stream = osp;
260         } else if (*fn == '>') {
261             fn += 1;
262             while (isspace(*fn)) fn += 1;
263             Inkscape::IO::dump_fopen_call(fn, "K");
264             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
265             if (!osf) {
266                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
267                         fn, strerror(errno));
268                 return false;
269             }
270             _stream = osf;
271         } else {
272             /* put cwd stuff in here */
273             gchar *qn = ( *fn
274                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
275                 : g_strdup("lpr") );
276 #ifndef WIN32
277             osp = popen(qn, "w");
278 #else
279             osp = _popen(qn, "w");
280 #endif
281             if (!osp) {
282                 fprintf(stderr, "inkscape: popen(%s): %s\n",
283                         qn, strerror(errno));
284                 return false;
285             }
286             g_free(qn);
287             _stream = osp;
288         }
289     }
291     g_free(local_fn);
293     if (_stream) {
294         /* fixme: this is kinda icky */
295 #if !defined(_WIN32) && !defined(__WIN32__)
296         (void) signal(SIGPIPE, SIG_IGN);
297 #endif
298     }
299             
300     return true;
303 cairo_surface_t*
304 CairoRenderContext::getSurface(void)
306     g_assert( _is_valid );
307     
308     return _surface;
311 bool
312 CairoRenderContext::saveAsPng(const char *file_name)
314     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
315     if (status)
316         return false;
317     else
318         return true;
321 void
322 CairoRenderContext::setRenderMode(CairoRenderMode mode)
324     switch (mode) {
325         case RENDER_MODE_NORMAL:
326         case RENDER_MODE_CLIP:
327             _render_mode = mode;
328             break;
329         default:
330             _render_mode = RENDER_MODE_NORMAL;
331             break;
332     }
335 CairoRenderContext::CairoRenderMode
336 CairoRenderContext::getRenderMode(void) const
338     return _render_mode;
341 void
342 CairoRenderContext::setClipMode(CairoClipMode mode)
344     switch (mode) {
345         case CLIP_MODE_PATH:
346         case CLIP_MODE_MASK:
347             _clip_mode = mode;
348             break;
349         default:
350             _clip_mode = CLIP_MODE_PATH;
351             break;
352     }
355 CairoRenderContext::CairoClipMode
356 CairoRenderContext::getClipMode(void) const
358     return _clip_mode;
361 CairoRenderState*
362 CairoRenderContext::_createState(void)
364     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
365     g_assert( state != NULL );
367     state->merge_opacity = TRUE;
368     state->opacity = 1.0;
369     state->need_layer = FALSE;
370     state->has_overflow = FALSE;
371     state->parent_has_userspace = FALSE;
372     state->clip_path = NULL;
373     state->mask = NULL;
375     return state;
378 void
379 CairoRenderContext::pushLayer(void)
381     g_assert( _is_valid );
383     TRACE(("--pushLayer\n"));
384     cairo_push_group(_cr);
385                 
386     // clear buffer
387     if (!_vector_based_target) {
388         cairo_save(_cr);
389         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
390         cairo_paint(_cr);
391         cairo_restore(_cr);
392     }
395 void
396 CairoRenderContext::popLayer(void)
398     g_assert( _is_valid );
400     float opacity = _state->opacity;
401     TRACE(("--popLayer w/ %f\n", opacity));
403     // apply clipPath or mask if present
404     SPClipPath *clip_path = _state->clip_path;
405     SPMask *mask = _state->mask;
406     if (clip_path || mask) {
408         CairoRenderContext *clip_ctx;
409         cairo_surface_t *clip_mask;
411         if (clip_path) {
412             if (_render_mode == RENDER_MODE_CLIP)
413                 mask = NULL;    // disable mask when performing nested clipping
415             if (_vector_based_target) {
416                 setClipMode(CLIP_MODE_PATH);
417                 if (!mask) {
418                     cairo_pop_group_to_source(_cr);
419                     _renderer->applyClipPath(this, clip_path);
420                     if (opacity == 1.0)
421                         cairo_paint(_cr);
422                     else
423                         cairo_paint_with_alpha(_cr, opacity);
425                 } else {
426                     // the clipPath will be applied before masking
427                 }
428             } else {
430                 // setup a new rendering context
431                 clip_ctx = _renderer->createContext();
432                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
433                 clip_ctx->setClipMode(CLIP_MODE_MASK);
434                 if (!clip_ctx->setupSurface(_width, _height)) {
435                     TRACE(("setupSurface failed\n"));
436                     _renderer->destroyContext(clip_ctx);
437                     return;
438                 }
440                 // clear buffer
441                 cairo_save(clip_ctx->_cr);
442                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
443                 cairo_paint(clip_ctx->_cr);
444                 cairo_restore(clip_ctx->_cr);
446                 // if a mask won't be applied set opacity too
447                 if (!mask)
448                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
449                 else
450                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
452                 // copy over the correct CTM
453                 if (_state->parent_has_userspace)
454                     clip_ctx->setTransform(&getParentState()->transform);
455                 else
456                     clip_ctx->setTransform(&_state->transform);
458                 // apply the clip path
459                 clip_ctx->pushState();
460                 _renderer->applyClipPath(clip_ctx, clip_path);
461                 clip_ctx->popState();
463                 clip_mask = clip_ctx->getSurface();
464                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
466                 if (!mask) {
467                     cairo_pop_group_to_source(_cr);
468                     cairo_mask_surface(_cr, clip_mask, 0, 0);
469                     _renderer->destroyContext(clip_ctx);
470                 }
471             }
472         }
474         if (mask) {
475             // create rendering context for mask
476             CairoRenderContext *mask_ctx = _renderer->createContext();
477             mask_ctx->setupSurface(_width, _height);
479             // set rendering mode to normal
480             setRenderMode(RENDER_MODE_NORMAL);
482             // copy the correct CTM to mask context
483             if (_state->parent_has_userspace)
484                 mask_ctx->setTransform(&getParentState()->transform);
485             else
486                 mask_ctx->setTransform(&_state->transform);
488             // render mask contents to mask_ctx
489             _renderer->applyMask(mask_ctx, mask);
491             TEST(mask_ctx->saveAsPng("mask.png"));
493             // composite with clip mask
494             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
495                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
496                 _renderer->destroyContext(clip_ctx);
497             }
499             cairo_surface_t *mask_image = mask_ctx->getSurface();
500             int width = cairo_image_surface_get_width(mask_image);
501             int height = cairo_image_surface_get_height(mask_image);
502             int stride = cairo_image_surface_get_stride(mask_image);
503             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
505             // premultiply with opacity
506             if (_state->opacity != 1.0) {
507                 TRACE(("premul w/ %f\n", opacity));
508                 guint8 int_opacity = (guint8)(255 * opacity);
509                 for (int row = 0 ; row < height; row++) {
510                     unsigned char *row_data = pixels + (row * stride);
511                     for (int i = 0 ; i < width; i++) {
512                         guint32 *pixel = (guint32 *)row_data + i;
513                         *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
514                                 ((*pixel & 0x0000ff00) >>  8) * 46518 +
515                                 ((*pixel & 0x000000ff)      ) * 4688) * 
516                                 int_opacity);
517                     }
518                 }
519             }
521             cairo_pop_group_to_source(_cr);
522             if (_clip_mode == CLIP_MODE_PATH) {
523                 // we have to do the clipping after cairo_pop_group_to_source
524                 _renderer->applyClipPath(this, clip_path);
525             }
526             // apply the mask onto the layer
527             cairo_mask_surface(_cr, mask_image, 0, 0);
528             _renderer->destroyContext(mask_ctx);
529         }
530     } else {
531         cairo_pop_group_to_source(_cr);
532         if (opacity == 1.0)
533             cairo_paint(_cr);
534         else
535             cairo_paint_with_alpha(_cr, opacity);
536     }
539 void
540 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
542     g_assert( _is_valid );
544     // here it should be checked whether the current clip winding changed
545     // so we could switch back to masked clipping
546     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
547         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
548     } else {
549         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
550     }
551     addBpath(bp);
554 void
555 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
557     g_assert( _is_valid );
559     cairo_rectangle(_cr, x, y, width, height);
560     cairo_clip(_cr);
563 bool
564 CairoRenderContext::setupSurface(double width, double height)
566     if (_vector_based_target && _stream == NULL)
567         return false;
569     cairo_surface_t *surface = NULL;
570     switch (_target) {
571         case CAIRO_SURFACE_TYPE_IMAGE:
572             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
573             break;
574 #ifdef CAIRO_HAS_PDF_SURFACE
575         case CAIRO_SURFACE_TYPE_PDF:
576             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
577             break;
578 #endif
579 #ifdef CAIRO_HAS_PS_SURFACE
580         case CAIRO_SURFACE_TYPE_PS:
581             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
582             break;
583 #endif
584         default:
585             return false;
586             break;
587     }
589     _cr = cairo_create(surface);
590     _surface = surface;
592     if (_vector_based_target) {
593         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
594     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
595         // set background color on non-alpha surfaces
596         // TODO: bgcolor should be derived from SPDocument
597         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
598         cairo_rectangle(_cr, 0, 0, _width, _height);
599         cairo_fill(_cr);
600     }
602     _is_valid = TRUE;
604     return true;
607 bool
608 CairoRenderContext::finish(void)
610     g_assert( _is_valid );
611     
612     if (_vector_based_target)
613         cairo_show_page(_cr);
614     
615     cairo_destroy(_cr);
616     cairo_surface_destroy(_surface);
617     _cr = NULL;
618     _surface = NULL;
619     
620     if (_layout)
621         g_object_unref(_layout);
622     
623     _is_valid = FALSE;
624     
625     if (_vector_based_target) {
626         /* Flush stream to be sure. */
627         (void) fflush(_stream);
628     
629         fclose(_stream);
630         _stream = NULL;
631     }
633     return true;
636 void
637 CairoRenderContext::transform(NRMatrix const *transform)
639     g_assert( _is_valid );
641     cairo_matrix_t matrix;
642     _initCairoMatrix(&matrix, transform);
643     cairo_transform(_cr, &matrix);
644     
645     // store new CTM
646     getTransform(&_state->transform);
649 void
650 CairoRenderContext::setTransform(NRMatrix const *transform)
652     g_assert( _is_valid );
653     
654     cairo_matrix_t matrix;
655     _initCairoMatrix(&matrix, transform);
656     cairo_set_matrix(_cr, &matrix);
657     _state->transform = *transform;
660 void
661 CairoRenderContext::getTransform(NRMatrix *copy) const
663     g_assert( _is_valid );
665     cairo_matrix_t ctm;
666     cairo_get_matrix(_cr, &ctm);
667     (*copy)[0] = ctm.xx;
668     (*copy)[1] = ctm.yx;
669     (*copy)[2] = ctm.xy;
670     (*copy)[3] = ctm.yy;
671     (*copy)[4] = ctm.x0;
672     (*copy)[5] = ctm.y0;
675 void
676 CairoRenderContext::getParentTransform(NRMatrix *copy) const
678     g_assert( _is_valid );
680     CairoRenderState *parent_state = getParentState();
681     memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
684 void
685 CairoRenderContext::pushState(void)
687     g_assert( _is_valid );
689     cairo_save(_cr);
691     CairoRenderState *new_state = _createState();
692     // copy current state's transform
693     new_state->transform = _state->transform;
694     _state_stack = g_slist_prepend(_state_stack, new_state);
695     _state = new_state;
698 void
699 CairoRenderContext::popState(void)
701     g_assert( _is_valid );
702     
703     cairo_restore(_cr);
704     
705     g_free(_state_stack->data);
706     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
707     _state = (CairoRenderState*)_state_stack->data;
709     g_assert( g_slist_length(_state_stack) > 0 );
712 static bool pattern_hasItemChildren (SPPattern *pat)
714     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
715         if (SP_IS_ITEM (child)) {
716             return true;
717         }
718     }
719     return false;
722 cairo_pattern_t*
723 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
725     g_assert( SP_IS_PATTERN(paintserver) );
726         
727     SPPattern *pat = SP_PATTERN (paintserver);
728     
729     NRMatrix ps2user, pcs2dev;
730     nr_matrix_set_identity(&ps2user);
731     nr_matrix_set_identity(&pcs2dev);
732     
733     double x = pattern_x(pat);
734     double y = pattern_y(pat);
735     double width = pattern_width(pat);
736     double height = pattern_height(pat);
737     double bbox_width_scaler;
738     double bbox_height_scaler;
740     TRACE(("%f x %f pattern\n", width, height));
741     
742     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
743         //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
744         bbox_width_scaler = pbox->x1 - pbox->x0;
745         bbox_height_scaler = pbox->y1 - pbox->y0;
746         ps2user[4] = x * bbox_width_scaler + pbox->x0;
747         ps2user[5] = y * bbox_height_scaler + pbox->y0;
748     } else {
749         bbox_width_scaler = 1.0;
750         bbox_height_scaler = 1.0;
751         ps2user[4] = x;
752         ps2user[5] = y;
753     }
754     
755     // apply pattern transformation
756     NRMatrix pattern_transform(pattern_patternTransform(pat));
757     nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
759     // create pattern contents coordinate system
760     if (pat->viewBox_set) {
761         NRRect *view_box = pattern_viewBox(pat);
762         
763         double x, y, w, h;
764         double view_width, view_height;
765         x = 0;
766         y = 0;
767         w = width * bbox_width_scaler;
768         h = height * bbox_height_scaler;
770         view_width = view_box->x1 - view_box->x0;
771         view_height = view_box->y1 - view_box->y0;
773         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
774         pcs2dev[0] = w / view_width;
775         pcs2dev[3] = h / view_height;
776         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
777         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
778     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
779         pcs2dev[0] = pbox->x1 - pbox->x0;
780         pcs2dev[3] = pbox->y1 - pbox->y0;
781     }
782     
783     // calculate the size of the surface which has to be created
784     // the scaling needs to be taken into account in the ctm after the pattern transformation
785     NRMatrix temp;
786     nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
787     double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
788     double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
790     if (_vector_based_target) {
791         // eliminate PT_PER_PX mul from these
792         width_scaler *= 1.25;
793         height_scaler *= 1.25;
794     }
795     double surface_width = ceil(bbox_width_scaler * width_scaler * width);
796     double surface_height = ceil(bbox_height_scaler * height_scaler * height);
797     TRACE(("surface size: %f x %f\n", surface_width, surface_height));
798     // create new rendering context
799     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
801     // adjust the size of the painted pattern to fit exactly the created surface
802     // this has to be done because of the rounding to obtain an integer pattern surface width/height
803     double scale_width = surface_width / (bbox_width_scaler * width);
804     double scale_height = surface_height / (bbox_height_scaler * height);
805     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
806         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
807         NRMatrix scale;
808         nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
809         nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
811         nr_matrix_set_scale(&scale, scale_width, scale_height);
812         nr_matrix_multiply(&ps2user, &ps2user, &scale);
813     }
815     pattern_ctx->setTransform(&pcs2dev);
816     pattern_ctx->pushState();
817     
818     // create arena and group
819     NRArena *arena = NRArena::create();
820     unsigned dkey = sp_item_display_key_new(1);
821     
822     // show items and render them
823     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
824         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
825             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
826                 if (SP_IS_ITEM (child)) {
827                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
828                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
829                 }
830             }
831             break; // do not go further up the chain if children are found
832         }
833     }
834     
835     pattern_ctx->popState();
836     
837     // setup a cairo_pattern_t
838     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
839     TEST(pattern_ctx->saveAsPng("pattern.png"));
840     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
841     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
843     // set pattern transformation
844     cairo_matrix_t pattern_matrix;
845     _initCairoMatrix(&pattern_matrix, &ps2user);
846     cairo_matrix_invert(&pattern_matrix);
847     cairo_pattern_set_matrix(result, &pattern_matrix);
848     
849     delete pattern_ctx;
850     
851     // hide all items
852     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
853         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
854             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
855                 if (SP_IS_ITEM (child)) {
856                     sp_item_invoke_hide (SP_ITEM (child), dkey);
857                 }
858             }
859             break; // do not go further up the chain if children are found
860         }
861     }
862     
863     return result;
866 cairo_pattern_t*
867 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
868                                                  NRRect const *pbox, float alpha)
870     cairo_pattern_t *pattern = NULL;
871     bool apply_bbox2user = FALSE;
872     
873     if (SP_IS_LINEARGRADIENT (paintserver)) {
875             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
876             
877             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
879             NR::Point p1 (lg->x1.computed, lg->y1.computed);
880             NR::Point p2 (lg->x2.computed, lg->y2.computed);
881             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
882                 // convert to userspace
883                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
884                 p1 *= bbox2user;
885                 p2 *= bbox2user;
886             }
887             
888             // create linear gradient pattern
889             pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
890             
891             // add stops
892             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
893                 float rgb[3];
894                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
895                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
896             }
897     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
899         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
901         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
903         NR::Point c (rg->cx.computed, rg->cy.computed);
904         NR::Point f (rg->fx.computed, rg->fy.computed);
905         double r = rg->r.computed;
906         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
907             apply_bbox2user = true;
908         
909         // create radial gradient pattern
910         pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
912         // add stops
913         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
914             float rgb[3];
915             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
916             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
917         }
918     } else if (SP_IS_PATTERN (paintserver)) {
920         pattern = _createPatternPainter(paintserver, pbox);
921     } else {
922         return NULL;
923     }
924         
925     if (pattern && SP_IS_GRADIENT (paintserver)) {
926         SPGradient *g = SP_GRADIENT(paintserver);
928         // set extend type
929         SPGradientSpread spread = sp_gradient_get_spread(g);
930         switch (spread) {
931             case SP_GRADIENT_SPREAD_REPEAT: {
932                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
933                 break;
934             }
935             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
936                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
937                 break;
938             }
939             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
940                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
941                 break;
942             }
943             default: {
944                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
945                 break;
946             }
947         }
949         cairo_matrix_t pattern_matrix;
950         if (g->gradientTransform_set) {
951             // apply gradient transformation
952             cairo_matrix_init(&pattern_matrix,
953                 g->gradientTransform[0], g->gradientTransform[1],
954                 g->gradientTransform[2], g->gradientTransform[3],
955                 g->gradientTransform[4], g->gradientTransform[5]);
956         } else {
957             cairo_matrix_init_identity (&pattern_matrix);
958         }
960         if (apply_bbox2user) {
961             // convert to userspace
962             cairo_matrix_t bbox2user;
963             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
964             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
965         }
966         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
967         cairo_pattern_set_matrix(pattern, &pattern_matrix);
968     }
969         
970     return pattern;
973 void
974 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
976     g_return_if_fail( style->fill.type == SP_PAINT_TYPE_COLOR
977                       || style->fill.type == SP_PAINT_TYPE_PAINTSERVER );
978     
979     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
980     if (_state->merge_opacity) {
981         alpha *= _state->opacity;
982         TRACE(("merged op=%f\n", alpha));
983     }
984     
985     if (style->fill.type == SP_PAINT_TYPE_COLOR) {
986         float rgb[3];
987         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
989         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
990     } else {
991         g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
992                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
993                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
994         
995         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
996         
997         if (pattern) {
998             cairo_set_source(_cr, pattern);
999             cairo_pattern_destroy(pattern);
1000         }
1001     }
1004 void
1005 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1007     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1008     if (_state->merge_opacity)
1009         alpha *= _state->opacity;
1010     
1011     if (style->stroke.type == SP_PAINT_TYPE_COLOR) {
1012         float rgb[3];
1013         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1014       
1015         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1016     } else {
1017         g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
1018                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1019                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1020                 
1021         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1022         
1023         if (pattern) {
1024             cairo_set_source(_cr, pattern);
1025             cairo_pattern_destroy(pattern);
1026         }
1027     }
1029     if (style->stroke_dash.n_dash   &&
1030         style->stroke_dash.dash       )
1031     {
1032         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1033     } else {
1034         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1035     }
1036     
1037     cairo_set_line_width(_cr, style->stroke_width.computed);
1039     // set line join type
1040     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1041     switch (style->stroke_linejoin.computed) {
1042         case SP_STROKE_LINEJOIN_MITER:
1043             join = CAIRO_LINE_JOIN_MITER;
1044             break;
1045         case SP_STROKE_LINEJOIN_ROUND:
1046             join = CAIRO_LINE_JOIN_ROUND;
1047             break;
1048         case SP_STROKE_LINEJOIN_BEVEL:
1049             join = CAIRO_LINE_JOIN_BEVEL;
1050             break;
1051     }           
1052     cairo_set_line_join(_cr, join);
1053     
1054     // set line cap type
1055     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1056     switch (style->stroke_linecap.computed) {
1057         case SP_STROKE_LINECAP_BUTT:
1058             cap = CAIRO_LINE_CAP_BUTT;
1059             break;
1060         case SP_STROKE_LINECAP_ROUND:
1061             cap = CAIRO_LINE_CAP_ROUND;
1062             break;
1063         case SP_STROKE_LINECAP_SQUARE:
1064             cap = CAIRO_LINE_CAP_SQUARE;
1065             break;
1066     }           
1067     cairo_set_line_cap(_cr, cap);
1068     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1071 bool
1072 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1074     g_assert( _is_valid );
1076     if (_render_mode == RENDER_MODE_CLIP) {
1077         if (_clip_mode == CLIP_MODE_PATH) {
1078             addClipPath(bpath->path, &style->fill_rule);
1079         } else {
1080             setBpath(bpath->path);
1081             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1082                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1083             } else {
1084                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1085             }
1086             cairo_fill(_cr);
1087             cairo_surface_write_to_png (_surface, "gtar2.png");
1088         }
1089         return true;
1090     }
1092     if (style->fill.type == SP_PAINT_TYPE_NONE && style->stroke.type == SP_PAINT_TYPE_NONE)
1093         return true;
1095     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1096                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1098     if (!need_layer)
1099         cairo_save(_cr);
1100     else
1101         pushLayer();
1103     if (style->fill.type != SP_PAINT_TYPE_NONE) {
1104         _setFillStyle(style, pbox);
1105         setBpath(bpath->path);
1106         
1107         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1108             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1109         } else {
1110             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1111         }
1112     
1113         if (style->stroke.type == SP_PAINT_TYPE_NONE)
1114             cairo_fill(_cr);
1115         else
1116             cairo_fill_preserve(_cr);
1117     }
1118     
1119     if (style->stroke.type != SP_PAINT_TYPE_NONE) {
1120         _setStrokeStyle(style, pbox);
1121         if (style->fill.type == SP_PAINT_TYPE_NONE)
1122             setBpath(bpath->path);
1124         cairo_stroke(_cr);    
1125     }
1126     
1127     if (need_layer)
1128         popLayer();
1129     else
1130         cairo_restore(_cr);
1132     return true;
1135 bool
1136 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1137                                 NRMatrix const *image_transform, SPStyle const *style)
1139     g_assert( _is_valid );
1140     
1141     if (_render_mode == RENDER_MODE_CLIP)
1142         return true;
1143     
1144     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1145     if (!px_rgba)
1146         return false;
1147     
1148     float opacity;
1149     if (_state->merge_opacity)
1150         opacity = _state->opacity;
1151     else
1152         opacity = 1.0;
1153     
1154     // make a copy of the original pixbuf with premultiplied alpha
1155     // if we pass the original pixbuf it will get messed up
1156     for (unsigned i = 0; i < h; i++) {
1157         for (unsigned j = 0; j < w; j++) {
1158             guchar const *src = px + i * rs + j * 4;
1159             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1160             guchar r, g, b, alpha_dst;
1161             
1162             // calculate opacity-modified alpha
1163             alpha_dst = src[3];
1164             if (opacity != 1.0 && _vector_based_target)
1165                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1167             // premul alpha (needed because this will be undone by cairo-pdf)
1168             r = src[0]*alpha_dst/255;
1169             g = src[1]*alpha_dst/255;
1170             b = src[2]*alpha_dst/255;
1171             
1172             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1173         }
1174     }
1175     
1176     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1177     if (cairo_surface_status(image_surface)) {
1178         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1179         return false;
1180     }
1181     
1182     // setup automatic freeing of the image data when destroying the surface
1183     static cairo_user_data_key_t key;
1184     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1186     cairo_save(_cr);
1187     
1188     // scaling by width & height is not needed because it will be done by Cairo
1189     if (image_transform)
1190         transform(image_transform);
1192     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1194     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1195     if (_vector_based_target) {
1196         cairo_new_path(_cr);
1197         cairo_rectangle(_cr, 0, 0, w, h);
1198         cairo_clip(_cr);
1199     }
1201     if (_vector_based_target)
1202         cairo_paint(_cr);
1203     else
1204         cairo_paint_with_alpha(_cr, opacity);
1205     
1206     cairo_restore(_cr);
1208     cairo_surface_destroy(image_surface);
1209     
1210     return true;
1213 #define GLYPH_ARRAY_SIZE 64
1215 unsigned int
1216 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1218     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1219     cairo_glyph_t *glyphs = glyph_array;
1220     unsigned int num_glyphs = glyphtext.size();
1221     if (num_glyphs > GLYPH_ARRAY_SIZE)
1222         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1224     unsigned int num_invalid_glyphs = 0;
1225     unsigned int i = 0;
1226     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1227         // skip empty or unknown glyphs
1228         if (it_info->index == PANGO_GLYPH_EMPTY || it_info->index & PANGO_GLYPH_UNKNOWN_FLAG) {
1229             TRACE(("INVALID GLYPH found\n"));
1230             num_invalid_glyphs++;
1231             continue;
1232         }
1233         glyphs[i - num_invalid_glyphs].index = it_info->index;
1234         glyphs[i - num_invalid_glyphs].x = it_info->x;
1235         glyphs[i - num_invalid_glyphs].y = it_info->y;
1236         i++;
1237     }
1239     if (is_stroke)
1240         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1241     else
1242         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1244     if (num_glyphs > GLYPH_ARRAY_SIZE)
1245         g_free(glyphs);
1247     return num_glyphs - num_invalid_glyphs;
1250 bool
1251 CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1252                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1254     // create a cairo_font_face from PangoFont
1255     double size = style->font_size.computed;
1256     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1257     FcPattern *fc_pattern = fc_font->font_pattern;
1258     
1259     cairo_save(_cr);
1261     cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1262     cairo_set_font_face(_cr, font_face);
1263     
1264     if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1265         size = 12.0;
1266     
1267     // set the given font matrix
1268     cairo_matrix_t matrix;
1269     _initCairoMatrix(&matrix, font_matrix);
1270     cairo_set_font_matrix(_cr, &matrix);
1272     if (_render_mode == RENDER_MODE_CLIP) {
1273         if (_clip_mode == CLIP_MODE_MASK) {
1274             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1275                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1276             } else {
1277                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1278             }
1279             cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
1280             cairo_rectangle (_cr, 0, 0, 30, 40);
1281             cairo_fill (_cr);
1282             _showGlyphs(_cr, font, glyphtext, FALSE);
1283             //cairo_fill(_cr);
1284         } else {
1285             // just add the glyph paths to the current context
1286             _showGlyphs(_cr, font, glyphtext, TRUE);
1287         }
1288     } else {
1290         if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
1291             // set fill style
1292             _setFillStyle(style, NULL);
1294             _showGlyphs(_cr, font, glyphtext, FALSE);
1295         }
1297         if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
1298             // set stroke style
1299             _setStrokeStyle(style, NULL);
1301             // paint stroke
1302             _showGlyphs(_cr, font, glyphtext, TRUE);
1303             cairo_stroke(_cr);
1304         }
1305     }
1307     cairo_restore(_cr);
1309     cairo_font_face_destroy(font_face);
1310     
1311     return true;
1314 /* Helper functions */
1316 void
1317 CairoRenderContext::addBpath(NArtBpath const *bp)
1319     bool closed = false;
1320     while (bp->code != NR_END) {
1321         switch (bp->code) {
1322             case NR_MOVETO:
1323                 if (closed) {
1324                     cairo_close_path(_cr);
1325                 }
1326                 closed = true;
1327                 cairo_move_to(_cr, bp->x3, bp->y3);
1328                 break;
1329             case NR_MOVETO_OPEN:
1330                 if (closed) {
1331                     cairo_close_path(_cr);
1332                 }
1333                 closed = false;
1334                 cairo_move_to(_cr, bp->x3, bp->y3);
1335                 break;
1336             case NR_LINETO:
1337                 cairo_line_to(_cr, bp->x3, bp->y3);
1338                 break;
1339             case NR_CURVETO:
1340                 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1341                 break;
1342             default:
1343                 break;
1344         }
1345         bp += 1;
1346     }
1347     if (closed) {
1348         cairo_close_path(_cr);
1349     }
1352 void
1353 CairoRenderContext::setBpath(NArtBpath const *bp)
1355     cairo_new_path(_cr);
1356     if (bp)
1357         addBpath(bp);
1360 void
1361 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1363     cairo_matrix_t matrix;
1365     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0); 
1366     cairo_transform(cr, &matrix);
1369 void
1370 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1372     matrix->xx = (*transform)[0];
1373     matrix->yx = (*transform)[1];
1374     matrix->xy = (*transform)[2];
1375     matrix->yy = (*transform)[3];
1376     matrix->x0 = (*transform)[4];
1377     matrix->y0 = (*transform)[5];
1380 void
1381 CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1383     _concatTransform(cr, (*transform)[0], (*transform)[1],
1384                      (*transform)[2], (*transform)[3],
1385                      (*transform)[4], (*transform)[5]);
1388 static cairo_status_t
1389 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1391     size_t written;
1392     FILE *file = (FILE*)closure;
1394     written = fwrite (data, 1, length, file);
1396     if (written == length)
1397         return CAIRO_STATUS_SUCCESS;
1398     else
1399         return CAIRO_STATUS_WRITE_ERROR;
1402 #include "clear-n_.h"
1404 }  /* namespace Internal */
1405 }  /* namespace Extension */
1406 }  /* namespace Inkscape */
1408 #undef TRACE
1409 #undef TEST
1411 /* End of GNU GPL code */
1413 #endif /* HAVE_CAIRO_PDF */
1416 /*
1417   Local Variables:
1418   mode:c++
1419   c-file-style:"stroustrup"
1420   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1421   indent-tabs-mode:nil
1422   fill-column:99
1423   End:
1424 */
1425 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :