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