4e9dd0177a6baf399d2f797da5818188f80d6229
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>
31 #include <libnr/n-art-bpath.h>
32 #include <libnr/nr-matrix-ops.h>
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-scale-ops.h>
35 #include <libnr/nr-matrix-translate-ops.h>
36 #include <libnr/nr-scale-matrix-ops.h>
38 #include <glib/gmem.h>
40 #include <glibmm/i18n.h>
41 #include "display/nr-arena.h"
42 #include "display/nr-arena-item.h"
43 #include "display/nr-arena-group.h"
44 #include "display/curve.h"
45 #include "display/canvas-bpath.h"
46 #include "sp-item.h"
47 #include "sp-item-group.h"
48 #include "style.h"
49 #include "sp-linear-gradient.h"
50 #include "sp-radial-gradient.h"
51 #include "sp-pattern.h"
52 #include "sp-mask.h"
53 #include "sp-clippath.h"
54 #ifdef WIN32
55 #include "FontFactory.h" // USE_PANGO_WIN32
56 #endif
58 #include <unit-constants.h>
60 #include "cairo-render-context.h"
61 #include "cairo-renderer.h"
62 #include "extension/system.h"
64 #include "io/sys.h"
66 #include <cairo.h>
68 // include support for only the compiled-in surface types
69 #ifdef CAIRO_HAS_PDF_SURFACE
70 #include <cairo-pdf.h>
71 #endif
72 #ifdef CAIRO_HAS_PS_SURFACE
73 #include <cairo-ps.h>
74 #endif
77 #ifdef CAIRO_HAS_FT_FONT
78 #include <cairo-ft.h>
79 #endif
80 #ifdef CAIRO_HAS_WIN32_FONT
81 #include <cairo-win32.h>
82 #include <pango/pangowin32.h>
83 #endif
85 #include <pango/pangofc-fontmap.h>
87 //#define TRACE(_args) g_printf _args
88 #define TRACE(_args)
89 //#define TEST(_args) _args
90 #define TEST(_args)
92 // FIXME: expose these from sp-clippath/mask.cpp
93 struct SPClipPathView {
94 SPClipPathView *next;
95 unsigned int key;
96 NRArenaItem *arenaitem;
97 NRRect bbox;
98 };
100 struct SPMaskView {
101 SPMaskView *next;
102 unsigned int key;
103 NRArenaItem *arenaitem;
104 NRRect bbox;
105 };
107 namespace Inkscape {
108 namespace Extension {
109 namespace Internal {
111 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
113 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
114 _dpi(72),
115 _pdf_level(0),
116 _ps_level(1),
117 _is_texttopath(FALSE),
118 _is_filtertobitmap(FALSE),
119 _bitmapresolution(72),
120 _stream(NULL),
121 _is_valid(FALSE),
122 _vector_based_target(FALSE),
123 _cr(NULL),
124 _surface(NULL),
125 _target(CAIRO_SURFACE_TYPE_IMAGE),
126 _target_format(CAIRO_FORMAT_ARGB32),
127 _layout(NULL),
128 _state(NULL),
129 _renderer(parent),
130 _render_mode(RENDER_MODE_NORMAL),
131 _clip_mode(CLIP_MODE_MASK)
132 {}
134 CairoRenderContext::~CairoRenderContext(void)
135 {
136 if (_cr) cairo_destroy(_cr);
137 if (_surface) cairo_surface_destroy(_surface);
138 if (_layout) g_object_unref(_layout);
139 }
141 CairoRenderer*
142 CairoRenderContext::getRenderer(void) const
143 {
144 return _renderer;
145 }
147 CairoRenderState*
148 CairoRenderContext::getCurrentState(void) const
149 {
150 return _state;
151 }
153 CairoRenderState*
154 CairoRenderContext::getParentState(void) const
155 {
156 // if this is the root node just return it
157 if (g_slist_length(_state_stack) == 1) {
158 return _state;
159 } else {
160 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
161 }
162 }
164 void
165 CairoRenderContext::setStateForStyle(SPStyle const *style)
166 {
167 // only opacity & overflow is stored for now
168 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
169 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
170 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
172 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
173 _state->merge_opacity = FALSE;
175 // disable rendering of opacity if there's a stroke on the fill
176 if (_state->merge_opacity
177 && !style->fill.isNone()
178 && !style->stroke.isNone())
179 _state->merge_opacity = FALSE;
180 }
182 /**
183 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
184 *
185 * \param width width of the surface to be created
186 * \param height height of the surface to be created
187 */
188 CairoRenderContext*
189 CairoRenderContext::cloneMe(double width, double height) const
190 {
191 g_assert( _is_valid );
192 g_assert( width > 0.0 && height > 0.0 );
194 CairoRenderContext *new_context = _renderer->createContext();
195 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
196 (int)ceil(width), (int)ceil(height));
197 new_context->_cr = cairo_create(surface);
198 new_context->_surface = surface;
199 new_context->_is_valid = TRUE;
201 return new_context;
202 }
204 CairoRenderContext*
205 CairoRenderContext::cloneMe(void) const
206 {
207 g_assert( _is_valid );
209 return cloneMe(_width, _height);
210 }
212 bool
213 CairoRenderContext::setImageTarget(cairo_format_t format)
214 {
215 // format cannot be set on an already initialized surface
216 if (_is_valid)
217 return false;
219 switch (format) {
220 case CAIRO_FORMAT_ARGB32:
221 case CAIRO_FORMAT_RGB24:
222 case CAIRO_FORMAT_A8:
223 case CAIRO_FORMAT_A1:
224 _target_format = format;
225 _target = CAIRO_SURFACE_TYPE_IMAGE;
226 return true;
227 break;
228 default:
229 break;
230 }
232 return false;
233 }
235 bool
236 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
237 {
238 #ifndef CAIRO_HAS_PDF_SURFACE
239 return false;
240 #else
241 _target = CAIRO_SURFACE_TYPE_PDF;
242 _vector_based_target = TRUE;
243 #endif
245 FILE *osf = NULL;
246 FILE *osp = NULL;
248 gsize bytesRead = 0;
249 gsize bytesWritten = 0;
250 GError *error = NULL;
251 gchar *local_fn = g_filename_from_utf8(utf8_fn,
252 -1, &bytesRead, &bytesWritten, &error);
253 gchar const *fn = local_fn;
255 /* TODO: Replace the below fprintf's with something that does the right thing whether in
256 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
257 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
258 * return code.
259 */
260 if (fn != NULL) {
261 if (*fn == '|') {
262 fn += 1;
263 while (isspace(*fn)) fn += 1;
264 #ifndef WIN32
265 osp = popen(fn, "w");
266 #else
267 osp = _popen(fn, "w");
268 #endif
269 if (!osp) {
270 fprintf(stderr, "inkscape: popen(%s): %s\n",
271 fn, strerror(errno));
272 return false;
273 }
274 _stream = osp;
275 } else if (*fn == '>') {
276 fn += 1;
277 while (isspace(*fn)) fn += 1;
278 Inkscape::IO::dump_fopen_call(fn, "K");
279 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
280 if (!osf) {
281 fprintf(stderr, "inkscape: fopen(%s): %s\n",
282 fn, strerror(errno));
283 return false;
284 }
285 _stream = osf;
286 } else {
287 /* put cwd stuff in here */
288 gchar *qn = ( *fn
289 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
290 : g_strdup("lpr") );
291 #ifndef WIN32
292 osp = popen(qn, "w");
293 #else
294 osp = _popen(qn, "w");
295 #endif
296 if (!osp) {
297 fprintf(stderr, "inkscape: popen(%s): %s\n",
298 qn, strerror(errno));
299 return false;
300 }
301 g_free(qn);
302 _stream = osp;
303 }
304 }
306 g_free(local_fn);
308 if (_stream) {
309 /* fixme: this is kinda icky */
310 #if !defined(_WIN32) && !defined(__WIN32__)
311 (void) signal(SIGPIPE, SIG_IGN);
312 #endif
313 }
315 return true;
316 }
318 bool
319 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
320 {
321 #ifndef CAIRO_HAS_PS_SURFACE
322 return false;
323 #else
324 _target = CAIRO_SURFACE_TYPE_PS;
325 _vector_based_target = TRUE;
326 #endif
328 FILE *osf = NULL;
329 FILE *osp = NULL;
331 gsize bytesRead = 0;
332 gsize bytesWritten = 0;
333 GError *error = NULL;
334 gchar *local_fn = g_filename_from_utf8(utf8_fn,
335 -1, &bytesRead, &bytesWritten, &error);
336 gchar const *fn = local_fn;
338 /* TODO: Replace the below fprintf's with something that does the right thing whether in
339 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
340 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
341 * return code.
342 */
343 if (fn != NULL) {
344 if (*fn == '|') {
345 fn += 1;
346 while (isspace(*fn)) fn += 1;
347 #ifndef WIN32
348 osp = popen(fn, "w");
349 #else
350 osp = _popen(fn, "w");
351 #endif
352 if (!osp) {
353 fprintf(stderr, "inkscape: popen(%s): %s\n",
354 fn, strerror(errno));
355 return false;
356 }
357 _stream = osp;
358 } else if (*fn == '>') {
359 fn += 1;
360 while (isspace(*fn)) fn += 1;
361 Inkscape::IO::dump_fopen_call(fn, "K");
362 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
363 if (!osf) {
364 fprintf(stderr, "inkscape: fopen(%s): %s\n",
365 fn, strerror(errno));
366 return false;
367 }
368 _stream = osf;
369 } else {
370 /* put cwd stuff in here */
371 gchar *qn = ( *fn
372 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
373 : g_strdup("lpr") );
374 #ifndef WIN32
375 osp = popen(qn, "w");
376 #else
377 osp = _popen(qn, "w");
378 #endif
379 if (!osp) {
380 fprintf(stderr, "inkscape: popen(%s): %s\n",
381 qn, strerror(errno));
382 return false;
383 }
384 g_free(qn);
385 _stream = osp;
386 }
387 }
389 g_free(local_fn);
391 if (_stream) {
392 /* fixme: this is kinda icky */
393 #if !defined(_WIN32) && !defined(__WIN32__)
394 (void) signal(SIGPIPE, SIG_IGN);
395 #endif
396 }
398 return true;
399 }
401 void CairoRenderContext::setPSLevel(unsigned int level)
402 {
403 _ps_level = level;
404 }
406 unsigned int CairoRenderContext::getPSLevel(void)
407 {
408 return _ps_level;
409 }
411 void CairoRenderContext::setPDFLevel(unsigned int level)
412 {
413 _pdf_level = level;
414 }
416 void CairoRenderContext::setTextToPath(bool texttopath)
417 {
418 _is_texttopath = texttopath;
419 }
421 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
422 {
423 _is_filtertobitmap = filtertobitmap;
424 }
426 bool CairoRenderContext::getFilterToBitmap(void)
427 {
428 return _is_filtertobitmap;
429 }
431 void CairoRenderContext::setBitmapResolution(int resolution)
432 {
433 _bitmapresolution = resolution;
434 }
436 int CairoRenderContext::getBitmapResolution(void)
437 {
438 return _bitmapresolution;
439 }
441 cairo_surface_t*
442 CairoRenderContext::getSurface(void)
443 {
444 g_assert( _is_valid );
446 return _surface;
447 }
449 bool
450 CairoRenderContext::saveAsPng(const char *file_name)
451 {
452 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
453 if (status)
454 return false;
455 else
456 return true;
457 }
459 void
460 CairoRenderContext::setRenderMode(CairoRenderMode mode)
461 {
462 switch (mode) {
463 case RENDER_MODE_NORMAL:
464 case RENDER_MODE_CLIP:
465 _render_mode = mode;
466 break;
467 default:
468 _render_mode = RENDER_MODE_NORMAL;
469 break;
470 }
471 }
473 CairoRenderContext::CairoRenderMode
474 CairoRenderContext::getRenderMode(void) const
475 {
476 return _render_mode;
477 }
479 void
480 CairoRenderContext::setClipMode(CairoClipMode mode)
481 {
482 switch (mode) {
483 case CLIP_MODE_PATH:
484 case CLIP_MODE_MASK:
485 _clip_mode = mode;
486 break;
487 default:
488 _clip_mode = CLIP_MODE_PATH;
489 break;
490 }
491 }
493 CairoRenderContext::CairoClipMode
494 CairoRenderContext::getClipMode(void) const
495 {
496 return _clip_mode;
497 }
499 CairoRenderState*
500 CairoRenderContext::_createState(void)
501 {
502 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
503 g_assert( state != NULL );
505 state->has_filtereffect = FALSE;
506 state->merge_opacity = TRUE;
507 state->opacity = 1.0;
508 state->need_layer = FALSE;
509 state->has_overflow = FALSE;
510 state->parent_has_userspace = FALSE;
511 state->clip_path = NULL;
512 state->mask = NULL;
514 return state;
515 }
517 void
518 CairoRenderContext::pushLayer(void)
519 {
520 g_assert( _is_valid );
522 TRACE(("--pushLayer\n"));
523 cairo_push_group(_cr);
525 // clear buffer
526 if (!_vector_based_target) {
527 cairo_save(_cr);
528 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
529 cairo_paint(_cr);
530 cairo_restore(_cr);
531 }
532 }
534 void
535 CairoRenderContext::popLayer(void)
536 {
537 g_assert( _is_valid );
539 float opacity = _state->opacity;
540 TRACE(("--popLayer w/ %f\n", opacity));
542 // apply clipPath or mask if present
543 SPClipPath *clip_path = _state->clip_path;
544 SPMask *mask = _state->mask;
545 if (clip_path || mask) {
547 CairoRenderContext *clip_ctx = 0;
548 cairo_surface_t *clip_mask = 0;
550 if (clip_path) {
551 if (_render_mode == RENDER_MODE_CLIP)
552 mask = NULL; // disable mask when performing nested clipping
554 if (_vector_based_target) {
555 setClipMode(CLIP_MODE_PATH);
556 if (!mask) {
557 cairo_pop_group_to_source(_cr);
558 _renderer->applyClipPath(this, clip_path);
559 if (opacity == 1.0)
560 cairo_paint(_cr);
561 else
562 cairo_paint_with_alpha(_cr, opacity);
564 } else {
565 // the clipPath will be applied before masking
566 }
567 } else {
569 // setup a new rendering context
570 clip_ctx = _renderer->createContext();
571 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
572 clip_ctx->setClipMode(CLIP_MODE_MASK);
573 if (!clip_ctx->setupSurface(_width, _height)) {
574 TRACE(("setupSurface failed\n"));
575 _renderer->destroyContext(clip_ctx);
576 return;
577 }
579 // clear buffer
580 cairo_save(clip_ctx->_cr);
581 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
582 cairo_paint(clip_ctx->_cr);
583 cairo_restore(clip_ctx->_cr);
585 // if a mask won't be applied set opacity too
586 if (!mask)
587 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
588 else
589 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
591 // copy over the correct CTM
592 if (_state->parent_has_userspace)
593 clip_ctx->setTransform(&getParentState()->transform);
594 else
595 clip_ctx->setTransform(&_state->transform);
597 // apply the clip path
598 clip_ctx->pushState();
599 _renderer->applyClipPath(clip_ctx, clip_path);
600 clip_ctx->popState();
602 clip_mask = clip_ctx->getSurface();
603 TEST(clip_ctx->saveAsPng("clip_mask.png"));
605 if (!mask) {
606 cairo_pop_group_to_source(_cr);
607 cairo_mask_surface(_cr, clip_mask, 0, 0);
608 _renderer->destroyContext(clip_ctx);
609 }
610 }
611 }
613 if (mask) {
614 // create rendering context for mask
615 CairoRenderContext *mask_ctx = _renderer->createContext();
616 mask_ctx->setupSurface(_width, _height);
618 // set rendering mode to normal
619 setRenderMode(RENDER_MODE_NORMAL);
621 // copy the correct CTM to mask context
622 if (_state->parent_has_userspace)
623 mask_ctx->setTransform(&getParentState()->transform);
624 else
625 mask_ctx->setTransform(&_state->transform);
627 // render mask contents to mask_ctx
628 _renderer->applyMask(mask_ctx, mask);
630 TEST(mask_ctx->saveAsPng("mask.png"));
632 // composite with clip mask
633 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
634 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
635 _renderer->destroyContext(clip_ctx);
636 }
638 cairo_surface_t *mask_image = mask_ctx->getSurface();
639 int width = cairo_image_surface_get_width(mask_image);
640 int height = cairo_image_surface_get_height(mask_image);
641 int stride = cairo_image_surface_get_stride(mask_image);
642 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
644 // premultiply with opacity
645 if (_state->opacity != 1.0) {
646 TRACE(("premul w/ %f\n", opacity));
647 guint8 int_opacity = (guint8)(255 * opacity);
648 for (int row = 0 ; row < height; row++) {
649 unsigned char *row_data = pixels + (row * stride);
650 for (int i = 0 ; i < width; i++) {
651 guint32 *pixel = (guint32 *)row_data + i;
652 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
653 ((*pixel & 0x0000ff00) >> 8) * 46518 +
654 ((*pixel & 0x000000ff) ) * 4688) *
655 int_opacity);
656 }
657 }
658 }
660 cairo_pop_group_to_source(_cr);
661 if (_clip_mode == CLIP_MODE_PATH) {
662 // we have to do the clipping after cairo_pop_group_to_source
663 _renderer->applyClipPath(this, clip_path);
664 }
665 // apply the mask onto the layer
666 cairo_mask_surface(_cr, mask_image, 0, 0);
667 _renderer->destroyContext(mask_ctx);
668 }
669 } else {
670 cairo_pop_group_to_source(_cr);
671 if (opacity == 1.0)
672 cairo_paint(_cr);
673 else
674 cairo_paint_with_alpha(_cr, opacity);
675 }
676 }
678 void
679 CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
680 {
681 g_assert( _is_valid );
683 // here it should be checked whether the current clip winding changed
684 // so we could switch back to masked clipping
685 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
686 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
687 } else {
688 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
689 }
690 addBpath(bp);
691 }
693 void
694 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
695 {
696 g_assert( _is_valid );
698 cairo_rectangle(_cr, x, y, width, height);
699 cairo_clip(_cr);
700 }
702 bool
703 CairoRenderContext::setupSurface(double width, double height)
704 {
705 // Is the surface already set up?
706 if (_is_valid)
707 return true;
709 if (_vector_based_target && _stream == NULL)
710 return false;
712 cairo_surface_t *surface = NULL;
713 switch (_target) {
714 case CAIRO_SURFACE_TYPE_IMAGE:
715 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
716 break;
717 #ifdef CAIRO_HAS_PDF_SURFACE
718 case CAIRO_SURFACE_TYPE_PDF:
719 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
720 break;
721 #endif
722 #ifdef CAIRO_HAS_PS_SURFACE
723 case CAIRO_SURFACE_TYPE_PS:
724 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
725 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
726 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
727 return FALSE;
728 }
729 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
730 #endif
731 break;
732 #endif
733 default:
734 return false;
735 break;
736 }
738 return _finishSurfaceSetup (surface);
739 }
741 bool
742 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
743 {
744 if (_is_valid || !surface)
745 return false;
747 _vector_based_target = is_vector;
748 bool ret = _finishSurfaceSetup (surface);
749 if (ret)
750 cairo_surface_reference (surface);
751 return ret;
752 }
754 bool
755 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
756 {
757 if(surface == NULL) {
758 return FALSE;
759 }
760 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
761 return FALSE;
762 }
764 _cr = cairo_create(surface);
765 _surface = surface;
767 if (_vector_based_target) {
768 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
769 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
770 // set background color on non-alpha surfaces
771 // TODO: bgcolor should be derived from SPDocument
772 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
773 cairo_rectangle(_cr, 0, 0, _width, _height);
774 cairo_fill(_cr);
775 }
777 _is_valid = TRUE;
779 return true;
780 }
782 bool
783 CairoRenderContext::finish(void)
784 {
785 g_assert( _is_valid );
787 if (_vector_based_target)
788 cairo_show_page(_cr);
790 cairo_destroy(_cr);
791 cairo_surface_destroy(_surface);
792 _cr = NULL;
793 _surface = NULL;
795 if (_layout)
796 g_object_unref(_layout);
798 _is_valid = FALSE;
800 if (_vector_based_target && _stream) {
801 /* Flush stream to be sure. */
802 (void) fflush(_stream);
804 fclose(_stream);
805 _stream = NULL;
806 }
808 return true;
809 }
811 void
812 CairoRenderContext::transform(NR::Matrix const *transform)
813 {
814 g_assert( _is_valid );
816 cairo_matrix_t matrix;
817 _initCairoMatrix(&matrix, transform);
818 cairo_transform(_cr, &matrix);
820 // store new CTM
821 getTransform(&_state->transform);
822 }
824 void
825 CairoRenderContext::setTransform(NR::Matrix const *transform)
826 {
827 g_assert( _is_valid );
829 cairo_matrix_t matrix;
830 _initCairoMatrix(&matrix, transform);
831 cairo_set_matrix(_cr, &matrix);
832 _state->transform = *transform;
833 }
835 void
836 CairoRenderContext::getTransform(NR::Matrix *copy) const
837 {
838 g_assert( _is_valid );
840 cairo_matrix_t ctm;
841 cairo_get_matrix(_cr, &ctm);
842 (*copy)[0] = ctm.xx;
843 (*copy)[1] = ctm.yx;
844 (*copy)[2] = ctm.xy;
845 (*copy)[3] = ctm.yy;
846 (*copy)[4] = ctm.x0;
847 (*copy)[5] = ctm.y0;
848 }
850 void
851 CairoRenderContext::getParentTransform(NR::Matrix *copy) const
852 {
853 g_assert( _is_valid );
855 CairoRenderState *parent_state = getParentState();
856 memcpy(copy, &parent_state->transform, sizeof(NR::Matrix));
857 }
859 void
860 CairoRenderContext::pushState(void)
861 {
862 g_assert( _is_valid );
864 cairo_save(_cr);
866 CairoRenderState *new_state = _createState();
867 // copy current state's transform
868 new_state->transform = _state->transform;
869 _state_stack = g_slist_prepend(_state_stack, new_state);
870 _state = new_state;
871 }
873 void
874 CairoRenderContext::popState(void)
875 {
876 g_assert( _is_valid );
878 cairo_restore(_cr);
880 g_free(_state_stack->data);
881 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
882 _state = (CairoRenderState*)_state_stack->data;
884 g_assert( g_slist_length(_state_stack) > 0 );
885 }
887 static bool pattern_hasItemChildren (SPPattern *pat)
888 {
889 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
890 if (SP_IS_ITEM (child)) {
891 return true;
892 }
893 }
894 return false;
895 }
897 cairo_pattern_t*
898 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
899 {
900 g_assert( SP_IS_PATTERN(paintserver) );
902 SPPattern *pat = SP_PATTERN (paintserver);
904 NR::Matrix ps2user, pcs2dev;
905 ps2user.set_identity();
906 pcs2dev.set_identity();
908 double x = pattern_x(pat);
909 double y = pattern_y(pat);
910 double width = pattern_width(pat);
911 double height = pattern_height(pat);
912 double bbox_width_scaler;
913 double bbox_height_scaler;
915 TRACE(("%f x %f pattern\n", width, height));
917 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
918 //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
919 bbox_width_scaler = pbox->x1 - pbox->x0;
920 bbox_height_scaler = pbox->y1 - pbox->y0;
921 ps2user[4] = x * bbox_width_scaler + pbox->x0;
922 ps2user[5] = y * bbox_height_scaler + pbox->y0;
923 } else {
924 bbox_width_scaler = 1.0;
925 bbox_height_scaler = 1.0;
926 ps2user[4] = x;
927 ps2user[5] = y;
928 }
930 // apply pattern transformation
931 NR::Matrix pattern_transform(pattern_patternTransform(pat));
932 ps2user *= pattern_transform;
934 // create pattern contents coordinate system
935 if (pat->viewBox_set) {
936 NRRect *view_box = pattern_viewBox(pat);
938 double x, y, w, h;
939 double view_width, view_height;
940 x = 0;
941 y = 0;
942 w = width * bbox_width_scaler;
943 h = height * bbox_height_scaler;
945 view_width = view_box->x1 - view_box->x0;
946 view_height = view_box->y1 - view_box->y0;
948 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
949 pcs2dev[0] = w / view_width;
950 pcs2dev[3] = h / view_height;
951 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
952 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
953 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
954 pcs2dev[0] = pbox->x1 - pbox->x0;
955 pcs2dev[3] = pbox->y1 - pbox->y0;
956 }
958 // calculate the size of the surface which has to be created
959 // the scaling needs to be taken into account in the ctm after the pattern transformation
960 NR::Matrix temp;
961 temp = pattern_transform * _state->transform;
962 double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
963 double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
965 if (_vector_based_target) {
966 // eliminate PT_PER_PX mul from these
967 width_scaler *= 1.25;
968 height_scaler *= 1.25;
969 }
970 double surface_width = ceil(bbox_width_scaler * width_scaler * width);
971 double surface_height = ceil(bbox_height_scaler * height_scaler * height);
972 TRACE(("surface size: %f x %f\n", surface_width, surface_height));
973 // create new rendering context
974 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
976 // adjust the size of the painted pattern to fit exactly the created surface
977 // this has to be done because of the rounding to obtain an integer pattern surface width/height
978 double scale_width = surface_width / (bbox_width_scaler * width);
979 double scale_height = surface_height / (bbox_height_scaler * height);
980 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
981 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
982 pcs2dev *= NR::scale(1.0 / scale_width, 1.0 / scale_height);
983 ps2user *= NR::scale(scale_width, scale_height);
984 }
986 pattern_ctx->setTransform(&pcs2dev);
987 pattern_ctx->pushState();
989 // create arena and group
990 NRArena *arena = NRArena::create();
991 unsigned dkey = sp_item_display_key_new(1);
993 // show items and render them
994 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
995 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
996 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
997 if (SP_IS_ITEM (child)) {
998 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
999 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1000 }
1001 }
1002 break; // do not go further up the chain if children are found
1003 }
1004 }
1006 pattern_ctx->popState();
1008 // setup a cairo_pattern_t
1009 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1010 TEST(pattern_ctx->saveAsPng("pattern.png"));
1011 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1012 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1014 // set pattern transformation
1015 cairo_matrix_t pattern_matrix;
1016 _initCairoMatrix(&pattern_matrix, &ps2user);
1017 cairo_matrix_invert(&pattern_matrix);
1018 cairo_pattern_set_matrix(result, &pattern_matrix);
1020 delete pattern_ctx;
1022 // hide all items
1023 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1024 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1025 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1026 if (SP_IS_ITEM (child)) {
1027 sp_item_invoke_hide (SP_ITEM (child), dkey);
1028 }
1029 }
1030 break; // do not go further up the chain if children are found
1031 }
1032 }
1034 return result;
1035 }
1037 cairo_pattern_t*
1038 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1039 NRRect const *pbox, float alpha)
1040 {
1041 cairo_pattern_t *pattern = NULL;
1042 bool apply_bbox2user = FALSE;
1044 if (SP_IS_LINEARGRADIENT (paintserver)) {
1046 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1048 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1050 NR::Point p1 (lg->x1.computed, lg->y1.computed);
1051 NR::Point p2 (lg->x2.computed, lg->y2.computed);
1052 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1053 // convert to userspace
1054 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1055 p1 *= bbox2user;
1056 p2 *= bbox2user;
1057 }
1059 // create linear gradient pattern
1060 pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1062 // add stops
1063 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1064 float rgb[3];
1065 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1066 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1067 }
1068 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1070 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1072 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1074 NR::Point c (rg->cx.computed, rg->cy.computed);
1075 NR::Point f (rg->fx.computed, rg->fy.computed);
1076 double r = rg->r.computed;
1077 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1078 apply_bbox2user = true;
1080 // create radial gradient pattern
1081 pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1083 // add stops
1084 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1085 float rgb[3];
1086 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1087 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1088 }
1089 } else if (SP_IS_PATTERN (paintserver)) {
1091 pattern = _createPatternPainter(paintserver, pbox);
1092 } else {
1093 return NULL;
1094 }
1096 if (pattern && SP_IS_GRADIENT (paintserver)) {
1097 SPGradient *g = SP_GRADIENT(paintserver);
1099 // set extend type
1100 SPGradientSpread spread = sp_gradient_get_spread(g);
1101 switch (spread) {
1102 case SP_GRADIENT_SPREAD_REPEAT: {
1103 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1104 break;
1105 }
1106 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1107 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1108 break;
1109 }
1110 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1111 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1112 break;
1113 }
1114 default: {
1115 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1116 break;
1117 }
1118 }
1120 cairo_matrix_t pattern_matrix;
1121 if (g->gradientTransform_set) {
1122 // apply gradient transformation
1123 cairo_matrix_init(&pattern_matrix,
1124 g->gradientTransform[0], g->gradientTransform[1],
1125 g->gradientTransform[2], g->gradientTransform[3],
1126 g->gradientTransform[4], g->gradientTransform[5]);
1127 } else {
1128 cairo_matrix_init_identity (&pattern_matrix);
1129 }
1131 if (apply_bbox2user) {
1132 // convert to userspace
1133 cairo_matrix_t bbox2user;
1134 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1135 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1136 }
1137 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1138 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1139 }
1141 return pattern;
1142 }
1144 void
1145 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1146 {
1147 g_return_if_fail( style->fill.isColor()
1148 || style->fill.isPaintserver() );
1150 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1151 if (_state->merge_opacity) {
1152 alpha *= _state->opacity;
1153 TRACE(("merged op=%f\n", alpha));
1154 }
1156 if (style->fill.isColor()) {
1157 float rgb[3];
1158 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1160 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1161 } else {
1162 g_assert( style->fill.isPaintserver()
1163 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1164 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1166 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1168 if (pattern) {
1169 cairo_set_source(_cr, pattern);
1170 cairo_pattern_destroy(pattern);
1171 }
1172 }
1173 }
1175 void
1176 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1177 {
1178 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1179 if (_state->merge_opacity)
1180 alpha *= _state->opacity;
1182 if (style->stroke.isColor()) {
1183 float rgb[3];
1184 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1186 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1187 } else {
1188 g_assert( style->fill.isPaintserver()
1189 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1190 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1192 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1194 if (pattern) {
1195 cairo_set_source(_cr, pattern);
1196 cairo_pattern_destroy(pattern);
1197 }
1198 }
1200 if (style->stroke_dash.n_dash &&
1201 style->stroke_dash.dash )
1202 {
1203 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1204 } else {
1205 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1206 }
1208 cairo_set_line_width(_cr, style->stroke_width.computed);
1210 // set line join type
1211 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1212 switch (style->stroke_linejoin.computed) {
1213 case SP_STROKE_LINEJOIN_MITER:
1214 join = CAIRO_LINE_JOIN_MITER;
1215 break;
1216 case SP_STROKE_LINEJOIN_ROUND:
1217 join = CAIRO_LINE_JOIN_ROUND;
1218 break;
1219 case SP_STROKE_LINEJOIN_BEVEL:
1220 join = CAIRO_LINE_JOIN_BEVEL;
1221 break;
1222 }
1223 cairo_set_line_join(_cr, join);
1225 // set line cap type
1226 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1227 switch (style->stroke_linecap.computed) {
1228 case SP_STROKE_LINECAP_BUTT:
1229 cap = CAIRO_LINE_CAP_BUTT;
1230 break;
1231 case SP_STROKE_LINECAP_ROUND:
1232 cap = CAIRO_LINE_CAP_ROUND;
1233 break;
1234 case SP_STROKE_LINECAP_SQUARE:
1235 cap = CAIRO_LINE_CAP_SQUARE;
1236 break;
1237 }
1238 cairo_set_line_cap(_cr, cap);
1239 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1240 }
1242 bool
1243 CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1244 {
1245 g_assert( _is_valid );
1247 if (_render_mode == RENDER_MODE_CLIP) {
1248 if (_clip_mode == CLIP_MODE_PATH) {
1249 addClipPath(bpath->path, &style->fill_rule);
1250 } else {
1251 setBpath(bpath->path);
1252 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1253 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1254 } else {
1255 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1256 }
1257 cairo_fill(_cr);
1258 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1259 }
1260 return true;
1261 }
1263 if (style->fill.isNone() && style->stroke.isNone())
1264 return true;
1266 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1267 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1269 if (!need_layer)
1270 cairo_save(_cr);
1271 else
1272 pushLayer();
1274 if (!style->fill.isNone()) {
1275 _setFillStyle(style, pbox);
1276 setBpath(bpath->path);
1278 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1279 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1280 } else {
1281 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1282 }
1284 if (style->stroke.isNone())
1285 cairo_fill(_cr);
1286 else
1287 cairo_fill_preserve(_cr);
1288 }
1290 if (!style->stroke.isNone()) {
1291 _setStrokeStyle(style, pbox);
1292 if (style->fill.isNone())
1293 setBpath(bpath->path);
1295 cairo_stroke(_cr);
1296 }
1298 if (need_layer)
1299 popLayer();
1300 else
1301 cairo_restore(_cr);
1303 return true;
1304 }
1306 bool
1307 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1308 NR::Matrix const *image_transform, SPStyle const *style)
1309 {
1310 g_assert( _is_valid );
1312 if (_render_mode == RENDER_MODE_CLIP)
1313 return true;
1315 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1316 if (!px_rgba)
1317 return false;
1319 float opacity;
1320 if (_state->merge_opacity)
1321 opacity = _state->opacity;
1322 else
1323 opacity = 1.0;
1325 // make a copy of the original pixbuf with premultiplied alpha
1326 // if we pass the original pixbuf it will get messed up
1327 for (unsigned i = 0; i < h; i++) {
1328 for (unsigned j = 0; j < w; j++) {
1329 guchar const *src = px + i * rs + j * 4;
1330 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1331 guchar r, g, b, alpha_dst;
1333 // calculate opacity-modified alpha
1334 alpha_dst = src[3];
1335 if (opacity != 1.0 && _vector_based_target)
1336 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1338 // premul alpha (needed because this will be undone by cairo-pdf)
1339 r = src[0]*alpha_dst/255;
1340 g = src[1]*alpha_dst/255;
1341 b = src[2]*alpha_dst/255;
1343 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1344 }
1345 }
1347 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1348 if (cairo_surface_status(image_surface)) {
1349 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1350 return false;
1351 }
1353 // setup automatic freeing of the image data when destroying the surface
1354 static cairo_user_data_key_t key;
1355 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1357 cairo_save(_cr);
1359 // scaling by width & height is not needed because it will be done by Cairo
1360 if (image_transform)
1361 transform(image_transform);
1363 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1365 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1366 if (_vector_based_target) {
1367 cairo_new_path(_cr);
1368 cairo_rectangle(_cr, 0, 0, w, h);
1369 cairo_clip(_cr);
1370 }
1372 if (_vector_based_target)
1373 cairo_paint(_cr);
1374 else
1375 cairo_paint_with_alpha(_cr, opacity);
1377 cairo_restore(_cr);
1379 cairo_surface_destroy(image_surface);
1381 return true;
1382 }
1384 #define GLYPH_ARRAY_SIZE 64
1386 unsigned int
1387 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1388 {
1389 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1390 cairo_glyph_t *glyphs = glyph_array;
1391 unsigned int num_glyphs = glyphtext.size();
1392 if (num_glyphs > GLYPH_ARRAY_SIZE)
1393 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1395 unsigned int num_invalid_glyphs = 0;
1396 unsigned int i = 0;
1397 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1398 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1399 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1400 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1401 TRACE(("INVALID GLYPH found\n"));
1402 num_invalid_glyphs++;
1403 continue;
1404 }
1405 glyphs[i - num_invalid_glyphs].index = it_info->index;
1406 glyphs[i - num_invalid_glyphs].x = it_info->x;
1407 glyphs[i - num_invalid_glyphs].y = it_info->y;
1408 i++;
1409 }
1411 if (is_stroke || _is_texttopath)
1412 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1413 else
1414 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1416 if (num_glyphs > GLYPH_ARRAY_SIZE)
1417 g_free(glyphs);
1419 return num_glyphs - num_invalid_glyphs;
1420 }
1422 bool
1423 CairoRenderContext::renderGlyphtext(PangoFont *font, NR::Matrix const *font_matrix,
1424 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1425 {
1426 // create a cairo_font_face from PangoFont
1427 double size = style->font_size.computed;
1428 cairo_font_face_t *font_face = NULL;
1430 FcPattern *fc_pattern = NULL;
1432 #ifdef USE_PANGO_WIN32
1433 # ifdef CAIRO_HAS_WIN32_FONT
1434 LOGFONTA *lfa = pango_win32_font_logfont(font);
1435 LOGFONTW lfw;
1437 ZeroMemory(&lfw, sizeof(LOGFONTW));
1438 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1439 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1441 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1442 # endif
1443 #else
1444 # ifdef CAIRO_HAS_FT_FONT
1445 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1446 fc_pattern = fc_font->font_pattern;
1447 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1448 # endif
1449 #endif
1451 cairo_save(_cr);
1452 cairo_set_font_face(_cr, font_face);
1454 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1455 size = 12.0;
1457 // set the given font matrix
1458 cairo_matrix_t matrix;
1459 _initCairoMatrix(&matrix, font_matrix);
1460 cairo_set_font_matrix(_cr, &matrix);
1462 if (_render_mode == RENDER_MODE_CLIP) {
1463 if (_clip_mode == CLIP_MODE_MASK) {
1464 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1465 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1466 } else {
1467 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1468 }
1469 _showGlyphs(_cr, font, glyphtext, FALSE);
1470 } else {
1471 // just add the glyph paths to the current context
1472 _showGlyphs(_cr, font, glyphtext, TRUE);
1473 }
1474 } else {
1476 if (style->fill.isColor() || style->fill.isPaintserver()) {
1477 // set fill style
1478 _setFillStyle(style, NULL);
1480 _showGlyphs(_cr, font, glyphtext, FALSE);
1481 }
1483 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1484 // set stroke style
1485 _setStrokeStyle(style, NULL);
1487 // paint stroke
1488 _showGlyphs(_cr, font, glyphtext, TRUE);
1489 cairo_stroke(_cr);
1490 }
1491 }
1493 cairo_restore(_cr);
1495 if (font_face)
1496 cairo_font_face_destroy(font_face);
1498 return true;
1499 }
1501 /* Helper functions */
1503 void
1504 CairoRenderContext::addBpath(NArtBpath const *bp)
1505 {
1506 bool closed = false;
1507 while (bp->code != NR_END) {
1508 switch (bp->code) {
1509 case NR_MOVETO:
1510 if (closed) {
1511 cairo_close_path(_cr);
1512 }
1513 closed = true;
1514 cairo_move_to(_cr, bp->x3, bp->y3);
1515 break;
1516 case NR_MOVETO_OPEN:
1517 if (closed) {
1518 cairo_close_path(_cr);
1519 }
1520 closed = false;
1521 cairo_move_to(_cr, bp->x3, bp->y3);
1522 break;
1523 case NR_LINETO:
1524 cairo_line_to(_cr, bp->x3, bp->y3);
1525 break;
1526 case NR_CURVETO:
1527 cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1528 break;
1529 default:
1530 break;
1531 }
1532 bp += 1;
1533 }
1534 if (closed) {
1535 cairo_close_path(_cr);
1536 }
1537 }
1539 void
1540 CairoRenderContext::setBpath(NArtBpath const *bp)
1541 {
1542 cairo_new_path(_cr);
1543 if (bp)
1544 addBpath(bp);
1545 }
1547 void
1548 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1549 {
1550 cairo_matrix_t matrix;
1552 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1553 cairo_transform(cr, &matrix);
1554 }
1556 void
1557 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NR::Matrix const *transform)
1558 {
1559 matrix->xx = (*transform)[0];
1560 matrix->yx = (*transform)[1];
1561 matrix->xy = (*transform)[2];
1562 matrix->yy = (*transform)[3];
1563 matrix->x0 = (*transform)[4];
1564 matrix->y0 = (*transform)[5];
1565 }
1567 void
1568 CairoRenderContext::_concatTransform(cairo_t *cr, NR::Matrix const *transform)
1569 {
1570 _concatTransform(cr, (*transform)[0], (*transform)[1],
1571 (*transform)[2], (*transform)[3],
1572 (*transform)[4], (*transform)[5]);
1573 }
1575 static cairo_status_t
1576 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1577 {
1578 size_t written;
1579 FILE *file = (FILE*)closure;
1581 written = fwrite (data, 1, length, file);
1583 if (written == length)
1584 return CAIRO_STATUS_SUCCESS;
1585 else
1586 return CAIRO_STATUS_WRITE_ERROR;
1587 }
1589 #include "clear-n_.h"
1591 } /* namespace Internal */
1592 } /* namespace Extension */
1593 } /* namespace Inkscape */
1595 #undef TRACE
1596 #undef TEST
1598 /* End of GNU GPL code */
1601 /*
1602 Local Variables:
1603 mode:c++
1604 c-file-style:"stroustrup"
1605 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1606 indent-tabs-mode:nil
1607 fill-column:99
1608 End:
1609 */
1610 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :