izzi
SVG SUBSET C++ API
Loading...
Searching...
No Matches
a60-svg-elements.h
Go to the documentation of this file.
1// svg elements -*- mode: C++ -*-
2
3// Copyright (C) 2014-2025 Benjamin De Kosnik <b.dekosnik@gmail.com>
4
5// This file is part of the alpha60-MiL SVG library. This library is
6// free software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the Free
8// Software Foundation; either version 3, or (at your option) any
9// later version.
10
11// This library is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15
16#ifndef MiL_SVG_ELEMENTS_H
17#define MiL_SVG_ELEMENTS_H 1
18
19
20namespace svg {
21
22/**
23 @defgroup elements SVG Elements
24 @brief All svg components are derived from element_base as per
25 [hierarchy](https://bdekoz.github.io/izzi/html/structsvg_1_1element__base__inherit__graph_org.svg "svg elements")
26
27 @{
28*/
29
30/// Abstract base class for all SVG Elements.
32{
33 using stream_type = std::ostringstream;
34 static constexpr const char* finish_tag = " >";
35 static constexpr string finish_tag_hard = string(finish_tag) + k::newline;
36 static constexpr const char* self_finish_tag = " />";
37
38 // Underlying units for 2D (x,y) mapping (same as area::atype).
39 using atype = space_type; // ... floating point cartesian points
40 //using atype = ssize_type; // ... integer cartesian points
41
42 /// Virtual, only one buffer.
44
45 virtual void
47
48 virtual void
50
51 /// Empty when the output buffer is.
52 bool
53 empty() { return _M_sstream.str().empty(); }
54
55 string
56 str() const { return _M_sstream.str(); }
57
58 void
59 str(const string& s) { return _M_sstream.str(s); }
60
61 // Add sub element e to base object in non-visible defs section
62 void
64
65 // Add sub element e to base object
66 void
68 { _M_sstream << e.str(); }
69
70 void
71 add_fill(const string id)
72 {
73 _M_sstream << k::space;
74 _M_sstream << "fill=" << k::quote;
75 _M_sstream << "url(#" << id << ")" << k::quote;
76 }
77
78 void
79 add_filter(const string id)
80 {
81 _M_sstream << k::space;
82 _M_sstream << "filter=" << k::quote;
83 _M_sstream << "url(#" << id << ")" << k::quote;
84 }
85
86 // Add raw string to group; filter, blend/gradient elements.
87 void
88 add_raw(const string& raw)
89 { _M_sstream << k::space << raw; }
90
91 void
92 add_style(const style& sty)
93 { _M_sstream << to_string(sty); }
94
95 void
96 add_title(const string& t);
97
98 /// Common transforms include rotate(180)
99 string
101 {
102 std::ostringstream oss;
103 oss << k::space << "transform=" << k::quote << s << k::quote;
104 return oss.str();
105 }
106
107 void
108 add_transform(const string s)
109 {
110 if (!s.empty())
112 }
113};
114
115
116/**
117 Group SVG element.
118
119 Specification reference:
120 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
121
122 Attributes:
123 x, y, width, height, xlink:xref, preserveAspectRatio
124 */
125struct group_element : virtual public element_base
126{
127 static string
128 start_group(const string name)
129 {
130 string ret("<g");
131 if (!name.empty())
132 {
133 ret += " id=";
134 ret += k::quote;
135 ret += name;
136 ret += k::quote;
137 }
138 ret += ">";
139 ret += k::newline;
140 return ret;
141 }
142
143 static string
145 { return string("</g>") + k::newline; }
146
147
148
149 /// For groups of elements that have the same name.
150 ///
151 /// Also, although one can nest SVG elements inside another SVG by
152 /// using an 'image_element', the display quality is lacking in
153 /// inkscape. To work around this, insert the contents of the nested
154 /// SVG into a named group element instead.
155 void
158
159 void
160 start_element(string name)
161 { _M_sstream << start_group(name); }
162
163 // For groups of elements with style as specified.
164 void
165 start_element(string name, const style& sty)
166 {
167 _M_sstream << "<g id=" << k::quote << name << k::quote << k::space;
168 add_style(sty);
169 _M_sstream << '>' << k::newline;
170 }
171
172 // For groups of elements with a transform and style if specified.
173 void
174 start_element(string name, const string ts, const style& sty = k::no_style)
175 {
176 _M_sstream << "<g id=" << k::quote << name << k::quote;
177 add_transform(ts);
178
179 // Only add style if it is not the default argument.
180 const color_qi nklr(color::none);
181 const bool stylep = to_string(sty._M_fill_color) != to_string(nklr);
182 if (stylep)
183 add_style(sty);
184 _M_sstream << '>' << k::newline;
185 }
186
187 void
189};
190
191void
194
195
196/**
197 Definitions SVG element. Storage space for elements used later.
198
199 Specification reference:
200 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
201
202 Attributes:
203 id
204 */
205struct defs_element : virtual public element_base
206{
207 static string
209 { return "<defs>"; }
210
211 static string
213 { return "</defs>"; }
214
215 void
217 { _M_sstream << start_defs() << k::newline; }
218
219 void
221};
222
223void
226
227void
234
235
236/**
237 Link SVG element. a
238
239 Specification reference:
240 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/a
241
242 Attributes:
243 id
244 */
245struct link_element : virtual public element_base
246{
247 void
249 { _M_sstream << "<a "; }
250
251 void
253
254 void
255 add_data(const string& url)
256 {
257 _M_sstream << "href=" << k::quote << url << k::quote;
259 }
260
261 /// Overload for rel=x form.
262 /// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload
263 /// @param relt attribute that describes the relationship of the
264 /// target object to the link object
265 /// @param ast type of resource.
266 void
267 add_data(const string& url, const string relt, const string ast,
268 const string cors = "anonymous")
269 {
270 _M_sstream << "rel=" << k::quote << relt << k::quote << k::space;
271 _M_sstream << "href=" << k::quote << url << k::quote << k::space;
272 _M_sstream << "as=" << k::quote << ast << k::quote << k::space;
273 _M_sstream << "crossorigin=" << k::quote << cors << k::quote << k::space;
274 _M_sstream << "referrerpolicy=" << k::quote << "no-referrer" << k::quote;
275 _M_sstream << k::space;
277 }
278};
279
280void
282{ _M_sstream << "</a>" << k::newline; }
283
284
285/**
286 Datum consolidating filter use and preferences.
287
288 \verbatim
289 <filter id="gblur10" x="0" y="0">
290 <feGaussianBlur in="SourceGraphic" stdDeviation="10" />
291 <feOffset dx="0" dy="0" />
292 </filter>
293 \endverbatim
294*/
295struct filter_element : virtual public element_base
296{
306
307 void
309 { _M_sstream << "<filter>" << k::newline; }
310
311 void
312 start_element(const string id)
313 {
314 _M_sstream << "<filter id=" << k::quote << id << k::quote << ">"
315 << k::newline;
316 }
317
318 // Some filter effects get clipped when appied to an element's area,
319 // so this allows filters to have an element + filter area instead.
320 void
321 start_element(const string id, const area<> blur_area, const point_2t p)
322 {
323 auto [ width, height ] = blur_area;
324 auto [ x, y ] = p;
325 _M_sstream << "<filter id=" << k::quote << id << k::quote << k::space
326 << "x=" << k::quote << x << k::quote << k::space
327 << "y=" << k::quote << y << k::quote << k::space
328 << "width=" << k::quote << width << k::quote << k::space
329 << "height=" << k::quote << height << k::quote
330 << ">"
331 << k::newline;
332 }
333
334 void
336
337 void
338 add_data(const string fltr)
339 { _M_sstream << fltr; }
340
341 // https://drafts.fxtf.org/filter-effects/#elementdef-fegaussianblur
342 // in == SourceGraphic, SourceAlpha, FillPaint, StrokePaint
343 // dev == 1 or 1,1 (0 default if two then x, y direction)
344 string
345 gaussian_blur(string in, string dev)
346 {
347 // <feGaussianBlur in="SourceGraphic" stdDeviation="20" />
348 stream_type stream;
349 stream << "<feGaussianBlur in=";
350 stream << k::quote << in << k::quote << k::space;
351 stream << "stdDeviation=" << k::quote << dev << k::quote << k::space;
352 stream << "/>";
353 return stream.str();
354 }
355
356 string
357 gaussian_blur(string dev)
358 { return gaussian_blur("SourceGraphic", dev); }
359};
360
361void
363{ _M_sstream << "</filter>" << k::newline; }
364
365
366/**
367 Gradient SVG elements.
368
369 Specification reference:
370 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient
371 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient
372
373 Note these are always inside a defs block.
374
375 Attributes:
376 id
377 */
378struct gradient_element : virtual public defs_element
379{
387
388 void
391
392 void
394
395 // off == 10%
396 void
397 stop(const string off, const color& klr, const double opacity = 1.0)
398 {
399 _M_sstream << "<stop offset=" << k::quote << off << k::quote << k::space
400 << "stop-color=" << k::quote << to_string(klr) << k::quote;
401 if (opacity < 1.0)
402 {
403 _M_sstream << k::space
404 << "stop-opacity=" << k::quote << opacity << k::quote;
405 }
406 _M_sstream << " />";
407 _M_sstream << k::newline;
408 }
409
410 // Express two integers as a suitable offset string percentage.
411 const string
412 offset_percentage(const ssize_type numer, const ssize_type denom)
413 {
414 const double ratio = static_cast<double>(numer)/static_cast<double>(denom);
415 const ssize_type perc(round(ratio * 100));
416 return std::to_string(perc) + "%";
417 }
418};
419
420void
423
424
425/// Circular gradients
426/// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient
427struct radial_gradient : virtual public gradient_element
428{
429 void
431 {
433 _M_sstream << "<radialGradient id=" << k::quote << "default" << k::quote;
434 _M_sstream << ">" << k::newline;
435 }
436
437 // Radial gradient centered at (cx, cy) of radius.
438 // Default for radius, cx, cy is "50%"
439 void
440 start_element(const string id, const ssize_type radius = 0,
441 const ssize_type cx = 0, const ssize_type cy = 0)
442 {
444 _M_sstream << "<radialGradient id=" << k::quote << id << k::quote;
445 if (radius > 0)
446 {
447 _M_sstream << k::space << "r=" << k::quote << radius << k::quote;
448 _M_sstream << k::space << "cx=" << k::quote << cx << k::quote;
449 _M_sstream << k::space << "cy=" << k::quote << cy << k::quote;
450 }
451 _M_sstream << ">" << k::newline;
452 }
453
454 // Radial gradient.
455 // End circle (aka 100% stop) at (cx, cy) with radius.
456 // Start circle (aka 0% stop) at (fx, fy) with radius fr.
457 void
458 start_element(const string id, const ssize_type radius,
459 const ssize_type cx, const ssize_type cy, const ssize_type fr,
460 const ssize_type fx, const ssize_type fy)
461 {
463 _M_sstream << "<radialGradient id=" << k::quote << id << k::quote
464 << k::space << "r=" << k::quote << radius << k::quote;
465 _M_sstream << k::space << "cx=" << k::quote << cx << k::quote;
466 _M_sstream << k::space << "cy=" << k::quote << cy << k::quote;
467 if (fr > 0)
468 {
469 _M_sstream << k::space << "fx=" << k::quote << fx << k::quote;
470 _M_sstream << k::space << "fy=" << k::quote << fy << k::quote;
471 _M_sstream << k::space << "fr=" << k::quote << fr << k::quote;
472 }
473 _M_sstream << ">" << k::newline;
474 }
475
476 void
478};
479
480void
482{
483 _M_sstream << "</radialGradient>" << k::newline;
485}
486
487
488/// Linear gradients
489/// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient
490struct linear_gradient : virtual public gradient_element
491{
492 void
494 {
496 _M_sstream << "<linearGradient id=" << k::quote << "default" << k::quote;
497 _M_sstream << ">" << k::newline;
498 }
499
500 void
502};
503
504void
506{
507 _M_sstream << "</linearGradient>" << k::newline;
509}
510
511
512/**
513 Marker SVG elements defines a graphic used for drawing
514 arrow[heads, tails, mid] on a poly line or path.
515
516 Specification reference:
517 https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/marker-mid
518
519 Note these are always inside a defs block.
520
521 Attributes:
522 id
523 */
524struct marker_element : virtual public element_base
525{
526 void
528
529 // markerWidth="8" markerHeight="8" refX="4" refY="4">
530 void
531 start_element(const string id, const area<> a, const point_2t p)
532 {
533 auto [ x, y ] = p;
534 auto [ w, h ] = a;
535 _M_sstream << "<marker id=" << k::quote << id << k::quote << k::space
536 << "markerWidth=" << k::quote << w << k::quote << k::space
537 << "markerHeight=" << k::quote << h << k::quote << k::space
538 << "refX=" << k::quote << x << k::quote << k::space
539 << "refY=" << k::quote << y << k::quote << " >"
540 << k::newline;
541 }
542
543 void
545};
546
547void
549{
550 _M_sstream << "</marker>" << k::newline;
551}
552
553
554/**
555 Title SVG element. This is accessible/alt text.
556 This element must be the first element in the svg objectc.
557
558 A title element with no string indicates a decorative element to AT
559 screenreaders.
560
561 Specification reference:
562 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
563*/
564struct title_element : virtual public element_base
565{
566 void
568 { _M_sstream << "<title>" << k::newline; }
569
570 void
571 start_element(const string t)
572 {
574 _M_sstream << t << k::newline;
575 }
576
577 void
579};
580
581void
583{ _M_sstream << "</title>" << k::newline; }
584
585
586/**
587 Description SVG element.
588
589 Specification reference:
590 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc
591*/
592struct desc_element : virtual public element_base
593{
594 void
596
597 void
598 start_element(const string dsc)
599 { _M_sstream << "<desc>" << k::newline << dsc << k::newline; }
600
601 void
603};
604
605void
607{ _M_sstream << "</desc>" << k::newline; }
608
609
610/**
611 Text SVG element.
612
613 Specification reference:
614 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/text
615
616 Attributes:
617 x, y, dx, dy, text-anchor, rotate, textLength, lengthAdjust
618*/
619struct text_element : virtual public element_base
620{
628
629 /// Either serialize immediately (as below), or create data structure
630 /// that adds data to data_vec and then finish_element serializes.
631 void
632 add_data(const data& d,
633 const string trans = "", const unit utype = svg::unit::point)
634 {
635 const string x("__x");
636 const string y("__y");
637 const string attr("__attr");
638 const string style("__style");
639
640 string strip = R"_delimiter_(x="__x" y="__y" __attr __style)_delimiter_";
641
642 // Add attributes.
643 string_replace(strip, x, std::to_string(d._M_x_origin));
644 string_replace(strip, y, std::to_string(d._M_y_origin));
645 string_replace(strip, attr, d._M_typo.add_attribute(utype));
647 _M_sstream << strip;
648 add_transform(trans);
649 _M_sstream << '>';
650
651 // Add text data.
652 add_raw(d._M_text);
653 }
654
655 void
657 { _M_sstream << "<text "; }
658
659 void
661
662 /// For text list output, use tspan for line breaks. This span
663 /// creates a new horizontal line for every tspan block, starting at
664 /// xpos with spacing dy (1.2em).
665 static string
666 start_tspan_y(uint xpos, string dy)
667 {
668 const string x("__x");
669 const string dys("__dy");
670 string strip = R"_delimiter_(<tspan x="__x" dy="__dy">)_delimiter_";
671 string_replace(strip, x, std::to_string(xpos));
672 string_replace(strip, dys, dy);
673 return strip;
674 }
675
676 static string
678 { return start_tspan_y(xpos, std::to_string(dy)); }
679
680 /// For text list output, use tspan for line breaks. This span
681 /// creates a new vertical line space for every tspan block, starting
682 /// at xpos with horizontal spacing dx ("1.4em").
683 static string
684 start_tspan_x(uint xpos, string dx)
685 {
686 const string x("__x");
687 const string dxs("__dx");
688 string strip = R"_delimiter_(<tspan x="__x" dx="__dx">)_delimiter_";
689 string_replace(strip, x, std::to_string(xpos));
690 string_replace(strip, dxs, dx);
691 return strip;
692 }
693
694 static string
696 { return start_tspan_x(xpos, std::to_string(dx)); }
697
698 static string
700 { return "</tspan>"; }
701};
702
703void
705{ _M_sstream << "</text>" << k::newline; }
706
707
708/// Make text span.
709string
710make_tspan_y_from_string_by_token(string s, uint xpos, const char token = ' ')
711{
712 string start(text_element::start_tspan_y(xpos, "0.5em"));
713 string ret = start;
714 for (uint i = 0; i < s.size(); ++i)
715 {
716 const char c = s[i];
717 if (c != token)
718 ret += c;
719 else
720 {
722 if (i < s.size() - 1)
723 ret += start;
724 }
725 }
727 return ret;
728}
729
730
731/**
732 Rectangle SVG element.
733
734 Specification reference:
735 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect
736
737 Attributes:
738 x, y, width, height, rx, ry
739 */
740struct rect_element : virtual public element_base
741{
749
750 // Verbose opening/closing pair tags for circle_element.
751 // Default assumes the more compact XML "self-closed tag" for circle element.
752 static constexpr const char* pair_open_tag = "<rect>";
753 static constexpr const char* pair_finish_tag = "</rect>";
754
755 /// Either serialize immediately (as below), or create data structure
756 /// that adds data to data_vec and then finish_element serializes.
757 void
758 add_data(const data& d)
759 {
760 const string x("__x");
761 const string y("__y");
762 const string w("__w");
763 const string h("__h");
764
765 string strip = R"_delimiter_(x="__x" y="__y" width="__w" height="__h"
766)_delimiter_";
767
768 string_replace(strip, x, std::to_string(d._M_x_origin));
769 string_replace(strip, y, std::to_string(d._M_y_origin));
770 string_replace(strip, w, std::to_string(d._M_width));
771 string_replace(strip, h, std::to_string(d._M_height));
772 _M_sstream << strip;
773 }
774
775 void
777 { _M_sstream << "<rect "; }
778
779 void
781};
782
783void
786
787
788/**
789 Circle SVG element.
790
791 Specification reference:
792 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle
793
794 Attributes:
795 x, y, width, height, xlink:xref, preserveAspectRatio
796 */
797struct circle_element : virtual public element_base
798{
805
806 // Verbose opening/closing pair tags for circle_element.
807 // Default assumes the more compact XML "self-closed tag" for circle element.
808 static constexpr const char* pair_open_tag = "<circle>";
809 static constexpr const char* pair_finish_tag = "</circle>";
810
811 // Either serialize immediately (as below), or create data structure
812 // that adds data to data_vec and then finish_element serializes.
813 void
814 add_data(const data& d, string trans = "")
815 {
816 const string x("__x");
817 const string y("__y");
818 const string r("__r");
819
820 string strip = R"_delimiter_(cx="__x" cy="__y" r="__r")_delimiter_";
821
822 string_replace(strip, x, std::to_string(d._M_x_origin));
823 string_replace(strip, y, std::to_string(d._M_y_origin));
824 string_replace(strip, r, std::to_string(d._M_radius));
825 _M_sstream << strip;
826 add_transform(trans);
827 }
828
829 void
831 { _M_sstream << "<circle "; }
832
833 void
835
836};
837
838void
841
842
843/**
844 Line SVG element.
845
846 Specification reference:
847 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line
848
849 Attributes:
850 x, y, width, height, xlink:xref, preserveAspectRatio
851*/
852struct line_element : virtual public element_base
853{
861
862 // Either serialize immediately (as below), or create data structure
863 // that adds data to data_vec and then finish_element serializes.
864 void
865 add_data(const data& d, const string dasharray = "")
866 {
867 const string x1("__x1");
868 const string x2("__x2");
869 const string y1("__y1");
870 const string y2("__y2");
871 const string dash("__darray");
872
873 const bool dashp = !dasharray.empty();
874 string stripf = \
875 R"_delimiter_(x1="__x1" y1="__y1" x2="__x2" y2="__y2")_delimiter_";
876 string stript = \
877 R"_delimiter_(x1="__x1" y1="__y1" x2="__x2" y2="__y2" stroke-dasharray="__darray")_delimiter_";
878
879 string strip = dashp ? stript : stripf;
880 string_replace(strip, x1, std::to_string(d._M_x_begin));
881 string_replace(strip, x2, std::to_string(d._M_x_end));
882 string_replace(strip, y1, std::to_string(d._M_y_begin));
883 string_replace(strip, y2, std::to_string(d._M_y_end));
884
885 if (dashp)
886 string_replace(strip, dash, dasharray);
887 _M_sstream << strip;
888 }
889
890 void
892 { _M_sstream << "<line "; }
893
894 void
895 start_element(string name)
896 { _M_sstream << "<line id=" << k::quote << name << k::quote << k::space; }
897
898 void
900};
901
902void
905
906
907/**
908 Polyline SVG element.
909
910 Specification reference:
911 https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/polyline
912
913 Attributes:
914 points, pathLength
915*/
916struct polyline_element : virtual public element_base
917{
918 // vector<point_2t> == vector<tuple<double,double>>
920
922
923 polyline_element(const vrange& points) : polypoints(points) { }
924
925 // Either serialize immediately (as below), or create data structure
926 // that adds data to data_vec and then finish_element serializes.
927 void
928 add_data(const stroke_style& sstyl)
929 {
930 if (!polypoints.empty())
931 {
932 _M_sstream << "points=" << k::quote;
933 for (const point_2t& pt : polypoints)
934 {
935 auto [ x, y ] = pt;
936 _M_sstream << x << k::comma << y << k::space;
937 }
938 _M_sstream << k::quote << k::space;
939
940 if (!sstyl.dasharray.empty())
941 {
942 _M_sstream << "stroke-dasharray=" << k::quote;
943 _M_sstream << sstyl.dasharray << k::quote << k::space;
944 }
945 if (!sstyl.dashoffset.empty())
946 {
947 _M_sstream << "stroke-dashoffset=" << k::quote;
948 _M_sstream << sstyl.dashoffset << k::quote << k::space;
949 }
950 if (!sstyl.linecap.empty())
951 {
952 _M_sstream << "stroke-linecap=" << k::quote;
953 _M_sstream << sstyl.linecap << k::quote << k::space;
954 }
955 if (!sstyl.markerspoints.empty())
956 {
957 string mkr;
958 mkr += k::quote;
959 mkr += "url(#";
960 mkr += sstyl.markerspoints;
961 mkr += ")";
962 mkr += k::quote;
963
964 _M_sstream << "marker-mid=" << mkr << k::space;
965 _M_sstream << "marker-end=" << mkr << k::space;
966 }
967 }
968 }
969
970 void
972 { _M_sstream << "<polyline "; }
973
974 void
975 start_element(string name)
976 { _M_sstream << "<polyline id=" << k::quote << name << k::quote << k::space; }
977
978 void
980};
981
982void
985
986
987
988/**
989 Path SVG element.
990
991 Specification reference:
992 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
993
994 Attributes:
995 d, pathLength
996*/
997struct path_element : virtual public element_base
998{
999 struct data
1000 {
1001 string _M_d;
1003 };
1004
1005 static constexpr const char* pair_finish_tag = "</path>";
1006
1007 /// Either serialize immediately (as below), or create data structure
1008 /// that adds data to data_vec and then finish_element serializes.
1009 void
1010 add_data(const data& d)
1011 {
1012 const string pathd("__d");
1013 const string len("__l");
1014
1015 string strip = R"_delimiter_(d="__d")_delimiter_";
1016
1017 string_replace(strip, pathd, d._M_d);
1018 string_replace(strip, len, std::to_string(d._M_length));
1019 _M_sstream << strip;
1020 }
1021
1022 void
1024 { _M_sstream << "<path "; }
1025
1026 void
1027 start_element(const string name)
1028 { _M_sstream << "<path id=" << k::quote << name << k::quote << k::space; }
1029
1030 void
1032};
1033
1034void
1040
1041
1042/**
1043 Text on a Path SVG element.
1044
1045 Specification reference:
1046 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/textPath
1047
1048 Attributes:
1049 href, path, method, side, spacing, startOffset, textLength, lengthAdjust
1050*/
1051struct text_path_element : virtual public text_element
1052{
1054 string offset; // "30%"
1055 string side; // "left" || "right" (use convex/concave side of path)
1056
1057 text_path_element(const string name,
1058 const string off = "", const string whichside = "")
1059 : path_name(name), offset(off), side(whichside) { }
1060
1061 virtual void
1062 add_text(string txt)
1063 {
1064 // Start text_path_element...
1065 _M_sstream << "<textPath xlink:href="
1066 << k::quote << '#' << path_name << k::quote;
1067 if (!offset.empty())
1068 _M_sstream << k::space << "startOffset="
1069 << k::quote << offset << k::quote;
1070 if (!side.empty())
1071 _M_sstream << k::space << "side="
1072 << k::quote << side << k::quote;
1073 _M_sstream << '>';
1074
1075 _M_sstream << txt;
1076
1077 // End text_path_element...
1078 _M_sstream << "</textPath>" << k::space;
1079 }
1080};
1081
1082
1083
1084/**
1085 Image SVG element. This can be another SVG file, or can be a raster
1086 image format like PNG or JPEG.
1087
1088 Specification reference:
1089 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
1090
1091 Attributes:
1092 x, y, width, height, xlink:xref, preserveAspectRatio
1093 */
1094struct image_element : virtual public element_base
1095{
1104
1105 void
1106 start_element(const string& id)
1107 {
1108 const string simg = "<image";
1109 _M_sstream << simg << k::space;
1110 if (!id.empty())
1111 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1112 }
1113
1114 void
1117
1118 void
1120
1121 /// Either serialize immediately (as below), or create data structure
1122 /// that adds data to data_vec and then finish_element serializes.
1123 void
1124 add_data(const data& d)
1125 {
1126 const string x("__x");
1127 const string y("__y");
1128 const string w("__w");
1129 const string h("__h");
1130 const string ref("__ref");
1131
1132 string strip = R"_delimiter_(href="__ref" x="__x" y="__y" width="__w" height="__h")_delimiter_";
1133
1134 string_replace(strip, ref, d._M_xref);
1135 string_replace(strip, x, std::to_string(d._M_x_origin));
1136 string_replace(strip, y, std::to_string(d._M_y_origin));
1137 string_replace(strip, w, std::to_string(d._M_width));
1138 string_replace(strip, h, std::to_string(d._M_height));
1139 _M_sstream << strip << k::space;
1140 }
1141
1142 /// Visibility and other HTML/img attributes.
1143 /// @param vattr = visibility attribute, "visible" or "hidden"
1144 /// @param display = display attribute, "none" or "unset" or "initial"
1145 /// @param cors = CORS, "anonymous" or "use-credentials"
1146 /// @param lattr = loading attribute, "lazy" or "eager"
1147 void
1148 add_data(const data& d, const string cors, const string vattr,
1149 const string display = "")
1150 {
1151 add_data(d);
1152
1153 if (!cors.empty())
1154 _M_sstream << "crossorigin=" << k::quote << cors << k::quote << k::space;
1155 if (!vattr.empty())
1156 _M_sstream << "visibility=" << k::quote << vattr << k::quote << k::space;
1157 if (!display.empty())
1158 _M_sstream << "display=" << k::quote << display << k::quote << k::space;
1159 }
1160};
1161
1162
1163void
1166
1167
1168/**
1169 So-called Foreign Objects.
1170
1171 Using to get HTML video elements.
1172
1173 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject
1174 https://stackoverflow.com/questions/40324916/video-tag-embedded-in-svg
1175
1176Translate moves the origin from the top left to the specified
1177coordinates. If you embed an object at 0,0 it will be placed at the
1178new origin. In this case you must embed it at -translation
1179coordinates.
1180
1181Even so, I had to increase the width and height. Why? I don't know. It
1182doesn't seem to be a scale by 2. If someone knows I am curious to
1183know.
1184
1185<svg version="1.1" class="center-block" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="600" style="border: 1px solid black;">
1186 <g>
1187 <g transform="translate(151,104) scale(1,1)">
1188 <rect x="0" y="0" width="300" height="200"></rect>
1189 <foreignObject x="-151" y="-104" width="500" height="400">
1190 <video xmlns="http://www.w3.org/1999/xhtml" width="300" height="200" controls="" style="position: fixed; left: 151px; top: 104px;">
1191 <source src="http://techslides.com/demos/sample-videos/small.mp4" type="video/mp4" />less
1192 </video>
1193 </foreignObject>
1194 </g>
1195 </g>
1196</svg>
1197*/
1198struct foreign_element : virtual public element_base
1199{
1200 void
1202 { _M_sstream << "<g>" << k::newline; }
1203
1204 // av == area of the foreign object and native video frame size
1205 // arect == area of displayed video as embedded inside svg element /aka page
1206 // scale_pair == x/y scaling factor
1207 void
1208 start_element(const point_2t origin, const area<> av, const area<> /*arect*/,
1209 const point_2t scale = std::make_tuple(1.0, 1.0))
1210 {
1211 const auto [ scalex, scaley ] = scale;
1212 const auto [ ox, oy ] = origin;
1213
1214 //const auto [ width, height ] = arect;
1215 //auto xo = width/2;
1216 //auto yo = height/2;
1217
1218 const auto [ vwidth, vheight ] = av;
1219
1220 // Outer group.
1221 group_element go;
1222 go.start_element();
1223 _M_sstream << go.str() << k::newline;
1224
1225 // Inner Group.
1226 group_element gi;
1227 //string tx = transform::translate(xo, yo);
1228 string tscl = transform::scale(scalex, scaley);
1229 gi.start_element(string("video-wrapper"), tscl);
1230 _M_sstream << gi.str() << k::newline;
1231
1232 // Foreign Object
1233 string strip = R"(<foreignObject x="XXX" y="YYY" width="WWW" height="HHH">)";
1234 string_replace(strip, "WWW", std::to_string(vwidth));
1235 string_replace(strip, "HHH", std::to_string(vheight));
1236 string_replace(strip, "XXX", std::to_string(ox));
1237 string_replace(strip, "YYY", std::to_string(oy));
1238 _M_sstream << strip << k::newline;
1239 }
1240
1241 void
1243};
1244
1245void
1247{ _M_sstream << " </foreignObject></g></g>" << k::newline; }
1248
1249
1250/// video HTML object embedded in SVG container.
1251/// NB: HTML elements video/audio/iframe/canvas can be used w/o foreignElement.
1252/// This approach uses HTML wrapped in foreign element.
1253/// https://www.w3.org/TR/SVG2/embedded.html#HTMLElements
1254/// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video
1255struct video_element : virtual public foreign_element
1256{
1257 void
1258 start_element(const string& id)
1259 {
1260 //const string svideo = "<html:video";
1261 const string svideo = R"(<video xmlns="http://www.w3.org/1999/xhtml" )";
1262 _M_sstream << svideo << k::space;
1263 if (!id.empty())
1264 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1265 }
1266
1267 void
1270
1271 /// Video.
1272 /// a is width and height of video as embedded in page
1273 /// r is the foreign object, with x/y offset and scaled size
1274 ///
1275 /// attr is attribues for video_element
1276 /// autoplay="true" or removed
1277 /// loop="true/false"
1278 /// muted="true/false"
1279 /// controls, controlslist,
1280 /// crossorigin, disablepictureinpicture, disableremoteplayback
1281 ///
1282 void
1283 add_data(const area<> a, const string src, const string mtype = "video/mp4",
1284 const string attr = R"(autoplay="true" loop="true" muted="true")")
1285 {
1286 string strip = R"(width="WWW" height="HHH" )";
1287 string_replace(strip, "WWW", std::to_string(a._M_width));
1288 string_replace(strip, "HHH", std::to_string(a._M_height));
1289 _M_sstream << k::space;
1290 _M_sstream << strip << k::space;
1291 _M_sstream << attr << k::space;
1293
1294 _M_sstream << "<source src=" << k::quote << src << k::quote << k::space;
1295 _M_sstream << "type=" << k::quote << mtype << k::quote;
1297 }
1298
1299 void
1301};
1302
1303void
1305{ _M_sstream << "</video>" << k::newline; }
1306
1307
1308/// iframe HTML object embedded in SVG container.
1309/// NB: HTML elements video/audio/iframe/canvas can be used w/o foreignElement.
1310/// This approach uses HTML wrapped in foreign element.
1311/// https://www.w3.org/TR/SVG2/embedded.html#HTMLElements
1312/// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
1313struct iframe_element : virtual public foreign_element
1314{
1315 void
1316 start_element(const string& id)
1317 {
1318 //const string siframe = "<html:iframe";
1319 const string siframe = R"(<iframe xmlns="http://www.w3.org/1999/xhtml" )";
1320 _M_sstream << siframe << k::space;
1321 if (!id.empty())
1322 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1323 }
1324
1325 void
1328
1329 /// iframe.
1330 /// a is width and height of video as embedded in page
1331 /// r is the foreign object, with x/y offset and scaled size
1332 ///
1333 void
1334 add_data(const area<> a, const string src, const string mtype = "image/jpeg",
1335 const string attr = R"(sandbox="allow-scripts allow-same-origin")")
1336 {
1337 string strip = R"(width="WWW" height="HHH" )";
1338 string_replace(strip, "WWW", std::to_string(a._M_width));
1339 string_replace(strip, "HHH", std::to_string(a._M_height));
1340 _M_sstream << k::space;
1341 _M_sstream << strip << k::space;
1342 // _M_sstream << "src=" << k::quote << src << k::quote << k::space;
1343 _M_sstream << attr;
1345
1346 // image/webp or image/jpeg
1347 _M_sstream << "<source src=" << k::quote << src << k::quote << k::space;
1348 _M_sstream << "type=" << k::quote << mtype << k::quote << k::space;
1350 }
1351
1352 void
1354};
1355
1356void
1358{ _M_sstream << "</iframe>" << k::newline; }
1359
1360
1361/// HTML object embedded in SVG container.
1362/// Unlike image_elements, object_elements are not locked down for scripting.
1363/// NB: HTML elements video/audio/object/canvas can be used w/o foreignElement.
1364/// This approach uses HTML wrapped in foreign element.
1365/// https://www.w3.org/TR/SVG2/embedded.html#HTMLElements
1366/// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object
1367struct object_element : virtual public foreign_element
1368{
1369 void
1370 start_element(const string& id)
1371 {
1372 //const string sobject = "<html:object";
1373 const string sobject = R"(<object xmlns="http://www.w3.org/1999/xhtml" )";
1374 _M_sstream << sobject << k::space;
1375 if (!id.empty())
1376 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1377 }
1378
1379 void
1382
1383 /// Add resource to object.
1384 /// @param a is width and height of video as embedded in page
1385 /// @param src is the resource URL
1386 /// @param mtype is the MIME type
1387 /// @param attr is any collection of ad-hoc HTML attributes.
1388 void
1389 add_data(const area<> a, const string src, const string mtype = "image/jpeg",
1390 const string attr = R"(sandbox="allow-scripts allow-same-origin")")
1391 {
1392 string strip = R"(width="WWW" height="HHH" )";
1393 string_replace(strip, "WWW", std::to_string(a._M_width));
1394 string_replace(strip, "HHH", std::to_string(a._M_height));
1395 _M_sstream << k::space;
1396 _M_sstream << strip << k::space;
1397 _M_sstream << "data=" << k::quote << src << k::quote << k::space;
1398 _M_sstream << "type=" << k::quote << mtype << k::quote << k::space;
1399 _M_sstream << attr << k::space;
1401 }
1402
1403 void
1405};
1406
1407void
1409{ _M_sstream << "</object>" << k::newline; }
1410
1411
1412/**
1413 A SVG script element.
1414
1415 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
1416*/
1417struct script_element : virtual public element_base
1418{
1419 /// Where is the script element placed? On/within the element
1420 /// itself, or at the document (global)? Neither (none),
1421 /// alongside/same level (parent)?
1422 enum class scope { none, document, parent, element };
1423
1424 void
1425 start_element(const string& id)
1426 {
1427 const string shead = R"(<script type="text/javascript" crossorigin="anonymous")";
1428 _M_sstream << shead << k::space;
1429 if (!id.empty())
1430 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1432 }
1433
1434 void
1437
1438 /// showTooltip(id)
1439 /// hideTooltip(id)
1440 /// event.x vs. event.pageX, event.y vs. event.pageY
1441 static const string&
1443 {
1444 static string js_show_element = R"(
1445 function showTooltip(event, tooltipId) {
1446 const tooltipimg = document.getElementById(tooltipId);
1447 if (tooltipimg) {
1448 const ge = tooltipimg.parentElement;
1449 const svge = ge.parentElement;
1450 const brect = ge.getBoundingClientRect();
1451 const bx = brect.left;
1452 const by = brect.top;
1453
1454 tooltipimg.setAttribute('x', event.x + bx);
1455 tooltipimg.setAttribute('y', event.y + by);
1456
1457 tooltipimg.setAttribute('visibility', 'visible');
1458 } else {
1459 console.error(`Element with ID "${tooltipId}" not found.`);
1460 }
1461 })";
1462
1463 static string js_show_document = R"(
1464 function showTooltip(event, tooltipId) {
1465 const tooltipimg = document.getElementById(tooltipId);
1466 if (tooltipimg) {
1467 //tooltipimg.onload = function() {
1468 const ge = tooltipimg.parentElement;
1469 const svge = ge.parentElement;
1470 const brect = ge.getBoundingClientRect();
1471 const bx = brect.left;
1472 const by = brect.top;
1473
1474 //const iheight = 150;
1475 const iheight = tooltipimg.offsetHeight; //!isNaN(iheight)
1476 tooltipimg.setAttribute('x', event.pageX - bx);
1477 tooltipimg.setAttribute('y', event.pageY - by - iheight);
1478 tooltipimg.setAttribute('visibility', 'visible');
1479 //tooltipimg.setAttribute('display', 'inline');
1480 } else {
1481 console.error(`Element with ID "${tooltipId}" not found.`);
1482 }
1483 })";
1484
1485 static string js_hide = R"(
1486 function hideTooltip(tooltipId) {
1487 const tooltipimg = document.getElementById(tooltipId);
1488 tooltipimg.setAttribute('visibility', 'hidden');
1489 //tooltipimg.setAttribute('display', 'none');
1490 })";
1491
1492 static string js;
1493 if (context == scope::element)
1494 js = js_show_element + k::newline + js_hide;
1495 if (context == scope::document || context == scope::parent)
1496 js = js_show_document + k::newline + js_hide;
1497
1498 return js;
1499 }
1500
1501 static const string
1502 tooltip_attribute(const string& id)
1503 {
1504 const string toolr("__toolt");
1505 string strip1 = R"_delimiter_(onmouseover="showTooltip(event, '__toolt')" )_delimiter_";
1506 string strip2 = R"_delimiter_(onmouseout="hideTooltip('__toolt')" )_delimiter_";
1507 string_replace(strip1, toolr, id);
1508 string_replace(strip2, toolr, id);
1509 return k::space + strip1 + k::space + strip2;
1510 }
1511
1512 // Script element for js to control visibility of images.
1513 static const script_element
1514 tooltip_script(const scope context)
1515 {
1516 script_element scrpt;
1517 scrpt.start_element("tooltip-js");
1519 scrpt.finish_element();
1520 return scrpt;
1521 }
1522
1523
1524 /// Add string with script source.
1525 /// @param scriptstr script source
1526 void
1527 add_data(const string scriptstr)
1528 {
1529 _M_sstream << scriptstr;
1530 _M_sstream << k::newline;
1531 }
1532
1533 void
1535};
1536
1537void
1539{ _M_sstream << "</script>" << k::newline; }
1540
1541
1542/**
1543 A SVG object element.
1544
1545 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
1546 https://developer.mozilla.org/en-US/docs/Web/SVG/SVG_as_an_Image
1547*/
1548struct svg_element : virtual public element_base
1549{
1551
1552 const string _M_name;
1556 const bool _M_lifetime; // scope document scope element
1557
1558 svg_element(const string __title, const area& __cv,
1559 const bool lifetime = true,
1560 const unit u = svg::unit::pixel,
1561 const typography& __typo = k::smono_typo)
1562 : _M_name(__title), _M_area(__cv), _M_unit(u),
1563 _M_typo(__typo), _M_lifetime(lifetime)
1564 {
1565 if (_M_lifetime)
1566 start();
1567 }
1568
1569 svg_element(const string __title, const string desc, const area& __cv,
1570 const bool lifetime = true)
1571 : _M_name(__title), _M_area(__cv), _M_unit(svg::unit::pixel),
1572 _M_typo(svg::k::smono_typo), _M_lifetime(lifetime)
1573 {
1574 if (_M_lifetime)
1575 start(desc);
1576 }
1577
1579 : _M_name(other._M_name), _M_area(other._M_area),
1580 _M_unit(other._M_unit), _M_typo(other._M_typo),
1582 { }
1583
1585 {
1586 if (_M_lifetime)
1587 finish();
1588 }
1589
1590 const point_2t
1592 { return _M_area.center_point(); }
1593
1594 void
1595 start_element();
1596
1597 void
1598 start_element(const point_2t p, const area destarea,
1599 const style& sty = k::no_style);
1600
1601 void
1603
1604 void
1607
1608 void
1609 add_desc(const string desc)
1610 {
1611 desc_element de;
1612 de.start_element(desc);
1613 de.finish_element();
1614 add_element(de);
1615 }
1616
1617 void
1619
1620 void
1621 write();
1622
1623 void
1624 start(const string& desc = "")
1625 {
1626 this->start_element();
1627 this->add_title();
1628 if (!desc.empty())
1629 this->add_desc(desc);
1630 }
1631
1632 void
1633 finish(const bool writep = true)
1634 {
1635 this->finish_element();
1636 if (writep)
1637 this->write();
1638 }
1639};
1640/// @} group elements
1641
1642} // namespace svg
1643
1644#endif
void start_element(const string dsc)
static string start_tspan_x(uint xpos, uint dx)
void add_data(const string &url)
void start_element(string name, const string ts, const style &sty=k::no_style)
void add_data(const stroke_style &sstyl)
string gaussian_blur(string in, string dev)
static constexpr const char * pair_open_tag
void add_filter(const string id)
void start_element(const string id, const ssize_type radius=0, const ssize_type cx=0, const ssize_type cy=0)
void add_data(const data &d)
Either serialize immediately (as below), or create data structure that adds data to data_vec and then...
stream_type _M_sstream
Virtual, only one buffer.
void start_element(const string &id)
void start_element(const string id, const ssize_type radius, const ssize_type cx, const ssize_type cy, const ssize_type fr, const ssize_type fx, const ssize_type fy)
static const script_element tooltip_script(const scope context)
void add_style(const style &sty)
virtual void add_text(string txt)
void str(const string &s)
void start_element(const string id)
void add_data(const data &d, const string dasharray="")
svg_element(const string __title, const area &__cv, const bool lifetime=true, const unit u=svg::unit::pixel, const typography &__typo=k::smono_typo)
void add_data(const string fltr)
void add_data(const area<> a, const string src, const string mtype="image/jpeg", const string attr=R"(sandbox="allow-scripts allow-same-origin")")
iframe. a is width and height of video as embedded in page r is the foreign object,...
const string offset_percentage(const ssize_type numer, const ssize_type denom)
void start_element(const string id, const area<> blur_area, const point_2t p)
static const string & tooltip_javascript(const scope context)
showTooltip(id) hideTooltip(id) event.x vs. event.pageX, event.y vs. event.pageY
void start_element(const point_2t origin, const area<> av, const area<>, const point_2t scale=std::make_tuple(1.0, 1.0))
void add_desc(const string desc)
text_path_element(const string name, const string off="", const string whichside="")
void add_data(const area<> a, const string src, const string mtype="image/jpeg", const string attr=R"(sandbox="allow-scripts allow-same-origin")")
Add resource to object.
static string finish_tspan()
void start_element(const string &id)
void add_data(const data &d, string trans="")
void add_transform(const string s)
void start_element()
For groups of elements that have the same name.
void start_element(string name)
static string finish_defs()
static constexpr const char * pair_finish_tag
static string finish_group()
const point_2t center_point()
void add_data(const area<> a, const string src, const string mtype="video/mp4", const string attr=R"(autoplay="true" loop="true" muted="true")")
Video. a is width and height of video as embedded in page r is the foreign object,...
static constexpr const char * self_finish_tag
void add_data(const string scriptstr)
Add string with script source.
void add_title(const string &t)
void add_data(const data &d)
Either serialize immediately (as below), or create data structure that adds data to data_vec and then...
void start_element(string name, const style &sty)
void start_element()
SVG element beginning boilerplate for outermost (containing) svg_element. Variable: unit,...
void stop(const string off, const color &klr, const double opacity=1.0)
static string start_defs()
string gaussian_blur(string dev)
static const string tooltip_attribute(const string &id)
void start_element(const point_2t p, const area destarea, const style &sty=k::no_style)
static constexpr const char * finish_tag
static string start_tspan_y(uint xpos, string dy)
For text list output, use tspan for line breaks. This span creates a new horizontal line for every ts...
void add_data(const data &d, const string trans="", const unit utype=svg::unit::point)
Either serialize immediately (as below), or create data structure that adds data to data_vec and then...
static string start_tspan_y(uint xpos, uint dy)
static constexpr const char * pair_finish_tag
virtual void start_element()=0
void start_element(const string &id)
svg_element(const string __title, const string desc, const area &__cv, const bool lifetime=true)
static string start_group(const string name)
void start_element(string name)
void add_data(const data &d, const string cors, const string vattr, const string display="")
Visibility and other HTML/img attributes.
static string start_tspan_x(uint xpos, string dx)
For text list output, use tspan for line breaks. This span creates a new vertical line space for ever...
std::ostringstream stream_type
static constexpr const char * pair_finish_tag
void add_data(const data &d)
Either serialize immediately (as below), or create data structure that adds data to data_vec and then...
void start_element(const string &id)
const typography & _M_typo
void add_raw(const string &raw)
void start_element(const string name)
scope
Where is the script element placed? On/within the element itself, or at the document (global)?...
virtual void finish_element()=0
bool empty()
Empty when the output buffer is.
void add_fill(const string id)
void start_element(const string id, const area<> a, const point_2t p)
void add_data(const string &url, const string relt, const string ast, const string cors="anonymous")
Overload for rel=x form. https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/p...
void start(const string &desc="")
string str() const
void add_element(const element_base &e)
void start_element(const string t)
svg::area< atype > area
string make_transform_attribute(const string s)
Common transforms include rotate(180)
void finish(const bool writep=true)
static constexpr const char * pair_open_tag
void start_element(const string &id)
static constexpr string finish_tag_hard
polyline_element(const vrange &points)
svg_element(const svg_element &other)
void start_element(string name)
string make_tspan_y_from_string_by_token(string s, uint xpos, const char token=' ')
Make text span.
void store_element(const element_base &e)
Abstract base class for all SVG Elements.
iframe HTML object embedded in SVG container. NB: HTML elements video/audio/iframe/canvas can be used...
Linear gradients https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient.
HTML object embedded in SVG container. Unlike image_elements, object_elements are not locked down for...
Circular gradients https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient.
video HTML object embedded in SVG container. NB: HTML elements video/audio/iframe/canvas can be used ...
color
Color enumerated as types.
unit
Measurement abstraction for absolute (not relative) measurements.
@ pt
Point where 1 pixel x 1/72 dpi x 96 PPI = .26 mm.
int ssize_type
Definition a60-svg.h:59
const string to_string(const unit e)
void string_replace(std::string &target, const std::string &match, const std::string &replace)
Definition a60-svg.h:43
double space_type
Base floating point type.
Definition a60-svg.h:62
std::vector< point_2t > vrange
Definition izzi-points.h:46
unsigned int uint
Definition a60-svg.h:57
std::tuple< space_type, space_type > point_2t
Point (x,y) in 2D space, space_type defaults to double.
Definition izzi-points.h:22
Color quantified as integral RGB components in the range [0,255]. aka like Scalar in OpenCV.
Additional path/line/polyline stroke styles.
Datum consolidating style preferences.
static string scale(double factor)
const std::string add_attribute(const svg::unit utype=svg::unit::pixel) const