kashida_core 0.1.0
kashida_core: ^0.1.0 copied to clipboard
Finding kashida (tatweel) insertion points and priorities, driven by a small pattern language. Pure Dart (no Flutter).
kashida_core #
Pure Dart library for finding kashida (tatweel, U+0640) insertion points and priorities in Arabic and Syriac text, driven by a small pattern language.
Port of raqim-kashida. No Flutter
dependency — use from CLI tools, servers, or tests. For Flutter painting and
the KashidaText widget, use
kashida, which re-exports this package.
Detection is based on text analysis and kashida rules. It does not take fonts or shaping into account by itself.
There are three separate steps:
- Find points —
findKashidaPoints - Insert a fixed number of tatweels at every point —
insertKashida(not justification) - Wrap to a width and fill by priority —
layoutParagraphwith a caller-suppliedmeasurecallback
KashidaPoint.index is a grapheme index. Do not pass it to substring;
use point.endOffsetIn(text) or insertKashidaAt.
For background, see Khaled Hosny’s introduction to raqim-kashida and the interactive demo.
Features #
- Compile pattern text, including
useof a built-in set - Built-in sets:
arabic-naskh,arabic-nastaliq,arabic-simple,syriac - Optional stripping of bare tatweels (mark-seated tatweels are kept)
- Insert tatweel into a string without choosing a font
- Wrap and fill lines given a width and a
measurecallback - Grapheme-cluster indices, joining analysis, and rasm folding matching the original library
Getting started #
dependencies:
kashida_core: ^0.1.0
import 'package:kashida_core/kashida_core.dart';
Usage #
import 'package:kashida_core/kashida_core.dart';
void main() {
final set = requiredBuiltinPatternSet('arabic-simple');
final found = findKashidaPoints('بيت', set);
for (final point in found.points) {
print('${point.priority} @ grapheme ${point.index}');
}
// Same count at every allowed join — not a justified line.
print(insertKashida('بيت', set));
}
findKashidaPointsIn (also named findKashidaPointsPatterns) skips stripping.
Fill a width with your own metrics:
layoutParagraph(
paragraph,
set,
width: 320,
measure: (line) => /* width of line in the same units as 320 */,
);
Custom rules can extend or override a built-in set:
final set = compilePatternText('''
use arabic-naskh
* 2 @Heh .
''');
The pattern language is the same as upstream. See the
raqim-kashida README
for the grammar, length guards, priorities, and ! suppression.
Built-in pattern sets #
| Name | Intended for |
|---|---|
arabic-naskh |
Classical naskh and naskh-like faces |
arabic-nastaliq |
Nastaliq (naskh rules plus nastaliq tailoring) |
arabic-simple |
Simple / kufic-style faces (Microsoft-style newspaper rules) |
syriac |
Syriac, following the LibreOffice / expert guidelines |
requiredBuiltinPatternSet(name) is the usual lookup (throws if unknown).
builtinPatternSet(name) returns null instead, for probing names.
Layout limits #
layoutParagraph uses greedy whitespace wrap. A single word wider than
the target width is not broken; it overflows. This is a basic filler, not a
HarfBuzz/ICU paragraph shaper. Leftover space thinner than one tatweel is
returned as KashidaLine.unusedWidth.
Regenerating Unicode tables #
Joining type/group tables are generated from Unicode 17.0.0 UCD files:
dart run tool/generate_joining_tables.dart
Downloads are cached under tool/ucd/ (gitignored). Generated
joining_tables.dart is committed; CI does not hit the network to regenerate.
Acknowledgements #
Port of raqim-kashida by Khaled Hosny and Alif Type. Any mistakes in the Dart implementation are ours.
Additional information #
Issues: github.com/byshy/kashida/issues. Include a small Arabic or Syriac sample and the pattern set name when reporting matching bugs.