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, Debug)]
73pub struct FontVariation {
74 pub tag: [u8; 4],
76 pub value: f32,
78}
79
80impl FontVariation {
81 pub fn new(tag: [u8; 4], value: f32) -> Self {
83 Self { tag, value }
84 }
85}
86
87impl PartialEq for FontVariation {
88 fn eq(&self, other: &Self) -> bool {
89 self.tag == other.tag && self.value.to_bits() == other.value.to_bits()
90 }
91}
92
93impl Eq for FontVariation {}
94
95impl std::hash::Hash for FontVariation {
96 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
97 self.tag.hash(state);
98 self.value.to_bits().hash(state);
99 }
100}
101
102#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
104pub enum FontStyle {
105 Normal,
107 Italic,
109 Oblique,
111}
112
113impl Default for FontStyle {
114 #[inline]
115 fn default() -> FontStyle {
116 Self::Normal
117 }
118}
119
120#[cfg(feature = "text")]
121impl From<fontdb::Style> for FontStyle {
122 fn from(style: fontdb::Style) -> Self {
123 match style {
124 fontdb::Style::Normal => FontStyle::Normal,
125 fontdb::Style::Italic => FontStyle::Italic,
126 fontdb::Style::Oblique => FontStyle::Oblique,
127 }
128 }
129}
130
131#[cfg(feature = "text")]
132impl From<FontStyle> for fontdb::Style {
133 fn from(style: FontStyle) -> Self {
134 match style {
135 FontStyle::Normal => fontdb::Style::Normal,
136 FontStyle::Italic => fontdb::Style::Italic,
137 FontStyle::Oblique => fontdb::Style::Oblique,
138 }
139 }
140}
141
142#[derive(Clone, Eq, PartialEq, Hash, Debug)]
144pub struct Font {
145 pub(crate) families: Vec<FontFamily>,
146 pub(crate) style: FontStyle,
147 pub(crate) stretch: FontStretch,
148 pub(crate) weight: u16,
149 pub(crate) variations: Vec<FontVariation>,
150}
151
152impl Font {
153 pub fn families(&self) -> &[FontFamily] {
157 &self.families
158 }
159
160 pub fn style(&self) -> FontStyle {
162 self.style
163 }
164
165 pub fn stretch(&self) -> FontStretch {
167 self.stretch
168 }
169
170 pub fn weight(&self) -> u16 {
172 self.weight
173 }
174
175 pub fn variations(&self) -> &[FontVariation] {
177 &self.variations
178 }
179}
180
181#[allow(missing_docs)]
183#[derive(Clone, Copy, PartialEq, Debug)]
184pub enum DominantBaseline {
185 Auto,
186 UseScript,
187 NoChange,
188 ResetSize,
189 Ideographic,
190 Alphabetic,
191 Hanging,
192 Mathematical,
193 Central,
194 Middle,
195 TextAfterEdge,
196 TextBeforeEdge,
197}
198
199impl Default for DominantBaseline {
200 fn default() -> Self {
201 Self::Auto
202 }
203}
204
205#[allow(missing_docs)]
207#[derive(Clone, Copy, PartialEq, Debug)]
208pub enum AlignmentBaseline {
209 Auto,
210 Baseline,
211 BeforeEdge,
212 TextBeforeEdge,
213 Middle,
214 Central,
215 AfterEdge,
216 TextAfterEdge,
217 Ideographic,
218 Alphabetic,
219 Hanging,
220 Mathematical,
221}
222
223impl Default for AlignmentBaseline {
224 fn default() -> Self {
225 Self::Auto
226 }
227}
228
229#[allow(missing_docs)]
231#[derive(Clone, Copy, PartialEq, Debug)]
232pub enum BaselineShift {
233 Baseline,
234 Subscript,
235 Superscript,
236 Number(f32),
237}
238
239impl Default for BaselineShift {
240 #[inline]
241 fn default() -> BaselineShift {
242 BaselineShift::Baseline
243 }
244}
245
246#[allow(missing_docs)]
248#[derive(Clone, Copy, PartialEq, Debug)]
249pub enum LengthAdjust {
250 Spacing,
251 SpacingAndGlyphs,
252}
253
254impl Default for LengthAdjust {
255 fn default() -> Self {
256 Self::Spacing
257 }
258}
259
260#[derive(Clone, Copy, PartialEq, Debug)]
265pub enum FontOpticalSizing {
266 Auto,
268 None,
270}
271
272impl Default for FontOpticalSizing {
273 fn default() -> Self {
274 Self::Auto
275 }
276}
277
278#[derive(Clone, Debug)]
285pub struct TextDecorationStyle {
286 pub(crate) fill: Option<Fill>,
287 pub(crate) stroke: Option<Stroke>,
288}
289
290impl TextDecorationStyle {
291 pub fn fill(&self) -> Option<&Fill> {
293 self.fill.as_ref()
294 }
295
296 pub fn stroke(&self) -> Option<&Stroke> {
298 self.stroke.as_ref()
299 }
300}
301
302#[derive(Clone, Debug)]
304pub struct TextDecoration {
305 pub(crate) underline: Option<TextDecorationStyle>,
306 pub(crate) overline: Option<TextDecorationStyle>,
307 pub(crate) line_through: Option<TextDecorationStyle>,
308}
309
310impl TextDecoration {
311 pub fn underline(&self) -> Option<&TextDecorationStyle> {
313 self.underline.as_ref()
314 }
315
316 pub fn overline(&self) -> Option<&TextDecorationStyle> {
318 self.overline.as_ref()
319 }
320
321 pub fn line_through(&self) -> Option<&TextDecorationStyle> {
323 self.line_through.as_ref()
324 }
325}
326
327#[derive(Clone, Debug)]
331pub struct TextSpan {
332 pub(crate) start: usize,
333 pub(crate) end: usize,
334 pub(crate) fill: Option<Fill>,
335 pub(crate) stroke: Option<Stroke>,
336 pub(crate) paint_order: PaintOrder,
337 pub(crate) font: Font,
338 pub(crate) font_size: NonZeroPositiveF32,
339 pub(crate) small_caps: bool,
340 pub(crate) apply_kerning: bool,
341 pub(crate) font_optical_sizing: FontOpticalSizing,
342 pub(crate) decoration: TextDecoration,
343 pub(crate) dominant_baseline: DominantBaseline,
344 pub(crate) alignment_baseline: AlignmentBaseline,
345 pub(crate) baseline_shift: Vec<BaselineShift>,
346 pub(crate) visible: bool,
347 pub(crate) letter_spacing: f32,
348 pub(crate) word_spacing: f32,
349 pub(crate) text_length: Option<f32>,
350 pub(crate) length_adjust: LengthAdjust,
351}
352
353impl TextSpan {
354 pub fn start(&self) -> usize {
358 self.start
359 }
360
361 pub fn end(&self) -> usize {
365 self.end
366 }
367
368 pub fn fill(&self) -> Option<&Fill> {
370 self.fill.as_ref()
371 }
372
373 pub fn stroke(&self) -> Option<&Stroke> {
375 self.stroke.as_ref()
376 }
377
378 pub fn paint_order(&self) -> PaintOrder {
380 self.paint_order
381 }
382
383 pub fn font(&self) -> &Font {
385 &self.font
386 }
387
388 pub fn font_size(&self) -> NonZeroPositiveF32 {
390 self.font_size
391 }
392
393 pub fn small_caps(&self) -> bool {
397 self.small_caps
398 }
399
400 pub fn apply_kerning(&self) -> bool {
404 self.apply_kerning
405 }
406
407 pub fn font_optical_sizing(&self) -> FontOpticalSizing {
413 self.font_optical_sizing
414 }
415
416 pub fn decoration(&self) -> &TextDecoration {
418 &self.decoration
419 }
420
421 pub fn dominant_baseline(&self) -> DominantBaseline {
423 self.dominant_baseline
424 }
425
426 pub fn alignment_baseline(&self) -> AlignmentBaseline {
428 self.alignment_baseline
429 }
430
431 pub fn baseline_shift(&self) -> &[BaselineShift] {
435 &self.baseline_shift
436 }
437
438 pub fn is_visible(&self) -> bool {
440 self.visible
441 }
442
443 pub fn letter_spacing(&self) -> f32 {
445 self.letter_spacing
446 }
447
448 pub fn word_spacing(&self) -> f32 {
450 self.word_spacing
451 }
452
453 pub fn text_length(&self) -> Option<f32> {
455 self.text_length
456 }
457
458 pub fn length_adjust(&self) -> LengthAdjust {
460 self.length_adjust
461 }
462}
463
464#[allow(missing_docs)]
466#[derive(Clone, Copy, PartialEq, Debug)]
467pub enum TextAnchor {
468 Start,
469 Middle,
470 End,
471}
472
473impl Default for TextAnchor {
474 fn default() -> Self {
475 Self::Start
476 }
477}
478
479#[derive(Debug)]
481pub struct TextPath {
482 pub(crate) id: NonEmptyString,
483 pub(crate) start_offset: f32,
484 pub(crate) path: Arc<tiny_skia_path::Path>,
485}
486
487impl TextPath {
488 pub fn id(&self) -> &str {
492 self.id.get()
493 }
494
495 pub fn start_offset(&self) -> f32 {
499 self.start_offset
500 }
501
502 pub fn path(&self) -> &tiny_skia_path::Path {
504 &self.path
505 }
506}
507
508#[derive(Clone, Debug)]
510pub enum TextFlow {
511 Linear,
515 Path(Arc<TextPath>),
517}
518
519#[derive(Clone, Debug)]
523pub struct TextChunk {
524 pub(crate) x: Option<f32>,
525 pub(crate) y: Option<f32>,
526 pub(crate) anchor: TextAnchor,
527 pub(crate) spans: Vec<TextSpan>,
528 pub(crate) text_flow: TextFlow,
529 pub(crate) text: String,
530}
531
532impl TextChunk {
533 pub fn x(&self) -> Option<f32> {
535 self.x
536 }
537
538 pub fn y(&self) -> Option<f32> {
540 self.y
541 }
542
543 pub fn anchor(&self) -> TextAnchor {
545 self.anchor
546 }
547
548 pub fn spans(&self) -> &[TextSpan] {
550 &self.spans
551 }
552
553 pub fn text_flow(&self) -> TextFlow {
555 self.text_flow.clone()
556 }
557
558 pub fn text(&self) -> &str {
560 &self.text
561 }
562}
563
564#[allow(missing_docs)]
566#[derive(Clone, Copy, PartialEq, Debug)]
567pub enum WritingMode {
568 LeftToRight,
569 TopToBottom,
570}
571
572#[derive(Clone, Debug)]
576pub struct Text {
577 pub(crate) id: String,
578 pub(crate) rendering_mode: TextRendering,
579 pub(crate) dx: Vec<f32>,
580 pub(crate) dy: Vec<f32>,
581 pub(crate) rotate: Vec<f32>,
582 pub(crate) writing_mode: WritingMode,
583 pub(crate) chunks: Vec<TextChunk>,
584 pub(crate) abs_transform: Transform,
585 pub(crate) bounding_box: Rect,
586 pub(crate) abs_bounding_box: Rect,
587 pub(crate) stroke_bounding_box: Rect,
588 pub(crate) abs_stroke_bounding_box: Rect,
589 pub(crate) flattened: Box<Group>,
590 #[cfg(feature = "text")]
591 pub(crate) layouted: Vec<Span>,
592}
593
594impl Text {
595 pub fn id(&self) -> &str {
601 &self.id
602 }
603
604 pub fn rendering_mode(&self) -> TextRendering {
608 self.rendering_mode
609 }
610
611 pub fn dx(&self) -> &[f32] {
615 &self.dx
616 }
617
618 pub fn dy(&self) -> &[f32] {
622 &self.dy
623 }
624
625 pub fn rotate(&self) -> &[f32] {
629 &self.rotate
630 }
631
632 pub fn writing_mode(&self) -> WritingMode {
634 self.writing_mode
635 }
636
637 pub fn chunks(&self) -> &[TextChunk] {
639 &self.chunks
640 }
641
642 pub fn abs_transform(&self) -> Transform {
649 self.abs_transform
650 }
651
652 pub fn bounding_box(&self) -> Rect {
664 self.bounding_box
665 }
666
667 pub fn abs_bounding_box(&self) -> Rect {
671 self.abs_bounding_box
672 }
673
674 pub fn stroke_bounding_box(&self) -> Rect {
680 self.stroke_bounding_box
681 }
682
683 pub fn abs_stroke_bounding_box(&self) -> Rect {
685 self.abs_stroke_bounding_box
686 }
687
688 pub fn flattened(&self) -> &Group {
705 &self.flattened
706 }
707
708 #[cfg(feature = "text")]
714 pub fn layouted(&self) -> &[Span] {
715 &self.layouted
716 }
717
718 pub(crate) fn subroots(&self, f: &mut dyn FnMut(&Group)) {
719 f(&self.flattened);
720 }
721}