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.set
1195 || style->fill.isColor()
1196 || style->fill.isPaintserver() );
1198 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1199 if (_state->merge_opacity) {
1200 alpha *= _state->opacity;
1201 TRACE(("merged op=%f\n", alpha));
1202 }
1204 if (style->fill.isColor()) {
1205 float rgb[3];
1206 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1208 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1210 } else if (!style->fill.set) { // unset fill is black
1211 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1213 } else {
1214 g_assert( style->fill.isPaintserver()
1215 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1216 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1218 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1220 if (pattern) {
1221 cairo_set_source(_cr, pattern);
1222 cairo_pattern_destroy(pattern);
1223 }
1224 }
1225 }
1227 void
1228 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1229 {
1230 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1231 if (_state->merge_opacity)
1232 alpha *= _state->opacity;
1234 if (style->stroke.isColor()) {
1235 float rgb[3];
1236 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1238 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1239 } else {
1240 g_assert( style->fill.isPaintserver()
1241 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1242 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1244 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1246 if (pattern) {
1247 cairo_set_source(_cr, pattern);
1248 cairo_pattern_destroy(pattern);
1249 }
1250 }
1252 if (style->stroke_dash.n_dash &&
1253 style->stroke_dash.dash )
1254 {
1255 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1256 } else {
1257 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1258 }
1260 cairo_set_line_width(_cr, style->stroke_width.computed);
1262 // set line join type
1263 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1264 switch (style->stroke_linejoin.computed) {
1265 case SP_STROKE_LINEJOIN_MITER:
1266 join = CAIRO_LINE_JOIN_MITER;
1267 break;
1268 case SP_STROKE_LINEJOIN_ROUND:
1269 join = CAIRO_LINE_JOIN_ROUND;
1270 break;
1271 case SP_STROKE_LINEJOIN_BEVEL:
1272 join = CAIRO_LINE_JOIN_BEVEL;
1273 break;
1274 }
1275 cairo_set_line_join(_cr, join);
1277 // set line cap type
1278 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1279 switch (style->stroke_linecap.computed) {
1280 case SP_STROKE_LINECAP_BUTT:
1281 cap = CAIRO_LINE_CAP_BUTT;
1282 break;
1283 case SP_STROKE_LINECAP_ROUND:
1284 cap = CAIRO_LINE_CAP_ROUND;
1285 break;
1286 case SP_STROKE_LINECAP_SQUARE:
1287 cap = CAIRO_LINE_CAP_SQUARE;
1288 break;
1289 }
1290 cairo_set_line_cap(_cr, cap);
1291 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1292 }
1294 bool
1295 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1296 {
1297 g_assert( _is_valid );
1299 if (_render_mode == RENDER_MODE_CLIP) {
1300 if (_clip_mode == CLIP_MODE_PATH) {
1301 addClipPath(pathv, &style->fill_rule);
1302 } else {
1303 setPathVector(pathv);
1304 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1305 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1306 } else {
1307 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1308 }
1309 cairo_fill(_cr);
1310 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1311 }
1312 return true;
1313 }
1315 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1316 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1317 style->stroke_opacity.value == 0;
1319 if (no_fill && no_stroke)
1320 return true;
1322 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1323 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1325 if (!need_layer)
1326 cairo_save(_cr);
1327 else
1328 pushLayer();
1330 if (!no_fill) {
1331 _setFillStyle(style, pbox);
1332 setPathVector(pathv);
1334 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1335 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1336 } else {
1337 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1338 }
1340 if (no_stroke)
1341 cairo_fill(_cr);
1342 else
1343 cairo_fill_preserve(_cr);
1344 }
1346 if (!no_stroke) {
1347 _setStrokeStyle(style, pbox);
1348 if (no_fill)
1349 setPathVector(pathv);
1351 cairo_stroke(_cr);
1352 }
1354 if (need_layer)
1355 popLayer();
1356 else
1357 cairo_restore(_cr);
1359 return true;
1360 }
1362 bool
1363 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1364 Geom::Matrix const *image_transform, SPStyle const *style)
1365 {
1366 g_assert( _is_valid );
1368 if (_render_mode == RENDER_MODE_CLIP)
1369 return true;
1371 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1372 if (!px_rgba)
1373 return false;
1375 float opacity;
1376 if (_state->merge_opacity)
1377 opacity = _state->opacity;
1378 else
1379 opacity = 1.0;
1381 // make a copy of the original pixbuf with premultiplied alpha
1382 // if we pass the original pixbuf it will get messed up
1383 for (unsigned i = 0; i < h; i++) {
1384 for (unsigned j = 0; j < w; j++) {
1385 guchar const *src = px + i * rs + j * 4;
1386 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1387 guchar r, g, b, alpha_dst;
1389 // calculate opacity-modified alpha
1390 alpha_dst = src[3];
1391 if (opacity != 1.0 && _vector_based_target)
1392 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1394 // premul alpha (needed because this will be undone by cairo-pdf)
1395 r = src[0]*alpha_dst/255;
1396 g = src[1]*alpha_dst/255;
1397 b = src[2]*alpha_dst/255;
1399 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1400 }
1401 }
1403 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1404 if (cairo_surface_status(image_surface)) {
1405 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1406 return false;
1407 }
1409 // setup automatic freeing of the image data when destroying the surface
1410 static cairo_user_data_key_t key;
1411 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1413 cairo_save(_cr);
1415 // scaling by width & height is not needed because it will be done by Cairo
1416 if (image_transform)
1417 transform(image_transform);
1419 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1421 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1422 if (_vector_based_target) {
1423 cairo_new_path(_cr);
1424 cairo_rectangle(_cr, 0, 0, w, h);
1425 cairo_clip(_cr);
1426 }
1428 if (_vector_based_target)
1429 cairo_paint(_cr);
1430 else
1431 cairo_paint_with_alpha(_cr, opacity);
1433 cairo_restore(_cr);
1435 cairo_surface_destroy(image_surface);
1437 return true;
1438 }
1440 #define GLYPH_ARRAY_SIZE 64
1442 unsigned int
1443 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1444 {
1445 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1446 cairo_glyph_t *glyphs = glyph_array;
1447 unsigned int num_glyphs = glyphtext.size();
1448 if (num_glyphs > GLYPH_ARRAY_SIZE)
1449 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1451 unsigned int num_invalid_glyphs = 0;
1452 unsigned int i = 0;
1453 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1454 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1455 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1456 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1457 TRACE(("INVALID GLYPH found\n"));
1458 num_invalid_glyphs++;
1459 continue;
1460 }
1461 glyphs[i - num_invalid_glyphs].index = it_info->index;
1462 glyphs[i - num_invalid_glyphs].x = it_info->x;
1463 glyphs[i - num_invalid_glyphs].y = it_info->y;
1464 i++;
1465 }
1467 if (is_stroke) {
1468 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1469 } else {
1470 if (_is_texttopath) {
1471 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1472 cairo_fill_preserve(cr);
1473 } else {
1474 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1475 }
1476 }
1478 if (num_glyphs > GLYPH_ARRAY_SIZE)
1479 g_free(glyphs);
1481 return num_glyphs - num_invalid_glyphs;
1482 }
1484 bool
1485 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1486 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1487 {
1488 // create a cairo_font_face from PangoFont
1489 double size = style->font_size.computed;
1490 cairo_font_face_t *font_face = NULL;
1492 FcPattern *fc_pattern = NULL;
1494 #ifdef USE_PANGO_WIN32
1495 # ifdef CAIRO_HAS_WIN32_FONT
1496 LOGFONTA *lfa = pango_win32_font_logfont(font);
1497 LOGFONTW lfw;
1499 ZeroMemory(&lfw, sizeof(LOGFONTW));
1500 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1501 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1503 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1504 # endif
1505 #else
1506 # ifdef CAIRO_HAS_FT_FONT
1507 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1508 fc_pattern = fc_font->font_pattern;
1509 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1510 # endif
1511 #endif
1513 cairo_save(_cr);
1514 cairo_set_font_face(_cr, font_face);
1516 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1517 size = 12.0;
1519 // set the given font matrix
1520 cairo_matrix_t matrix;
1521 _initCairoMatrix(&matrix, font_matrix);
1522 cairo_set_font_matrix(_cr, &matrix);
1524 if (_render_mode == RENDER_MODE_CLIP) {
1525 if (_clip_mode == CLIP_MODE_MASK) {
1526 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1527 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1528 } else {
1529 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1530 }
1531 _showGlyphs(_cr, font, glyphtext, FALSE);
1532 } else {
1533 // just add the glyph paths to the current context
1534 _showGlyphs(_cr, font, glyphtext, TRUE);
1535 }
1536 } else {
1538 if (style->fill.isColor() || style->fill.isPaintserver()) {
1539 // set fill style
1540 _setFillStyle(style, NULL);
1542 _showGlyphs(_cr, font, glyphtext, FALSE);
1543 }
1545 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1546 // set stroke style
1547 _setStrokeStyle(style, NULL);
1549 // paint stroke
1550 _showGlyphs(_cr, font, glyphtext, TRUE);
1551 cairo_stroke(_cr);
1552 }
1553 }
1555 cairo_restore(_cr);
1557 if (font_face)
1558 cairo_font_face_destroy(font_face);
1560 return true;
1561 }
1563 /* Helper functions */
1565 void
1566 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1567 {
1568 cairo_new_path(_cr);
1569 addPathVector(pv);
1570 }
1572 void
1573 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1574 {
1575 feed_pathvector_to_cairo(_cr, pv);
1576 }
1578 void
1579 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1580 {
1581 cairo_matrix_t matrix;
1583 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1584 cairo_transform(cr, &matrix);
1585 }
1587 void
1588 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1589 {
1590 matrix->xx = (*transform)[0];
1591 matrix->yx = (*transform)[1];
1592 matrix->xy = (*transform)[2];
1593 matrix->yy = (*transform)[3];
1594 matrix->x0 = (*transform)[4];
1595 matrix->y0 = (*transform)[5];
1596 }
1598 void
1599 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1600 {
1601 _concatTransform(cr, (*transform)[0], (*transform)[1],
1602 (*transform)[2], (*transform)[3],
1603 (*transform)[4], (*transform)[5]);
1604 }
1606 static cairo_status_t
1607 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1608 {
1609 size_t written;
1610 FILE *file = (FILE*)closure;
1612 written = fwrite (data, 1, length, file);
1614 if (written == length)
1615 return CAIRO_STATUS_SUCCESS;
1616 else
1617 return CAIRO_STATUS_WRITE_ERROR;
1618 }
1620 #include "clear-n_.h"
1622 } /* namespace Internal */
1623 } /* namespace Extension */
1624 } /* namespace Inkscape */
1626 #undef TRACE
1627 #undef TEST
1629 /* End of GNU GPL code */
1632 /*
1633 Local Variables:
1634 mode:c++
1635 c-file-style:"stroustrup"
1636 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1637 indent-tabs-mode:nil
1638 fill-column:99
1639 End:
1640 */
1641 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :