1use std::collections::HashSet;
2use std::convert::{TryFrom, TryInto};
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7use crate::parse::{Parse, ParseError, ParseResult};
8
9use super::charset::Charset;
10
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32#[derive(Clone, Debug, Default, PartialEq, Eq)]
33pub struct Font {
34 pub info: Info,
36 pub common: Common,
38 pub pages: Vec<String>,
40 pub chars: Vec<Char>,
42 pub kernings: Vec<Kerning>,
44}
45
46impl Font {
47 #[inline(always)]
51 pub fn new(
52 info: Info,
53 common: Common,
54 pages: Vec<String>,
55 chars: Vec<Char>,
56 kernings: Vec<Kerning>,
57 ) -> Self {
58 Self { info, common, pages, chars, kernings }
59 }
60
61 pub fn validate_references(&self) -> crate::Result<()> {
64 self.validate_char_references()?;
65 self.validate_kerning_references()?;
66 Ok(())
67 }
68
69 fn validate_char_references(&self) -> crate::Result<()> {
70 for char in &self.chars {
71 if self.pages.len() <= char.page as usize {
72 return Err(crate::Error::InvalidCharPage {
73 char_id: char.id,
74 page_id: char.page as u32,
75 });
76 }
77 }
78 Ok(())
79 }
80
81 fn validate_kerning_references(&self) -> crate::Result<()> {
82 let set: HashSet<u32> = self.chars.iter().map(|u| u.id).collect();
83 for kerning in &self.kernings {
84 if !set.contains(&kerning.first) {
85 return Err(crate::Error::InvalidKerningChar { id: kerning.first });
86 }
87 if !set.contains(&kerning.second) {
88 return Err(crate::Error::InvalidKerningChar { id: kerning.second });
89 }
90 }
91 Ok(())
92 }
93}
94
95#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
101pub struct Char {
102 pub id: u32,
104 pub x: u16,
106 pub y: u16,
108 pub width: u16,
110 pub height: u16,
112 pub xoffset: i16,
115 pub yoffset: i16,
118 pub xadvance: i16,
120 pub page: u8,
122 pub chnl: Chnl,
124}
125
126impl Char {
127 #[allow(clippy::too_many_arguments)]
131 #[inline(always)]
132 pub fn new(
133 id: u32,
134 x: u16,
135 y: u16,
136 width: u16,
137 height: u16,
138 xoffset: i16,
139 yoffset: i16,
140 xadvance: i16,
141 page: u8,
142 chnl: Chnl,
143 ) -> Self {
144 Self { id, x, y, width, height, xoffset, yoffset, xadvance, page, chnl }
145 }
146}
147
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
152#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
153pub struct Common {
154 pub line_height: u16,
156 pub base: u16,
158 pub scale_w: u16,
160 pub scale_h: u16,
162 pub pages: u16,
164 #[cfg_attr(
167 feature = "serde",
168 serde(serialize_with = "se_bool"),
169 serde(deserialize_with = "de_bool")
170 )]
171 pub packed: bool,
172 pub alpha_chnl: Packing,
174 pub red_chnl: Packing,
176 pub green_chnl: Packing,
178 pub blue_chnl: Packing,
180}
181
182impl Common {
183 #[allow(clippy::too_many_arguments)]
187 #[inline(always)]
188 pub fn new(
189 line_height: u16,
190 base: u16,
191 scale_w: u16,
192 scale_h: u16,
193 pages: u16,
194 packed: bool,
195 alpha_chnl: Packing,
196 red_chnl: Packing,
197 green_chnl: Packing,
198 blue_chnl: Packing,
199 ) -> Self {
200 Self {
201 line_height,
202 base,
203 scale_w,
204 scale_h,
205 pages,
206 packed,
207 alpha_chnl,
208 red_chnl,
209 green_chnl,
210 blue_chnl,
211 }
212 }
213}
214
215#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
219#[derive(Clone, Debug, PartialEq, Eq)]
220pub struct Info {
221 pub face: String,
223 pub size: i16,
225 #[cfg_attr(
227 feature = "serde",
228 serde(serialize_with = "se_bool"),
229 serde(deserialize_with = "de_bool")
230 )]
231 pub bold: bool,
232 #[cfg_attr(
234 feature = "serde",
235 serde(serialize_with = "se_bool"),
236 serde(deserialize_with = "de_bool")
237 )]
238 pub italic: bool,
239 pub charset: Charset,
241 #[cfg_attr(
242 feature = "serde",
243 serde(serialize_with = "se_bool"),
244 serde(deserialize_with = "de_bool")
245 )]
246 pub unicode: bool,
248 pub stretch_h: u16,
250 #[cfg_attr(
252 feature = "serde",
253 serde(serialize_with = "se_bool"),
254 serde(deserialize_with = "de_bool")
255 )]
256 pub smooth: bool,
257 pub aa: u8,
259 pub padding: Padding,
261 pub spacing: Spacing,
263 #[cfg_attr(feature = "serde", serde(default))]
265 pub outline: u8,
266}
267
268impl Info {
269 #[allow(clippy::too_many_arguments)]
273 #[inline(always)]
274 pub fn new(
275 face: String,
276 size: i16,
277 bold: bool,
278 italic: bool,
279 charset: Charset,
280 unicode: bool,
281 stretch_height: u16,
282 smooth: bool,
283 aa: u8,
284 padding: Padding,
285 spacing: Spacing,
286 outline: u8,
287 ) -> Self {
288 Self {
289 face,
290 size,
291 bold,
292 italic,
293 charset,
294 unicode,
295 stretch_h: stretch_height,
296 smooth,
297 aa,
298 padding,
299 spacing,
300 outline,
301 }
302 }
303
304 #[allow(dead_code)]
305 pub(crate) fn check_encoding(&self) -> crate::Result<()> {
306 if self.unicode && self.charset != Charset::Null {
307 return Err(crate::Error::InvalidCharsetEncoding {
308 unicode: true,
309 charset: self.charset.clone(),
310 });
311 }
312 Ok(())
313 }
314}
315
316impl Default for Info {
317 fn default() -> Self {
318 Self {
319 face: Default::default(),
320 size: Default::default(),
321 bold: Default::default(),
322 italic: Default::default(),
323 charset: Charset::Tagged(0),
324 unicode: Default::default(),
325 stretch_h: Default::default(),
326 smooth: Default::default(),
327 aa: Default::default(),
328 padding: Default::default(),
329 spacing: Default::default(),
330 outline: Default::default(),
331 }
332 }
333}
334
335#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
336#[derive(Clone, Debug, Default)]
337pub struct Page {
338 pub id: u16,
339 pub file: String,
340}
341
342#[cfg_attr(
353 feature = "serde",
354 derive(Serialize, Deserialize),
355 serde(from = "[u8; 4]"),
356 serde(into = "[u8; 4]")
357)]
358#[allow(missing_docs)]
359#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
360pub struct Padding {
361 pub up: u8,
362 pub right: u8,
363 pub down: u8,
364 pub left: u8,
365}
366
367impl Padding {
368 #[inline(always)]
372 pub fn new(up: u8, right: u8, down: u8, left: u8) -> Self {
373 Self { up, right, down, left }
374 }
375}
376
377impl Parse for Padding {
378 fn parse(src: &str) -> ParseResult<Self> {
379 <[u8; 4]>::parse(src).map(Into::into)
380 }
381
382 fn parse_bytes(bytes: &[u8]) -> ParseResult<Self> {
383 <[u8; 4]>::parse_bytes(bytes).map(Into::into)
384 }
385}
386
387impl From<[u8; 4]> for Padding {
388 #[inline(always)]
389 fn from(array: [u8; 4]) -> Self {
390 Self { up: array[0], right: array[1], down: array[2], left: array[3] }
391 }
392}
393
394impl From<Padding> for [u8; 4] {
395 #[inline(always)]
396 fn from(padding: Padding) -> Self {
397 [padding.up, padding.right, padding.down, padding.left]
398 }
399}
400
401#[cfg_attr(
405 feature = "serde",
406 derive(Serialize, Deserialize),
407 serde(from = "[u8; 2]"),
408 serde(into = "[u8; 2]")
409)]
410#[allow(missing_docs)]
411#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
412pub struct Spacing {
413 pub horizontal: u8,
414 pub vertical: u8,
415}
416
417impl Spacing {
418 #[inline(always)]
422 pub fn new(horizontal: u8, vertical: u8) -> Self {
423 Self { horizontal, vertical }
424 }
425}
426
427impl Parse for Spacing {
428 fn parse(src: &str) -> ParseResult<Self> {
429 <[u8; 2]>::parse(src).map(Into::into)
430 }
431
432 fn parse_bytes(bytes: &[u8]) -> ParseResult<Self> {
433 <[u8; 2]>::parse_bytes(bytes).map(Into::into)
434 }
435}
436
437impl From<[u8; 2]> for Spacing {
438 #[inline(always)]
439 fn from(array: [u8; 2]) -> Self {
440 Self { horizontal: array[0], vertical: array[1] }
441 }
442}
443
444impl From<Spacing> for [u8; 2] {
445 #[inline(always)]
446 fn from(spacing: Spacing) -> Self {
447 [spacing.horizontal, spacing.vertical]
448 }
449}
450
451#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
456#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
457pub struct Kerning {
458 pub first: u32,
460 pub second: u32,
462 pub amount: i16,
465}
466
467impl Kerning {
468 #[inline(always)]
472 pub fn new(first: u32, second: u32, amount: i16) -> Self {
473 Self { first, second, amount }
474 }
475}
476
477#[cfg_attr(
482 feature = "serde",
483 derive(Serialize, Deserialize),
484 serde(try_from = "u8"),
485 serde(into = "u8")
486)]
487#[derive(Clone, Copy, Debug, PartialEq, Eq)]
488#[repr(u8)]
489pub enum Packing {
490 Glyph = 0,
492 Outline = 1,
494 GlyphOutline = 2,
496 Zero = 3,
498 One = 4,
500}
501
502impl Default for Packing {
503 #[inline(always)]
504 fn default() -> Self {
505 Self::Glyph
506 }
507}
508
509impl From<Packing> for u8 {
510 #[inline(always)]
511 fn from(chnl: Packing) -> Self {
512 chnl as u8
513 }
514}
515
516impl TryFrom<u8> for Packing {
517 type Error = ParseError;
518
519 fn try_from(chnl: u8) -> Result<Self, Self::Error> {
520 match chnl {
521 0 => Ok(Self::Glyph),
522 1 => Ok(Self::Outline),
523 2 => Ok(Self::GlyphOutline),
524 3 => Ok(Self::Zero),
525 4 => Ok(Self::One),
526 u => Err(ParseError::Other(format!("Packing: invalid u8: {}", u))),
527 }
528 }
529}
530
531impl Parse for Packing {
532 fn parse(src: &str) -> ParseResult<Self> {
533 let u: u8 = src.parse()?;
534 let packing: Packing = u.try_into()?;
535 Ok(packing)
536 }
537}
538
539#[cfg_attr(
582 feature = "serde",
583 derive(Serialize, Deserialize),
584 serde(try_from = "u8"),
585 serde(into = "u8")
586)]
587#[derive(Clone, Copy, Debug, PartialEq, Eq)]
588pub struct Chnl(u8);
589
590impl Chnl {
591 pub const ALL: Chnl = Chnl(15);
593
594 pub const ALPHA: Chnl = Chnl(8);
596
597 pub const RED: Chnl = Chnl(4);
599
600 pub const GREEN: Chnl = Chnl(2);
602
603 pub const BLUE: Chnl = Chnl(1);
605
606 #[inline(always)]
608 pub fn alpha(self) -> bool {
609 self.0 & 8 != 0
610 }
611
612 #[inline(always)]
614 pub fn set_alpha(&mut self, v: bool) {
615 if v {
616 self.0 |= 8;
617 } else {
618 self.0 &= !8;
619 }
620 }
621
622 #[inline(always)]
624 pub fn red(self) -> bool {
625 self.0 & 4 != 0
626 }
627
628 #[inline(always)]
630 pub fn set_red(&mut self, v: bool) {
631 if v {
632 self.0 |= 4;
633 } else {
634 self.0 &= !4;
635 }
636 }
637
638 #[inline(always)]
640 pub fn green(self) -> bool {
641 self.0 & 2 != 0
642 }
643
644 #[inline(always)]
646 pub fn set_green(&mut self, v: bool) {
647 if v {
648 self.0 |= 2;
649 } else {
650 self.0 &= !2;
651 }
652 }
653
654 #[inline(always)]
656 pub fn blue(self) -> bool {
657 self.0 & 1 != 0
658 }
659
660 #[inline(always)]
662 pub fn set_blue(&mut self, v: bool) {
663 if v {
664 self.0 |= 1;
665 } else {
666 self.0 &= !1;
667 }
668 }
669}
670
671impl Default for Chnl {
672 #[inline(always)]
673 fn default() -> Self {
674 Self::ALL
675 }
676}
677
678impl From<Chnl> for u8 {
679 #[inline(always)]
680 fn from(chnl: Chnl) -> Self {
681 chnl.0
682 }
683}
684
685impl TryFrom<u8> for Chnl {
686 type Error = ParseError;
687
688 fn try_from(u: u8) -> Result<Self, Self::Error> {
689 if u < 0x10 {
690 Ok(Self(u))
691 } else {
692 Err(ParseError::Other(format!("Chnl: invalid u8: {}", u)))
693 }
694 }
695}
696
697impl Parse for Chnl {
698 fn parse(src: &str) -> ParseResult<Self> {
699 let u: u8 = src.parse()?;
700 let packing: Chnl = u.try_into()?;
701 Ok(packing)
702 }
703}
704
705#[cfg(feature = "serde")]
706pub fn se_bool<S: Serializer>(v: &bool, s: S) -> Result<S::Ok, S::Error> {
707 (*v as u8).serialize(s)
708}
709
710#[cfg(feature = "serde")]
711pub fn de_bool<'de, D: Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
712 Ok(u8::deserialize(d)? != 0)
713}