97 case ElementKind::Defs:
return "defs";
98 case ElementKind::LinearGradient:
return "linearGradient";
99 case ElementKind::RadialGradient:
return "radialGradient";
100 case ElementKind::Stop:
return "stop";
101 case ElementKind::Symbol:
return "symbol";
102 case ElementKind::Use:
return "use";
103 case ElementKind::SVG:
return "svg";
104 case ElementKind::Style:
return "style";
105 case ElementKind::Path:
return "path";
106 case ElementKind::Text:
return "text";
107 case ElementKind::Title:
return "title";
108 case ElementKind::Group:
return "g";
109 case ElementKind::Line:
return "line";
110 case ElementKind::Rect:
return "rect";
111 case ElementKind::Circle:
return "circle";
112 case ElementKind::Polygon:
return "polygon";
113 case ElementKind::Custom:
return "";
127 using SVGAttrib = std::map<std::string, std::string>;
130 using SVGAttrib::SVGAttrib;
132 using Point = std::pair<double, double>;
134 const static Margins DEFAULT_MARGINS = { 10, 10, 10, 10 };
135 const static Margins NO_MARGINS = { 0, 0, 0, 0 };
139 bool _autoscale_nested_svgs =
true) :
177 return relative | anchor;
185 return Color(channel(red), channel(green), channel(blue));
190 if (!value.empty() && value[0] ==
'#') {
191 value.erase(value.begin());
193 if (value.size() != 3 && value.size() != 6) {
194 throw std::invalid_argument(
"Hex colors must use 3 or 6 digits");
197 for (
auto& ch : value) {
198 if (!std::isxdigit(
static_cast<unsigned char>(ch))) {
199 throw std::invalid_argument(
"Hex colors may only contain hexadecimal digits");
201 ch =
static_cast<char>(std::tolower(
static_cast<unsigned char>(ch)));
204 if (value.size() == 3) {
205 value = std::string{ value[0], value[0],
207 value[2], value[2] };
210 return Color(from_hex_pair(value, 0),
211 from_hex_pair(value, 2),
212 from_hex_pair(value, 4));
216 static Color hsl(
double hue,
double saturation,
double lightness) {
217 validate_percentage(saturation,
"Saturation");
218 validate_percentage(lightness,
"Lightness");
220 hue = std::fmod(hue, 360.0);
225 const auto s = saturation / 100.0;
226 const auto l = lightness / 100.0;
227 const auto c = (1.0 - std::fabs(2.0 * l - 1.0)) * s;
228 const auto x = c * (1.0 - std::fabs(std::fmod(hue / 60.0, 2.0) - 1.0));
229 const auto m = l - c / 2.0;
237 }
else if (hue < 120) {
240 }
else if (hue < 180) {
243 }
else if (hue < 240) {
246 }
else if (hue < 300) {
254 return Color(channel_from_unit(red + m),
255 channel_from_unit(green + m),
256 channel_from_unit(blue + m));
261 validate_unit(amount,
"Mix amount");
262 return Color(mix_channel(this->red_, other.red_, amount),
263 mix_channel(this->green_, other.green_, amount),
264 mix_channel(this->blue_, other.blue_, amount));
279 return rgb(255, 255, 255);
288 operator std::string()
const {
293 Color(uint8_t red, uint8_t green, uint8_t blue) : red_(red), green_(green), blue_(blue) {}
295 std::string serialize()
const {
296 std::ostringstream ss;
298 << std::hex << std::setfill(
'0') << std::nouppercase
299 << std::setw(2) <<
static_cast<int>(red_)
300 << std::setw(2) <<
static_cast<int>(green_)
301 << std::setw(2) <<
static_cast<int>(blue_);
305 static uint8_t channel(
int value) {
306 if (value < 0 || value > 255) {
307 throw std::invalid_argument(
"RGB channels must be between 0 and 255");
309 return static_cast<uint8_t
>(value);
312 static uint8_t channel_from_unit(
double value) {
313 value = std::max(0.0, std::min(1.0, value));
314 return channel(
static_cast<int>(std::round(value * 255.0)));
317 static uint8_t from_hex_pair(
const std::string& value,
size_t offset) {
318 return static_cast<uint8_t
>(std::stoi(value.substr(offset, 2),
nullptr, 16));
321 static uint8_t mix_channel(uint8_t from, uint8_t to,
double amount) {
322 return channel(
static_cast<int>(std::round(from + (to - from) * amount)));
325 static void validate_percentage(
double value,
const std::string& label) {
326 if (value < 0 || value > 100) {
327 throw std::invalid_argument(label +
" must be between 0 and 100");
331 static void validate_unit(
double value,
const std::string& label) {
332 if (value < 0 || value > 1) {
333 throw std::invalid_argument(label +
" must be between 0 and 1");
342#if __cplusplus < 201402L
344 template<
typename T,
typename... Args>
345 std::unique_ptr<T> make_unique(Args&&... args) {
346 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
351 using std::make_unique;
357 template<
size_t... I>
358 struct index_sequence {};
360 template<
size_t N,
size_t... I>
361 struct make_index_sequence : make_index_sequence<N - 1, N - 1, I...> {};
363 template<
size_t... I>
364 struct make_index_sequence<0, I...> {
365 using type = index_sequence<I...>;
369 struct is_attrs : std::is_same<typename std::decay<T>::type, Attrs> {};
371 template<
typename... Args>
372 struct last_is_attrs : std::false_type {};
375 struct last_is_attrs<T> : is_attrs<T> {};
377 template<
typename T,
typename... Rest>
378 struct last_is_attrs<T, Rest...> : last_is_attrs<Rest...> {};
380 template<
typename T,
typename Tuple,
size_t... I>
381 std::unique_ptr<T> make_child_with_attrs(Tuple&& tuple, index_sequence<I...>) {
382 const auto attrs = std::get<
sizeof...(I)>(tuple);
383 auto child = detail::make_unique<T>(std::get<I>(std::move(tuple))...);
384 for (
const auto& attr : attrs) {
385 child->set_attr(attr.first, attr.second);
390 template<
typename T,
typename... Args>
391 std::unique_ptr<T> make_child_impl(std::false_type, Args&&... args) {
392 return detail::make_unique<T>(std::forward<Args>(args)...);
395 template<
typename T,
typename... Args>
396 std::unique_ptr<T> make_child_impl(std::true_type, Args&&... args) {
397 auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
398 return make_child_with_attrs<T>(
400 typename make_index_sequence<
sizeof...(Args) - 1>::type{});
403 template<
typename T,
typename... Args>
404 std::unique_ptr<T> make_child(Args&&... args) {
405 return make_child_impl<T>(last_is_attrs<Args...>{}, std::forward<Args>(args)...);
408 struct AffineTransform {
409 AffineTransform() =
default;
410 AffineTransform(
double a_,
double b_,
double c_,
double d_,
double e_,
double f_) :
411 a(a_), b(b_), c(c_), d(d_), e(e_), f(f_) {}
420 Point apply(Point point)
const {
422 a * point.first + c * point.second + e,
423 b * point.first + d * point.second + f
428 inline AffineTransform multiply(
const AffineTransform& left,
const AffineTransform& right) {
430 left.a * right.a + left.c * right.b,
431 left.b * right.a + left.d * right.b,
432 left.a * right.c + left.c * right.d,
433 left.b * right.c + left.d * right.d,
434 left.a * right.e + left.c * right.f + left.e,
435 left.b * right.e + left.d * right.f + left.f
439 inline AffineTransform rotate_transform(
double degrees,
double cx = 0,
double cy = 0) {
440 const auto radians = degrees * PI / 180.0;
441 const auto cos_value = std::cos(radians);
442 const auto sin_value = std::sin(radians);
448 cx - cos_value * cx + sin_value * cy,
449 cy - sin_value * cx - cos_value * cy
453 inline AffineTransform translate_transform(
double x,
double y = 0) {
454 return { 1, 0, 0, 1, x, y };
457 inline double parse_number_or(
const std::string& value,
double fallback) {
458 if (value.empty())
return fallback;
460 return std::stof(value);
461 }
catch (
const std::invalid_argument&) {
463 }
catch (
const std::out_of_range&) {
469 struct is_numeric_attr_type {
470 static const bool value =
471 std::is_arithmetic<T>::value &&
472 !std::is_same<typename std::remove_cv<T>::type,
bool>::value &&
473 !std::is_same<typename std::remove_cv<T>::type,
char>::value &&
474 !std::is_same<typename std::remove_cv<T>::type,
signed char>::value &&
475 !std::is_same<typename std::remove_cv<T>::type,
unsigned char>::value;
478 inline std::vector<double> parse_transform_args(std::string args) {
479 for (
auto& ch : args) {
485 std::istringstream stream(args);
486 std::vector<double> values;
488 while (stream >> value) {
489 values.push_back(value);
494 class TextEstimator {
503 TextEstimator(std::string text_,
double font_size_, std::string font_weight_) :
504 text(std::move(text_)),
505 font_size(font_size_ > 0 ? font_size_ : 16),
506 font_weight(std::move(font_weight_)) {}
509 Metrics estimate()
const {
510 const auto bold = is_bold();
511 const double weight_multiplier = bold ? 1.06 : 1.0;
512 const double side_padding = bold ? font_size * 0.05 : font_size * 0.02;
513 const double vertical_padding = bold ? font_size * 0.10 : font_size * 0.06;
514 const double line_height = font_size * (bold ? 1.38 : 1.32);
515 const double ascent = font_size * (bold ? 1.00 : 0.96) + vertical_padding;
516 const double descent = font_size * (bold ? 0.38 : 0.34) + vertical_padding;
520 std::size_t lines = text.empty() ? 0 : 1;
521 bool previous_was_joiner =
false;
522 bool regional_pair_open =
false;
523 std::size_t index = 0;
525 while (index < text.size()) {
526 const auto codepoint = next_codepoint(index);
527 if (codepoint ==
'\r') {
528 if (index < text.size() && text[index] ==
'\n') ++index;
529 longest = std::max(longest, current);
532 previous_was_joiner =
false;
533 regional_pair_open =
false;
536 if (codepoint ==
'\n') {
537 longest = std::max(longest, current);
540 previous_was_joiner =
false;
541 regional_pair_open =
false;
545 current += glyph_advance(codepoint, previous_was_joiner, regional_pair_open);
546 previous_was_joiner = codepoint == 0x200d;
549 longest = std::max(longest, current);
551 metrics.width = longest == 0 ? 0 : longest * font_size * weight_multiplier + side_padding * 2;
552 metrics.ascent = ascent;
553 metrics.descent = descent;
554 metrics.height = lines == 0 ? 0 : ascent + descent +
static_cast<double>(lines - 1) * line_height;
561 std::string font_weight;
563 bool is_bold()
const {
564 auto value = font_weight;
565 for (
auto& ch : value) ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
566 if (value ==
"bold" || value ==
"bolder")
return true;
567 return parse_number_or(value, 400) >= 600;
570 uint32_t next_codepoint(std::size_t& index)
const {
571 const auto first =
static_cast<unsigned char>(text[index++]);
572 if (first < 0x80)
return first;
574 uint32_t codepoint = 0xfffd;
575 std::size_t continuation_count = 0;
576 if ((first & 0xe0) == 0xc0) {
577 codepoint = first & 0x1f;
578 continuation_count = 1;
579 }
else if ((first & 0xf0) == 0xe0) {
580 codepoint = first & 0x0f;
581 continuation_count = 2;
582 }
else if ((first & 0xf8) == 0xf0) {
583 codepoint = first & 0x07;
584 continuation_count = 3;
589 for (std::size_t i = 0; i < continuation_count; ++i) {
590 if (index >= text.size())
return 0xfffd;
591 const auto next =
static_cast<unsigned char>(text[index]);
592 if ((next & 0xc0) != 0x80)
return 0xfffd;
594 codepoint = (codepoint << 6) | (next & 0x3f);
597 if ((continuation_count == 1 && codepoint < 0x80) ||
598 (continuation_count == 2 && codepoint < 0x800) ||
599 (continuation_count == 3 && codepoint < 0x10000) ||
600 codepoint > 0x10ffff ||
601 (codepoint >= 0xd800 && codepoint <= 0xdfff)) {
607 static bool in_range(uint32_t value, uint32_t first, uint32_t last) {
608 return value >= first && value <= last;
611 static bool is_combining_mark(uint32_t codepoint) {
612 return in_range(codepoint, 0x0300, 0x036f) ||
613 in_range(codepoint, 0x0483, 0x0489) ||
614 in_range(codepoint, 0x0591, 0x05bd) ||
615 codepoint == 0x05bf ||
616 in_range(codepoint, 0x05c1, 0x05c2) ||
617 in_range(codepoint, 0x05c4, 0x05c5) ||
618 codepoint == 0x05c7 ||
619 in_range(codepoint, 0x0610, 0x061a) ||
620 in_range(codepoint, 0x064b, 0x065f) ||
621 codepoint == 0x0670 ||
622 in_range(codepoint, 0x06d6, 0x06dc) ||
623 in_range(codepoint, 0x06df, 0x06e4) ||
624 in_range(codepoint, 0x06e7, 0x06e8) ||
625 in_range(codepoint, 0x06ea, 0x06ed) ||
626 in_range(codepoint, 0x1ab0, 0x1aff) ||
627 in_range(codepoint, 0x1dc0, 0x1dff) ||
628 in_range(codepoint, 0x20d0, 0x20ff) ||
629 in_range(codepoint, 0xfe20, 0xfe2f);
632 static bool is_variation_selector(uint32_t codepoint) {
633 return in_range(codepoint, 0xfe00, 0xfe0f) ||
634 in_range(codepoint, 0xe0100, 0xe01ef);
637 static bool is_full_width(uint32_t codepoint) {
638 return in_range(codepoint, 0x1100, 0x115f) ||
639 in_range(codepoint, 0x2329, 0x232a) ||
640 in_range(codepoint, 0x2e80, 0xa4cf) ||
641 in_range(codepoint, 0xac00, 0xd7a3) ||
642 in_range(codepoint, 0xf900, 0xfaff) ||
643 in_range(codepoint, 0xfe10, 0xfe19) ||
644 in_range(codepoint, 0xfe30, 0xfe6f) ||
645 in_range(codepoint, 0xff00, 0xff60) ||
646 in_range(codepoint, 0xffe0, 0xffe6);
649 static bool is_emoji_or_symbol(uint32_t codepoint) {
650 return in_range(codepoint, 0x1f000, 0x1faff) ||
651 in_range(codepoint, 0x2600, 0x27bf) ||
652 in_range(codepoint, 0x2190, 0x21ff) ||
653 in_range(codepoint, 0x2300, 0x23ff);
656 static bool is_regional_indicator(uint32_t codepoint) {
657 return in_range(codepoint, 0x1f1e6, 0x1f1ff);
660 static bool is_latin_letter(uint32_t codepoint) {
661 return in_range(codepoint,
'A',
'Z') ||
662 in_range(codepoint,
'a',
'z') ||
663 in_range(codepoint, 0x00c0, 0x024f);
666 static double ascii_advance(uint32_t codepoint) {
708 if (in_range(codepoint,
'0',
'9'))
return 0.64;
709 if (in_range(codepoint,
'A',
'Z'))
return 0.72;
710 if (in_range(codepoint,
'a',
'z'))
return 0.62;
714 static double glyph_advance(uint32_t codepoint,
715 bool previous_was_joiner,
716 bool& regional_pair_open) {
717 if (is_combining_mark(codepoint) ||
718 is_variation_selector(codepoint) ||
719 codepoint == 0x200d ||
720 in_range(codepoint, 0x1f3fb, 0x1f3ff)) {
724 if (previous_was_joiner)
return 0;
726 if (is_regional_indicator(codepoint)) {
727 regional_pair_open = !regional_pair_open;
728 return regional_pair_open ? 1.10 : 0;
730 regional_pair_open =
false;
732 if (codepoint < 0x80)
return ascii_advance(codepoint);
733 if (is_full_width(codepoint))
return 1.05;
734 if (is_emoji_or_symbol(codepoint))
return 1.10;
735 if (is_latin_letter(codepoint))
return 0.66;
740 inline AffineTransform parse_supported_transform(
const std::string& transform) {
741 AffineTransform current;
743 while (pos < transform.size()) {
744 while (pos < transform.size() && std::isspace(
static_cast<unsigned char>(transform[pos]))) {
747 const auto name_start = pos;
748 while (pos < transform.size() && std::isalpha(
static_cast<unsigned char>(transform[pos]))) {
751 if (name_start == pos) {
755 const auto name = transform.substr(name_start, pos - name_start);
756 while (pos < transform.size() && std::isspace(
static_cast<unsigned char>(transform[pos]))) {
759 if (pos >= transform.size() || transform[pos] !=
'(') {
762 const auto args_start = ++pos;
763 const auto args_end = transform.find(
')', args_start);
764 if (args_end == std::string::npos) {
769 const auto args = parse_transform_args(transform.substr(args_start, args_end - args_start));
770 if (name ==
"rotate" && (args.size() == 1 || args.size() == 3)) {
773 args.size() == 1 ? rotate_transform(args[0])
774 : rotate_transform(args[0], args[1], args[2]));
775 }
else if (name ==
"translate" && (args.size() == 1 || args.size() == 2)) {
778 args.size() == 1 ? translate_transform(args[0])
779 : translate_transform(args[0], args[1]));
787 inline std::string
to_string(
const double& value);
788 inline std::string
to_string(
const Point& point);
790 inline std::string
to_string(
const Color& color);
791 inline std::string
to_string(
const std::map<std::string, AttributeMap>& css,
const size_t indent_level=0);
793 inline std::string
escape_xml(
const std::string& text);
795 std::vector<Point> bounding_polygon(
const std::vector<Shape*>& shapes);
797 SVG merge(
SVG& left,
SVG& right,
const Margins& margins = DEFAULT_MARGINS);
798 SVG merge(std::vector<SVG>& frames,
const double width,
const int max_frame_width);
806 explicit ClassList(std::string& value) : mutable_value_(&value), value_(&value) {}
807 explicit ClassList(
const std::string& value) : value_(&value) {}
811 validate_token(token);
812 const auto values =
tokens();
813 return std::find(values.begin(), values.end(), token) != values.end();
818 validate_token(token);
820 if (std::find(values.begin(), values.end(), token) == values.end()) {
821 values.push_back(token);
829 validate_token(token);
831 const auto original_size = values.size();
832 values.erase(std::remove(values.begin(), values.end(), token), values.end());
833 if (values.size() != original_size) {
852 write(parse(class_names));
868 std::vector<std::string>
tokens()
const {
869 return parse(value());
873 static bool is_space(
char ch) {
874 return std::isspace(
static_cast<unsigned char>(ch)) != 0;
877 static void validate_token(
const std::string& token) {
879 throw std::invalid_argument(
"class token cannot be empty");
881 if (std::find_if(token.begin(), token.end(), is_space) != token.end()) {
882 throw std::invalid_argument(
"class token cannot contain whitespace");
886 static std::vector<std::string> parse(
const std::string& class_names) {
887 std::vector<std::string> result;
889 for (
const auto ch : class_names) {
891 if (!token.empty()) {
892 if (std::find(result.begin(), result.end(), token) == result.end()) {
893 result.push_back(token);
901 if (!token.empty() && std::find(result.begin(), result.end(), token) == result.end()) {
902 result.push_back(token);
907 static std::string join(
const std::vector<std::string>& values) {
909 for (std::size_t i = 0; i < values.size(); ++i) {
918 const std::string& value()
const {
919 static const std::string empty;
920 return value_ ? *value_ : empty;
923 void write(
const std::vector<std::string>& values) {
924 if (!mutable_value_) {
925 throw std::logic_error(
"cannot mutate a const class list");
927 *mutable_value_ = join(values);
928 value_ = mutable_value_;
931 std::string* mutable_value_ =
nullptr;
932 const std::string* value_ =
nullptr;
942 mutable_value_(&value), value_(&value), owner_(owner) {}
944 value_(&value), owner_(owner) {}
948 validate_appendable();
949 if (transform.empty()) {
950 throw std::invalid_argument(
"transform cannot be empty");
953 if (
str().empty() ||
str() ==
"none") {
956 write(
str() +
" " + transform);
973 TransformList& matrix(
double a,
double b,
double c,
double d,
double e,
double f) {
974 std::stringstream ss;
980 TransformList& translate(
double x) {
984 TransformList& translate(
double x,
double y) {
988 TransformList& scale(
double factor) {
992 TransformList& scale(
double x,
double y) {
996 TransformList& rotate(
double degrees) {
1000 TransformList& rotate(
double degrees,
double cx,
double cy) {
1008 TransformList& skew_x(
double degrees) {
1012 TransformList& skew_y(
double degrees) {
1022 static bool is_keyword(
const std::string& transform) {
1023 return transform ==
"inherit" || transform ==
"initial" || transform ==
"revert" ||
1024 transform ==
"revert-layer" || transform ==
"unset";
1027 const std::string& value()
const {
1028 static const std::string empty;
1029 return value_ ? *value_ : empty;
1032 void validate_appendable()
const {
1033 if (is_keyword(value())) {
1034 throw std::logic_error(
"cannot append transform functions to a transform keyword");
1038 void write(
const std::string& value) {
1039 if (!mutable_value_) {
1040 throw std::logic_error(
"cannot mutate a const transform list");
1042 *mutable_value_ = value;
1043 value_ = mutable_value_;
1046 std::string* mutable_value_ =
nullptr;
1047 const std::string* value_ =
nullptr;
1048 const Element* owner_ =
nullptr;
1056 COLINEAR, CLOCKWISE, COUNTERCLOCKWISE
1059 inline std::vector<Point> polar_points(
int n,
int a,
int b,
double radius);
1061 template<
typename T>
1062 inline T min_or_not_nan(T first, T second) {
1066 if (isnan(first) && isnan(second))
1068 else if (isnan(first) || isnan(second))
1069 return isnan(first) ? second : first;
1071 return std::min(first, second);
1074 template<
typename T>
1075 inline T max_or_not_nan(T first, T second) {
1079 if (isnan(first) && isnan(second))
1081 else if (isnan(first) || isnan(second))
1082 return isnan(first) ? second : first;
1084 return std::max(first, second);
1087 inline Orientation orientation(Point& p1, Point& p2, Point& p3) {
1088 double value = ((p2.second - p1.second) * (p3.first - p2.first) -
1089 (p2.first - p1.first) * (p3.second - p2.second));
1091 if (value == 0)
return COLINEAR;
1092 else if (value > 0)
return CLOCKWISE;
1093 else return COUNTERCLOCKWISE;
1096 inline std::vector<Point> convex_hull(std::vector<Point>& points) {
1103 if (points.size() < 3)
return {};
1104 std::vector<Point> hull;
1108 for (
size_t i = 0; i < points.size(); i++)
1109 if (points[i].first < points[left].first) left = (int)i;
1112 int current = left, next;
1115 hull.push_back(points[current]);
1118 next = (current + 1) % points.size();
1119 for (
size_t i = 0; i < points.size(); i++) {
1121 if (orientation(points[current], points[next], points[i]) == COUNTERCLOCKWISE)
1126 }
while (current != left);
1131 inline std::vector<Point> polar_points(
int n,
int a,
int b,
double radius) {
1138 std::vector<Point> ret;
1139 for (
double degree = 0; degree < 360; degree += 360/n) {
1140 ret.push_back(Point(
1141 a + radius * cos(degree * (PI/180)),
1142 b + radius * sin(degree * (PI/180))
1152 std::stringstream ss;
1153 ss << std::fixed << std::setprecision(1);
1165 return static_cast<std::string
>(color);
1170 out.reserve(text.size());
1171 for (
const char ch : text) {
1203 bool _normalize_class =
false,
1204 std::function<
void(
const std::string&)> _on_update = {}) :
1205 attr_(_attr), normalize_class_(_normalize_class), on_update_(_on_update) {};
1209 attr_ +=
static_cast<std::string
>(value);
1215 template<
typename T>
1217 attr_ += std::to_string(value);
1225 if (normalize_class_) {
1236 SVGAttrib::mapped_type& attr_;
1237 bool normalize_class_ =
false;
1238 std::function<void(
const std::string&)> on_update_;
1241 AttributeMap() =
default;
1242 virtual ~AttributeMap() =
default;
1243 AttributeMap(SVGAttrib _attr) : attr_(std::move(_attr)) {};
1245 const SVGAttrib& attrs()
const {
1249 bool has_attr(
const std::string& key)
const {
1250 return this->attr_.find(key) != this->attr_.end();
1253 std::string get_attr(
const std::string& key,
const std::string& fallback =
"")
const {
1254 const auto found = this->attr_.find(key);
1255 return found == this->attr_.end() ? fallback : found->second;
1259 template<
typename T>
1260 typename std::enable_if<detail::is_numeric_attr_type<T>::value, T>::type
1262 const auto found = this->attr_.find(key);
1263 if (found == this->attr_.end())
return fallback;
1264 return static_cast<T
>(detail::parse_number_or(found->second,
static_cast<double>(fallback)));
1267 template<
typename T>
1268 AttributeMap& set_attr(
const std::string key, T value) {
1269 this->set_attr_value(key, std::to_string(value));
1274 AttributeMap& set_attr(
const std::string key,
const Color& value);
1278 for (
const auto& pair : values) {
1279 this->set_attr_value(pair.first, pair.second);
1287 for (
const auto& pair : values) {
1288 this->set_attr_value(pair.first, pair.second);
1294 AttrSetter set_attr(
const std::string key) {
1295 return this->make_attr_setter(key);
1298 ClassList class_list() {
1299 return ClassList(this->attr_[
"class"]);
1302 ClassList class_list()
const {
1303 const auto found = this->attr_.find(
"class");
1304 return found == this->attr_.end() ? ClassList() : ClassList(found->second);
1307 TransformList transform_list() {
1308 return TransformList(this->attr_[
"transform"]);
1311 TransformList transform_list()
const {
1312 const auto found = this->attr_.find(
"transform");
1313 return found == this->attr_.end() ? TransformList() : TransformList(found->second);
1316 TransformList transform() {
1317 return transform_list();
1320 TransformList transform()
const {
1321 return transform_list();
1325 virtual void set_attr_value(
const std::string& key,
const std::string& value) {
1326 if (key ==
"class") {
1327 ClassList(this->attr_[key]).set(value);
1330 this->attr_[key] = value;
1333 virtual AttrSetter make_attr_setter(
const std::string& key) {
1334 if (this->attr_.find(key) == this->attr_.end()) this->attr_[key] =
"";
1335 return AttrSetter(this->attr_.at(key), key ==
"class");
1338 SVGAttrib& mutable_attrs() {
1363 inline AttributeMap& AttributeMap::set_attr(
const std::string key,
const double value) {
1365 this->set_attr_value(key,
to_string(value));
1370 this->set_attr_value(key,
static_cast<std::string
>(value));
1375 inline AttributeMap& AttributeMap::set_attr(
const std::string key,
const char * value) {
1377 this->set_attr_value(key, value);
1382 inline AttributeMap& AttributeMap::set_attr(
const std::string key,
const std::string value) {
1384 this->set_attr_value(key, value);
1391 template<
typename T>
1393 static_assert(std::is_enum<T>::value,
"Typed CSS keys must be an enum type.");
1397 std::string name(T key)
const {
1398 const auto found = this->names_.find(key);
1399 if (found == this->names_.end()) {
1400 throw std::invalid_argument(
"Unknown typed CSS key");
1402 return found->second;
1407 void add_name(T key,
const std::string& normalized_name,
const std::string& duplicate_label) {
1408 if (this->names_.find(key) != this->names_.end()) {
1409 throw std::invalid_argument(
"Duplicate typed CSS key");
1411 if (this->used_names_.find(normalized_name) != this->used_names_.end()) {
1412 throw std::invalid_argument(
"Duplicate " + duplicate_label +
": " + normalized_name);
1415 this->names_[key] = normalized_name;
1416 this->used_names_.insert(normalized_name);
1421 std::map<T, std::string> names_;
1423 std::set<std::string> used_names_;
1429 template<
typename T>
1436 bool has_value =
false;
1442 key(_key), css_name(std::move(_css_name)) {}
1447 css_name(std::move(_css_name)),
1449 value(std::move(_value)) {}
1453 template<
typename T>
1462 key(_key), css_class(std::move(_css_class)) {}
1466 template<
typename T>
1472 std::vector<std::pair<T, std::string>> initial_values;
1474 for (
const auto& spec : specs) {
1475 const auto normalized_name = normalize_name(spec.css_name);
1476 this->add_name(spec.key, normalized_name,
"CSS variable name");
1477 if (spec.has_value) {
1478 initial_values.push_back({ spec.key, spec.value });
1482 for (
const auto& initial : initial_values) {
1483 this->set(initial.first, initial.second);
1488 std::string
var(T key)
const {
1489 return "var(" + this->name(key) +
")";
1494 this->target_->set_attr(this->name(key), value);
1500 return this->set(key, std::string(value));
1504 template<
typename... Keys>
1505 std::string
format(
const std::string& pattern, Keys... keys)
const {
1506 std::vector<std::string> args = { this->var(keys)... };
1507 std::vector<bool> used(args.size(),
false);
1510 for (
size_t i = 0; i < pattern.size(); ++i) {
1511 if (pattern[i] ==
'{') {
1512 if (i + 1 < pattern.size() && pattern[i + 1] ==
'{') {
1518 const auto start = i + 1;
1521 while (pos < pattern.size() && std::isdigit(
static_cast<unsigned char>(pattern[pos]))) {
1522 index = index * 10 +
static_cast<size_t>(pattern[pos] -
'0');
1525 if (pos == start || pos >= pattern.size() || pattern[pos] !=
'}') {
1526 throw std::invalid_argument(
"Malformed CSS variable format placeholder");
1528 if (index >= args.size()) {
1529 throw std::invalid_argument(
"CSS variable format placeholder index out of range");
1535 }
else if (pattern[i] ==
'}') {
1536 if (i + 1 < pattern.size() && pattern[i + 1] ==
'}') {
1541 throw std::invalid_argument(
"Unmatched CSS variable format brace");
1543 out.push_back(pattern[i]);
1547 for (
const auto was_used : used) {
1549 throw std::invalid_argument(
"Unused CSS variable format argument");
1558 static std::string normalize_name(
const std::string& css_name) {
1559 if (css_name.empty()) {
1560 throw std::invalid_argument(
"CSS variable name cannot be empty");
1562 if (css_name.size() >= 2 && css_name[0] ==
'-' && css_name[1] ==
'-') {
1565 return "--" + css_name;
1569 AttributeMap* target_;
1573 template<
typename T>
1578 for (
const auto& spec : specs) {
1579 this->add_name(spec.key, normalize_name(spec.css_class),
"CSS class name");
1584 template<
typename... Keys>
1586 const auto names = this->names(keys...);
1588 for (
const auto& name : names) {
1595 template<
typename... Keys>
1597 const auto names = this->names(keys...);
1599 for (
const auto& name : names) {
1600 if (!out.empty()) out +=
" ";
1607 static std::string normalize_name(std::string css_class) {
1608 if (!css_class.empty() && css_class[0] ==
'.') {
1609 css_class.erase(0, 1);
1611 if (css_class.empty()) {
1612 throw std::invalid_argument(
"CSS class name cannot be empty");
1614 for (
const auto ch : css_class) {
1615 if (std::isspace(
static_cast<unsigned char>(ch))) {
1616 throw std::invalid_argument(
"CSS class name cannot contain whitespace");
1622 template<
typename... Keys>
1623 std::vector<std::string> names(Keys... keys)
const {
1624 return std::vector<std::string>{ this->name(keys)... };
1638 using QuadCoord::QuadCoord;
1644 using namespace util;
1646 new_box.x1 = min_or_not_nan(this->x1, other.x1);
1647 new_box.x2 = max_or_not_nan(this->x2, other.x2);
1648 new_box.y1 = min_or_not_nan(this->y1, other.y1);
1649 new_box.y2 = max_or_not_nan(this->y2, other.y2);
1653 using ChildList = std::vector<Element*>;
1654 using ConstChildList = std::vector<const Element*>;
1655 using ChildMap = std::map<std::string, ChildList>;
1656 using ConstChildMap = std::map<std::string, ConstChildList>;
1657 class DepthFirstIterator;
1658 class ConstDepthFirstIterator;
1659 class DepthFirstRange;
1660 class ConstDepthFirstRange;
1663 descend_into_nested_svgs(_descend_into_nested_svgs) {}
1675 children(std::move(other.children)),
1679 has_layout_bbox_(other.has_layout_bbox_),
1680 layout_bbox_(other.layout_bbox_),
1681 layout_bbox_padding_(other.layout_bbox_padding_) {
1682 other.has_layout_bbox_ =
false;
1683 reparent_children();
1687 if (
this != &other) {
1688 AttributeMap::operator=(std::move(other));
1689 children = std::move(other.children);
1692 indexed_id_.clear();
1693 has_layout_bbox_ = other.has_layout_bbox_;
1694 layout_bbox_ = other.layout_bbox_;
1695 layout_bbox_padding_ = other.layout_bbox_padding_;
1696 other.has_layout_bbox_ =
false;
1697 reparent_children();
1702 Element(
const char*
id) : AttributeMap(
1703 SVGAttrib({ {
"id",
id } })) {};
1704 using AttributeMap::AttributeMap;
1707 operator std::string()
const {
return this->svg_to_string(0); };
1712 std::unique_ptr<Element> clone_element()
const;
1714 template<
typename T,
typename... Args>
1718 auto child = detail::make_child<T>(std::forward<Args>(args)...);
1719 return static_cast<T*
>(this->insert_child(std::move(child), this->children.end()));
1722 template<
typename T>
1726 auto child = detail::make_unique<T>(std::move(node));
1727 this->insert_child(std::move(child), this->children.end());
1731 template<
typename T>
1735 std::vector<T*> ret;
1736 auto child_elems = this->get_children_helper();
1738 for (
auto& child: child_elems)
1739 if (child->kind() == T::static_kind) ret.push_back(
static_cast<T*
>(child));
1744 template<
typename T>
1748 std::vector<const T*> ret;
1749 auto child_elems = this->get_children_helper();
1751 for (
auto& child: child_elems)
1752 if (child->kind() == T::static_kind) ret.push_back(
static_cast<const T*
>(child));
1757 template<
typename T>
1761 std::vector<T*> ret;
1762 for (
auto& child : this->children) {
1763 if (child->kind() == T::static_kind) ret.push_back(
static_cast<T*
>(child.get()));
1769 template<
typename T>
1773 std::vector<const T*> ret;
1774 for (
auto& child : this->children) {
1775 if (child->kind() == T::static_kind) ret.push_back(
static_cast<const T*
>(child.get()));
1781 Element* get_element_by_id(
const std::string&
id);
1782 const Element* get_element_by_id(
const std::string&
id)
const;
1783 std::vector<Element*> get_elements_by_class(
const std::string& clsname);
1784 std::vector<const Element*> get_elements_by_class(
const std::string& clsname)
const;
1785 const Element* parent()
const {
return parent_; }
1788 Element& id(
const std::string& value);
1789 std::string id()
const;
1790 void autoscale(
const Margins& margins=DEFAULT_MARGINS);
1791 void autoscale(
const double margin);
1795 void responsive_autoscale(
const Margins& margins=DEFAULT_MARGINS);
1797 void responsive_autoscale(
const double margin);
1801 Element& layout_bbox(
const BoundingBox& bbox);
1805 Element& bbox_padding(
double padding);
1811 BoundingBox layout_bbox()
const;
1815 Point offset = Point(0, 0));
1819 Point offset = Point(0, 0));
1823 Point offset = Point(0, 0));
1828 Point offset = Point(0, 0));
1830 return TransformList(this->mutable_attrs()[
"transform"],
this);
1832 TransformList transform_list()
const {
1833 const auto& attributes = this->attrs();
1834 const auto found = attributes.find(
"transform");
1835 return found == attributes.end() ? TransformList() : TransformList(found->second, this);
1837 TransformList transform() {
1838 return transform_list();
1840 TransformList transform()
const {
1841 return transform_list();
1843 virtual BoundingBox get_bbox()
const;
1844 ChildMap get_children();
1845 ConstChildMap get_children()
const;
1847 DepthFirstIterator begin();
1849 ConstDepthFirstIterator begin()
const;
1850 DepthFirstIterator end();
1851 ConstDepthFirstIterator end()
const;
1853 DepthFirstRange depth_first();
1855 DepthFirstRange depth_first(TraversalOptions options);
1857 ConstDepthFirstRange depth_first()
const;
1859 ConstDepthFirstRange depth_first(TraversalOptions options)
const;
1861 DepthFirstRange descendants();
1863 DepthFirstRange descendants(TraversalOptions options);
1865 ConstDepthFirstRange descendants()
const;
1867 ConstDepthFirstRange descendants(TraversalOptions options)
const;
1870 std::vector<std::unique_ptr<Element>> children;
1872 std::vector<Element*> get_children_helper();
1873 std::vector<const Element*> get_children_helper()
const;
1877 void autoscale_nested_svgs(
const AutoscaleOptions& options,
bool responsive);
1878 static detail::AffineTransform transform_for(
const Element* element,
1879 const detail::AffineTransform& parent_transform);
1882 virtual std::string svg_to_string(
const size_t indent_level)
const;
1886 virtual std::unique_ptr<Element> clone_element_impl()
const;
1888 template<
typename T>
1889 std::unique_ptr<T> clone_as()
const {
1890 static_assert(std::is_copy_constructible<T>::value,
1891 "Cloneable SVG element subclasses must be copy constructible.");
1892 return detail::make_unique<T>(
static_cast<const T&
>(*
this));
1895 void set_attr_value(
const std::string& key,
const std::string& value)
override;
1896 AttrSetter make_attr_setter(
const std::string& key)
override;
1898 const SVG* owner_svg()
const;
1899 void set_owner_svg(
SVG* owner);
1900 void register_subtree_ids();
1901 void unregister_subtree_ids();
1902 void register_own_id();
1903 void unregister_own_id();
1904 void clear_children();
1906 Element* insert_child(std::unique_ptr<Element> child, ChildIterator position) {
1907 child->parent_ =
this;
1908 child->set_owner_svg(this->owner_svg());
1910 child->register_subtree_ids();
1912 child->unregister_subtree_ids();
1913 child->set_owner_svg(
nullptr);
1914 child->parent_ =
nullptr;
1917 return children.insert(position, std::move(child))->get();
1920 void reparent_children() {
1921 for (
auto& child : children) {
1922 child->parent_ =
this;
1923 child->set_owner_svg(this->owner_);
1924 child->reparent_children();
1933 if (this->has_attr(key))
1934 return std::stof(this->get_attr(key));
1940 SVG* owner_ =
nullptr;
1941 std::string indexed_id_;
1942 bool has_layout_bbox_ =
false;
1943 BoundingBox layout_bbox_ = BoundingBox(NAN, NAN, NAN, NAN);
1944 Margins layout_bbox_padding_ = NO_MARGINS;
1950 Element::ChildList ret;
1951 for (
auto& child : this->children) ret.push_back(child.get());
1958 Element::ConstChildList ret;
1959 for (
auto& child : this->children) ret.push_back(child.get());
1965 auto child_elems = this->get_children_helper();
1966 for (
auto& current: child_elems)
1967 if (current->id() == id)
return current;
1974 auto child_elems = this->get_children_helper();
1975 for (
auto& current: child_elems)
1976 if (current->id() == id)
return current;
1983 std::vector<Element*> ret;
1984 auto child_elems = this->get_children_helper();
1986 for (
auto& current: child_elems) {
1987 if (current->has_attr(
"class")
1988 && current->class_list().contains(clsname))
1989 ret.push_back(current);
1997 std::vector<const Element*> ret;
1998 auto child_elems = this->get_children_helper();
2000 for (
auto& current: child_elems) {
2001 if (current->has_attr(
"class")
2002 && current->class_list().contains(clsname))
2003 ret.push_back(current);
2010 return this->clone_element_impl();
2018 has_layout_bbox_(other.has_layout_bbox_),
2019 layout_bbox_(other.layout_bbox_),
2020 layout_bbox_padding_(other.layout_bbox_padding_) {
2021 for (
const auto& child : other.children) {
2022 this->insert_child(child->clone_element(), this->children.end());
2026 inline std::unique_ptr<Element> Element::clone_element_impl()
const {
2027 throw std::logic_error(
"Custom SVG element subclasses must override clone_element_impl() to be cloned.");
2032 using iterator_category = std::input_iterator_tag;
2034 using difference_type = std::ptrdiff_t;
2043 const auto root_transform = transform_for(root, detail::AffineTransform());
2045 pending_.push_back(Frame(root, root_transform,
true));
2047 push_children(root, root_transform);
2052 Element* operator*()
const {
return current_.element; }
2053 const detail::AffineTransform& transform()
const {
return current_.transform; }
2067 if (end_ || other.end_)
return end_ == other.end_;
2068 return current_.element == other.current_.element;
2072 return !(*
this == other);
2077 Frame() : element(
nullptr), transform(), root(
false) {}
2078 Frame(
Element* element,
const detail::AffineTransform& transform,
bool root =
false) :
2080 transform(transform),
2084 detail::AffineTransform transform;
2088 static detail::AffineTransform transform_for(
Element* element,
2089 const detail::AffineTransform& parent_transform) {
2090 if (element->has_attr(
"transform")) {
2091 return detail::multiply(parent_transform,
2092 detail::parse_supported_transform(element->get_attr(
"transform")));
2094 return parent_transform;
2097 void push_children(
Element* element,
const detail::AffineTransform& transform) {
2098 for (
auto child = element->children.rbegin(); child != element->children.rend(); ++child) {
2099 auto* child_ptr = child->get();
2100 pending_.push_back(Frame(child_ptr, transform_for(child_ptr, transform)));
2105 if (pending_.empty()) {
2111 current_ = pending_.back();
2112 pending_.pop_back();
2115 current_.element->kind() != ElementKind::SVG) {
2116 push_children(current_.element, current_.transform);
2120 std::vector<Frame> pending_;
2128 using iterator_category = std::input_iterator_tag;
2130 using difference_type = std::ptrdiff_t;
2141 const auto root_transform = transform_for(root, detail::AffineTransform());
2143 pending_.push_back(Frame(root, root_transform,
true));
2145 push_children(root, root_transform);
2150 const Element* operator*()
const {
return current_.element; }
2151 const detail::AffineTransform& transform()
const {
return current_.transform; }
2165 if (end_ || other.end_)
return end_ == other.end_;
2166 return current_.element == other.current_.element;
2170 return !(*
this == other);
2175 Frame() : element(
nullptr), transform(), root(
false) {}
2176 Frame(
const Element* element,
const detail::AffineTransform& transform,
bool root =
false) :
2178 transform(transform),
2182 detail::AffineTransform transform;
2186 static detail::AffineTransform transform_for(
const Element* element,
2187 const detail::AffineTransform& parent_transform) {
2188 if (element->has_attr(
"transform")) {
2189 return detail::multiply(parent_transform,
2190 detail::parse_supported_transform(element->get_attr(
"transform")));
2192 return parent_transform;
2195 void push_children(
const Element* element,
const detail::AffineTransform& transform) {
2196 for (
auto child = element->children.rbegin(); child != element->children.rend(); ++child) {
2197 const auto* child_ptr = child->get();
2198 pending_.push_back(Frame(child_ptr, transform_for(child_ptr, transform)));
2203 if (pending_.empty()) {
2209 current_ = pending_.back();
2210 pending_.pop_back();
2213 current_.element->kind() != ElementKind::SVG) {
2214 push_children(current_.element, current_.transform);
2218 std::vector<Frame> pending_;
2227 root_(root), include_root_(include_root), options_(options) {}
2243 root_(root), include_root_(include_root), options_(options) {}
2255 return {
this,
true };
2259 return {
this,
true, options };
2263 return {
this,
true };
2267 return {
this,
true, options };
2279 return DepthFirstIterator();
2282 inline Element::ConstDepthFirstIterator Element::end()
const {
2283 return ConstDepthFirstIterator();
2287 return {
this,
false };
2291 return {
this,
false, options };
2295 return {
this,
false };
2299 return {
this,
false, options };
2304 return { NAN, NAN, NAN, NAN };
2312 using Element::Element;
2314 operator Point()
const {
2316 return std::make_pair(this->x(), this->y());
2323 Point(bbox.x1, bbox.y1),
2324 Point(bbox.x2, bbox.y1),
2325 Point(bbox.x1, bbox.y2),
2326 Point(bbox.x2, bbox.y2)
2330 virtual double x()
const {
return this->
find_numeric(
"x"); }
2331 virtual double y()
const {
return this->
find_numeric(
"y"); }
2349 static constexpr ElementKind static_kind = ElementKind::Defs;
2350 using Element::Element;
2360 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Defs>(); }
2366 class Stop :
public Element {
2368 static constexpr ElementKind static_kind = ElementKind::Stop;
2370 using Element::Element;
2371 ElementKind kind()
const override {
return static_kind; }
2373 Stop(std::string offset, std::string color) {
2374 this->set_attr(
"offset", std::move(offset));
2375 this->set_attr(
"stop-color", std::move(color));
2378 Stop(std::string offset, std::string color,
double opacity) :
2379 Stop(std::move(offset), std::move(color)) {
2380 this->set_attr(
"stop-opacity", opacity);
2384 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Stop>(); }
2388 class GradientElement :
public Element {
2390 using Element::Element;
2393 std::string url()
const {
2394 const auto gradient_id = this->id();
2395 if (gradient_id.empty()) {
2396 throw std::logic_error(
"SVG gradient must have an id before it can be referenced");
2398 return "url(#" + gradient_id +
")";
2402 void add_stop(std::string offset, std::string color) {
2403 this->add_child<Stop>(std::move(offset), std::move(color));
2406 void add_stop(std::string offset, std::string color,
double opacity) {
2407 this->add_child<Stop>(std::move(offset), std::move(color), opacity);
2410 void set_solid_segments(std::initializer_list<std::string> colors) {
2411 if (colors.size() == 0) {
2412 throw std::invalid_argument(
"solid_segments requires at least one color");
2415 this->clear_children();
2416 if (colors.size() == 1) {
2417 const auto color = *colors.begin();
2418 add_stop(
"0%", color);
2419 add_stop(
"100%", color);
2423 const auto segment_width = 100.0 /
static_cast<double>(colors.size());
2424 std::size_t index = 0;
2425 for (
const auto& color : colors) {
2426 const auto start = percent(index * segment_width);
2427 const auto end = percent((index + 1) * segment_width);
2428 add_stop(start, color);
2429 add_stop(end, color);
2435 static std::string percent(
double value) {
2436 std::stringstream ss;
2437 ss << std::fixed << std::setprecision(1) << value <<
"%";
2447 static constexpr ElementKind static_kind = ElementKind::LinearGradient;
2449 using detail::GradientElement::GradientElement;
2450 ElementKind kind()
const override {
return static_kind; }
2457 return endpoints(
"0%",
"0%",
"100%",
"0%");
2461 return endpoints(
"0%",
"0%",
"0%",
"100%");
2464 LinearGradient& endpoints(std::string x1, std::string y1, std::string x2, std::string y2) {
2465 this->set_attr(
"x1", std::move(x1));
2466 this->set_attr(
"y1", std::move(y1));
2467 this->set_attr(
"x2", std::move(x2));
2468 this->set_attr(
"y2", std::move(y2));
2473 add_stop(std::move(offset), std::move(color));
2478 return stop(std::move(offset),
static_cast<std::string
>(color));
2481 LinearGradient& stop(std::string offset, std::string color,
double opacity) {
2482 add_stop(std::move(offset), std::move(color), opacity);
2487 return stop(std::move(offset),
static_cast<std::string
>(color), opacity);
2490 LinearGradient& solid_segments(std::initializer_list<std::string> colors) {
2491 set_solid_segments(colors);
2496 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<LinearGradient>(); }
2502 static constexpr ElementKind static_kind = ElementKind::RadialGradient;
2504 using detail::GradientElement::GradientElement;
2505 ElementKind kind()
const override {
return static_kind; }
2512 this->set_attr(
"cx", std::move(cx));
2513 this->set_attr(
"cy", std::move(cy));
2518 this->set_attr(
"r", std::move(r));
2523 this->set_attr(
"fx", std::move(fx));
2524 this->set_attr(
"fy", std::move(fy));
2529 add_stop(std::move(offset), std::move(color));
2534 return stop(std::move(offset),
static_cast<std::string
>(color));
2537 RadialGradient& stop(std::string offset, std::string color,
double opacity) {
2538 add_stop(std::move(offset), std::move(color), opacity);
2543 return stop(std::move(offset),
static_cast<std::string
>(color), opacity);
2546 RadialGradient& solid_segments(std::initializer_list<std::string> colors) {
2547 set_solid_segments(colors);
2552 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<RadialGradient>(); }
2558 static constexpr ElementKind static_kind = ElementKind::Symbol;
2560 using Element::Element;
2562 explicit Symbol(std::string
id) {
2567 std::string
href()
const;
2572 Use use(
double x,
double y)
const;
2574 Use use(
double x,
double y,
double width,
double height)
const;
2577 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Symbol>(); }
2583 static constexpr ElementKind static_kind = ElementKind::Use;
2587 explicit Use(std::string href) {
2588 set_attr(
"href", std::move(href));
2591 Use(std::string href,
double x,
double y) :
Use(std::move(href)) {
2596 Use(std::string href,
double x,
double y,
double width,
double height) :
2597 Use(std::move(href), x, y) {
2598 set_attr(
"width",
width);
2599 set_attr(
"height",
height);
2604 set_attr(
"xlink:href", std::move(href));
2612 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Use>(); }
2618 std::map<std::string, Element*> id_index_;
2619 Defs* defs_ =
nullptr;
2624 static constexpr ElementKind static_kind = ElementKind::Style;
2626 using Element::Element;
2633 std::string svg_to_string(
const size_t)
const override;
2634 std::unique_ptr<Element> clone_element_impl()
const override;
2637 static constexpr ElementKind static_kind = ElementKind::SVG;
2640 { {
"xmlns",
"http://www.w3.org/2000/svg" } }
2642 set_owner_svg(
this);
2647 SVG(
const SVG& other) :
Shape(other.attrs()), id_index_(), defs_(nullptr), css(nullptr) {
2648 clone_children_from(other);
2649 refresh_special_children();
2650 set_owner_svg(
this);
2654 SVG(
SVG&& other) noexcept :
2655 Shape(std::move(other)),
2656 id_index_(std::move(other.id_index_)),
2659 refresh_special_children();
2660 set_owner_svg(
this);
2664 SVG& operator=(
SVG&& other)
noexcept {
2665 if (
this != &other) {
2666 Shape::operator=(std::move(other));
2667 id_index_ = std::move(other.id_index_);
2670 refresh_special_children();
2671 set_owner_svg(
this);
2677 SVG& operator=(
const SVG& other) =
delete;
2695 return this->css->media_queries[query][key];
2700 this->media_style(query, key).
set_attrs(attrs);
2705 template<
typename T>
2707 return this->set_vars<T>(
":root", specs);
2711 template<
typename T>
2717 template<
typename T>
2719 const std::string& selector,
2721 return Variables<T>(this->media_style(query, selector), specs);
2724 std::map<std::string, AttributeMap>&
keyframes(
const std::string& key) {
2729 if (!this->css) this->css = this->add_child<Style>();
2730 return this->css->keyframes[key];
2735 if (!this->defs_) this->defs_ = this->add_child<Defs>();
2739 template<
typename T,
typename... Args>
2740 typename std::enable_if<!std::is_same<T, Defs>::value, T*>::type add_child(Args&&... args) {
2741 return Element::add_child<T>(std::forward<Args>(args)...);
2744 template<
typename T,
typename... Args>
2745 typename std::enable_if<std::is_same<T, Defs>::value, T*>::type add_child(Args&&... args) {
2746 if (this->defs_)
return this->defs_;
2748 auto child = detail::make_child<T>(std::forward<Args>(args)...);
2749 auto* inserted =
static_cast<T*
>(
2750 this->insert_child(std::move(child), this->defs_insert_position()));
2751 this->defs_ = inserted;
2755 Element* get_element_by_id(
const std::string&
id) {
2756 const auto found = this->id_index_.find(
id);
2757 return found == this->id_index_.end() ? nullptr : found->second;
2760 const Element* get_element_by_id(
const std::string&
id)
const {
2761 const auto found = this->id_index_.find(
id);
2762 return found == this->id_index_.end() ? nullptr : found->second;
2766 template<
typename T>
2769 auto* element = this->get_element_by_id(
id);
2770 if (!element || element->kind() != T::static_kind)
return nullptr;
2771 return static_cast<T*
>(element);
2775 template<
typename T>
2778 auto* element = this->get_element_by_id(
id);
2779 if (!element || element->kind() != T::static_kind)
return nullptr;
2780 return static_cast<const T*
>(element);
2786 BoundingBox get_bbox()
const override;
2789 std::unique_ptr<Element> clone_element_impl()
const override {
2790 return detail::make_unique<SVG>(*
this);
2794 void clone_children_from(
const SVG& other) {
2795 for (
const auto& child : other.children) {
2796 this->insert_child(child->clone_element(), this->children.end());
2800 void refresh_special_children() {
2801 this->css =
nullptr;
2802 this->defs_ =
nullptr;
2803 for (
auto& child : this->children) {
2804 if (child->kind() == Style::static_kind) {
2805 this->css =
static_cast<Style*
>(child.get());
2806 }
else if (child->kind() == Defs::static_kind) {
2807 this->defs_ =
static_cast<Defs*
>(child.get());
2812 void register_id(Element& element,
const std::string&
id) {
2813 if (
id.empty())
return;
2814 const auto found = this->id_index_.find(
id);
2815 if (found != this->id_index_.end() && found->second != &element) {
2816 throw std::invalid_argument(
"Duplicate SVG element id: " +
id);
2818 this->id_index_[id] = &element;
2821 void unregister_id(Element& element,
const std::string&
id) {
2822 if (
id.empty())
return;
2823 const auto found = this->id_index_.find(
id);
2824 if (found != this->id_index_.end() && found->second == &element) {
2825 this->id_index_.erase(found);
2829 void rebuild_id_index() {
2830 this->id_index_.clear();
2831 this->register_subtree_ids();
2834 ChildIterator defs_insert_position() {
2835 if (!this->css)
return this->children.begin();
2837 for (
auto it = this->children.begin(); it != this->children.end(); ++it) {
2838 if (it->get() == this->css)
return it + 1;
2840 return this->children.begin();
2845 inline SVG* Element::owner_svg() {
2846 return this->owner_;
2849 inline const SVG* Element::owner_svg()
const {
2850 return this->owner_;
2853 inline void Element::set_owner_svg(
SVG* owner) {
2854 this->owner_ = owner;
2855 for (
auto& child : this->children) {
2856 child->set_owner_svg(owner);
2860 inline Element& Element::id(
const std::string& value) {
2861 const auto old_indexed_id = this->indexed_id_;
2862 auto* root = this->owner_svg();
2864 if (value.empty()) {
2865 if (root && !old_indexed_id.empty()) {
2866 root->unregister_id(*
this, old_indexed_id);
2868 this->mutable_attrs().erase(
"id");
2869 this->indexed_id_.clear();
2873 if (root && old_indexed_id != value) {
2874 root->register_id(*
this, value);
2876 if (root && !old_indexed_id.empty() && old_indexed_id != value) {
2877 root->unregister_id(*
this, old_indexed_id);
2880 this->mutable_attrs()[
"id"] = value;
2881 this->indexed_id_ = root ? value :
"";
2885 inline std::string Element::id()
const {
2886 return this->get_attr(
"id");
2889 inline void Element::set_attr_value(
const std::string& key,
const std::string& value) {
2894 AttributeMap::set_attr_value(key, value);
2897 inline AttributeMap::AttrSetter Element::make_attr_setter(
const std::string& key) {
2899 return AttributeMap::make_attr_setter(key);
2903 this->mutable_attrs()[key] =
"";
2904 return AttrSetter(this->mutable_attrs().at(key),
false, [
this](
const std::string& value) {
2905 const auto previous = this->indexed_id_;
2909 if (previous.empty()) {
2910 this->mutable_attrs().erase(
"id");
2912 this->mutable_attrs()[
"id"] = previous;
2919 inline void Element::register_subtree_ids() {
2920 this->register_own_id();
2921 for (
auto& child : this->children) {
2922 child->register_subtree_ids();
2926 inline void Element::unregister_subtree_ids() {
2927 this->unregister_own_id();
2928 for (
auto& child : this->children) {
2929 child->unregister_subtree_ids();
2933 inline void Element::register_own_id() {
2934 const auto current_id = this->id();
2935 if (current_id.empty())
return;
2937 if (
auto* root = this->owner_svg()) {
2938 root->register_id(*
this, current_id);
2939 this->indexed_id_ = current_id;
2943 inline void Element::unregister_own_id() {
2944 if (this->indexed_id_.empty())
return;
2946 if (
auto* root = this->owner_svg()) {
2947 root->unregister_id(*
this, this->indexed_id_);
2949 this->indexed_id_.clear();
2952 inline void Element::clear_children() {
2953 for (
auto& child : this->children) {
2954 child->unregister_subtree_ids();
2955 child->set_owner_svg(
nullptr);
2956 child->parent_ =
nullptr;
2958 this->children.clear();
2962 for (
auto* child : this->get_immediate_children<LinearGradient>()) {
2963 if (child->id() == id)
return *child;
2965 return *this->add_child<LinearGradient>(std::move(
id));
2969 for (
auto* child : this->get_immediate_children<RadialGradient>()) {
2970 if (child->id() == id)
return *child;
2972 return *this->add_child<RadialGradient>(std::move(
id));
2976 for (
auto* child : this->get_immediate_children<Symbol>()) {
2977 if (child->id() == id)
return child;
2979 return this->add_child<Symbol>(std::move(
id));
2983 const auto symbol_id = this->id();
2984 if (symbol_id.empty()) {
2985 throw std::logic_error(
"SVG symbol must have an id before it can be referenced");
2987 return "#" + symbol_id;
2997 return Use(this->
href(), x, y);
3001 return Use(this->
href(), x, y, width, height);
3006 static constexpr ElementKind static_kind = ElementKind::Path;
3010 template<
typename T>
3015 this->set_attr(
"d",
"M " + std::to_string(x) +
" " + std::to_string(y));
3020 template<
typename T>
3027 if (!this->has_attr(
"d"))
3030 this->mutable_attrs()[
"d"] +=
" L " + std::to_string(x) +
3031 " " + std::to_string(y);
3034 inline void line_to(std::pair<double, double> coord) {
3035 this->
line_to(coord.first, coord.second);
3040 this->
line_to(x_start, y_start);
3044 std::unique_ptr<Element> clone_element_impl()
const override {
3045 return clone_as<Path>();
3056 static constexpr ElementKind static_kind = ElementKind::Text;
3058 using Element::Element;
3061 Text(
double x,
double y, std::string _content) {
3067 Text(std::pair<double, double> xy, std::string _content) :
3068 Text(xy.first, xy.second, _content) {};
3071 BoundingBox
get_bbox()
const override;
3074 std::string content;
3075 std::string svg_to_string(
const size_t)
const override;
3076 std::unique_ptr<Element> clone_element_impl()
const override {
3077 return clone_as<Text>();
3098 std::unique_ptr<Element> clone_element_impl()
const override {
3099 return clone_as<Title>();
3105 static constexpr ElementKind static_kind = ElementKind::Group;
3106 using Element::Element;
3110 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Group>(); }
3118 static constexpr ElementKind static_kind = ElementKind::Line;
3123 Line(
double x1,
double x2,
double y1,
double y2) :
Shape({
3130 Line(Point x, Point y) : Line(x.first, y.first, x.second, y.second) {};
3132 virtual double x()
const override {
return x1() + (x2() - x1()) / 2; }
3133 virtual double y()
const override {
return y1() + (y2() - y1()) / 2; }
3139 double width()
const override {
return std::abs(x2() - x1()); }
3140 double height()
const override {
return std::abs(y2() - y1()); }
3141 double length()
const {
return std::sqrt(pow(width(), 2) + pow(height(), 2)); }
3142 double slope()
const {
return (y2() - y1()) / (x2() - x1()); }
3143 double angle()
const {
return atan(this->slope()) * RAD_TO_DEG; }
3145 std::pair<double, double>
along(
double percent)
const;
3148 Element::BoundingBox get_bbox()
const override;
3149 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Line>(); }
3154 static constexpr ElementKind static_kind = ElementKind::Rect;
3160 double x,
double y,
double width,
double height) :
3168 Element::BoundingBox get_bbox()
const override;
3171 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Rect>(); }
3176 static constexpr ElementKind static_kind = ElementKind::Circle;
3181 Circle(
double cx,
double cy,
double radius) :
3189 Circle(std::pair<double, double> xy,
double radius) : Circle(xy.first, xy.second, radius) {};
3190 double radius()
const {
return this->
find_numeric(
"r"); }
3191 virtual double x()
const override {
return this->
find_numeric(
"cx"); }
3192 virtual double y()
const override {
return this->
find_numeric(
"cy"); }
3193 virtual double width()
const override {
return this->radius() * 2; }
3194 virtual double height()
const override {
return this->width(); }
3195 Element::BoundingBox get_bbox()
const override;
3198 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Circle>(); }
3203 static constexpr ElementKind static_kind = ElementKind::Polygon;
3205 using Element::Element;
3208 Polygon(
const std::vector<Point>& points) {
3210 std::string point_str;
3211 for (
auto& pt : points)
3213 this->set_attr(
"points", point_str);
3217 std::unique_ptr<Element> clone_element_impl()
const override {
return clone_as<Polygon>(); }
3221 if (content.empty()) {
3222 const auto x = get_attr<double>(
"x", 0) + get_attr<double>(
"dx", 0);
3223 const auto y = get_attr<double>(
"y", 0) + get_attr<double>(
"dy", 0);
3224 return include_stroke_width({ x, x, y, y });
3227 double font_size = get_attr<double>(
"font-size", 16);
3228 if (!(font_size > 0)) font_size = 16;
3230 const double x = get_attr<double>(
"x", 0) + get_attr<double>(
"dx", 0);
3231 const double y = get_attr<double>(
"y", 0) + get_attr<double>(
"dy", 0);
3232 const auto metrics = detail::TextEstimator(content, font_size, get_attr(
"font-weight")).estimate();
3233 const double width = metrics.width;
3234 const double height = metrics.height;
3237 const auto anchor = this->get_attr(
"text-anchor",
"start");
3238 if (anchor ==
"middle") {
3240 }
else if (anchor ==
"end") {
3244 const auto baseline = this->get_attr(
3245 "dominant-baseline",
3246 this->get_attr(
"alignment-baseline",
"alphabetic"));
3247 double y1 = y - metrics.ascent;
3248 if (baseline ==
"middle" || baseline ==
"central" || baseline ==
"mathematical") {
3249 y1 = y - height / 2;
3250 }
else if (baseline ==
"hanging" || baseline ==
"text-before-edge" || baseline ==
"text-top") {
3252 }
else if (baseline ==
"text-after-edge" || baseline ==
"text-bottom") {
3256 return include_stroke_width({ x1, x1 + width, y1, y1 + height });
3260 return include_stroke_width({ std::min(x1(), x2()), std::max(x1(), x2()),
3261 std::min(y1(), y2()), std::max(y1(), y2()) });
3264 inline Element::BoundingBox Rect::get_bbox()
const {
3265 double x = this->x(), y = this->y(),
3267 return include_stroke_width({ x, x +
width, y, y +
height });
3270 inline Element::BoundingBox Circle::get_bbox()
const {
3271 double x = this->x(), y = this->y(), radius = this->radius();
3273 return include_stroke_width({
3286 double x_pos, y_pos;
3289 double length = percent * this->length();
3290 double discrim = std::sqrt(4 * pow(length, 2) * (1 / (1 + pow(slope(), 2))));
3292 double x_a = (2 * x1() + discrim) / 2;
3293 double x_b = (2 * x1() - discrim) / 2;
3296 if ((x_a > x1() && x_a > x2()) || (x_a < x1() && x_a < x2()))
3299 y_pos = slope() * (x_pos - x1()) + y1();
3305 y_pos = y1() - percent * this->length();
3307 y_pos = y1() + percent * this->length();
3310 return std::make_pair(x_pos, y_pos);
3319 auto indent = std::string(indent_level,
'\t');
3320 std::string ret = indent +
"<" +
tag();
3323 for (
auto& pair: attrs())
3324 ret +=
" " + pair.first +
"=" +
"\"" +
escape_xml(pair.second) +
"\"";
3326 if (!this->children.empty()) {
3330 for (
auto& child : children) {
3332 auto str = child->svg_to_string(indent_level + 1);
3333 if (str.size()) ret += str +
"\n";
3336 return ret += indent +
"</" +
tag() +
">";
3339 return ret +=
" />";
3345 const std::string& tag,
3346 const std::string& content,
3347 const size_t indent_level) {
3348 auto indent = std::string(indent_level,
'\t');
3349 std::string ret = indent +
"<" + tag;
3350 for (
auto& pair: element.attrs())
3351 ret +=
" " + pair.first +
"=" +
"\"" +
escape_xml(pair.second) +
"\"";
3352 return ret +=
">" +
escape_xml(content) +
"</" + tag +
">";
3356 inline std::string
to_string(
const std::map<std::string, AttributeMap>& css,
const size_t indent_level) {
3358 auto indent = std::string(indent_level,
'\t'), ret = std::string();
3359 for (
auto& selector : css) {
3361 ret += indent +
"\t\t" + selector.first +
" {\n";
3362 for (
auto& attr : selector.second.attrs())
3363 ret += indent +
"\t\t\t" + attr.first +
": " + attr.second +
";\n";
3364 ret += indent +
"\t\t" +
"}\n";
3371 auto indent = std::string(indent_level,
'\t');
3373 if (!this->css.empty() || !this->
media_queries.empty() || !this->keyframes.empty()) {
3374 std::string ret = indent +
"<style type=\"text/css\">\n" +
3375 indent +
"\t<![CDATA[\n";
3378 ret +=
to_string(this->css, indent_level);
3381 for (
auto& media : this->media_queries) {
3382 ret += indent +
"\t\t@media " + media.first +
" {\n" +
3383 to_string(media.second, indent_level + 1) +
3384 indent +
"\t\t" +
"}\n";
3388 for (
auto& anim : this->keyframes) {
3389 ret += indent +
"\t\t@keyframes " + anim.first +
" {\n" +
3390 to_string(anim.second, indent_level + 1) +
3391 indent +
"\t\t" +
"}\n";
3394 ret += indent +
"\t]]>\n";
3395 return ret + indent +
"</style>";
3401 inline std::unique_ptr<Element> SVG::Style::clone_element_impl()
const {
3402 return clone_as<Style>();
3405 inline std::string Text::svg_to_string(
const size_t indent_level)
const {
3406 return detail::text_content_element_to_string(*
this,
tag_name(this->
kind()), this->content, indent_level);
3410 return detail::text_content_element_to_string(*
this,
tag_name(this->
kind()), this->
content, indent_level);
3414 layout_bbox_ = bbox;
3415 has_layout_bbox_ =
true;
3420 layout_bbox_padding_ = padding;
3425 return bbox_padding({ padding, padding, padding, padding });
3429 has_layout_bbox_ =
false;
3435 return measured_layout_bbox();
3439 if (has_layout_bbox_)
return layout_bbox_;
3441 if (std::isnan(bbox.x1) || std::isnan(bbox.x2) ||
3442 std::isnan(bbox.y1) || std::isnan(bbox.y2)) {
3446 bbox.x1 - layout_bbox_padding_.x1,
3447 bbox.x2 + layout_bbox_padding_.x2,
3448 bbox.y1 - layout_bbox_padding_.y1,
3449 bbox.y2 + layout_bbox_padding_.y2
3453 inline detail::AffineTransform Element::transform_for(
3454 const Element* element,
3455 const detail::AffineTransform& parent_transform) {
3456 if (element->has_attr(
"transform")) {
3457 return detail::multiply(parent_transform,
3458 detail::parse_supported_transform(element->get_attr(
"transform")));
3460 return parent_transform;
3464 inline unsigned alignment_count_bits(
Alignment value) {
3467 count += value & 1u;
3473 inline bool bbox_is_measured(
const Element::BoundingBox& bbox) {
3474 return !(std::isnan(bbox.x1) || std::isnan(bbox.x2) ||
3475 std::isnan(bbox.y1) || std::isnan(bbox.y2));
3478 inline double visible_stroke_width(
const Element& element) {
3479 const auto stroke = element.get_attr(
"stroke");
3480 if (stroke.empty() || stroke ==
"none")
return 0;
3482 const auto width = element.get_attr<
double>(
"stroke-width", 1);
3483 return width > 0 ? width : 0;
3486 inline bool renders_in_place(
const Element* element) {
3487 for (
auto* current = element; current; current = current->parent()) {
3488 const auto kind = current->kind();
3489 if (kind == ElementKind::Defs || kind == ElementKind::Style ||
3490 kind == ElementKind::Symbol ||
3491 kind == ElementKind::LinearGradient ||
3492 kind == ElementKind::RadialGradient ||
3493 kind == ElementKind::Stop) {
3500 inline double bbox_center_x(
const Element::BoundingBox& bbox) {
3501 return bbox.x1 + (bbox.x2 - bbox.x1) / 2;
3504 inline double bbox_center_y(
const Element::BoundingBox& bbox) {
3505 return bbox.y1 + (bbox.y2 - bbox.y1) / 2;
3508 inline double bbox_anchor_x(
const Element::BoundingBox& bbox,
Anchor anchor) {
3509 if (anchor == Anchor::Start)
return bbox.x1;
3510 if (anchor == Anchor::Center)
return bbox_center_x(bbox);
3511 if (anchor == Anchor::End)
return bbox.x2;
3512 throw std::invalid_argument(
"rotate_about_bbox requires a valid x anchor");
3515 inline double bbox_anchor_y(
const Element::BoundingBox& bbox,
Anchor anchor) {
3516 if (anchor == Anchor::Start)
return bbox.y1;
3517 if (anchor == Anchor::Center)
return bbox_center_y(bbox);
3518 if (anchor == Anchor::End)
return bbox.y2;
3519 throw std::invalid_argument(
"rotate_about_bbox requires a valid y anchor");
3523 inline Element::BoundingBox Element::include_stroke_width(
const BoundingBox& bbox)
const {
3524 if (!detail::bbox_is_measured(bbox))
return bbox;
3525 const auto outset = detail::visible_stroke_width(*
this) / 2;
3526 return { bbox.x1 - outset, bbox.x2 + outset, bbox.y1 - outset, bbox.y2 + outset };
3532 validate_appendable();
3534 throw std::logic_error(
"rotate_about_bbox requires an owning element transform list");
3537 if (!detail::bbox_is_measured(bbox)) {
3538 throw std::logic_error(
"rotate_about_bbox requires measured layout bounds");
3540 return rotate(degrees,
3541 detail::bbox_anchor_x(bbox, x_anchor),
3542 detail::bbox_anchor_y(bbox, y_anchor));
3546 const auto href = this->get_attr(
"href", this->get_attr(
"xlink:href"));
3547 if (href.empty() || href[0] !=
'#')
return { NAN, NAN, NAN, NAN };
3549 const auto* root = this->owner_svg();
3550 if (!root)
return { NAN, NAN, NAN, NAN };
3552 const auto* referenced = root->get_element_by_id(href.substr(1));
3553 if (!referenced || referenced ==
this)
return { NAN, NAN, NAN, NAN };
3556 if (!detail::bbox_is_measured(bbox))
return bbox;
3558 const auto x = this->get_attr<double>(
"x", 0);
3559 const auto y = this->get_attr<double>(
"y", 0);
3560 const auto width = this->get_attr<double>(
"width", NAN);
3561 const auto height = this->get_attr<double>(
"height", NAN);
3562 const auto viewbox = detail::parse_transform_args(referenced->get_attr(
"viewBox"));
3563 if (!std::isnan(
width) && !std::isnan(
height) && viewbox.size() == 4 &&
3564 viewbox[2] != 0 && viewbox[3] != 0) {
3565 const auto sx =
width / viewbox[2];
3566 const auto sy =
height / viewbox[3];
3568 x + (bbox.x1 - viewbox[0]) * sx,
3569 x + (bbox.x2 - viewbox[0]) * sx,
3570 y + (bbox.y1 - viewbox[1]) * sy,
3571 y + (bbox.y2 - viewbox[1]) * sy
3573 return include_stroke_width(bbox);
3576 bbox = { bbox.x1 + x, bbox.x2 + x, bbox.y1 + y, bbox.y2 + y };
3577 return include_stroke_width(bbox);
3581 const double x = this->get_attr<double>(
"x", 0);
3582 const double y = this->get_attr<double>(
"y", 0);
3583 const double width = this->get_attr<double>(
"width", NAN);
3584 const double height = this->get_attr<double>(
"height", NAN);
3586 if (!std::isnan(width) && !std::isnan(height)) {
3587 return { x, x + width, y, y + height };
3592 if (!detail::bbox_is_measured(bbox))
return bbox;
3593 return { bbox.x1 + x, bbox.x2 + x, bbox.y1 + y, bbox.y2 + y };
3606 static_cast<Alignment>(RelativeAlignment::Left) |
3607 static_cast<Alignment>(RelativeAlignment::Top) |
3608 static_cast<Alignment>(RelativeAlignment::Right) |
3609 static_cast<Alignment>(RelativeAlignment::Bottom);
3612 static_cast<Alignment>(Anchor::Center) |
3615 const Alignment relative = alignment & relative_mask;
3616 Alignment anchor = alignment & anchor_mask;
3617 if (detail::alignment_count_bits(relative) != 1) {
3618 throw std::invalid_argument(
"snap_to requires exactly one relative alignment");
3621 anchor =
static_cast<Alignment>(Anchor::Center);
3622 }
else if (detail::alignment_count_bits(anchor) != 1) {
3623 throw std::invalid_argument(
"snap_to requires at most one anchor");
3625 if ((alignment & ~(relative_mask | anchor_mask)) != 0) {
3626 throw std::invalid_argument(
"snap_to received unknown alignment bits");
3631 if (!detail::bbox_is_measured(source_box) || !detail::bbox_is_measured(target_box)) {
3632 throw std::logic_error(
"snap_to requires measured layout bounds");
3637 if (relative ==
static_cast<Alignment>(RelativeAlignment::Left)) {
3638 dx = target_box.x1 - source_box.x2;
3639 }
else if (relative ==
static_cast<Alignment>(RelativeAlignment::Right)) {
3640 dx = target_box.x2 - source_box.x1;
3641 }
else if (relative ==
static_cast<Alignment>(RelativeAlignment::Top)) {
3642 dy = target_box.y1 - source_box.y2;
3643 }
else if (relative ==
static_cast<Alignment>(RelativeAlignment::Bottom)) {
3644 dy = target_box.y2 - source_box.y1;
3647 if (relative ==
static_cast<Alignment>(RelativeAlignment::Left) ||
3648 relative ==
static_cast<Alignment>(RelativeAlignment::Right)) {
3649 if (anchor ==
static_cast<Alignment>(Anchor::Start)) {
3650 dy = target_box.y1 - source_box.y1;
3651 }
else if (anchor ==
static_cast<Alignment>(Anchor::Center)) {
3652 dy = detail::bbox_center_y(target_box) - detail::bbox_center_y(source_box);
3654 dy = target_box.y2 - source_box.y2;
3657 if (anchor ==
static_cast<Alignment>(Anchor::Start)) {
3658 dx = target_box.x1 - source_box.x1;
3659 }
else if (anchor ==
static_cast<Alignment>(Anchor::Center)) {
3660 dx = detail::bbox_center_x(target_box) - detail::bbox_center_x(source_box);
3662 dx = target_box.x2 - source_box.x2;
3666 this->transform().translate(dx + offset.first, dy + offset.second);
3673 return align_to(target, axis, Anchor::Center, offset);
3680 if (axis != Axis::X && axis != Axis::Y) {
3681 throw std::invalid_argument(
"align_to requires a valid axis");
3683 if (anchor != Anchor::Start && anchor != Anchor::Center && anchor != Anchor::End) {
3684 throw std::invalid_argument(
"align_to requires a valid anchor");
3689 if (!detail::bbox_is_measured(source_box) || !detail::bbox_is_measured(target_box)) {
3690 throw std::logic_error(
"align_to requires measured layout bounds");
3693 double dx = offset.first;
3694 double dy = offset.second;
3695 if (axis == Axis::X) {
3696 if (anchor == Anchor::Start) {
3697 dx += target_box.x1 - source_box.x1;
3698 }
else if (anchor == Anchor::Center) {
3699 dx += detail::bbox_center_x(target_box) - detail::bbox_center_x(source_box);
3701 dx += target_box.x2 - source_box.x2;
3704 if (anchor == Anchor::Start) {
3705 dy += target_box.y1 - source_box.y1;
3706 }
else if (anchor == Anchor::Center) {
3707 dy += detail::bbox_center_y(target_box) - detail::bbox_center_y(source_box);
3709 dy += target_box.y2 - source_box.y2;
3713 this->transform().translate(dx, dy);
3717 inline void Element::autoscale_nested_svgs(
const AutoscaleOptions& options,
bool responsive) {
3718 for (
auto& child : this->children) {
3719 if (child->kind() == SVG::static_kind) {
3720 auto* svg_child =
static_cast<SVG*
>(child.get());
3722 svg_child->responsive_autoscale(options);
3724 svg_child->autoscale(options);
3727 child->autoscale_nested_svgs(options, responsive);
3732 inline Element::BoundingBox Element::get_autoscale_bbox()
const {
3733 Element::BoundingBox bbox =
3735 ? Element::BoundingBox(NAN, NAN, NAN, NAN)
3736 : this->measured_layout_bbox();
3741 inline void Element::set_viewbox_from_bbox(
const BoundingBox& bbox,
const Margins& margins) {
3742 double width = bbox.x2 - bbox.x1,
3743 height = bbox.y2 - bbox.y1;
3745 width += margins.x1 + margins.x2;
3746 height += margins.y1 + margins.y2;
3747 double x1 = bbox.x1 - margins.x1,
3748 y1 = bbox.y1 - margins.y1;
3750 std::stringstream viewbox;
3751 viewbox << std::fixed << std::setprecision(1)
3756 this->set_attr(
"viewBox", viewbox.str());
3759 inline void Element::autoscale(
const double margin) {
3762 this->autoscale_nested_svgs(nested_options,
false);
3763 auto bbox = this->get_autoscale_bbox();
3764 double width = bbox.x2 - bbox.x1,
3765 height = bbox.y2 - bbox.y1;
3768 width * margin, width * margin,
3769 height * margin, height * margin
3771 this->autoscale(options);
3774 inline void Element::autoscale(
const Margins& margins) {
3785 this->autoscale_nested_svgs(options,
false);
3787 auto bbox = this->get_autoscale_bbox();
3788 double width = bbox.x2 - bbox.x1 + options.margins.x1 + options.margins.x2,
3789 height = bbox.y2 - bbox.y1 + options.margins.y1 + options.margins.y2;
3791 this->set_attr(
"width", width)
3792 .set_attr(
"height", height);
3794 this->set_viewbox_from_bbox(bbox, options.margins);
3799 this->autoscale_nested_svgs(nested_options,
true);
3800 auto bbox = this->get_autoscale_bbox();
3801 double width = bbox.x2 - bbox.x1,
3802 height = bbox.y2 - bbox.y1;
3805 width * margin, width * margin,
3806 height * margin, height * margin
3816 this->autoscale_nested_svgs(options,
true);
3818 this->set_viewbox_from_bbox(this->get_autoscale_bbox(), options.margins);
3824 for (
auto element = elements.begin(); element != elements.end(); ++element) {
3825 const auto* current = *element;
3826 if (visible_only && !detail::renders_in_place(current)) {
3829 auto current_box = current->measured_layout_bbox();
3830 if (!detail::bbox_is_measured(current_box)) {
3834 const auto& transform = element.transform();
3835 const auto p1 = transform.apply({ current_box.x1, current_box.y1 });
3836 const auto p2 = transform.apply({ current_box.x2, current_box.y1 });
3837 const auto p3 = transform.apply({ current_box.x2, current_box.y2 });
3838 const auto p4 = transform.apply({ current_box.x1, current_box.y2 });
3840 std::min(std::min(p1.first, p2.first), std::min(p3.first, p4.first)),
3841 std::max(std::max(p1.first, p2.first), std::max(p3.first, p4.first)),
3842 std::min(std::min(p1.second, p2.second), std::min(p3.second, p4.second)),
3843 std::max(std::max(p1.second, p2.second), std::max(p3.second, p4.second)));
3844 box = transformed_box + box;
3850 Element::ChildMap child_map;
3852 child_map[child->tag()].push_back(child);
3858 Element::ConstChildMap child_map;
3860 child_map[child->tag()].push_back(child);
3866 std::vector<Element*> ret;
3867 for (
auto* child : this->
descendants()) ret.push_back(child);
3874 std::vector<const Element*> ret;
3875 for (
const auto* child : this->
descendants()) ret.push_back(child);
3885 ret << std::move(left) << std::move(right);
3889 svg_child->autoscale(margins);
3892 double x = 0, height = 0;
3894 svg_child->set_attr(
"x", x).set_attr(
"y", 0);
3895 x += svg_child->width();
3896 height = std::max(height, svg_child->height());
3899 ret.set_attr(
"width", x).set_attr(
"height", height);
3903 inline std::vector<Point> bounding_polygon(std::vector<Shape*>& shapes) {
3907 std::vector<Point> points;
3908 for (
auto& shp : shapes) {
3909 auto temp_points = shp->points();
3910 std::move(temp_points.begin(), temp_points.end(), std::back_inserter(points));
3913 return util::convex_hull(points);
3916 inline SVG merge(std::vector<SVG>& frames,
const double width,
const int max_frame_width) {
3921 double x = 0, y = 0, total_width = 0, total_height = 0;
3922 for (
auto& frame : frames) {
3925 if (frame.width() > max_frame_width) {
3926 const double scale_factor = max_frame_width/frame.width();
3927 frame.set_attr(
"width", max_frame_width);
3928 frame.set_attr(
"height", frame.height() * scale_factor);
3933 double current_height = 0;
3934 for (
auto& frame : frames) {
3936 if ((x + frame.width()) > width) {
3937 total_width = std::max(total_width, x);
3939 y += current_height;
3943 frame.set_attr(
"x", x).set_attr(
"y", y);
3945 current_height = std::max(current_height, frame.height());
3946 root << std::move(frame);
3949 total_height = y + current_height;
3952 root.set_attr(
"viewBox") << 0 <<
" " << 0 <<
" " << total_width <<
" " << total_height;
3953 root.set_attr(
"width", total_width).set_attr(
"height", total_height);
3960 const double duration = (double)frames.size() / fps;
3961 const double frame_step = 1.0 / fps;
3962 int current_frame = 0;
3964 root.
style(
"svg.animated").set_attr(
"animation-iteration-count",
"infinite")
3965 .set_attr(
"animation-timing-function",
"step-end")
3966 .set_attr(
"animation-duration", std::to_string(duration) +
"s")
3967 .set_attr(
"opacity", 0);
3970 for (
auto& frame : frames) {
3971 std::string frame_id =
"frame_" + std::to_string(current_frame);
3972 frame.set_attr(
"id", frame_id).set_attr(
"class",
"animated");
3973 root.
style(
"#" + frame_id).set_attr(
"animation-name",
3974 "anim_" + std::to_string(current_frame));
3976 root << std::move(frame);
3980 for (
size_t i = 0, ilen = frames.size(); i < ilen; i++) {
3981 auto& anim = root.
keyframes(
"anim_" + std::to_string(i));
3982 double begin_pct = (double)i / frames.size(),
3983 end_pct = (double)(i + 1) / frames.size();
3984 anim[
"0%"].set_attr(
"opacity", 0);
3985 anim[std::to_string(begin_pct * 100) +
"%"].set_attr(
"opacity", 1);
3986 anim[std::to_string(end_pct * 100) +
"%"].set_attr(
"opacity", 0);
3990 double width = 0, height = 0;
3994 width = std::max(width, child->width());
3995 height = std::max(height, child->height());
3998 root.set_attr(
"viewBox",
"0 0 " + std::to_string(width) +
" " + std::to_string(height));
4002 child->set_attr(
"x", (width - child->width())/2).set_attr(
"y", (height - child->height())/2);