1use alloc::boxed::Box;
5use alloc::string::String;
6use alloc::sync::Arc;
7use alloc::vec::Vec;
8
9use strict_num::NonZeroPositiveF32;
10pub use svgtypes::FontFamily;
11
12#[cfg(feature = "text")]
13use crate::layout::Span;
14use crate::{Fill, Group, NonEmptyString, PaintOrder, Rect, Stroke, TextRendering, Transform};
15
16#[allow(missing_docs)]
18#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
19pub enum FontStretch {
20 UltraCondensed,
21 ExtraCondensed,
22 Condensed,
23 SemiCondensed,
24 Normal,
25 SemiExpanded,
26 Expanded,
27 ExtraExpanded,
28 UltraExpanded,
29}
30
31impl Default for FontStretch {
32 #[inline]
33 fn default() -> Self {
34 Self::Normal
35 }
36}
37
38#[cfg(feature = "text")]
39impl From<fontdb::Stretch> for FontStretch {
40 fn from(stretch: fontdb::Stretch) -> Self {
41 match stretch {
42 fontdb::Stretch::UltraCondensed => FontStretch::UltraCondensed,
43 fontdb::Stretch::ExtraCondensed => FontStretch::ExtraCondensed,
44 fontdb::Stretch::Condensed => FontStretch::Condensed,
45 fontdb::Stretch::SemiCondensed => FontStretch::SemiCondensed,
46 fontdb::Stretch::Normal => FontStretch::Normal,
47 fontdb::Stretch::SemiExpanded => FontStretch::SemiExpanded,
48 fontdb::Stretch::Expanded => FontStretch::Expanded,
49 fontdb::Stretch::ExtraExpanded => FontStretch::ExtraExpanded,
50 fontdb::Stretch::UltraExpanded => FontStretch::UltraExpanded,
51 }
52 }
53}
54
55#[cfg(feature = "text")]
56impl From<FontStretch> for fontdb::Stretch {
57 fn from(stretch: FontStretch) -> Self {
58 match stretch {
59 FontStretch::UltraCondensed => fontdb::Stretch::UltraCondensed,
60 FontStretch::ExtraCondensed => fontdb::Stretch::ExtraCondensed,
61 FontStretch::Condensed => fontdb::Stretch::Condensed,
62 FontStretch::SemiCondensed => fontdb::Stretch::SemiCondensed,
63 FontStretch::Normal => fontdb::Stretch::Normal,
64 FontStretch::SemiExpanded => fontdb::Stretch::SemiExpanded,
65 FontStretch::Expanded => fontdb::Stretch::Expanded,
66 FontStretch::ExtraExpanded => fontdb::Stretch::ExtraExpanded,
67 FontStretch::UltraExpanded => fontdb::Stretch::UltraExpanded,
68 }
69 }
70}
71
72#[derive(Clone, Copy, Debug)]
76pub struct FontVariation {
77 pub tag: [u8; 4],
79 pub value: f32,
81}
82
83impl FontVariation {
84 pub fn new(tag: [u8; 4], value: f32) -> Self {
86 Self { tag, value }
87 }
88}
89
90impl PartialEq for FontVariation {
91 fn eq(&self, other: &Self) -> bool {
92 self.tag == other.tag && self.value.to_bits() == other.value.to_bits()
93 }
94}
95
96impl Eq for FontVariation {}
97
98impl core::hash::Hash for FontVariation {
99 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
100 self.tag.hash(state);
101 self.value.to_bits().hash(state);
102 }
103}
104
105#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
107pub enum FontStyle {
108 Normal,
110 Italic,
112 Oblique,
114}
115
116impl Default for FontStyle {
117 #[inline]
118 fn default() -> FontStyle {
119 Self::Normal
120 }
121}
122
123#[cfg(feature = "text")]
124impl From<fontdb::Style> for FontStyle {
125 fn from(style: fontdb::Style) -> Self {
126 match style {
127 fontdb::Style::Normal => FontStyle::Normal,
128 fontdb::Style::Italic => FontStyle::Italic,
129 fontdb::Style::Oblique => FontStyle::Oblique,
130 }
131 }
132}
133
134#[cfg(feature = "text")]
135impl From<FontStyle> for fontdb::Style {
136 fn from(style: FontStyle) -> Self {
137 match style {
138 FontStyle::Normal => fontdb::Style::Normal,
139 FontStyle::Italic => fontdb::Style::Italic,
140 FontStyle::Oblique => fontdb::Style::Oblique,
141 }
142 }
143}
144
145#[derive(Clone, Eq, PartialEq, Hash, Debug)]
147pub struct Font {
148 pub(crate) families: Vec<FontFamily>,
149 pub(crate) style: FontStyle,
150 pub(crate) stretch: FontStretch,
151 pub(crate) weight: u16,
152 pub(crate) variations: Vec<FontVariation>,
153}
154
155impl Font {
156 pub fn families(&self) -> &[FontFamily] {
160 &self.families
161 }
162
163 pub fn style(&self) -> FontStyle {
165 self.style
166 }
167
168 pub fn stretch(&self) -> FontStretch {
170 self.stretch
171 }
172
173 pub fn weight(&self) -> u16 {
175 self.weight
176 }
177
178 pub fn variations(&self) -> &[FontVariation] {
180 &self.variations
181 }
182}
183
184#[allow(missing_docs)]
186#[derive(Clone, Copy, PartialEq, Debug)]
187pub enum DominantBaseline {
188 Auto,
189 UseScript,
190 NoChange,
191 ResetSize,
192 Ideographic,
193 Alphabetic,
194 Hanging,
195 Mathematical,
196 Central,
197 Middle,
198 TextAfterEdge,
199 TextBeforeEdge,
200}
201
202impl Default for DominantBaseline {
203 fn default() -> Self {
204 Self::Auto
205 }
206}
207
208#[allow(missing_docs)]
210#[derive(Clone, Copy, PartialEq, Debug)]
211pub enum AlignmentBaseline {
212 Auto,
213 Baseline,
214 BeforeEdge,
215 TextBeforeEdge,
216 Middle,
217 Central,
218 AfterEdge,
219 TextAfterEdge,
220 Ideographic,
221 Alphabetic,
222 Hanging,
223 Mathematical,
224}
225
226impl Default for AlignmentBaseline {
227 fn default() -> Self {
228 Self::Auto
229 }
230}
231
232#[allow(missing_docs)]
234#[derive(Clone, Copy, PartialEq, Debug)]
235pub enum BaselineShift {
236 Baseline,
237 Subscript,
238 Superscript,
239 Number(f32),
240}
241
242impl Default for BaselineShift {
243 #[inline]
244 fn default() -> BaselineShift {
245 BaselineShift::Baseline
246 }
247}
248
249#[allow(missing_docs)]
251#[derive(Clone, Copy, PartialEq, Debug)]
252pub enum LengthAdjust {
253 Spacing,
254 SpacingAndGlyphs,
255}
256
257impl Default for LengthAdjust {
258 fn default() -> Self {
259 Self::Spacing
260 }
261}
262
263#[derive(Clone, Copy, PartialEq, Debug)]
268pub enum FontOpticalSizing {
269 Auto,
271 None,
273}
274
275impl Default for FontOpticalSizing {
276 fn default() -> Self {
277 Self::Auto
278 }
279}
280
281#[derive(Clone, Debug)]
288pub struct TextDecorationStyle {
289 pub(crate) fill: Option<Fill>,
290 pub(crate) stroke: Option<Stroke>,
291}
292
293impl TextDecorationStyle {
294 pub fn fill(&self) -> Option<&Fill> {
296 self.fill.as_ref()
297 }
298
299 pub fn stroke(&self) -> Option<&Stroke> {
301 self.stroke.as_ref()
302 }
303}
304
305#[derive(Clone, Debug)]
307pub struct TextDecoration {
308 pub(crate) underline: Option<TextDecorationStyle>,
309 pub(crate) overline: Option<TextDecorationStyle>,
310 pub(crate) line_through: Option<TextDecorationStyle>,
311}
312
313impl TextDecoration {
314 pub fn underline(&self) -> Option<&TextDecorationStyle> {
316 self.underline.as_ref()
317 }
318
319 pub fn overline(&self) -> Option<&TextDecorationStyle> {
321 self.overline.as_ref()
322 }
323
324 pub fn line_through(&self) -> Option<&TextDecorationStyle> {
326 self.line_through.as_ref()
327 }
328}
329
330#[derive(Clone, Debug)]
334pub struct TextSpan {
335 pub(crate) start: usize,
336 pub(crate) end: usize,
337 pub(crate) fill: Option<Fill>,
338 pub(crate) stroke: Option<Stroke>,
339 pub(crate) paint_order: PaintOrder,
340 pub(crate) font: Font,
341 pub(crate) font_size: NonZeroPositiveF32,
342 pub(crate) small_caps: bool,
343 pub(crate) apply_kerning: bool,
344 pub(crate) font_optical_sizing: FontOpticalSizing,
345 pub(crate) decoration: TextDecoration,
346 pub(crate) dominant_baseline: DominantBaseline,
347 pub(crate) alignment_baseline: AlignmentBaseline,
348 pub(crate) baseline_shift: Vec<BaselineShift>,
349 pub(crate) visible: bool,
350 pub(crate) letter_spacing: f32,
351 pub(crate) word_spacing: f32,
352 pub(crate) text_length: Option<f32>,
353 pub(crate) length_adjust: LengthAdjust,
354}
355
356impl TextSpan {
357 pub fn start(&self) -> usize {
361 self.start
362 }
363
364 pub fn end(&self) -> usize {
368 self.end
369 }
370
371 pub fn fill(&self) -> Option<&Fill> {
373 self.fill.as_ref()
374 }
375
376 pub fn stroke(&self) -> Option<&Stroke> {
378 self.stroke.as_ref()
379 }
380
381 pub fn paint_order(&self) -> PaintOrder {
383 self.paint_order
384 }
385
386 pub fn font(&self) -> &Font {
388 &self.font
389 }
390
391 pub fn font_size(&self) -> NonZeroPositiveF32 {
393 self.font_size
394 }
395
396 pub fn small_caps(&self) -> bool {
400 self.small_caps
401 }
402
403 pub fn apply_kerning(&self) -> bool {
407 self.apply_kerning
408 }
409
410 pub fn font_optical_sizing(&self) -> FontOpticalSizing {
416 self.font_optical_sizing
417 }
418
419 pub fn decoration(&self) -> &TextDecoration {
421 &self.decoration
422 }
423
424 pub fn dominant_baseline(&self) -> DominantBaseline {
426 self.dominant_baseline
427 }
428
429 pub fn alignment_baseline(&self) -> AlignmentBaseline {
431 self.alignment_baseline
432 }
433
434 pub fn baseline_shift(&self) -> &[BaselineShift] {
438 &self.baseline_shift
439 }
440
441 pub fn is_visible(&self) -> bool {
443 self.visible
444 }
445
446 pub fn letter_spacing(&self) -> f32 {
448 self.letter_spacing
449 }
450
451 pub fn word_spacing(&self) -> f32 {
453 self.word_spacing
454 }
455
456 pub fn text_length(&self) -> Option<f32> {
458 self.text_length
459 }
460
461 pub fn length_adjust(&self) -> LengthAdjust {
463 self.length_adjust
464 }
465}
466
467#[allow(missing_docs)]
469#[derive(Clone, Copy, PartialEq, Debug)]
470pub enum TextAnchor {
471 Start,
472 Middle,
473 End,
474}
475
476impl Default for TextAnchor {
477 fn default() -> Self {
478 Self::Start
479 }
480}
481
482#[derive(Debug)]
484pub struct TextPath {
485 pub(crate) id: NonEmptyString,
486 pub(crate) start_offset: f32,
487 pub(crate) path: Arc<tiny_skia_path::Path>,
488}
489
490impl TextPath {
491 pub fn id(&self) -> &str {
495 self.id.get()
496 }
497
498 pub fn start_offset(&self) -> f32 {
502 self.start_offset
503 }
504
505 pub fn path(&self) -> &tiny_skia_path::Path {
507 &self.path
508 }
509}
510
511#[derive(Clone, Debug)]
513pub enum TextFlow {
514 Linear,
518 Path(Arc<TextPath>),
520}
521
522#[derive(Clone, Debug)]
526pub struct TextChunk {
527 pub(crate) x: Option<f32>,
528 pub(crate) y: Option<f32>,
529 pub(crate) anchor: TextAnchor,
530 pub(crate) spans: Vec<TextSpan>,
531 pub(crate) text_flow: TextFlow,
532 pub(crate) text: String,
533}
534
535impl TextChunk {
536 pub fn x(&self) -> Option<f32> {
538 self.x
539 }
540
541 pub fn y(&self) -> Option<f32> {
543 self.y
544 }
545
546 pub fn anchor(&self) -> TextAnchor {
548 self.anchor
549 }
550
551 pub fn spans(&self) -> &[TextSpan] {
553 &self.spans
554 }
555
556 pub fn text_flow(&self) -> TextFlow {
558 self.text_flow.clone()
559 }
560
561 pub fn text(&self) -> &str {
563 &self.text
564 }
565}
566
567#[allow(missing_docs)]
569#[derive(Clone, Copy, PartialEq, Debug)]
570pub enum WritingMode {
571 LeftToRight,
572 TopToBottom,
573}
574
575#[derive(Clone, Debug)]
579pub struct Text {
580 pub(crate) id: String,
581 pub(crate) rendering_mode: TextRendering,
582 pub(crate) dx: Vec<f32>,
583 pub(crate) dy: Vec<f32>,
584 pub(crate) rotate: Vec<f32>,
585 pub(crate) writing_mode: WritingMode,
586 pub(crate) chunks: Vec<TextChunk>,
587 pub(crate) abs_transform: Transform,
588 pub(crate) bounding_box: Rect,
589 pub(crate) abs_bounding_box: Rect,
590 pub(crate) stroke_bounding_box: Rect,
591 pub(crate) abs_stroke_bounding_box: Rect,
592 pub(crate) flattened: Box<Group>,
593 #[cfg(feature = "text")]
594 pub(crate) layouted: Vec<Span>,
595}
596
597impl Text {
598 pub fn id(&self) -> &str {
604 &self.id
605 }
606
607 pub fn rendering_mode(&self) -> TextRendering {
611 self.rendering_mode
612 }
613
614 pub fn dx(&self) -> &[f32] {
618 &self.dx
619 }
620
621 pub fn dy(&self) -> &[f32] {
625 &self.dy
626 }
627
628 pub fn rotate(&self) -> &[f32] {
632 &self.rotate
633 }
634
635 pub fn writing_mode(&self) -> WritingMode {
637 self.writing_mode
638 }
639
640 pub fn chunks(&self) -> &[TextChunk] {
642 &self.chunks
643 }
644
645 pub fn abs_transform(&self) -> Transform {
652 self.abs_transform
653 }
654
655 pub fn bounding_box(&self) -> Rect {
667 self.bounding_box
668 }
669
670 pub fn abs_bounding_box(&self) -> Rect {
674 self.abs_bounding_box
675 }
676
677 pub fn stroke_bounding_box(&self) -> Rect {
683 self.stroke_bounding_box
684 }
685
686 pub fn abs_stroke_bounding_box(&self) -> Rect {
688 self.abs_stroke_bounding_box
689 }
690
691 pub fn flattened(&self) -> &Group {
708 &self.flattened
709 }
710
711 #[cfg(feature = "text")]
717 pub fn layouted(&self) -> &[Span] {
718 &self.layouted
719 }
720
721 pub(crate) fn subroots(&self, f: &mut dyn FnMut(&Group)) {
722 f(&self.flattened);
723 }
724}