1use std::sync::Arc;
5
6use strict_num::NonZeroPositiveF32;
7pub use svgtypes::FontFamily;
8
9#[cfg(feature = "text")]
10use crate::layout::Span;
11use crate::{Fill, Group, NonEmptyString, PaintOrder, Rect, Stroke, TextRendering, Transform};
12
13#[allow(missing_docs)]
15#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
16pub enum FontStretch {
17 UltraCondensed,
18 ExtraCondensed,
19 Condensed,
20 SemiCondensed,
21 Normal,
22 SemiExpanded,
23 Expanded,
24 ExtraExpanded,
25 UltraExpanded,
26}
27
28impl Default for FontStretch {
29 #[inline]
30 fn default() -> Self {
31 Self::Normal
32 }
33}
34
35#[cfg(feature = "text")]
36impl From<fontdb::Stretch> for FontStretch {
37 fn from(stretch: fontdb::Stretch) -> Self {
38 match stretch {
39 fontdb::Stretch::UltraCondensed => FontStretch::UltraCondensed,
40 fontdb::Stretch::ExtraCondensed => FontStretch::ExtraCondensed,
41 fontdb::Stretch::Condensed => FontStretch::Condensed,
42 fontdb::Stretch::SemiCondensed => FontStretch::SemiCondensed,
43 fontdb::Stretch::Normal => FontStretch::Normal,
44 fontdb::Stretch::SemiExpanded => FontStretch::SemiExpanded,
45 fontdb::Stretch::Expanded => FontStretch::Expanded,
46 fontdb::Stretch::ExtraExpanded => FontStretch::ExtraExpanded,
47 fontdb::Stretch::UltraExpanded => FontStretch::UltraExpanded,
48 }
49 }
50}
51
52#[cfg(feature = "text")]
53impl From<FontStretch> for fontdb::Stretch {
54 fn from(stretch: FontStretch) -> Self {
55 match stretch {
56 FontStretch::UltraCondensed => fontdb::Stretch::UltraCondensed,
57 FontStretch::ExtraCondensed => fontdb::Stretch::ExtraCondensed,
58 FontStretch::Condensed => fontdb::Stretch::Condensed,
59 FontStretch::SemiCondensed => fontdb::Stretch::SemiCondensed,
60 FontStretch::Normal => fontdb::Stretch::Normal,
61 FontStretch::SemiExpanded => fontdb::Stretch::SemiExpanded,
62 FontStretch::Expanded => fontdb::Stretch::Expanded,
63 FontStretch::ExtraExpanded => fontdb::Stretch::ExtraExpanded,
64 FontStretch::UltraExpanded => fontdb::Stretch::UltraExpanded,
65 }
66 }
67}
68
69#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
71pub enum FontStyle {
72 Normal,
74 Italic,
76 Oblique,
78}
79
80impl Default for FontStyle {
81 #[inline]
82 fn default() -> FontStyle {
83 Self::Normal
84 }
85}
86
87#[cfg(feature = "text")]
88impl From<fontdb::Style> for FontStyle {
89 fn from(style: fontdb::Style) -> Self {
90 match style {
91 fontdb::Style::Normal => FontStyle::Normal,
92 fontdb::Style::Italic => FontStyle::Italic,
93 fontdb::Style::Oblique => FontStyle::Oblique,
94 }
95 }
96}
97
98#[cfg(feature = "text")]
99impl From<FontStyle> for fontdb::Style {
100 fn from(style: FontStyle) -> Self {
101 match style {
102 FontStyle::Normal => fontdb::Style::Normal,
103 FontStyle::Italic => fontdb::Style::Italic,
104 FontStyle::Oblique => fontdb::Style::Oblique,
105 }
106 }
107}
108
109#[derive(Clone, Eq, PartialEq, Hash, Debug)]
111pub struct Font {
112 pub(crate) families: Vec<FontFamily>,
113 pub(crate) style: FontStyle,
114 pub(crate) stretch: FontStretch,
115 pub(crate) weight: u16,
116}
117
118impl Font {
119 pub fn families(&self) -> &[FontFamily] {
123 &self.families
124 }
125
126 pub fn style(&self) -> FontStyle {
128 self.style
129 }
130
131 pub fn stretch(&self) -> FontStretch {
133 self.stretch
134 }
135
136 pub fn weight(&self) -> u16 {
138 self.weight
139 }
140}
141
142#[allow(missing_docs)]
144#[derive(Clone, Copy, PartialEq, Debug)]
145pub enum DominantBaseline {
146 Auto,
147 UseScript,
148 NoChange,
149 ResetSize,
150 Ideographic,
151 Alphabetic,
152 Hanging,
153 Mathematical,
154 Central,
155 Middle,
156 TextAfterEdge,
157 TextBeforeEdge,
158}
159
160impl Default for DominantBaseline {
161 fn default() -> Self {
162 Self::Auto
163 }
164}
165
166#[allow(missing_docs)]
168#[derive(Clone, Copy, PartialEq, Debug)]
169pub enum AlignmentBaseline {
170 Auto,
171 Baseline,
172 BeforeEdge,
173 TextBeforeEdge,
174 Middle,
175 Central,
176 AfterEdge,
177 TextAfterEdge,
178 Ideographic,
179 Alphabetic,
180 Hanging,
181 Mathematical,
182}
183
184impl Default for AlignmentBaseline {
185 fn default() -> Self {
186 Self::Auto
187 }
188}
189
190#[allow(missing_docs)]
192#[derive(Clone, Copy, PartialEq, Debug)]
193pub enum BaselineShift {
194 Baseline,
195 Subscript,
196 Superscript,
197 Number(f32),
198}
199
200impl Default for BaselineShift {
201 #[inline]
202 fn default() -> BaselineShift {
203 BaselineShift::Baseline
204 }
205}
206
207#[allow(missing_docs)]
209#[derive(Clone, Copy, PartialEq, Debug)]
210pub enum LengthAdjust {
211 Spacing,
212 SpacingAndGlyphs,
213}
214
215impl Default for LengthAdjust {
216 fn default() -> Self {
217 Self::Spacing
218 }
219}
220
221#[derive(Clone, Debug)]
228pub struct TextDecorationStyle {
229 pub(crate) fill: Option<Fill>,
230 pub(crate) stroke: Option<Stroke>,
231}
232
233impl TextDecorationStyle {
234 pub fn fill(&self) -> Option<&Fill> {
236 self.fill.as_ref()
237 }
238
239 pub fn stroke(&self) -> Option<&Stroke> {
241 self.stroke.as_ref()
242 }
243}
244
245#[derive(Clone, Debug)]
247pub struct TextDecoration {
248 pub(crate) underline: Option<TextDecorationStyle>,
249 pub(crate) overline: Option<TextDecorationStyle>,
250 pub(crate) line_through: Option<TextDecorationStyle>,
251}
252
253impl TextDecoration {
254 pub fn underline(&self) -> Option<&TextDecorationStyle> {
256 self.underline.as_ref()
257 }
258
259 pub fn overline(&self) -> Option<&TextDecorationStyle> {
261 self.overline.as_ref()
262 }
263
264 pub fn line_through(&self) -> Option<&TextDecorationStyle> {
266 self.line_through.as_ref()
267 }
268}
269
270#[derive(Clone, Debug)]
274pub struct TextSpan {
275 pub(crate) start: usize,
276 pub(crate) end: usize,
277 pub(crate) fill: Option<Fill>,
278 pub(crate) stroke: Option<Stroke>,
279 pub(crate) paint_order: PaintOrder,
280 pub(crate) font: Font,
281 pub(crate) font_size: NonZeroPositiveF32,
282 pub(crate) small_caps: bool,
283 pub(crate) apply_kerning: bool,
284 pub(crate) decoration: TextDecoration,
285 pub(crate) dominant_baseline: DominantBaseline,
286 pub(crate) alignment_baseline: AlignmentBaseline,
287 pub(crate) baseline_shift: Vec<BaselineShift>,
288 pub(crate) visible: bool,
289 pub(crate) letter_spacing: f32,
290 pub(crate) word_spacing: f32,
291 pub(crate) text_length: Option<f32>,
292 pub(crate) length_adjust: LengthAdjust,
293}
294
295impl TextSpan {
296 pub fn start(&self) -> usize {
300 self.start
301 }
302
303 pub fn end(&self) -> usize {
307 self.end
308 }
309
310 pub fn fill(&self) -> Option<&Fill> {
312 self.fill.as_ref()
313 }
314
315 pub fn stroke(&self) -> Option<&Stroke> {
317 self.stroke.as_ref()
318 }
319
320 pub fn paint_order(&self) -> PaintOrder {
322 self.paint_order
323 }
324
325 pub fn font(&self) -> &Font {
327 &self.font
328 }
329
330 pub fn font_size(&self) -> NonZeroPositiveF32 {
332 self.font_size
333 }
334
335 pub fn small_caps(&self) -> bool {
339 self.small_caps
340 }
341
342 pub fn apply_kerning(&self) -> bool {
346 self.apply_kerning
347 }
348
349 pub fn decoration(&self) -> &TextDecoration {
351 &self.decoration
352 }
353
354 pub fn dominant_baseline(&self) -> DominantBaseline {
356 self.dominant_baseline
357 }
358
359 pub fn alignment_baseline(&self) -> AlignmentBaseline {
361 self.alignment_baseline
362 }
363
364 pub fn baseline_shift(&self) -> &[BaselineShift] {
368 &self.baseline_shift
369 }
370
371 pub fn is_visible(&self) -> bool {
373 self.visible
374 }
375
376 pub fn letter_spacing(&self) -> f32 {
378 self.letter_spacing
379 }
380
381 pub fn word_spacing(&self) -> f32 {
383 self.word_spacing
384 }
385
386 pub fn text_length(&self) -> Option<f32> {
388 self.text_length
389 }
390
391 pub fn length_adjust(&self) -> LengthAdjust {
393 self.length_adjust
394 }
395}
396
397#[allow(missing_docs)]
399#[derive(Clone, Copy, PartialEq, Debug)]
400pub enum TextAnchor {
401 Start,
402 Middle,
403 End,
404}
405
406impl Default for TextAnchor {
407 fn default() -> Self {
408 Self::Start
409 }
410}
411
412#[derive(Debug)]
414pub struct TextPath {
415 pub(crate) id: NonEmptyString,
416 pub(crate) start_offset: f32,
417 pub(crate) path: Arc<tiny_skia_path::Path>,
418}
419
420impl TextPath {
421 pub fn id(&self) -> &str {
425 self.id.get()
426 }
427
428 pub fn start_offset(&self) -> f32 {
432 self.start_offset
433 }
434
435 pub fn path(&self) -> &tiny_skia_path::Path {
437 &self.path
438 }
439}
440
441#[derive(Clone, Debug)]
443pub enum TextFlow {
444 Linear,
448 Path(Arc<TextPath>),
450}
451
452#[derive(Clone, Debug)]
456pub struct TextChunk {
457 pub(crate) x: Option<f32>,
458 pub(crate) y: Option<f32>,
459 pub(crate) anchor: TextAnchor,
460 pub(crate) spans: Vec<TextSpan>,
461 pub(crate) text_flow: TextFlow,
462 pub(crate) text: String,
463}
464
465impl TextChunk {
466 pub fn x(&self) -> Option<f32> {
468 self.x
469 }
470
471 pub fn y(&self) -> Option<f32> {
473 self.y
474 }
475
476 pub fn anchor(&self) -> TextAnchor {
478 self.anchor
479 }
480
481 pub fn spans(&self) -> &[TextSpan] {
483 &self.spans
484 }
485
486 pub fn text_flow(&self) -> TextFlow {
488 self.text_flow.clone()
489 }
490
491 pub fn text(&self) -> &str {
493 &self.text
494 }
495}
496
497#[allow(missing_docs)]
499#[derive(Clone, Copy, PartialEq, Debug)]
500pub enum WritingMode {
501 LeftToRight,
502 TopToBottom,
503}
504
505#[derive(Clone, Debug)]
509pub struct Text {
510 pub(crate) id: String,
511 pub(crate) rendering_mode: TextRendering,
512 pub(crate) dx: Vec<f32>,
513 pub(crate) dy: Vec<f32>,
514 pub(crate) rotate: Vec<f32>,
515 pub(crate) writing_mode: WritingMode,
516 pub(crate) chunks: Vec<TextChunk>,
517 pub(crate) abs_transform: Transform,
518 pub(crate) bounding_box: Rect,
519 pub(crate) abs_bounding_box: Rect,
520 pub(crate) stroke_bounding_box: Rect,
521 pub(crate) abs_stroke_bounding_box: Rect,
522 pub(crate) flattened: Box<Group>,
523 #[cfg(feature = "text")]
524 pub(crate) layouted: Vec<Span>,
525}
526
527impl Text {
528 pub fn id(&self) -> &str {
534 &self.id
535 }
536
537 pub fn rendering_mode(&self) -> TextRendering {
541 self.rendering_mode
542 }
543
544 pub fn dx(&self) -> &[f32] {
548 &self.dx
549 }
550
551 pub fn dy(&self) -> &[f32] {
555 &self.dy
556 }
557
558 pub fn rotate(&self) -> &[f32] {
562 &self.rotate
563 }
564
565 pub fn writing_mode(&self) -> WritingMode {
567 self.writing_mode
568 }
569
570 pub fn chunks(&self) -> &[TextChunk] {
572 &self.chunks
573 }
574
575 pub fn abs_transform(&self) -> Transform {
582 self.abs_transform
583 }
584
585 pub fn bounding_box(&self) -> Rect {
597 self.bounding_box
598 }
599
600 pub fn abs_bounding_box(&self) -> Rect {
604 self.abs_bounding_box
605 }
606
607 pub fn stroke_bounding_box(&self) -> Rect {
613 self.stroke_bounding_box
614 }
615
616 pub fn abs_stroke_bounding_box(&self) -> Rect {
618 self.abs_stroke_bounding_box
619 }
620
621 pub fn flattened(&self) -> &Group {
638 &self.flattened
639 }
640
641 #[cfg(feature = "text")]
647 pub fn layouted(&self) -> &[Span] {
648 &self.layouted
649 }
650
651 pub(crate) fn subroots(&self, f: &mut dyn FnMut(&Group)) {
652 f(&self.flattened);
653 }
654}