1 | // Copyright 2019 the Resvg Authors |
2 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
3 | |
4 | use svgtypes::{Length, LengthUnit as Unit}; |
5 | |
6 | use super::converter; |
7 | use super::svgtree::{AId, SvgNode}; |
8 | use crate::Units; |
9 | |
10 | #[inline (never)] |
11 | pub(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 | |
67 | pub(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, object_units:Units::UserSpaceOnUse, state) |
74 | } |
75 | |
76 | #[inline (never)] |
77 | pub(crate) fn convert_list(node: SvgNode, aid: AId, state: &converter::State) -> Option<Vec<f32>> { |
78 | if let Some(text: &str) = node.attribute::<&str>(aid) { |
79 | let mut num_list: Vec = Vec::new(); |
80 | for length: 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 | |
90 | fn convert_percent(length: Length, base: f32) -> f32 { |
91 | base * (length.number as f32) / 100.0 |
92 | } |
93 | |
94 | #[inline (never)] |
95 | pub(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 | // skip Root |
100 | 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 | // If `font-size` has percent units that it's value |
114 | // is relative to the parent node `font-size`. |
115 | 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 | |
126 | fn convert_named_font_size(name: &str, parent_font_size: f32) -> f32 { |
127 | let factor: i32 = 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 | // 'On a computer screen a scaling factor of 1.2 is suggested between adjacent indexes.' |
144 | parent_font_size * 1.2f32.powi(factor) |
145 | } |
146 | |