1use svgtypes::{Length, LengthUnit as Unit};
5
6use super::converter;
7use super::svgtree::{AId, SvgNode};
8use crate::Units;
9
10#[inline(never)]
11pub(crate) fn convert_length(
12 length: Length,
13 node: SvgNode,
14 aid: AId,
15 object_units: Units,
16 state: &converter::State,
17) -> f32 {
18 let dpi = state.opt.dpi;
19 let n = length.number as f32;
20 match length.unit {
21 Unit::None | Unit::Px => n,
22 Unit::Em => n * resolve_font_size(node, state),
23 Unit::Ex => n * resolve_font_size(node, state) / 2.0,
24 Unit::In => n * dpi,
25 Unit::Cm => n * dpi / 2.54,
26 Unit::Mm => n * dpi / 25.4,
27 Unit::Pt => n * dpi / 72.0,
28 Unit::Pc => n * dpi / 6.0,
29 Unit::Percent => {
30 if object_units == Units::ObjectBoundingBox {
31 n / 100.0
32 } else {
33 let view_box = state.view_box;
34
35 match aid {
36 AId::Cx
37 | AId::Dx
38 | AId::Fx
39 | AId::MarkerWidth
40 | AId::RefX
41 | AId::Rx
42 | AId::Width
43 | AId::X
44 | AId::X1
45 | AId::X2 => convert_percent(length, view_box.width()),
46 AId::Cy
47 | AId::Dy
48 | AId::Fy
49 | AId::Height
50 | AId::MarkerHeight
51 | AId::RefY
52 | AId::Ry
53 | AId::Y
54 | AId::Y1
55 | AId::Y2 => convert_percent(length, view_box.height()),
56 _ => {
57 let mut vb_len = view_box.width().powi(2) + view_box.height().powi(2);
58 vb_len = (vb_len / 2.0).sqrt();
59 convert_percent(length, vb_len)
60 }
61 }
62 }
63 }
64 }
65}
66
67pub(crate) fn convert_user_length(
68 length: Length,
69 node: SvgNode,
70 aid: AId,
71 state: &converter::State,
72) -> f32 {
73 convert_length(length, node, aid, Units::UserSpaceOnUse, state)
74}
75
76#[inline(never)]
77pub(crate) fn convert_list(node: SvgNode, aid: AId, state: &converter::State) -> Option<Vec<f32>> {
78 if let Some(text) = node.attribute::<&str>(aid) {
79 let mut num_list = Vec::new();
80 for length in svgtypes::LengthListParser::from(text).flatten() {
81 num_list.push(convert_user_length(length, node, aid, state));
82 }
83
84 Some(num_list)
85 } else {
86 None
87 }
88}
89
90fn convert_percent(length: Length, base: f32) -> f32 {
91 base * (length.number as f32) / 100.0
92}
93
94#[inline(never)]
95pub(crate) fn resolve_font_size(node: SvgNode, state: &converter::State) -> f32 {
96 let nodes: Vec<_> = node.ancestors().collect();
97 let mut font_size = state.opt.font_size;
98 for n in nodes.iter().rev().skip(1) {
99 if let Some(length) = n.try_attribute::<Length>(AId::FontSize) {
101 let dpi = state.opt.dpi;
102 let n = length.number as f32;
103 font_size = match length.unit {
104 Unit::None | Unit::Px => n,
105 Unit::Em => n * font_size,
106 Unit::Ex => n * font_size / 2.0,
107 Unit::In => n * dpi,
108 Unit::Cm => n * dpi / 2.54,
109 Unit::Mm => n * dpi / 25.4,
110 Unit::Pt => n * dpi / 72.0,
111 Unit::Pc => n * dpi / 6.0,
112 Unit::Percent => {
113 length.number as f32 * font_size * 0.01
116 }
117 }
118 } else if let Some(name) = n.attribute(AId::FontSize) {
119 font_size = convert_named_font_size(name, font_size);
120 }
121 }
122
123 font_size
124}
125
126fn convert_named_font_size(name: &str, parent_font_size: f32) -> f32 {
127 let factor = match name {
128 "xx-small" => -3,
129 "x-small" => -2,
130 "small" => -1,
131 "medium" => 0,
132 "large" => 1,
133 "x-large" => 2,
134 "xx-large" => 3,
135 "smaller" => -1,
136 "larger" => 1,
137 _ => {
138 log::warn!("Invalid 'font-size' value: '{}'.", name);
139 0
140 }
141 };
142
143 parent_font_size * 1.2f32.powi(factor)
145}