#![doc = include_str!("../examples/justify.rs")]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![warn(unreachable_pub)]
mod builtin;
mod error;
mod grapheme;
mod pattern;
mod rasm;
mod resolve;
mod segmenter;
#[cfg(test)]
mod tests;
pub use builtin::{builtin_pattern_set, builtin_pattern_set_names, is_builtin_pattern_set};
pub use error::{CompileError, CompileErrorKind};
pub use pattern::{compile_pattern_text, PatternSet};
pub use segmenter::JoinedRunSegmenter;
use grapheme::{find_run_indices, is_bare_tatweel_at, split_graphemes, KASHIDA};
use resolve::resolve_run;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct KashidaPoint {
pub index: u32,
pub priority: u8,
}
fn strip_bare_tatweel(text: &str) -> String {
if !text.contains(KASHIDA) {
return text.to_string();
}
let chars: Vec<char> = text.chars().collect();
let mut out = String::with_capacity(text.len());
for k in 0..chars.len() {
if is_bare_tatweel_at(&chars, k) {
continue;
}
out.push(chars[k]);
}
out
}
pub fn find_kashida_points(
text: &str,
set: &PatternSet,
remove_existing_kashida: bool,
) -> (String, Vec<KashidaPoint>) {
let cleaned = if remove_existing_kashida {
strip_bare_tatweel(text)
} else {
text.to_string()
};
let boundaries: Vec<usize> = JoinedRunSegmenter::new().segment_str(&cleaned).collect();
let mut points = Vec::new();
let mut grapheme_offset = 0;
for pair in boundaries.windows(2) {
let graphemes = split_graphemes(&cleaned[pair[0]..pair[1]]);
let run = find_run_indices(&graphemes);
points.extend(
resolve_run(&graphemes, &run, set)
.into_iter()
.map(|point| KashidaPoint {
index: point.index + grapheme_offset,
..point
}),
);
grapheme_offset += graphemes.len() as u32;
}
(cleaned, points)
}