Code

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