use std::{
fmt::{Display, Formatter},
str::FromStr,
};
use harfrust::GlyphBuffer;
use serde::{Deserialize, Serialize};
use crate::Checker;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ShapingInput {
pub text: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub features: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
}
impl ShapingInput {
pub fn new_simple(text: String) -> Self {
Self {
text,
features: Vec::new(),
language: None,
}
}
pub fn new_with_feature(text: String, feature: impl AsRef<str>) -> Self {
Self {
text,
features: vec![feature.as_ref().to_string()],
language: None,
}
}
pub fn shape(&self, checker: &Checker) -> Result<GlyphBuffer, String> {
let mut buffer = harfrust::UnicodeBuffer::new();
buffer.push_str(&self.text);
buffer.guess_segment_properties();
if let Some(language) = &self.language {
buffer.set_language(harfrust::Language::from_str(language)?);
}
let mut features = Vec::new();
for f in &self.features {
features.push(harfrust::Feature::from_str(f)?);
}
let shaper = checker.shaper();
let glyph_buffer = shaper.shape(buffer, &features);
Ok(glyph_buffer)
}
pub fn describe(&self) -> String {
let mut description = format!("shaping the text '{}'", self.text);
if let Some(language) = &self.language {
description.push_str(&format!(" in language '{}'", language));
}
if !self.features.is_empty() {
description.push_str(" with features: ");
description.push_str(&self.features.join(", "));
}
description
}
pub fn char_at(&self, pos: usize) -> Option<char> {
self.text.chars().nth(pos)
}
}
impl Display for ShapingInput {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.describe())
}
}