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}