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