kas_text/
format.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Parsers for formatted text
7
8use crate::fonts::FontSelector;
9use crate::{Effect, OwningVecIter};
10#[allow(unused)]
11use crate::{Text, TextDisplay}; // for doc-links
12
13mod plain;
14
15#[cfg(feature = "markdown")]
16mod markdown;
17#[cfg(feature = "markdown")]
18pub use markdown::{Error as MarkdownError, Markdown};
19
20/// Text, optionally with formatting data
21///
22/// Any `F: FormattableText` automatically support [`FormattableTextDyn`].
23/// Implement either this or [`FormattableTextDyn`], not both.
24pub trait FormattableText: std::cmp::PartialEq + std::fmt::Debug {
25    type FontTokenIter<'a>: Iterator<Item = FontToken>
26    where
27        Self: 'a;
28
29    /// Length of text
30    ///
31    /// Default implementation uses [`FormattableText::as_str`].
32    #[inline]
33    fn str_len(&self) -> usize {
34        self.as_str().len()
35    }
36
37    /// Access whole text as contiguous `str`
38    fn as_str(&self) -> &str;
39
40    /// Construct an iterator over formatting items
41    ///
42    /// It is expected that [`FontToken::start`] of yielded items is strictly
43    /// increasing; if not, formatting may not be applied correctly.
44    ///
45    /// The default [font size][crate::Text::set_font_size] (`dpem`) is passed
46    /// as a reference.
47    ///
48    /// For plain text this iterator will be empty.
49    fn font_tokens<'a>(&'a self, dpem: f32) -> Self::FontTokenIter<'a>;
50
51    /// Get the sequence of effect tokens
52    ///
53    /// This method has some limitations: (1) it may only return a reference to
54    /// an existing sequence, (2) effect tokens cannot be generated dependent
55    /// on input state, and (3) it does not incorporate color information. For
56    /// most uses it should still be sufficient, but for other cases it may be
57    /// preferable not to use this method (use a dummy implementation returning
58    /// `&[]` and use inherent methods on the text object via [`Text::text`]).
59    fn effect_tokens(&self) -> &[Effect];
60}
61
62impl<F: FormattableText + ?Sized> FormattableText for &F {
63    type FontTokenIter<'a>
64        = F::FontTokenIter<'a>
65    where
66        Self: 'a;
67
68    fn as_str(&self) -> &str {
69        F::as_str(self)
70    }
71
72    fn font_tokens<'a>(&'a self, dpem: f32) -> Self::FontTokenIter<'a> {
73        F::font_tokens(self, dpem)
74    }
75
76    fn effect_tokens(&self) -> &[Effect] {
77        F::effect_tokens(self)
78    }
79}
80
81/// Text, optionally with formatting data
82///
83/// This is an object-safe version of the [`FormattableText`] trait (i.e.
84/// `dyn FormattableTextDyn` is a valid type).
85///
86/// This trait is auto-implemented for every implementation of [`FormattableText`].
87/// The type `&dyn FormattableTextDyn` implements [`FormattableText`].
88/// Implement either this or (preferably) [`FormattableText`], not both.
89pub trait FormattableTextDyn: std::fmt::Debug {
90    /// Produce a boxed clone of self
91    fn clone_boxed(&self) -> Box<dyn FormattableTextDyn>;
92
93    /// Length of text
94    fn str_len(&self) -> usize;
95
96    /// Access whole text as contiguous `str`
97    fn as_str(&self) -> &str;
98
99    /// Construct an iterator over formatting items
100    ///
101    /// It is expected that [`FontToken::start`] of yielded items is strictly
102    /// increasing; if not, formatting may not be applied correctly.
103    ///
104    /// The default [font size][crate::Text::set_font_size] (`dpem`) is passed
105    /// as a reference.
106    ///
107    /// For plain text this iterator will be empty.
108    fn font_tokens(&self, dpem: f32) -> OwningVecIter<FontToken>;
109
110    /// Get the sequence of effect tokens
111    ///
112    /// This method has some limitations: (1) it may only return a reference to
113    /// an existing sequence, (2) effect tokens cannot be generated dependent
114    /// on input state, and (3) it does not incorporate color information. For
115    /// most uses it should still be sufficient, but for other cases it may be
116    /// preferable not to use this method (use a dummy implementation returning
117    /// `&[]` and use inherent methods on the text object via [`Text::text`]).
118    fn effect_tokens(&self) -> &[Effect];
119}
120
121impl<F: FormattableText + Clone + 'static> FormattableTextDyn for F {
122    fn clone_boxed(&self) -> Box<dyn FormattableTextDyn> {
123        Box::new(self.clone())
124    }
125
126    fn str_len(&self) -> usize {
127        FormattableText::str_len(self)
128    }
129    fn as_str(&self) -> &str {
130        FormattableText::as_str(self)
131    }
132
133    fn font_tokens(&self, dpem: f32) -> OwningVecIter<FontToken> {
134        let iter = FormattableText::font_tokens(self, dpem);
135        OwningVecIter::new(iter.collect())
136    }
137
138    fn effect_tokens(&self) -> &[Effect] {
139        FormattableText::effect_tokens(self)
140    }
141}
142
143/// References to [`FormattableTextDyn`] always compare unequal
144impl<'t> PartialEq for &'t dyn FormattableTextDyn {
145    fn eq(&self, _: &Self) -> bool {
146        false
147    }
148}
149
150impl<'t> FormattableText for &'t dyn FormattableTextDyn {
151    type FontTokenIter<'a>
152        = OwningVecIter<FontToken>
153    where
154        Self: 'a;
155
156    #[inline]
157    fn str_len(&self) -> usize {
158        FormattableTextDyn::str_len(*self)
159    }
160
161    #[inline]
162    fn as_str(&self) -> &str {
163        FormattableTextDyn::as_str(*self)
164    }
165
166    #[inline]
167    fn font_tokens(&self, dpem: f32) -> OwningVecIter<FontToken> {
168        FormattableTextDyn::font_tokens(*self, dpem)
169    }
170
171    fn effect_tokens(&self) -> &[Effect] {
172        FormattableTextDyn::effect_tokens(*self)
173    }
174}
175
176impl Clone for Box<dyn FormattableTextDyn> {
177    fn clone(&self) -> Self {
178        (**self).clone_boxed()
179    }
180}
181
182/// Font formatting token
183#[derive(Clone, Debug, PartialEq)]
184pub struct FontToken {
185    /// Index in text at which formatting becomes active
186    ///
187    /// (Note that we use `u32` not `usize` since it can be assumed text length
188    /// will never exceed `u32::MAX`.)
189    pub start: u32,
190    /// Font size, in dots-per-em (pixel width of an 'M')
191    ///
192    /// This may be calculated from point size as `pt_size * dpp`, where `dpp`
193    /// is the number of pixels per point (see [`crate::fonts`] documentation).
194    pub dpem: f32,
195    /// Font selector
196    pub font: FontSelector,
197}