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->_is_valid = TRUE;
197 return new_context;
198 }
200 CairoRenderContext*
201 CairoRenderContext::cloneMe(void) const
202 {
203 g_assert( _is_valid );
205 return cloneMe(_width, _height);
206 }
208 bool
209 CairoRenderContext::setImageTarget(cairo_format_t format)
210 {
211 // format cannot be set on an already initialized surface
212 if (_is_valid)
213 return false;
215 switch (format) {
216 case CAIRO_FORMAT_ARGB32:
217 case CAIRO_FORMAT_RGB24:
218 case CAIRO_FORMAT_A8:
219 case CAIRO_FORMAT_A1:
220 _target_format = format;
221 _target = CAIRO_SURFACE_TYPE_IMAGE;
222 return true;
223 break;
224 default:
225 break;
226 }
228 return false;
229 }
231 bool
232 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
233 {
234 #ifndef CAIRO_HAS_PDF_SURFACE
235 return false;
236 #else
237 _target = CAIRO_SURFACE_TYPE_PDF;
238 _vector_based_target = TRUE;
239 #endif
241 FILE *osf = NULL;
242 FILE *osp = NULL;
244 gsize bytesRead = 0;
245 gsize bytesWritten = 0;
246 GError *error = NULL;
247 gchar *local_fn = g_filename_from_utf8(utf8_fn,
248 -1, &bytesRead, &bytesWritten, &error);
249 gchar const *fn = local_fn;
251 /* TODO: Replace the below fprintf's with something that does the right thing whether in
252 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
253 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
254 * return code.
255 */
256 if (fn != NULL) {
257 if (*fn == '|') {
258 fn += 1;
259 while (isspace(*fn)) fn += 1;
260 #ifndef WIN32
261 osp = popen(fn, "w");
262 #else
263 osp = _popen(fn, "w");
264 #endif
265 if (!osp) {
266 fprintf(stderr, "inkscape: popen(%s): %s\n",
267 fn, strerror(errno));
268 return false;
269 }
270 _stream = osp;
271 } else if (*fn == '>') {
272 fn += 1;
273 while (isspace(*fn)) fn += 1;
274 Inkscape::IO::dump_fopen_call(fn, "K");
275 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
276 if (!osf) {
277 fprintf(stderr, "inkscape: fopen(%s): %s\n",
278 fn, strerror(errno));
279 return false;
280 }
281 _stream = osf;
282 } else {
283 /* put cwd stuff in here */
284 gchar *qn = ( *fn
285 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
286 : g_strdup("lpr") );
287 #ifndef WIN32
288 osp = popen(qn, "w");
289 #else
290 osp = _popen(qn, "w");
291 #endif
292 if (!osp) {
293 fprintf(stderr, "inkscape: popen(%s): %s\n",
294 qn, strerror(errno));
295 return false;
296 }
297 g_free(qn);
298 _stream = osp;
299 }
300 }
302 g_free(local_fn);
304 if (_stream) {
305 /* fixme: this is kinda icky */
306 #if !defined(_WIN32) && !defined(__WIN32__)
307 (void) signal(SIGPIPE, SIG_IGN);
308 #endif
309 }
311 return true;
312 }
314 bool
315 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
316 {
317 #ifndef CAIRO_HAS_PS_SURFACE
318 return false;
319 #else
320 _target = CAIRO_SURFACE_TYPE_PS;
321 _vector_based_target = TRUE;
322 #endif
324 FILE *osf = NULL;
325 FILE *osp = NULL;
327 gsize bytesRead = 0;
328 gsize bytesWritten = 0;
329 GError *error = NULL;
330 gchar *local_fn = g_filename_from_utf8(utf8_fn,
331 -1, &bytesRead, &bytesWritten, &error);
332 gchar const *fn = local_fn;
334 /* TODO: Replace the below fprintf's with something that does the right thing whether in
335 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
336 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
337 * return code.
338 */
339 if (fn != NULL) {
340 if (*fn == '|') {
341 fn += 1;
342 while (isspace(*fn)) fn += 1;
343 #ifndef WIN32
344 osp = popen(fn, "w");
345 #else
346 osp = _popen(fn, "w");
347 #endif
348 if (!osp) {
349 fprintf(stderr, "inkscape: popen(%s): %s\n",
350 fn, strerror(errno));
351 return false;
352 }
353 _stream = osp;
354 } else if (*fn == '>') {
355 fn += 1;
356 while (isspace(*fn)) fn += 1;
357 Inkscape::IO::dump_fopen_call(fn, "K");
358 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
359 if (!osf) {
360 fprintf(stderr, "inkscape: fopen(%s): %s\n",
361 fn, strerror(errno));
362 return false;
363 }
364 _stream = osf;
365 } else {
366 /* put cwd stuff in here */
367 gchar *qn = ( *fn
368 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
369 : g_strdup("lpr") );
370 #ifndef WIN32
371 osp = popen(qn, "w");
372 #else
373 osp = _popen(qn, "w");
374 #endif
375 if (!osp) {
376 fprintf(stderr, "inkscape: popen(%s): %s\n",
377 qn, strerror(errno));
378 return false;
379 }
380 g_free(qn);
381 _stream = osp;
382 }
383 }
385 g_free(local_fn);
387 if (_stream) {
388 /* fixme: this is kinda icky */
389 #if !defined(_WIN32) && !defined(__WIN32__)
390 (void) signal(SIGPIPE, SIG_IGN);
391 #endif
392 }
394 return true;
395 }
397 void CairoRenderContext::setPSLevel(unsigned int level)
398 {
399 _ps_level = level;
400 }
402 void CairoRenderContext::setEPS(bool eps)
403 {
404 _eps = eps;
405 }
407 unsigned int CairoRenderContext::getPSLevel(void)
408 {
409 return _ps_level;
410 }
412 void CairoRenderContext::setPDFLevel(unsigned int level)
413 {
414 _pdf_level = level;
415 }
417 void CairoRenderContext::setTextToPath(bool texttopath)
418 {
419 _is_texttopath = texttopath;
420 }
422 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
423 {
424 _is_filtertobitmap = filtertobitmap;
425 }
427 bool CairoRenderContext::getFilterToBitmap(void)
428 {
429 return _is_filtertobitmap;
430 }
432 void CairoRenderContext::setBitmapResolution(int resolution)
433 {
434 _bitmapresolution = resolution;
435 }
437 int CairoRenderContext::getBitmapResolution(void)
438 {
439 return _bitmapresolution;
440 }
442 cairo_surface_t*
443 CairoRenderContext::getSurface(void)
444 {
445 g_assert( _is_valid );
447 return _surface;
448 }
450 bool
451 CairoRenderContext::saveAsPng(const char *file_name)
452 {
453 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
454 if (status)
455 return false;
456 else
457 return true;
458 }
460 void
461 CairoRenderContext::setRenderMode(CairoRenderMode mode)
462 {
463 switch (mode) {
464 case RENDER_MODE_NORMAL:
465 case RENDER_MODE_CLIP:
466 _render_mode = mode;
467 break;
468 default:
469 _render_mode = RENDER_MODE_NORMAL;
470 break;
471 }
472 }
474 CairoRenderContext::CairoRenderMode
475 CairoRenderContext::getRenderMode(void) const
476 {
477 return _render_mode;
478 }
480 void
481 CairoRenderContext::setClipMode(CairoClipMode mode)
482 {
483 switch (mode) {
484 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
485 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
486 _clip_mode = mode;
487 break;
488 default:
489 _clip_mode = CLIP_MODE_PATH;
490 break;
491 }
492 }
494 CairoRenderContext::CairoClipMode
495 CairoRenderContext::getClipMode(void) const
496 {
497 return _clip_mode;
498 }
500 CairoRenderState*
501 CairoRenderContext::_createState(void)
502 {
503 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
504 g_assert( state != NULL );
506 state->has_filtereffect = FALSE;
507 state->merge_opacity = TRUE;
508 state->opacity = 1.0;
509 state->need_layer = FALSE;
510 state->has_overflow = FALSE;
511 state->parent_has_userspace = FALSE;
512 state->clip_path = NULL;
513 state->mask = NULL;
515 return state;
516 }
518 void
519 CairoRenderContext::pushLayer(void)
520 {
521 g_assert( _is_valid );
523 TRACE(("--pushLayer\n"));
524 cairo_push_group(_cr);
526 // clear buffer
527 if (!_vector_based_target) {
528 cairo_save(_cr);
529 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
530 cairo_paint(_cr);
531 cairo_restore(_cr);
532 }
533 }
535 void
536 CairoRenderContext::popLayer(void)
537 {
538 g_assert( _is_valid );
540 float opacity = _state->opacity;
541 TRACE(("--popLayer w/ opacity %f\n", opacity));
543 /*
544 At this point, the Cairo source is ready. A Cairo mask must be created if required.
545 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
546 masks independently of the objects they effect while in SVG the clip paths and masks
547 are defined relative to the objects they are attached to.
548 Notes:
549 1. An SVG object may have both a clip path and a mask!
550 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
551 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
552 the page (document). This is also not handled properly.
553 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
554 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
555 alpha. This is handled here by doing a pixel by pixel conversion.
556 */
558 SPClipPath *clip_path = _state->clip_path;
559 SPMask *mask = _state->mask;
560 if (clip_path || mask) {
562 CairoRenderContext *clip_ctx = 0;
563 cairo_surface_t *clip_mask = 0;
565 // Apply any clip path first
566 if (clip_path) {
567 TRACE((" Applying clip\n"));
568 if (_render_mode == RENDER_MODE_CLIP)
569 mask = NULL; // disable mask when performing nested clipping
571 if (_vector_based_target) {
572 setClipMode(CLIP_MODE_PATH); // Vector
573 if (!mask) {
574 cairo_pop_group_to_source(_cr);
575 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
576 if (opacity == 1.0)
577 cairo_paint(_cr);
578 else
579 cairo_paint_with_alpha(_cr, opacity);
581 } else {
582 // the clipPath will be applied before masking
583 }
584 } else {
586 // setup a new rendering context
587 clip_ctx = _renderer->createContext();
588 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
589 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
590 // This code ties the clipping to the document coordinates. It doesn't allow
591 // for a clipped object intially drawn off the page and then translated onto
592 // the page.
593 if (!clip_ctx->setupSurface(_width, _height)) {
594 TRACE(("clip: setupSurface failed\n"));
595 _renderer->destroyContext(clip_ctx);
596 return;
597 }
599 // clear buffer
600 cairo_save(clip_ctx->_cr);
601 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
602 cairo_paint(clip_ctx->_cr);
603 cairo_restore(clip_ctx->_cr);
605 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
606 if (!mask)
607 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
608 else
609 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
611 // copy over the correct CTM
612 // It must be stored in item_transform of current state after pushState.
613 Geom::Matrix item_transform;
614 if (_state->parent_has_userspace)
615 item_transform = getParentState()->transform * _state->item_transform;
616 else
617 item_transform = _state->item_transform;
619 // apply the clip path
620 clip_ctx->pushState();
621 clip_ctx->getCurrentState()->item_transform = item_transform;
622 _renderer->applyClipPath(clip_ctx, clip_path);
623 clip_ctx->popState();
625 clip_mask = clip_ctx->getSurface();
626 TEST(clip_ctx->saveAsPng("clip_mask.png"));
628 if (!mask) {
629 cairo_pop_group_to_source(_cr);
630 cairo_mask_surface(_cr, clip_mask, 0, 0);
631 _renderer->destroyContext(clip_ctx);
632 }
633 }
634 }
636 // Apply any mask second
637 if (mask) {
638 TRACE((" Applying mask\n"));
639 // create rendering context for mask
640 CairoRenderContext *mask_ctx = _renderer->createContext();
642 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
643 // Cairo surface is expecting the mask to be 90 dpi.
644 float surface_width = _width;
645 float surface_height = _height;
646 if( _vector_based_target ) {
647 surface_width *= 1.25;
648 surface_height *= 1.25;
649 }
650 mask_ctx->setupSurface( surface_width, surface_height );
651 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
653 // set rendering mode to normal
654 setRenderMode(RENDER_MODE_NORMAL);
656 // copy the correct CTM to mask context
657 /*
658 if (_state->parent_has_userspace)
659 mask_ctx->setTransform(&getParentState()->transform);
660 else
661 mask_ctx->setTransform(&_state->transform);
662 */
663 // This is probably not correct... but it seems to do the trick.
664 mask_ctx->setTransform(&_state->item_transform);
666 // render mask contents to mask_ctx
667 _renderer->applyMask(mask_ctx, mask);
669 TEST(mask_ctx->saveAsPng("mask.png"));
671 // composite with clip mask
672 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
673 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
674 _renderer->destroyContext(clip_ctx);
675 }
677 cairo_surface_t *mask_image = mask_ctx->getSurface();
678 int width = cairo_image_surface_get_width(mask_image);
679 int height = cairo_image_surface_get_height(mask_image);
680 int stride = cairo_image_surface_get_stride(mask_image);
681 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
683 // premultiply with opacity
684 // In SVG, the rgb channels as well as the alpha channel is used in masking.
685 // In Cairo, only the alpha channel is used thus requiring this conversion.
686 TRACE(("premul w/ %f\n", opacity));
687 guint8 int_opacity = (guint8)(255 * opacity);
688 for (int row = 0 ; row < height; row++) {
689 unsigned char *row_data = pixels + (row * stride);
690 for (int i = 0 ; i < width; i++) {
691 guint32 *pixel = (guint32 *)row_data + i;
692 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
693 ((*pixel & 0x0000ff00) >> 8) * 46518 +
694 ((*pixel & 0x000000ff) ) * 4688) *
695 int_opacity);
696 }
697 }
699 cairo_pop_group_to_source(_cr);
700 if (_clip_mode == CLIP_MODE_PATH) {
701 // we have to do the clipping after cairo_pop_group_to_source
702 _renderer->applyClipPath(this, clip_path);
703 }
704 // apply the mask onto the layer
705 cairo_mask_surface(_cr, mask_image, 0, 0);
706 _renderer->destroyContext(mask_ctx);
707 }
708 } else {
709 // No clip path or mask
710 cairo_pop_group_to_source(_cr);
711 if (opacity == 1.0)
712 cairo_paint(_cr);
713 else
714 cairo_paint_with_alpha(_cr, opacity);
715 }
716 }
718 void
719 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
720 {
721 g_assert( _is_valid );
723 // here it should be checked whether the current clip winding changed
724 // so we could switch back to masked clipping
725 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
726 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
727 } else {
728 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
729 }
730 addPathVector(pv);
731 }
733 void
734 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
735 {
736 g_assert( _is_valid );
738 cairo_rectangle(_cr, x, y, width, height);
739 cairo_clip(_cr);
740 }
742 bool
743 CairoRenderContext::setupSurface(double width, double height)
744 {
745 // Is the surface already set up?
746 if (_is_valid)
747 return true;
749 if (_vector_based_target && _stream == NULL)
750 return false;
752 cairo_surface_t *surface = NULL;
753 switch (_target) {
754 case CAIRO_SURFACE_TYPE_IMAGE:
755 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
756 break;
757 #ifdef CAIRO_HAS_PDF_SURFACE
758 case CAIRO_SURFACE_TYPE_PDF:
759 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
760 break;
761 #endif
762 #ifdef CAIRO_HAS_PS_SURFACE
763 case CAIRO_SURFACE_TYPE_PS:
764 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
765 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
766 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
767 return FALSE;
768 }
769 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
770 cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
771 #endif
772 break;
773 #endif
774 default:
775 return false;
776 break;
777 }
779 return _finishSurfaceSetup (surface);
780 }
782 bool
783 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
784 {
785 if (_is_valid || !surface)
786 return false;
788 _vector_based_target = is_vector;
789 bool ret = _finishSurfaceSetup (surface, ctm);
790 if (ret)
791 cairo_surface_reference (surface);
792 return ret;
793 }
795 bool
796 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
797 {
798 if(surface == NULL) {
799 return FALSE;
800 }
801 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
802 return FALSE;
803 }
805 _cr = cairo_create(surface);
806 if (ctm)
807 cairo_set_matrix(_cr, ctm);
808 _surface = surface;
810 if (_vector_based_target) {
811 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
812 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
813 // set background color on non-alpha surfaces
814 // TODO: bgcolor should be derived from SPDocument
815 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
816 cairo_rectangle(_cr, 0, 0, _width, _height);
817 cairo_fill(_cr);
818 }
820 _is_valid = TRUE;
822 return true;
823 }
825 bool
826 CairoRenderContext::finish(void)
827 {
828 g_assert( _is_valid );
830 if (_vector_based_target)
831 cairo_show_page(_cr);
833 cairo_destroy(_cr);
834 cairo_surface_finish(_surface);
835 cairo_status_t status = cairo_surface_status(_surface);
836 cairo_surface_destroy(_surface);
837 _cr = NULL;
838 _surface = NULL;
840 if (_layout)
841 g_object_unref(_layout);
843 _is_valid = FALSE;
845 if (_vector_based_target && _stream) {
846 /* Flush stream to be sure. */
847 (void) fflush(_stream);
849 fclose(_stream);
850 _stream = NULL;
851 }
853 if (status == CAIRO_STATUS_SUCCESS)
854 return true;
855 else
856 return false;
857 }
859 void
860 CairoRenderContext::transform(Geom::Matrix const *transform)
861 {
862 g_assert( _is_valid );
864 cairo_matrix_t matrix;
865 _initCairoMatrix(&matrix, transform);
866 cairo_transform(_cr, &matrix);
868 // store new CTM
869 getTransform(&_state->transform);
870 }
872 void
873 CairoRenderContext::setTransform(Geom::Matrix const *transform)
874 {
875 g_assert( _is_valid );
877 cairo_matrix_t matrix;
878 _initCairoMatrix(&matrix, transform);
879 cairo_set_matrix(_cr, &matrix);
880 _state->transform = *transform;
881 }
883 void
884 CairoRenderContext::getTransform(Geom::Matrix *copy) const
885 {
886 g_assert( _is_valid );
888 cairo_matrix_t ctm;
889 cairo_get_matrix(_cr, &ctm);
890 (*copy)[0] = ctm.xx;
891 (*copy)[1] = ctm.yx;
892 (*copy)[2] = ctm.xy;
893 (*copy)[3] = ctm.yy;
894 (*copy)[4] = ctm.x0;
895 (*copy)[5] = ctm.y0;
896 }
898 void
899 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
900 {
901 g_assert( _is_valid );
903 CairoRenderState *parent_state = getParentState();
904 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
905 }
907 void
908 CairoRenderContext::pushState(void)
909 {
910 g_assert( _is_valid );
912 cairo_save(_cr);
914 CairoRenderState *new_state = _createState();
915 // copy current state's transform
916 new_state->transform = _state->transform;
917 _state_stack = g_slist_prepend(_state_stack, new_state);
918 _state = new_state;
919 }
921 void
922 CairoRenderContext::popState(void)
923 {
924 g_assert( _is_valid );
926 cairo_restore(_cr);
928 g_free(_state_stack->data);
929 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
930 _state = (CairoRenderState*)_state_stack->data;
932 g_assert( g_slist_length(_state_stack) > 0 );
933 }
935 static bool pattern_hasItemChildren (SPPattern *pat)
936 {
937 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
938 if (SP_IS_ITEM (child)) {
939 return true;
940 }
941 }
942 return false;
943 }
945 cairo_pattern_t*
946 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
947 {
948 g_assert( SP_IS_PATTERN(paintserver) );
950 SPPattern *pat = SP_PATTERN (paintserver);
952 Geom::Matrix ps2user, pcs2dev;
953 ps2user = Geom::identity();
954 pcs2dev = Geom::identity();
956 double x = pattern_x(pat);
957 double y = pattern_y(pat);
958 double width = pattern_width(pat);
959 double height = pattern_height(pat);
960 double bbox_width_scaler;
961 double bbox_height_scaler;
963 TRACE(("%f x %f pattern\n", width, height));
965 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
966 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
967 bbox_width_scaler = pbox->x1 - pbox->x0;
968 bbox_height_scaler = pbox->y1 - pbox->y0;
969 ps2user[4] = x * bbox_width_scaler + pbox->x0;
970 ps2user[5] = y * bbox_height_scaler + pbox->y0;
971 } else {
972 bbox_width_scaler = 1.0;
973 bbox_height_scaler = 1.0;
974 ps2user[4] = x;
975 ps2user[5] = y;
976 }
978 // apply pattern transformation
979 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
980 ps2user *= pattern_transform;
981 Geom::Point ori (ps2user[4], ps2user[5]);
983 // create pattern contents coordinate system
984 if (pat->viewBox_set) {
985 NRRect *view_box = pattern_viewBox(pat);
987 double x, y, w, h;
988 double view_width, view_height;
989 x = 0;
990 y = 0;
991 w = width * bbox_width_scaler;
992 h = height * bbox_height_scaler;
994 view_width = view_box->x1 - view_box->x0;
995 view_height = view_box->y1 - view_box->y0;
997 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
998 pcs2dev[0] = w / view_width;
999 pcs2dev[3] = h / view_height;
1000 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1001 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1002 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1003 pcs2dev[0] = pbox->x1 - pbox->x0;
1004 pcs2dev[3] = pbox->y1 - pbox->y0;
1006 }
1008 // Calculate the size of the surface which has to be created
1009 #define SUBPIX_SCALE 100
1010 // Cairo requires an integer pattern surface width/height.
1011 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1012 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1013 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1014 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1015 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1016 // create new rendering context
1017 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1019 // adjust the size of the painted pattern to fit exactly the created surface
1020 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1021 double scale_width = surface_width / (bbox_width_scaler * width);
1022 double scale_height = surface_height / (bbox_height_scaler * height);
1023 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1024 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1025 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1026 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1027 }
1029 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1030 ps2user[4] = ori[Geom::X];
1031 ps2user[5] = ori[Geom::Y];
1033 pattern_ctx->setTransform(&pcs2dev);
1034 pattern_ctx->pushState();
1036 // create arena and group
1037 NRArena *arena = NRArena::create();
1038 unsigned dkey = sp_item_display_key_new(1);
1040 // show items and render them
1041 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1042 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1043 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1044 if (SP_IS_ITEM (child)) {
1045 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1046 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1047 }
1048 }
1049 break; // do not go further up the chain if children are found
1050 }
1051 }
1053 pattern_ctx->popState();
1055 // setup a cairo_pattern_t
1056 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1057 TEST(pattern_ctx->saveAsPng("pattern.png"));
1058 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1059 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1061 // set pattern transformation
1062 cairo_matrix_t pattern_matrix;
1063 _initCairoMatrix(&pattern_matrix, &ps2user);
1064 cairo_matrix_invert(&pattern_matrix);
1065 cairo_pattern_set_matrix(result, &pattern_matrix);
1067 delete pattern_ctx;
1069 // hide all items
1070 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1071 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1072 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1073 if (SP_IS_ITEM (child)) {
1074 sp_item_invoke_hide (SP_ITEM (child), dkey);
1075 }
1076 }
1077 break; // do not go further up the chain if children are found
1078 }
1079 }
1081 return result;
1082 }
1084 cairo_pattern_t*
1085 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1086 NRRect const *pbox, float alpha)
1087 {
1088 cairo_pattern_t *pattern = NULL;
1089 bool apply_bbox2user = FALSE;
1091 if (SP_IS_LINEARGRADIENT (paintserver)) {
1093 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1095 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1097 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1098 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1099 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1100 // convert to userspace
1101 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1102 p1 *= bbox2user;
1103 p2 *= bbox2user;
1104 }
1106 // create linear gradient pattern
1107 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1109 // add stops
1110 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1111 float rgb[3];
1112 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1113 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1114 }
1115 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1117 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1119 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1121 Geom::Point c (rg->cx.computed, rg->cy.computed);
1122 Geom::Point f (rg->fx.computed, rg->fy.computed);
1123 double r = rg->r.computed;
1124 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1125 apply_bbox2user = true;
1127 // create radial gradient pattern
1128 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1130 // add stops
1131 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1132 float rgb[3];
1133 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1134 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1135 }
1136 } else if (SP_IS_PATTERN (paintserver)) {
1138 pattern = _createPatternPainter(paintserver, pbox);
1139 } else {
1140 return NULL;
1141 }
1143 if (pattern && SP_IS_GRADIENT (paintserver)) {
1144 SPGradient *g = SP_GRADIENT(paintserver);
1146 // set extend type
1147 SPGradientSpread spread = sp_gradient_get_spread(g);
1148 switch (spread) {
1149 case SP_GRADIENT_SPREAD_REPEAT: {
1150 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1151 break;
1152 }
1153 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1154 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1155 break;
1156 }
1157 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1158 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1159 break;
1160 }
1161 default: {
1162 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1163 break;
1164 }
1165 }
1167 cairo_matrix_t pattern_matrix;
1168 if (g->gradientTransform_set) {
1169 // apply gradient transformation
1170 cairo_matrix_init(&pattern_matrix,
1171 g->gradientTransform[0], g->gradientTransform[1],
1172 g->gradientTransform[2], g->gradientTransform[3],
1173 g->gradientTransform[4], g->gradientTransform[5]);
1174 } else {
1175 cairo_matrix_init_identity (&pattern_matrix);
1176 }
1178 if (apply_bbox2user) {
1179 // convert to userspace
1180 cairo_matrix_t bbox2user;
1181 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1182 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1183 }
1184 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1185 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1186 }
1188 return pattern;
1189 }
1191 void
1192 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1193 {
1194 g_return_if_fail( style->fill.isColor()
1195 || style->fill.isPaintserver() );
1197 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1198 if (_state->merge_opacity) {
1199 alpha *= _state->opacity;
1200 TRACE(("merged op=%f\n", alpha));
1201 }
1203 if (style->fill.isColor()) {
1204 float rgb[3];
1205 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1207 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1208 } else {
1209 g_assert( style->fill.isPaintserver()
1210 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1211 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1213 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1215 if (pattern) {
1216 cairo_set_source(_cr, pattern);
1217 cairo_pattern_destroy(pattern);
1218 }
1219 }
1220 }
1222 void
1223 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1224 {
1225 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1226 if (_state->merge_opacity)
1227 alpha *= _state->opacity;
1229 if (style->stroke.isColor()) {
1230 float rgb[3];
1231 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1233 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1234 } else {
1235 g_assert( style->fill.isPaintserver()
1236 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1237 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1239 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1241 if (pattern) {
1242 cairo_set_source(_cr, pattern);
1243 cairo_pattern_destroy(pattern);
1244 }
1245 }
1247 if (style->stroke_dash.n_dash &&
1248 style->stroke_dash.dash )
1249 {
1250 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1251 } else {
1252 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1253 }
1255 cairo_set_line_width(_cr, style->stroke_width.computed);
1257 // set line join type
1258 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1259 switch (style->stroke_linejoin.computed) {
1260 case SP_STROKE_LINEJOIN_MITER:
1261 join = CAIRO_LINE_JOIN_MITER;
1262 break;
1263 case SP_STROKE_LINEJOIN_ROUND:
1264 join = CAIRO_LINE_JOIN_ROUND;
1265 break;
1266 case SP_STROKE_LINEJOIN_BEVEL:
1267 join = CAIRO_LINE_JOIN_BEVEL;
1268 break;
1269 }
1270 cairo_set_line_join(_cr, join);
1272 // set line cap type
1273 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1274 switch (style->stroke_linecap.computed) {
1275 case SP_STROKE_LINECAP_BUTT:
1276 cap = CAIRO_LINE_CAP_BUTT;
1277 break;
1278 case SP_STROKE_LINECAP_ROUND:
1279 cap = CAIRO_LINE_CAP_ROUND;
1280 break;
1281 case SP_STROKE_LINECAP_SQUARE:
1282 cap = CAIRO_LINE_CAP_SQUARE;
1283 break;
1284 }
1285 cairo_set_line_cap(_cr, cap);
1286 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1287 }
1289 bool
1290 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1291 {
1292 g_assert( _is_valid );
1294 if (_render_mode == RENDER_MODE_CLIP) {
1295 if (_clip_mode == CLIP_MODE_PATH) {
1296 addClipPath(pathv, &style->fill_rule);
1297 } else {
1298 setPathVector(pathv);
1299 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1300 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1301 } else {
1302 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1303 }
1304 cairo_fill(_cr);
1305 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1306 }
1307 return true;
1308 }
1310 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1311 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1312 style->fill_opacity.value == 0;
1314 if (no_fill && no_stroke)
1315 return true;
1317 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1318 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1320 if (!need_layer)
1321 cairo_save(_cr);
1322 else
1323 pushLayer();
1325 if (!no_fill) {
1326 _setFillStyle(style, pbox);
1327 setPathVector(pathv);
1329 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1330 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1331 } else {
1332 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1333 }
1335 if (no_stroke)
1336 cairo_fill(_cr);
1337 else
1338 cairo_fill_preserve(_cr);
1339 }
1341 if (!no_stroke) {
1342 _setStrokeStyle(style, pbox);
1343 if (no_fill)
1344 setPathVector(pathv);
1346 cairo_stroke(_cr);
1347 }
1349 if (need_layer)
1350 popLayer();
1351 else
1352 cairo_restore(_cr);
1354 return true;
1355 }
1357 bool
1358 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1359 Geom::Matrix const *image_transform, SPStyle const *style)
1360 {
1361 g_assert( _is_valid );
1363 if (_render_mode == RENDER_MODE_CLIP)
1364 return true;
1366 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1367 if (!px_rgba)
1368 return false;
1370 float opacity;
1371 if (_state->merge_opacity)
1372 opacity = _state->opacity;
1373 else
1374 opacity = 1.0;
1376 // make a copy of the original pixbuf with premultiplied alpha
1377 // if we pass the original pixbuf it will get messed up
1378 for (unsigned i = 0; i < h; i++) {
1379 for (unsigned j = 0; j < w; j++) {
1380 guchar const *src = px + i * rs + j * 4;
1381 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1382 guchar r, g, b, alpha_dst;
1384 // calculate opacity-modified alpha
1385 alpha_dst = src[3];
1386 if (opacity != 1.0 && _vector_based_target)
1387 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1389 // premul alpha (needed because this will be undone by cairo-pdf)
1390 r = src[0]*alpha_dst/255;
1391 g = src[1]*alpha_dst/255;
1392 b = src[2]*alpha_dst/255;
1394 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1395 }
1396 }
1398 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1399 if (cairo_surface_status(image_surface)) {
1400 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1401 return false;
1402 }
1404 // setup automatic freeing of the image data when destroying the surface
1405 static cairo_user_data_key_t key;
1406 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1408 cairo_save(_cr);
1410 // scaling by width & height is not needed because it will be done by Cairo
1411 if (image_transform)
1412 transform(image_transform);
1414 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1416 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1417 if (_vector_based_target) {
1418 cairo_new_path(_cr);
1419 cairo_rectangle(_cr, 0, 0, w, h);
1420 cairo_clip(_cr);
1421 }
1423 if (_vector_based_target)
1424 cairo_paint(_cr);
1425 else
1426 cairo_paint_with_alpha(_cr, opacity);
1428 cairo_restore(_cr);
1430 cairo_surface_destroy(image_surface);
1432 return true;
1433 }
1435 #define GLYPH_ARRAY_SIZE 64
1437 unsigned int
1438 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1439 {
1440 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1441 cairo_glyph_t *glyphs = glyph_array;
1442 unsigned int num_glyphs = glyphtext.size();
1443 if (num_glyphs > GLYPH_ARRAY_SIZE)
1444 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1446 unsigned int num_invalid_glyphs = 0;
1447 unsigned int i = 0;
1448 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1449 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1450 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1451 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1452 TRACE(("INVALID GLYPH found\n"));
1453 num_invalid_glyphs++;
1454 continue;
1455 }
1456 glyphs[i - num_invalid_glyphs].index = it_info->index;
1457 glyphs[i - num_invalid_glyphs].x = it_info->x;
1458 glyphs[i - num_invalid_glyphs].y = it_info->y;
1459 i++;
1460 }
1462 if (is_stroke || _is_texttopath)
1463 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1464 else
1465 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1467 if (num_glyphs > GLYPH_ARRAY_SIZE)
1468 g_free(glyphs);
1470 return num_glyphs - num_invalid_glyphs;
1471 }
1473 bool
1474 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1475 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1476 {
1477 // create a cairo_font_face from PangoFont
1478 double size = style->font_size.computed;
1479 cairo_font_face_t *font_face = NULL;
1481 FcPattern *fc_pattern = NULL;
1483 #ifdef USE_PANGO_WIN32
1484 # ifdef CAIRO_HAS_WIN32_FONT
1485 LOGFONTA *lfa = pango_win32_font_logfont(font);
1486 LOGFONTW lfw;
1488 ZeroMemory(&lfw, sizeof(LOGFONTW));
1489 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1490 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1492 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1493 # endif
1494 #else
1495 # ifdef CAIRO_HAS_FT_FONT
1496 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1497 fc_pattern = fc_font->font_pattern;
1498 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1499 # endif
1500 #endif
1502 cairo_save(_cr);
1503 cairo_set_font_face(_cr, font_face);
1505 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1506 size = 12.0;
1508 // set the given font matrix
1509 cairo_matrix_t matrix;
1510 _initCairoMatrix(&matrix, font_matrix);
1511 cairo_set_font_matrix(_cr, &matrix);
1513 if (_render_mode == RENDER_MODE_CLIP) {
1514 if (_clip_mode == CLIP_MODE_MASK) {
1515 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1516 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1517 } else {
1518 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1519 }
1520 _showGlyphs(_cr, font, glyphtext, FALSE);
1521 } else {
1522 // just add the glyph paths to the current context
1523 _showGlyphs(_cr, font, glyphtext, TRUE);
1524 }
1525 } else {
1527 if (style->fill.isColor() || style->fill.isPaintserver()) {
1528 // set fill style
1529 _setFillStyle(style, NULL);
1531 _showGlyphs(_cr, font, glyphtext, FALSE);
1532 }
1534 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1535 // set stroke style
1536 _setStrokeStyle(style, NULL);
1538 // paint stroke
1539 _showGlyphs(_cr, font, glyphtext, TRUE);
1540 cairo_stroke(_cr);
1541 }
1542 }
1544 cairo_restore(_cr);
1546 if (font_face)
1547 cairo_font_face_destroy(font_face);
1549 return true;
1550 }
1552 /* Helper functions */
1554 void
1555 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1556 {
1557 cairo_new_path(_cr);
1558 addPathVector(pv);
1559 }
1561 void
1562 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1563 {
1564 feed_pathvector_to_cairo(_cr, pv);
1565 }
1567 void
1568 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1569 {
1570 cairo_matrix_t matrix;
1572 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1573 cairo_transform(cr, &matrix);
1574 }
1576 void
1577 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1578 {
1579 matrix->xx = (*transform)[0];
1580 matrix->yx = (*transform)[1];
1581 matrix->xy = (*transform)[2];
1582 matrix->yy = (*transform)[3];
1583 matrix->x0 = (*transform)[4];
1584 matrix->y0 = (*transform)[5];
1585 }
1587 void
1588 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1589 {
1590 _concatTransform(cr, (*transform)[0], (*transform)[1],
1591 (*transform)[2], (*transform)[3],
1592 (*transform)[4], (*transform)[5]);
1593 }
1595 static cairo_status_t
1596 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1597 {
1598 size_t written;
1599 FILE *file = (FILE*)closure;
1601 written = fwrite (data, 1, length, file);
1603 if (written == length)
1604 return CAIRO_STATUS_SUCCESS;
1605 else
1606 return CAIRO_STATUS_WRITE_ERROR;
1607 }
1609 #include "clear-n_.h"
1611 } /* namespace Internal */
1612 } /* namespace Extension */
1613 } /* namespace Inkscape */
1615 #undef TRACE
1616 #undef TEST
1618 /* End of GNU GPL code */
1621 /*
1622 Local Variables:
1623 mode:c++
1624 c-file-style:"stroustrup"
1625 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1626 indent-tabs-mode:nil
1627 fill-column:99
1628 End:
1629 */
1630 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :