1use super::internal::{head::Os2, RawFont};
4use super::{tag_from_bytes, FontRef, Setting, Tag};
5
6use core::fmt;
7use core::hash::{Hash, Hasher};
8
9const WDTH: Tag = tag_from_bytes(b"wdth");
11const WGHT: Tag = tag_from_bytes(b"wght");
12const SLNT: Tag = tag_from_bytes(b"slnt");
13const ITAL: Tag = tag_from_bytes(b"ital");
14
15#[derive(Copy, Clone)]
19pub struct Attributes(pub u32);
20
21impl Attributes {
22 pub const fn new(stretch: Stretch, weight: Weight, style: Style) -> Self {
25 let stretch = stretch.0 as u32 & 0x1FF;
26 let weight = weight.0 as u32 & 0x3FF;
27 let style = style.pack();
28 Self(style | weight << 9 | stretch << 19)
29 }
30
31 pub fn from_font<'a>(font: &FontRef<'a>) -> Self {
33 let mut attrs = Self::from_os2(font.os2().as_ref());
34 let mut var_bits = 0;
35 for var in font.variations() {
36 match var.tag() {
37 WDTH => var_bits |= 1,
38 WGHT => var_bits |= 2,
39 SLNT => var_bits |= 4,
40 ITAL => var_bits |= 8,
41 _ => {}
42 }
43 }
44 attrs.0 |= var_bits << 28;
45 attrs
46 }
47
48 pub(crate) fn from_os2(os2: Option<&Os2>) -> Self {
49 if let Some(os2) = os2 {
50 let flags = os2.selection_flags();
51 let style = if flags.italic() {
52 Style::Italic
53 } else if flags.oblique() {
54 Style::Oblique(ObliqueAngle::default())
55 } else {
56 Style::Normal
57 };
58 let weight = Weight(os2.weight_class() as u16);
59 let stretch = Stretch::from_raw(os2.width_class() as u16);
60 Self::new(stretch, weight, style)
61 } else {
62 Self::default()
63 }
64 }
65
66 pub fn stretch(&self) -> Stretch {
68 Stretch((self.0 >> 19 & 0x1FF) as u16)
69 }
70
71 pub fn weight(&self) -> Weight {
73 Weight((self.0 >> 9 & 0x3FF) as u16)
74 }
75
76 pub fn style(&self) -> Style {
78 Style::unpack(self.0 & 0x1FF)
79 }
80
81 pub fn parts(&self) -> (Stretch, Weight, Style) {
83 (self.stretch(), self.weight(), self.style())
84 }
85
86 pub fn has_variations(&self) -> bool {
89 (self.0 >> 28) != 0
90 }
91
92 pub fn has_stretch_variation(&self) -> bool {
94 let var_bits = self.0 >> 28;
95 var_bits & 1 != 0
96 }
97
98 pub fn has_weight_variation(&self) -> bool {
100 let var_bits = self.0 >> 28;
101 var_bits & 2 != 0
102 }
103
104 pub fn has_oblique_variation(&self) -> bool {
107 let var_bits = self.0 >> 28;
108 var_bits & 4 != 0
109 }
110
111 pub fn has_italic_variation(&self) -> bool {
114 let var_bits = self.0 >> 28;
115 var_bits & 8 != 0
116 }
117
118 pub fn synthesize(&self, requested: Attributes) -> Synthesis {
121 let mut synth = Synthesis::default();
122 if self.0 << 4 == requested.0 << 4 {
123 return synth;
124 }
125 let mut len = 0usize;
126 if self.has_stretch_variation() {
127 let stretch = self.stretch();
128 let req_stretch = requested.stretch();
129 if stretch != requested.stretch() {
130 synth.vars[len] = Setting {
131 tag: WDTH,
132 value: req_stretch.to_percentage(),
133 };
134 len += 1;
135 }
136 }
137 let (weight, req_weight) = (self.weight(), requested.weight());
138 if weight != req_weight {
139 if self.has_weight_variation() {
140 synth.vars[len] = Setting {
141 tag: WGHT,
142 value: req_weight.0 as f32,
143 };
144 len += 1;
145 } else if req_weight > weight {
146 synth.embolden = true;
147 }
148 }
149 let (style, req_style) = (self.style(), requested.style());
150 if style != req_style {
151 match req_style {
152 Style::Normal => {}
153 Style::Italic => {
154 if style == Style::Normal {
155 if self.has_italic_variation() {
156 synth.vars[len] = Setting {
157 tag: ITAL,
158 value: 1.,
159 };
160 len += 1;
161 } else if self.has_oblique_variation() {
162 synth.vars[len] = Setting {
163 tag: SLNT,
164 value: 14.,
165 };
166 len += 1;
167 } else {
168 synth.skew = 14;
169 }
170 }
171 }
172 Style::Oblique(angle) => {
173 if style == Style::Normal {
174 let degrees = angle.to_degrees();
175 if self.has_oblique_variation() {
176 synth.vars[len] = Setting {
177 tag: SLNT,
178 value: degrees,
179 };
180 len += 1;
181 } else if self.has_italic_variation() && degrees > 0. {
182 synth.vars[len] = Setting {
183 tag: ITAL,
184 value: 1.,
185 };
186 len += 1;
187 } else {
188 synth.skew = degrees as i8;
189 }
190 }
191 }
192 }
193 }
194 synth.len = len as u8;
195 synth
196 }
197}
198
199impl Default for Attributes {
200 fn default() -> Self {
201 Self::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal)
202 }
203}
204
205impl PartialEq for Attributes {
206 fn eq(&self, other: &Self) -> bool {
207 self.0 << 4 == other.0 << 4
208 }
209}
210
211impl Eq for Attributes {}
212
213impl Hash for Attributes {
214 fn hash<H: Hasher>(&self, state: &mut H) {
215 (self.0 << 4).hash(state);
216 }
217}
218
219impl fmt::Display for Attributes {
220 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221 let mut space = "";
222 let (stretch, weight, style) = self.parts();
223 if style == Style::Normal && weight == Weight::NORMAL && stretch == Stretch::NORMAL {
224 return write!(f, "regular");
225 }
226 if stretch != Stretch::NORMAL {
227 write!(f, "{}", stretch)?;
228 space = " ";
229 }
230 if style != Style::Normal {
231 write!(f, "{}{}", space, style)?;
232 space = " ";
233 }
234 if weight != Weight::NORMAL {
235 write!(f, "{}{}", space, weight)?;
236 }
237 Ok(())
238 }
239}
240
241impl fmt::Debug for Attributes {
242 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
243 write!(f, "{:?}", self.parts())?;
244 if self.has_stretch_variation() {
245 write!(f, "+wdth")?;
246 }
247 if self.has_weight_variation() {
248 write!(f, "+wght")?;
249 }
250 if self.has_italic_variation() {
251 write!(f, "+ital")?;
252 }
253 if self.has_oblique_variation() {
254 write!(f, "+slnt")?;
255 }
256 Ok(())
257 }
258}
259
260impl From<Stretch> for Attributes {
261 fn from(s: Stretch) -> Self {
262 Self::new(s, Weight::default(), Style::default())
263 }
264}
265
266impl From<Weight> for Attributes {
267 fn from(w: Weight) -> Self {
268 Self::new(Stretch::default(), w, Style::default())
269 }
270}
271
272impl From<Style> for Attributes {
273 fn from(s: Style) -> Self {
274 Self::new(Stretch::default(), Weight::default(), s)
275 }
276}
277
278impl From<()> for Attributes {
279 fn from(_: ()) -> Self {
280 Self::default()
281 }
282}
283
284impl From<(Stretch, Weight, Style)> for Attributes {
285 fn from(parts: (Stretch, Weight, Style)) -> Self {
286 Self::new(parts.0, parts.1, parts.2)
287 }
288}
289
290#[derive(Copy, Clone, PartialEq, Eq, Debug)]
292pub struct ObliqueAngle(pub(crate) u8);
293
294impl ObliqueAngle {
295 pub fn from_degrees(degrees: f32) -> Self {
297 let a = degrees.clamp(-90., 90.) + 90.;
298 Self(a as u8)
299 }
300
301 pub fn from_radians(radians: f32) -> Self {
303 let degrees = radians * 180. / core::f32::consts::PI;
304 Self::from_degrees(degrees)
305 }
306
307 pub fn from_gradians(gradians: f32) -> Self {
309 Self::from_degrees(gradians / 400. * 360.)
310 }
311
312 pub fn from_turns(turns: f32) -> Self {
314 Self::from_degrees(turns * 360.)
315 }
316
317 pub fn to_degrees(self) -> f32 {
319 self.0 as f32 - 90.
320 }
321}
322
323impl Default for ObliqueAngle {
324 fn default() -> Self {
325 Self::from_degrees(14.)
326 }
327}
328
329#[derive(Copy, Clone, PartialEq, Eq, Debug)]
331pub enum Style {
332 Normal,
333 Italic,
334 Oblique(ObliqueAngle),
335}
336
337impl Style {
338 pub fn parse(mut s: &str) -> Option<Self> {
340 s = s.trim();
341 Some(match s {
342 "normal" => Self::Normal,
343 "italic" => Self::Italic,
344 "oblique" => Self::Oblique(ObliqueAngle::from_degrees(14.)),
345 _ => {
346 if s.starts_with("oblique ") {
347 s = s.get(8..)?;
348 if s.ends_with("deg") {
349 s = s.get(..s.len() - 3)?;
350 if let Ok(a) = s.trim().parse::<f32>() {
351 return Some(Self::Oblique(ObliqueAngle::from_degrees(a)));
352 }
353 } else if s.ends_with("grad") {
354 s = s.get(..s.len() - 4)?;
355 if let Ok(a) = s.trim().parse::<f32>() {
356 return Some(Self::Oblique(ObliqueAngle::from_gradians(a)));
357 }
358 } else if s.ends_with("rad") {
359 s = s.get(..s.len() - 3)?;
360 if let Ok(a) = s.trim().parse::<f32>() {
361 return Some(Self::Oblique(ObliqueAngle::from_radians(a)));
362 }
363 } else if s.ends_with("turn") {
364 s = s.get(..s.len() - 4)?;
365 if let Ok(a) = s.trim().parse::<f32>() {
366 return Some(Self::Oblique(ObliqueAngle::from_turns(a)));
367 }
368 }
369 return Some(Self::Oblique(ObliqueAngle::default()));
370 }
371 return None;
372 }
373 })
374 }
375
376 pub fn from_degrees(degrees: f32) -> Self {
379 Self::Oblique(ObliqueAngle::from_degrees(degrees))
380 }
381
382 pub fn to_degrees(self) -> f32 {
384 match self {
385 Self::Italic => 14.,
386 Self::Oblique(angle) => angle.to_degrees(),
387 _ => 0.,
388 }
389 }
390
391 fn unpack(bits: u32) -> Self {
392 if bits & 1 != 0 {
393 Self::Oblique(ObliqueAngle((bits >> 1) as u8))
394 } else if bits == 0b110 {
395 Self::Italic
396 } else {
397 Self::Normal
398 }
399 }
400
401 const fn pack(&self) -> u32 {
402 match self {
403 Self::Normal => 0b10,
404 Self::Italic => 0b110,
405 Self::Oblique(angle) => 1 | (angle.0 as u32) << 1,
406 }
407 }
408}
409
410impl fmt::Display for Style {
411 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
412 write!(
413 f,
414 "{}",
415 match self {
416 Self::Normal => "normal",
417 Self::Italic => "italic",
418 Self::Oblique(angle) => {
419 let degrees = angle.to_degrees();
420 if degrees == 14. {
421 "oblique"
422 } else {
423 return write!(f, "oblique({}deg)", degrees);
424 }
425 }
426 }
427 )
428 }
429}
430
431impl Default for Style {
432 fn default() -> Self {
433 Self::Normal
434 }
435}
436
437#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
439pub struct Weight(pub u16);
440
441impl Weight {
442 pub const THIN: Weight = Weight(100);
443 pub const EXTRA_LIGHT: Weight = Weight(200);
444 pub const LIGHT: Weight = Weight(300);
445 pub const NORMAL: Weight = Weight(400);
446 pub const MEDIUM: Weight = Weight(500);
447 pub const SEMI_BOLD: Weight = Weight(600);
448 pub const BOLD: Weight = Weight(700);
449 pub const EXTRA_BOLD: Weight = Weight(800);
450 pub const BLACK: Weight = Weight(900);
451
452 pub fn parse(s: &str) -> Option<Self> {
454 let s = s.trim();
455 Some(match s {
456 "normal" => Self::NORMAL,
457 "bold" => Self::BOLD,
458 _ => Self(s.parse::<u32>().ok()?.clamp(1, 1000) as u16),
459 })
460 }
461}
462
463impl fmt::Display for Weight {
464 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
465 let s = match *self {
466 Self::THIN => "thin",
467 Self::EXTRA_LIGHT => "extra-light",
468 Self::LIGHT => "light",
469 Self::NORMAL => "normal",
470 Self::MEDIUM => "medium",
471 Self::SEMI_BOLD => "semi-bold",
472 Self::BOLD => "bold",
473 Self::EXTRA_BOLD => "extra-bold",
474 Self::BLACK => "black",
475 _ => "",
476 };
477 if s.is_empty() {
478 write!(f, "{}", self.0)
479 } else {
480 write!(f, "{}", s)
481 }
482 }
483}
484
485impl Default for Weight {
486 fn default() -> Self {
487 Self::NORMAL
488 }
489}
490
491#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
494pub struct Stretch(pub(crate) u16);
495
496impl Stretch {
497 pub const ULTRA_CONDENSED: Self = Self(0);
498 pub const EXTRA_CONDENSED: Self = Self(25);
499 pub const CONDENSED: Self = Self(50);
500 pub const SEMI_CONDENSED: Self = Self(75);
501 pub const NORMAL: Self = Self(100);
502 pub const SEMI_EXPANDED: Self = Self(125);
503 pub const EXPANDED: Self = Self(150);
504 pub const EXTRA_EXPANDED: Self = Self(200);
505 pub const ULTRA_EXPANDED: Self = Self(300);
506
507 pub fn from_percentage(percentage: f32) -> Self {
511 let value = ((percentage.clamp(50., 200.) - 50.) * 2.) as u16;
512 Self(value)
513 }
514
515 pub fn to_percentage(self) -> f32 {
517 (self.0 as f32) * 0.5 + 50.
518 }
519
520 pub fn is_normal(self) -> bool {
522 self == Self::NORMAL
523 }
524
525 pub fn is_condensed(self) -> bool {
527 self < Self::NORMAL
528 }
529
530 pub fn is_expanded(self) -> bool {
532 self > Self::NORMAL
533 }
534
535 pub fn parse(s: &str) -> Option<Self> {
537 let s = s.trim();
538 Some(match s {
539 "ultra-condensed" => Self::ULTRA_CONDENSED,
540 "extra-condensed" => Self::EXTRA_CONDENSED,
541 "condensed" => Self::CONDENSED,
542 "semi-condensed" => Self::SEMI_CONDENSED,
543 "normal" => Self::NORMAL,
544 "semi-expanded" => Self::SEMI_EXPANDED,
545 "extra-expanded" => Self::EXTRA_EXPANDED,
546 "ultra-expanded" => Self::ULTRA_EXPANDED,
547 _ => {
548 if s.ends_with('%') {
549 let p = s.get(..s.len() - 1)?.parse::<f32>().ok()?;
550 return Some(Self::from_percentage(p));
551 }
552 return None;
553 }
554 })
555 }
556
557 pub fn raw(self) -> u16 {
559 self.0
560 }
561
562 pub(crate) fn from_raw(raw: u16) -> Self {
563 match raw {
564 1 => Self::ULTRA_CONDENSED,
565 2 => Self::EXTRA_CONDENSED,
566 3 => Self::CONDENSED,
567 4 => Self::SEMI_CONDENSED,
568 5 => Self::NORMAL,
569 6 => Self::SEMI_EXPANDED,
570 7 => Self::EXPANDED,
571 8 => Self::EXTRA_EXPANDED,
572 9 => Self::ULTRA_EXPANDED,
573 _ => Self::NORMAL,
574 }
575 }
576}
577
578impl fmt::Display for Stretch {
579 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
580 write!(
581 f,
582 "{}",
583 match *self {
584 Self::ULTRA_CONDENSED => "ultra-condensed",
585 Self::EXTRA_CONDENSED => "extra-condensed",
586 Self::CONDENSED => "condensed",
587 Self::SEMI_CONDENSED => "semi-condensed",
588 Self::NORMAL => "normal",
589 Self::SEMI_EXPANDED => "semi-expanded",
590 Self::EXPANDED => "expanded",
591 Self::EXTRA_EXPANDED => "extra-expanded",
592 Self::ULTRA_EXPANDED => "ultra-expanded",
593 _ => {
594 return write!(f, "{}%", self.to_percentage());
595 }
596 }
597 )
598 }
599}
600
601impl fmt::Debug for Stretch {
602 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
603 write!(f, "Stretch({})", self.to_percentage())
604 }
605}
606
607impl Default for Stretch {
608 fn default() -> Self {
609 Self::NORMAL
610 }
611}
612
613#[derive(Copy, Clone, Default, Debug)]
618pub struct Synthesis {
619 vars: [Setting<f32>; 4],
620 len: u8,
621 embolden: bool,
622 skew: i8,
623}
624
625impl Synthesis {
626 #[doc(hidden)]
627 pub fn new(variations: impl Iterator<Item = Setting<f32>>, embolden: bool, skew: f32) -> Self {
628 let mut synth = Self {
629 embolden,
630 skew: skew as i8,
631 ..Default::default()
632 };
633 for (i, setting) in variations.take(4).enumerate() {
634 synth.vars[i] = setting;
635 synth.len = i as u8 + 1;
636 }
637 synth
638 }
639
640 pub fn any(&self) -> bool {
642 self.len != 0 || self.embolden || self.skew != 0
643 }
644
645 pub fn variations(&self) -> &[Setting<f32>] {
648 &self.vars[..self.len as usize]
649 }
650
651 pub fn embolden(&self) -> bool {
653 self.embolden
654 }
655
656 pub fn skew(&self) -> Option<f32> {
658 if self.skew != 0 {
659 Some(self.skew as f32)
660 } else {
661 None
662 }
663 }
664}
665
666impl PartialEq for Synthesis {
667 fn eq(&self, other: &Self) -> bool {
668 if self.len != other.len {
669 return false;
670 }
671 if self.len != 0 && self.variations() != other.variations() {
672 return false;
673 }
674 self.embolden == other.embolden && self.skew == other.skew
675 }
676}