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)
784 {
785 if (_is_valid || !surface)
786 return false;
788 _vector_based_target = is_vector;
789 bool ret = _finishSurfaceSetup (surface);
790 if (ret)
791 cairo_surface_reference (surface);
792 return ret;
793 }
795 bool
796 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
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 _surface = surface;
808 if (_vector_based_target) {
809 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
810 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
811 // set background color on non-alpha surfaces
812 // TODO: bgcolor should be derived from SPDocument
813 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
814 cairo_rectangle(_cr, 0, 0, _width, _height);
815 cairo_fill(_cr);
816 }
818 _is_valid = TRUE;
820 return true;
821 }
823 bool
824 CairoRenderContext::finish(void)
825 {
826 g_assert( _is_valid );
828 if (_vector_based_target)
829 cairo_show_page(_cr);
831 cairo_destroy(_cr);
832 cairo_surface_finish(_surface);
833 cairo_status_t status = cairo_surface_status(_surface);
834 cairo_surface_destroy(_surface);
835 _cr = NULL;
836 _surface = NULL;
838 if (_layout)
839 g_object_unref(_layout);
841 _is_valid = FALSE;
843 if (_vector_based_target && _stream) {
844 /* Flush stream to be sure. */
845 (void) fflush(_stream);
847 fclose(_stream);
848 _stream = NULL;
849 }
851 if (status == CAIRO_STATUS_SUCCESS)
852 return true;
853 else
854 return false;
855 }
857 void
858 CairoRenderContext::transform(Geom::Matrix const *transform)
859 {
860 g_assert( _is_valid );
862 cairo_matrix_t matrix;
863 _initCairoMatrix(&matrix, transform);
864 cairo_transform(_cr, &matrix);
866 // store new CTM
867 getTransform(&_state->transform);
868 }
870 void
871 CairoRenderContext::setTransform(Geom::Matrix const *transform)
872 {
873 g_assert( _is_valid );
875 cairo_matrix_t matrix;
876 _initCairoMatrix(&matrix, transform);
877 cairo_set_matrix(_cr, &matrix);
878 _state->transform = *transform;
879 }
881 void
882 CairoRenderContext::getTransform(Geom::Matrix *copy) const
883 {
884 g_assert( _is_valid );
886 cairo_matrix_t ctm;
887 cairo_get_matrix(_cr, &ctm);
888 (*copy)[0] = ctm.xx;
889 (*copy)[1] = ctm.yx;
890 (*copy)[2] = ctm.xy;
891 (*copy)[3] = ctm.yy;
892 (*copy)[4] = ctm.x0;
893 (*copy)[5] = ctm.y0;
894 }
896 void
897 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
898 {
899 g_assert( _is_valid );
901 CairoRenderState *parent_state = getParentState();
902 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
903 }
905 void
906 CairoRenderContext::pushState(void)
907 {
908 g_assert( _is_valid );
910 cairo_save(_cr);
912 CairoRenderState *new_state = _createState();
913 // copy current state's transform
914 new_state->transform = _state->transform;
915 _state_stack = g_slist_prepend(_state_stack, new_state);
916 _state = new_state;
917 }
919 void
920 CairoRenderContext::popState(void)
921 {
922 g_assert( _is_valid );
924 cairo_restore(_cr);
926 g_free(_state_stack->data);
927 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
928 _state = (CairoRenderState*)_state_stack->data;
930 g_assert( g_slist_length(_state_stack) > 0 );
931 }
933 static bool pattern_hasItemChildren (SPPattern *pat)
934 {
935 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
936 if (SP_IS_ITEM (child)) {
937 return true;
938 }
939 }
940 return false;
941 }
943 cairo_pattern_t*
944 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
945 {
946 g_assert( SP_IS_PATTERN(paintserver) );
948 SPPattern *pat = SP_PATTERN (paintserver);
950 Geom::Matrix ps2user, pcs2dev;
951 ps2user = Geom::identity();
952 pcs2dev = Geom::identity();
954 double x = pattern_x(pat);
955 double y = pattern_y(pat);
956 double width = pattern_width(pat);
957 double height = pattern_height(pat);
958 double bbox_width_scaler;
959 double bbox_height_scaler;
961 TRACE(("%f x %f pattern\n", width, height));
963 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
964 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
965 bbox_width_scaler = pbox->x1 - pbox->x0;
966 bbox_height_scaler = pbox->y1 - pbox->y0;
967 ps2user[4] = x * bbox_width_scaler + pbox->x0;
968 ps2user[5] = y * bbox_height_scaler + pbox->y0;
969 } else {
970 bbox_width_scaler = 1.0;
971 bbox_height_scaler = 1.0;
972 ps2user[4] = x;
973 ps2user[5] = y;
974 }
976 // apply pattern transformation
977 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
978 ps2user *= pattern_transform;
979 Geom::Point ori (ps2user[4], ps2user[5]);
981 // create pattern contents coordinate system
982 if (pat->viewBox_set) {
983 NRRect *view_box = pattern_viewBox(pat);
985 double x, y, w, h;
986 double view_width, view_height;
987 x = 0;
988 y = 0;
989 w = width * bbox_width_scaler;
990 h = height * bbox_height_scaler;
992 view_width = view_box->x1 - view_box->x0;
993 view_height = view_box->y1 - view_box->y0;
995 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
996 pcs2dev[0] = w / view_width;
997 pcs2dev[3] = h / view_height;
998 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
999 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1000 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1001 pcs2dev[0] = pbox->x1 - pbox->x0;
1002 pcs2dev[3] = pbox->y1 - pbox->y0;
1004 }
1006 // Calculate the size of the surface which has to be created
1007 #define SUBPIX_SCALE 100
1008 // Cairo requires an integer pattern surface width/height.
1009 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1010 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1011 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1012 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1013 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1014 // create new rendering context
1015 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1017 // adjust the size of the painted pattern to fit exactly the created surface
1018 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1019 double scale_width = surface_width / (bbox_width_scaler * width);
1020 double scale_height = surface_height / (bbox_height_scaler * height);
1021 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1022 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1023 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1024 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1025 }
1027 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1028 ps2user[4] = ori[Geom::X];
1029 ps2user[5] = ori[Geom::Y];
1031 pattern_ctx->setTransform(&pcs2dev);
1032 pattern_ctx->pushState();
1034 // create arena and group
1035 NRArena *arena = NRArena::create();
1036 unsigned dkey = sp_item_display_key_new(1);
1038 // show items and render them
1039 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1040 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1041 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1042 if (SP_IS_ITEM (child)) {
1043 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1044 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1045 }
1046 }
1047 break; // do not go further up the chain if children are found
1048 }
1049 }
1051 pattern_ctx->popState();
1053 // setup a cairo_pattern_t
1054 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1055 TEST(pattern_ctx->saveAsPng("pattern.png"));
1056 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1057 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1059 // set pattern transformation
1060 cairo_matrix_t pattern_matrix;
1061 _initCairoMatrix(&pattern_matrix, &ps2user);
1062 cairo_matrix_invert(&pattern_matrix);
1063 cairo_pattern_set_matrix(result, &pattern_matrix);
1065 delete pattern_ctx;
1067 // hide all items
1068 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1069 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1070 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1071 if (SP_IS_ITEM (child)) {
1072 sp_item_invoke_hide (SP_ITEM (child), dkey);
1073 }
1074 }
1075 break; // do not go further up the chain if children are found
1076 }
1077 }
1079 return result;
1080 }
1082 cairo_pattern_t*
1083 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1084 NRRect const *pbox, float alpha)
1085 {
1086 cairo_pattern_t *pattern = NULL;
1087 bool apply_bbox2user = FALSE;
1089 if (SP_IS_LINEARGRADIENT (paintserver)) {
1091 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1093 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1095 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1096 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1097 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1098 // convert to userspace
1099 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1100 p1 *= bbox2user;
1101 p2 *= bbox2user;
1102 }
1104 // create linear gradient pattern
1105 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1107 // add stops
1108 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1109 float rgb[3];
1110 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1111 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1112 }
1113 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1115 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1117 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1119 Geom::Point c (rg->cx.computed, rg->cy.computed);
1120 Geom::Point f (rg->fx.computed, rg->fy.computed);
1121 double r = rg->r.computed;
1122 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1123 apply_bbox2user = true;
1125 // create radial gradient pattern
1126 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1128 // add stops
1129 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1130 float rgb[3];
1131 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1132 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1133 }
1134 } else if (SP_IS_PATTERN (paintserver)) {
1136 pattern = _createPatternPainter(paintserver, pbox);
1137 } else {
1138 return NULL;
1139 }
1141 if (pattern && SP_IS_GRADIENT (paintserver)) {
1142 SPGradient *g = SP_GRADIENT(paintserver);
1144 // set extend type
1145 SPGradientSpread spread = sp_gradient_get_spread(g);
1146 switch (spread) {
1147 case SP_GRADIENT_SPREAD_REPEAT: {
1148 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1149 break;
1150 }
1151 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1152 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1153 break;
1154 }
1155 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1156 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1157 break;
1158 }
1159 default: {
1160 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1161 break;
1162 }
1163 }
1165 cairo_matrix_t pattern_matrix;
1166 if (g->gradientTransform_set) {
1167 // apply gradient transformation
1168 cairo_matrix_init(&pattern_matrix,
1169 g->gradientTransform[0], g->gradientTransform[1],
1170 g->gradientTransform[2], g->gradientTransform[3],
1171 g->gradientTransform[4], g->gradientTransform[5]);
1172 } else {
1173 cairo_matrix_init_identity (&pattern_matrix);
1174 }
1176 if (apply_bbox2user) {
1177 // convert to userspace
1178 cairo_matrix_t bbox2user;
1179 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1180 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1181 }
1182 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1183 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1184 }
1186 return pattern;
1187 }
1189 void
1190 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1191 {
1192 g_return_if_fail( style->fill.isColor()
1193 || style->fill.isPaintserver() );
1195 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1196 if (_state->merge_opacity) {
1197 alpha *= _state->opacity;
1198 TRACE(("merged op=%f\n", alpha));
1199 }
1201 if (style->fill.isColor()) {
1202 float rgb[3];
1203 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1205 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1206 } else {
1207 g_assert( style->fill.isPaintserver()
1208 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1209 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1211 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1213 if (pattern) {
1214 cairo_set_source(_cr, pattern);
1215 cairo_pattern_destroy(pattern);
1216 }
1217 }
1218 }
1220 void
1221 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1222 {
1223 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1224 if (_state->merge_opacity)
1225 alpha *= _state->opacity;
1227 if (style->stroke.isColor()) {
1228 float rgb[3];
1229 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1231 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1232 } else {
1233 g_assert( style->fill.isPaintserver()
1234 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1235 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1237 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1239 if (pattern) {
1240 cairo_set_source(_cr, pattern);
1241 cairo_pattern_destroy(pattern);
1242 }
1243 }
1245 if (style->stroke_dash.n_dash &&
1246 style->stroke_dash.dash )
1247 {
1248 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1249 } else {
1250 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1251 }
1253 cairo_set_line_width(_cr, style->stroke_width.computed);
1255 // set line join type
1256 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1257 switch (style->stroke_linejoin.computed) {
1258 case SP_STROKE_LINEJOIN_MITER:
1259 join = CAIRO_LINE_JOIN_MITER;
1260 break;
1261 case SP_STROKE_LINEJOIN_ROUND:
1262 join = CAIRO_LINE_JOIN_ROUND;
1263 break;
1264 case SP_STROKE_LINEJOIN_BEVEL:
1265 join = CAIRO_LINE_JOIN_BEVEL;
1266 break;
1267 }
1268 cairo_set_line_join(_cr, join);
1270 // set line cap type
1271 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1272 switch (style->stroke_linecap.computed) {
1273 case SP_STROKE_LINECAP_BUTT:
1274 cap = CAIRO_LINE_CAP_BUTT;
1275 break;
1276 case SP_STROKE_LINECAP_ROUND:
1277 cap = CAIRO_LINE_CAP_ROUND;
1278 break;
1279 case SP_STROKE_LINECAP_SQUARE:
1280 cap = CAIRO_LINE_CAP_SQUARE;
1281 break;
1282 }
1283 cairo_set_line_cap(_cr, cap);
1284 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1285 }
1287 bool
1288 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1289 {
1290 g_assert( _is_valid );
1292 if (_render_mode == RENDER_MODE_CLIP) {
1293 if (_clip_mode == CLIP_MODE_PATH) {
1294 addClipPath(pathv, &style->fill_rule);
1295 } else {
1296 setPathVector(pathv);
1297 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1298 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1299 } else {
1300 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1301 }
1302 cairo_fill(_cr);
1303 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1304 }
1305 return true;
1306 }
1308 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1309 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1310 style->fill_opacity.value == 0;
1312 if (no_fill && no_stroke)
1313 return true;
1315 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1316 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1318 if (!need_layer)
1319 cairo_save(_cr);
1320 else
1321 pushLayer();
1323 if (!no_fill) {
1324 _setFillStyle(style, pbox);
1325 setPathVector(pathv);
1327 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1328 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1329 } else {
1330 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1331 }
1333 if (no_stroke)
1334 cairo_fill(_cr);
1335 else
1336 cairo_fill_preserve(_cr);
1337 }
1339 if (!no_stroke) {
1340 _setStrokeStyle(style, pbox);
1341 if (no_fill)
1342 setPathVector(pathv);
1344 cairo_stroke(_cr);
1345 }
1347 if (need_layer)
1348 popLayer();
1349 else
1350 cairo_restore(_cr);
1352 return true;
1353 }
1355 bool
1356 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1357 Geom::Matrix const *image_transform, SPStyle const *style)
1358 {
1359 g_assert( _is_valid );
1361 if (_render_mode == RENDER_MODE_CLIP)
1362 return true;
1364 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1365 if (!px_rgba)
1366 return false;
1368 float opacity;
1369 if (_state->merge_opacity)
1370 opacity = _state->opacity;
1371 else
1372 opacity = 1.0;
1374 // make a copy of the original pixbuf with premultiplied alpha
1375 // if we pass the original pixbuf it will get messed up
1376 for (unsigned i = 0; i < h; i++) {
1377 for (unsigned j = 0; j < w; j++) {
1378 guchar const *src = px + i * rs + j * 4;
1379 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1380 guchar r, g, b, alpha_dst;
1382 // calculate opacity-modified alpha
1383 alpha_dst = src[3];
1384 if (opacity != 1.0 && _vector_based_target)
1385 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1387 // premul alpha (needed because this will be undone by cairo-pdf)
1388 r = src[0]*alpha_dst/255;
1389 g = src[1]*alpha_dst/255;
1390 b = src[2]*alpha_dst/255;
1392 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1393 }
1394 }
1396 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1397 if (cairo_surface_status(image_surface)) {
1398 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1399 return false;
1400 }
1402 // setup automatic freeing of the image data when destroying the surface
1403 static cairo_user_data_key_t key;
1404 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1406 cairo_save(_cr);
1408 // scaling by width & height is not needed because it will be done by Cairo
1409 if (image_transform)
1410 transform(image_transform);
1412 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1414 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1415 if (_vector_based_target) {
1416 cairo_new_path(_cr);
1417 cairo_rectangle(_cr, 0, 0, w, h);
1418 cairo_clip(_cr);
1419 }
1421 if (_vector_based_target)
1422 cairo_paint(_cr);
1423 else
1424 cairo_paint_with_alpha(_cr, opacity);
1426 cairo_restore(_cr);
1428 cairo_surface_destroy(image_surface);
1430 return true;
1431 }
1433 #define GLYPH_ARRAY_SIZE 64
1435 unsigned int
1436 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1437 {
1438 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1439 cairo_glyph_t *glyphs = glyph_array;
1440 unsigned int num_glyphs = glyphtext.size();
1441 if (num_glyphs > GLYPH_ARRAY_SIZE)
1442 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1444 unsigned int num_invalid_glyphs = 0;
1445 unsigned int i = 0;
1446 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1447 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1448 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1449 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1450 TRACE(("INVALID GLYPH found\n"));
1451 num_invalid_glyphs++;
1452 continue;
1453 }
1454 glyphs[i - num_invalid_glyphs].index = it_info->index;
1455 glyphs[i - num_invalid_glyphs].x = it_info->x;
1456 glyphs[i - num_invalid_glyphs].y = it_info->y;
1457 i++;
1458 }
1460 if (is_stroke || _is_texttopath)
1461 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1462 else
1463 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1465 if (num_glyphs > GLYPH_ARRAY_SIZE)
1466 g_free(glyphs);
1468 return num_glyphs - num_invalid_glyphs;
1469 }
1471 bool
1472 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1473 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1474 {
1475 // create a cairo_font_face from PangoFont
1476 double size = style->font_size.computed;
1477 cairo_font_face_t *font_face = NULL;
1479 FcPattern *fc_pattern = NULL;
1481 #ifdef USE_PANGO_WIN32
1482 # ifdef CAIRO_HAS_WIN32_FONT
1483 LOGFONTA *lfa = pango_win32_font_logfont(font);
1484 LOGFONTW lfw;
1486 ZeroMemory(&lfw, sizeof(LOGFONTW));
1487 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1488 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1490 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1491 # endif
1492 #else
1493 # ifdef CAIRO_HAS_FT_FONT
1494 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1495 fc_pattern = fc_font->font_pattern;
1496 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1497 # endif
1498 #endif
1500 cairo_save(_cr);
1501 cairo_set_font_face(_cr, font_face);
1503 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1504 size = 12.0;
1506 // set the given font matrix
1507 cairo_matrix_t matrix;
1508 _initCairoMatrix(&matrix, font_matrix);
1509 cairo_set_font_matrix(_cr, &matrix);
1511 if (_render_mode == RENDER_MODE_CLIP) {
1512 if (_clip_mode == CLIP_MODE_MASK) {
1513 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1514 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1515 } else {
1516 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1517 }
1518 _showGlyphs(_cr, font, glyphtext, FALSE);
1519 } else {
1520 // just add the glyph paths to the current context
1521 _showGlyphs(_cr, font, glyphtext, TRUE);
1522 }
1523 } else {
1525 if (style->fill.isColor() || style->fill.isPaintserver()) {
1526 // set fill style
1527 _setFillStyle(style, NULL);
1529 _showGlyphs(_cr, font, glyphtext, FALSE);
1530 }
1532 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1533 // set stroke style
1534 _setStrokeStyle(style, NULL);
1536 // paint stroke
1537 _showGlyphs(_cr, font, glyphtext, TRUE);
1538 cairo_stroke(_cr);
1539 }
1540 }
1542 cairo_restore(_cr);
1544 if (font_face)
1545 cairo_font_face_destroy(font_face);
1547 return true;
1548 }
1550 /* Helper functions */
1552 void
1553 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1554 {
1555 cairo_new_path(_cr);
1556 addPathVector(pv);
1557 }
1559 void
1560 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1561 {
1562 feed_pathvector_to_cairo(_cr, pv);
1563 }
1565 void
1566 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1567 {
1568 cairo_matrix_t matrix;
1570 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1571 cairo_transform(cr, &matrix);
1572 }
1574 void
1575 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1576 {
1577 matrix->xx = (*transform)[0];
1578 matrix->yx = (*transform)[1];
1579 matrix->xy = (*transform)[2];
1580 matrix->yy = (*transform)[3];
1581 matrix->x0 = (*transform)[4];
1582 matrix->y0 = (*transform)[5];
1583 }
1585 void
1586 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1587 {
1588 _concatTransform(cr, (*transform)[0], (*transform)[1],
1589 (*transform)[2], (*transform)[3],
1590 (*transform)[4], (*transform)[5]);
1591 }
1593 static cairo_status_t
1594 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1595 {
1596 size_t written;
1597 FILE *file = (FILE*)closure;
1599 written = fwrite (data, 1, length, file);
1601 if (written == length)
1602 return CAIRO_STATUS_SUCCESS;
1603 else
1604 return CAIRO_STATUS_WRITE_ERROR;
1605 }
1607 #include "clear-n_.h"
1609 } /* namespace Internal */
1610 } /* namespace Extension */
1611 } /* namespace Inkscape */
1613 #undef TRACE
1614 #undef TEST
1616 /* End of GNU GPL code */
1619 /*
1620 Local Variables:
1621 mode:c++
1622 c-file-style:"stroustrup"
1623 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1624 indent-tabs-mode:nil
1625 fill-column:99
1626 End:
1627 */
1628 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :