1 #define __SP_CAIRO_RENDER_CONTEXT_C__
3 /** \file
4 * Rendering with Cairo.
5 */
6 /*
7 * Author:
8 * Miklos Erdelyi <erdelyim@gmail.com>
9 *
10 * Copyright (C) 2006 Miklos Erdelyi
11 *
12 * Licensed under GNU GPL
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #ifndef PANGO_ENABLE_BACKEND
20 #define PANGO_ENABLE_BACKEND
21 #endif
23 #ifndef PANGO_ENABLE_ENGINE
24 #define PANGO_ENABLE_ENGINE
25 #endif
28 #include <signal.h>
29 #include <errno.h>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 #define TRACE(_args)
84 //#define TEST(_args) _args
85 #define TEST(_args)
87 // FIXME: expose these from sp-clippath/mask.cpp
88 struct SPClipPathView {
89 SPClipPathView *next;
90 unsigned int key;
91 NRArenaItem *arenaitem;
92 NRRect bbox;
93 };
95 struct SPMaskView {
96 SPMaskView *next;
97 unsigned int key;
98 NRArenaItem *arenaitem;
99 NRRect bbox;
100 };
102 namespace Inkscape {
103 namespace Extension {
104 namespace Internal {
106 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
108 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
109 _dpi(72),
110 _pdf_level(0),
111 _ps_level(1),
112 _eps(false),
113 _is_texttopath(FALSE),
114 _is_filtertobitmap(FALSE),
115 _bitmapresolution(72),
116 _stream(NULL),
117 _is_valid(FALSE),
118 _vector_based_target(FALSE),
119 _cr(NULL), // Cairo context
120 _surface(NULL),
121 _target(CAIRO_SURFACE_TYPE_IMAGE),
122 _target_format(CAIRO_FORMAT_ARGB32),
123 _layout(NULL),
124 _state(NULL),
125 _renderer(parent),
126 _render_mode(RENDER_MODE_NORMAL),
127 _clip_mode(CLIP_MODE_MASK)
128 {}
130 CairoRenderContext::~CairoRenderContext(void)
131 {
132 if (_cr) cairo_destroy(_cr);
133 if (_surface) cairo_surface_destroy(_surface);
134 if (_layout) g_object_unref(_layout);
135 }
137 CairoRenderer*
138 CairoRenderContext::getRenderer(void) const
139 {
140 return _renderer;
141 }
143 CairoRenderState*
144 CairoRenderContext::getCurrentState(void) const
145 {
146 return _state;
147 }
149 CairoRenderState*
150 CairoRenderContext::getParentState(void) const
151 {
152 // if this is the root node just return it
153 if (g_slist_length(_state_stack) == 1) {
154 return _state;
155 } else {
156 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
157 }
158 }
160 void
161 CairoRenderContext::setStateForStyle(SPStyle const *style)
162 {
163 // only opacity & overflow is stored for now
164 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
165 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
166 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
168 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
169 _state->merge_opacity = FALSE;
171 // disable rendering of opacity if there's a stroke on the fill
172 if (_state->merge_opacity
173 && !style->fill.isNone()
174 && !style->stroke.isNone())
175 _state->merge_opacity = FALSE;
176 }
178 /**
179 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
180 *
181 * \param width width of the surface to be created
182 * \param height height of the surface to be created
183 */
184 CairoRenderContext*
185 CairoRenderContext::cloneMe(double width, double height) const
186 {
187 g_assert( _is_valid );
188 g_assert( width > 0.0 && height > 0.0 );
190 CairoRenderContext *new_context = _renderer->createContext();
191 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
192 (int)ceil(width), (int)ceil(height));
193 new_context->_cr = cairo_create(surface);
194 new_context->_surface = surface;
195 new_context->_width = width;
196 new_context->_height = height;
197 new_context->_is_valid = TRUE;
199 return new_context;
200 }
202 CairoRenderContext*
203 CairoRenderContext::cloneMe(void) const
204 {
205 g_assert( _is_valid );
207 return cloneMe(_width, _height);
208 }
210 bool
211 CairoRenderContext::setImageTarget(cairo_format_t format)
212 {
213 // format cannot be set on an already initialized surface
214 if (_is_valid)
215 return false;
217 switch (format) {
218 case CAIRO_FORMAT_ARGB32:
219 case CAIRO_FORMAT_RGB24:
220 case CAIRO_FORMAT_A8:
221 case CAIRO_FORMAT_A1:
222 _target_format = format;
223 _target = CAIRO_SURFACE_TYPE_IMAGE;
224 return true;
225 break;
226 default:
227 break;
228 }
230 return false;
231 }
233 bool
234 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
235 {
236 #ifndef CAIRO_HAS_PDF_SURFACE
237 return false;
238 #else
239 _target = CAIRO_SURFACE_TYPE_PDF;
240 _vector_based_target = TRUE;
241 #endif
243 FILE *osf = NULL;
244 FILE *osp = NULL;
246 gsize bytesRead = 0;
247 gsize bytesWritten = 0;
248 GError *error = NULL;
249 gchar *local_fn = g_filename_from_utf8(utf8_fn,
250 -1, &bytesRead, &bytesWritten, &error);
251 gchar const *fn = local_fn;
253 /* TODO: Replace the below fprintf's with something that does the right thing whether in
254 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
255 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
256 * return code.
257 */
258 if (fn != NULL) {
259 if (*fn == '|') {
260 fn += 1;
261 while (isspace(*fn)) fn += 1;
262 #ifndef WIN32
263 osp = popen(fn, "w");
264 #else
265 osp = _popen(fn, "w");
266 #endif
267 if (!osp) {
268 fprintf(stderr, "inkscape: popen(%s): %s\n",
269 fn, strerror(errno));
270 return false;
271 }
272 _stream = osp;
273 } else if (*fn == '>') {
274 fn += 1;
275 while (isspace(*fn)) fn += 1;
276 Inkscape::IO::dump_fopen_call(fn, "K");
277 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
278 if (!osf) {
279 fprintf(stderr, "inkscape: fopen(%s): %s\n",
280 fn, strerror(errno));
281 return false;
282 }
283 _stream = osf;
284 } else {
285 /* put cwd stuff in here */
286 gchar *qn = ( *fn
287 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
288 : g_strdup("lpr") );
289 #ifndef WIN32
290 osp = popen(qn, "w");
291 #else
292 osp = _popen(qn, "w");
293 #endif
294 if (!osp) {
295 fprintf(stderr, "inkscape: popen(%s): %s\n",
296 qn, strerror(errno));
297 return false;
298 }
299 g_free(qn);
300 _stream = osp;
301 }
302 }
304 g_free(local_fn);
306 if (_stream) {
307 /* fixme: this is kinda icky */
308 #if !defined(_WIN32) && !defined(__WIN32__)
309 (void) signal(SIGPIPE, SIG_IGN);
310 #endif
311 }
313 return true;
314 }
316 bool
317 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
318 {
319 #ifndef CAIRO_HAS_PS_SURFACE
320 return false;
321 #else
322 _target = CAIRO_SURFACE_TYPE_PS;
323 _vector_based_target = TRUE;
324 #endif
326 FILE *osf = NULL;
327 FILE *osp = NULL;
329 gsize bytesRead = 0;
330 gsize bytesWritten = 0;
331 GError *error = NULL;
332 gchar *local_fn = g_filename_from_utf8(utf8_fn,
333 -1, &bytesRead, &bytesWritten, &error);
334 gchar const *fn = local_fn;
336 /* TODO: Replace the below fprintf's with something that does the right thing whether in
337 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
338 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
339 * return code.
340 */
341 if (fn != NULL) {
342 if (*fn == '|') {
343 fn += 1;
344 while (isspace(*fn)) fn += 1;
345 #ifndef WIN32
346 osp = popen(fn, "w");
347 #else
348 osp = _popen(fn, "w");
349 #endif
350 if (!osp) {
351 fprintf(stderr, "inkscape: popen(%s): %s\n",
352 fn, strerror(errno));
353 return false;
354 }
355 _stream = osp;
356 } else if (*fn == '>') {
357 fn += 1;
358 while (isspace(*fn)) fn += 1;
359 Inkscape::IO::dump_fopen_call(fn, "K");
360 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
361 if (!osf) {
362 fprintf(stderr, "inkscape: fopen(%s): %s\n",
363 fn, strerror(errno));
364 return false;
365 }
366 _stream = osf;
367 } else {
368 /* put cwd stuff in here */
369 gchar *qn = ( *fn
370 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
371 : g_strdup("lpr") );
372 #ifndef WIN32
373 osp = popen(qn, "w");
374 #else
375 osp = _popen(qn, "w");
376 #endif
377 if (!osp) {
378 fprintf(stderr, "inkscape: popen(%s): %s\n",
379 qn, strerror(errno));
380 return false;
381 }
382 g_free(qn);
383 _stream = osp;
384 }
385 }
387 g_free(local_fn);
389 if (_stream) {
390 /* fixme: this is kinda icky */
391 #if !defined(_WIN32) && !defined(__WIN32__)
392 (void) signal(SIGPIPE, SIG_IGN);
393 #endif
394 }
396 return true;
397 }
399 void CairoRenderContext::setPSLevel(unsigned int level)
400 {
401 _ps_level = level;
402 }
404 void CairoRenderContext::setEPS(bool eps)
405 {
406 _eps = eps;
407 }
409 unsigned int CairoRenderContext::getPSLevel(void)
410 {
411 return _ps_level;
412 }
414 void CairoRenderContext::setPDFLevel(unsigned int level)
415 {
416 _pdf_level = level;
417 }
419 void CairoRenderContext::setTextToPath(bool texttopath)
420 {
421 _is_texttopath = texttopath;
422 }
424 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
425 {
426 _is_filtertobitmap = filtertobitmap;
427 }
429 bool CairoRenderContext::getFilterToBitmap(void)
430 {
431 return _is_filtertobitmap;
432 }
434 void CairoRenderContext::setBitmapResolution(int resolution)
435 {
436 _bitmapresolution = resolution;
437 }
439 int CairoRenderContext::getBitmapResolution(void)
440 {
441 return _bitmapresolution;
442 }
444 cairo_surface_t*
445 CairoRenderContext::getSurface(void)
446 {
447 g_assert( _is_valid );
449 return _surface;
450 }
452 bool
453 CairoRenderContext::saveAsPng(const char *file_name)
454 {
455 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
456 if (status)
457 return false;
458 else
459 return true;
460 }
462 void
463 CairoRenderContext::setRenderMode(CairoRenderMode mode)
464 {
465 switch (mode) {
466 case RENDER_MODE_NORMAL:
467 case RENDER_MODE_CLIP:
468 _render_mode = mode;
469 break;
470 default:
471 _render_mode = RENDER_MODE_NORMAL;
472 break;
473 }
474 }
476 CairoRenderContext::CairoRenderMode
477 CairoRenderContext::getRenderMode(void) const
478 {
479 return _render_mode;
480 }
482 void
483 CairoRenderContext::setClipMode(CairoClipMode mode)
484 {
485 switch (mode) {
486 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
487 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
488 _clip_mode = mode;
489 break;
490 default:
491 _clip_mode = CLIP_MODE_PATH;
492 break;
493 }
494 }
496 CairoRenderContext::CairoClipMode
497 CairoRenderContext::getClipMode(void) const
498 {
499 return _clip_mode;
500 }
502 CairoRenderState*
503 CairoRenderContext::_createState(void)
504 {
505 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
506 g_assert( state != NULL );
508 state->has_filtereffect = FALSE;
509 state->merge_opacity = TRUE;
510 state->opacity = 1.0;
511 state->need_layer = FALSE;
512 state->has_overflow = FALSE;
513 state->parent_has_userspace = FALSE;
514 state->clip_path = NULL;
515 state->mask = NULL;
517 return state;
518 }
520 void
521 CairoRenderContext::pushLayer(void)
522 {
523 g_assert( _is_valid );
525 TRACE(("--pushLayer\n"));
526 cairo_push_group(_cr);
528 // clear buffer
529 if (!_vector_based_target) {
530 cairo_save(_cr);
531 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
532 cairo_paint(_cr);
533 cairo_restore(_cr);
534 }
535 }
537 void
538 CairoRenderContext::popLayer(void)
539 {
540 g_assert( _is_valid );
542 float opacity = _state->opacity;
543 TRACE(("--popLayer w/ opacity %f\n", opacity));
545 /*
546 At this point, the Cairo source is ready. A Cairo mask must be created if required.
547 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
548 masks independently of the objects they effect while in SVG the clip paths and masks
549 are defined relative to the objects they are attached to.
550 Notes:
551 1. An SVG object may have both a clip path and a mask!
552 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
553 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
554 the page (document). This is also not handled properly.
555 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
556 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
557 alpha. This is handled here by doing a pixel by pixel conversion.
558 */
560 SPClipPath *clip_path = _state->clip_path;
561 SPMask *mask = _state->mask;
562 if (clip_path || mask) {
564 CairoRenderContext *clip_ctx = 0;
565 cairo_surface_t *clip_mask = 0;
567 // Apply any clip path first
568 if (clip_path) {
569 TRACE((" Applying clip\n"));
570 if (_render_mode == RENDER_MODE_CLIP)
571 mask = NULL; // disable mask when performing nested clipping
573 if (_vector_based_target) {
574 setClipMode(CLIP_MODE_PATH); // Vector
575 if (!mask) {
576 cairo_pop_group_to_source(_cr);
577 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
578 if (opacity == 1.0)
579 cairo_paint(_cr);
580 else
581 cairo_paint_with_alpha(_cr, opacity);
583 } else {
584 // the clipPath will be applied before masking
585 }
586 } else {
588 // setup a new rendering context
589 clip_ctx = _renderer->createContext();
590 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
591 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
592 // This code ties the clipping to the document coordinates. It doesn't allow
593 // for a clipped object intially drawn off the page and then translated onto
594 // the page.
595 if (!clip_ctx->setupSurface(_width, _height)) {
596 TRACE(("clip: setupSurface failed\n"));
597 _renderer->destroyContext(clip_ctx);
598 return;
599 }
601 // clear buffer
602 cairo_save(clip_ctx->_cr);
603 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
604 cairo_paint(clip_ctx->_cr);
605 cairo_restore(clip_ctx->_cr);
607 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
608 if (!mask)
609 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
610 else
611 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
613 // copy over the correct CTM
614 // It must be stored in item_transform of current state after pushState.
615 Geom::Matrix item_transform;
616 if (_state->parent_has_userspace)
617 item_transform = getParentState()->transform * _state->item_transform;
618 else
619 item_transform = _state->item_transform;
621 // apply the clip path
622 clip_ctx->pushState();
623 clip_ctx->getCurrentState()->item_transform = item_transform;
624 _renderer->applyClipPath(clip_ctx, clip_path);
625 clip_ctx->popState();
627 clip_mask = clip_ctx->getSurface();
628 TEST(clip_ctx->saveAsPng("clip_mask.png"));
630 if (!mask) {
631 cairo_pop_group_to_source(_cr);
632 cairo_mask_surface(_cr, clip_mask, 0, 0);
633 _renderer->destroyContext(clip_ctx);
634 }
635 }
636 }
638 // Apply any mask second
639 if (mask) {
640 TRACE((" Applying mask\n"));
641 // create rendering context for mask
642 CairoRenderContext *mask_ctx = _renderer->createContext();
644 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
645 // Cairo surface is expecting the mask to be 90 dpi.
646 float surface_width = _width;
647 float surface_height = _height;
648 if( _vector_based_target ) {
649 surface_width *= 1.25;
650 surface_height *= 1.25;
651 }
652 mask_ctx->setupSurface( surface_width, surface_height );
653 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
655 // set rendering mode to normal
656 setRenderMode(RENDER_MODE_NORMAL);
658 // copy the correct CTM to mask context
659 /*
660 if (_state->parent_has_userspace)
661 mask_ctx->setTransform(&getParentState()->transform);
662 else
663 mask_ctx->setTransform(&_state->transform);
664 */
665 // This is probably not correct... but it seems to do the trick.
666 mask_ctx->setTransform(&_state->item_transform);
668 // render mask contents to mask_ctx
669 _renderer->applyMask(mask_ctx, mask);
671 TEST(mask_ctx->saveAsPng("mask.png"));
673 // composite with clip mask
674 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
675 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
676 _renderer->destroyContext(clip_ctx);
677 }
679 cairo_surface_t *mask_image = mask_ctx->getSurface();
680 int width = cairo_image_surface_get_width(mask_image);
681 int height = cairo_image_surface_get_height(mask_image);
682 int stride = cairo_image_surface_get_stride(mask_image);
683 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
685 // premultiply with opacity
686 // In SVG, the rgb channels as well as the alpha channel is used in masking.
687 // In Cairo, only the alpha channel is used thus requiring this conversion.
688 TRACE(("premul w/ %f\n", opacity));
689 guint8 int_opacity = (guint8)(255 * opacity);
690 for (int row = 0 ; row < height; row++) {
691 unsigned char *row_data = pixels + (row * stride);
692 for (int i = 0 ; i < width; i++) {
693 guint32 *pixel = (guint32 *)row_data + i;
694 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
695 ((*pixel & 0x0000ff00) >> 8) * 46518 +
696 ((*pixel & 0x000000ff) ) * 4688) *
697 int_opacity);
698 }
699 }
701 cairo_pop_group_to_source(_cr);
702 if (_clip_mode == CLIP_MODE_PATH) {
703 // we have to do the clipping after cairo_pop_group_to_source
704 _renderer->applyClipPath(this, clip_path);
705 }
706 // apply the mask onto the layer
707 cairo_mask_surface(_cr, mask_image, 0, 0);
708 _renderer->destroyContext(mask_ctx);
709 }
710 } else {
711 // No clip path or mask
712 cairo_pop_group_to_source(_cr);
713 if (opacity == 1.0)
714 cairo_paint(_cr);
715 else
716 cairo_paint_with_alpha(_cr, opacity);
717 }
718 }
720 void
721 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
722 {
723 g_assert( _is_valid );
725 // here it should be checked whether the current clip winding changed
726 // so we could switch back to masked clipping
727 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
728 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
729 } else {
730 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
731 }
732 addPathVector(pv);
733 }
735 void
736 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
737 {
738 g_assert( _is_valid );
740 cairo_rectangle(_cr, x, y, width, height);
741 cairo_clip(_cr);
742 }
744 bool
745 CairoRenderContext::setupSurface(double width, double height)
746 {
747 // Is the surface already set up?
748 if (_is_valid)
749 return true;
751 if (_vector_based_target && _stream == NULL)
752 return false;
754 _width = width;
755 _height = height;
757 cairo_surface_t *surface = NULL;
758 switch (_target) {
759 case CAIRO_SURFACE_TYPE_IMAGE:
760 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
761 break;
762 #ifdef CAIRO_HAS_PDF_SURFACE
763 case CAIRO_SURFACE_TYPE_PDF:
764 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
765 break;
766 #endif
767 #ifdef CAIRO_HAS_PS_SURFACE
768 case CAIRO_SURFACE_TYPE_PS:
769 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
770 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
771 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
772 return FALSE;
773 }
774 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
775 cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
776 #endif
777 break;
778 #endif
779 default:
780 return false;
781 break;
782 }
784 return _finishSurfaceSetup (surface);
785 }
787 bool
788 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
789 {
790 if (_is_valid || !surface)
791 return false;
793 _vector_based_target = is_vector;
794 bool ret = _finishSurfaceSetup (surface, ctm);
795 if (ret)
796 cairo_surface_reference (surface);
797 return ret;
798 }
800 bool
801 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
802 {
803 if(surface == NULL) {
804 return false;
805 }
806 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
807 return false;
808 }
810 _cr = cairo_create(surface);
811 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
812 return false;
813 }
814 if (ctm)
815 cairo_set_matrix(_cr, ctm);
816 _surface = surface;
818 if (_vector_based_target) {
819 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
820 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
821 // set background color on non-alpha surfaces
822 // TODO: bgcolor should be derived from SPDocument
823 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
824 cairo_rectangle(_cr, 0, 0, _width, _height);
825 cairo_fill(_cr);
826 }
828 _is_valid = TRUE;
830 return true;
831 }
833 bool
834 CairoRenderContext::finish(void)
835 {
836 g_assert( _is_valid );
838 if (_vector_based_target)
839 cairo_show_page(_cr);
841 cairo_destroy(_cr);
842 cairo_surface_finish(_surface);
843 cairo_status_t status = cairo_surface_status(_surface);
844 cairo_surface_destroy(_surface);
845 _cr = NULL;
846 _surface = NULL;
848 if (_layout)
849 g_object_unref(_layout);
851 _is_valid = FALSE;
853 if (_vector_based_target && _stream) {
854 /* Flush stream to be sure. */
855 (void) fflush(_stream);
857 fclose(_stream);
858 _stream = NULL;
859 }
861 if (status == CAIRO_STATUS_SUCCESS)
862 return true;
863 else
864 return false;
865 }
867 void
868 CairoRenderContext::transform(Geom::Matrix const *transform)
869 {
870 g_assert( _is_valid );
872 cairo_matrix_t matrix;
873 _initCairoMatrix(&matrix, transform);
874 cairo_transform(_cr, &matrix);
876 // store new CTM
877 getTransform(&_state->transform);
878 }
880 void
881 CairoRenderContext::setTransform(Geom::Matrix const *transform)
882 {
883 g_assert( _is_valid );
885 cairo_matrix_t matrix;
886 _initCairoMatrix(&matrix, transform);
887 cairo_set_matrix(_cr, &matrix);
888 _state->transform = *transform;
889 }
891 void
892 CairoRenderContext::getTransform(Geom::Matrix *copy) const
893 {
894 g_assert( _is_valid );
896 cairo_matrix_t ctm;
897 cairo_get_matrix(_cr, &ctm);
898 (*copy)[0] = ctm.xx;
899 (*copy)[1] = ctm.yx;
900 (*copy)[2] = ctm.xy;
901 (*copy)[3] = ctm.yy;
902 (*copy)[4] = ctm.x0;
903 (*copy)[5] = ctm.y0;
904 }
906 void
907 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
908 {
909 g_assert( _is_valid );
911 CairoRenderState *parent_state = getParentState();
912 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
913 }
915 void
916 CairoRenderContext::pushState(void)
917 {
918 g_assert( _is_valid );
920 cairo_save(_cr);
922 CairoRenderState *new_state = _createState();
923 // copy current state's transform
924 new_state->transform = _state->transform;
925 _state_stack = g_slist_prepend(_state_stack, new_state);
926 _state = new_state;
927 }
929 void
930 CairoRenderContext::popState(void)
931 {
932 g_assert( _is_valid );
934 cairo_restore(_cr);
936 g_free(_state_stack->data);
937 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
938 _state = (CairoRenderState*)_state_stack->data;
940 g_assert( g_slist_length(_state_stack) > 0 );
941 }
943 static bool pattern_hasItemChildren (SPPattern *pat)
944 {
945 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
946 if (SP_IS_ITEM (child)) {
947 return true;
948 }
949 }
950 return false;
951 }
953 cairo_pattern_t*
954 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
955 {
956 g_assert( SP_IS_PATTERN(paintserver) );
958 SPPattern *pat = SP_PATTERN (paintserver);
960 Geom::Matrix ps2user, pcs2dev;
961 ps2user = Geom::identity();
962 pcs2dev = Geom::identity();
964 double x = pattern_x(pat);
965 double y = pattern_y(pat);
966 double width = pattern_width(pat);
967 double height = pattern_height(pat);
968 double bbox_width_scaler;
969 double bbox_height_scaler;
971 TRACE(("%f x %f pattern\n", width, height));
973 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
974 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
975 bbox_width_scaler = pbox->x1 - pbox->x0;
976 bbox_height_scaler = pbox->y1 - pbox->y0;
977 ps2user[4] = x * bbox_width_scaler + pbox->x0;
978 ps2user[5] = y * bbox_height_scaler + pbox->y0;
979 } else {
980 bbox_width_scaler = 1.0;
981 bbox_height_scaler = 1.0;
982 ps2user[4] = x;
983 ps2user[5] = y;
984 }
986 // apply pattern transformation
987 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
988 ps2user *= pattern_transform;
989 Geom::Point ori (ps2user[4], ps2user[5]);
991 // create pattern contents coordinate system
992 if (pat->viewBox_set) {
993 NRRect *view_box = pattern_viewBox(pat);
995 double x, y, w, h;
996 double view_width, view_height;
997 x = 0;
998 y = 0;
999 w = width * bbox_width_scaler;
1000 h = height * bbox_height_scaler;
1002 view_width = view_box->x1 - view_box->x0;
1003 view_height = view_box->y1 - view_box->y0;
1005 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1006 pcs2dev[0] = w / view_width;
1007 pcs2dev[3] = h / view_height;
1008 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1009 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1010 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1011 pcs2dev[0] = pbox->x1 - pbox->x0;
1012 pcs2dev[3] = pbox->y1 - pbox->y0;
1014 }
1016 // Calculate the size of the surface which has to be created
1017 #define SUBPIX_SCALE 100
1018 // Cairo requires an integer pattern surface width/height.
1019 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1020 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1021 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1022 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1023 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1024 // create new rendering context
1025 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1027 // adjust the size of the painted pattern to fit exactly the created surface
1028 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1029 double scale_width = surface_width / (bbox_width_scaler * width);
1030 double scale_height = surface_height / (bbox_height_scaler * height);
1031 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1032 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1033 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1034 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1035 }
1037 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1038 ps2user[4] = ori[Geom::X];
1039 ps2user[5] = ori[Geom::Y];
1041 pattern_ctx->setTransform(&pcs2dev);
1042 pattern_ctx->pushState();
1044 // create arena and group
1045 NRArena *arena = NRArena::create();
1046 unsigned dkey = sp_item_display_key_new(1);
1048 // show items and render them
1049 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1050 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1051 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1052 if (SP_IS_ITEM (child)) {
1053 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1054 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1055 }
1056 }
1057 break; // do not go further up the chain if children are found
1058 }
1059 }
1061 pattern_ctx->popState();
1063 // setup a cairo_pattern_t
1064 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1065 TEST(pattern_ctx->saveAsPng("pattern.png"));
1066 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1067 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1069 // set pattern transformation
1070 cairo_matrix_t pattern_matrix;
1071 _initCairoMatrix(&pattern_matrix, &ps2user);
1072 cairo_matrix_invert(&pattern_matrix);
1073 cairo_pattern_set_matrix(result, &pattern_matrix);
1075 delete pattern_ctx;
1077 // hide all items
1078 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1079 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1080 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1081 if (SP_IS_ITEM (child)) {
1082 sp_item_invoke_hide (SP_ITEM (child), dkey);
1083 }
1084 }
1085 break; // do not go further up the chain if children are found
1086 }
1087 }
1089 return result;
1090 }
1092 cairo_pattern_t*
1093 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1094 NRRect const *pbox, float alpha)
1095 {
1096 cairo_pattern_t *pattern = NULL;
1097 bool apply_bbox2user = FALSE;
1099 if (SP_IS_LINEARGRADIENT (paintserver)) {
1101 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1103 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1105 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1106 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1107 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1108 // convert to userspace
1109 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1110 p1 *= bbox2user;
1111 p2 *= bbox2user;
1112 }
1114 // create linear gradient pattern
1115 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1117 // add stops
1118 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1119 float rgb[3];
1120 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1121 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1122 }
1123 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1125 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1127 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1129 Geom::Point c (rg->cx.computed, rg->cy.computed);
1130 Geom::Point f (rg->fx.computed, rg->fy.computed);
1131 double r = rg->r.computed;
1132 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1133 apply_bbox2user = true;
1135 // create radial gradient pattern
1136 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1138 // add stops
1139 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1140 float rgb[3];
1141 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1142 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1143 }
1144 } else if (SP_IS_PATTERN (paintserver)) {
1146 pattern = _createPatternPainter(paintserver, pbox);
1147 } else {
1148 return NULL;
1149 }
1151 if (pattern && SP_IS_GRADIENT (paintserver)) {
1152 SPGradient *g = SP_GRADIENT(paintserver);
1154 // set extend type
1155 SPGradientSpread spread = sp_gradient_get_spread(g);
1156 switch (spread) {
1157 case SP_GRADIENT_SPREAD_REPEAT: {
1158 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1159 break;
1160 }
1161 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1162 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1163 break;
1164 }
1165 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1166 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1167 break;
1168 }
1169 default: {
1170 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1171 break;
1172 }
1173 }
1175 cairo_matrix_t pattern_matrix;
1176 if (g->gradientTransform_set) {
1177 // apply gradient transformation
1178 cairo_matrix_init(&pattern_matrix,
1179 g->gradientTransform[0], g->gradientTransform[1],
1180 g->gradientTransform[2], g->gradientTransform[3],
1181 g->gradientTransform[4], g->gradientTransform[5]);
1182 } else {
1183 cairo_matrix_init_identity (&pattern_matrix);
1184 }
1186 if (apply_bbox2user) {
1187 // convert to userspace
1188 cairo_matrix_t bbox2user;
1189 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1190 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1191 }
1192 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1193 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1194 }
1196 return pattern;
1197 }
1199 void
1200 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1201 {
1202 g_return_if_fail( !style->fill.set
1203 || style->fill.isColor()
1204 || style->fill.isPaintserver() );
1206 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1207 if (_state->merge_opacity) {
1208 alpha *= _state->opacity;
1209 TRACE(("merged op=%f\n", alpha));
1210 }
1212 if (style->fill.isColor()) {
1213 float rgb[3];
1214 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1216 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1218 } else if (!style->fill.set) { // unset fill is black
1219 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1221 } else {
1222 g_assert( style->fill.isPaintserver()
1223 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1224 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1226 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1228 if (pattern) {
1229 cairo_set_source(_cr, pattern);
1230 cairo_pattern_destroy(pattern);
1231 }
1232 }
1233 }
1235 void
1236 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1237 {
1238 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1239 if (_state->merge_opacity)
1240 alpha *= _state->opacity;
1242 if (style->stroke.isColor()) {
1243 float rgb[3];
1244 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1246 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1247 } else {
1248 g_assert( style->fill.isPaintserver()
1249 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1250 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1252 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1254 if (pattern) {
1255 cairo_set_source(_cr, pattern);
1256 cairo_pattern_destroy(pattern);
1257 }
1258 }
1260 if (style->stroke_dash.n_dash &&
1261 style->stroke_dash.dash )
1262 {
1263 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1264 } else {
1265 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1266 }
1268 cairo_set_line_width(_cr, style->stroke_width.computed);
1270 // set line join type
1271 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1272 switch (style->stroke_linejoin.computed) {
1273 case SP_STROKE_LINEJOIN_MITER:
1274 join = CAIRO_LINE_JOIN_MITER;
1275 break;
1276 case SP_STROKE_LINEJOIN_ROUND:
1277 join = CAIRO_LINE_JOIN_ROUND;
1278 break;
1279 case SP_STROKE_LINEJOIN_BEVEL:
1280 join = CAIRO_LINE_JOIN_BEVEL;
1281 break;
1282 }
1283 cairo_set_line_join(_cr, join);
1285 // set line cap type
1286 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1287 switch (style->stroke_linecap.computed) {
1288 case SP_STROKE_LINECAP_BUTT:
1289 cap = CAIRO_LINE_CAP_BUTT;
1290 break;
1291 case SP_STROKE_LINECAP_ROUND:
1292 cap = CAIRO_LINE_CAP_ROUND;
1293 break;
1294 case SP_STROKE_LINECAP_SQUARE:
1295 cap = CAIRO_LINE_CAP_SQUARE;
1296 break;
1297 }
1298 cairo_set_line_cap(_cr, cap);
1299 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1300 }
1302 bool
1303 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1304 {
1305 g_assert( _is_valid );
1307 if (_render_mode == RENDER_MODE_CLIP) {
1308 if (_clip_mode == CLIP_MODE_PATH) {
1309 addClipPath(pathv, &style->fill_rule);
1310 } else {
1311 setPathVector(pathv);
1312 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1313 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1314 } else {
1315 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1316 }
1317 cairo_fill(_cr);
1318 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1319 }
1320 return true;
1321 }
1323 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1324 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1325 style->stroke_opacity.value == 0;
1327 if (no_fill && no_stroke)
1328 return true;
1330 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1331 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1333 if (!need_layer)
1334 cairo_save(_cr);
1335 else
1336 pushLayer();
1338 if (!no_fill) {
1339 _setFillStyle(style, pbox);
1340 setPathVector(pathv);
1342 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1343 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1344 } else {
1345 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1346 }
1348 if (no_stroke)
1349 cairo_fill(_cr);
1350 else
1351 cairo_fill_preserve(_cr);
1352 }
1354 if (!no_stroke) {
1355 _setStrokeStyle(style, pbox);
1356 if (no_fill)
1357 setPathVector(pathv);
1359 cairo_stroke(_cr);
1360 }
1362 if (need_layer)
1363 popLayer();
1364 else
1365 cairo_restore(_cr);
1367 return true;
1368 }
1370 bool
1371 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1372 Geom::Matrix const *image_transform, SPStyle const *style)
1373 {
1374 g_assert( _is_valid );
1376 if (_render_mode == RENDER_MODE_CLIP)
1377 return true;
1379 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1380 if (!px_rgba)
1381 return false;
1383 float opacity;
1384 if (_state->merge_opacity)
1385 opacity = _state->opacity;
1386 else
1387 opacity = 1.0;
1389 // make a copy of the original pixbuf with premultiplied alpha
1390 // if we pass the original pixbuf it will get messed up
1391 for (unsigned i = 0; i < h; i++) {
1392 for (unsigned j = 0; j < w; j++) {
1393 guchar const *src = px + i * rs + j * 4;
1394 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1395 guchar r, g, b, alpha_dst;
1397 // calculate opacity-modified alpha
1398 alpha_dst = src[3];
1399 if (opacity != 1.0 && _vector_based_target)
1400 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1402 // premul alpha (needed because this will be undone by cairo-pdf)
1403 r = src[0]*alpha_dst/255;
1404 g = src[1]*alpha_dst/255;
1405 b = src[2]*alpha_dst/255;
1407 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1408 }
1409 }
1411 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1412 if (cairo_surface_status(image_surface)) {
1413 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1414 return false;
1415 }
1417 // setup automatic freeing of the image data when destroying the surface
1418 static cairo_user_data_key_t key;
1419 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1421 cairo_save(_cr);
1423 // scaling by width & height is not needed because it will be done by Cairo
1424 if (image_transform)
1425 transform(image_transform);
1427 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1429 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1430 if (_vector_based_target) {
1431 cairo_new_path(_cr);
1432 cairo_rectangle(_cr, 0, 0, w, h);
1433 cairo_clip(_cr);
1434 }
1436 if (_vector_based_target)
1437 cairo_paint(_cr);
1438 else
1439 cairo_paint_with_alpha(_cr, opacity);
1441 cairo_restore(_cr);
1443 cairo_surface_destroy(image_surface);
1445 return true;
1446 }
1448 #define GLYPH_ARRAY_SIZE 64
1450 unsigned int
1451 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1452 {
1453 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1454 cairo_glyph_t *glyphs = glyph_array;
1455 unsigned int num_glyphs = glyphtext.size();
1456 if (num_glyphs > GLYPH_ARRAY_SIZE)
1457 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1459 unsigned int num_invalid_glyphs = 0;
1460 unsigned int i = 0;
1461 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1462 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1463 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1464 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1465 TRACE(("INVALID GLYPH found\n"));
1466 num_invalid_glyphs++;
1467 continue;
1468 }
1469 glyphs[i - num_invalid_glyphs].index = it_info->index;
1470 glyphs[i - num_invalid_glyphs].x = it_info->x;
1471 glyphs[i - num_invalid_glyphs].y = it_info->y;
1472 i++;
1473 }
1475 if (is_stroke) {
1476 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1477 } else {
1478 if (_is_texttopath) {
1479 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1480 cairo_fill_preserve(cr);
1481 } else {
1482 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1483 }
1484 }
1486 if (num_glyphs > GLYPH_ARRAY_SIZE)
1487 g_free(glyphs);
1489 return num_glyphs - num_invalid_glyphs;
1490 }
1492 bool
1493 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1494 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1495 {
1496 // create a cairo_font_face from PangoFont
1497 double size = style->font_size.computed;
1498 cairo_font_face_t *font_face = NULL;
1500 FcPattern *fc_pattern = NULL;
1502 #ifdef USE_PANGO_WIN32
1503 # ifdef CAIRO_HAS_WIN32_FONT
1504 LOGFONTA *lfa = pango_win32_font_logfont(font);
1505 LOGFONTW lfw;
1507 ZeroMemory(&lfw, sizeof(LOGFONTW));
1508 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1509 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1511 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1512 # endif
1513 #else
1514 # ifdef CAIRO_HAS_FT_FONT
1515 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1516 fc_pattern = fc_font->font_pattern;
1517 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1518 # endif
1519 #endif
1521 cairo_save(_cr);
1522 cairo_set_font_face(_cr, font_face);
1524 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1525 size = 12.0;
1527 // set the given font matrix
1528 cairo_matrix_t matrix;
1529 _initCairoMatrix(&matrix, font_matrix);
1530 cairo_set_font_matrix(_cr, &matrix);
1532 if (_render_mode == RENDER_MODE_CLIP) {
1533 if (_clip_mode == CLIP_MODE_MASK) {
1534 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1535 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1536 } else {
1537 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1538 }
1539 _showGlyphs(_cr, font, glyphtext, FALSE);
1540 } else {
1541 // just add the glyph paths to the current context
1542 _showGlyphs(_cr, font, glyphtext, TRUE);
1543 }
1544 } else {
1546 if (style->fill.isColor() || style->fill.isPaintserver()) {
1547 // set fill style
1548 _setFillStyle(style, NULL);
1550 _showGlyphs(_cr, font, glyphtext, FALSE);
1551 }
1553 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1554 // set stroke style
1555 _setStrokeStyle(style, NULL);
1557 // paint stroke
1558 _showGlyphs(_cr, font, glyphtext, TRUE);
1559 cairo_stroke(_cr);
1560 }
1561 }
1563 cairo_restore(_cr);
1565 if (font_face)
1566 cairo_font_face_destroy(font_face);
1568 return true;
1569 }
1571 /* Helper functions */
1573 void
1574 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1575 {
1576 cairo_new_path(_cr);
1577 addPathVector(pv);
1578 }
1580 void
1581 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1582 {
1583 feed_pathvector_to_cairo(_cr, pv);
1584 }
1586 void
1587 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1588 {
1589 cairo_matrix_t matrix;
1591 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1592 cairo_transform(cr, &matrix);
1593 }
1595 void
1596 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1597 {
1598 matrix->xx = (*transform)[0];
1599 matrix->yx = (*transform)[1];
1600 matrix->xy = (*transform)[2];
1601 matrix->yy = (*transform)[3];
1602 matrix->x0 = (*transform)[4];
1603 matrix->y0 = (*transform)[5];
1604 }
1606 void
1607 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1608 {
1609 _concatTransform(cr, (*transform)[0], (*transform)[1],
1610 (*transform)[2], (*transform)[3],
1611 (*transform)[4], (*transform)[5]);
1612 }
1614 static cairo_status_t
1615 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1616 {
1617 size_t written;
1618 FILE *file = (FILE*)closure;
1620 written = fwrite (data, 1, length, file);
1622 if (written == length)
1623 return CAIRO_STATUS_SUCCESS;
1624 else
1625 return CAIRO_STATUS_WRITE_ERROR;
1626 }
1628 #include "clear-n_.h"
1630 } /* namespace Internal */
1631 } /* namespace Extension */
1632 } /* namespace Inkscape */
1634 #undef TRACE
1635 #undef TEST
1637 /* End of GNU GPL code */
1640 /*
1641 Local Variables:
1642 mode:c++
1643 c-file-style:"stroustrup"
1644 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1645 indent-tabs-mode:nil
1646 fill-column:99
1647 End:
1648 */
1649 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :