| 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 | |