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) g_message _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 {
129 font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
130 }
132 CairoRenderContext::~CairoRenderContext(void)
133 {
134 if(font_table != NULL) {
135 g_hash_table_remove_all(font_table);
136 }
138 if (_cr) cairo_destroy(_cr);
139 if (_surface) cairo_surface_destroy(_surface);
140 if (_layout) g_object_unref(_layout);
141 }
142 void CairoRenderContext::font_data_free(gpointer data)
143 {
144 cairo_font_face_t *font_face = (cairo_font_face_t *)data;
145 if (font_face) {
146 cairo_font_face_destroy(font_face);
147 }
148 }
150 CairoRenderer*
151 CairoRenderContext::getRenderer(void) const
152 {
153 return _renderer;
154 }
156 CairoRenderState*
157 CairoRenderContext::getCurrentState(void) const
158 {
159 return _state;
160 }
162 CairoRenderState*
163 CairoRenderContext::getParentState(void) const
164 {
165 // if this is the root node just return it
166 if (g_slist_length(_state_stack) == 1) {
167 return _state;
168 } else {
169 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
170 }
171 }
173 void
174 CairoRenderContext::setStateForStyle(SPStyle const *style)
175 {
176 // only opacity & overflow is stored for now
177 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
178 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
179 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
181 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
182 _state->merge_opacity = FALSE;
184 // disable rendering of opacity if there's a stroke on the fill
185 if (_state->merge_opacity
186 && !style->fill.isNone()
187 && !style->stroke.isNone())
188 _state->merge_opacity = FALSE;
189 }
191 /**
192 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
193 *
194 * \param width width of the surface to be created
195 * \param height height of the surface to be created
196 */
197 CairoRenderContext*
198 CairoRenderContext::cloneMe(double width, double height) const
199 {
200 g_assert( _is_valid );
201 g_assert( width > 0.0 && height > 0.0 );
203 CairoRenderContext *new_context = _renderer->createContext();
204 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
205 (int)ceil(width), (int)ceil(height));
206 new_context->_cr = cairo_create(surface);
207 new_context->_surface = surface;
208 new_context->_width = width;
209 new_context->_height = height;
210 new_context->_is_valid = TRUE;
212 return new_context;
213 }
215 CairoRenderContext*
216 CairoRenderContext::cloneMe(void) const
217 {
218 g_assert( _is_valid );
220 return cloneMe(_width, _height);
221 }
223 bool
224 CairoRenderContext::setImageTarget(cairo_format_t format)
225 {
226 // format cannot be set on an already initialized surface
227 if (_is_valid)
228 return false;
230 switch (format) {
231 case CAIRO_FORMAT_ARGB32:
232 case CAIRO_FORMAT_RGB24:
233 case CAIRO_FORMAT_A8:
234 case CAIRO_FORMAT_A1:
235 _target_format = format;
236 _target = CAIRO_SURFACE_TYPE_IMAGE;
237 return true;
238 break;
239 default:
240 break;
241 }
243 return false;
244 }
246 bool
247 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
248 {
249 #ifndef CAIRO_HAS_PDF_SURFACE
250 return false;
251 #else
252 _target = CAIRO_SURFACE_TYPE_PDF;
253 _vector_based_target = TRUE;
254 #endif
256 FILE *osf = NULL;
257 FILE *osp = NULL;
259 gsize bytesRead = 0;
260 gsize bytesWritten = 0;
261 GError *error = NULL;
262 gchar *local_fn = g_filename_from_utf8(utf8_fn,
263 -1, &bytesRead, &bytesWritten, &error);
264 gchar const *fn = local_fn;
266 /* TODO: Replace the below fprintf's with something that does the right thing whether in
267 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
268 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
269 * return code.
270 */
271 if (fn != NULL) {
272 if (*fn == '|') {
273 fn += 1;
274 while (isspace(*fn)) fn += 1;
275 #ifndef WIN32
276 osp = popen(fn, "w");
277 #else
278 osp = _popen(fn, "w");
279 #endif
280 if (!osp) {
281 fprintf(stderr, "inkscape: popen(%s): %s\n",
282 fn, strerror(errno));
283 return false;
284 }
285 _stream = osp;
286 } else if (*fn == '>') {
287 fn += 1;
288 while (isspace(*fn)) fn += 1;
289 Inkscape::IO::dump_fopen_call(fn, "K");
290 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
291 if (!osf) {
292 fprintf(stderr, "inkscape: fopen(%s): %s\n",
293 fn, strerror(errno));
294 return false;
295 }
296 _stream = osf;
297 } else {
298 /* put cwd stuff in here */
299 gchar *qn = ( *fn
300 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
301 : g_strdup("lpr") );
302 #ifndef WIN32
303 osp = popen(qn, "w");
304 #else
305 osp = _popen(qn, "w");
306 #endif
307 if (!osp) {
308 fprintf(stderr, "inkscape: popen(%s): %s\n",
309 qn, strerror(errno));
310 return false;
311 }
312 g_free(qn);
313 _stream = osp;
314 }
315 }
317 g_free(local_fn);
319 if (_stream) {
320 /* fixme: this is kinda icky */
321 #if !defined(_WIN32) && !defined(__WIN32__)
322 (void) signal(SIGPIPE, SIG_IGN);
323 #endif
324 }
326 return true;
327 }
329 bool
330 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
331 {
332 #ifndef CAIRO_HAS_PS_SURFACE
333 return false;
334 #else
335 _target = CAIRO_SURFACE_TYPE_PS;
336 _vector_based_target = TRUE;
337 #endif
339 FILE *osf = NULL;
340 FILE *osp = NULL;
342 gsize bytesRead = 0;
343 gsize bytesWritten = 0;
344 GError *error = NULL;
345 gchar *local_fn = g_filename_from_utf8(utf8_fn,
346 -1, &bytesRead, &bytesWritten, &error);
347 gchar const *fn = local_fn;
349 /* TODO: Replace the below fprintf's with something that does the right thing whether in
350 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
351 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
352 * return code.
353 */
354 if (fn != NULL) {
355 if (*fn == '|') {
356 fn += 1;
357 while (isspace(*fn)) fn += 1;
358 #ifndef WIN32
359 osp = popen(fn, "w");
360 #else
361 osp = _popen(fn, "w");
362 #endif
363 if (!osp) {
364 fprintf(stderr, "inkscape: popen(%s): %s\n",
365 fn, strerror(errno));
366 return false;
367 }
368 _stream = osp;
369 } else if (*fn == '>') {
370 fn += 1;
371 while (isspace(*fn)) fn += 1;
372 Inkscape::IO::dump_fopen_call(fn, "K");
373 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
374 if (!osf) {
375 fprintf(stderr, "inkscape: fopen(%s): %s\n",
376 fn, strerror(errno));
377 return false;
378 }
379 _stream = osf;
380 } else {
381 /* put cwd stuff in here */
382 gchar *qn = ( *fn
383 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
384 : g_strdup("lpr") );
385 #ifndef WIN32
386 osp = popen(qn, "w");
387 #else
388 osp = _popen(qn, "w");
389 #endif
390 if (!osp) {
391 fprintf(stderr, "inkscape: popen(%s): %s\n",
392 qn, strerror(errno));
393 return false;
394 }
395 g_free(qn);
396 _stream = osp;
397 }
398 }
400 g_free(local_fn);
402 if (_stream) {
403 /* fixme: this is kinda icky */
404 #if !defined(_WIN32) && !defined(__WIN32__)
405 (void) signal(SIGPIPE, SIG_IGN);
406 #endif
407 }
409 return true;
410 }
412 void CairoRenderContext::setPSLevel(unsigned int level)
413 {
414 _ps_level = level;
415 }
417 void CairoRenderContext::setEPS(bool eps)
418 {
419 _eps = eps;
420 }
422 unsigned int CairoRenderContext::getPSLevel(void)
423 {
424 return _ps_level;
425 }
427 void CairoRenderContext::setPDFLevel(unsigned int level)
428 {
429 _pdf_level = level;
430 }
432 void CairoRenderContext::setTextToPath(bool texttopath)
433 {
434 _is_texttopath = texttopath;
435 }
437 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
438 {
439 _is_filtertobitmap = filtertobitmap;
440 }
442 bool CairoRenderContext::getFilterToBitmap(void)
443 {
444 return _is_filtertobitmap;
445 }
447 void CairoRenderContext::setBitmapResolution(int resolution)
448 {
449 _bitmapresolution = resolution;
450 }
452 int CairoRenderContext::getBitmapResolution(void)
453 {
454 return _bitmapresolution;
455 }
457 cairo_surface_t*
458 CairoRenderContext::getSurface(void)
459 {
460 g_assert( _is_valid );
462 return _surface;
463 }
465 bool
466 CairoRenderContext::saveAsPng(const char *file_name)
467 {
468 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
469 if (status)
470 return false;
471 else
472 return true;
473 }
475 void
476 CairoRenderContext::setRenderMode(CairoRenderMode mode)
477 {
478 switch (mode) {
479 case RENDER_MODE_NORMAL:
480 case RENDER_MODE_CLIP:
481 _render_mode = mode;
482 break;
483 default:
484 _render_mode = RENDER_MODE_NORMAL;
485 break;
486 }
487 }
489 CairoRenderContext::CairoRenderMode
490 CairoRenderContext::getRenderMode(void) const
491 {
492 return _render_mode;
493 }
495 void
496 CairoRenderContext::setClipMode(CairoClipMode mode)
497 {
498 switch (mode) {
499 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
500 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
501 _clip_mode = mode;
502 break;
503 default:
504 _clip_mode = CLIP_MODE_PATH;
505 break;
506 }
507 }
509 CairoRenderContext::CairoClipMode
510 CairoRenderContext::getClipMode(void) const
511 {
512 return _clip_mode;
513 }
515 CairoRenderState*
516 CairoRenderContext::_createState(void)
517 {
518 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
519 g_assert( state != NULL );
521 state->has_filtereffect = FALSE;
522 state->merge_opacity = TRUE;
523 state->opacity = 1.0;
524 state->need_layer = FALSE;
525 state->has_overflow = FALSE;
526 state->parent_has_userspace = FALSE;
527 state->clip_path = NULL;
528 state->mask = NULL;
530 return state;
531 }
533 void
534 CairoRenderContext::pushLayer(void)
535 {
536 g_assert( _is_valid );
538 TRACE(("--pushLayer\n"));
539 cairo_push_group(_cr);
541 // clear buffer
542 if (!_vector_based_target) {
543 cairo_save(_cr);
544 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
545 cairo_paint(_cr);
546 cairo_restore(_cr);
547 }
548 }
550 void
551 CairoRenderContext::popLayer(void)
552 {
553 g_assert( _is_valid );
555 float opacity = _state->opacity;
556 TRACE(("--popLayer w/ opacity %f\n", opacity));
558 /*
559 At this point, the Cairo source is ready. A Cairo mask must be created if required.
560 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
561 masks independently of the objects they effect while in SVG the clip paths and masks
562 are defined relative to the objects they are attached to.
563 Notes:
564 1. An SVG object may have both a clip path and a mask!
565 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
566 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
567 the page (document). This is also not handled properly.
568 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
569 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
570 alpha. This is handled here by doing a pixel by pixel conversion.
571 */
573 SPClipPath *clip_path = _state->clip_path;
574 SPMask *mask = _state->mask;
575 if (clip_path || mask) {
577 CairoRenderContext *clip_ctx = 0;
578 cairo_surface_t *clip_mask = 0;
580 // Apply any clip path first
581 if (clip_path) {
582 TRACE((" Applying clip\n"));
583 if (_render_mode == RENDER_MODE_CLIP)
584 mask = NULL; // disable mask when performing nested clipping
586 if (_vector_based_target) {
587 setClipMode(CLIP_MODE_PATH); // Vector
588 if (!mask) {
589 cairo_pop_group_to_source(_cr);
590 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
591 if (opacity == 1.0)
592 cairo_paint(_cr);
593 else
594 cairo_paint_with_alpha(_cr, opacity);
596 } else {
597 // the clipPath will be applied before masking
598 }
599 } else {
601 // setup a new rendering context
602 clip_ctx = _renderer->createContext();
603 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
604 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
605 // This code ties the clipping to the document coordinates. It doesn't allow
606 // for a clipped object intially drawn off the page and then translated onto
607 // the page.
608 if (!clip_ctx->setupSurface(_width, _height)) {
609 TRACE(("clip: setupSurface failed\n"));
610 _renderer->destroyContext(clip_ctx);
611 return;
612 }
614 // clear buffer
615 cairo_save(clip_ctx->_cr);
616 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
617 cairo_paint(clip_ctx->_cr);
618 cairo_restore(clip_ctx->_cr);
620 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
621 if (!mask)
622 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
623 else
624 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
626 // copy over the correct CTM
627 // It must be stored in item_transform of current state after pushState.
628 Geom::Matrix item_transform;
629 if (_state->parent_has_userspace)
630 item_transform = getParentState()->transform * _state->item_transform;
631 else
632 item_transform = _state->item_transform;
634 // apply the clip path
635 clip_ctx->pushState();
636 clip_ctx->getCurrentState()->item_transform = item_transform;
637 _renderer->applyClipPath(clip_ctx, clip_path);
638 clip_ctx->popState();
640 clip_mask = clip_ctx->getSurface();
641 TEST(clip_ctx->saveAsPng("clip_mask.png"));
643 if (!mask) {
644 cairo_pop_group_to_source(_cr);
645 cairo_mask_surface(_cr, clip_mask, 0, 0);
646 _renderer->destroyContext(clip_ctx);
647 }
648 }
649 }
651 // Apply any mask second
652 if (mask) {
653 TRACE((" Applying mask\n"));
654 // create rendering context for mask
655 CairoRenderContext *mask_ctx = _renderer->createContext();
657 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
658 // Cairo surface is expecting the mask to be 90 dpi.
659 float surface_width = _width;
660 float surface_height = _height;
661 if( _vector_based_target ) {
662 surface_width *= 1.25;
663 surface_height *= 1.25;
664 }
665 if (!mask_ctx->setupSurface( surface_width, surface_height )) {
666 TRACE(("mask: setupSurface failed\n"));
667 _renderer->destroyContext(mask_ctx);
668 return;
669 }
670 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
672 // set rendering mode to normal
673 setRenderMode(RENDER_MODE_NORMAL);
675 // copy the correct CTM to mask context
676 /*
677 if (_state->parent_has_userspace)
678 mask_ctx->setTransform(&getParentState()->transform);
679 else
680 mask_ctx->setTransform(&_state->transform);
681 */
682 // This is probably not correct... but it seems to do the trick.
683 mask_ctx->setTransform(&_state->item_transform);
685 // render mask contents to mask_ctx
686 _renderer->applyMask(mask_ctx, mask);
688 TEST(mask_ctx->saveAsPng("mask.png"));
690 // composite with clip mask
691 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
692 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
693 _renderer->destroyContext(clip_ctx);
694 }
696 cairo_surface_t *mask_image = mask_ctx->getSurface();
697 int width = cairo_image_surface_get_width(mask_image);
698 int height = cairo_image_surface_get_height(mask_image);
699 int stride = cairo_image_surface_get_stride(mask_image);
700 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
702 // premultiply with opacity
703 // In SVG, the rgb channels as well as the alpha channel is used in masking.
704 // In Cairo, only the alpha channel is used thus requiring this conversion.
705 TRACE(("premul w/ %f\n", opacity));
706 guint8 int_opacity = (guint8)(255 * opacity);
707 for (int row = 0 ; row < height; row++) {
708 unsigned char *row_data = pixels + (row * stride);
709 for (int i = 0 ; i < width; i++) {
710 guint32 *pixel = (guint32 *)row_data + i;
711 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
712 ((*pixel & 0x0000ff00) >> 8) * 46518 +
713 ((*pixel & 0x000000ff) ) * 4688) *
714 int_opacity);
715 }
716 }
718 cairo_pop_group_to_source(_cr);
719 if (_clip_mode == CLIP_MODE_PATH) {
720 // we have to do the clipping after cairo_pop_group_to_source
721 _renderer->applyClipPath(this, clip_path);
722 }
723 // apply the mask onto the layer
724 cairo_mask_surface(_cr, mask_image, 0, 0);
725 _renderer->destroyContext(mask_ctx);
726 }
727 } else {
728 // No clip path or mask
729 cairo_pop_group_to_source(_cr);
730 if (opacity == 1.0)
731 cairo_paint(_cr);
732 else
733 cairo_paint_with_alpha(_cr, opacity);
734 }
735 }
737 void
738 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
739 {
740 g_assert( _is_valid );
742 // here it should be checked whether the current clip winding changed
743 // so we could switch back to masked clipping
744 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
745 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
746 } else {
747 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
748 }
749 addPathVector(pv);
750 }
752 void
753 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
754 {
755 g_assert( _is_valid );
757 cairo_rectangle(_cr, x, y, width, height);
758 cairo_clip(_cr);
759 }
761 bool
762 CairoRenderContext::setupSurface(double width, double height)
763 {
764 // Is the surface already set up?
765 if (_is_valid)
766 return true;
768 if (_vector_based_target && _stream == NULL)
769 return false;
771 _width = width;
772 _height = height;
774 cairo_surface_t *surface = NULL;
775 cairo_matrix_t ctm;
776 cairo_matrix_init_identity (&ctm);
777 switch (_target) {
778 case CAIRO_SURFACE_TYPE_IMAGE:
779 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
780 break;
781 #ifdef CAIRO_HAS_PDF_SURFACE
782 case CAIRO_SURFACE_TYPE_PDF:
783 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
784 break;
785 #endif
786 #ifdef CAIRO_HAS_PS_SURFACE
787 case CAIRO_SURFACE_TYPE_PS:
788 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
789 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
790 return FALSE;
791 }
792 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
793 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
794 cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
795 #endif
796 break;
797 #endif
798 default:
799 return false;
800 break;
801 }
803 return _finishSurfaceSetup (surface, &ctm);
804 }
806 bool
807 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
808 {
809 if (_is_valid || !surface)
810 return false;
812 _vector_based_target = is_vector;
813 bool ret = _finishSurfaceSetup (surface, ctm);
814 if (ret)
815 cairo_surface_reference (surface);
816 return ret;
817 }
819 bool
820 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
821 {
822 g_message("enter");
823 if(surface == NULL) {
824 return false;
825 }
826 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
827 return false;
828 }
830 _cr = cairo_create(surface);
831 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
832 return false;
833 }
834 if (ctm)
835 cairo_set_matrix(_cr, ctm);
836 _surface = surface;
838 if (_vector_based_target) {
839 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
840 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
841 // set background color on non-alpha surfaces
842 // TODO: bgcolor should be derived from SPDocument
843 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
844 cairo_rectangle(_cr, 0, 0, _width, _height);
845 cairo_fill(_cr);
846 }
848 _is_valid = TRUE;
849 g_message("leave");
850 return true;
851 }
853 bool
854 CairoRenderContext::finish(void)
855 {
856 g_assert( _is_valid );
858 if (_vector_based_target)
859 cairo_show_page(_cr);
861 cairo_destroy(_cr);
862 cairo_surface_finish(_surface);
863 cairo_status_t status = cairo_surface_status(_surface);
864 cairo_surface_destroy(_surface);
865 _cr = NULL;
866 _surface = NULL;
868 if (_layout)
869 g_object_unref(_layout);
871 _is_valid = FALSE;
873 if (_vector_based_target && _stream) {
874 /* Flush stream to be sure. */
875 (void) fflush(_stream);
877 fclose(_stream);
878 _stream = NULL;
879 }
881 if (status == CAIRO_STATUS_SUCCESS)
882 return true;
883 else
884 return false;
885 }
887 void
888 CairoRenderContext::transform(Geom::Matrix const *transform)
889 {
890 g_assert( _is_valid );
892 cairo_matrix_t matrix;
893 _initCairoMatrix(&matrix, transform);
894 cairo_transform(_cr, &matrix);
896 // store new CTM
897 getTransform(&_state->transform);
898 }
900 void
901 CairoRenderContext::setTransform(Geom::Matrix const *transform)
902 {
903 g_assert( _is_valid );
905 cairo_matrix_t matrix;
906 _initCairoMatrix(&matrix, transform);
907 cairo_set_matrix(_cr, &matrix);
908 _state->transform = *transform;
909 }
911 void
912 CairoRenderContext::getTransform(Geom::Matrix *copy) const
913 {
914 g_assert( _is_valid );
916 cairo_matrix_t ctm;
917 cairo_get_matrix(_cr, &ctm);
918 (*copy)[0] = ctm.xx;
919 (*copy)[1] = ctm.yx;
920 (*copy)[2] = ctm.xy;
921 (*copy)[3] = ctm.yy;
922 (*copy)[4] = ctm.x0;
923 (*copy)[5] = ctm.y0;
924 }
926 void
927 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
928 {
929 g_assert( _is_valid );
931 CairoRenderState *parent_state = getParentState();
932 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
933 }
935 void
936 CairoRenderContext::pushState(void)
937 {
938 g_assert( _is_valid );
940 cairo_save(_cr);
942 CairoRenderState *new_state = _createState();
943 // copy current state's transform
944 new_state->transform = _state->transform;
945 _state_stack = g_slist_prepend(_state_stack, new_state);
946 _state = new_state;
947 }
949 void
950 CairoRenderContext::popState(void)
951 {
952 g_assert( _is_valid );
954 cairo_restore(_cr);
956 g_free(_state_stack->data);
957 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
958 _state = (CairoRenderState*)_state_stack->data;
960 g_assert( g_slist_length(_state_stack) > 0 );
961 }
963 static bool pattern_hasItemChildren (SPPattern *pat)
964 {
965 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
966 if (SP_IS_ITEM (child)) {
967 return true;
968 }
969 }
970 return false;
971 }
973 cairo_pattern_t*
974 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
975 {
976 g_assert( SP_IS_PATTERN(paintserver) );
978 SPPattern *pat = SP_PATTERN (paintserver);
980 Geom::Matrix ps2user, pcs2dev;
981 ps2user = Geom::identity();
982 pcs2dev = Geom::identity();
984 double x = pattern_x(pat);
985 double y = pattern_y(pat);
986 double width = pattern_width(pat);
987 double height = pattern_height(pat);
988 double bbox_width_scaler;
989 double bbox_height_scaler;
991 TRACE(("%f x %f pattern\n", width, height));
993 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
994 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
995 bbox_width_scaler = pbox->x1 - pbox->x0;
996 bbox_height_scaler = pbox->y1 - pbox->y0;
997 ps2user[4] = x * bbox_width_scaler + pbox->x0;
998 ps2user[5] = y * bbox_height_scaler + pbox->y0;
999 } else {
1000 bbox_width_scaler = 1.0;
1001 bbox_height_scaler = 1.0;
1002 ps2user[4] = x;
1003 ps2user[5] = y;
1004 }
1006 // apply pattern transformation
1007 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1008 ps2user *= pattern_transform;
1009 Geom::Point ori (ps2user[4], ps2user[5]);
1011 // create pattern contents coordinate system
1012 if (pat->viewBox_set) {
1013 NRRect *view_box = pattern_viewBox(pat);
1015 double x, y, w, h;
1016 double view_width, view_height;
1017 x = 0;
1018 y = 0;
1019 w = width * bbox_width_scaler;
1020 h = height * bbox_height_scaler;
1022 view_width = view_box->x1 - view_box->x0;
1023 view_height = view_box->y1 - view_box->y0;
1025 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1026 pcs2dev[0] = w / view_width;
1027 pcs2dev[3] = h / view_height;
1028 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1029 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1030 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1031 pcs2dev[0] = pbox->x1 - pbox->x0;
1032 pcs2dev[3] = pbox->y1 - pbox->y0;
1034 }
1036 // Calculate the size of the surface which has to be created
1037 #define SUBPIX_SCALE 100
1038 // Cairo requires an integer pattern surface width/height.
1039 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1040 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1041 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1042 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1043 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1044 // create new rendering context
1045 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1047 // adjust the size of the painted pattern to fit exactly the created surface
1048 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1049 double scale_width = surface_width / (bbox_width_scaler * width);
1050 double scale_height = surface_height / (bbox_height_scaler * height);
1051 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1052 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1053 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1054 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1055 }
1057 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1058 ps2user[4] = ori[Geom::X];
1059 ps2user[5] = ori[Geom::Y];
1061 pattern_ctx->setTransform(&pcs2dev);
1062 pattern_ctx->pushState();
1064 // create arena and group
1065 NRArena *arena = NRArena::create();
1066 unsigned dkey = sp_item_display_key_new(1);
1068 // show items and render them
1069 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1070 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1071 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1072 if (SP_IS_ITEM (child)) {
1073 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1074 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1075 }
1076 }
1077 break; // do not go further up the chain if children are found
1078 }
1079 }
1081 pattern_ctx->popState();
1083 // setup a cairo_pattern_t
1084 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1085 TEST(pattern_ctx->saveAsPng("pattern.png"));
1086 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1087 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1089 // set pattern transformation
1090 cairo_matrix_t pattern_matrix;
1091 _initCairoMatrix(&pattern_matrix, &ps2user);
1092 cairo_matrix_invert(&pattern_matrix);
1093 cairo_pattern_set_matrix(result, &pattern_matrix);
1095 delete pattern_ctx;
1097 // hide all items
1098 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1099 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1100 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1101 if (SP_IS_ITEM (child)) {
1102 sp_item_invoke_hide (SP_ITEM (child), dkey);
1103 }
1104 }
1105 break; // do not go further up the chain if children are found
1106 }
1107 }
1109 return result;
1110 }
1112 cairo_pattern_t*
1113 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1114 NRRect const *pbox, float alpha)
1115 {
1116 cairo_pattern_t *pattern = NULL;
1117 bool apply_bbox2user = FALSE;
1119 if (SP_IS_LINEARGRADIENT (paintserver)) {
1121 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1123 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1125 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1126 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1127 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1128 // convert to userspace
1129 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1130 p1 *= bbox2user;
1131 p2 *= bbox2user;
1132 }
1134 // create linear gradient pattern
1135 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1137 // add stops
1138 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1139 float rgb[3];
1140 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1141 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1142 }
1143 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1145 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1147 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1149 Geom::Point c (rg->cx.computed, rg->cy.computed);
1150 Geom::Point f (rg->fx.computed, rg->fy.computed);
1151 double r = rg->r.computed;
1152 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1153 apply_bbox2user = true;
1155 // create radial gradient pattern
1156 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1158 // add stops
1159 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1160 float rgb[3];
1161 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1162 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1163 }
1164 } else if (SP_IS_PATTERN (paintserver)) {
1166 pattern = _createPatternPainter(paintserver, pbox);
1167 } else {
1168 return NULL;
1169 }
1171 if (pattern && SP_IS_GRADIENT (paintserver)) {
1172 SPGradient *g = SP_GRADIENT(paintserver);
1174 // set extend type
1175 SPGradientSpread spread = sp_gradient_get_spread(g);
1176 switch (spread) {
1177 case SP_GRADIENT_SPREAD_REPEAT: {
1178 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1179 break;
1180 }
1181 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1182 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1183 break;
1184 }
1185 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1186 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1187 break;
1188 }
1189 default: {
1190 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1191 break;
1192 }
1193 }
1195 cairo_matrix_t pattern_matrix;
1196 if (g->gradientTransform_set) {
1197 // apply gradient transformation
1198 cairo_matrix_init(&pattern_matrix,
1199 g->gradientTransform[0], g->gradientTransform[1],
1200 g->gradientTransform[2], g->gradientTransform[3],
1201 g->gradientTransform[4], g->gradientTransform[5]);
1202 } else {
1203 cairo_matrix_init_identity (&pattern_matrix);
1204 }
1206 if (apply_bbox2user) {
1207 // convert to userspace
1208 cairo_matrix_t bbox2user;
1209 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1210 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1211 }
1212 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1213 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1214 }
1216 return pattern;
1217 }
1219 void
1220 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1221 {
1222 g_return_if_fail( !style->fill.set
1223 || style->fill.isColor()
1224 || style->fill.isPaintserver() );
1226 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1227 if (_state->merge_opacity) {
1228 alpha *= _state->opacity;
1229 TRACE(("merged op=%f\n", alpha));
1230 }
1232 if (style->fill.isColor()) {
1233 float rgb[3];
1234 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1236 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1238 } else if (!style->fill.set) { // unset fill is black
1239 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1241 } else {
1242 g_assert( style->fill.isPaintserver()
1243 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1244 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1246 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1248 if (pattern) {
1249 cairo_set_source(_cr, pattern);
1250 cairo_pattern_destroy(pattern);
1251 }
1252 }
1253 }
1255 void
1256 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1257 {
1258 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1259 if (_state->merge_opacity)
1260 alpha *= _state->opacity;
1262 if (style->stroke.isColor()) {
1263 float rgb[3];
1264 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1266 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1267 } else {
1268 g_assert( style->fill.isPaintserver()
1269 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1270 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1272 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1274 if (pattern) {
1275 cairo_set_source(_cr, pattern);
1276 cairo_pattern_destroy(pattern);
1277 }
1278 }
1280 if (style->stroke_dash.n_dash &&
1281 style->stroke_dash.dash )
1282 {
1283 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1284 } else {
1285 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1286 }
1288 cairo_set_line_width(_cr, style->stroke_width.computed);
1290 // set line join type
1291 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1292 switch (style->stroke_linejoin.computed) {
1293 case SP_STROKE_LINEJOIN_MITER:
1294 join = CAIRO_LINE_JOIN_MITER;
1295 break;
1296 case SP_STROKE_LINEJOIN_ROUND:
1297 join = CAIRO_LINE_JOIN_ROUND;
1298 break;
1299 case SP_STROKE_LINEJOIN_BEVEL:
1300 join = CAIRO_LINE_JOIN_BEVEL;
1301 break;
1302 }
1303 cairo_set_line_join(_cr, join);
1305 // set line cap type
1306 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1307 switch (style->stroke_linecap.computed) {
1308 case SP_STROKE_LINECAP_BUTT:
1309 cap = CAIRO_LINE_CAP_BUTT;
1310 break;
1311 case SP_STROKE_LINECAP_ROUND:
1312 cap = CAIRO_LINE_CAP_ROUND;
1313 break;
1314 case SP_STROKE_LINECAP_SQUARE:
1315 cap = CAIRO_LINE_CAP_SQUARE;
1316 break;
1317 }
1318 cairo_set_line_cap(_cr, cap);
1319 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1320 }
1322 bool
1323 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1324 {
1325 g_assert( _is_valid );
1327 if (_render_mode == RENDER_MODE_CLIP) {
1328 if (_clip_mode == CLIP_MODE_PATH) {
1329 addClipPath(pathv, &style->fill_rule);
1330 } else {
1331 setPathVector(pathv);
1332 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1333 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1334 } else {
1335 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1336 }
1337 cairo_fill(_cr);
1338 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1339 }
1340 return true;
1341 }
1343 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1344 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1345 style->stroke_opacity.value == 0;
1347 if (no_fill && no_stroke)
1348 return true;
1350 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1351 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1353 if (!need_layer)
1354 cairo_save(_cr);
1355 else
1356 pushLayer();
1358 if (!no_fill) {
1359 _setFillStyle(style, pbox);
1360 setPathVector(pathv);
1362 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1363 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1364 } else {
1365 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1366 }
1368 if (no_stroke)
1369 cairo_fill(_cr);
1370 else
1371 cairo_fill_preserve(_cr);
1372 }
1374 if (!no_stroke) {
1375 _setStrokeStyle(style, pbox);
1376 if (no_fill)
1377 setPathVector(pathv);
1379 cairo_stroke(_cr);
1380 }
1382 if (need_layer)
1383 popLayer();
1384 else
1385 cairo_restore(_cr);
1387 return true;
1388 }
1390 bool
1391 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1392 Geom::Matrix const *image_transform, SPStyle const *style)
1393 {
1394 g_assert( _is_valid );
1396 if (_render_mode == RENDER_MODE_CLIP)
1397 return true;
1399 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1400 if (!px_rgba)
1401 return false;
1403 float opacity;
1404 if (_state->merge_opacity)
1405 opacity = _state->opacity;
1406 else
1407 opacity = 1.0;
1409 // make a copy of the original pixbuf with premultiplied alpha
1410 // if we pass the original pixbuf it will get messed up
1411 for (unsigned i = 0; i < h; i++) {
1412 for (unsigned j = 0; j < w; j++) {
1413 guchar const *src = px + i * rs + j * 4;
1414 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1415 guchar r, g, b, alpha_dst;
1417 // calculate opacity-modified alpha
1418 alpha_dst = src[3];
1419 if (opacity != 1.0 && _vector_based_target)
1420 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1422 // premul alpha (needed because this will be undone by cairo-pdf)
1423 r = src[0]*alpha_dst/255;
1424 g = src[1]*alpha_dst/255;
1425 b = src[2]*alpha_dst/255;
1427 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1428 }
1429 }
1431 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1432 if (cairo_surface_status(image_surface)) {
1433 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1434 return false;
1435 }
1437 // setup automatic freeing of the image data when destroying the surface
1438 static cairo_user_data_key_t key;
1439 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1441 cairo_save(_cr);
1443 // scaling by width & height is not needed because it will be done by Cairo
1444 if (image_transform)
1445 transform(image_transform);
1447 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1449 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1450 if (_vector_based_target) {
1451 cairo_new_path(_cr);
1452 cairo_rectangle(_cr, 0, 0, w, h);
1453 cairo_clip(_cr);
1454 }
1456 if (_vector_based_target)
1457 cairo_paint(_cr);
1458 else
1459 cairo_paint_with_alpha(_cr, opacity);
1461 cairo_restore(_cr);
1463 cairo_surface_destroy(image_surface);
1465 return true;
1466 }
1468 #define GLYPH_ARRAY_SIZE 64
1470 unsigned int
1471 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1472 {
1473 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1474 cairo_glyph_t *glyphs = glyph_array;
1475 unsigned int num_glyphs = glyphtext.size();
1476 if (num_glyphs > GLYPH_ARRAY_SIZE)
1477 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1479 unsigned int num_invalid_glyphs = 0;
1480 unsigned int i = 0;
1481 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1482 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1483 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1484 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1485 TRACE(("INVALID GLYPH found\n"));
1486 num_invalid_glyphs++;
1487 continue;
1488 }
1489 glyphs[i - num_invalid_glyphs].index = it_info->index;
1490 glyphs[i - num_invalid_glyphs].x = it_info->x;
1491 glyphs[i - num_invalid_glyphs].y = it_info->y;
1492 i++;
1493 }
1495 if (path) {
1496 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1497 } else {
1498 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1499 }
1501 if (num_glyphs > GLYPH_ARRAY_SIZE)
1502 g_free(glyphs);
1504 return num_glyphs - num_invalid_glyphs;
1505 }
1507 bool
1508 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1509 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1510 {
1511 // create a cairo_font_face from PangoFont
1512 double size = style->font_size.computed;
1513 gpointer fonthash = (gpointer)font;
1514 cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1516 FcPattern *fc_pattern = NULL;
1518 #ifdef USE_PANGO_WIN32
1519 # ifdef CAIRO_HAS_WIN32_FONT
1520 LOGFONTA *lfa = pango_win32_font_logfont(font);
1521 LOGFONTW lfw;
1523 ZeroMemory(&lfw, sizeof(LOGFONTW));
1524 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1525 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1527 if(font_face == NULL) {
1528 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1529 g_hash_table_insert(font_table, fonthash, font_face);
1530 }
1531 # endif
1532 #else
1533 # ifdef CAIRO_HAS_FT_FONT
1534 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1535 fc_pattern = fc_font->font_pattern;
1536 if(font_face == NULL) {
1537 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1538 g_hash_table_insert(font_table, fonthash, font_face);
1539 }
1540 # endif
1541 #endif
1543 cairo_save(_cr);
1544 cairo_set_font_face(_cr, font_face);
1546 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1547 size = 12.0;
1549 // set the given font matrix
1550 cairo_matrix_t matrix;
1551 _initCairoMatrix(&matrix, font_matrix);
1552 cairo_set_font_matrix(_cr, &matrix);
1554 if (_render_mode == RENDER_MODE_CLIP) {
1555 if (_clip_mode == CLIP_MODE_MASK) {
1556 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1557 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1558 } else {
1559 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1560 }
1561 _showGlyphs(_cr, font, glyphtext, FALSE);
1562 } else {
1563 // just add the glyph paths to the current context
1564 _showGlyphs(_cr, font, glyphtext, TRUE);
1565 }
1566 } else {
1567 bool fill = false, stroke = false, have_path = false;
1568 if (style->fill.isColor() || style->fill.isPaintserver()) {
1569 fill = true;
1570 }
1572 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1573 stroke = true;
1574 }
1575 if (fill) {
1576 _setFillStyle(style, NULL);
1577 if (_is_texttopath) {
1578 _showGlyphs(_cr, font, glyphtext, true);
1579 have_path = true;
1580 if (stroke) cairo_fill_preserve(_cr);
1581 else cairo_fill(_cr);
1582 } else {
1583 _showGlyphs(_cr, font, glyphtext, false);
1584 }
1585 }
1586 if (stroke) {
1587 _setStrokeStyle(style, NULL);
1588 if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1589 cairo_stroke(_cr);
1590 }
1591 }
1593 cairo_restore(_cr);
1595 // if (font_face)
1596 // cairo_font_face_destroy(font_face);
1598 return true;
1599 }
1601 /* Helper functions */
1603 void
1604 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1605 {
1606 cairo_new_path(_cr);
1607 addPathVector(pv);
1608 }
1610 void
1611 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1612 {
1613 feed_pathvector_to_cairo(_cr, pv);
1614 }
1616 void
1617 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1618 {
1619 cairo_matrix_t matrix;
1621 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1622 cairo_transform(cr, &matrix);
1623 }
1625 void
1626 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1627 {
1628 matrix->xx = (*transform)[0];
1629 matrix->yx = (*transform)[1];
1630 matrix->xy = (*transform)[2];
1631 matrix->yy = (*transform)[3];
1632 matrix->x0 = (*transform)[4];
1633 matrix->y0 = (*transform)[5];
1634 }
1636 void
1637 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1638 {
1639 _concatTransform(cr, (*transform)[0], (*transform)[1],
1640 (*transform)[2], (*transform)[3],
1641 (*transform)[4], (*transform)[5]);
1642 }
1644 static cairo_status_t
1645 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1646 {
1647 size_t written;
1648 FILE *file = (FILE*)closure;
1650 written = fwrite (data, 1, length, file);
1652 if (written == length)
1653 return CAIRO_STATUS_SUCCESS;
1654 else
1655 return CAIRO_STATUS_WRITE_ERROR;
1656 }
1658 #include "clear-n_.h"
1660 } /* namespace Internal */
1661 } /* namespace Extension */
1662 } /* namespace Inkscape */
1664 #undef TRACE
1665 #undef TEST
1667 /* End of GNU GPL code */
1670 /*
1671 Local Variables:
1672 mode:c++
1673 c-file-style:"stroustrup"
1674 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1675 indent-tabs-mode:nil
1676 fill-column:99
1677 End:
1678 */
1679 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :