Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
cairo-render-context.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
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
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 "colors/color.h"
38#include "display/drawing.h"
39#include "display/cairo-utils.h"
41
42#include "object/sp-clippath.h"
43#include "object/sp-flowtext.h"
44#include "object/sp-hatch.h"
45#include "object/sp-image.h"
46#include "object/sp-item.h"
50#include "object/sp-pattern.h"
51#include "object/sp-mask.h"
52#include "object/sp-text.h"
53
55#include "util/units.h"
56
57#include "cairo-renderer.h"
58#include "extension/system.h"
59#include "inkscape-version.h"
60#include "io/sys.h"
61#include "rdf.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#undef NOGDI
79#include <pango/pangowin32.h>
80#include <cairo-win32.h>
81#endif
82
83#include <pango/pangofc-fontmap.h>
84
85//#define TRACE(_args) g_printf _args
86//#define TRACE(_args) g_message _args
87#define TRACE(_args)
88//#define TEST(_args) _args
89#define TEST(_args)
90
91// FIXME: expose these from sp-clippath/mask.cpp
92/*struct SPClipPathView {
93 SPClipPathView *next;
94 unsigned int key;
95 Inkscape::DrawingItem *arenaitem;
96 Geom::OptRect bbox;
97};
98
99struct SPMaskView {
100 SPMaskView *next;
101 unsigned int key;
102 Inkscape::DrawingItem *arenaitem;
103 Geom::OptRect bbox;
104};*/
105
106namespace Inkscape {
107namespace Extension {
108namespace Internal {
109
110static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
111
117
119{
120 for (auto const &font : _font_table) {
121 font_data_free(font.second);
122 }
123 _font_table.clear();
124
125 if (_cr) {
126 cairo_destroy(_cr);
127 _cr = nullptr;
128 }
129 if (_surface) {
130 cairo_surface_destroy(_surface);
131 _surface = nullptr;
132 }
133 if (_layout) {
134 g_object_unref(_layout);
135 _layout = nullptr;
136 }
137 if (_stream) {
138 fclose(_stream);
139 _stream = nullptr;
140 }
141}
142
143CairoRenderContext::CairoRenderContext(CairoRenderContext &&other) { *this = std::move(other); }
144
146
148{
149 if (this == &other) {
150 return *this;
151 }
153
154 // Copy scalars
155 _renderer = other._renderer;
156 _width = other._width;
157 _height = other._height;
158 _dpi = other._dpi;
159 _pdf_level = other._pdf_level;
160 _ps_level = other._ps_level;
161 _bitmapresolution = other._bitmapresolution;
162 _is_valid = other._is_valid;
163 _eps = other._eps;
164 _is_texttopath = other._is_texttopath;
165 _is_omittext = other._is_omittext;
166 _is_show_page = other._is_show_page;
167 _is_filtertobitmap = other._is_filtertobitmap;
168 _is_pdf = other._is_pdf;
169 _is_ps = other._is_ps;
170 _clip_rule = other._clip_rule;
171 _clip_winding_failed = other._clip_winding_failed;
172 _vector_based_target = other._vector_based_target;
173 _omittext_state = other._omittext_state;
174 _target = other._target;
175 _target_format = other._target_format;
176 _render_mode = other._render_mode;
177 _clip_mode = other._clip_mode;
178
179 // Steal resources of the moved-from context
180 _stream = other._stream;
181 other._stream = nullptr;
182
183 _surface = other._surface;
184 other._surface = nullptr;
185
186 _cr = other._cr;
187 other._cr = nullptr;
188
189 _layout = other._layout;
190 other._layout = nullptr;
191
192 _state_stack = std::move(other._state_stack);
193 _metadata = std::move(other._metadata);
194
195 // Point to the same renderer and unparent the moved-from context
196 _renderer = other._renderer;
197 other._renderer = nullptr;
198 other._is_valid = false;
199
200 return *this;
201}
202
204{
205 if (auto font_face = static_cast<cairo_font_face_t *>(data)) {
206 cairo_font_face_destroy(font_face);
207 }
208}
209
211{
212 // if this is the root node just return it
213 if (_state_stack.size() == 1) {
214 return getCurrentState();
215 } else {
216 return &_state_stack[_state_stack.size() - 2];
217 }
218}
219
221{
222 // only opacity & overflow is stored for now
223 auto &state = _state_stack.back();
224 state.opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
225 state.has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
226 state.has_filtereffect = style->filter.set;
227
228 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
229 state.merge_opacity = false;
230
231 // disable rendering of opacity if there's a stroke on the fill
232 if (state.merge_opacity && !style->fill.isNone() && !style->stroke.isNone())
233 state.merge_opacity = false;
234}
235
237{
238 g_assert(_is_valid);
240
241 auto &state = _state_stack.back();
242 state.clip_path = item->getClipObject();
243 state.mask = item->getMaskObject();
244 state.item_transform = item->transform;
245
246 // If parent_has_userspace is true the parent state's transform
247 // has to be used for the mask's/clippath's context.
248 // This is so because we use the image's/(flow)text's transform for positioning
249 // instead of explicitly specifying it and letting the renderer do the
250 // transformation before rendering the item.
251 if (is<SPText>(item) || is<SPFlowtext>(item) || is<SPImage>(item)) {
252 state.parent_has_userspace = true;
253 }
254 TRACE(("setStateForItem opacity: %f\n", state.opacity));
255}
256
264{
265 g_assert(_is_valid);
266 g_assert(width > 0.0 && height > 0.0);
268 new_context._surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
269 (int)std::ceil(width), (int)std::ceil(height));
270 new_context._cr = cairo_create(new_context._surface);
271 new_context._width = width;
272 new_context._height = height;
273 new_context._is_valid = true;
274
275 return new_context;
276}
277
278bool CairoRenderContext::setImageTarget(cairo_format_t format)
279{
280 // format cannot be set on an already initialized surface
281 if (_is_valid)
282 return false;
283
284 switch (format) {
285 case CAIRO_FORMAT_ARGB32:
286 case CAIRO_FORMAT_RGB24:
287 case CAIRO_FORMAT_A8:
288 case CAIRO_FORMAT_A1:
289 _target_format = format;
290 _target = CAIRO_SURFACE_TYPE_IMAGE;
291 return true;
292 break;
293 default:
294 break;
295 }
296
297 return false;
298}
299
300template <cairo_surface_type_t type>
301bool CairoRenderContext::_setVectorTarget(gchar const *utf8_fn)
302{
303#ifndef CAIRO_HAS_PDF_SURFACE
304 if constexpr (type == CAIRO_SURFACE_TYPE_PDF) {
305 return false;
306 }
307#endif
308
309#ifndef CAIRO_HAS_PS_SURFACE
310 if constexpr (type == CAIRO_SURFACE_TYPE_PS) {
311 return false;
312 }
313#endif
314 _target = type;
316
317 FILE *osf = nullptr;
318 FILE *osp = nullptr;
319
320 gsize bytesRead = 0;
321 gsize bytesWritten = 0;
322 GError *error = nullptr;
323 gchar *local_fn = g_filename_from_utf8(utf8_fn,
324 -1, &bytesRead, &bytesWritten, &error);
325 gchar const *fn = local_fn;
326
327 /* TODO: Replace the below fprintf's with something that does the right thing whether in
328 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
329 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
330 * return code.
331 */
332 if (fn != nullptr) {
333 if (*fn == '|') {
334 fn += 1;
335 while (isspace(*fn)) fn += 1;
336#ifndef _WIN32
337 osp = popen(fn, "w");
338#else
339 osp = _popen(fn, "w");
340#endif
341 if (!osp) {
342 fprintf(stderr, "inkscape: popen(%s): %s\n",
343 fn, strerror(errno));
344 return false;
345 }
346 _stream = osp;
347 } else if (*fn == '>') {
348 fn += 1;
349 while (isspace(*fn)) fn += 1;
351 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
352 if (!osf) {
353 fprintf(stderr, "inkscape: fopen(%s): %s\n",
354 fn, strerror(errno));
355 return false;
356 }
357 _stream = osf;
358 } else {
359 /* put cwd stuff in here */
360 gchar *qn = ( *fn
361 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
362 : g_strdup("lpr") );
363#ifndef _WIN32
364 osp = popen(qn, "w");
365#else
366 osp = _popen(qn, "w");
367#endif
368 if (!osp) {
369 fprintf(stderr, "inkscape: popen(%s): %s\n",
370 qn, strerror(errno));
371 return false;
372 }
373 g_free(qn);
374 _stream = osp;
375 }
376 }
377
378 g_free(local_fn);
379
380 return true;
381}
382
383bool CairoRenderContext::setPdfTarget(gchar const *utf8_fn) { return _setVectorTarget<CAIRO_SURFACE_TYPE_PDF>(utf8_fn); }
384bool CairoRenderContext::setPsTarget(gchar const *utf8_fn) { return _setVectorTarget<CAIRO_SURFACE_TYPE_PS>(utf8_fn); }
385
386void CairoRenderContext::setPSLevel(unsigned int level)
387{
388 _ps_level = level;
389 _is_pdf = false;
390 _is_ps = true;
391}
392
393void CairoRenderContext::setPDFLevel(unsigned int level)
394{
395 _pdf_level = level;
396 _is_pdf = true;
397 _is_ps = false;
398}
399
401{
402 g_assert(_is_valid);
403
404 return _surface;
405}
406
407bool CairoRenderContext::saveAsPng(const char *file_name)
408{
409 return cairo_surface_write_to_png(_surface, file_name) == CAIRO_STATUS_SUCCESS;
410}
411
413{
414 switch (mode) {
416 case RENDER_MODE_CLIP:
418 break;
419 default:
421 break;
422 }
423}
424
426{
427 switch (mode) {
428 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
429 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
431 break;
432 default:
434 break;
435 }
436}
437
439{
440 g_assert( _is_valid );
441
442 TRACE(("--pushLayer\n"));
443 cairo_push_group(_cr);
444
445 // clear buffer
447 cairo_save(_cr);
448 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
449 cairo_paint(_cr);
450 cairo_restore(_cr);
451 }
452}
453
454void CairoRenderContext::popLayer(cairo_operator_t composite)
455{
456 g_assert( _is_valid );
457
458 float opacity = getCurrentState()->opacity;
459 TRACE(("--popLayer w/ opacity %f\n", opacity));
460
461 /*
462 At this point, the Cairo source is ready. A Cairo mask must be created if required.
463 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
464 masks independently of the objects they effect while in SVG the clip paths and masks
465 are defined relative to the objects they are attached to.
466 Notes:
467 1. An SVG object may have both a clip path and a mask!
468 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
469 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
470 the page (document). This is also not handled properly.
471 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
472 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
473 alpha. This is handled here by doing a pixel by pixel conversion.
474 */
475
476 const SPClipPath *clip_path = getCurrentState()->clip_path;
477 const SPMask *mask = getCurrentState()->mask;
478 if (clip_path || mask) {
479
480 cairo_surface_t *clip_mask = nullptr;
481
482 // Apply any clip path first (before masking)
483 if (clip_path) {
484 TRACE((" Applying clip\n"));
486 mask = nullptr; // disable mask when performing nested clipping
487
489 setClipMode(CLIP_MODE_PATH); // Vector
490 if (!mask) {
491 cairo_pop_group_to_source(_cr);
492 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
493 if (opacity == 1.0)
494 cairo_paint(_cr);
495 else
496 cairo_paint_with_alpha(_cr, opacity);
497 }
498 } else {
499
500 // setup a new rendering context
501 auto clip_ctx = _renderer->createContext();
502 clip_ctx.setImageTarget(CAIRO_FORMAT_A8);
503 clip_ctx.setClipMode(CLIP_MODE_MASK); // Raster
504 // This code ties the clipping to the document coordinates. It doesn't allow
505 // for a clipped object initially drawn off the page and then translated onto
506 // the page.
507 if (!clip_ctx.setupSurface(_width, _height)) {
508 TRACE(("clip: setupSurface failed\n"));
509 return;
510 }
511
512 // clear buffer
513 cairo_save(clip_ctx._cr);
514 cairo_set_operator(clip_ctx._cr, CAIRO_OPERATOR_CLEAR);
515 cairo_paint(clip_ctx._cr);
516 cairo_restore(clip_ctx._cr);
517
518 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
519 cairo_set_source_rgba(clip_ctx._cr, 1.0, 1.0, 1.0, mask ? 1.0 : opacity);
520
521 // It must be copied before pushState and stored after.
522 auto item_transform = getItemTransform();
523 // apply the clip path
524 clip_ctx.pushState();
525 clip_ctx.setItemTransform(item_transform);
526 _renderer->applyClipPath(&clip_ctx, clip_path);
527 clip_ctx.popState();
528
529 clip_mask = clip_ctx.getSurface();
530 TEST(clip_ctx->saveAsPng("clip_mask.png"));
531
532 if (!mask) {
533 cairo_pop_group_to_source(_cr);
534 if (composite != CAIRO_OPERATOR_CLEAR){
535 cairo_set_operator(_cr, composite);
536 }
537 cairo_mask_surface(_cr, clip_mask, 0, 0);
538 }
539 }
540 }
541
542 // Apply any mask second
543 if (mask) {
544 TRACE((" Applying mask\n"));
545 // create rendering context for mask
547
548 if (!mask_ctx.setupSurface(_width, _height)) {
549 TRACE(("mask: setupSurface failed\n"));
550 return;
551 }
552 TRACE(("mask surface: %f x %f at %i dpi\n", _width, _height, _dpi));
553
554 // Mask should start black, but it is created white.
555 cairo_set_source_rgba(mask_ctx._cr, 0.0, 0.0, 0.0, 1.0);
556 cairo_rectangle(mask_ctx._cr, 0, 0, _width, _height);
557 cairo_fill(mask_ctx._cr);
559
560 // copy the correct CTM to mask context
561 auto state = getCurrentState();
562 mask_ctx.setTransform(state->parent_has_userspace ? state->item_transform * getParentState()->transform
563 : state->transform);
564
565 // render mask contents to mask_ctx
566 _renderer->applyMask(&mask_ctx, mask);
567
568 TEST(mask_ctx->saveAsPng("mask.png"));
569
570 // composite with clip mask
571 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
572 cairo_mask_surface(mask_ctx._cr, clip_mask, 0, 0);
573 }
574
575 cairo_surface_t *mask_image = mask_ctx.getSurface();
576 int width = cairo_image_surface_get_width(mask_image);
577 int height = cairo_image_surface_get_height(mask_image);
578 int stride = cairo_image_surface_get_stride(mask_image);
579 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
580
581 // In SVG, the rgb channels as well as the alpha channel is used in masking.
582 // In Cairo, only the alpha channel is used thus requiring this conversion.
583 // SVG specifies that RGB be converted to alpha using luminance-to-alpha.
584 // Notes: This calculation assumes linear RGB values. VERIFY COLOR SPACE!
585 // The incoming pixel values already include alpha, fill-opacity, etc.,
586 // however, opacity must still be applied.
587 TRACE(("premul w/ %f\n", opacity));
588 const float coeff_r = 0.2125 / 255.0;
589 const float coeff_g = 0.7154 / 255.0;
590 const float coeff_b = 0.0721 / 255.0;
591 for (int row = 0 ; row < height; row++) {
592 unsigned char *row_data = pixels + (row * stride);
593 for (int i = 0 ; i < width; i++) {
594 guint32 *pixel = reinterpret_cast<guint32 *>(row_data) + i;
595 float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
596 ((*pixel & 0x0000ff00) >> 8) * coeff_g +
597 ((*pixel & 0x000000ff) ) * coeff_b );
598 // lum_alpha can be slightly greater than 1 due to rounding errors...
599 // but this should be OK since it doesn't matter what the lower
600 // six hexadecimal numbers of *pixel are.
601 *pixel = (guint32)(0xff000000 * lum_alpha * opacity);
602 }
603 }
604
605 cairo_pop_group_to_source(_cr);
606 if (composite != CAIRO_OPERATOR_CLEAR){
607 cairo_set_operator(_cr, composite);
608 }
609 if (_clip_mode == CLIP_MODE_PATH) {
610 // we have to do the clipping after cairo_pop_group_to_source
611 _renderer->applyClipPath(this, clip_path);
612 }
613
614 {
615 // Transformations are applied when rendering the mask, so
616 // do not apply them again when applying the mask.
617 cairo_matrix_t old_transform;
618 cairo_get_matrix(_cr, &old_transform);
619 cairo_identity_matrix(_cr);
620
621 // apply the mask onto the layer
622 cairo_mask_surface(_cr, mask_image, 0, 0);
623 cairo_set_matrix(_cr, &old_transform);
624 }
625 }
626 } else {
627 // No clip path or mask
628 cairo_pop_group_to_source(_cr);
629 if (composite != CAIRO_OPERATOR_CLEAR){
630 cairo_set_operator(_cr, composite);
631 }
632 if (opacity == 1.0)
633 cairo_paint(_cr);
634 else
635 cairo_paint_with_alpha(_cr, opacity);
636 }
637}
638
640{
641#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
642 cairo_tag_begin(_cr, CAIRO_TAG_LINK, l);
643#endif
644}
645
647{
648#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
649 cairo_tag_end(_cr, CAIRO_TAG_LINK);
650#endif
651}
652
654{
655#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
656 char* dest = g_strdup_printf("name='%s'", l);
657 cairo_tag_begin(_cr, CAIRO_TAG_DEST, dest);
658 g_free(dest);
659#endif
660}
661
663#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
664 cairo_tag_end(_cr, CAIRO_TAG_DEST);
665#endif
666}
667
668
669void
671{
672 g_assert( _is_valid );
673
674 // here it should be checked whether the current clip winding changed
675 // so we could switch back to masked clipping
676 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
677 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
678 } else {
679 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
680 }
681 addPathVector(pv);
682}
683
684void
685CairoRenderContext::addClippingRect(double x, double y, double width, double height)
686{
687 g_assert( _is_valid );
688
690 cairo_clip(_cr);
691}
692
693bool
695{
696 // Is the surface already set up?
697 if (_is_valid)
698 return true;
699
700 if (_vector_based_target && _stream == nullptr)
701 return false;
702
703 _width = width;
704 _height = height;
705
706 cairo_surface_t *surface = nullptr;
707 cairo_matrix_t ctm;
708 cairo_matrix_init_identity (&ctm);
709 switch (_target) {
710 case CAIRO_SURFACE_TYPE_IMAGE:
711 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
712 break;
713#ifdef CAIRO_HAS_PDF_SURFACE
714 case CAIRO_SURFACE_TYPE_PDF:
715 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
716 cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
717 break;
718#endif
719#ifdef CAIRO_HAS_PS_SURFACE
720 case CAIRO_SURFACE_TYPE_PS:
721 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
722 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
723 return FALSE;
724 }
725 cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
726 cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
727 break;
728#endif
729 default:
730 return false;
731 break;
732 }
733
735
736 return _finishSurfaceSetup (surface, &ctm);
737}
738
739bool CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
740{
741 if (_is_valid || !surface)
742 return false;
743
744 _vector_based_target = is_vector;
745 bool ret = _finishSurfaceSetup(surface, ctm);
746 if (ret)
747 cairo_surface_reference(surface);
748 return ret;
749}
750
752{
753 if (const char *title = rdf_get_work_entity(&doc, rdf_find_entity("title"))) {
754 _metadata.title = title;
755 }
756
757 if (const char *author = rdf_get_work_entity(&doc, rdf_find_entity("creator"))) {
758 _metadata.author = author;
759 }
760
761 if (const char *subject = rdf_get_work_entity(&doc, rdf_find_entity("description"))) {
762 _metadata.subject = subject;
763 }
764
765 if (const char *keywords = rdf_get_work_entity(&doc, rdf_find_entity("subject"))) {
766 _metadata.keywords = keywords;
767 }
768
769 if (const char *copyright = rdf_get_work_entity(&doc, rdf_find_entity("rights"))) {
770 _metadata.copyright = copyright;
771 }
772
773 // creator
774 _metadata.creator = Glib::ustring::compose("Inkscape %1 (https://inkscape.org)",
776
777 // cdate (only used for for reproducible builds hack)
778 Glib::ustring cdate = ReproducibleBuilds::now_iso_8601();
779 if (!cdate.empty()) {
780 _metadata.cdate = cdate;
781 }
782
783 // mdate (currently unused)
784}
785
787{
788 if(surface == nullptr) {
789 return false;
790 }
791 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
792 return false;
793 }
794
795 _cr = cairo_create(surface);
796 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
797 return false;
798 }
799 if (ctm)
800 cairo_set_matrix(_cr, ctm);
802
804 cairo_scale(_cr, Inkscape::Util::Quantity::convert(1, "px", "pt"), Inkscape::Util::Quantity::convert(1, "px", "pt"));
805 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
806 // set background color on non-alpha surfaces
807 // TODO: bgcolor should be derived from SPDocument (see IconImpl)
808 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
810 cairo_fill(_cr);
811 }
812
813 _is_valid = true;
814
815 return true;
816}
817
818void
820{
821 switch (_target) {
822#if defined CAIRO_HAS_PDF_SURFACE && CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 4)
823 case CAIRO_SURFACE_TYPE_PDF:
824 if (!_metadata.title.empty()) {
825 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_TITLE, _metadata.title.c_str());
826 }
827 if (!_metadata.author.empty()) {
828 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_AUTHOR, _metadata.author.c_str());
829 }
830 if (!_metadata.subject.empty()) {
831 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_SUBJECT, _metadata.subject.c_str());
832 }
833 if (!_metadata.keywords.empty()) {
834 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_KEYWORDS, _metadata.keywords.c_str());
835 }
836 if (!_metadata.creator.empty()) {
837 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATOR, _metadata.creator.c_str());
838 }
839 if (!_metadata.cdate.empty()) {
840 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATE_DATE, _metadata.cdate.c_str());
841 }
842 if (!_metadata.mdate.empty()) {
843 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_MOD_DATE, _metadata.mdate.c_str());
844 }
845 break;
846#endif
847#if defined CAIRO_HAS_PS_SURFACE
848 case CAIRO_SURFACE_TYPE_PS:
849 if (!_metadata.title.empty()) {
850 cairo_ps_surface_dsc_comment(surface, (Glib::ustring("%%Title: ") + _metadata.title).c_str());
851 }
852 if (!_metadata.copyright.empty()) {
853 cairo_ps_surface_dsc_comment(surface, (Glib::ustring("%%Copyright: ") + _metadata.copyright).c_str());
854 }
855 break;
856#endif
857 default:
858 break;
859 }
860}
861
866{
867 g_assert(_is_valid);
869 return false;
870
871 // Protect against finish() showing one too many pages.
872 if (!_is_show_page) {
873 cairo_show_page(_cr);
874 _is_show_page = true;
875 }
876
877 auto status = cairo_status(_cr);
878 if (status != CAIRO_STATUS_SUCCESS) {
879 g_critical("error while rendering page: %s", cairo_status_to_string(status));
880 return false;
881 }
882 return true;
883}
884
888bool CairoRenderContext::nextPage(double width, double height, char const *label)
889{
890 g_assert(_is_valid);
892 return false;
893
894 _width = width;
895 _height = height;
896 _is_show_page = false;
897
898 if (_is_pdf) {
899 cairo_pdf_surface_set_size(_surface, width, height);
900
901 if (label) {
902 cairo_pdf_surface_set_page_label(_surface, label);
903 }
904 }
905 if (_is_ps) {
906 cairo_ps_surface_set_size(_surface, width, height);
907 }
908
909 auto status = cairo_surface_status(_surface);
910 if (status != CAIRO_STATUS_SUCCESS) {
911 g_critical("error while sizing page: %s", cairo_status_to_string(status));
912 return false;
913 }
914 return true;
915}
916
917bool CairoRenderContext::finish(bool finish_surface)
918{
919 g_assert(_is_valid);
920
921 if (_vector_based_target && !_is_show_page && finish_surface)
922 cairo_show_page(_cr);
923
924 cairo_status_t status = cairo_status(_cr);
925 if (status != CAIRO_STATUS_SUCCESS)
926 g_critical("error while rendering output: %s", cairo_status_to_string(status));
927
928 cairo_destroy(_cr);
929 _cr = nullptr;
930
931 if (finish_surface)
932 cairo_surface_finish(_surface);
933 status = cairo_surface_status(_surface);
934 cairo_surface_destroy(_surface);
935 _surface = nullptr;
936
937 if (_layout) {
938 g_object_unref(_layout);
939 _layout = nullptr;
940 }
941
942 _is_valid = false;
943
945 /* Flush stream to be sure. */
946 fflush(_stream);
947 fclose(_stream);
948 _stream = nullptr;
949 }
950
951 return status == CAIRO_STATUS_SUCCESS;
952}
953
955{
956 g_assert(_is_valid);
957
959
960 // store new CTM
961 _state_stack.back().transform = getTransform();
962}
963
965{
966 g_assert(_is_valid);
967 cairo_matrix_t matrix;
969 cairo_set_matrix(_cr, &matrix);
970 _state_stack.back().transform = transform;
971}
972
974{
975 g_assert(_is_valid);
976 _state_stack.back().item_transform = transform;
977}
978
980{
981 g_assert(_is_valid);
982
983 cairo_matrix_t ctm;
984 cairo_get_matrix(_cr, &ctm);
985 return ink_matrix_to_2geom(ctm);
986}
987
989{
990 auto state = getCurrentState();
991 if (state->parent_has_userspace) {
992 return getParentTransform() * state->item_transform;
993 }
994 return state->item_transform;
995}
996
1002
1004{
1005 g_assert(_is_valid);
1006
1007 cairo_save(_cr);
1008
1009 // copy current state's transform
1010 auto const current_transform = _state_stack.back().transform;
1011 _addState()->transform = current_transform;
1012}
1013
1015{
1016 g_assert(_is_valid);
1017
1018 cairo_restore(_cr);
1019 _state_stack.pop_back();
1020
1021 g_assert(!_state_stack.empty());
1022}
1023
1026{
1027 g_assert( is<SPPattern>(paintserver) );
1028
1029 SPPattern *pat = const_cast<SPPattern*>(cast<SPPattern>(paintserver));
1030
1031 Geom::Affine ps2user, pcs2dev;
1032 ps2user = Geom::identity();
1033 pcs2dev = Geom::identity();
1034
1035 double x = pat->x();
1036 double y = pat->y();
1037 double width = pat->width();
1038 double height = pat->height();
1039 double bbox_width_scaler;
1040 double bbox_height_scaler;
1041
1042 TRACE(("%f x %f pattern\n", width, height));
1043
1044 if (pbox && pat->patternUnits() == SPPattern::UNITS_OBJECTBOUNDINGBOX) {
1045 bbox_width_scaler = pbox->width();
1046 bbox_height_scaler = pbox->height();
1047 ps2user[4] = x * bbox_width_scaler + pbox->left();
1048 ps2user[5] = y * bbox_height_scaler + pbox->top();
1049 } else {
1050 bbox_width_scaler = 1.0;
1051 bbox_height_scaler = 1.0;
1052 ps2user[4] = x;
1053 ps2user[5] = y;
1054 }
1055
1056 // apply pattern transformation
1057 Geom::Affine pattern_transform(pat->getTransform());
1058 ps2user *= pattern_transform;
1059 Geom::Point ori (ps2user[4], ps2user[5]);
1060
1061 // create pattern contents coordinate system
1062 if (pat->viewBox_set) {
1063 Geom::Rect view_box = *pat->viewbox();
1064
1065 double x, y, w, h;
1066 x = 0;
1067 y = 0;
1068 w = width * bbox_width_scaler;
1069 h = height * bbox_height_scaler;
1070
1071 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1072 pcs2dev[0] = w / view_box.width();
1073 pcs2dev[3] = h / view_box.height();
1074 pcs2dev[4] = x - view_box.left() * pcs2dev[0];
1075 pcs2dev[5] = y - view_box.top() * pcs2dev[3];
1076 } else if (pbox && pat->patternContentUnits() == SPPattern::UNITS_OBJECTBOUNDINGBOX) {
1077 pcs2dev[0] = pbox->width();
1078 pcs2dev[3] = pbox->height();
1079 }
1080
1081 // Calculate the size of the surface which has to be created
1082#define SUBPIX_SCALE 100
1083 // Cairo requires an integer pattern surface width/height.
1084 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1085 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1086 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1087 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1088 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1089 // create new rendering context
1090 CairoRenderContext pattern_ctx = createSimilar(surface_width, surface_height);
1091
1092 // adjust the size of the painted pattern to fit exactly the created surface
1093 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1094 double scale_width = surface_width / (bbox_width_scaler * width);
1095 double scale_height = surface_height / (bbox_height_scaler * height);
1096 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1097 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1098 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1099 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1100 }
1101
1102 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1103 ps2user[4] = ori[Geom::X];
1104 ps2user[5] = ori[Geom::Y];
1105
1106 pattern_ctx.setTransform(pcs2dev);
1107 pattern_ctx.pushState();
1108
1109 // create drawing and group
1110 Inkscape::Drawing drawing;
1111 unsigned dkey = SPItem::display_key_new(1);
1112
1113 // show items and render them
1114 for (SPPattern *pat_i = pat; pat_i != nullptr; pat_i = pat_i->ref.getObject()) {
1115 if (pat_i && pat_i->hasItemChildren()) { // find the first one with item children
1116 for (auto& child: pat_i->children) {
1117 if (is<SPItem>(&child)) {
1118 cast<SPItem>(&child)->invoke_show(drawing, dkey, SP_ITEM_REFERENCE_FLAGS);
1119 _renderer->renderItem(&pattern_ctx, cast<SPItem>(&child));
1120 }
1121 }
1122 break; // do not go further up the chain if children are found
1123 }
1124 }
1125
1126 pattern_ctx.popState();
1127
1128 // setup a cairo_pattern_t
1129 cairo_surface_t *pattern_surface = pattern_ctx.getSurface();
1130 TEST(pattern_ctx->saveAsPng("pattern.png"));
1131 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1132 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1133
1134 // set pattern transformation
1135 cairo_matrix_t pattern_matrix;
1136 ink_matrix_to_cairo(pattern_matrix, ps2user);
1137 cairo_matrix_invert(&pattern_matrix);
1138 cairo_pattern_set_matrix(result, &pattern_matrix);
1139
1140 // hide all items
1141 for (SPPattern *pat_i = pat; pat_i != nullptr; pat_i = pat_i->ref.getObject()) {
1142 if (pat_i && pat_i->hasItemChildren()) { // find the first one with item children
1143 for (auto& child: pat_i->children) {
1144 if (is<SPItem>(&child)) {
1145 cast<SPItem>(&child)->invoke_hide(dkey);
1146 }
1147 }
1148 break; // do not go further up the chain if children are found
1149 }
1150 }
1151
1152 return result;
1153}
1154
1157 SPHatch const *hatch = cast<SPHatch>(paintserver);
1158 g_assert( hatch );
1159
1160 g_assert(hatch->pitch() > 0);
1161
1162 // create drawing and group
1163 Inkscape::Drawing drawing;
1164 unsigned dkey = SPItem::display_key_new(1);
1165
1166 // TODO need to refactor 'evil' referenced code for const correctness.
1167 SPHatch *evil = const_cast<SPHatch *>(hatch);
1168 evil->show(drawing, dkey, pbox);
1169
1170 SPHatch::RenderInfo render_info = hatch->calculateRenderInfo(dkey);
1171 Geom::Rect tile_rect = render_info.tile_rect;
1172
1173 // Cairo requires an integer pattern surface width/height.
1174 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1175 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1176 const int subpix_scale = 10;
1177 double surface_width = MAX(ceil(subpix_scale * tile_rect.width() - 0.5), 1);
1178 double surface_height = MAX(ceil(subpix_scale * tile_rect.height() - 0.5), 1);
1179 Geom::Affine drawing_scale = Geom::Scale(surface_width / tile_rect.width(), surface_height / tile_rect.height());
1180 Geom::Affine drawing_transform = Geom::Translate(-tile_rect.min()) * drawing_scale;
1181
1182 Geom::Affine child_transform = render_info.child_transform;
1183 child_transform *= drawing_transform;
1184
1185 //The rendering of hatch overflow is implemented by repeated drawing
1186 //of hatch paths over one strip. Within each iteration paths are moved by pitch value.
1187 //The movement progresses from right to left. This gives the same result
1188 //as drawing whole strips in left-to-right order.
1189 gdouble overflow_right_strip = 0.0;
1190 int overflow_steps = 1;
1191 Geom::Affine overflow_transform;
1192 if (hatch->style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) {
1193 Geom::Interval bounds = hatch->bounds();
1194 overflow_right_strip = floor(bounds.max() / hatch->pitch()) * hatch->pitch();
1195 overflow_steps = ceil((overflow_right_strip - bounds.min()) / hatch->pitch()) + 1;
1196 overflow_transform = Geom::Translate(hatch->pitch(), 0.0);
1197 }
1198
1199 CairoRenderContext pattern_ctx = createSimilar(surface_width, surface_height);
1200 pattern_ctx.setTransform(child_transform);
1201 pattern_ctx.transform(Geom::Translate(-overflow_right_strip, 0.0));
1202 pattern_ctx.pushState();
1203
1204 std::vector<SPHatchPath *> children(evil->hatchPaths());
1205
1206 for (int i = 0; i < overflow_steps; i++) {
1207 for (auto path : children) {
1208 _renderer->renderHatchPath(&pattern_ctx, *path, dkey);
1209 }
1210 pattern_ctx.transform(overflow_transform);
1211 }
1212
1213 pattern_ctx.popState();
1214
1215 // setup a cairo_pattern_t
1216 cairo_surface_t *pattern_surface = pattern_ctx.getSurface();
1217 TEST(pattern_ctx->saveAsPng("hatch.png"));
1218 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1219 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1220
1221 Geom::Affine pattern_transform;
1222 pattern_transform = render_info.pattern_to_user_transform.inverse() * drawing_transform;
1223 ink_cairo_pattern_set_matrix(result, pattern_transform);
1224
1225 evil->hide(dkey);
1226 return result;
1227}
1228
1231 Geom::OptRect const &pbox, float alpha)
1232{
1233 cairo_pattern_t *pattern = nullptr;
1234 bool apply_bbox2user = FALSE;
1235
1236 auto const paintserver_mutable = const_cast<SPPaintServer *>(paintserver);
1237
1238 if (auto lg = cast<SPLinearGradient>(paintserver_mutable)) {
1239
1240 lg->ensureVector(); // when exporting from commandline, vector is not built
1241
1242 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1243 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1244 if (pbox && lg->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1245 // convert to userspace
1246 Geom::Affine bbox2user(pbox->width(), 0, 0, pbox->height(), pbox->left(), pbox->top());
1247 p1 *= bbox2user;
1248 p2 *= bbox2user;
1249 }
1250
1251 // create linear gradient pattern
1252 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1253
1254 // add stops
1255 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1256 ink_cairo_pattern_add_color_stop(pattern, lg->vector.stops[i].offset, *lg->vector.stops[i].color, alpha);
1257 }
1258 } else if (auto rg = cast<SPRadialGradient>(paintserver_mutable)) {
1259
1260 rg->ensureVector(); // when exporting from commandline, vector is not built
1261
1262 Geom::Point c (rg->cx.computed, rg->cy.computed);
1263 Geom::Point f (rg->fx.computed, rg->fy.computed);
1264 double r = rg->r.computed;
1265 double fr = rg->fr.computed;
1266 if (pbox && rg->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1267 apply_bbox2user = true;
1268
1269 // create radial gradient pattern
1270 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], fr, c[Geom::X], c[Geom::Y], r);
1271
1272 // add stops
1273 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1274 ink_cairo_pattern_add_color_stop(pattern, rg->vector.stops[i].offset, *rg->vector.stops[i].color, alpha);
1275 }
1276 } else if (auto mg = cast<SPMeshGradient>(paintserver_mutable)) {
1277 pattern = mg->create_drawing_paintserver()->create_pattern(_cr, pbox, 1.0);
1278 } else if (is<SPPattern>(paintserver)) {
1279 pattern = _createPatternPainter(paintserver, pbox);
1280 } else if (is<SPHatch>(paintserver) ) {
1281 pattern = _createHatchPainter(paintserver, pbox);
1282 } else {
1283 return nullptr;
1284 }
1285
1286 if (pattern && is<SPGradient>(paintserver)) {
1287 auto g = cast<SPGradient>(paintserver_mutable);
1288
1289 // set extend type
1290 SPGradientSpread spread = g->fetchSpread();
1291 switch (spread) {
1293 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1294 break;
1295 }
1296 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1297 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1298 break;
1299 }
1300 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1301 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1302 break;
1303 }
1304 default: {
1305 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1306 break;
1307 }
1308 }
1309
1310 cairo_matrix_t pattern_matrix;
1311 if (g->gradientTransform_set) {
1312 // apply gradient transformation
1313 cairo_matrix_init(&pattern_matrix,
1314 g->gradientTransform[0], g->gradientTransform[1],
1315 g->gradientTransform[2], g->gradientTransform[3],
1316 g->gradientTransform[4], g->gradientTransform[5]);
1317 } else {
1318 cairo_matrix_init_identity (&pattern_matrix);
1319 }
1320
1321 if (apply_bbox2user) {
1322 // convert to userspace
1323 cairo_matrix_t bbox2user;
1324 cairo_matrix_init (&bbox2user, pbox->width(), 0, 0, pbox->height(), pbox->left(), pbox->top());
1325 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1326 }
1327 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1328 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1329 }
1330
1331 return pattern;
1332}
1333
1334float CairoRenderContext::_mergedOpacity(float source_opacity) const
1335{
1336 auto const &state = _state_stack.back();
1337 return state.merge_opacity ? source_opacity * state.opacity : source_opacity;
1338}
1339
1340void CairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect const &pbox)
1341{
1342 g_return_if_fail( !style->fill.set
1343 || style->fill.isColor()
1344 || style->fill.isContext()
1345 || style->fill.isPaintserver() );
1346
1347 float const alpha = _mergedOpacity(SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
1348 SPPaintServer const *paint_server = style->getFillPaintServer();
1349 if (style->fill.isContext()) {
1350 // Do nothing. These are valid values but if not inside a <use> or <marker> element do nothing.
1351 } else if (paint_server && paint_server->isValid()) {
1352
1353 g_assert(is<SPGradient>(SP_STYLE_FILL_SERVER(style))
1354 || is<SPPattern>(SP_STYLE_FILL_SERVER(style))
1355 || is<SPHatch>(SP_STYLE_FILL_SERVER(style)));
1356
1357 if (cairo_pattern_t *pattern = _createPatternForPaintServer(paint_server, pbox, alpha)) {
1358 cairo_set_source(_cr, pattern);
1359 cairo_pattern_destroy(pattern);
1360 }
1361 } else if (style->fill.isColor()) {
1362 ink_cairo_set_source_color(_cr, style->fill.getColor(), alpha);
1363 } else { // unset fill is black
1364 g_assert(!style->fill.set
1365 || (paint_server && !paint_server->isValid()));
1366
1367 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1368 }
1369}
1370
1372{
1373 float const alpha = _mergedOpacity(SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
1374 if (style->stroke.isContext()) {
1375 // Do nothing. These are valid values but if not inside a <use> or <marker> element do nothing.
1376 } else if (style->stroke.isColor() || (style->stroke.isPaintserver() && !style->getStrokePaintServer()->isValid())) {
1377 ink_cairo_set_source_color(_cr, style->stroke.getColor(), alpha);
1378 } else {
1379 g_assert( style->stroke.isPaintserver()
1380 || is<SPGradient>(SP_STYLE_STROKE_SERVER(style))
1381 || is<SPPattern>(SP_STYLE_STROKE_SERVER(style))
1382 || cast<SPHatch>(SP_STYLE_STROKE_SERVER(style)));
1383
1384 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1385
1386 if (pattern) {
1387 cairo_set_source(_cr, pattern);
1388 cairo_pattern_destroy(pattern);
1389 }
1390 }
1391
1392 if (!style->stroke_dasharray.values.empty() && style->stroke_dasharray.is_valid())
1393 {
1394 auto const &dash_values = style->stroke_dasharray.values;
1395 size_t num_dashes = dash_values.size();
1396 std::vector<double> dashes;
1397
1398 dashes.reserve(num_dashes);
1399 std::transform(dash_values.begin(), dash_values.end(), std::back_inserter(dashes),
1400 [](SPILength const &dash_length) -> double { return dash_length.value; });
1401
1402 cairo_set_dash(_cr, dashes.data(), num_dashes, style->stroke_dashoffset.value);
1403 } else {
1404 cairo_set_dash(_cr, nullptr, 0, 0.0); // disable dashing
1405 }
1406
1407 // This allows hairlines to be drawn properly in PDF, PS, Win32-Print, etc.
1408 // It requires the following pull request in Cairo:
1409 // https://gitlab.freedesktop.org/cairo/cairo/merge_requests/21
1410 if (style->stroke_extensions.hairline) {
1412 } else {
1413 cairo_set_line_width(_cr, style->stroke_width.computed);
1414 }
1415
1416 // set line join type
1417 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1418 switch (style->stroke_linejoin.computed) {
1420 join = CAIRO_LINE_JOIN_MITER;
1421 break;
1423 join = CAIRO_LINE_JOIN_ROUND;
1424 break;
1426 join = CAIRO_LINE_JOIN_BEVEL;
1427 break;
1428 }
1429 cairo_set_line_join(_cr, join);
1430
1431 // set line cap type
1432 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1433 switch (style->stroke_linecap.computed) {
1435 cap = CAIRO_LINE_CAP_BUTT;
1436 break;
1438 cap = CAIRO_LINE_CAP_ROUND;
1439 break;
1441 cap = CAIRO_LINE_CAP_SQUARE;
1442 break;
1443 }
1444 cairo_set_line_cap(_cr, cap);
1445 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1446}
1447
1449{
1450 // Only PDFLaTeX supports importing a single page of a graphics file,
1451 // so only PDF backend gets interleaved text/graphics
1453 && _target == CAIRO_SURFACE_TYPE_PDF && _render_mode != RENDER_MODE_CLIP)
1454 {
1455 // better set this immediately (not sure if masks applied during "popLayer" could call
1456 // this function, too, triggering the same code again in error
1458
1459 // As we can not emit the page in the middle of a layer (aka group) - it will not be fully painted yet! -
1460 // the following basically mirrors the calls in CairoRenderer::renderItem (but in reversed order)
1461 // - first traverse all saved states in reversed order (i.e. from deepest nesting to the top)
1462 // and apply clipping / masking to layers on the way (this is done in popLayer)
1463 // - then emit the page using cairo_show_page()
1464 // - finally restore the previous state with proper transforms and appropriate layers again
1465 //
1466 // TODO: While this appears to be an ugly hack it seems to work
1467 // Somebody with a more intimate understanding of cairo and the renderer implementation might
1468 // be able to implement this in a cleaner way, though.
1469 auto const original_stack = _state_stack;
1470
1471 while (_state_stack.size() > 1) {
1472 if (getCurrentState()->need_layer)
1473 popLayer();
1474 cairo_restore(_cr);
1475 _state_stack.pop_back();
1476 }
1477
1478 g_assert(_state_stack.size() == 1);
1479 cairo_show_page(_cr);
1480
1481 for (int i = 1; i < original_stack.size(); i++) {
1482 _state_stack.push_back(original_stack[i]);
1483 cairo_save(_cr);
1484 if (getCurrentState()->need_layer)
1485 pushLayer();
1487 }
1488 }
1490}
1491
1493{
1494 // Only PDFLaTeX supports importing a single page of a graphics file,
1495 // so only PDF backend gets interleaved text/graphics
1496 if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF &&_omittext_state == OmitTextPageState::GRAPHIC_ON_TOP) {
1498 }
1499}
1500
1501/* We need CairoPaintOrder as markers are rendered in a separate step and may be rendered
1502 * in between fill and stroke.
1503 */
1505 SPStyle const *style,
1506 Geom::OptRect const &pbox,
1508{
1509 g_assert( _is_valid );
1510
1512
1514 if (_clip_mode == CLIP_MODE_PATH) {
1515 addClipPath(pathv, &style->fill_rule);
1516 } else {
1517 setPathVector(pathv);
1518 if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
1519 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1520 } else {
1521 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1522 }
1523 if (style->mix_blend_mode.set && style->mix_blend_mode.value) {
1524 cairo_set_operator(_cr, ink_css_blend_to_cairo_operator(style->mix_blend_mode.value));
1525 }
1526 cairo_fill(_cr);
1527 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1528 }
1529 return true;
1530 }
1531
1532 bool no_fill =
1533 style->fill.isNone() ||
1534 style->fill.isContext() ||
1535 style->fill_opacity.value == 0 ||
1536 order == STROKE_ONLY;
1537 bool no_stroke =
1538 style->stroke.isNone() ||
1539 style->stroke.isContext() ||
1540 (!style->stroke_extensions.hairline && style->stroke_width.computed < 1e-9) ||
1541 style->stroke_opacity.value == 0 ||
1542 order == FILL_ONLY;
1543
1544 if (no_fill && no_stroke)
1545 return true;
1546
1547 auto const &state = _state_stack.back();
1548 bool need_layer = !state.merge_opacity && !state.need_layer &&
1549 (state.opacity != 1.0 || state.clip_path != nullptr || state.mask != nullptr);
1550 bool blend = false;
1551 if (style->mix_blend_mode.set && style->mix_blend_mode.value != SP_CSS_BLEND_NORMAL) {
1552 need_layer = true;
1553 blend = true;
1554 }
1555 if (!need_layer)
1556 cairo_save(_cr);
1557 else
1558 pushLayer();
1559
1560 if (!no_fill) {
1561 if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
1562 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1563 } else {
1564 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1565 }
1566 }
1567
1568 setPathVector(pathv);
1569
1570 if (!no_fill && (order == STROKE_OVER_FILL || order == FILL_ONLY)) {
1571 _setFillStyle(style, pbox);
1572
1573 if (no_stroke)
1574 cairo_fill(_cr);
1575 else
1576 cairo_fill_preserve(_cr);
1577 }
1578
1579 if (!no_stroke) {
1580 _setStrokeStyle(style, pbox);
1581
1582 if (no_fill || order == STROKE_OVER_FILL)
1583 cairo_stroke(_cr);
1584 else
1585 cairo_stroke_preserve(_cr);
1586 }
1587
1588 if (!no_fill && order == FILL_OVER_STROKE) {
1589 _setFillStyle(style, pbox);
1590 cairo_fill(_cr);
1591 }
1592
1593 if (need_layer) {
1594 if (blend) {
1596 } else {
1597 popLayer();
1598 }
1599 } else {
1600 cairo_restore(_cr);
1601 }
1602
1603 return true;
1604}
1605
1607 Geom::Affine const &image_transform, SPStyle const *style)
1608{
1609 g_assert( _is_valid );
1610
1612 return true;
1613 }
1614
1616
1617 int w = pb->width();
1618 int h = pb->height();
1619
1620 // TODO: reenable merge_opacity if useful
1621
1622 cairo_surface_t const *image_surface = pb->getSurfaceRaw();
1623 if (cairo_surface_status(const_cast<cairo_surface_t*>(image_surface))) { // cairo_surface_status does not modify argument
1624 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1625 return false;
1626 }
1627
1628 cairo_save(_cr);
1629
1630 // scaling by width & height is not needed because it will be done by Cairo
1631 transform(image_transform);
1632
1633 // cairo_set_source_surface only modifies refcount of 'image_surface', which is an implementation detail
1634 cairo_set_source_surface(_cr, const_cast<cairo_surface_t*>(image_surface), 0.0, 0.0);
1635
1636 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1638 cairo_new_path(_cr);
1639 cairo_rectangle(_cr, 0, 0, w, h);
1640 cairo_clip(_cr);
1641 }
1642
1643 // Cairo filter method will be mapped to PS/PDF 'interpolate' true/false).
1644 // See cairo-pdf-surface.c
1645 if (style) {
1646 // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty
1647 // https://drafts.csswg.org/css-images-3/#the-image-rendering
1648 // style.h/style.cpp, drawing-image.cpp
1649 //
1650 // CSS 3 defines:
1651 // 'optimizeSpeed' as alias for "pixelated"
1652 // 'optimizeQuality' as alias for "smooth"
1653 switch (style->image_rendering.computed) {
1656 // we don't have an implementation for crisp-edges, but it should *not* smooth or blur
1658 cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_NEAREST);
1659 break;
1662 default:
1663 cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_BEST);
1664 break;
1665 }
1666 }
1667
1668 if (style->mix_blend_mode.set && style->mix_blend_mode.value) {
1669 cairo_set_operator(_cr, ink_css_blend_to_cairo_operator(style->mix_blend_mode.value));
1670 }
1671
1672 cairo_paint(_cr);
1673
1674 cairo_restore(_cr);
1675 return true;
1676}
1677
1678#define GLYPH_ARRAY_SIZE 64
1679
1680// TODO investigate why the font is being ignored:
1682 PangoFont * /*font*/,
1683 std::vector<CairoGlyphInfo> const &glyphtext,
1684 bool path)
1685{
1686 std::vector<cairo_glyph_t> glyphs;
1687 glyphs.reserve(glyphtext.size());
1688
1689 for (const auto &it_info : glyphtext) {
1690 // skip glyphs which are PANGO_GLYPH_EMPTY or have the PANGO_GLYPH_UNKNOWN_FLAG set
1691 if (it_info.index == PANGO_GLYPH_EMPTY || it_info.index & PANGO_GLYPH_UNKNOWN_FLAG) {
1692 TRACE(("INVALID GLYPH found\n"));
1693 g_message("Invalid glyph found, continuing...");
1694 continue;
1695 }
1696 glyphs.push_back({
1697 .index = it_info.index,
1698 .x = it_info.x,
1699 .y = it_info.y
1700 });
1701 }
1702
1703 const unsigned num_valid_glyphs = glyphs.size();
1704 if (path) {
1705 cairo_glyph_path(cr, glyphs.data(), num_valid_glyphs);
1706 } else {
1707 cairo_show_glyphs(cr, glyphs.data(), num_valid_glyphs);
1708 }
1709
1710 return num_valid_glyphs;
1711}
1712
1725bool
1727 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style,
1728 bool second_pass)
1729{
1731 if (_is_omittext)
1732 return false;
1733
1734 gpointer fonthash = (gpointer)font;
1735 cairo_font_face_t *font_face = nullptr;
1736 if (auto const it = _font_table.find(fonthash); it != _font_table.end()) {
1737 font_face = it->second;
1738 }
1739
1740 FcPattern *fc_pattern = nullptr;
1741
1742# ifdef CAIRO_HAS_FT_FONT
1743 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1744 fc_pattern = fc_font->font_pattern;
1745 if (font_face == nullptr) {
1746 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1747 _font_table[fonthash] = font_face;
1748 }
1749# endif
1750
1751 cairo_save(_cr);
1752 cairo_set_font_face(_cr, font_face);
1753
1754 // set the given font matrix
1755 cairo_matrix_t matrix;
1756 ink_matrix_to_cairo(matrix, font_matrix);
1757 cairo_set_font_matrix(_cr, &matrix);
1758
1760 if (_clip_mode == CLIP_MODE_MASK) {
1761 if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
1762 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1763 } else {
1764 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1765 }
1766 _showGlyphs(_cr, font, glyphtext, FALSE);
1767 } else {
1768 // just add the glyph paths to the current context
1769 _showGlyphs(_cr, font, glyphtext, TRUE);
1770 }
1771 cairo_restore(_cr);
1772 return false;
1773 }
1774
1775 if (style->mix_blend_mode.set && style->mix_blend_mode.value) {
1776 cairo_set_operator(_cr, ink_css_blend_to_cairo_operator(style->mix_blend_mode.value));
1777 }
1778
1779 bool fill = style->fill.isColor() || style->fill.isPaintserver();
1780 bool stroke = style->stroke.isColor() || style->stroke.isPaintserver();
1781 if (!fill && !stroke) {
1782 cairo_restore(_cr);
1783 return false;
1784 }
1785
1786 // Text never has markers, and no-fill doesn't matter.
1787 bool stroke_over_fill = style->paint_order.get_order(SP_CSS_PAINT_ORDER_STROKE)
1788 > style->paint_order.get_order(SP_CSS_PAINT_ORDER_FILL)
1789 || !fill || !stroke;
1790
1791 bool fill_pass = fill && stroke_over_fill != second_pass;
1792 bool stroke_pass = stroke && !second_pass;
1793
1794 if (fill_pass) {
1795 _setFillStyle(style, Geom::OptRect());
1796 _showGlyphs(_cr, font, glyphtext, _is_texttopath);
1797 if (_is_texttopath)
1798 cairo_fill_preserve(_cr);
1799 }
1800
1801 // Stroke paths are generated for texttopath AND glyph output
1802 // because PDF text output doesn't support stroke and fill
1803 if (stroke_pass) {
1804 // And now we don't have a path to stroke, so make one.
1805 if (!_is_texttopath || !fill_pass)
1806 _showGlyphs(_cr, font, glyphtext, true);
1808 cairo_stroke(_cr);
1809 }
1810
1811 cairo_restore(_cr);
1812 return !stroke_over_fill && !second_pass;
1813}
1814
1815/* Helper functions */
1816
1818{
1819 cairo_new_path(_cr);
1820 addPathVector(pv);
1821}
1822
1827
1828static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length)
1829{
1830 size_t const written = fwrite(data, 1, length, (FILE*)closure);
1831 return (written == length) ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
1832}
1833
1834#include "clear-n_.h"
1835
1836} /* namespace Internal */
1837} /* namespace Extension */
1838} /* namespace Inkscape */
1839
1840#undef TRACE
1841#undef TEST
1842
1843
1844/*
1845 Local Variables:
1846 mode:c++
1847 c-file-style:"stroustrup"
1848 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1849 indent-tabs-mode:nil
1850 fill-column:99
1851 End:
1852*/
1853// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
TEST(AngleIntervalTest, InnerAngleConstrutor)
Declaration of CairoRenderContext, a class used for rendering with Cairo.
struct _PangoFont PangoFont
Declaration of CairoRenderer, a class used for rendering via a CairoRenderContext.
void ink_cairo_set_source_color(Cairo::RefPtr< Cairo::Context > ctx, Inkscape::Colors::Color const &color, double opacity)
The following functions interact between Inkscape color model, and cairo surface rendering.
void ink_cairo_transform(cairo_t *ct, Geom::Affine const &m)
void ink_cairo_pattern_add_color_stop(cairo_pattern_t *ptn, double offset, Inkscape::Colors::Color const &color, double opacity)
void ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m)
void feed_pathvector_to_cairo(cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width)
Feeds path-creating calls to the cairo context translating them from the PathVector,...
void ink_cairo_set_hairline(cairo_t *ct)
void ink_matrix_to_cairo(cairo_matrix_t &cm, Geom::Affine const &m)
void ink_matrix_to_2geom(Geom::Affine &m, cairo_matrix_t const &cm)
cairo_operator_t ink_css_blend_to_cairo_operator(SPBlendMode css_blend)
Cairo integration helpers.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition canvas.cpp:137
Geom::IntRect bounds
Definition canvas.cpp:182
float qn[SIZE+1]
Definition cielab.cpp:47
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
C top() const
Return top coordinate of the rectangle (+Y is downwards).
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Range of real numbers that is never empty.
Definition interval.h:59
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
bool _finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm=nullptr)
CairoRenderContext(CairoRenderContext const &other)=delete
cairo_pattern_t * _createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox)
unsigned int _showGlyphs(cairo_t *cr, PangoFont *font, std::vector< CairoGlyphInfo > const &glyphtext, bool is_stroke)
void addClipPath(Geom::PathVector const &pv, SPIEnum< SPWindRule > const *fill_rule)
bool saveAsPng(const char *file_name)
Saves the contents of the context to a PNG file.
struct Inkscape::Extension::Internal::CairoRenderContext::CairoRenderContextMetadata _metadata
void setMetadata(SPDocument const &document)
Extract metadata from the document and store it in the context.
std::map< gpointer, cairo_font_face_t * > _font_table
cairo_pattern_t * _createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha)
bool renderPathVector(Geom::PathVector const &pathv, SPStyle const *style, Geom::OptRect const &pbox, CairoPaintOrder order=STROKE_OVER_FILL)
bool renderGlyphtext(PangoFont *font, Geom::Affine const &font_matrix, std::vector< CairoGlyphInfo > const &glyphtext, SPStyle const *style, bool second_pass=false)
Called by Layout-TNG-Output, this function decides how to apply styles and write out the final shapes...
bool renderImage(Inkscape::Pixbuf const *pb, Geom::Affine const &image_transform, SPStyle const *style)
CairoRenderContext createSimilar(double width, double height) const
Creates a new render context which will be compatible with the given context's Cairo surface.
CairoRenderContext & operator=(CairoRenderContext const &other)=delete
bool setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm=nullptr)
Set the cairo_surface_t from an external source.
bool nextPage(double width, double height, char const *label)
When writing multiple pages, resize the next page.
void _setStrokeStyle(SPStyle const *style, Geom::OptRect const &pbox)
bool finishPage()
Each page that's made should call finishPage to complete it.
bool setupSurface(double width, double height)
Creates the cairo_surface_t for the context with the given width, height and with the currently set t...
void addClippingRect(double x, double y, double width, double height)
cairo_pattern_t * _createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox)
void popLayer(cairo_operator_t composite=CAIRO_OPERATOR_CLEAR)
void _setFillStyle(SPStyle const *style, Geom::OptRect const &pbox)
void renderItem(CairoRenderContext *ctx, SPItem const *item, SPItem const *origin=nullptr, SPPage const *page=nullptr)
Traverses the object tree and invokes the render methods.
void applyMask(CairoRenderContext *ctx, SPMask const *mask)
void renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key)
void applyClipPath(CairoRenderContext *ctx, SPClipPath const *cp)
Class to hold image data for raster images.
Definition cairo-utils.h:31
int width() const
cairo_surface_t * getSurfaceRaw()
Converts the pixbuf to Cairo pixel format and returns an image surface which can be used as a source.
int height() const
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:525
Typed SVG document implementation.
Definition document.h:103
RenderInfo calculateRenderInfo(unsigned key) const
Definition sp-hatch.cpp:613
Inkscape::DrawingPattern * show(Inkscape::Drawing &drawing, unsigned key, Geom::OptRect const &bbox) override
Definition sp-hatch.cpp:557
void hide(unsigned key) override
Definition sp-hatch.cpp:578
std::vector< SPHatchPath * > hatchPaths()
Definition sp-hatch.cpp:239
Geom::Interval bounds() const
Definition sp-hatch.cpp:598
gdouble pitch() const
Definition sp-hatch.cpp:447
Enum type internal to SPStyle.
Length type internal to SPStyle.
Base class for visual SVG elements.
Definition sp-item.h:109
SPMask * getMaskObject() const
Definition sp-item.cpp:177
Geom::Affine transform
Definition sp-item.h:138
static unsigned int display_key_new(unsigned numkeys)
Allocates unique integer keys.
Definition sp-item.cpp:1253
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
virtual bool isValid() const
SPPattern * getObject() const
SPPattern::PatternUnits patternUnits() const
double width() const
SPPattern::PatternUnits patternContentUnits() const
double x() const
double y() const
Geom::OptRect viewbox() const
double height() const
Geom::Affine const & getTransform() const
@ UNITS_OBJECTBOUNDINGBOX
Definition sp-pattern.h:57
SPPatternReference ref
Definition sp-pattern.h:66
An SVG style object.
Definition style.h:45
SPPaintServer * getFillPaintServer()
Definition style.h:339
T< SPAttr::FILL, SPIPaint > fill
fill
Definition style.h:240
T< SPAttr::STROKE_DASHARRAY, SPIDashArray > stroke_dasharray
stroke-dasharray
Definition style.h:257
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
SPPaintServer * getStrokePaintServer()
Definition style.h:343
T< SPAttr::PAINT_ORDER, SPIPaintOrder > paint_order
Definition style.h:222
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
T< SPAttr::FILL_RULE, SPIEnum< SPWindRule > > fill_rule
fill-rule: 0 nonzero, 1 evenodd
Definition style.h:244
T< SPAttr::FILL_OPACITY, SPIScale24 > fill_opacity
fill-opacity
Definition style.h:242
T< SPAttr::STROKE_LINEJOIN, SPIEnum< SPStrokeJoinType > > stroke_linejoin
stroke-linejoin
Definition style.h:253
T< SPAttr::STROKE_OPACITY, SPIScale24 > stroke_opacity
stroke-opacity
Definition style.h:261
T< SPAttr::STROKE_MITERLIMIT, SPIFloat > stroke_miterlimit
stroke-miterlimit
Definition style.h:255
T< SPAttr::MIX_BLEND_MODE, SPIEnum< SPBlendMode > > mix_blend_mode
Definition style.h:220
T< SPAttr::OPACITY, SPIScale24 > opacity
opacity
Definition style.h:216
T< SPAttr::FILTER, SPIFilter > filter
Filter effect.
Definition style.h:275
T< SPAttr::STROKE_LINECAP, SPIEnum< SPStrokeCapType > > stroke_linecap
stroke-linecap
Definition style.h:251
T< SPAttr::IMAGE_RENDERING, SPIEnum< SPImageRendering > > image_rendering
Definition style.h:292
T< SPAttr::STROKE_DASHOFFSET, SPILength > stroke_dashoffset
stroke-dashoffset
Definition style.h:259
T< SPAttr::STROKE_EXTENSIONS, SPIStrokeExtensions > stroke_extensions
-inkscape-stroke
Definition style.h:263
T< SPAttr::OVERFLOW_, SPIEnum< SPOverflow > > overflow
overflow
Definition style.h:210
bool viewBox_set
Definition viewbox.h:34
A way to clear the N_ macro, which is defined as an inline function.
const double w
Definition conic-4.cpp:19
Css & result
double c[8][4]
const unsigned order
static char const *const parent
Definition dir-util.cpp:70
unsigned int guint32
Representation of paint servers used when rendering.
_cairo_pattern cairo_pattern_t
struct _cairo_surface cairo_surface_t
SVG drawing for display.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
auto floor(Geom::Rect const &rect)
Definition geom.h:130
SPItem * item
Mini static library that contains the version of Inkscape.
Glib::ustring label
Affine identity()
Create an identity matrix.
Definition affine.h:210
static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length)
void dump_fopen_call(char const *utf8name, char const *id)
Definition sys.cpp:37
FILE * fopen_utf8name(char const *utf8name, char const *mode)
Open a file with g_fopen().
Definition sys.cpp:72
Helper class to stream background task notifications as a series of messages.
static Glib::ustring join(std::vector< Glib::ustring > const &accels, char const separator)
char const * version_string_without_revision
version string excluding revision and date
Glib::ustring now_iso_8601()
like ReproducibleBuilds::now() but returns a ISO 8601 formatted string
int mode
void cairo_rectangle(cairo_t *cr, Geom::Rect const &r)
struct _cairo cairo_t
Definition path-cairo.h:16
PathVector - a sequence of subpaths.
int stride
Ocnode * child[8]
Definition quantize.cpp:33
struct rdf_work_entity_t * rdf_find_entity(gchar const *name)
Retrieves a known RDF/Work entity by name.
Definition rdf.cpp:364
const gchar * rdf_get_work_entity(SPDocument const *doc, struct rdf_work_entity_t *entity)
Retrieves a known RDF/Work entity's contents from the document XML by name.
Definition rdf.cpp:885
headers for RDF types
Functions to parse the "SOURCE_DATE_EPOCH" environment variable for reproducible build hacks,...
TODO: insert short description here.
SPGradientSpread
@ SP_GRADIENT_SPREAD_PAD
@ SP_GRADIENT_SPREAD_REPEAT
@ SP_GRADIENT_SPREAD_REFLECT
@ SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX
SVG <hatch> implementation.
SVG <image> implementation.
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
TODO: insert short description here.
TODO: insert short description here.
SVG <pattern> implementation.
TODO: insert short description here.
static const Point data[]
Geom::Rect tile_rect
Definition sp-hatch.h:53
Geom::Affine child_transform
Definition sp-hatch.h:51
Geom::Affine pattern_to_user_transform
Definition sp-hatch.h:52
@ SP_WIND_RULE_EVENODD
Definition style-enums.h:26
@ SP_CSS_OVERFLOW_VISIBLE
@ SP_CSS_BLEND_NORMAL
@ SP_STROKE_LINEJOIN_MITER
Definition style-enums.h:34
@ SP_STROKE_LINEJOIN_BEVEL
Definition style-enums.h:36
@ SP_STROKE_LINEJOIN_ROUND
Definition style-enums.h:35
@ SP_CSS_IMAGE_RENDERING_PIXELATED
@ SP_CSS_IMAGE_RENDERING_OPTIMIZEQUALITY
@ SP_CSS_IMAGE_RENDERING_AUTO
@ SP_CSS_IMAGE_RENDERING_OPTIMIZESPEED
@ SP_CSS_IMAGE_RENDERING_CRISPEDGES
@ SP_STROKE_LINECAP_SQUARE
Definition style-enums.h:43
@ SP_STROKE_LINECAP_ROUND
Definition style-enums.h:42
@ SP_STROKE_LINECAP_BUTT
Definition style-enums.h:41
@ SP_CSS_PAINT_ORDER_STROKE
@ SP_CSS_PAINT_ORDER_FILL
double height
double width
void cairo_set_source_rgba(cairo_t *cr, colour c)