1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** \file
3 * Rendering with Cairo.
4 */
5 /*
6 * Author:
7 * Miklos Erdelyi <erdelyim@gmail.com>
8 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2006 Miklos Erdelyi
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h" // only include where actually required!
18 #endif
19
20 #ifndef PANGO_ENABLE_BACKEND
21 #define PANGO_ENABLE_BACKEND
22 #endif
23
24 #ifndef PANGO_ENABLE_ENGINE
25 #define PANGO_ENABLE_ENGINE
26 #endif
27
28 #include "cairo-render-context.h"
29
30 #include <csignal>
31 #include <cerrno>
32 #include <2geom/pathvector.h>
33
34 #include <glib.h>
35 #include <glibmm/i18n.h>
36
37 #include "display/drawing.h"
38 #include "display/curve.h"
39 #include "display/cairo-utils.h"
40
41 #include "object/sp-item.h"
42 #include "object/sp-item-group.h"
43 #include "object/sp-hatch.h"
44 #include "object/sp-linear-gradient.h"
45 #include "object/sp-radial-gradient.h"
46 #include "object/sp-mesh-gradient.h"
47 #include "object/sp-pattern.h"
48 #include "object/sp-mask.h"
49 #include "object/sp-clippath.h"
50
51 #include "util/units.h"
52 #ifdef _WIN32
53 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
54 #endif
55
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
58
59 #include "io/sys.h"
60
61 #include "svg/stringstream.h"
62
63 #include <cairo.h>
64
65 // include support for only the compiled-in surface types
66 #ifdef CAIRO_HAS_PDF_SURFACE
67 #include <cairo-pdf.h>
68 #endif
69 #ifdef CAIRO_HAS_PS_SURFACE
70 #include <cairo-ps.h>
71 #endif
72
73
74 #ifdef CAIRO_HAS_FT_FONT
75 #include <cairo-ft.h>
76 #endif
77 #ifdef CAIRO_HAS_WIN32_FONT
78 #include <pango/pangowin32.h>
79 #include <cairo-win32.h>
80 #endif
81
82 #include <pango/pangofc-fontmap.h>
83
84 //#define TRACE(_args) g_printf _args
85 //#define TRACE(_args) g_message _args
86 #define TRACE(_args)
87 //#define TEST(_args) _args
88 #define TEST(_args)
89
90 // FIXME: expose these from sp-clippath/mask.cpp
91 /*struct SPClipPathView {
92 SPClipPathView *next;
93 unsigned int key;
94 Inkscape::DrawingItem *arenaitem;
95 Geom::OptRect bbox;
96 };
97
98 struct SPMaskView {
99 SPMaskView *next;
100 unsigned int key;
101 Inkscape::DrawingItem *arenaitem;
102 Geom::OptRect bbox;
103 };*/
104
105 namespace Inkscape {
106 namespace Extension {
107 namespace Internal {
108
109 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
110
CairoRenderContext(CairoRenderer * parent)111 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
112 _dpi(72),
113 _pdf_level(1),
114 _ps_level(1),
115 _eps(false),
116 _is_texttopath(FALSE),
117 _is_omittext(FALSE),
118 _is_filtertobitmap(FALSE),
119 _bitmapresolution(72),
120 _stream(nullptr),
121 _is_valid(FALSE),
122 _vector_based_target(FALSE),
123 _cr(nullptr), // Cairo context
124 _surface(nullptr),
125 _target(CAIRO_SURFACE_TYPE_IMAGE),
126 _target_format(CAIRO_FORMAT_ARGB32),
127 _layout(nullptr),
128 _state(nullptr),
129 _renderer(parent),
130 _render_mode(RENDER_MODE_NORMAL),
131 _clip_mode(CLIP_MODE_MASK),
132 _omittext_state(EMPTY)
133 {
134 }
135
~CairoRenderContext()136 CairoRenderContext::~CairoRenderContext()
137 {
138 for (std::map<gpointer, cairo_font_face_t *>::const_iterator iter = font_table.begin(); iter != font_table.end(); ++iter)
139 font_data_free(iter->second);
140
141 if (_cr) cairo_destroy(_cr);
142 if (_surface) cairo_surface_destroy(_surface);
143 if (_layout) g_object_unref(_layout);
144 }
font_data_free(gpointer data)145 void CairoRenderContext::font_data_free(gpointer data)
146 {
147 cairo_font_face_t *font_face = (cairo_font_face_t *)data;
148 if (font_face) {
149 cairo_font_face_destroy(font_face);
150 }
151 }
152
getRenderer() const153 CairoRenderer* CairoRenderContext::getRenderer() const
154 {
155 return _renderer;
156 }
157
getCurrentState() const158 CairoRenderState* CairoRenderContext::getCurrentState() const
159 {
160 return _state;
161 }
162
getParentState() const163 CairoRenderState* CairoRenderContext::getParentState() const
164 {
165 // if this is the root node just return it
166 if (_state_stack.size() == 1) {
167 return _state;
168 } else {
169 return _state_stack[_state_stack.size()-2];
170 }
171 }
172
setStateForStyle(SPStyle const * style)173 void CairoRenderContext::setStateForStyle(SPStyle const *style)
174 {
175 // only opacity & overflow is stored for now
176 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
177 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
178 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
179
180 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
181 _state->merge_opacity = FALSE;
182
183 // disable rendering of opacity if there's a stroke on the fill
184 if (_state->merge_opacity
185 && !style->fill.isNone()
186 && !style->stroke.isNone())
187 _state->merge_opacity = FALSE;
188 }
189
190 /**
191 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
192 *
193 * \param width width of the surface to be created
194 * \param height height of the surface to be created
195 */
196 CairoRenderContext*
cloneMe(double width,double height) const197 CairoRenderContext::cloneMe(double width, double height) const
198 {
199 g_assert( _is_valid );
200 g_assert( width > 0.0 && height > 0.0 );
201
202 CairoRenderContext *new_context = _renderer->createContext();
203 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
204 (int)ceil(width), (int)ceil(height));
205 new_context->_cr = cairo_create(surface);
206 new_context->_surface = surface;
207 new_context->_width = width;
208 new_context->_height = height;
209 new_context->_is_valid = TRUE;
210
211 return new_context;
212 }
213
cloneMe() const214 CairoRenderContext* CairoRenderContext::cloneMe() const
215 {
216 g_assert( _is_valid );
217
218 return cloneMe(_width, _height);
219 }
220
setImageTarget(cairo_format_t format)221 bool CairoRenderContext::setImageTarget(cairo_format_t format)
222 {
223 // format cannot be set on an already initialized surface
224 if (_is_valid)
225 return false;
226
227 switch (format) {
228 case CAIRO_FORMAT_ARGB32:
229 case CAIRO_FORMAT_RGB24:
230 case CAIRO_FORMAT_A8:
231 case CAIRO_FORMAT_A1:
232 _target_format = format;
233 _target = CAIRO_SURFACE_TYPE_IMAGE;
234 return true;
235 break;
236 default:
237 break;
238 }
239
240 return false;
241 }
242
setPdfTarget(gchar const * utf8_fn)243 bool CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
244 {
245 #ifndef CAIRO_HAS_PDF_SURFACE
246 return false;
247 #else
248 _target = CAIRO_SURFACE_TYPE_PDF;
249 _vector_based_target = TRUE;
250 #endif
251
252 FILE *osf = nullptr;
253 FILE *osp = nullptr;
254
255 gsize bytesRead = 0;
256 gsize bytesWritten = 0;
257 GError *error = nullptr;
258 gchar *local_fn = g_filename_from_utf8(utf8_fn,
259 -1, &bytesRead, &bytesWritten, &error);
260 gchar const *fn = local_fn;
261
262 /* TODO: Replace the below fprintf's with something that does the right thing whether in
263 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
264 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
265 * return code.
266 */
267 if (fn != nullptr) {
268 if (*fn == '|') {
269 fn += 1;
270 while (isspace(*fn)) fn += 1;
271 #ifndef _WIN32
272 osp = popen(fn, "w");
273 #else
274 osp = _popen(fn, "w");
275 #endif
276 if (!osp) {
277 fprintf(stderr, "inkscape: popen(%s): %s\n",
278 fn, strerror(errno));
279 return false;
280 }
281 _stream = osp;
282 } else if (*fn == '>') {
283 fn += 1;
284 while (isspace(*fn)) fn += 1;
285 Inkscape::IO::dump_fopen_call(fn, "K");
286 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
287 if (!osf) {
288 fprintf(stderr, "inkscape: fopen(%s): %s\n",
289 fn, strerror(errno));
290 return false;
291 }
292 _stream = osf;
293 } else {
294 /* put cwd stuff in here */
295 gchar *qn = ( *fn
296 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
297 : g_strdup("lpr") );
298 #ifndef _WIN32
299 osp = popen(qn, "w");
300 #else
301 osp = _popen(qn, "w");
302 #endif
303 if (!osp) {
304 fprintf(stderr, "inkscape: popen(%s): %s\n",
305 qn, strerror(errno));
306 return false;
307 }
308 g_free(qn);
309 _stream = osp;
310 }
311 }
312
313 g_free(local_fn);
314
315 if (_stream) {
316 /* fixme: this is kinda icky */
317 #if !defined(_WIN32) && !defined(__WIN32__)
318 (void) signal(SIGPIPE, SIG_IGN);
319 #endif
320 }
321
322 return true;
323 }
324
setPsTarget(gchar const * utf8_fn)325 bool CairoRenderContext::setPsTarget(gchar const *utf8_fn)
326 {
327 #ifndef CAIRO_HAS_PS_SURFACE
328 return false;
329 #else
330 _target = CAIRO_SURFACE_TYPE_PS;
331 _vector_based_target = TRUE;
332 #endif
333
334 FILE *osf = nullptr;
335 FILE *osp = nullptr;
336
337 gsize bytesRead = 0;
338 gsize bytesWritten = 0;
339 GError *error = nullptr;
340 gchar *local_fn = g_filename_from_utf8(utf8_fn,
341 -1, &bytesRead, &bytesWritten, &error);
342 gchar const *fn = local_fn;
343
344 /* TODO: Replace the below fprintf's with something that does the right thing whether in
345 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
346 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
347 * return code.
348 */
349 if (fn != nullptr) {
350 if (*fn == '|') {
351 fn += 1;
352 while (isspace(*fn)) fn += 1;
353 #ifndef _WIN32
354 osp = popen(fn, "w");
355 #else
356 osp = _popen(fn, "w");
357 #endif
358 if (!osp) {
359 fprintf(stderr, "inkscape: popen(%s): %s\n",
360 fn, strerror(errno));
361 return false;
362 }
363 _stream = osp;
364 } else if (*fn == '>') {
365 fn += 1;
366 while (isspace(*fn)) fn += 1;
367 Inkscape::IO::dump_fopen_call(fn, "K");
368 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
369 if (!osf) {
370 fprintf(stderr, "inkscape: fopen(%s): %s\n",
371 fn, strerror(errno));
372 return false;
373 }
374 _stream = osf;
375 } else {
376 /* put cwd stuff in here */
377 gchar *qn = ( *fn
378 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
379 : g_strdup("lpr") );
380 #ifndef _WIN32
381 osp = popen(qn, "w");
382 #else
383 osp = _popen(qn, "w");
384 #endif
385 if (!osp) {
386 fprintf(stderr, "inkscape: popen(%s): %s\n",
387 qn, strerror(errno));
388 return false;
389 }
390 g_free(qn);
391 _stream = osp;
392 }
393 }
394
395 g_free(local_fn);
396
397 if (_stream) {
398 /* fixme: this is kinda icky */
399 #if !defined(_WIN32) && !defined(__WIN32__)
400 (void) signal(SIGPIPE, SIG_IGN);
401 #endif
402 }
403
404 return true;
405 }
406
setPSLevel(unsigned int level)407 void CairoRenderContext::setPSLevel(unsigned int level)
408 {
409 _ps_level = level;
410 }
411
setEPS(bool eps)412 void CairoRenderContext::setEPS(bool eps)
413 {
414 _eps = eps;
415 }
416
getPSLevel()417 unsigned int CairoRenderContext::getPSLevel()
418 {
419 return _ps_level;
420 }
421
setPDFLevel(unsigned int level)422 void CairoRenderContext::setPDFLevel(unsigned int level)
423 {
424 _pdf_level = level;
425 }
426
setTextToPath(bool texttopath)427 void CairoRenderContext::setTextToPath(bool texttopath)
428 {
429 _is_texttopath = texttopath;
430 }
431
setOmitText(bool omittext)432 void CairoRenderContext::setOmitText(bool omittext)
433 {
434 _is_omittext = omittext;
435 }
436
getOmitText()437 bool CairoRenderContext::getOmitText()
438 {
439 return _is_omittext;
440 }
441
setFilterToBitmap(bool filtertobitmap)442 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
443 {
444 _is_filtertobitmap = filtertobitmap;
445 }
446
getFilterToBitmap()447 bool CairoRenderContext::getFilterToBitmap()
448 {
449 return _is_filtertobitmap;
450 }
451
setBitmapResolution(int resolution)452 void CairoRenderContext::setBitmapResolution(int resolution)
453 {
454 _bitmapresolution = resolution;
455 }
456
getBitmapResolution()457 int CairoRenderContext::getBitmapResolution()
458 {
459 return _bitmapresolution;
460 }
461
462 cairo_surface_t*
getSurface()463 CairoRenderContext::getSurface()
464 {
465 g_assert( _is_valid );
466
467 return _surface;
468 }
469
470 bool
saveAsPng(const char * file_name)471 CairoRenderContext::saveAsPng(const char *file_name)
472 {
473 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
474 if (status)
475 return false;
476 else
477 return true;
478 }
479
480 void
setRenderMode(CairoRenderMode mode)481 CairoRenderContext::setRenderMode(CairoRenderMode mode)
482 {
483 switch (mode) {
484 case RENDER_MODE_NORMAL:
485 case RENDER_MODE_CLIP:
486 _render_mode = mode;
487 break;
488 default:
489 _render_mode = RENDER_MODE_NORMAL;
490 break;
491 }
492 }
493
494 CairoRenderContext::CairoRenderMode
getRenderMode() const495 CairoRenderContext::getRenderMode() const
496 {
497 return _render_mode;
498 }
499
500 void
setClipMode(CairoClipMode mode)501 CairoRenderContext::setClipMode(CairoClipMode mode)
502 {
503 switch (mode) {
504 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
505 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
506 _clip_mode = mode;
507 break;
508 default:
509 _clip_mode = CLIP_MODE_PATH;
510 break;
511 }
512 }
513
514 CairoRenderContext::CairoClipMode
getClipMode() const515 CairoRenderContext::getClipMode() const
516 {
517 return _clip_mode;
518 }
519
_createState()520 CairoRenderState* CairoRenderContext::_createState()
521 {
522 CairoRenderState *state = static_cast<CairoRenderState*>(g_try_malloc(sizeof(CairoRenderState)));
523 g_assert( state != nullptr );
524
525 state->has_filtereffect = FALSE;
526 state->merge_opacity = TRUE;
527 state->opacity = 1.0;
528 state->need_layer = FALSE;
529 state->has_overflow = FALSE;
530 state->parent_has_userspace = FALSE;
531 state->clip_path = nullptr;
532 state->mask = nullptr;
533
534 return state;
535 }
536
pushLayer()537 void CairoRenderContext::pushLayer()
538 {
539 g_assert( _is_valid );
540
541 TRACE(("--pushLayer\n"));
542 cairo_push_group(_cr);
543
544 // clear buffer
545 if (!_vector_based_target) {
546 cairo_save(_cr);
547 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
548 cairo_paint(_cr);
549 cairo_restore(_cr);
550 }
551 }
552
553 void
popLayer(cairo_operator_t composite)554 CairoRenderContext::popLayer(cairo_operator_t composite)
555 {
556 g_assert( _is_valid );
557
558 float opacity = _state->opacity;
559 TRACE(("--popLayer w/ opacity %f\n", opacity));
560
561 /*
562 At this point, the Cairo source is ready. A Cairo mask must be created if required.
563 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
564 masks independently of the objects they effect while in SVG the clip paths and masks
565 are defined relative to the objects they are attached to.
566 Notes:
567 1. An SVG object may have both a clip path and a mask!
568 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
569 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
570 the page (document). This is also not handled properly.
571 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
572 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
573 alpha. This is handled here by doing a pixel by pixel conversion.
574 */
575
576 SPClipPath *clip_path = _state->clip_path;
577 SPMask *mask = _state->mask;
578 if (clip_path || mask) {
579
580 CairoRenderContext *clip_ctx = nullptr;
581 cairo_surface_t *clip_mask = nullptr;
582
583 // Apply any clip path first
584 if (clip_path) {
585 TRACE((" Applying clip\n"));
586 if (_render_mode == RENDER_MODE_CLIP)
587 mask = nullptr; // disable mask when performing nested clipping
588
589 if (_vector_based_target) {
590 setClipMode(CLIP_MODE_PATH); // Vector
591 if (!mask) {
592 cairo_pop_group_to_source(_cr);
593 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
594 if (opacity == 1.0)
595 cairo_paint(_cr);
596 else
597 cairo_paint_with_alpha(_cr, opacity);
598
599 } else {
600 // the clipPath will be applied before masking
601 }
602 } else {
603
604 // setup a new rendering context
605 clip_ctx = _renderer->createContext();
606 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
607 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
608 // This code ties the clipping to the document coordinates. It doesn't allow
609 // for a clipped object initially drawn off the page and then translated onto
610 // the page.
611 if (!clip_ctx->setupSurface(_width, _height)) {
612 TRACE(("clip: setupSurface failed\n"));
613 _renderer->destroyContext(clip_ctx);
614 return;
615 }
616
617 // clear buffer
618 cairo_save(clip_ctx->_cr);
619 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
620 cairo_paint(clip_ctx->_cr);
621 cairo_restore(clip_ctx->_cr);
622
623 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
624 if (!mask)
625 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
626 else
627 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
628
629 // copy over the correct CTM
630 // It must be stored in item_transform of current state after pushState.
631 Geom::Affine item_transform;
632 if (_state->parent_has_userspace)
633 item_transform = getParentState()->transform * _state->item_transform;
634 else
635 item_transform = _state->item_transform;
636
637 // apply the clip path
638 clip_ctx->pushState();
639 clip_ctx->getCurrentState()->item_transform = item_transform;
640 _renderer->applyClipPath(clip_ctx, clip_path);
641 clip_ctx->popState();
642
643 clip_mask = clip_ctx->getSurface();
644 TEST(clip_ctx->saveAsPng("clip_mask.png"));
645
646 if (!mask) {
647 cairo_pop_group_to_source(_cr);
648 if (composite != CAIRO_OPERATOR_CLEAR){
649 cairo_set_operator(_cr, composite);
650 }
651 cairo_mask_surface(_cr, clip_mask, 0, 0);
652 _renderer->destroyContext(clip_ctx);
653 }
654 }
655 }
656
657 // Apply any mask second
658 if (mask) {
659 TRACE((" Applying mask\n"));
660 // create rendering context for mask
661 CairoRenderContext *mask_ctx = _renderer->createContext();
662
663 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
664 // Cairo surface is expecting the mask to be 96 dpi.
665 float surface_width = _width;
666 float surface_height = _height;
667 if( _vector_based_target ) {
668 surface_width *= 4.0/3.0;
669 surface_height *= 4.0/3.0;
670 }
671 if (!mask_ctx->setupSurface( surface_width, surface_height )) {
672 TRACE(("mask: setupSurface failed\n"));
673 _renderer->destroyContext(mask_ctx);
674 return;
675 }
676 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
677
678 // Mask should start black, but it is created white.
679 cairo_set_source_rgba(mask_ctx->_cr, 0.0, 0.0, 0.0, 1.0);
680 cairo_rectangle(mask_ctx->_cr, 0, 0, surface_width, surface_height);
681 cairo_fill(mask_ctx->_cr);
682
683 // set rendering mode to normal
684 setRenderMode(RENDER_MODE_NORMAL);
685
686 // copy the correct CTM to mask context
687 /*
688 if (_state->parent_has_userspace)
689 mask_ctx->setTransform(getParentState()->transform);
690 else
691 mask_ctx->setTransform(_state->transform);
692 */
693 // This is probably not correct... but it seems to do the trick.
694 mask_ctx->setTransform(_state->item_transform);
695
696 // render mask contents to mask_ctx
697 _renderer->applyMask(mask_ctx, mask);
698
699 TEST(mask_ctx->saveAsPng("mask.png"));
700
701 // composite with clip mask
702 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
703 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
704 _renderer->destroyContext(clip_ctx);
705 }
706
707 cairo_surface_t *mask_image = mask_ctx->getSurface();
708 int width = cairo_image_surface_get_width(mask_image);
709 int height = cairo_image_surface_get_height(mask_image);
710 int stride = cairo_image_surface_get_stride(mask_image);
711 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
712
713 // In SVG, the rgb channels as well as the alpha channel is used in masking.
714 // In Cairo, only the alpha channel is used thus requiring this conversion.
715 // SVG specifies that RGB be converted to alpha using luminance-to-alpha.
716 // Notes: This calculation assumes linear RGB values. VERIFY COLOR SPACE!
717 // The incoming pixel values already include alpha, fill-opacity, etc.,
718 // however, opacity must still be applied.
719 TRACE(("premul w/ %f\n", opacity));
720 const float coeff_r = 0.2125 / 255.0;
721 const float coeff_g = 0.7154 / 255.0;
722 const float coeff_b = 0.0721 / 255.0;
723 for (int row = 0 ; row < height; row++) {
724 unsigned char *row_data = pixels + (row * stride);
725 for (int i = 0 ; i < width; i++) {
726 guint32 *pixel = reinterpret_cast<guint32 *>(row_data) + i;
727 float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
728 ((*pixel & 0x0000ff00) >> 8) * coeff_g +
729 ((*pixel & 0x000000ff) ) * coeff_b );
730 // lum_alpha can be slightly greater than 1 due to rounding errors...
731 // but this should be OK since it doesn't matter what the lower
732 // six hexadecimal numbers of *pixel are.
733 *pixel = (guint32)(0xff000000 * lum_alpha * opacity);
734 }
735 }
736
737 cairo_pop_group_to_source(_cr);
738 if (composite != CAIRO_OPERATOR_CLEAR){
739 cairo_set_operator(_cr, composite);
740 }
741 if (_clip_mode == CLIP_MODE_PATH) {
742 // we have to do the clipping after cairo_pop_group_to_source
743 _renderer->applyClipPath(this, clip_path);
744 }
745 // apply the mask onto the layer
746 cairo_mask_surface(_cr, mask_image, 0, 0);
747 _renderer->destroyContext(mask_ctx);
748 }
749 } else {
750 // No clip path or mask
751 cairo_pop_group_to_source(_cr);
752 if (composite != CAIRO_OPERATOR_CLEAR){
753 cairo_set_operator(_cr, composite);
754 }
755 if (opacity == 1.0)
756 cairo_paint(_cr);
757 else
758 cairo_paint_with_alpha(_cr, opacity);
759 }
760 }
tagBegin(const char * l)761 void CairoRenderContext::tagBegin(const char* l){
762 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
763 char* link = g_strdup_printf("uri='%s'", l);
764 cairo_tag_begin(_cr, CAIRO_TAG_LINK, link);
765 g_free(link);
766 #endif
767 }
768
tagEnd()769 void CairoRenderContext::tagEnd(){
770 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
771 cairo_tag_end(_cr, CAIRO_TAG_LINK);
772 #endif
773 }
774
775
776 void
addClipPath(Geom::PathVector const & pv,SPIEnum<SPWindRule> const * fill_rule)777 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum<SPWindRule> const *fill_rule)
778 {
779 g_assert( _is_valid );
780
781 // here it should be checked whether the current clip winding changed
782 // so we could switch back to masked clipping
783 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
784 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
785 } else {
786 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
787 }
788 addPathVector(pv);
789 }
790
791 void
addClippingRect(double x,double y,double width,double height)792 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
793 {
794 g_assert( _is_valid );
795
796 cairo_rectangle(_cr, x, y, width, height);
797 cairo_clip(_cr);
798 }
799
800 bool
setupSurface(double width,double height)801 CairoRenderContext::setupSurface(double width, double height)
802 {
803 // Is the surface already set up?
804 if (_is_valid)
805 return true;
806
807 if (_vector_based_target && _stream == nullptr)
808 return false;
809
810 _width = width;
811 _height = height;
812
813 cairo_surface_t *surface = nullptr;
814 cairo_matrix_t ctm;
815 cairo_matrix_init_identity (&ctm);
816 switch (_target) {
817 case CAIRO_SURFACE_TYPE_IMAGE:
818 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
819 break;
820 #ifdef CAIRO_HAS_PDF_SURFACE
821 case CAIRO_SURFACE_TYPE_PDF:
822 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
823 cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
824 break;
825 #endif
826 #ifdef CAIRO_HAS_PS_SURFACE
827 case CAIRO_SURFACE_TYPE_PS:
828 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
829 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
830 return FALSE;
831 }
832 cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
833 cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
834 break;
835 #endif
836 default:
837 return false;
838 break;
839 }
840
841 _setSurfaceMetadata(surface);
842
843 return _finishSurfaceSetup (surface, &ctm);
844 }
845
846 bool
setSurfaceTarget(cairo_surface_t * surface,bool is_vector,cairo_matrix_t * ctm)847 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
848 {
849 if (_is_valid || !surface)
850 return false;
851
852 _vector_based_target = is_vector;
853 bool ret = _finishSurfaceSetup (surface, ctm);
854 if (ret)
855 cairo_surface_reference (surface);
856 return ret;
857 }
858
859 bool
_finishSurfaceSetup(cairo_surface_t * surface,cairo_matrix_t * ctm)860 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
861 {
862 if(surface == nullptr) {
863 return false;
864 }
865 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
866 return false;
867 }
868
869 _cr = cairo_create(surface);
870 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
871 return false;
872 }
873 if (ctm)
874 cairo_set_matrix(_cr, ctm);
875 _surface = surface;
876
877 if (_vector_based_target) {
878 cairo_scale(_cr, Inkscape::Util::Quantity::convert(1, "px", "pt"), Inkscape::Util::Quantity::convert(1, "px", "pt"));
879 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
880 // set background color on non-alpha surfaces
881 // TODO: bgcolor should be derived from SPDocument (see IconImpl)
882 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
883 cairo_rectangle(_cr, 0, 0, _width, _height);
884 cairo_fill(_cr);
885 }
886
887 _is_valid = TRUE;
888
889 return true;
890 }
891
892 void
_setSurfaceMetadata(cairo_surface_t * surface)893 CairoRenderContext::_setSurfaceMetadata(cairo_surface_t *surface)
894 {
895 switch (_target) {
896 #if defined CAIRO_HAS_PDF_SURFACE && CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
897 case CAIRO_SURFACE_TYPE_PDF:
898 if (!_metadata.title.empty()) {
899 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_TITLE, _metadata.title.c_str());
900 }
901 if (!_metadata.author.empty()) {
902 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_AUTHOR, _metadata.author.c_str());
903 }
904 if (!_metadata.subject.empty()) {
905 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_SUBJECT, _metadata.subject.c_str());
906 }
907 if (!_metadata.keywords.empty()) {
908 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_KEYWORDS, _metadata.keywords.c_str());
909 }
910 if (!_metadata.creator.empty()) {
911 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATOR, _metadata.creator.c_str());
912 }
913 if (!_metadata.cdate.empty()) {
914 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATE_DATE, _metadata.cdate.c_str());
915 }
916 if (!_metadata.mdate.empty()) {
917 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_MOD_DATE, _metadata.mdate.c_str());
918 }
919 break;
920 #endif
921 #if defined CAIRO_HAS_PS_SURFACE
922 case CAIRO_SURFACE_TYPE_PS:
923 if (!_metadata.title.empty()) {
924 cairo_ps_surface_dsc_comment(surface, (Glib::ustring("%%Title: ") + _metadata.title).c_str());
925 }
926 if (!_metadata.copyright.empty()) {
927 cairo_ps_surface_dsc_comment(surface, (Glib::ustring("%%Copyright: ") + _metadata.copyright).c_str());
928 }
929 break;
930 #endif
931 default:
932 g_warning("unsupported target %d\n", _target);
933 }
934 }
935
936 bool
finish(bool finish_surface)937 CairoRenderContext::finish(bool finish_surface)
938 {
939 g_assert( _is_valid );
940
941 if (_vector_based_target && finish_surface)
942 cairo_show_page(_cr);
943
944 cairo_status_t status = cairo_status(_cr);
945 if (status != CAIRO_STATUS_SUCCESS)
946 g_critical("error while rendering output: %s", cairo_status_to_string(status));
947
948 cairo_destroy(_cr);
949 _cr = nullptr;
950
951 if (finish_surface)
952 cairo_surface_finish(_surface);
953 status = cairo_surface_status(_surface);
954 cairo_surface_destroy(_surface);
955 _surface = nullptr;
956
957 if (_layout)
958 g_object_unref(_layout);
959
960 _is_valid = FALSE;
961
962 if (_vector_based_target && _stream) {
963 /* Flush stream to be sure. */
964 (void) fflush(_stream);
965
966 fclose(_stream);
967 _stream = nullptr;
968 }
969
970 if (status == CAIRO_STATUS_SUCCESS)
971 return true;
972 else
973 return false;
974 }
975
976 void
transform(Geom::Affine const & transform)977 CairoRenderContext::transform(Geom::Affine const &transform)
978 {
979 g_assert( _is_valid );
980
981 cairo_matrix_t matrix;
982 _initCairoMatrix(&matrix, transform);
983 cairo_transform(_cr, &matrix);
984
985 // store new CTM
986 _state->transform = getTransform();
987 }
988
989 void
setTransform(Geom::Affine const & transform)990 CairoRenderContext::setTransform(Geom::Affine const &transform)
991 {
992 g_assert( _is_valid );
993
994 cairo_matrix_t matrix;
995 _initCairoMatrix(&matrix, transform);
996 cairo_set_matrix(_cr, &matrix);
997 _state->transform = transform;
998 }
999
getTransform() const1000 Geom::Affine CairoRenderContext::getTransform() const
1001 {
1002 g_assert( _is_valid );
1003
1004 cairo_matrix_t ctm;
1005 cairo_get_matrix(_cr, &ctm);
1006 Geom::Affine ret;
1007 ret[0] = ctm.xx;
1008 ret[1] = ctm.yx;
1009 ret[2] = ctm.xy;
1010 ret[3] = ctm.yy;
1011 ret[4] = ctm.x0;
1012 ret[5] = ctm.y0;
1013 return ret;
1014 }
1015
getParentTransform() const1016 Geom::Affine CairoRenderContext::getParentTransform() const
1017 {
1018 g_assert( _is_valid );
1019
1020 CairoRenderState *parent_state = getParentState();
1021 return parent_state->transform;
1022 }
1023
pushState()1024 void CairoRenderContext::pushState()
1025 {
1026 g_assert( _is_valid );
1027
1028 cairo_save(_cr);
1029
1030 CairoRenderState *new_state = _createState();
1031 // copy current state's transform
1032 new_state->transform = _state->transform;
1033 _state_stack.push_back(new_state);
1034 _state = new_state;
1035 }
1036
popState()1037 void CairoRenderContext::popState()
1038 {
1039 g_assert( _is_valid );
1040
1041 cairo_restore(_cr);
1042
1043 g_free(_state_stack.back());
1044 _state_stack.pop_back();
1045
1046 g_assert( !_state_stack.empty());
1047 _state = _state_stack.back();
1048 }
1049
pattern_hasItemChildren(SPPattern * pat)1050 static bool pattern_hasItemChildren(SPPattern *pat)
1051 {
1052 for (auto& child: pat->children) {
1053 if (SP_IS_ITEM (&child)) {
1054 return true;
1055 }
1056 }
1057 return false;
1058 }
1059
1060 cairo_pattern_t*
_createPatternPainter(SPPaintServer const * const paintserver,Geom::OptRect const & pbox)1061 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox)
1062 {
1063 g_assert( SP_IS_PATTERN(paintserver) );
1064
1065 SPPattern *pat = SP_PATTERN (paintserver);
1066
1067 Geom::Affine ps2user, pcs2dev;
1068 ps2user = Geom::identity();
1069 pcs2dev = Geom::identity();
1070
1071 double x = pat->x();
1072 double y = pat->y();
1073 double width = pat->width();
1074 double height = pat->height();
1075 double bbox_width_scaler;
1076 double bbox_height_scaler;
1077
1078 TRACE(("%f x %f pattern\n", width, height));
1079
1080 if (pbox && pat->patternUnits() == SPPattern::UNITS_OBJECTBOUNDINGBOX) {
1081 bbox_width_scaler = pbox->width();
1082 bbox_height_scaler = pbox->height();
1083 ps2user[4] = x * bbox_width_scaler + pbox->left();
1084 ps2user[5] = y * bbox_height_scaler + pbox->top();
1085 } else {
1086 bbox_width_scaler = 1.0;
1087 bbox_height_scaler = 1.0;
1088 ps2user[4] = x;
1089 ps2user[5] = y;
1090 }
1091
1092 // apply pattern transformation
1093 Geom::Affine pattern_transform(pat->getTransform());
1094 ps2user *= pattern_transform;
1095 Geom::Point ori (ps2user[4], ps2user[5]);
1096
1097 // create pattern contents coordinate system
1098 if (pat->viewBox_set) {
1099 Geom::Rect view_box = *pat->viewbox();
1100
1101 double x, y, w, h;
1102 x = 0;
1103 y = 0;
1104 w = width * bbox_width_scaler;
1105 h = height * bbox_height_scaler;
1106
1107 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1108 pcs2dev[0] = w / view_box.width();
1109 pcs2dev[3] = h / view_box.height();
1110 pcs2dev[4] = x - view_box.left() * pcs2dev[0];
1111 pcs2dev[5] = y - view_box.top() * pcs2dev[3];
1112 } else if (pbox && pat->patternContentUnits() == SPPattern::UNITS_OBJECTBOUNDINGBOX) {
1113 pcs2dev[0] = pbox->width();
1114 pcs2dev[3] = pbox->height();
1115 }
1116
1117 // Calculate the size of the surface which has to be created
1118 #define SUBPIX_SCALE 100
1119 // Cairo requires an integer pattern surface width/height.
1120 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1121 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1122 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1123 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1124 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1125 // create new rendering context
1126 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1127
1128 // adjust the size of the painted pattern to fit exactly the created surface
1129 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1130 double scale_width = surface_width / (bbox_width_scaler * width);
1131 double scale_height = surface_height / (bbox_height_scaler * height);
1132 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1133 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1134 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1135 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1136 }
1137
1138 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1139 ps2user[4] = ori[Geom::X];
1140 ps2user[5] = ori[Geom::Y];
1141
1142 pattern_ctx->setTransform(pcs2dev);
1143 pattern_ctx->pushState();
1144
1145 // create drawing and group
1146 Inkscape::Drawing drawing;
1147 unsigned dkey = SPItem::display_key_new(1);
1148
1149 // show items and render them
1150 for (SPPattern *pat_i = pat; pat_i != nullptr; pat_i = pat_i->ref ? pat_i->ref->getObject() : nullptr) {
1151 if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1152 for (auto& child: pat_i->children) {
1153 if (SP_IS_ITEM(&child)) {
1154 SP_ITEM(&child)->invoke_show(drawing, dkey, SP_ITEM_REFERENCE_FLAGS);
1155 _renderer->renderItem(pattern_ctx, SP_ITEM(&child));
1156 }
1157 }
1158 break; // do not go further up the chain if children are found
1159 }
1160 }
1161
1162 pattern_ctx->popState();
1163
1164 // setup a cairo_pattern_t
1165 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1166 TEST(pattern_ctx->saveAsPng("pattern.png"));
1167 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1168 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1169
1170 // set pattern transformation
1171 cairo_matrix_t pattern_matrix;
1172 _initCairoMatrix(&pattern_matrix, ps2user);
1173 cairo_matrix_invert(&pattern_matrix);
1174 cairo_pattern_set_matrix(result, &pattern_matrix);
1175
1176 delete pattern_ctx;
1177
1178 // hide all items
1179 for (SPPattern *pat_i = pat; pat_i != nullptr; pat_i = pat_i->ref ? pat_i->ref->getObject() : nullptr) {
1180 if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1181 for (auto& child: pat_i->children) {
1182 if (SP_IS_ITEM(&child)) {
1183 SP_ITEM(&child)->invoke_hide(dkey);
1184 }
1185 }
1186 break; // do not go further up the chain if children are found
1187 }
1188 }
1189
1190 return result;
1191 }
1192
1193 cairo_pattern_t*
_createHatchPainter(SPPaintServer const * const paintserver,Geom::OptRect const & pbox)1194 CairoRenderContext::_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox) {
1195 SPHatch const *hatch = dynamic_cast<SPHatch const *>(paintserver);
1196 g_assert( hatch );
1197
1198 g_assert(hatch->pitch() > 0);
1199
1200 // create drawing and group
1201 Inkscape::Drawing drawing;
1202 unsigned dkey = SPItem::display_key_new(1);
1203
1204 // TODO need to refactor 'evil' referenced code for const correctness.
1205 SPHatch *evil = const_cast<SPHatch *>(hatch);
1206 evil->show(drawing, dkey, pbox);
1207
1208 SPHatch::RenderInfo render_info = hatch->calculateRenderInfo(dkey);
1209 Geom::Rect tile_rect = render_info.tile_rect;
1210
1211 // Cairo requires an integer pattern surface width/height.
1212 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1213 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1214 const int subpix_scale = 10;
1215 double surface_width = MAX(ceil(subpix_scale * tile_rect.width() - 0.5), 1);
1216 double surface_height = MAX(ceil(subpix_scale * tile_rect.height() - 0.5), 1);
1217 Geom::Affine drawing_scale = Geom::Scale(surface_width / tile_rect.width(), surface_height / tile_rect.height());
1218 Geom::Affine drawing_transform = Geom::Translate(-tile_rect.min()) * drawing_scale;
1219
1220 Geom::Affine child_transform = render_info.child_transform;
1221 child_transform *= drawing_transform;
1222
1223 //The rendering of hatch overflow is implemented by repeated drawing
1224 //of hatch paths over one strip. Within each iteration paths are moved by pitch value.
1225 //The movement progresses from right to left. This gives the same result
1226 //as drawing whole strips in left-to-right order.
1227 gdouble overflow_right_strip = 0.0;
1228 int overflow_steps = 1;
1229 Geom::Affine overflow_transform;
1230 if (hatch->style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) {
1231 Geom::Interval bounds = hatch->bounds();
1232 overflow_right_strip = floor(bounds.max() / hatch->pitch()) * hatch->pitch();
1233 overflow_steps = ceil((overflow_right_strip - bounds.min()) / hatch->pitch()) + 1;
1234 overflow_transform = Geom::Translate(hatch->pitch(), 0.0);
1235 }
1236
1237 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1238 pattern_ctx->setTransform(child_transform);
1239 pattern_ctx->transform(Geom::Translate(-overflow_right_strip, 0.0));
1240 pattern_ctx->pushState();
1241
1242 std::vector<SPHatchPath *> children(evil->hatchPaths());
1243
1244 for (int i = 0; i < overflow_steps; i++) {
1245 for (auto path : children) {
1246 _renderer->renderHatchPath(pattern_ctx, *path, dkey);
1247 }
1248 pattern_ctx->transform(overflow_transform);
1249 }
1250
1251 pattern_ctx->popState();
1252
1253 // setup a cairo_pattern_t
1254 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1255 TEST(pattern_ctx->saveAsPng("hatch.png"));
1256 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1257 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1258
1259 Geom::Affine pattern_transform;
1260 pattern_transform = render_info.pattern_to_user_transform.inverse() * drawing_transform;
1261 ink_cairo_pattern_set_matrix(result, pattern_transform);
1262
1263 evil->hide(dkey);
1264
1265 delete pattern_ctx;
1266 return result;
1267 }
1268
1269 cairo_pattern_t*
_createPatternForPaintServer(SPPaintServer const * const paintserver,Geom::OptRect const & pbox,float alpha)1270 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1271 Geom::OptRect const &pbox, float alpha)
1272 {
1273 cairo_pattern_t *pattern = nullptr;
1274 bool apply_bbox2user = FALSE;
1275
1276 if (SP_IS_LINEARGRADIENT (paintserver)) {
1277
1278 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1279
1280 SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1281
1282 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1283 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1284 if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1285 // convert to userspace
1286 Geom::Affine bbox2user(pbox->width(), 0, 0, pbox->height(), pbox->left(), pbox->top());
1287 p1 *= bbox2user;
1288 p2 *= bbox2user;
1289 }
1290
1291 // create linear gradient pattern
1292 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1293
1294 // add stops
1295 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1296 float rgb[3];
1297 lg->vector.stops[i].color.get_rgb_floatv(rgb);
1298 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1299 }
1300 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1301
1302 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1303
1304 SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1305
1306 Geom::Point c (rg->cx.computed, rg->cy.computed);
1307 Geom::Point f (rg->fx.computed, rg->fy.computed);
1308 double r = rg->r.computed;
1309 double fr = rg->fr.computed;
1310 if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1311 apply_bbox2user = true;
1312
1313 // create radial gradient pattern
1314 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], fr, c[Geom::X], c[Geom::Y], r);
1315
1316 // add stops
1317 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1318 float rgb[3];
1319 rg->vector.stops[i].color.get_rgb_floatv(rgb);
1320 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1321 }
1322 } else if (SP_IS_MESHGRADIENT (paintserver)) {
1323 SPMeshGradient *mg = SP_MESHGRADIENT(paintserver);
1324
1325 pattern = mg->pattern_new(_cr, pbox, 1.0);
1326 } else if (SP_IS_PATTERN (paintserver)) {
1327 pattern = _createPatternPainter(paintserver, pbox);
1328 } else if ( dynamic_cast<SPHatch const *>(paintserver) ) {
1329 pattern = _createHatchPainter(paintserver, pbox);
1330 } else {
1331 return nullptr;
1332 }
1333
1334 if (pattern && SP_IS_GRADIENT(paintserver)) {
1335 SPGradient *g = SP_GRADIENT(paintserver);
1336
1337 // set extend type
1338 SPGradientSpread spread = g->fetchSpread();
1339 switch (spread) {
1340 case SP_GRADIENT_SPREAD_REPEAT: {
1341 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1342 break;
1343 }
1344 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1345 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1346 break;
1347 }
1348 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1349 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1350 break;
1351 }
1352 default: {
1353 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1354 break;
1355 }
1356 }
1357
1358 cairo_matrix_t pattern_matrix;
1359 if (g->gradientTransform_set) {
1360 // apply gradient transformation
1361 cairo_matrix_init(&pattern_matrix,
1362 g->gradientTransform[0], g->gradientTransform[1],
1363 g->gradientTransform[2], g->gradientTransform[3],
1364 g->gradientTransform[4], g->gradientTransform[5]);
1365 } else {
1366 cairo_matrix_init_identity (&pattern_matrix);
1367 }
1368
1369 if (apply_bbox2user) {
1370 // convert to userspace
1371 cairo_matrix_t bbox2user;
1372 cairo_matrix_init (&bbox2user, pbox->width(), 0, 0, pbox->height(), pbox->left(), pbox->top());
1373 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1374 }
1375 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1376 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1377 }
1378
1379 return pattern;
1380 }
1381
1382 void
_setFillStyle(SPStyle const * const style,Geom::OptRect const & pbox)1383 CairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect const &pbox)
1384 {
1385 g_return_if_fail( !style->fill.set
1386 || style->fill.isColor()
1387 || style->fill.isPaintserver() );
1388
1389 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1390 if (_state->merge_opacity) {
1391 alpha *= _state->opacity;
1392 TRACE(("merged op=%f\n", alpha));
1393 }
1394
1395 SPPaintServer const *paint_server = style->getFillPaintServer();
1396 if (paint_server && paint_server->isValid()) {
1397
1398 g_assert(SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1399 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style))
1400 || dynamic_cast<SPHatch *>(SP_STYLE_FILL_SERVER(style)));
1401
1402 cairo_pattern_t *pattern = _createPatternForPaintServer(paint_server, pbox, alpha);
1403 if (pattern) {
1404 cairo_set_source(_cr, pattern);
1405 cairo_pattern_destroy(pattern);
1406 }
1407 } else if (style->fill.colorSet) {
1408 float rgb[3];
1409 style->fill.value.color.get_rgb_floatv(rgb);
1410
1411 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1412
1413 } else { // unset fill is black
1414 g_assert(!style->fill.set
1415 || (paint_server && !paint_server->isValid()));
1416
1417 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1418 }
1419 }
1420
1421 void
_setStrokeStyle(SPStyle const * style,Geom::OptRect const & pbox)1422 CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &pbox)
1423 {
1424 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1425 if (_state->merge_opacity)
1426 alpha *= _state->opacity;
1427
1428 if (style->stroke.isColor() || (style->stroke.isPaintserver() && !style->getStrokePaintServer()->isValid())) {
1429 float rgb[3];
1430 style->stroke.value.color.get_rgb_floatv(rgb);
1431
1432 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1433 } else {
1434 g_assert( style->stroke.isPaintserver()
1435 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1436 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style))
1437 || dynamic_cast<SPHatch *>(SP_STYLE_STROKE_SERVER(style)));
1438
1439 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1440
1441 if (pattern) {
1442 cairo_set_source(_cr, pattern);
1443 cairo_pattern_destroy(pattern);
1444 }
1445 }
1446
1447 if (!style->stroke_dasharray.values.empty())
1448 {
1449 size_t ndashes = style->stroke_dasharray.values.size();
1450 double* dashes =(double*)malloc(ndashes*sizeof(double));
1451 for( unsigned i = 0; i < ndashes; ++i ) {
1452 dashes[i] = style->stroke_dasharray.values[i].value;
1453 }
1454 cairo_set_dash(_cr, dashes, ndashes, style->stroke_dashoffset.value);
1455 free(dashes);
1456 } else {
1457 cairo_set_dash(_cr, nullptr, 0, 0.0); // disable dashing
1458 }
1459
1460 // This allows hairlines to be drawn properly in PDF, PS, Win32-Print, etc.
1461 // It requires the following pull request in Cairo:
1462 // https://gitlab.freedesktop.org/cairo/cairo/merge_requests/21
1463 if (style->stroke_extensions.hairline) {
1464 ink_cairo_set_hairline(_cr);
1465 } else {
1466 cairo_set_line_width(_cr, style->stroke_width.computed);
1467 }
1468
1469 // set line join type
1470 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1471 switch (style->stroke_linejoin.computed) {
1472 case SP_STROKE_LINEJOIN_MITER:
1473 join = CAIRO_LINE_JOIN_MITER;
1474 break;
1475 case SP_STROKE_LINEJOIN_ROUND:
1476 join = CAIRO_LINE_JOIN_ROUND;
1477 break;
1478 case SP_STROKE_LINEJOIN_BEVEL:
1479 join = CAIRO_LINE_JOIN_BEVEL;
1480 break;
1481 }
1482 cairo_set_line_join(_cr, join);
1483
1484 // set line cap type
1485 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1486 switch (style->stroke_linecap.computed) {
1487 case SP_STROKE_LINECAP_BUTT:
1488 cap = CAIRO_LINE_CAP_BUTT;
1489 break;
1490 case SP_STROKE_LINECAP_ROUND:
1491 cap = CAIRO_LINE_CAP_ROUND;
1492 break;
1493 case SP_STROKE_LINECAP_SQUARE:
1494 cap = CAIRO_LINE_CAP_SQUARE;
1495 break;
1496 }
1497 cairo_set_line_cap(_cr, cap);
1498 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1499 }
1500
1501 void
_prepareRenderGraphic()1502 CairoRenderContext::_prepareRenderGraphic()
1503 {
1504 // Only PDFLaTeX supports importing a single page of a graphics file,
1505 // so only PDF backend gets interleaved text/graphics
1506 if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF && _render_mode != RENDER_MODE_CLIP) {
1507 if (_omittext_state == NEW_PAGE_ON_GRAPHIC) {
1508 // better set this immediately (not sure if masks applied during "popLayer" could call
1509 // this function, too, triggering the same code again in error
1510 _omittext_state = GRAPHIC_ON_TOP;
1511
1512 // As we can not emit the page in the middle of a layer (aka group) - it will not be fully painted yet! -
1513 // the following basically mirrors the calls in CairoRenderer::renderItem (but in reversed order)
1514 // - first traverse all saved states in reversed order (i.e. from deepest nesting to the top)
1515 // and apply clipping / masking to layers on the way (this is done in popLayer)
1516 // - then emit the page using cairo_show_page()
1517 // - finally restore the previous state with proper transforms and appropriate layers again
1518 //
1519 // TODO: While this appears to be an ugly hack it seems to work
1520 // Somebody with a more intimate understanding of cairo and the renderer implementation might
1521 // be able to implement this in a cleaner way, though.
1522 int stack_size = _state_stack.size();
1523 for (int i = stack_size-1; i > 0; i--) {
1524 if (_state_stack[i]->need_layer)
1525 popLayer();
1526 cairo_restore(_cr);
1527 _state = _state_stack[i-1];
1528 }
1529
1530 cairo_show_page(_cr);
1531
1532 for (int i = 1; i < stack_size; i++) {
1533 cairo_save(_cr);
1534 _state = _state_stack[i];
1535 if (_state->need_layer)
1536 pushLayer();
1537 setTransform(_state->transform);
1538 }
1539 }
1540 _omittext_state = GRAPHIC_ON_TOP;
1541 }
1542 }
1543
1544 void
_prepareRenderText()1545 CairoRenderContext::_prepareRenderText()
1546 {
1547 // Only PDFLaTeX supports importing a single page of a graphics file,
1548 // so only PDF backend gets interleaved text/graphics
1549 if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF) {
1550 if (_omittext_state == GRAPHIC_ON_TOP)
1551 _omittext_state = NEW_PAGE_ON_GRAPHIC;
1552 }
1553 }
1554
1555 /* We need CairoPaintOrder as markers are rendered in a separate step and may be rendered
1556 * in between fill and stroke.
1557 */
1558 bool
renderPathVector(Geom::PathVector const & pathv,SPStyle const * style,Geom::OptRect const & pbox,CairoPaintOrder order)1559 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, Geom::OptRect const &pbox, CairoPaintOrder order)
1560 {
1561 g_assert( _is_valid );
1562
1563 _prepareRenderGraphic();
1564
1565 if (_render_mode == RENDER_MODE_CLIP) {
1566 if (_clip_mode == CLIP_MODE_PATH) {
1567 addClipPath(pathv, &style->fill_rule);
1568 } else {
1569 setPathVector(pathv);
1570 if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
1571 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1572 } else {
1573 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1574 }
1575 if (style->mix_blend_mode.set && style->mix_blend_mode.value) {
1576 cairo_set_operator(_cr, ink_css_blend_to_cairo_operator(style->mix_blend_mode.value));
1577 }
1578 cairo_fill(_cr);
1579 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1580 }
1581 return true;
1582 }
1583
1584 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0 ||
1585 order == STROKE_ONLY;
1586 bool no_stroke = style->stroke.isNone() || (!style->stroke_extensions.hairline && style->stroke_width.computed < 1e-9) ||
1587 style->stroke_opacity.value == 0 || order == FILL_ONLY;
1588
1589 if (no_fill && no_stroke)
1590 return true;
1591
1592 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1593 ( _state->opacity != 1.0 || _state->clip_path != nullptr || _state->mask != nullptr ) );
1594 bool blend = false;
1595 if (style->mix_blend_mode.set && style->mix_blend_mode.value != SP_CSS_BLEND_NORMAL) {
1596 need_layer = true;
1597 blend = true;
1598 }
1599 if (!need_layer)
1600 cairo_save(_cr);
1601 else
1602 pushLayer();
1603
1604 if (!no_fill) {
1605 if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
1606 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1607 } else {
1608 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1609 }
1610 }
1611
1612 setPathVector(pathv);
1613
1614 if (!no_fill && (order == STROKE_OVER_FILL || order == FILL_ONLY)) {
1615 _setFillStyle(style, pbox);
1616
1617 if (no_stroke)
1618 cairo_fill(_cr);
1619 else
1620 cairo_fill_preserve(_cr);
1621 }
1622
1623 if (!no_stroke) {
1624 _setStrokeStyle(style, pbox);
1625
1626 if (no_fill || order == STROKE_OVER_FILL)
1627 cairo_stroke(_cr);
1628 else
1629 cairo_stroke_preserve(_cr);
1630 }
1631
1632 if (!no_fill && order == FILL_OVER_STROKE) {
1633 _setFillStyle(style, pbox);
1634
1635 cairo_fill(_cr);
1636 }
1637
1638 if (need_layer) {
1639 if (blend) {
1640 popLayer(ink_css_blend_to_cairo_operator(style->mix_blend_mode.value));
1641 } else {
1642 popLayer();
1643 }
1644 } else {
1645 cairo_restore(_cr);
1646 }
1647
1648 return true;
1649 }
1650
renderImage(Inkscape::Pixbuf * pb,Geom::Affine const & image_transform,SPStyle const * style)1651 bool CairoRenderContext::renderImage(Inkscape::Pixbuf *pb,
1652 Geom::Affine const &image_transform, SPStyle const *style)
1653 {
1654 g_assert( _is_valid );
1655
1656 if (_render_mode == RENDER_MODE_CLIP) {
1657 return true;
1658 }
1659
1660 _prepareRenderGraphic();
1661
1662 int w = pb->width();
1663 int h = pb->height();
1664
1665 // TODO: reenable merge_opacity if useful
1666
1667 cairo_surface_t *image_surface = pb->getSurfaceRaw();
1668 if (cairo_surface_status(image_surface)) {
1669 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1670 return false;
1671 }
1672
1673 cairo_save(_cr);
1674
1675 // scaling by width & height is not needed because it will be done by Cairo
1676 transform(image_transform);
1677
1678 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1679
1680 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1681 if (_vector_based_target) {
1682 cairo_new_path(_cr);
1683 cairo_rectangle(_cr, 0, 0, w, h);
1684 cairo_clip(_cr);
1685 }
1686
1687 // Cairo filter method will be mapped to PS/PDF 'interpolate' true/false).
1688 // See cairo-pdf-surface.c
1689 if (style) {
1690 // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty
1691 // https://drafts.csswg.org/css-images-3/#the-image-rendering
1692 // style.h/style.cpp, drawing-image.cpp
1693 //
1694 // CSS 3 defines:
1695 // 'optimizeSpeed' as alias for "pixelated"
1696 // 'optimizeQuality' as alias for "smooth"
1697 switch (style->image_rendering.computed) {
1698 case SP_CSS_IMAGE_RENDERING_OPTIMIZESPEED:
1699 case SP_CSS_IMAGE_RENDERING_PIXELATED:
1700 // we don't have an implementation for crisp-edges, but it should *not* smooth or blur
1701 case SP_CSS_IMAGE_RENDERING_CRISPEDGES:
1702 cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_NEAREST);
1703 break;
1704 case SP_CSS_IMAGE_RENDERING_OPTIMIZEQUALITY:
1705 case SP_CSS_IMAGE_RENDERING_AUTO:
1706 default:
1707 cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_BEST);
1708 break;
1709 }
1710 }
1711
1712 if (style->mix_blend_mode.set && style->mix_blend_mode.value) {
1713 cairo_set_operator(_cr, ink_css_blend_to_cairo_operator(style->mix_blend_mode.value));
1714 }
1715
1716 cairo_paint(_cr);
1717
1718 cairo_restore(_cr);
1719 return true;
1720 }
1721
1722 #define GLYPH_ARRAY_SIZE 64
1723
1724 // TODO investigate why the font is being ignored:
_showGlyphs(cairo_t * cr,PangoFont *,std::vector<CairoGlyphInfo> const & glyphtext,bool path)1725 unsigned int CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont * /*font*/, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1726 {
1727 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1728 cairo_glyph_t *glyphs = glyph_array;
1729 unsigned int num_glyphs = glyphtext.size();
1730 if (num_glyphs > GLYPH_ARRAY_SIZE) {
1731 glyphs = (cairo_glyph_t*)g_try_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1732 if(glyphs == nullptr) {
1733 g_warning("CairorenderContext::_showGlyphs: can not allocate memory for %d glyphs.", num_glyphs);
1734 return 0;
1735 }
1736 }
1737
1738 unsigned int num_invalid_glyphs = 0;
1739 unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1740 for (const auto & it_info : glyphtext) {
1741 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1742 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1743 if (it_info.index == 0x0FFFFFFF || it_info.index & 0x10000000) {
1744 TRACE(("INVALID GLYPH found\n"));
1745 g_message("Invalid glyph found, continuing...");
1746 num_invalid_glyphs++;
1747 continue;
1748 }
1749 glyphs[i].index = it_info.index;
1750 glyphs[i].x = it_info.x;
1751 glyphs[i].y = it_info.y;
1752 i++;
1753 }
1754
1755 if (path) {
1756 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1757 } else {
1758 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1759 }
1760
1761 if (num_glyphs > GLYPH_ARRAY_SIZE) {
1762 g_free(glyphs);
1763 }
1764
1765 return num_glyphs - num_invalid_glyphs;
1766 }
1767
1768 bool
renderGlyphtext(PangoFont * font,Geom::Affine const & font_matrix,std::vector<CairoGlyphInfo> const & glyphtext,SPStyle const * style)1769 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Affine const &font_matrix,
1770 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1771 {
1772
1773 _prepareRenderText();
1774 if (_is_omittext)
1775 return true;
1776
1777 // create a cairo_font_face from PangoFont
1778 // double size = style->font_size.computed; /// \fixme why is this variable never used?
1779 gpointer fonthash = (gpointer)font;
1780 cairo_font_face_t *font_face = nullptr;
1781 if(font_table.find(fonthash)!=font_table.end())
1782 font_face = font_table[fonthash];
1783
1784 FcPattern *fc_pattern = nullptr;
1785
1786 #ifdef USE_PANGO_WIN32
1787 # ifdef CAIRO_HAS_WIN32_FONT
1788 LOGFONTA *lfa = pango_win32_font_logfont(font);
1789 LOGFONTW lfw;
1790
1791 ZeroMemory(&lfw, sizeof(LOGFONTW));
1792 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1793 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1794
1795 if(font_face == NULL) {
1796 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1797 font_table[fonthash] = font_face;
1798 }
1799 # endif
1800 #else
1801 # ifdef CAIRO_HAS_FT_FONT
1802 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1803 fc_pattern = fc_font->font_pattern;
1804 if(font_face == nullptr) {
1805 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1806 font_table[fonthash] = font_face;
1807 }
1808 # endif
1809 #endif
1810
1811 cairo_save(_cr);
1812 cairo_set_font_face(_cr, font_face);
1813
1814 //if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1815 // size = 12.0;
1816
1817 // set the given font matrix
1818 cairo_matrix_t matrix;
1819 _initCairoMatrix(&matrix, font_matrix);
1820 cairo_set_font_matrix(_cr, &matrix);
1821
1822 if (_render_mode == RENDER_MODE_CLIP) {
1823 if (_clip_mode == CLIP_MODE_MASK) {
1824 if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
1825 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1826 } else {
1827 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1828 }
1829 _showGlyphs(_cr, font, glyphtext, FALSE);
1830 } else {
1831 // just add the glyph paths to the current context
1832 _showGlyphs(_cr, font, glyphtext, TRUE);
1833 }
1834 } else {
1835
1836 bool fill = false;
1837 if (style->fill.isColor() || style->fill.isPaintserver()) {
1838 fill = true;
1839 }
1840
1841 bool stroke = false;
1842 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1843 stroke = true;
1844 }
1845
1846 if (style->mix_blend_mode.set && style->mix_blend_mode.value) {
1847 cairo_set_operator(_cr, ink_css_blend_to_cairo_operator(style->mix_blend_mode.value));
1848 }
1849
1850 // Text never has markers
1851 bool stroke_over_fill = true;
1852 if ( (style->paint_order.layer[0] == SP_CSS_PAINT_ORDER_STROKE &&
1853 style->paint_order.layer[1] == SP_CSS_PAINT_ORDER_FILL) ||
1854
1855 (style->paint_order.layer[0] == SP_CSS_PAINT_ORDER_STROKE &&
1856 style->paint_order.layer[2] == SP_CSS_PAINT_ORDER_FILL) ||
1857
1858 (style->paint_order.layer[1] == SP_CSS_PAINT_ORDER_STROKE &&
1859 style->paint_order.layer[2] == SP_CSS_PAINT_ORDER_FILL) ) {
1860 stroke_over_fill = false;
1861 }
1862
1863 bool have_path = false;
1864 if (fill && stroke_over_fill) {
1865 _setFillStyle(style, Geom::OptRect());
1866 if (_is_texttopath) {
1867 _showGlyphs(_cr, font, glyphtext, true);
1868 if (stroke) {
1869 cairo_fill_preserve(_cr);
1870 have_path = true;
1871 } else {
1872 cairo_fill(_cr);
1873 }
1874 } else {
1875 _showGlyphs(_cr, font, glyphtext, false);
1876 }
1877 }
1878
1879 if (stroke) {
1880 _setStrokeStyle(style, Geom::OptRect());
1881 if (!have_path) {
1882 _showGlyphs(_cr, font, glyphtext, true);
1883 }
1884 if (fill && _is_texttopath && !stroke_over_fill) {
1885 cairo_stroke_preserve(_cr);
1886 have_path = true;
1887 } else {
1888 cairo_stroke(_cr);
1889 }
1890 }
1891
1892 if (fill && !stroke_over_fill) {
1893 _setFillStyle(style, Geom::OptRect());
1894 if (_is_texttopath) {
1895 if (!have_path) {
1896 // Could happen if both 'stroke' and 'stroke_over_fill' are false
1897 _showGlyphs(_cr, font, glyphtext, true);
1898 }
1899 cairo_fill(_cr);
1900 } else {
1901 _showGlyphs(_cr, font, glyphtext, false);
1902 }
1903 }
1904
1905 }
1906
1907 cairo_restore(_cr);
1908
1909 // if (font_face)
1910 // cairo_font_face_destroy(font_face);
1911
1912 return true;
1913 }
1914
1915 /* Helper functions */
1916
1917 void
setPathVector(Geom::PathVector const & pv)1918 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1919 {
1920 cairo_new_path(_cr);
1921 addPathVector(pv);
1922 }
1923
1924 void
addPathVector(Geom::PathVector const & pv)1925 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1926 {
1927 feed_pathvector_to_cairo(_cr, pv);
1928 }
1929
1930 void
_concatTransform(cairo_t * cr,double xx,double yx,double xy,double yy,double x0,double y0)1931 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1932 {
1933 cairo_matrix_t matrix;
1934
1935 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1936 cairo_transform(cr, &matrix);
1937 }
1938
1939 void
_initCairoMatrix(cairo_matrix_t * matrix,Geom::Affine const & transform)1940 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Affine const &transform)
1941 {
1942 matrix->xx = transform[0];
1943 matrix->yx = transform[1];
1944 matrix->xy = transform[2];
1945 matrix->yy = transform[3];
1946 matrix->x0 = transform[4];
1947 matrix->y0 = transform[5];
1948 }
1949
1950 void
_concatTransform(cairo_t * cr,Geom::Affine const & transform)1951 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Affine const &transform)
1952 {
1953 _concatTransform(cr, transform[0], transform[1],
1954 transform[2], transform[3],
1955 transform[4], transform[5]);
1956 }
1957
1958 static cairo_status_t
_write_callback(void * closure,const unsigned char * data,unsigned int length)1959 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1960 {
1961 size_t written;
1962 FILE *file = (FILE*)closure;
1963
1964 written = fwrite (data, 1, length, file);
1965
1966 if (written == length)
1967 return CAIRO_STATUS_SUCCESS;
1968 else
1969 return CAIRO_STATUS_WRITE_ERROR;
1970 }
1971
1972 #include "clear-n_.h"
1973
1974 } /* namespace Internal */
1975 } /* namespace Extension */
1976 } /* namespace Inkscape */
1977
1978 #undef TRACE
1979 #undef TEST
1980
1981
1982 /*
1983 Local Variables:
1984 mode:c++
1985 c-file-style:"stroustrup"
1986 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1987 indent-tabs-mode:nil
1988 fill-column:99
1989 End:
1990 */
1991 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
1992