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 void
100 add_transform(const string s)
101 {
102 if (!s.empty())
103 _M_sstream << k::space << "transform=" << k::quote << s << k::quote;
104 }
105};
106
107
108/**
109 Group SVG element.
110
111 Specification reference:
112 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
113
114 Attributes:
115 x, y, width, height, xlink:xref, preserveAspectRatio
116 */
117struct group_element : virtual public element_base
118{
119 static string
120 start_group(const string name = "")
121 {
122 string ret("<g");
123 if (!name.empty())
124 {
125 ret += " id=";
126 ret += k::quote;
127 ret += name;
128 ret += k::quote;
129 }
130 ret += ">";
131 ret += k::newline;
132 return ret;
133 }
134
135 static string
137 { return "</g>"; }
138
139 void
142
143 /// For groups of elements that have the same name.
144 ///
145 /// Also, although one can nest SVG elements inside another SVG by
146 /// using an 'image_element', the display quality is lacking in
147 /// inkscape. To work around this, insert the contents of the nested
148 /// SVG into a named group element instead.
149 void
150 start_element(string name)
151 { _M_sstream << start_group(name); }
152
153 // For groups of elements with style as specified.
154 void
155 start_element(string name, const style& sty)
156 {
157 _M_sstream << "<g id=" << k::quote << name << k::quote << k::space;
158 add_style(sty);
159 _M_sstream << '>' << k::newline;
160 }
161
162 // For groups of elements with transforms and style if specified.
163 void
164 start_element(string name, const transform, const string ts,
165 const style& sty = k::no_style)
166 {
167 _M_sstream << "<g id=" << k::quote << name << k::quote;
168 add_transform(ts);
169
170 // Only add style if it is not the default argument.
171 const color_qi nklr(color::none);
172 const bool stylep = to_string(sty._M_fill_color) != to_string(nklr);
173 if (stylep)
174 add_style(sty);
175 _M_sstream << '>' << k::newline;
176 }
177
178 void
180};
181
182void
185
186
187/**
188 Definitions SVG element. Storage space for elements used later.
189
190 Specification reference:
191 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
192
193 Attributes:
194 id
195 */
196struct defs_element : virtual public element_base
197{
198 static string
200 { return "<defs>"; }
201
202 static string
204 { return "</defs>"; }
205
206 void
208 { _M_sstream << start_defs() << k::newline; }
209
210 void
212};
213
214void
217
218void
225
226
227/**
228 Link SVG element. a
229
230 Specification reference:
231 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/a
232
233 Attributes:
234 id
235 */
236struct link_element : virtual public element_base
237{
238 void
240 { _M_sstream << "<a "; }
241
242 void
244
245 void
246 add_data(const string& url)
247 {
248 _M_sstream << "href=" << k::quote << url << k::quote;
250 }
251
252 /// Overload for rel=x form.
253 /// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload
254 /// @param relt attribute that describes the relationship of the
255 /// target object to the link object
256 /// @param ast type of resource.
257 void
258 add_data(const string& url, const string relt, const string ast,
259 const string cors = "anonymous")
260 {
261 _M_sstream << "rel=" << k::quote << relt << k::quote << k::space;
262 _M_sstream << "href=" << k::quote << url << k::quote << k::space;
263 _M_sstream << "as=" << k::quote << ast << k::quote << k::space;
264 _M_sstream << "crossorigin=" << k::quote << cors << k::quote << k::space;
265 _M_sstream << "referrerpolicy=" << k::quote << "no-referrer" << k::quote;
266 _M_sstream << k::space;
268 }
269};
270
271void
273{ _M_sstream << "</a>" << k::newline; }
274
275
276/**
277 Datum consolidating filter use and preferences.
278
279 \verbatim
280 <filter id="gblur10" x="0" y="0">
281 <feGaussianBlur in="SourceGraphic" stdDeviation="10" />
282 <feOffset dx="0" dy="0" />
283 </filter>
284 \endverbatim
285*/
286struct filter_element : virtual public element_base
287{
297
298 void
300 { _M_sstream << "<filter>" << k::newline; }
301
302 void
303 start_element(const string id)
304 {
305 _M_sstream << "<filter id=" << k::quote << id << k::quote << ">"
306 << k::newline;
307 }
308
309 // Some filter effects get clipped when appied to an element's area,
310 // so this allows filters to have an element + filter area instead.
311 void
312 start_element(const string id, const area<> blur_area, const point_2t p)
313 {
314 auto [ width, height ] = blur_area;
315 auto [ x, y ] = p;
316 _M_sstream << "<filter id=" << k::quote << id << k::quote << k::space
317 << "x=" << k::quote << x << k::quote << k::space
318 << "y=" << k::quote << y << k::quote << k::space
319 << "width=" << k::quote << width << k::quote << k::space
320 << "height=" << k::quote << height << k::quote
321 << ">"
322 << k::newline;
323 }
324
325 void
327
328 void
329 add_data(const string fltr)
330 { _M_sstream << fltr; }
331
332 // https://drafts.fxtf.org/filter-effects/#elementdef-fegaussianblur
333 // in == SourceGraphic, SourceAlpha, FillPaint, StrokePaint
334 // dev == 1 or 1,1 (0 default if two then x, y direction)
335 string
336 gaussian_blur(string in, string dev)
337 {
338 // <feGaussianBlur in="SourceGraphic" stdDeviation="20" />
339 stream_type stream;
340 stream << "<feGaussianBlur in=";
341 stream << k::quote << in << k::quote << k::space;
342 stream << "stdDeviation=" << k::quote << dev << k::quote << k::space;
343 stream << "/>";
344 return stream.str();
345 }
346
347 string
348 gaussian_blur(string dev)
349 { return gaussian_blur("SourceGraphic", dev); }
350};
351
352void
354{ _M_sstream << "</filter>" << k::newline; }
355
356
357/**
358 Gradient SVG elements.
359
360 Specification reference:
361 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient
362 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient
363
364 Note these are always inside a defs block.
365
366 Attributes:
367 id
368 */
369struct gradient_element : virtual public defs_element
370{
378
379 void
382
383 void
385
386 // off == 10%
387 void
388 stop(const string off, const color& klr, const double opacity = 1.0)
389 {
390 _M_sstream << "<stop offset=" << k::quote << off << k::quote << k::space
391 << "stop-color=" << k::quote << to_string(klr) << k::quote;
392 if (opacity < 1.0)
393 {
394 _M_sstream << k::space
395 << "stop-opacity=" << k::quote << opacity << k::quote;
396 }
397 _M_sstream << " />";
398 _M_sstream << k::newline;
399 }
400
401 // Express two integers as a suitable offset string percentage.
402 const string
403 offset_percentage(const ssize_type numer, const ssize_type denom)
404 {
405 const double ratio = static_cast<double>(numer)/static_cast<double>(denom);
406 const ssize_type perc(round(ratio * 100));
407 return std::to_string(perc) + "%";
408 }
409};
410
411void
414
415
416/// Circular gradients
417/// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient
418struct radial_gradient : virtual public gradient_element
419{
420 void
422 {
424 _M_sstream << "<radialGradient id=" << k::quote << "default" << k::quote;
425 _M_sstream << ">" << k::newline;
426 }
427
428 // Radial gradient centered at (cx, cy) of radius.
429 // Default for radius, cx, cy is "50%"
430 void
431 start_element(const string id, const ssize_type radius = 0,
432 const ssize_type cx = 0, const ssize_type cy = 0)
433 {
435 _M_sstream << "<radialGradient id=" << k::quote << id << k::quote;
436 if (radius > 0)
437 {
438 _M_sstream << k::space << "r=" << k::quote << radius << k::quote;
439 _M_sstream << k::space << "cx=" << k::quote << cx << k::quote;
440 _M_sstream << k::space << "cy=" << k::quote << cy << k::quote;
441 }
442 _M_sstream << ">" << k::newline;
443 }
444
445 // Radial gradient.
446 // End circle (aka 100% stop) at (cx, cy) with radius.
447 // Start circle (aka 0% stop) at (fx, fy) with radius fr.
448 void
449 start_element(const string id, const ssize_type radius,
450 const ssize_type cx, const ssize_type cy, const ssize_type fr,
451 const ssize_type fx, const ssize_type fy)
452 {
454 _M_sstream << "<radialGradient id=" << k::quote << id << k::quote
455 << k::space << "r=" << k::quote << radius << k::quote;
456 _M_sstream << k::space << "cx=" << k::quote << cx << k::quote;
457 _M_sstream << k::space << "cy=" << k::quote << cy << k::quote;
458 if (fr > 0)
459 {
460 _M_sstream << k::space << "fx=" << k::quote << fx << k::quote;
461 _M_sstream << k::space << "fy=" << k::quote << fy << k::quote;
462 _M_sstream << k::space << "fr=" << k::quote << fr << k::quote;
463 }
464 _M_sstream << ">" << k::newline;
465 }
466
467 void
469};
470
471void
473{
474 _M_sstream << "</radialGradient>" << k::newline;
476}
477
478
479/// Linear gradients
480/// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient
481struct linear_gradient : virtual public gradient_element
482{
483 void
485 {
487 _M_sstream << "<linearGradient id=" << k::quote << "default" << k::quote;
488 _M_sstream << ">" << k::newline;
489 }
490
491 void
493};
494
495void
497{
498 _M_sstream << "</linearGradient>" << k::newline;
500}
501
502
503/**
504 Marker SVG elements defines a graphic used for drawing
505 arrow[heads, tails, mid] on a poly line or path.
506
507 Specification reference:
508 https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/marker-mid
509
510 Note these are always inside a defs block.
511
512 Attributes:
513 id
514 */
515struct marker_element : virtual public element_base
516{
517 void
519
520 // markerWidth="8" markerHeight="8" refX="4" refY="4">
521 void
522 start_element(const string id, const area<> a, const point_2t p)
523 {
524 auto [ x, y ] = p;
525 auto [ w, h ] = a;
526 _M_sstream << "<marker id=" << k::quote << id << k::quote << k::space
527 << "markerWidth=" << k::quote << w << k::quote << k::space
528 << "markerHeight=" << k::quote << h << k::quote << k::space
529 << "refX=" << k::quote << x << k::quote << k::space
530 << "refY=" << k::quote << y << k::quote << " >"
531 << k::newline;
532 }
533
534 void
536};
537
538void
540{
541 _M_sstream << "</marker>" << k::newline;
542}
543
544
545/**
546 Title SVG element. This is accessible/alt text.
547 This element must be the first element in the svg objectc.
548
549 A title element with no string indicates a decorative element to AT
550 screenreaders.
551
552 Specification reference:
553 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
554*/
555struct title_element : virtual public element_base
556{
557 void
559 { _M_sstream << "<title>" << k::newline; }
560
561 void
562 start_element(const string t)
563 {
565 _M_sstream << t << k::newline;
566 }
567
568 void
570};
571
572void
574{ _M_sstream << "</title>" << k::newline; }
575
576
577/**
578 Description SVG element.
579
580 Specification reference:
581 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc
582*/
583struct desc_element : virtual public element_base
584{
585 void
587
588 void
589 start_element(const string dsc)
590 { _M_sstream << "<desc>" << k::newline << dsc << k::newline; }
591
592 void
594};
595
596void
598{ _M_sstream << "</desc>" << k::newline; }
599
600
601/**
602 Text SVG element.
603
604 Specification reference:
605 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/text
606
607 Attributes:
608 x, y, dx, dy, text-anchor, rotate, textLength, lengthAdjust
609*/
610struct text_element : virtual public element_base
611{
619
620 // So text_path_element can substitute the text_path part without
621 // duplicating the text formatting and style parts....
622 virtual void
623 add_text(string txt)
624 { _M_sstream << txt; }
625
626 /// Either serialize immediately (as below), or create data structure
627 /// that adds data to data_vec and then finish_element serializes.
628 void
629 add_data(const data& d,
630 const string trans = "", const unit utype = svg::unit::point)
631 {
632 const string x("__x");
633 const string y("__y");
634 const string attr("__attr");
635 const string style("__style");
636
637 string strip = R"_delimiter_(x="__x" y="__y" __attr __style)_delimiter_";
638
639 // Add attributes.
640 string_replace(strip, x, std::to_string(d._M_x_origin));
641 string_replace(strip, y, std::to_string(d._M_y_origin));
642 string_replace(strip, attr, d._M_typo.add_attribute(utype));
644 _M_sstream << strip;
645 add_transform(trans);
646 _M_sstream << '>';
647
648 // Add text data.
649 add_text(d._M_text);
650 }
651
652 void
654 { _M_sstream << "<text "; }
655
656 void
658
659 /// For text list output, use tspan for line breaks. This span
660 /// creates a new horizontal line for every tspan block, starting at
661 /// xpos with spacing dy (1.2em).
662 static string
663 start_tspan_y(uint xpos, string dy)
664 {
665 const string x("__x");
666 const string dys("__dy");
667 string strip = R"_delimiter_(<tspan x="__x" dy="__dy">)_delimiter_";
668 string_replace(strip, x, std::to_string(xpos));
669 string_replace(strip, dys, dy);
670 return strip;
671 }
672
673 static string
675 { return start_tspan_y(xpos, std::to_string(dy)); }
676
677 /// For text list output, use tspan for line breaks. This span
678 /// creates a new vertical line space for every tspan block, starting
679 /// at xpos with horizontal spacing dx ("1.4em").
680 static string
681 start_tspan_x(uint xpos, string dx)
682 {
683 const string x("__x");
684 const string dxs("__dx");
685 string strip = R"_delimiter_(<tspan x="__x" dx="__dx">)_delimiter_";
686 string_replace(strip, x, std::to_string(xpos));
687 string_replace(strip, dxs, dx);
688 return strip;
689 }
690
691 static string
693 { return start_tspan_x(xpos, std::to_string(dx)); }
694
695 static string
697 { return "</tspan>"; }
698};
699
700void
702{ _M_sstream << "</text>" << k::newline; }
703
704
705/// Make text span.
706string
707make_tspan_y_from_string_by_token(string s, uint xpos, const char token = ' ')
708{
709 string start(text_element::start_tspan_y(xpos, "0.5em"));
710 string ret = start;
711 for (uint i = 0; i < s.size(); ++i)
712 {
713 const char c = s[i];
714 if (c != token)
715 ret += c;
716 else
717 {
719 if (i < s.size() - 1)
720 ret += start;
721 }
722 }
724 return ret;
725}
726
727
728/**
729 Rectangle SVG element.
730
731 Specification reference:
732 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect
733
734 Attributes:
735 x, y, width, height, rx, ry
736 */
737struct rect_element : virtual public element_base
738{
746
747 // Verbose opening/closing pair tags for circle_element.
748 // Default assumes the more compact XML "self-closed tag" for circle element.
749 static constexpr const char* pair_open_tag = "<rect>";
750 static constexpr const char* pair_finish_tag = "</rect>";
751
752 /// Either serialize immediately (as below), or create data structure
753 /// that adds data to data_vec and then finish_element serializes.
754 void
755 add_data(const data& d)
756 {
757 const string x("__x");
758 const string y("__y");
759 const string w("__w");
760 const string h("__h");
761
762 string strip = R"_delimiter_(x="__x" y="__y" width="__w" height="__h"
763)_delimiter_";
764
765 string_replace(strip, x, std::to_string(d._M_x_origin));
766 string_replace(strip, y, std::to_string(d._M_y_origin));
767 string_replace(strip, w, std::to_string(d._M_width));
768 string_replace(strip, h, std::to_string(d._M_height));
769 _M_sstream << strip;
770 }
771
772 void
774 { _M_sstream << "<rect "; }
775
776 void
778};
779
780void
783
784
785/**
786 Circle SVG element.
787
788 Specification reference:
789 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle
790
791 Attributes:
792 x, y, width, height, xlink:xref, preserveAspectRatio
793 */
794struct circle_element : virtual public element_base
795{
802
803 // Verbose opening/closing pair tags for circle_element.
804 // Default assumes the more compact XML "self-closed tag" for circle element.
805 static constexpr const char* pair_open_tag = "<circle>";
806 static constexpr const char* pair_finish_tag = "</circle>";
807
808 // Either serialize immediately (as below), or create data structure
809 // that adds data to data_vec and then finish_element serializes.
810 void
811 add_data(const data& d, string trans = "")
812 {
813 const string x("__x");
814 const string y("__y");
815 const string r("__r");
816
817 string strip = R"_delimiter_(cx="__x" cy="__y" r="__r")_delimiter_";
818
819 string_replace(strip, x, std::to_string(d._M_x_origin));
820 string_replace(strip, y, std::to_string(d._M_y_origin));
821 string_replace(strip, r, std::to_string(d._M_radius));
822 _M_sstream << strip;
823 add_transform(trans);
824 }
825
826 void
828 { _M_sstream << "<circle "; }
829
830 void
832
833};
834
835void
838
839
840/**
841 Line SVG element.
842
843 Specification reference:
844 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line
845
846 Attributes:
847 x, y, width, height, xlink:xref, preserveAspectRatio
848*/
849struct line_element : virtual public element_base
850{
858
859 // Either serialize immediately (as below), or create data structure
860 // that adds data to data_vec and then finish_element serializes.
861 void
862 add_data(const data& d, const string dasharray = "")
863 {
864 const string x1("__x1");
865 const string x2("__x2");
866 const string y1("__y1");
867 const string y2("__y2");
868 const string dash("__darray");
869
870 const bool dashp = !dasharray.empty();
871 string stripf = \
872 R"_delimiter_(x1="__x1" y1="__y1" x2="__x2" y2="__y2")_delimiter_";
873 string stript = \
874 R"_delimiter_(x1="__x1" y1="__y1" x2="__x2" y2="__y2" stroke-dasharray="__darray")_delimiter_";
875
876 string strip = dashp ? stript : stripf;
877 string_replace(strip, x1, std::to_string(d._M_x_begin));
878 string_replace(strip, x2, std::to_string(d._M_x_end));
879 string_replace(strip, y1, std::to_string(d._M_y_begin));
880 string_replace(strip, y2, std::to_string(d._M_y_end));
881
882 if (dashp)
883 string_replace(strip, dash, dasharray);
884 _M_sstream << strip;
885 }
886
887 void
889 { _M_sstream << "<line "; }
890
891 void
892 start_element(string name)
893 { _M_sstream << "<line id=" << k::quote << name << k::quote << k::space; }
894
895 void
897};
898
899void
902
903
904/**
905 Polyline SVG element.
906
907 Specification reference:
908 https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/polyline
909
910 Attributes:
911 points, pathLength
912*/
913struct polyline_element : virtual public element_base
914{
915 // vector<point_2t> == vector<tuple<double,double>>
917
919
920 polyline_element(const vrange& points) : polypoints(points) { }
921
922 // Either serialize immediately (as below), or create data structure
923 // that adds data to data_vec and then finish_element serializes.
924 void
925 add_data(const stroke_style& sstyl)
926 {
927 if (!polypoints.empty())
928 {
929 _M_sstream << "points=" << k::quote;
930 for (const point_2t& pt : polypoints)
931 {
932 auto [ x, y ] = pt;
933 _M_sstream << x << k::comma << y << k::space;
934 }
935 _M_sstream << k::quote << k::space;
936
937 if (!sstyl.dasharray.empty())
938 {
939 _M_sstream << "stroke-dasharray=" << k::quote;
940 _M_sstream << sstyl.dasharray << k::quote << k::space;
941 }
942 if (!sstyl.dashoffset.empty())
943 {
944 _M_sstream << "stroke-dashoffset=" << k::quote;
945 _M_sstream << sstyl.dashoffset << k::quote << k::space;
946 }
947 if (!sstyl.linecap.empty())
948 {
949 _M_sstream << "stroke-linecap=" << k::quote;
950 _M_sstream << sstyl.linecap << k::quote << k::space;
951 }
952 if (!sstyl.markerspoints.empty())
953 {
954 string mkr;
955 mkr += k::quote;
956 mkr += "url(#";
957 mkr += sstyl.markerspoints;
958 mkr += ")";
959 mkr += k::quote;
960
961 _M_sstream << "marker-mid=" << mkr << k::space;
962 _M_sstream << "marker-end=" << mkr << k::space;
963 }
964 }
965 }
966
967 void
969 { _M_sstream << "<polyline "; }
970
971 void
972 start_element(string name)
973 { _M_sstream << "<polyline id=" << k::quote << name << k::quote << k::space; }
974
975 void
977};
978
979void
982
983
984
985/**
986 Path SVG element.
987
988 Specification reference:
989 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
990
991 Attributes:
992 d, pathLength
993*/
994struct path_element : virtual public element_base
995{
996 struct data
997 {
998 string _M_d;
1000 };
1001
1002 static constexpr const char* pair_finish_tag = "</path>";
1003
1004 /// Either serialize immediately (as below), or create data structure
1005 /// that adds data to data_vec and then finish_element serializes.
1006 void
1007 add_data(const data& d)
1008 {
1009 const string pathd("__d");
1010 const string len("__l");
1011
1012 string strip = R"_delimiter_(d="__d")_delimiter_";
1013
1014 string_replace(strip, pathd, d._M_d);
1015 string_replace(strip, len, std::to_string(d._M_length));
1016 _M_sstream << strip;
1017 }
1018
1019 void
1021 { _M_sstream << "<path "; }
1022
1023 void
1024 start_element(const string name)
1025 { _M_sstream << "<path id=" << k::quote << name << k::quote << k::space; }
1026
1027 void
1029};
1030
1031void
1037
1038
1039/**
1040 Text on a Path SVG element.
1041
1042 Specification reference:
1043 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/textPath
1044
1045 Attributes:
1046 href, path, method, side, spacing, startOffset, textLength, lengthAdjust
1047*/
1048struct text_path_element : virtual public text_element
1049{
1051 string offset; // "30%"
1052 string side; // "left" || "right" (use convex/concave side of path)
1053
1054 text_path_element(const string name,
1055 const string off = "", const string whichside = "")
1056 : path_name(name), offset(off), side(whichside) { }
1057
1058 virtual void
1059 add_text(string txt)
1060 {
1061 // Start text_path_element...
1062 _M_sstream << "<textPath xlink:href="
1063 << k::quote << '#' << path_name << k::quote;
1064 if (!offset.empty())
1065 _M_sstream << k::space << "startOffset="
1066 << k::quote << offset << k::quote;
1067 if (!side.empty())
1068 _M_sstream << k::space << "side="
1069 << k::quote << side << k::quote;
1070 _M_sstream << '>';
1071
1072 _M_sstream << txt;
1073
1074 // End text_path_element...
1075 _M_sstream << "</textPath>" << k::space;
1076 }
1077};
1078
1079
1080
1081/**
1082 Image SVG element. This can be another SVG file, or can be a raster
1083 image format like PNG or JPEG.
1084
1085 Specification reference:
1086 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
1087
1088 Attributes:
1089 x, y, width, height, xlink:xref, preserveAspectRatio
1090 */
1091struct image_element : virtual public element_base
1092{
1101
1102 void
1103 start_element(const string& id)
1104 {
1105 const string simg = "<image ";
1106 _M_sstream << simg << k::space;
1107 if (!id.empty())
1108 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1109 }
1110
1111 void
1114
1115 void
1117
1118 /// Either serialize immediately (as below), or create data structure
1119 /// that adds data to data_vec and then finish_element serializes.
1120 void
1121 add_data(const data& d)
1122 {
1123 const string x("__x");
1124 const string y("__y");
1125 const string w("__w");
1126 const string h("__h");
1127 const string ref("__ref");
1128
1129 string strip = R"_delimiter_(href="__ref" x="__x" y="__y" width="__w" height="__h"
1130)_delimiter_";
1131
1132 string_replace(strip, ref, d._M_xref);
1133 string_replace(strip, x, std::to_string(d._M_x_origin));
1134 string_replace(strip, y, std::to_string(d._M_y_origin));
1135 string_replace(strip, w, std::to_string(d._M_width));
1136 string_replace(strip, h, std::to_string(d._M_height));
1137 _M_sstream << strip << k::space;
1138 }
1139
1140 /// Visibility and other HTML/img attributes.
1141 /// @param vattr = visibility attribute, "visible" or "hidden"
1142 /// @param cors = CORS, "anonymous" or "use-credentials"
1143 /// @param lattr = loading attribute, "lazy" or "eager"
1144 void
1145 add_data(const data& d, const string vattr, const string cors)
1146 {
1147 add_data(d);
1148 _M_sstream << "visibility=" << k::quote << vattr << k::quote << k::space;
1149 _M_sstream << "crossorigin=" << k::quote << cors << k::quote << k::space;
1150 }
1151};
1152
1153
1154void
1157
1158
1159/**
1160 So-called Foreign Objects.
1161
1162 Using to get HTML video elements.
1163
1164 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject
1165 https://stackoverflow.com/questions/40324916/video-tag-embedded-in-svg
1166
1167Translate moves the origin from the top left to the specified
1168coordinates. If you embed an object at 0,0 it will be placed at the
1169new origin. In this case you must embed it at -translation
1170coordinates.
1171
1172Even so, I had to increase the width and height. Why? I don't know. It
1173doesn't seem to be a scale by 2. If someone knows I am curious to
1174know.
1175
1176<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;">
1177 <g>
1178 <g transform="translate(151,104) scale(1,1)">
1179 <rect x="0" y="0" width="300" height="200"></rect>
1180 <foreignObject x="-151" y="-104" width="500" height="400">
1181 <video xmlns="http://www.w3.org/1999/xhtml" width="300" height="200" controls="" style="position: fixed; left: 151px; top: 104px;">
1182 <source src="http://techslides.com/demos/sample-videos/small.mp4" type="video/mp4" />less
1183 </video>
1184 </foreignObject>
1185 </g>
1186 </g>
1187</svg>
1188*/
1189struct foreign_element : virtual public element_base
1190{
1191 void
1193 { _M_sstream << "<g>" << k::newline; }
1194
1195 // av == area of the foreign object and native video frame size
1196 // arect == area of displayed video as embedded inside svg element /aka page
1197 // scale_pair == x/y scaling factor
1198 void
1199 start_element(const point_2t origin, const area<> av, const area<> arect,
1200 const point_2t scale = std::make_tuple(1.0, 1.0))
1201 {
1202 const auto [ scalex, scaley ] = scale;
1203 const auto [ ox, oy ] = origin;
1204
1205 const auto [ width, height ] = arect;
1206 auto xo = width/2;
1207 auto yo = height/2;
1208
1209 const auto [ vwidth, vheight ] = av;
1210
1211 // Outer group.
1212 group_element go;
1213 go.start_element();
1214 _M_sstream << go.str() << k::newline;
1215
1216 // Inner Group.
1217 group_element gi;
1218 const transform txfm;
1219 string tx = transform::translate(xo, yo);
1220 string tscl = transform::scale(scalex, scaley);
1221 gi.start_element(string("video-wrapper"), txfm, tscl);
1222 _M_sstream << gi.str() << k::newline;
1223
1224 // Foreign Object
1225 string strip = R"(<foreignObject x="XXX" y="YYY" width="WWW" height="HHH">)";
1226 string_replace(strip, "WWW", std::to_string(vwidth));
1227 string_replace(strip, "HHH", std::to_string(vheight));
1228 string_replace(strip, "XXX", std::to_string(ox));
1229 string_replace(strip, "YYY", std::to_string(oy));
1230 _M_sstream << strip << k::newline;
1231 }
1232
1233 void
1235};
1236
1237void
1239{ _M_sstream << " </foreignObject></g></g>" << k::newline; }
1240
1241
1242/// video HTML object embedded in SVG container.
1243/// NB: HTML elements video/audio/iframe/canvas can be used w/o foreignElement.
1244/// This approach uses HTML wrapped in foreign element.
1245/// https://www.w3.org/TR/SVG2/embedded.html#HTMLElements
1246/// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video
1247struct video_element : virtual public foreign_element
1248{
1249 void
1250 start_element(const string& id)
1251 {
1252 //const string svideo = "<html:video";
1253 const string svideo = R"(<video xmlns="http://www.w3.org/1999/xhtml" )";
1254 _M_sstream << svideo << k::space;
1255 if (!id.empty())
1256 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1257 }
1258
1259 void
1262
1263 /// Video.
1264 /// a is width and height of video as embedded in page
1265 /// r is the foreign object, with x/y offset and scaled size
1266 ///
1267 /// attr is attribues for video_element
1268 /// autoplay="true" or removed
1269 /// loop="true/false"
1270 /// muted="true/false"
1271 /// controls, controlslist,
1272 /// crossorigin, disablepictureinpicture, disableremoteplayback
1273 ///
1274 void
1275 add_data(const area<> a, const string src, const string mtype = "video/mp4",
1276 const string attr = R"(autoplay="true" loop="true" muted="true")")
1277 {
1278 string strip = R"(width="WWW" height="HHH" )";
1279 string_replace(strip, "WWW", std::to_string(a._M_width));
1280 string_replace(strip, "HHH", std::to_string(a._M_height));
1281 _M_sstream << k::space;
1282 _M_sstream << strip << k::space;
1283 _M_sstream << attr << k::space;
1285
1286 _M_sstream << "<source src=" << k::quote << src << k::quote << k::space;
1287 _M_sstream << "type=" << k::quote << mtype << k::quote;
1289 }
1290
1291 void
1293};
1294
1295void
1297{ _M_sstream << "</video>" << k::newline; }
1298
1299
1300/// iframe HTML object embedded in SVG container.
1301/// NB: HTML elements video/audio/iframe/canvas can be used w/o foreignElement.
1302/// This approach uses HTML wrapped in foreign element.
1303/// https://www.w3.org/TR/SVG2/embedded.html#HTMLElements
1304/// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
1305struct iframe_element : virtual public foreign_element
1306{
1307 void
1308 start_element(const string& id)
1309 {
1310 //const string siframe = "<html:iframe";
1311 const string siframe = R"(<iframe xmlns="http://www.w3.org/1999/xhtml" )";
1312 _M_sstream << siframe << k::space;
1313 if (!id.empty())
1314 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1315 }
1316
1317 void
1320
1321 /// iframe.
1322 /// a is width and height of video as embedded in page
1323 /// r is the foreign object, with x/y offset and scaled size
1324 ///
1325 void
1326 add_data(const area<> a, const string src, const string mtype = "image/jpeg",
1327 const string attr = R"(sandbox="allow-scripts allow-same-origin")")
1328 {
1329 string strip = R"(width="WWW" height="HHH" )";
1330 string_replace(strip, "WWW", std::to_string(a._M_width));
1331 string_replace(strip, "HHH", std::to_string(a._M_height));
1332 _M_sstream << k::space;
1333 _M_sstream << strip << k::space;
1334 // _M_sstream << "src=" << k::quote << src << k::quote << k::space;
1335 _M_sstream << attr;
1337
1338 // image/webp or image/jpeg
1339 _M_sstream << "<source src=" << k::quote << src << k::quote << k::space;
1340 _M_sstream << "type=" << k::quote << mtype << k::quote << k::space;
1342 }
1343
1344 void
1346};
1347
1348void
1350{ _M_sstream << "</iframe>" << k::newline; }
1351
1352
1353/// HTML object embedded in SVG container.
1354/// Unlike image_elements, object_elements are not locked down for scripting.
1355/// NB: HTML elements video/audio/object/canvas can be used w/o foreignElement.
1356/// This approach uses HTML wrapped in foreign element.
1357/// https://www.w3.org/TR/SVG2/embedded.html#HTMLElements
1358/// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object
1359struct object_element : virtual public foreign_element
1360{
1361 void
1362 start_element(const string& id)
1363 {
1364 //const string sobject = "<html:object";
1365 const string sobject = R"(<object xmlns="http://www.w3.org/1999/xhtml" )";
1366 _M_sstream << sobject << k::space;
1367 if (!id.empty())
1368 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1369 }
1370
1371 void
1374
1375 /// Add resource to object.
1376 /// @param a is width and height of video as embedded in page
1377 /// @param src is the resource URL
1378 /// @param mtype is the MIME type
1379 /// @param attr is any collection of ad-hoc HTML attributes.
1380 void
1381 add_data(const area<> a, const string src, const string mtype = "image/jpeg",
1382 const string attr = R"(sandbox="allow-scripts allow-same-origin")")
1383 {
1384 string strip = R"(width="WWW" height="HHH" )";
1385 string_replace(strip, "WWW", std::to_string(a._M_width));
1386 string_replace(strip, "HHH", std::to_string(a._M_height));
1387 _M_sstream << k::space;
1388 _M_sstream << strip << k::space;
1389 _M_sstream << "data=" << k::quote << src << k::quote << k::space;
1390 _M_sstream << "type=" << k::quote << mtype << k::quote << k::space;
1391 _M_sstream << attr << k::space;
1393 }
1394
1395 void
1397};
1398
1399void
1401{ _M_sstream << "</object>" << k::newline; }
1402
1403
1404/**
1405 A SVG script element.
1406
1407 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
1408*/
1409struct script_element : virtual public element_base
1410{
1411 void
1412 start_element(const string& id)
1413 {
1414 const string shead = R"(<script type="text/javascript" crossorigin="anonymous")";
1415 _M_sstream << shead << k::space;
1416 if (!id.empty())
1417 _M_sstream << "id=" << k::quote << id << k::quote << k::space;
1419 }
1420
1421 void
1424
1425 /// showTooltip(id)
1426 /// hideTooltip(id)
1427 static const string&
1429 {
1430 static string js = R"(
1431 function showTooltip(event, tooltipId) {
1432 const tooltipimg = document.getElementById(tooltipId);
1433 tooltipimg.setAttribute('x', event.pageX + 10);
1434 tooltipimg.setAttribute('y', event.pageY - 150);
1435 tooltipimg.setAttribute('visibility', 'visible');
1436 }
1437
1438 function hideTooltip(tooltipId) {
1439 const tooltipimg = document.getElementById(tooltipId);
1440 tooltipimg.setAttribute('visibility', 'hidden');
1441 }
1442 )";
1443 return js;
1444 }
1445
1446 static const string
1447 tooltip_attribute(const string& id)
1448 {
1449 const string toolr("__toolt");
1450 string strip1 = R"_delimiter_(onmouseover="showTooltip(event, '__toolt')" )_delimiter_";
1451 string strip2 = R"_delimiter_(onmouseout="hideTooltip('__toolt')" )_delimiter_";
1452 string_replace(strip1, toolr, id);
1453 string_replace(strip2, toolr, id);
1454 return k::space + strip1 + k::space + strip2;
1455 }
1456
1457 /// Add string with script source.
1458 /// @param scriptstr script source
1459 void
1460 add_data(const string scriptstr)
1461 {
1462 _M_sstream << scriptstr;
1463 _M_sstream << k::newline;
1464 }
1465
1466 void
1468};
1469
1470void
1472{ _M_sstream << "</script>" << k::newline; }
1473
1474
1475/**
1476 A SVG object element.
1477
1478 https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
1479 https://developer.mozilla.org/en-US/docs/Web/SVG/SVG_as_an_Image
1480*/
1481struct svg_element : virtual public element_base
1482{
1484
1485 const string _M_name;
1489 const bool _M_lifetime;
1490
1491 svg_element(const string __title, const area& __cv,
1492 const bool lifetime = true,
1493 const unit u = svg::unit::pixel,
1494 const typography& __typo = k::smono_typo)
1495 : _M_name(__title), _M_area(__cv), _M_unit(u),
1496 _M_typo(__typo), _M_lifetime(lifetime)
1497 {
1498 if (_M_lifetime)
1499 start();
1500 }
1501
1502 svg_element(const string __title, const string desc, const area& __cv,
1503 const bool lifetime = true)
1504 : _M_name(__title), _M_area(__cv), _M_unit(svg::unit::pixel),
1505 _M_typo(svg::k::smono_typo), _M_lifetime(lifetime)
1506 {
1507 if (_M_lifetime)
1508 start(desc);
1509 }
1510
1512 : _M_name(other._M_name), _M_area(other._M_area),
1513 _M_unit(other._M_unit), _M_typo(other._M_typo),
1515 { }
1516
1518 {
1519 if (_M_lifetime)
1520 finish();
1521 }
1522
1523 const point_2t
1525 { return _M_area.center_point(); }
1526
1527 void
1528 start_element();
1529
1530 void
1531 start_element(const point_2t p, const area destarea,
1532 const style& sty = k::no_style);
1533
1534 void
1536
1537 void
1540
1541 void
1542 add_desc(const string desc)
1543 {
1544 desc_element de;
1545 de.start_element(desc);
1546 de.finish_element();
1547 add_element(de);
1548 }
1549
1550 void
1552
1553 void
1554 write();
1555
1556 void
1557 start(const string& desc = "")
1558 {
1559 this->start_element();
1560 this->add_title();
1561 if (!desc.empty())
1562 this->add_desc(desc);
1563 }
1564
1565 void
1567 {
1568 this->finish_element();
1569 this->write();
1570 }
1571};
1572/// @} group elements
1573
1574} // namespace svg
1575
1576#endif
void start_element(const string dsc)
static string start_tspan_x(uint xpos, uint dx)
void start_element(string name, const transform, const string ts, const style &sty=k::no_style)
void add_data(const string &url)
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...
virtual void add_text(string txt)
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)
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)
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)
Common transforms include rotate(180)
void start_element(string name)
static string finish_defs()
static constexpr const char * pair_finish_tag
static string finish_group()
void add_data(const data &d, const string vattr, const string cors)
Visibility and other HTML/img attributes.
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,...
static const string & tooltip_script()
showTooltip(id) hideTooltip(id)
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_group(const string name="")
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)
void start_element(string name)
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)
virtual void finish_element()=0
bool empty()
Empty when the output buffer is.
void add_fill(const string id)
void start_element(const point_2t origin, const area<> av, const area<> arect, const point_2t scale=std::make_tuple(1.0, 1.0))
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
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)
For groups of elements that have the same 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 a60-svg.h:86
unsigned int uint
Definition a60-svg.h:57
std::tuple< space_type, space_type > point_2t
Point (x,y) in 2D space.
Definition a60-svg.h:65
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.
Datum consolidating transform possibilities. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribu...
static string translate(int x, int y)
static string scale(double factor)
const std::string add_attribute(const svg::unit utype=svg::unit::pixel) const