Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 40819b7

Browse files
committedDec 7, 2024·
Vendor Setting and Tag into styled_text
This lets us remove the `swash` dependency from `styled_text`.
1 parent 1882226 commit 40819b7

File tree

7 files changed

+245
-12
lines changed

7 files changed

+245
-12
lines changed
 

‎Cargo.lock

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎parley/src/resolve/mod.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ impl ResolveContext {
251251
/// Resolves font variation settings.
252252
pub(crate) fn resolve_variations(
253253
&mut self,
254-
variations: &FontSettings<FontVariation>,
254+
variations: &FontSettings<styled_text::FontVariation>,
255255
) -> Resolved<FontVariation> {
256256
match variations {
257257
FontSettings::Source(source) => {
@@ -261,7 +261,11 @@ impl ResolveContext {
261261
}
262262
FontSettings::List(settings) => {
263263
self.tmp_variations.clear();
264-
self.tmp_variations.extend_from_slice(settings);
264+
self.tmp_variations.extend(
265+
settings
266+
.iter()
267+
.map(|v| FontVariation::from((v.tag, v.value))),
268+
);
265269
}
266270
}
267271
if self.tmp_variations.is_empty() {
@@ -276,7 +280,7 @@ impl ResolveContext {
276280
/// Resolves font feature settings.
277281
pub(crate) fn resolve_features(
278282
&mut self,
279-
features: &FontSettings<FontFeature>,
283+
features: &FontSettings<styled_text::FontFeature>,
280284
) -> Resolved<FontFeature> {
281285
match features {
282286
FontSettings::Source(source) => {
@@ -285,7 +289,8 @@ impl ResolveContext {
285289
}
286290
FontSettings::List(settings) => {
287291
self.tmp_features.clear();
288-
self.tmp_features.extend_from_slice(settings);
292+
self.tmp_features
293+
.extend(settings.iter().map(|f| FontFeature::from((f.tag, f.value))));
289294
}
290295
}
291296
if self.tmp_features.is_empty() {

‎parley/src/style/mod.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ mod styleset;
77

88
use alloc::borrow::Cow;
99
pub use styled_text::{
10-
Brush, FontFamily, FontFeature, FontSettings, FontStack, FontVariation, GenericFamily,
11-
Stretch as FontStretch, Style as FontStyle, StyleProperty, Weight as FontWeight,
10+
Brush, FontFamily, FontSettings, FontStack, GenericFamily, Stretch as FontStretch,
11+
Style as FontStyle, StyleProperty, Weight as FontWeight,
1212
};
1313

1414
pub use styleset::StyleSet;
1515

16+
/// Setting for a font variation.
17+
pub type FontVariation = swash::Setting<f32>;
18+
19+
/// Setting for a font feature.
20+
pub type FontFeature = swash::Setting<u16>;
21+
1622
#[derive(Debug, Clone, Copy)]
1723
pub enum WhiteSpaceCollapse {
1824
Collapse,
@@ -33,9 +39,9 @@ pub struct TextStyle<'a, B: Brush> {
3339
/// Font weight.
3440
pub font_weight: FontWeight,
3541
/// Font variation settings.
36-
pub font_variations: FontSettings<'a, FontVariation>,
42+
pub font_variations: FontSettings<'a, styled_text::FontVariation>,
3743
/// Font feature settings.
38-
pub font_features: FontSettings<'a, FontFeature>,
44+
pub font_features: FontSettings<'a, styled_text::FontFeature>,
3945
/// Locale.
4046
pub locale: Option<&'a str>,
4147
/// Brush for rendering text.

‎styled_text/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,3 @@ libm = ["dep:core_maths"]
2121

2222
[dependencies]
2323
core_maths = { version = "0.1.0", optional = true }
24-
swash = { workspace = true }

‎styled_text/src/font_settings.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ use core::fmt;
77

88
/// Setting for a font variation.
99
// FIXME(style): We should copy the Setting definition from swash instead of having a dep
10-
pub type FontVariation = swash::Setting<f32>;
10+
pub type FontVariation = crate::setting::Setting<f32>;
1111

1212
/// Setting for a font feature.
1313
// FIXME(style): We should copy the Setting definition from swash instead of having a dep
14-
pub type FontFeature = swash::Setting<u16>;
14+
pub type FontFeature = crate::setting::Setting<u16>;
1515

1616
/// Font settings that can be supplied as a raw source string or
1717
/// a parsed slice.

‎styled_text/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod font_family;
2727
mod font_settings;
2828
mod font_stack;
2929
mod generic;
30+
pub mod setting;
3031
mod style_property;
3132

3233
pub use attributes::{Stretch, Style, Weight};

‎styled_text/src/setting.rs

+223
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// Copyright 2024 the Parley Authors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
use core::fmt;
5+
6+
/// Four byte tag value.
7+
pub type Tag = u32;
8+
9+
/// Creates a tag from four bytes.
10+
pub const fn tag_from_bytes(bytes: &[u8; 4]) -> Tag {
11+
(bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 | bytes[3] as u32
12+
}
13+
14+
/// Creates a tag from the first four bytes of a string, inserting
15+
/// spaces for any missing bytes.
16+
pub fn tag_from_str_lossy(s: &str) -> Tag {
17+
let mut bytes = [b' '; 4];
18+
for (i, b) in s.as_bytes().iter().enumerate().take(4) {
19+
bytes[i] = *b;
20+
}
21+
tag_from_bytes(&bytes)
22+
}
23+
24+
/// Setting combining a tag and a value for features and variations.
25+
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
26+
pub struct Setting<T> {
27+
/// The tag that identifies the setting.
28+
pub tag: Tag,
29+
/// The value for the setting.
30+
pub value: T,
31+
}
32+
33+
impl<T: fmt::Display> fmt::Display for Setting<T> {
34+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35+
let bytes = self.tag.to_be_bytes();
36+
let tag_name = core::str::from_utf8(&bytes).unwrap_or("");
37+
write!(f, "\"{}\" {}", tag_name, self.value)
38+
}
39+
}
40+
41+
impl Setting<u16> {
42+
/// Parses a feature setting according to the CSS grammar.
43+
pub fn parse(s: &str) -> Option<Self> {
44+
Self::parse_list(s).next()
45+
}
46+
47+
/// Parses a comma separated list of feature settings according to the CSS
48+
/// grammar.
49+
pub fn parse_list(s: &str) -> impl Iterator<Item = Self> + '_ + Clone {
50+
ParseList::new(s)
51+
.map(|(_, tag, value_str)| {
52+
let (ok, value) = match value_str {
53+
"on" | "" => (true, 1),
54+
"off" => (true, 0),
55+
_ => match value_str.parse::<u16>() {
56+
Ok(value) => (true, value),
57+
_ => (false, 0),
58+
},
59+
};
60+
(ok, tag, value)
61+
})
62+
.take_while(|(ok, _, _)| *ok)
63+
.map(|(_, tag, value)| Self { tag, value })
64+
}
65+
}
66+
67+
impl Setting<f32> {
68+
/// Parses a variation setting according to the CSS grammar.
69+
pub fn parse(s: &str) -> Option<Self> {
70+
Self::parse_list(s).next()
71+
}
72+
73+
/// Parses a comma separated list of variation settings according to the
74+
/// CSS grammar.
75+
pub fn parse_list(s: &str) -> impl Iterator<Item = Self> + '_ + Clone {
76+
ParseList::new(s)
77+
.map(|(_, tag, value_str)| {
78+
let (ok, value) = match value_str.parse::<f32>() {
79+
Ok(value) => (true, value),
80+
_ => (false, 0.),
81+
};
82+
(ok, tag, value)
83+
})
84+
.take_while(|(ok, _, _)| *ok)
85+
.map(|(_, tag, value)| Self { tag, value })
86+
}
87+
}
88+
89+
impl<T> From<(Tag, T)> for Setting<T> {
90+
fn from(v: (Tag, T)) -> Self {
91+
Self {
92+
tag: v.0,
93+
value: v.1,
94+
}
95+
}
96+
}
97+
98+
impl<T: Copy> From<&(Tag, T)> for Setting<T> {
99+
fn from(v: &(Tag, T)) -> Self {
100+
Self {
101+
tag: v.0,
102+
value: v.1,
103+
}
104+
}
105+
}
106+
107+
impl<T: Copy> From<&([u8; 4], T)> for Setting<T> {
108+
fn from(v: &([u8; 4], T)) -> Self {
109+
Self {
110+
tag: tag_from_bytes(&v.0),
111+
value: v.1,
112+
}
113+
}
114+
}
115+
116+
impl<T: Copy> From<&(&[u8; 4], T)> for Setting<T> {
117+
fn from(v: &(&[u8; 4], T)) -> Self {
118+
Self {
119+
tag: tag_from_bytes(v.0),
120+
value: v.1,
121+
}
122+
}
123+
}
124+
125+
impl<T> From<(&str, T)> for Setting<T> {
126+
fn from(v: (&str, T)) -> Self {
127+
Self {
128+
tag: tag_from_str_lossy(v.0),
129+
value: v.1,
130+
}
131+
}
132+
}
133+
134+
impl<T: Copy> From<&(&str, T)> for Setting<T> {
135+
fn from(v: &(&str, T)) -> Self {
136+
Self {
137+
tag: tag_from_str_lossy(v.0),
138+
value: v.1,
139+
}
140+
}
141+
}
142+
143+
#[derive(Clone)]
144+
struct ParseList<'a> {
145+
source: &'a [u8],
146+
len: usize,
147+
pos: usize,
148+
}
149+
150+
impl<'a> ParseList<'a> {
151+
fn new(source: &'a str) -> Self {
152+
Self {
153+
source: source.as_bytes(),
154+
len: source.len(),
155+
pos: 0,
156+
}
157+
}
158+
}
159+
160+
impl<'a> Iterator for ParseList<'a> {
161+
type Item = (usize, Tag, &'a str);
162+
163+
fn next(&mut self) -> Option<Self::Item> {
164+
let mut pos = self.pos;
165+
while pos < self.len && {
166+
let ch = self.source[pos];
167+
ch.is_ascii_whitespace() || ch == b','
168+
} {
169+
pos += 1;
170+
}
171+
self.pos = pos;
172+
if pos >= self.len {
173+
return None;
174+
}
175+
let first = self.source[pos];
176+
let mut start = pos;
177+
let quote = match first {
178+
b'"' | b'\'' => {
179+
pos += 1;
180+
start += 1;
181+
first
182+
}
183+
_ => return None,
184+
};
185+
let mut tag_str = None;
186+
while pos < self.len {
187+
if self.source[pos] == quote {
188+
tag_str = core::str::from_utf8(self.source.get(start..pos)?).ok();
189+
pos += 1;
190+
break;
191+
}
192+
pos += 1;
193+
}
194+
self.pos = pos;
195+
let tag_str = tag_str?;
196+
if tag_str.len() != 4 || !tag_str.is_ascii() {
197+
return None;
198+
}
199+
let tag = tag_from_str_lossy(tag_str);
200+
while pos < self.len {
201+
if !self.source[pos].is_ascii_whitespace() {
202+
break;
203+
}
204+
pos += 1;
205+
}
206+
self.pos = pos;
207+
start = pos;
208+
let mut end = start;
209+
while pos < self.len {
210+
if self.source[pos] == b',' {
211+
pos += 1;
212+
break;
213+
}
214+
pos += 1;
215+
end += 1;
216+
}
217+
let value = core::str::from_utf8(self.source.get(start..end)?)
218+
.ok()?
219+
.trim();
220+
self.pos = pos;
221+
Some((pos, tag, value))
222+
}
223+
}

0 commit comments

Comments
 (0)
Please sign in to comment.