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
5use svgtypes::{Length, LengthUnit as Unit};
6
7use super::converter;
8use super::svgtree::{AId, SvgNode};
9use crate::Units;
10
11#[inline(never)]
12pub(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
68pub(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)]
78pub(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
91fn convert_percent(length: Length, base: f32) -> f32 {
92 base * (length.number as f32) / 100.0
93}
94
95#[inline(never)]
96pub(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
127fn 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