Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
desktop-style.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Desktop style management
4 *
5 * Authors:
6 * bulia byak
7 * verbalshadow
8 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2004, 2006 authors
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include <string>
17#include <cstring>
18
19#include <glibmm.h>
20
21#include "desktop-style.h"
22
23#include "colors/color.h"
24#include "colors/color-set.h"
25#include "desktop.h"
26#include "filter-chemistry.h"
27#include "inkscape.h"
28#include "selection.h"
29#include "message-context.h"
30#include "message-stack.h"
31
32
33#include "object/box3d-side.h"
36#include "object/sp-flowdiv.h"
38#include "object/sp-flowtext.h"
39#include "object/sp-hatch.h"
41#include "object/sp-path.h"
42#include "object/sp-pattern.h"
44#include "object/sp-textpath.h"
45#include "object/sp-tref.h"
46#include "object/sp-tspan.h"
47#include "object/sp-use.h"
48#include "style.h"
49
51#include "svg/svg.h"
52
53#include "ui/tools/tool-base.h"
54
55#include <glibmm/i18n.h>
56#include "xml/sp-css-attr.h"
58
59static bool isTextualItem(SPObject const *obj)
60{
61 return is<SPText>(obj)
62 || is<SPFlowtext>(obj)
63 || is<SPTSpan>(obj)
64 || is<SPTRef>(obj)
65 || is<SPTextPath>(obj)
66 || is<SPFlowdiv>(obj)
67 || is<SPFlowpara>(obj)
68 || is<SPFlowtspan>(obj);
69}
70
74void
75sp_desktop_set_color(SPDesktop *desktop, Color const &color, bool is_relative, bool fill)
76{
78 if (is_relative) {
79 g_warning("FIXME: relative color setting not yet implemented");
80 return;
81 }
82
84 sp_repr_css_set_property_string(css, fill ? "fill" : "stroke", color.toString(false));
85 sp_repr_css_set_property_double(css, fill ? "fill-opacity" : "stroke-opacity", color.getOpacity());
87
89}
90
94void
96{
97 // non-items should not have style
98 auto item = cast<SPItem>(o);
99 if (!item) {
100 return;
101 }
102
103 // 1. tspans with role=line are not regular objects in that they are not supposed to have style of their own,
104 // but must always inherit from the parent text. Same for textPath.
105 // However, if the line tspan or textPath contains some style (old file?), we reluctantly set our style to it too.
106
107 // 2. Generally we allow setting style on clones, but when it's inside flowRegion, do not touch
108 // it, be it clone or not; it's just styleless shape (because that's how Inkscape does
109 // flowtext).
110
111 auto tspan = cast<SPTSpan>(o);
112
113 if (!(skip_lines
114 && ((tspan && tspan->role == SP_TSPAN_ROLE_LINE)
115 || is<SPFlowdiv>(o)
116 || is<SPFlowpara>(o)
117 || is<SPTextPath>(o))
118 && !o->getAttribute("style"))
119 &&
120 !(is<SPFlowregionbreak>(o) ||
121 is<SPFlowregionExclude>(o) ||
122 (is<SPUse>(o) &&
123 o->parent &&
124 (is<SPFlowregion>(o->parent) ||
125 is<SPFlowregionExclude>(o->parent)
126 )
127 )
128 )
129 ) {
130
131 SPCSSAttr *css_set = sp_repr_css_attr_new();
132 sp_repr_css_merge(css_set, css);
133
134 // Scale the style by the inverse of the accumulated parent transform in the paste context.
135 {
136 Geom::Affine const local(item->i2doc_affine());
137 double const ex(local.descrim());
138 if ( ( ex != 0. )
139 && ( ex != 1. ) ) {
140 sp_css_attr_scale(css_set, 1/ex);
141 }
142 }
143
144 o->changeCSS(css_set,"style");
145
146 sp_repr_css_attr_unref(css_set);
147 }
148
149 // setting style on child of clone spills into the clone original (via shared repr), don't do it!
150 if (is<SPUse>(o)) {
151 return;
152 }
153
154 for (auto& child: o->children) {
155 if (sp_repr_css_property(css, "opacity", nullptr) != nullptr) {
156 // Unset properties which are accumulating and thus should not be set recursively.
157 // For example, setting opacity 0.5 on a group recursively would result in the visible opacity of 0.25 for an item in the group.
158 SPCSSAttr *css_recurse = sp_repr_css_attr_new();
159 sp_repr_css_merge(css_recurse, css);
160 sp_repr_css_set_property(css_recurse, "opacity", nullptr);
161 sp_desktop_apply_css_recursive(&child, css_recurse, skip_lines);
162 sp_repr_css_attr_unref(css_recurse);
163 } else {
165 }
166 }
167}
168
173void sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write_current, bool switch_style)
174{
175 return sp_desktop_set_style(desktop->getSelection(), desktop, css, change, write_current, switch_style);
176}
177
178void
179sp_desktop_set_style(Inkscape::ObjectSet *set, SPDesktop *desktop, SPCSSAttr *css, bool change, bool write_current, bool switch_style)
180{
181 if (write_current) {
183 // 1. Set internal value
185
186 // 1a. Write to prefs; make a copy and unset any URIs first
187 SPCSSAttr *css_write = sp_repr_css_attr_new();
188 sp_repr_css_merge(css_write, css);
189 sp_css_attr_unset_uris(css_write);
190 prefs->mergeStyle("/desktop/style", css_write);
191 auto itemlist = set->items();
192 for (auto i = itemlist.begin(); i!= itemlist.end(); ++i) {
193 /* last used styles for 3D box faces are stored separately */
194 SPObject *obj = *i;
195 auto side = cast<Box3DSide>(obj);
196 if (side) {
197 prefs->mergeStyle(
198 Glib::ustring("/desktop/") + side->axes_string() + "/style", css_write);
199 }
200 }
201 sp_repr_css_attr_unref(css_write);
202 }
203
204 if (!change)
205 return;
206
207// 2. Emit signal... See desktop->connectSetStyle in text-tool, tweak-tool, and gradient-drag.
208 bool intercepted = desktop->_set_style_signal.emit(css, switch_style);
209
216// 3. If nobody has intercepted the signal, apply the style to the selection
217 if (!intercepted) {
218 // If we have an event context, update its cursor (TODO: it could be neater to do this with the signal sent above, but what if the signal gets intercepted?)
219 if (auto const tool = desktop->getTool()) {
220 tool->use_tool_cursor();
221 }
222
223 // Remove text attributes if not text...
224 // Do this once in case a zillion objects are selected.
225 SPCSSAttr *css_no_text = sp_repr_css_attr_new();
226 sp_repr_css_merge(css_no_text, css);
227 css_no_text = sp_css_attr_unset_text(css_no_text);
228
229 auto itemlist = set->items();
230 for (auto i = itemlist.begin(); i!= itemlist.end(); ++i) {
231 SPItem *item = *i;
232
233 // If not text, don't apply text attributes (can a group have text attributes? Yes! FIXME)
234 if (isTextualItem(item)) {
235 // If any font property has changed, then we have written out the font
236 // properties in longhand and we need to remove the 'font' shorthand.
237 if (!sp_repr_css_property_is_unset(css, "font-family")) {
239 }
241 } else {
242 sp_desktop_apply_css_recursive(item, css_no_text, true);
243 }
244 }
245 sp_repr_css_attr_unref(css_no_text);
246 }
247}
248
252SPCSSAttr *
254{
257 const auto & l = css->attributeList();
258 if (l.empty()) {
260 return nullptr;
261 } else {
262 if (!with_text) {
264 }
265 return css;
266 }
267}
268
272std::optional<Color>
274{
275 gchar const *property = sp_repr_css_property(desktop->current,
276 is_fill ? "fill" : "stroke",
277 "#000");
278
279 // if there is style and the property in it,
280 return Color::parse(desktop->current ? property : nullptr);
281}
282
283double
284sp_desktop_get_master_opacity_tool(SPDesktop *desktop, Glib::ustring const &tool, bool *has_opacity)
285{
287 SPCSSAttr *css = nullptr;
288 gfloat value = 1.0; // default if nothing else found
289 if (has_opacity)
290 *has_opacity = false;
291 if (prefs->getBool(tool + "/usecurrent")) {
293 } else {
294 css = prefs->getStyle(tool + "/style");
295 }
296
297 if (css) {
298 gchar const *property = css ? sp_repr_css_property(css, "opacity", "1.000") : nullptr;
299
300 if (desktop->current && property) { // if there is style and the property in it,
301 if ( !sp_svg_number_read_f(property, &value) ) {
302 value = 1.0; // things failed. set back to the default
303 } else {
304 if (has_opacity)
305 *has_opacity = true;
306 }
307 }
308
310 }
311
312 return value;
313}
314double
315sp_desktop_get_opacity_tool(SPDesktop *desktop, Glib::ustring const &tool, bool is_fill)
316{
318 SPCSSAttr *css = nullptr;
319 gfloat value = 1.0; // default if nothing else found
320 if (prefs->getBool(tool + "/usecurrent")) {
322 } else {
323 css = prefs->getStyle(tool + "/style");
324 }
325
326 if (css) {
327 gchar const *property = css ? sp_repr_css_property(css, is_fill ? "fill-opacity": "stroke-opacity", "1.000") : nullptr;
328
329 if (desktop->current && property) { // if there is style and the property in it,
330 if ( !sp_svg_number_read_f(property, &value) ) {
331 value = 1.0; // things failed. set back to the default
332 }
333 }
334
336 }
337
338 return value;
339}
340
341std::optional<Color>
342sp_desktop_get_color_tool(SPDesktop *desktop, Glib::ustring const &tool, bool is_fill)
343{
345 SPCSSAttr *css = nullptr;
346 bool styleFromCurrent = prefs->getBool(tool + "/usecurrent");
347 if (styleFromCurrent) {
349 } else {
350 css = prefs->getStyle(tool + "/style");
352 }
353
354 std::optional<Color> ret;
355 if (css) {
356 gchar const *property = sp_repr_css_property(css, is_fill ? "fill" : "stroke", "#000");
357 // if there is style and the property in it,
358 ret = Color::parse(desktop->current ? property : nullptr);
360 }
361 return ret;
362}
363
367void
368sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
369{
370 SPCSSAttr *css_current = sp_desktop_get_style(desktop, with_text);
372
373 if (prefs->getBool(tool_path + "/usecurrent") && css_current) {
374 sp_repr_css_unset_property(css_current, "shape-inside");
375 sp_repr_css_unset_property(css_current, "mix-blend-mode");
376 sp_repr_css_unset_property(css_current, "filter");
377 sp_repr_css_unset_property(css_current, "stop-color");
378 sp_repr_css_unset_property(css_current, "stop-opacity");
379 sp_repr_css_set(repr, css_current, "style");
380 } else {
381 SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style");
382 sp_repr_css_set(repr, css, "style");
384 }
385 if (css_current) {
386 sp_repr_css_attr_unref(css_current);
387 }
388}
389
394double
396{
397 (void)desktop; // TODO cleanup
399 Glib::ustring desktop_style = prefs->getString("/desktop/style");
400 Glib::ustring style_str;
401 if ((prefs->getBool("/tools/text/usecurrent")) && !desktop_style.empty()) {
402 style_str = desktop_style;
403 } else {
404 style_str = prefs->getString("/tools/text/style");
405 }
406
407 double ret = 12;
408 if (!style_str.empty()) {
409 SPStyle style(SP_ACTIVE_DOCUMENT);
410 style.mergeString(style_str.data());
411 ret = style.font_size.computed;
412 }
413 return ret;
414}
415
417gdouble
418stroke_average_width (const std::vector<SPItem*> &objects)
419{
420 if (objects.empty())
421 return Geom::infinity();
422
423 gdouble avgwidth = 0.0;
424 bool notstroked = true;
425 int n_notstroked = 0;
426 for (auto item : objects) {
427 if (!item) {
428 continue;
429 }
430
431 Geom::Affine i2dt = item->i2dt_affine();
432
433 double width = item->style->stroke_width.computed * i2dt.descrim();
434
435 // Width becomes NaN when scaling a diagonal line to a horizontal line (lp:825840)
436 if (item->style->stroke.isNone() || std::isnan(width)) {
437 ++n_notstroked; // do not count nonstroked objects
438 continue;
439 } else {
440 notstroked = false;
441 }
442
443 avgwidth += width;
444 }
445
446 if (notstroked)
447 return Geom::infinity();
448
449 return avgwidth / (objects.size() - n_notstroked);
450}
451
455int
456objects_query_fillstroke (const std::vector<SPItem*> &objects, SPStyle *style_res, bool const isfill)
457{
458 if (objects.empty()) {
459 /* No objects, set empty */
460 return QUERY_STYLE_NOTHING;
461 }
462
463 SPIPaint *paint_res = style_res->getFillOrStroke(isfill);
464 paint_res->set = true;
465
466 bool paintImpossible = true;
467 Colors::ColorSet colors;
468
469 for (auto obj : objects) {
470 if (!obj) {
471 continue;
472 }
473 SPStyle *style = obj->style;
474 if (!style) {
475 continue;
476 }
477
478 SPIPaint *paint = style->getFillOrStroke(isfill);
479 if (!paint) {
480 continue;
481 }
482
483 // We consider paint "effectively set" for anything within text hierarchy
484 SPObject *parent = obj->parent;
485 bool paint_effectively_set =
486 paint->set || is<SPText>(parent) || is<SPTextPath>(parent) || is<SPTSpan>(parent)
487 || is<SPFlowtext>(parent) || is<SPFlowdiv>(parent) || is<SPFlowpara>(parent)
488 || is<SPFlowtspan>(parent) || is<SPFlowline>(parent);
489
490 // 1. Bail out with QUERY_STYLE_MULTIPLE_DIFFERENT if necessary
491
492 // cppcheck-suppress comparisonOfBoolWithInt
493 if ((!paintImpossible) && (!paint->isSameType(*paint_res) || (paint_res->set != paint_effectively_set))) {
494 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different types of paint
495 }
496
497 if (paint_res->set && paint->set && paint_res->isPaintserver()) {
498 // both previous paint and this paint were a server, see if the servers are compatible
499
500 SPPaintServer *server_res = isfill ? style_res->getFillPaintServer() : style_res->getStrokePaintServer();
501 SPPaintServer *server = isfill ? style->getFillPaintServer() : style->getStrokePaintServer();
502
503 auto linear_res = cast<SPLinearGradient>(server_res);
504 SPRadialGradient *radial_res = linear_res ? nullptr : cast<SPRadialGradient>(server_res);
505 SPPattern *pattern_res = (linear_res || radial_res) ? nullptr : cast<SPPattern>(server_res);
506 SPHatch *hatch_res =
507 (linear_res || radial_res || pattern_res) ? nullptr : cast<SPHatch>(server_res);
508
509 if (linear_res) {
510 auto linear = cast<SPLinearGradient>(server);
511 if (!linear) {
512 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different kind of server
513 }
514
515 SPGradient *vector = linear->getVector();
516 SPGradient *vector_res = linear_res->getVector();
517 if (vector_res != vector) {
518 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different gradient vectors
519 }
520 } else if (radial_res) {
521 auto radial = cast<SPRadialGradient>(server);
522 if (!radial) {
523 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different kind of server
524 }
525
526 SPGradient *vector = radial->getVector();
527 SPGradient *vector_res = radial_res->getVector();
528 if (vector_res != vector) {
529 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different gradient vectors
530 }
531 } else if (pattern_res) {
532 auto pattern = cast<SPPattern>(server);
533 if (!pattern) {
534 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different kind of server
535 }
536
537 auto pat = cast<SPPattern>(server)->rootPattern();
538 auto pat_res = cast<SPPattern>(server_res)->rootPattern();
539 if (pat_res != pat) {
540 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different pattern roots
541 }
542 } else if (hatch_res) {
543 auto hatch = cast<SPHatch>(server);
544 if (!hatch) {
545 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different kind of server
546 }
547
548 auto hat = cast<SPHatch>(server)->rootHatch();
549 auto hat_res = cast<SPHatch>(server_res)->rootHatch();
550 if (hat_res != hat) {
551 return QUERY_STYLE_MULTIPLE_DIFFERENT; // different hatch roots
552 }
553 }
554 }
555
556 // 2. Sum color, copy server from paint to paint_res
557 if (paint_res->set && paint->isColor()) {
558 auto copy = paint->getColor();
559 copy.addOpacity(isfill ? style->fill_opacity : style->stroke_opacity);
560
561 if (colors.isEmpty()) {
562 paint_res->setColor(copy);
563 }
564 // Remove colors from this list somehow?
565 colors.set(obj->getId(), copy);
566 }
567
568 paintImpossible = false;
569 paint_res->paintOrigin = paint->paintOrigin;
570 if (paint_res->set && paint_effectively_set && paint->isPaintserver()) { // copy the server
571 if (isfill) {
572 sp_style_set_to_uri(style_res, true, style->getFillURI());
573 } else {
574 sp_style_set_to_uri(style_res, false, style->getStrokeURI());
575 }
576 }
577 paint_res->set = paint_effectively_set;
578 style_res->fill_rule.computed = style->fill_rule.computed; // no averaging on this, just use the last one
579 }
580
581 if (paint_res->set && paint_res->isColor() && !colors.isEmpty()) {
582 auto color = colors.getAverage();
583 if (isfill) {
584 style_res->fill_opacity.set_double(color.stealOpacity());
585 } else {
586 style_res->stroke_opacity.set_double(color.stealOpacity());
587 }
588 paint_res->setColor(color);
589
590 if (colors.size() > 1) {
591 if (colors.isSame())
593 else
595 } else {
596 return QUERY_STYLE_SINGLE;
597 }
598 }
599
600 // Not color
601 if (objects.size() > 1) {
603 } else {
604 return QUERY_STYLE_SINGLE;
605 }
606}
607
611int
612objects_query_opacity (const std::vector<SPItem*> &objects, SPStyle *style_res)
613{
614 if (objects.empty()) {
615 /* No objects, set empty */
616 return QUERY_STYLE_NOTHING;
617 }
618
619 gdouble opacity_sum = 0;
620 gdouble opacity_prev = -1;
621 bool same_opacity = true;
622 guint opacity_items = 0;
623
624
625 for (auto obj : objects) {
626 if (!obj) {
627 continue;
628 }
629 SPStyle *style = obj->style;
630 if (!style) {
631 continue;
632 }
633
634 double opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
635 opacity_sum += opacity;
636 if (opacity_prev != -1 && opacity != opacity_prev) {
637 same_opacity = false;
638 }
639 opacity_prev = opacity;
640 opacity_items ++;
641 }
642 if (opacity_items > 1) {
643 opacity_sum /= opacity_items;
644 }
645
646 style_res->opacity.value = SP_SCALE24_FROM_FLOAT(opacity_sum);
647
648 if (opacity_items == 0) {
649 return QUERY_STYLE_NOTHING;
650 } else if (opacity_items == 1) {
651 return QUERY_STYLE_SINGLE;
652 } else {
653 if (same_opacity) {
655 } else {
657 }
658 }
659}
660
664int
665objects_query_strokewidth (const std::vector<SPItem*> &objects, SPStyle *style_res)
666{
667 if (objects.empty()) {
668 /* No objects, set empty */
669 return QUERY_STYLE_NOTHING;
670 }
671
672 gdouble avgwidth = 0.0;
673
674 gdouble prev_sw = -1;
675 bool same_sw = true;
676 bool noneSet = true; // is stroke set to none?
677 bool prev_hairline;
678
679 int n_stroked = 0;
680
681 for (auto obj : objects) {
682 if (!obj) {
683 continue;
684 }
685 auto item = obj;
686 if (!item) {
687 continue;
688 }
689 SPStyle *style = obj->style;
690 if (!style) {
691 continue;
692 }
693
694 noneSet &= style->stroke.isNone();
695
696 if (style->stroke_extensions.hairline) {
697 // Can't average a bool. It's true if there's any hairlines in the selection.
698 style_res->stroke_extensions.hairline = true;
699 }
700
701 if (n_stroked > 0 && prev_hairline != style->stroke_extensions.hairline) {
702 same_sw = false;
703 }
704 prev_hairline = style->stroke_extensions.hairline;
705
707 double sw = style->stroke_width.computed * i2d.descrim();
708
709 if (!std::isnan(sw)) {
710 if (prev_sw != -1 && fabs(sw - prev_sw) > 1e-3)
711 same_sw = false;
712 prev_sw = sw;
713
714 avgwidth += sw;
715 n_stroked ++;
716 } else if (style->stroke_extensions.hairline) {
717 n_stroked ++;
718 }
719 }
720
721 if (n_stroked > 1)
722 avgwidth /= (n_stroked);
723
724 style_res->stroke_width.computed = avgwidth;
725 style_res->stroke_width.set = true;
726 style_res->stroke.noneSet = noneSet; // Will only be true if none of the selected objects has it's stroke set.
727
728 if (n_stroked == 0) {
729 return QUERY_STYLE_NOTHING;
730 } else if (n_stroked == 1) {
731 return QUERY_STYLE_SINGLE;
732 } else {
733 if (same_sw)
735 else
737 }
738}
739
743int
744objects_query_miterlimit (const std::vector<SPItem*> &objects, SPStyle *style_res)
745{
746 if (objects.empty()) {
747 /* No objects, set empty */
748 return QUERY_STYLE_NOTHING;
749 }
750
751 gdouble avgml = 0.0;
752 int n_stroked = 0;
753
754 gdouble prev_ml = -1;
755 bool same_ml = true;
756
757 for (auto obj : objects) {
758 if (!obj) {
759 continue;
760 }
761 SPStyle *style = obj->style;
762 if (!style) {
763 continue;
764 }
765
766 if ( style->stroke.isNone() ) {
767 continue;
768 }
769
770 n_stroked ++;
771
772 if (prev_ml != -1 && fabs(style->stroke_miterlimit.value - prev_ml) > 1e-3) {
773 same_ml = false;
774 }
775 prev_ml = style->stroke_miterlimit.value;
776
777 avgml += style->stroke_miterlimit.value;
778 }
779
780 if (n_stroked > 1) {
781 avgml /= (n_stroked);
782 }
783
784 style_res->stroke_miterlimit.value = avgml;
785 style_res->stroke_miterlimit.set = true;
786
787 if (n_stroked == 0) {
788 return QUERY_STYLE_NOTHING;
789 } else if (n_stroked == 1) {
790 return QUERY_STYLE_SINGLE;
791 } else {
792 if (same_ml)
794 else
796 }
797}
798
802int
803objects_query_strokecap (const std::vector<SPItem*> &objects, SPStyle *style_res)
804{
805 if (objects.empty()) {
806 /* No objects, set empty */
807 return QUERY_STYLE_NOTHING;
808 }
809
810 auto prev_cap = SP_STROKE_LINECAP_BUTT;
811 bool same_cap = true;
812 int n_stroked = 0;
813
814 for (auto obj : objects) {
815 if (!obj) {
816 continue;
817 }
818 SPStyle *style = obj->style;
819 if (!style) {
820 continue;
821 }
822
823 if ( style->stroke.isNone() ) {
824 continue;
825 }
826
827 n_stroked ++;
828
829 if (n_stroked > 1 && style->stroke_linecap.value != prev_cap)
830 same_cap = false;
831 prev_cap = style->stroke_linecap.value;
832 }
833
834 style_res->stroke_linecap.value = prev_cap;
835 style_res->stroke_linecap.set = true;
836
837 if (n_stroked == 0) {
838 return QUERY_STYLE_NOTHING;
839 } else if (n_stroked == 1) {
840 return QUERY_STYLE_SINGLE;
841 } else {
842 if (same_cap)
844 else
846 }
847}
848
852int
853objects_query_strokejoin (const std::vector<SPItem*> &objects, SPStyle *style_res)
854{
855 if (objects.empty()) {
856 /* No objects, set empty */
857 return QUERY_STYLE_NOTHING;
858 }
859
860 auto prev_join = SP_STROKE_LINEJOIN_MITER;
861 bool same_join = true;
862 int n_stroked = 0;
863
864 for (auto obj : objects) {
865 if (!obj) {
866 continue;
867 }
868 SPStyle *style = obj->style;
869 if (!style) {
870 continue;
871 }
872
873 if ( style->stroke.isNone() ) {
874 continue;
875 }
876
877 n_stroked ++;
878
879 if (n_stroked > 1 && style->stroke_linejoin.value != prev_join) {
880 same_join = false;
881 }
882 prev_join = style->stroke_linejoin.value;
883 }
884
885 style_res->stroke_linejoin.value = prev_join;
886 style_res->stroke_linejoin.set = true;
887
888 if (n_stroked == 0) {
889 return QUERY_STYLE_NOTHING;
890 } else if (n_stroked == 1) {
891 return QUERY_STYLE_SINGLE;
892 } else {
893 if (same_join)
895 else
897 }
898}
899
903int
904objects_query_paintorder (const std::vector<SPItem*> &objects, SPStyle *style_res)
905{
906 if (objects.empty()) {
907 /* No objects, set empty */
908 return QUERY_STYLE_NOTHING;
909 }
910
911 std::string prev_order;
912 bool same_order = true;
913 int n_order = 0;
914
915 for (auto obj : objects) {
916 if (!obj) {
917 continue;
918 }
919 SPStyle *style = obj->style;
920 if (!style) {
921 continue;
922 }
923
924 if ( style->stroke.isNone() ) {
925 continue;
926 }
927
928 n_order ++;
929
930 if (style->paint_order.set) {
931 if (!prev_order.empty() && prev_order.compare( style->paint_order.value ) != 0) {
932 same_order = false;
933 }
934 prev_order = style->paint_order.value;
935 }
936 }
937
938
939 g_free( style_res->paint_order.value );
940 style_res->paint_order.value= g_strdup( prev_order.c_str() );
941 style_res->paint_order.set = true;
942
943 if (n_order == 0) {
944 return QUERY_STYLE_NOTHING;
945 } else if (n_order == 1) {
946 return QUERY_STYLE_SINGLE;
947 } else {
948 if (same_order)
950 else
952 }
953}
954
958int
959objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_res)
960{
961 bool different = false;
962 bool different_lineheight = false;
963 bool different_lineheight_unit = false;
964
965 double size = 0;
966 double letterspacing = 0;
967 double wordspacing = 0;
968 double lineheight = 0;
969 bool letterspacing_normal = false;
970 bool wordspacing_normal = false;
971 bool lineheight_normal = false;
972 bool lineheight_unit_proportional = false;
973 bool lineheight_unit_absolute = false;
974 bool lineheight_set = false; // Set true if any object has lineheight set.
975
976 double size_prev = 0;
977 double letterspacing_prev = 0;
978 double wordspacing_prev = 0;
979 double lineheight_prev = 0;
980 int lineheight_unit_prev = -1;
981
982 int texts = 0;
983 int no_size = 0;
984
985 for (auto obj : objects) {
986 if (!isTextualItem(obj)) {
987 continue;
988 }
989
990 SPStyle *style = obj->style;
991 if (!style) {
992 continue;
993 }
994
995 texts ++;
996 auto item = obj;
997 g_assert(item != nullptr);
998
999 // Quick way of getting document scale. Should be same as:
1000 // item->document->getDocumentScale().Affine().descrim()
1001 double doc_scale = Geom::Affine(item->i2dt_affine()).descrim();
1002
1003 double dummy = style->font_size.computed * doc_scale;
1004 if (!std::isnan(dummy)) {
1005 size += dummy;
1006 } else {
1007 no_size++;
1008 }
1009
1010 if (style->letter_spacing.normal) {
1011 if (!different && (letterspacing_prev == 0 || letterspacing_prev == letterspacing)) {
1012 letterspacing_normal = true;
1013 }
1014 } else {
1015 letterspacing += style->letter_spacing.computed * doc_scale;;
1016 letterspacing_normal = false;
1017 }
1018
1019 if (style->word_spacing.normal) {
1020 if (!different && (wordspacing_prev == 0 || wordspacing_prev == wordspacing)) {
1021 wordspacing_normal = true;
1022 }
1023 } else {
1024 wordspacing += style->word_spacing.computed * doc_scale;
1025 wordspacing_normal = false;
1026 }
1027
1028 // If all line spacing units the same, use that (average line spacing).
1029 // Else if all line spacings absolute, use 'px' (average line spacing).
1030 // Else if all line spacings proportional, use % (average line spacing).
1031 // Else use default.
1032 double lineheight_current;
1033 int lineheight_unit_current;
1034 if (style->line_height.normal) {
1035 lineheight_current = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
1036 lineheight_unit_current = SP_CSS_UNIT_NONE;
1037 if (!different_lineheight &&
1038 (lineheight_prev == 0 || lineheight_prev == lineheight_current))
1039 lineheight_normal = true;
1040 } else if (style->line_height.unit == SP_CSS_UNIT_NONE ||
1041 style->line_height.unit == SP_CSS_UNIT_PERCENT ||
1042 style->line_height.unit == SP_CSS_UNIT_EM ||
1043 style->line_height.unit == SP_CSS_UNIT_EX ||
1044 style->font_size.computed == 0) {
1045 lineheight_current = style->line_height.value;
1046 lineheight_unit_current = style->line_height.unit;
1047 lineheight_unit_proportional = true;
1048 lineheight_normal = false;
1049 lineheight += lineheight_current;
1050 } else {
1051 // Always 'px' internally
1052 lineheight_current = style->line_height.computed;
1053 lineheight_unit_current = style->line_height.unit;
1054 lineheight_unit_absolute = true;
1055 lineheight_normal = false;
1056 lineheight += lineheight_current * doc_scale;
1057 }
1058 if (style->line_height.set) {
1059 lineheight_set = true;
1060 }
1061
1062 if ((size_prev != 0 && style->font_size.computed != size_prev) ||
1063 (letterspacing_prev != 0 && style->letter_spacing.computed != letterspacing_prev) ||
1064 (wordspacing_prev != 0 && style->word_spacing.computed != wordspacing_prev)) {
1065 different = true;
1066 }
1067
1068 if (lineheight_prev != 0 && lineheight_current != lineheight_prev) {
1069 different_lineheight = true;
1070 }
1071
1072 if (lineheight_unit_prev != -1 && lineheight_unit_current != lineheight_unit_prev) {
1073 different_lineheight_unit = true;
1074 }
1075
1076 size_prev = style->font_size.computed;
1077 letterspacing_prev = style->letter_spacing.computed;
1078 wordspacing_prev = style->word_spacing.computed;
1079 lineheight_prev = lineheight_current;
1080 lineheight_unit_prev = lineheight_unit_current;
1081
1082 // FIXME: we must detect MULTIPLE_DIFFERENT for these too
1083 style_res->text_anchor.computed = style->text_anchor.computed;
1084 }
1085
1086 if (texts == 0)
1087 return QUERY_STYLE_NOTHING;
1088
1089 if (texts > 1) {
1090 if (texts - no_size > 0) {
1091 size /= (texts - no_size);
1092 }
1093 letterspacing /= texts;
1094 wordspacing /= texts;
1095 lineheight /= texts;
1096 }
1097
1098 style_res->font_size.computed = size;
1099 style_res->font_size.type = SP_FONT_SIZE_LENGTH;
1100
1101 style_res->letter_spacing.normal = letterspacing_normal;
1102 style_res->letter_spacing.computed = letterspacing;
1103
1104 style_res->word_spacing.normal = wordspacing_normal;
1105 style_res->word_spacing.computed = wordspacing;
1106
1107 style_res->line_height.normal = lineheight_normal;
1108 style_res->line_height.computed = lineheight;
1109 style_res->line_height.value = lineheight;
1110 if (different_lineheight_unit) {
1111 if (lineheight_unit_absolute && !lineheight_unit_proportional) {
1112 // Mixture of absolute units
1113 style_res->line_height.unit = SP_CSS_UNIT_PX;
1114 } else {
1115 // Mixture of relative units
1116 style_res->line_height.unit = SP_CSS_UNIT_PERCENT;
1117 }
1118 if (lineheight_unit_absolute && lineheight_unit_proportional) {
1119 // Mixed types of units, fallback to default
1120 style_res->line_height.computed = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL * 100.0;
1122 }
1123 } else {
1124 // Same units.
1125 if (lineheight_unit_prev != -1) {
1126 style_res->line_height.unit = lineheight_unit_prev;
1127 } else {
1128 // No text object... use default.
1129 style_res->line_height.unit = SP_CSS_UNIT_NONE;
1132 }
1133 }
1134
1135 // Used by text toolbar unset 'line-height'
1136 style_res->line_height.set = lineheight_set;
1137
1138 if (texts > 1) {
1139 if (different || different_lineheight) {
1141 } else {
1143 }
1144 } else {
1145 return QUERY_STYLE_SINGLE;
1146 }
1147}
1148
1152int
1153objects_query_fontstyle (const std::vector<SPItem*> &objects, SPStyle *style_res)
1154{
1155 bool different = false;
1156 bool set = false;
1157
1158 int texts = 0;
1159
1160 for (auto obj : objects) {
1161 if (!isTextualItem(obj)) {
1162 continue;
1163 }
1164
1165 SPStyle *style = obj->style;
1166 if (!style) {
1167 continue;
1168 }
1169
1170 texts ++;
1171
1172 if (set &&
1173 ( ( style_res->font_weight.computed != style->font_weight.computed ) ||
1174 ( style_res->font_style.computed != style->font_style.computed ) ||
1175 ( style_res->font_stretch.computed != style->font_stretch.computed ) ||
1176 ( style_res->font_variant.computed != style->font_variant.computed ) ||
1177 ( style_res->font_variation_settings != style->font_variation_settings ) ) ) {
1178 different = true; // different styles
1179 }
1180
1181 set = true;
1182 style_res->font_weight.value = style_res->font_weight.computed = style->font_weight.computed;
1183 style_res->font_style.value = style_res->font_style.computed = style->font_style.computed;
1184 style_res->font_stretch.value = style_res->font_stretch.computed = style->font_stretch.computed;
1185 style_res->font_variant.value = style_res->font_variant.computed = style->font_variant.computed;
1187 style_res->text_align.value = style_res->text_align.computed = style->text_align.computed;
1188 style_res->font_size.value = style->font_size.value;
1189 style_res->font_size.unit = style->font_size.unit;
1190 }
1191
1192 if (texts == 0 || !set)
1193 return QUERY_STYLE_NOTHING;
1194
1195 if (texts > 1) {
1196 if (different) {
1198 } else {
1200 }
1201 } else {
1202 return QUERY_STYLE_SINGLE;
1203 }
1204}
1205
1206int
1207objects_query_fontvariants (const std::vector<SPItem*> &objects, SPStyle *style_res)
1208{
1209 bool set = false;
1210
1211 int texts = 0;
1212
1213 SPILigatures* ligatures_res = &(style_res->font_variant_ligatures);
1214 SPINumeric* numeric_res = &(style_res->font_variant_numeric);
1215 SPIEastAsian* asian_res = &(style_res->font_variant_east_asian);
1216
1217 // Stores 'and' of all values
1219 int position_computed = SP_CSS_FONT_VARIANT_POSITION_NORMAL;
1220 int caps_computed = SP_CSS_FONT_VARIANT_CAPS_NORMAL;
1223
1224 // Stores only differences
1225 ligatures_res->value = 0;
1226 int position_value = 0;
1227 int caps_value = 0;
1228 numeric_res->value = 0;
1229 asian_res->value = 0;
1230
1231 for (auto obj : objects) {
1232 if (!isTextualItem(obj)) {
1233 continue;
1234 }
1235
1236 SPStyle *style = obj->style;
1237 if (!style) {
1238 continue;
1239 }
1240
1241 texts ++;
1242
1243 SPILigatures* ligatures_in = &(style->font_variant_ligatures);
1244 auto* position_in = &(style->font_variant_position);
1245 auto* caps_in = &(style->font_variant_caps);
1246 SPINumeric* numeric_in = &(style->font_variant_numeric);
1247 SPIEastAsian* asian_in = &(style->font_variant_east_asian);
1248
1249 // computed stores which bits are on/off, only valid if same between all selected objects.
1250 // value stores which bits are different between objects. This is a bit of an abuse of
1251 // the values but then we don't need to add new variables to class.
1252 if (set) {
1253 ligatures_res->value |= (ligatures_res->computed ^ ligatures_in->computed );
1254 ligatures_res->computed &= ligatures_in->computed;
1255
1256 position_value |= (position_computed ^ position_in->computed);
1257 position_computed &= position_in->computed;
1258
1259 caps_value |= (caps_computed ^ caps_in->computed);
1260 caps_computed &= caps_in->computed;
1261
1262 numeric_res->value |= (numeric_res->computed ^ numeric_in->computed );
1263 numeric_res->computed &= numeric_in->computed;
1264
1265 asian_res->value |= (asian_res->computed ^ asian_in->computed );
1266 asian_res->computed &= asian_in->computed;
1267
1268 } else {
1269 ligatures_res->computed = ligatures_in->computed;
1270 position_computed = position_in->computed;
1271 caps_computed = caps_in->computed;
1272 numeric_res->computed = numeric_in->computed;
1273 asian_res->computed = asian_in->computed;
1274 }
1275
1276 set = true;
1277 }
1278
1279 // violates enum type safety!
1280 style_res->font_variant_position.value = static_cast<SPCSSFontVariantPosition>(position_value);
1281 style_res->font_variant_position.computed = static_cast<SPCSSFontVariantPosition>(position_computed);
1282 style_res->font_variant_caps.value = static_cast<SPCSSFontVariantCaps>(caps_value);
1283 style_res->font_variant_caps.computed = static_cast<SPCSSFontVariantCaps>(caps_computed);
1284
1285 bool different = (style_res->font_variant_ligatures.value != 0 ||
1286 position_value != 0 ||
1287 caps_value != 0 ||
1288 style_res->font_variant_numeric.value != 0 ||
1289 style_res->font_variant_east_asian.value != 0);
1290
1291 if (texts == 0 || !set)
1292 return QUERY_STYLE_NOTHING;
1293
1294 if (texts > 1) {
1295 if (different) {
1297 } else {
1299 }
1300 } else {
1301 return QUERY_STYLE_SINGLE;
1302 }
1303}
1304
1305
1309int
1310objects_query_writing_modes (const std::vector<SPItem*> &objects, SPStyle *style_res)
1311{
1312 bool different = false;
1313 bool set = false;
1314
1315 int texts = 0;
1316
1317 for (auto obj : objects) {
1318 if (!isTextualItem(obj)) {
1319 continue;
1320 }
1321
1322 SPStyle *style = obj->style;
1323 if (!style) {
1324 continue;
1325 }
1326
1327 texts ++;
1328
1329 if (set &&
1330 ( ( style_res->writing_mode.computed != style->writing_mode.computed ) ||
1331 ( style_res->direction.computed != style->direction.computed ) ||
1332 ( style_res->text_orientation.computed != style->text_orientation.computed ) ) ) {
1333 different = true; // different styles
1334 }
1335
1336 set = true;
1337 style_res->writing_mode.computed = style->writing_mode.computed;
1338 style_res->direction.computed = style->direction.computed;
1339 style_res->text_orientation.computed = style->text_orientation.computed;
1340 }
1341
1342 if (texts == 0 || !set)
1343 return QUERY_STYLE_NOTHING;
1344
1345 if (texts > 1) {
1346 if (different) {
1348 } else {
1350 }
1351 } else {
1352 return QUERY_STYLE_SINGLE;
1353 }
1354}
1355
1356int
1357objects_query_fontfeaturesettings (const std::vector<SPItem*> &objects, SPStyle *style_res)
1358{
1359 bool different = false;
1360 int texts = 0;
1361
1362 style_res->font_feature_settings.clear();
1363
1364 for (auto obj : objects) {
1365 // std::cout << " " << reinterpret_cast<SPObject*>(i->data)->getId() << std::endl;
1366 if (!isTextualItem(obj)) {
1367 continue;
1368 }
1369
1370 SPStyle *style = obj->style;
1371 if (!style) {
1372 continue;
1373 }
1374
1375 texts ++;
1376
1377 if (style_res->font_feature_settings.set && //
1378 strcmp(style_res->font_feature_settings.value(),
1379 style->font_feature_settings.value())) {
1380 different = true; // different fonts
1381 }
1382
1383 style_res->font_feature_settings = style->font_feature_settings;
1384 style_res->font_feature_settings.set = true;
1385 }
1386
1387 if (texts == 0 || !style_res->font_feature_settings.set) {
1388 return QUERY_STYLE_NOTHING;
1389 }
1390
1391 if (texts > 1) {
1392 if (different) {
1394 } else {
1396 }
1397 } else {
1398 return QUERY_STYLE_SINGLE;
1399 }
1400}
1401
1402
1406static int
1407objects_query_baselines (const std::vector<SPItem*> &objects, SPStyle *style_res)
1408{
1409 bool different = false;
1410
1411 // Only baseline-shift at the moment
1412 // We will return:
1413 // If baseline-shift is same for all objects:
1414 // The full baseline-shift data (used for subscripts and superscripts)
1415 // If baseline-shift is different:
1416 // The average baseline-shift (not implemented at the moment as this is complicated June 2010)
1417 SPIBaselineShift old;
1418 old.value = 0.0;
1419 old.computed = 0.0;
1420
1421 // double baselineshift = 0.0;
1422 bool set = false;
1423
1424 int texts = 0;
1425
1426 for (auto obj : objects) {
1427 if (!isTextualItem(obj)) {
1428 continue;
1429 }
1430
1431 SPStyle *style = obj->style;
1432 if (!style) {
1433 continue;
1434 }
1435
1436 texts ++;
1437
1439 if(style->baseline_shift.set) {
1440
1441 current.set = style->baseline_shift.set;
1442 current.inherit = style->baseline_shift.inherit;
1443 current.type = style->baseline_shift.type;
1444 current.literal = style->baseline_shift.literal;
1445 current.value = style->baseline_shift.value;
1446 current.computed = style->baseline_shift.computed;
1447
1448 if( set ) {
1449 if( current.set != old.set ||
1450 current.inherit != old.inherit ||
1451 current.type != old.type ||
1452 current.literal != old.literal ||
1453 current.value != old.value ||
1454 current.computed != old.computed ) {
1455 // Maybe this needs to be better thought out.
1456 different = true;
1457 }
1458 }
1459
1460 set = true;
1461
1462 old.set = current.set;
1463 old.inherit = current.inherit;
1464 old.type = current.type;
1465 old.literal = current.literal;
1466 old.value = current.value;
1467 old.computed = current.computed;
1468 }
1469 }
1470
1471 if (different || !set ) {
1472 style_res->baseline_shift.set = false;
1473 style_res->baseline_shift.computed = 0.0;
1474 } else {
1475 style_res->baseline_shift.set = old.set;
1476 style_res->baseline_shift.inherit = old.inherit;
1477 style_res->baseline_shift.type = old.type;
1478 style_res->baseline_shift.literal = old.literal;
1479 style_res->baseline_shift.value = old.value;
1480 style_res->baseline_shift.computed = old.computed;
1481 }
1482
1483 if (texts == 0 || !set)
1484 return QUERY_STYLE_NOTHING;
1485
1486 if (texts > 1) {
1487 if (different) {
1489 } else {
1491 }
1492 } else {
1493 return QUERY_STYLE_SINGLE;
1494 }
1495}
1496
1500int
1501objects_query_fontfamily (const std::vector<SPItem*> &objects, SPStyle *style_res)
1502{
1503 bool different = false;
1504 int texts = 0;
1505
1506 style_res->font_family.clear();
1507
1508 for (auto obj : objects) {
1509 // std::cout << " " << reinterpret_cast<SPObject*>(i->data)->getId() << std::endl;
1510 if (!isTextualItem(obj)) {
1511 continue;
1512 }
1513
1514 SPStyle *style = obj->style;
1515 if (!style) {
1516 continue;
1517 }
1518
1519 texts ++;
1520
1521 if (style_res->font_family.set && //
1522 strcmp(style_res->font_family.value(), style->font_family.value())) {
1523 different = true; // different fonts
1524 }
1525
1526 style_res->font_family = style->font_family;
1527 style_res->font_family.set = true;
1528 }
1529
1530 if (texts == 0 || !style_res->font_family.set) {
1531 return QUERY_STYLE_NOTHING;
1532 }
1533
1534 if (texts > 1) {
1535 if (different) {
1537 } else {
1539 }
1540 } else {
1541 return QUERY_STYLE_SINGLE;
1542 }
1543}
1544
1545static int
1546objects_query_fontspecification (const std::vector<SPItem*> &objects, SPStyle *style_res)
1547{
1548 bool different = false;
1549 int texts = 0;
1550
1551 style_res->font_specification.clear();
1552
1553 for (auto obj : objects) {
1554 // std::cout << " " << reinterpret_cast<SPObject*>(i->data)->getId() << std::endl;
1555 if (!isTextualItem(obj)) {
1556 continue;
1557 }
1558
1559 SPStyle *style = obj->style;
1560 if (!style) {
1561 continue;
1562 }
1563
1564 texts ++;
1565
1566 if (style_res->font_specification.set &&
1567 g_strcmp0(style_res->font_specification.value(), style->font_specification.value())) {
1568 different = true; // different fonts
1569 }
1570
1571 if (style->font_specification.set) {
1572 style_res->font_specification = style->font_specification;
1573 style_res->font_specification.set = true;
1574 }
1575 }
1576
1577 if (texts == 0) {
1578 return QUERY_STYLE_NOTHING;
1579 }
1580
1581 if (texts > 1) {
1582 if (different) {
1584 } else {
1586 }
1587 } else {
1588 return QUERY_STYLE_SINGLE;
1589 }
1590}
1591
1592int
1593objects_query_blend (const std::vector<SPItem*> &objects, SPStyle *style_res)
1594{
1595 auto blend = SP_CSS_BLEND_NORMAL;
1596 auto blend_prev = blend;
1597 bool same_blend = true;
1598 guint items = 0;
1599
1600 for (auto obj : objects) {
1601 if (!obj) {
1602 continue;
1603 }
1604 SPStyle *style = obj->style;
1605 if (!style || !obj) {
1606 continue;
1607 }
1608
1609 items++;
1610
1611 if (style->mix_blend_mode.set) {
1612 blend = style->mix_blend_mode.value;
1613 }
1614 // defaults to blend mode = "normal"
1615 else {
1616 if (style->filter.set && style->getFilter()) {
1617 blend = filter_get_legacy_blend(obj);
1618 } else {
1619 blend = SP_CSS_BLEND_NORMAL;
1620 }
1621 }
1622
1623 if (items > 1 && blend_prev != blend)
1624 same_blend = false;
1625 blend_prev = blend;
1626 }
1627
1628 if (items > 0) {
1629 style_res->mix_blend_mode.value = blend;
1630 }
1631
1632 if (items == 0) {
1633 return QUERY_STYLE_NOTHING;
1634 } else if (items == 1) {
1635 return QUERY_STYLE_SINGLE;
1636 } else {
1637 if(same_blend)
1639 else
1641 }
1642}
1643
1644int objects_query_isolation(const std::vector<SPItem *> &objects, SPStyle *style_res)
1645{
1646 auto isolation = SP_CSS_ISOLATION_AUTO;
1647 auto isolation_prev = isolation;
1648 bool same_isolation = true;
1649 guint items = 0;
1650
1651 for (auto obj : objects) {
1652 if (!obj) {
1653 continue;
1654 }
1655 SPStyle *style = obj->style;
1656 if (!style || !obj) {
1657 continue;
1658 }
1659
1660 items++;
1661
1662 if (style->isolation.set) {
1663 isolation = style->isolation.value;
1664 }
1665 else {
1666 isolation = SP_CSS_ISOLATION_AUTO;
1667 }
1668
1669 if (items > 1 && isolation_prev != isolation)
1670 same_isolation = false;
1671 isolation_prev = isolation;
1672 }
1673
1674 if (items > 0) {
1675 style_res->isolation.value = isolation;
1676 }
1677
1678 if (items == 0) {
1679 return QUERY_STYLE_NOTHING;
1680 } else if (items == 1) {
1681 return QUERY_STYLE_SINGLE;
1682 } else {
1683 if (same_isolation)
1685 else
1687 }
1688}
1689
1693int
1694objects_query_blur (const std::vector<SPItem*> &objects, SPStyle *style_res)
1695{
1696 if (objects.empty()) {
1697 /* No objects, set empty */
1698 return QUERY_STYLE_NOTHING;
1699 }
1700
1701 float blur_sum = 0;
1702 float blur_prev = -1;
1703 bool same_blur = true;
1704 guint blur_items = 0;
1705 guint items = 0;
1706
1707 for (auto obj : objects) {
1708 if (!obj) {
1709 continue;
1710 }
1711 SPStyle *style = obj->style;
1712 if (!style) {
1713 continue;
1714 }
1715 auto item = obj;
1716 if (!item) {
1717 continue;
1718 }
1719
1720 Geom::Affine i2d = item->i2dt_affine();
1721
1722 items ++;
1723
1724 //if object has a filter
1725 if (style->filter.set && style->getFilter()) {
1726 //cycle through filter primitives
1727 for(auto& primitive_obj: style->getFilter()->children) {
1728 auto primitive = cast<SPFilterPrimitive>(&primitive_obj);
1729 if (primitive) {
1730
1731 //if primitive is gaussianblur
1732 auto spblur = cast<SPGaussianBlur>(primitive);
1733 if (spblur) {
1734 float num = spblur->get_std_deviation().getNumber();
1735 float dummy = num * i2d.descrim();
1736 if (!std::isnan(dummy)) {
1737 blur_sum += dummy;
1738 if (blur_prev != -1 && fabs (num - blur_prev) > 1e-2) // rather low tolerance because difference in blur radii is much harder to notice than e.g. difference in sizes
1739 same_blur = false;
1740 blur_prev = num;
1741 //TODO: deal with opt number, for the moment it's not necessary to the ui.
1742 blur_items ++;
1743 }
1744 }
1745 }
1746 }
1747 }
1748 }
1749
1750 if (items > 0) {
1751 if (blur_items > 0)
1752 blur_sum /= blur_items;
1753 style_res->filter_gaussianBlur_deviation.value = blur_sum;
1754 }
1755
1756 if (items == 0) {
1757 return QUERY_STYLE_NOTHING;
1758 } else if (items == 1) {
1759 return QUERY_STYLE_SINGLE;
1760 } else {
1761 if (same_blur)
1763 else
1765 }
1766}
1767
1772int
1773sp_desktop_query_style_from_list (const std::vector<SPItem*> &list, SPStyle *style, int property)
1774{
1775 if (property == QUERY_STYLE_PROPERTY_FILL) {
1776 return objects_query_fillstroke (list, style, true);
1777 } else if (property == QUERY_STYLE_PROPERTY_STROKE) {
1778 return objects_query_fillstroke (list, style, false);
1779
1780 } else if (property == QUERY_STYLE_PROPERTY_STROKEWIDTH) {
1781 return objects_query_strokewidth (list, style);
1782 } else if (property == QUERY_STYLE_PROPERTY_STROKEMITERLIMIT) {
1783 return objects_query_miterlimit (list, style);
1784 } else if (property == QUERY_STYLE_PROPERTY_STROKECAP) {
1785 return objects_query_strokecap (list, style);
1786 } else if (property == QUERY_STYLE_PROPERTY_STROKEJOIN) {
1787 return objects_query_strokejoin (list, style);
1788
1789 } else if (property == QUERY_STYLE_PROPERTY_PAINTORDER) {
1790 return objects_query_paintorder (list, style);
1791 } else if (property == QUERY_STYLE_PROPERTY_MASTEROPACITY) {
1792 return objects_query_opacity (list, style);
1793
1794 } else if (property == QUERY_STYLE_PROPERTY_FONT_SPECIFICATION) {
1795 return objects_query_fontspecification (list, style);
1796 } else if (property == QUERY_STYLE_PROPERTY_FONTFAMILY) {
1797 return objects_query_fontfamily (list, style);
1798 } else if (property == QUERY_STYLE_PROPERTY_FONTSTYLE) {
1799 return objects_query_fontstyle (list, style);
1800 } else if (property == QUERY_STYLE_PROPERTY_FONTVARIANTS) {
1801 return objects_query_fontvariants (list, style);
1802 } else if (property == QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS) {
1803 return objects_query_fontfeaturesettings (list, style);
1804 } else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) {
1805 return objects_query_fontnumbers (list, style);
1806 } else if (property == QUERY_STYLE_PROPERTY_WRITINGMODES) {
1807 return objects_query_writing_modes (list, style);
1808 } else if (property == QUERY_STYLE_PROPERTY_BASELINES) {
1809 return objects_query_baselines (list, style);
1810
1811 } else if (property == QUERY_STYLE_PROPERTY_BLEND) {
1812 return objects_query_blend (list, style);
1813 } else if (property == QUERY_STYLE_PROPERTY_ISOLATION) {
1814 return objects_query_isolation(list, style);
1815 } else if (property == QUERY_STYLE_PROPERTY_BLUR) {
1816 return objects_query_blur (list, style);
1817 }
1818 return QUERY_STYLE_NOTHING;
1819}
1820
1821
1826int
1828{
1829 // Used by text tool and in gradient dragging. See connectQueryStyle.
1830 int ret = desktop->_query_style_signal.emit(style, property);
1831
1832 if (ret != QUERY_STYLE_NOTHING)
1833 return ret; // subselection returned a style, pass it on
1834
1835 // otherwise, do querying and averaging over selection
1836 if (auto selection = desktop->getSelection()) {
1837 std::vector<SPItem *> vec(selection->items().begin(), selection->items().end());
1838 return sp_desktop_query_style_from_list (vec, style, property);
1839 }
1840
1841 return QUERY_STYLE_NOTHING;
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 :
TODO: insert short description here.
uint32_t Color
SVG blend filter effect.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
SPCSSAttr * getStyle(Glib::ustring const &pref_path)
Retrieve a CSS style.
SPCSSAttr * getInheritedStyle(Glib::ustring const &pref_path)
Retrieve an inherited CSS style.
void mergeStyle(Glib::ustring const &pref_path, SPCSSAttr *style)
Merge a CSS style with the current preference value.
static const double LINE_HEIGHT_NORMAL
The CSS spec allows line-height:normal to be whatever the user agent thinks will look good.
Definition Layout-TNG.h:209
Interface for refcounted XML nodes.
Definition node.h:80
To do: update description of desktop.
Definition desktop.h:149
SPCSSAttr * current
Current style.
Definition desktop.h:226
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
sigc::signal< bool(SPCSSAttr const *, bool)>::accumulated< StopOnTrue > _set_style_signal
Definition desktop.h:246
sigc::signal< int(SPStyle *, int)>::accumulated< StopOnNonZero > _query_style_signal
Definition desktop.h:247
Gradient.
Definition sp-gradient.h:86
SPGradient * getVector(bool force_private=false)
Returns private vector of given gradient (the gradient at the end of the href chain which has stops),...
Baseline shift type internal to SPStyle. (This is actually just like SPIFontSize)
Paint type internal to SPStyle.
bool isPaintserver() const
bool isColor() const
bool isSameType(SPIPaint const &other) const
SPPaintOrigin paintOrigin
void setColor(Colors::Color const &other)
Colors::Color const & getColor() const
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1821
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1816
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void changeCSS(SPCSSAttr *css, char const *attr)
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPObject * parent
Definition sp-object.h:189
char const * getAttribute(char const *name) const
ChildrenList children
Definition sp-object.h:907
Radial gradient.
An SVG style object.
Definition style.h:45
T< SPAttr::FONT_VARIANT_EAST_ASIAN, SPIEastAsian > font_variant_east_asian
Font variant East Asian.
Definition style.h:138
T< SPAttr::FONT_WEIGHT, SPIEnum< SPCSSFontWeight > > font_weight
Weight of the font.
Definition style.h:112
SPPaintServer * getFillPaintServer()
Definition style.h:339
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
SPFilter * getFilter()
Definition style.h:335
T< SPAttr::LINE_HEIGHT, SPILengthOrNormal > line_height
Line height (css2 10.8.1)
Definition style.h:118
Inkscape::URI const * getStrokeURI() const
Definition style.h:345
SPPaintServer * getStrokePaintServer()
Definition style.h:343
T< SPAttr::FONT_FEATURE_SETTINGS, SPIString > font_feature_settings
Font feature settings (Low level access to TrueType tables)
Definition style.h:140
T< SPAttr::LETTER_SPACING, SPILengthOrNormal > letter_spacing
letter spacing (css2 16.4)
Definition style.h:153
T< SPAttr::PAINT_ORDER, SPIPaintOrder > paint_order
Definition style.h:222
T< SPAttr::FONT_FAMILY, SPIString > font_family
Font family.
Definition style.h:120
T< SPAttr::INKSCAPE_FONT_SPEC, SPIString > font_specification
Full font name, as FontFactory::ConstructFontSpecification would give, for internal use.
Definition style.h:124
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
T< SPAttr::DIRECTION, SPIEnum< SPCSSDirection > > direction
text direction (svg1.1)
Definition style.h:161
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::FONT_STYLE, SPIEnum< SPCSSFontStyle > > font_style
Font style.
Definition style.h:108
T< SPAttr::FONT_VARIANT, SPIEnum< SPCSSFontVariant > > font_variant
Which substyle of the font (CSS 2.
Definition style.h:110
T< SPAttr::STROKE_LINEJOIN, SPIEnum< SPStrokeJoinType > > stroke_linejoin
stroke-linejoin
Definition style.h:253
SPIPaint * getFillOrStroke(bool fill_)
Get either the fill or the stroke property.
Definition style.h:355
T< SPAttr::WORD_SPACING, SPILengthOrNormal > word_spacing
word spacing (also css2 16.4)
Definition style.h:155
T< SPAttr::STROKE_OPACITY, SPIScale24 > stroke_opacity
stroke-opacity
Definition style.h:261
T< SPAttr::FONT_STRETCH, SPIEnum< SPCSSFontStretch > > font_stretch
Stretch of the font.
Definition style.h:114
void mergeString(char const *p)
Parses a style="..." string and merges it with an existing SPStyle.
Definition style.cpp:854
T< SPAttr::TEXT_ALIGN, SPIEnum< SPCSSTextAlign > > text_align
text alignment (css2 16.2) (not to be confused with text-anchor)
Definition style.h:150
T< SPAttr::STROKE_MITERLIMIT, SPIFloat > stroke_miterlimit
stroke-miterlimit
Definition style.h:255
T< SPAttr::FONT_VARIANT_NUMERIC, SPINumeric > font_variant_numeric
Font variant numeric (numerical formatting)
Definition style.h:134
T< SPAttr::TEXT_ANCHOR, SPIEnum< SPTextAnchor > > text_anchor
Anchor of the text (svg1.1 10.9.1)
Definition style.h:173
T< SPAttr::ISOLATION, SPIEnum< SPIsolation > > isolation
mix-blend-mode: CSS Compositing and Blending Level 1
Definition style.h:219
Inkscape::URI const * getFillURI() const
Definition style.h:341
T< SPAttr::FONT_VARIANT_CAPS, SPIEnum< SPCSSFontVariantCaps > > font_variant_caps
Font variant caps (small caps)
Definition style.h:132
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::FONT_VARIANT_POSITION, SPIEnum< SPCSSFontVariantPosition > > font_variant_position
Font variant position (subscript/superscript)
Definition style.h:130
T< SPAttr::WRITING_MODE, SPIEnum< SPCSSWritingMode > > writing_mode
Writing mode (svg1.1 10.7.2, CSS Writing Modes 3)
Definition style.h:163
T< SPAttr::STROKE_EXTENSIONS, SPIStrokeExtensions > stroke_extensions
-inkscape-stroke
Definition style.h:263
T< SPAttr::TEXT_ORIENTATION, SPIEnum< SPCSSTextOrientation > > text_orientation
Text orientation (CSS Writing Modes 3)
Definition style.h:165
T< SPAttr::FONT_SIZE, SPIFontSize > font_size
Size of the font.
Definition style.h:116
T< SPAttr::INVALID, SPILength > filter_gaussianBlur_deviation
normally not used, but duplicates the Gaussian blur deviation (if any) from the attached filter when ...
Definition style.h:279
T< SPAttr::FONT_VARIANT_LIGATURES, SPILigatures > font_variant_ligatures
Font variant ligatures.
Definition style.h:128
T< SPAttr::FONT_VARIATION_SETTINGS, SPIFontVariationSettings > font_variation_settings
Font variation settings (Low level access to OpenType variable font design-coordinate values)
Definition style.h:143
T< SPAttr::BASELINE_SHIFT, SPIBaselineShift > baseline_shift
Baseline shift (svg1.1 10.9.2)
Definition style.h:169
A set of colors which can be modified together used for color pickers.
TODO: insert short description here.
std::shared_ptr< Css const > css
double sp_desktop_get_font_size_tool(SPDesktop *desktop)
Returns the font size (in SVG pixels) of the text tool style (if text tool uses its own style) or des...
int objects_query_writing_modes(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average writing modes style of objects.
int objects_query_blend(const std::vector< SPItem * > &objects, SPStyle *style_res)
int objects_query_blur(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average blurring of a list of objects.
int objects_query_strokewidth(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average stroke width of a list of objects.
int sp_desktop_query_style_from_list(const std::vector< SPItem * > &list, SPStyle *style, int property)
Query the given list of objects for the given property, write the result to style,...
void sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
Apply the desktop's current style or the tool style to repr.
double sp_desktop_get_master_opacity_tool(SPDesktop *desktop, Glib::ustring const &tool, bool *has_opacity)
std::optional< Color > sp_desktop_get_color(SPDesktop *desktop, bool is_fill)
Return the desktop's current color.
std::optional< Color > sp_desktop_get_color_tool(SPDesktop *desktop, Glib::ustring const &tool, bool is_fill)
double sp_desktop_get_opacity_tool(SPDesktop *desktop, Glib::ustring const &tool, bool is_fill)
int objects_query_opacity(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average opacity of a list of objects.
void sp_desktop_apply_css_recursive(SPObject *o, SPCSSAttr *css, bool skip_lines)
Apply style on object and children, recursively.
int objects_query_strokejoin(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the stroke join of a list of objects.
int objects_query_fontvariants(const std::vector< SPItem * > &objects, SPStyle *style_res)
gdouble stroke_average_width(const std::vector< SPItem * > &objects)
Determine average stroke width, simple method.
int objects_query_paintorder(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the paint order of a list of objects.
void sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write_current, bool switch_style)
Apply style on selection on desktop.
int objects_query_fontnumbers(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average font size and spacing of objects.
SPCSSAttr * sp_desktop_get_style(SPDesktop *desktop, bool with_text)
Return the desktop's current style.
int objects_query_isolation(const std::vector< SPItem * > &objects, SPStyle *style_res)
int objects_query_fontfamily(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average font family of objects.
int objects_query_strokecap(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the stroke cap of a list of objects.
int sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property)
Query the subselection (if any) or selection on the given desktop for the given property,...
static int objects_query_fontspecification(const std::vector< SPItem * > &objects, SPStyle *style_res)
int objects_query_fillstroke(const std::vector< SPItem * > &objects, SPStyle *style_res, bool const isfill)
Write to style_res the average fill or stroke of list of objects, if applicable.
static int objects_query_baselines(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the baseline numbers.
static bool isTextualItem(SPObject const *obj)
int objects_query_miterlimit(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average miter limit of a list of objects.
int objects_query_fontfeaturesettings(const std::vector< SPItem * > &objects, SPStyle *style_res)
void sp_desktop_set_color(SPDesktop *desktop, Color const &color, bool is_relative, bool fill)
Set color on selection on desktop.
int objects_query_fontstyle(const std::vector< SPItem * > &objects, SPStyle *style_res)
Write to style_res the average font style of objects.
@ QUERY_STYLE_PROPERTY_STROKE
@ QUERY_STYLE_PROPERTY_BLEND
@ QUERY_STYLE_PROPERTY_STROKEWIDTH
@ QUERY_STYLE_PROPERTY_FONTNUMBERS
@ QUERY_STYLE_PROPERTY_FONT_SPECIFICATION
@ QUERY_STYLE_PROPERTY_BASELINES
@ QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS
@ QUERY_STYLE_PROPERTY_ISOLATION
@ QUERY_STYLE_PROPERTY_FONTSTYLE
@ QUERY_STYLE_PROPERTY_BLUR
@ QUERY_STYLE_PROPERTY_FONTFAMILY
@ QUERY_STYLE_PROPERTY_STROKEMITERLIMIT
@ QUERY_STYLE_PROPERTY_PAINTORDER
@ QUERY_STYLE_PROPERTY_FILL
@ QUERY_STYLE_PROPERTY_FONTVARIANTS
@ QUERY_STYLE_PROPERTY_STROKEJOIN
@ QUERY_STYLE_PROPERTY_MASTEROPACITY
@ QUERY_STYLE_PROPERTY_STROKECAP
@ QUERY_STYLE_PROPERTY_WRITINGMODES
@ QUERY_STYLE_MULTIPLE_DIFFERENT
@ QUERY_STYLE_SINGLE
@ QUERY_STYLE_NOTHING
@ QUERY_STYLE_MULTIPLE_AVERAGED
@ QUERY_STYLE_MULTIPLE_SAME
Editable view implementation.
static char const *const current
Definition dir-util.cpp:71
static char const *const parent
Definition dir-util.cpp:70
SPBlendMode filter_get_legacy_blend(SPObject *item)
Get if the filter have a < 1.0 blending filter @params: the item to get filtered blend.
SVG Gaussian blur filter effect.
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
SPItem * item
Interface for locally managing a current status message.
Raw stack of active status messages.
static R & anchor(R &r)
Increments the reference count of a anchored object.
Definition gc-anchored.h:92
int size
Ocnode * child[8]
Definition quantize.cpp:33
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition repr-css.cpp:67
void sp_repr_css_merge(SPCSSAttr *dst, SPCSSAttr *src)
Merges two SPCSSAttr's.
Definition repr-css.cpp:299
void sp_repr_css_set(Node *repr, SPCSSAttr *css, gchar const *attr)
Sets an attribute (e.g.
Definition repr-css.cpp:265
void sp_repr_css_set_property_double(SPCSSAttr *css, gchar const *name, double value)
Set a style property to a new float value (e.g.
Definition repr-css.cpp:224
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
void sp_repr_css_set_property_string(SPCSSAttr *css, char const *name, std::string const &value)
Set a style property to a standard string.
Definition repr-css.cpp:235
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
void sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
Set a style property to "inkscape:unset".
Definition repr-css.cpp:202
bool sp_repr_css_property_is_unset(SPCSSAttr *css, gchar const *name)
Returns true if a style property is present and its value is unset.
Definition repr-css.cpp:178
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition repr-css.cpp:191
Linear linear(double ax, double b)
GList * items
int num
Definition scribble.cpp:47
SPCSSAttr - interface for CSS Attributes.
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
SVG <hatch> implementation.
TODO: insert short description here.
SVG <pattern> implementation.
TODO: insert short description here.
TODO: insert short description here.
SVG <tref> implementation, see sp-tref.cpp.
TODO: insert short description here.
@ SP_TSPAN_ROLE_LINE
Definition sp-tspan.h:23
@ SP_CSS_FONT_VARIANT_LIGATURES_NORMAL
@ SP_CSS_FONT_VARIANT_NUMERIC_NORMAL
@ SP_CSS_BLEND_NORMAL
@ SP_STROKE_LINEJOIN_MITER
Definition style-enums.h:34
@ SP_CSS_ISOLATION_AUTO
SPCSSFontVariantCaps
@ SP_CSS_FONT_VARIANT_CAPS_NORMAL
@ SP_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL
@ SP_STROKE_LINECAP_BUTT
Definition style-enums.h:41
SPCSSFontVariantPosition
@ SP_CSS_FONT_VARIANT_POSITION_NORMAL
@ SP_FONT_SIZE_LENGTH
@ SP_CSS_UNIT_PX
@ SP_CSS_UNIT_PERCENT
@ SP_CSS_UNIT_NONE
@ SP_CSS_UNIT_EM
@ SP_CSS_UNIT_EX
SPCSSAttr * sp_css_attr_unset_text(SPCSSAttr *css)
Unset any text-related properties.
Definition style.cpp:1444
SPCSSAttr * sp_css_attr_scale(SPCSSAttr *css, double ex)
Scale any properties that may hold <length> by ex.
Definition style.cpp:1625
void sp_style_set_to_uri(SPStyle *style, bool isfill, Inkscape::URI const *uri)
Definition style.cpp:1298
SPCSSAttr * sp_css_attr_unset_uris(SPCSSAttr *css)
Unset any properties that contain URI values.
Definition style.cpp:1543
SPStyle - a style object for SPItem objects.
unsigned int sp_svg_number_read_f(gchar const *str, float *val)
SPDesktop * desktop
double width