use std::sync::Arc;
use strict_num::NonZeroPositiveF32;
pub use svgrtypes::FontFamily;
use crate::{
Fill, Group, NonEmptyString, PaintOrder, Rect, Stroke, TextRendering, Transform, Visibility,
};
#[allow(missing_docs)]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub enum FontStretch {
UltraCondensed,
ExtraCondensed,
Condensed,
SemiCondensed,
Normal,
SemiExpanded,
Expanded,
ExtraExpanded,
UltraExpanded,
}
impl Default for FontStretch {
#[inline]
fn default() -> Self {
Self::Normal
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum FontStyle {
Normal,
Italic,
Oblique,
}
impl Default for FontStyle {
#[inline]
fn default() -> FontStyle {
Self::Normal
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct Font {
pub(crate) families: Vec<FontFamily>,
pub(crate) style: FontStyle,
pub(crate) stretch: FontStretch,
pub(crate) weight: u16,
}
impl Font {
pub fn families(&self) -> &[FontFamily] {
&self.families
}
pub fn style(&self) -> FontStyle {
self.style
}
pub fn stretch(&self) -> FontStretch {
self.stretch
}
pub fn weight(&self) -> u16 {
self.weight
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
pub enum DominantBaseline {
Auto,
UseScript,
NoChange,
ResetSize,
Ideographic,
Alphabetic,
Hanging,
Mathematical,
Central,
Middle,
TextAfterEdge,
TextBeforeEdge,
}
impl Default for DominantBaseline {
fn default() -> Self {
Self::Auto
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
pub enum AlignmentBaseline {
Auto,
Baseline,
BeforeEdge,
TextBeforeEdge,
Middle,
Central,
AfterEdge,
TextAfterEdge,
Ideographic,
Alphabetic,
Hanging,
Mathematical,
}
impl Default for AlignmentBaseline {
fn default() -> Self {
Self::Auto
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum BaselineShift {
Baseline,
Subscript,
Superscript,
Number(f32),
}
impl std::hash::Hash for BaselineShift {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
BaselineShift::Baseline => 0.hash(state),
BaselineShift::Subscript => 1.hash(state),
BaselineShift::Superscript => 2.hash(state),
BaselineShift::Number(v) => (3 + v.to_bits()).hash(state),
}
}
}
impl Default for BaselineShift {
#[inline]
fn default() -> BaselineShift {
BaselineShift::Baseline
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
pub enum LengthAdjust {
Spacing,
SpacingAndGlyphs,
}
impl Default for LengthAdjust {
fn default() -> Self {
Self::Spacing
}
}
#[derive(Clone, Debug, Hash)]
pub struct TextDecorationStyle {
pub(crate) fill: Option<Fill>,
pub(crate) stroke: Option<Stroke>,
}
impl TextDecorationStyle {
pub fn fill(&self) -> Option<&Fill> {
self.fill.as_ref()
}
pub fn stroke(&self) -> Option<&Stroke> {
self.stroke.as_ref()
}
}
#[derive(Clone, Debug, Hash)]
pub struct TextDecoration {
pub(crate) underline: Option<TextDecorationStyle>,
pub(crate) overline: Option<TextDecorationStyle>,
pub(crate) line_through: Option<TextDecorationStyle>,
}
impl TextDecoration {
pub fn underline(&self) -> Option<&TextDecorationStyle> {
self.underline.as_ref()
}
pub fn overline(&self) -> Option<&TextDecorationStyle> {
self.overline.as_ref()
}
pub fn line_through(&self) -> Option<&TextDecorationStyle> {
self.line_through.as_ref()
}
}
#[derive(Clone, Debug)]
pub struct TextSpan {
pub(crate) start: usize,
pub(crate) end: usize,
pub(crate) fill: Option<Fill>,
pub(crate) stroke: Option<Stroke>,
pub(crate) paint_order: PaintOrder,
pub(crate) font: Font,
pub(crate) font_size: NonZeroPositiveF32,
pub(crate) small_caps: bool,
pub(crate) apply_kerning: bool,
pub(crate) decoration: TextDecoration,
pub(crate) dominant_baseline: DominantBaseline,
pub(crate) alignment_baseline: AlignmentBaseline,
pub(crate) baseline_shift: Vec<BaselineShift>,
pub(crate) visibility: Visibility,
pub(crate) letter_spacing: f32,
pub(crate) word_spacing: f32,
pub(crate) text_length: Option<f32>,
pub(crate) length_adjust: LengthAdjust,
}
impl std::hash::Hash for TextSpan {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.start.hash(state);
self.end.hash(state);
self.fill.hash(state);
self.stroke.hash(state);
self.paint_order.hash(state);
self.font.hash(state);
self.font_size.hash(state);
self.small_caps.hash(state);
self.apply_kerning.hash(state);
self.decoration.hash(state);
self.dominant_baseline.hash(state);
self.alignment_baseline.hash(state);
self.baseline_shift.hash(state);
self.visibility.hash(state);
self.letter_spacing.to_bits().hash(state);
self.word_spacing.to_bits().hash(state);
self.text_length.map(|f| f.to_bits()).hash(state);
self.length_adjust.hash(state);
}
}
impl TextSpan {
pub fn start(&self) -> usize {
self.start
}
pub fn end(&self) -> usize {
self.end
}
pub fn fill(&self) -> Option<&Fill> {
self.fill.as_ref()
}
pub fn stroke(&self) -> Option<&Stroke> {
self.stroke.as_ref()
}
pub fn paint_order(&self) -> PaintOrder {
self.paint_order
}
pub fn font(&self) -> &Font {
&self.font
}
pub fn font_size(&self) -> NonZeroPositiveF32 {
self.font_size
}
pub fn small_caps(&self) -> bool {
self.small_caps
}
pub fn apply_kerning(&self) -> bool {
self.apply_kerning
}
pub fn decoration(&self) -> &TextDecoration {
&self.decoration
}
pub fn dominant_baseline(&self) -> DominantBaseline {
self.dominant_baseline
}
pub fn alignment_baseline(&self) -> AlignmentBaseline {
self.alignment_baseline
}
pub fn baseline_shift(&self) -> &[BaselineShift] {
&self.baseline_shift
}
pub fn visibility(&self) -> Visibility {
self.visibility
}
pub fn letter_spacing(&self) -> f32 {
self.letter_spacing
}
pub fn word_spacing(&self) -> f32 {
self.word_spacing
}
pub fn text_length(&self) -> Option<f32> {
self.text_length
}
pub fn length_adjust(&self) -> LengthAdjust {
self.length_adjust
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
pub enum TextAnchor {
Start,
Middle,
End,
}
impl Default for TextAnchor {
fn default() -> Self {
Self::Start
}
}
#[derive(Debug)]
pub struct TextPath {
pub(crate) id: NonEmptyString,
pub(crate) start_offset: f32,
pub(crate) path: Arc<tiny_skia_path::Path>,
}
impl std::hash::Hash for TextPath {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use crate::hashers::CustomHash;
self.id.hash(state);
self.start_offset.to_bits().hash(state);
self.path.custom_hash(state);
}
}
impl TextPath {
pub fn id(&self) -> &str {
self.id.get()
}
pub fn start_offset(&self) -> f32 {
self.start_offset
}
pub fn path(&self) -> &tiny_skia_path::Path {
&self.path
}
}
#[derive(Clone, Debug, Hash)]
pub enum TextFlow {
Linear,
Path(Arc<TextPath>),
}
#[derive(Clone, Debug)]
pub struct TextChunk {
pub(crate) x: Option<f32>,
pub(crate) y: Option<f32>,
pub(crate) anchor: TextAnchor,
pub(crate) spans: Vec<TextSpan>,
pub(crate) text_flow: TextFlow,
pub(crate) text: String,
}
impl std::hash::Hash for TextChunk {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.x.map(|f| f.to_bits()).hash(state);
self.y.map(|f| f.to_bits()).hash(state);
self.anchor.hash(state);
self.spans.hash(state);
self.text_flow.hash(state);
self.text.hash(state);
}
}
impl TextChunk {
pub fn x(&self) -> Option<f32> {
self.x
}
pub fn y(&self) -> Option<f32> {
self.y
}
pub fn anchor(&self) -> TextAnchor {
self.anchor
}
pub fn spans(&self) -> &[TextSpan] {
&self.spans
}
pub fn text_flow(&self) -> TextFlow {
self.text_flow.clone()
}
pub fn text(&self) -> &str {
&self.text
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
pub enum WritingMode {
LeftToRight,
TopToBottom,
}
#[derive(Clone, Debug)]
pub struct Text {
pub(crate) id: String,
pub(crate) rendering_mode: TextRendering,
pub(crate) dx: Vec<f32>,
pub(crate) dy: Vec<f32>,
pub(crate) rotate: Vec<f32>,
pub(crate) writing_mode: WritingMode,
pub(crate) chunks: Vec<TextChunk>,
pub(crate) abs_transform: Transform,
pub(crate) bounding_box: Rect,
pub(crate) abs_bounding_box: Rect,
pub(crate) stroke_bounding_box: Rect,
pub(crate) abs_stroke_bounding_box: Rect,
pub(crate) flattened: Box<Group>,
pub(crate) static_hash: Option<u64>,
}
impl std::hash::Hash for Text {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use crate::hashers::CustomHash;
self.id.hash(state);
self.rendering_mode.hash(state);
self.dx
.iter()
.map(|x| x.to_bits())
.collect::<Vec<_>>()
.hash(state);
self.dy
.iter()
.map(|y| y.to_bits())
.collect::<Vec<_>>()
.hash(state);
self.rotate
.iter()
.map(|y| y.to_bits())
.collect::<Vec<_>>()
.hash(state);
self.writing_mode.hash(state);
self.chunks.hash(state);
self.abs_transform.custom_hash(state);
self.bounding_box.custom_hash(state);
self.abs_bounding_box.custom_hash(state);
self.stroke_bounding_box.custom_hash(state);
self.abs_stroke_bounding_box.custom_hash(state);
self.flattened.hash(state);
}
}
impl Text {
pub fn id(&self) -> &str {
&self.id
}
pub fn rendering_mode(&self) -> TextRendering {
self.rendering_mode
}
pub fn dx(&self) -> &[f32] {
&self.dx
}
pub fn dy(&self) -> &[f32] {
&self.dy
}
pub fn rotate(&self) -> &[f32] {
&self.rotate
}
pub fn writing_mode(&self) -> WritingMode {
self.writing_mode
}
pub fn chunks(&self) -> &[TextChunk] {
&self.chunks
}
pub fn abs_transform(&self) -> Transform {
self.abs_transform
}
pub fn bounding_box(&self) -> Rect {
self.bounding_box
}
pub fn abs_bounding_box(&self) -> Rect {
self.abs_bounding_box
}
pub fn stroke_bounding_box(&self) -> Rect {
self.stroke_bounding_box
}
pub fn abs_stroke_bounding_box(&self) -> Rect {
self.abs_stroke_bounding_box
}
pub fn flattened(&self) -> &Group {
&self.flattened
}
pub(crate) fn subroots(&self, f: &mut dyn FnMut(&Group)) {
f(&self.flattened);
}
}