# raqim-kashida
A library for finding _kashida_ (_tatweel_) insertion points and priorities,
driven by a small pattern language.
Given a word and a compiled pattern set, the crate returns the possible
_kashida_ insertion points and their priorities:
```rust
use kashida::{builtin_pattern_set, find_kashida_points};
let set = builtin_pattern_set("arabic-simple").unwrap();
let (cleaned, points) = find_kashida_points("بيت", set, true);
for point in points {
// A kashida can be inserted after the grapheme cluster `point.index`.
println!("{} @ {}", point.priority, point.index);
}
```
The _kashida_ insertion points are detected based on text analysis and
_kashida_ rules. It does not take fonts or shaping into account. The actual
justification is also out of scope. It is up to the caller to use the _kashida_
points to justify the text.
## API
| `compile_pattern_text(text)` | a `PatternSet` |
| `builtin_pattern_set(name)` | a built-in set |
| `is_builtin_pattern_set(name)` | whether that name is built in |
| `find_kashida_points(word, set, remove_existing)` | `(cleaned_text, Vec<KashidaPoint>)` |
| `find_kashida_points_patterns(word, set)` | `Vec<KashidaPoint>` |
A `KashidaPoint` is an `index` (the grapheme cluster the kashida goes after)
and a `priority` of 0–9 (with higher meaning more preferable).
`find_kashida_points` first strips any **bare** kashida already in the text
(unless told not to). A _kashida_ that serves as a seat for small _alef_
(U+0670) or a combining _hamza_ (U+0654 and U+0655) is always kept, as the
combination serves as a unit in Quranic orthography. It returns the cleaned
text and the `KashidaPoint` indices are relative to it.
## Pattern sets
Pattern sets describe where _kashida_ can be inserted and the priority of each
insertion point.
They are compiled from a textual representation loosely inspired by Knuth-Liang
hyphenation patterns.
The [built-in sets](data/) are written in the same language. Callers can
compile their own with `compile_pattern_text()`.
### Lines
A set textual representation is a sequence of lines. Blank lines and everything
after a `#` are ignored:
```
# Elongate after an initial or medial seen.
@Seen 6 * # trailing comments work too
```
Each non-blank line is a **pattern**.
### Grammar
```
text ::= line ("\n" line)*
line ::= pattern? comment?
comment ::= "#" anything
pattern ::= guard? element+
guard ::= "[" bound ("+" | "-" bound)? "]"
token ::= reference | set | "^" (set | reference) | letter | "*"
set ::= "{" member+ "}"
member ::= reference | letter
reference ::= ("@" | "=") name
weight ::= digit ("\" digit)? | "!"
```
### Junction placement
A kashida at the junction between graphemes `i` and `i+1` is inserted before
grapheme `i+1`’s cluster. A junction is a candidate only where there is a real
join: never after a right-joining letter such as `د ر و`, and never across a
join a ZWNJ suppresses. Suppressing kashida inside lam-alef ligature, for
instance, is up to the pattern itself.
## License
Licensed under the [MIT license](LICENSE).