kashida

Flutter package for justifying Arabic and Syriac text with kashida (tatweel, U+0640): find insertion points, wrap to a pixel width, fill by priority, and paint with KashidaText.

Matching and measure-based layout live in kashida_core (sibling README). This package re-exports that API and adds Flutter measuring plus the widget.

Demo

Example app Justify tab: pick a face and a pattern set, then fill a width. The last line of each paragraph stays short unless Justify last line is on.

Justifying a paragraph with kashida

Features

  • KashidaText — justified block with cached layout
  • layoutParagraphStyled / TextWidthCache — Flutter TextPainter metrics
  • Optional justifyLastLine — stretch the last line of each paragraph (off by default; see design notes)
  • Full kashida_core API via re-export (patterns, find, insert, layoutParagraph)
  • Example app (justify / find-and-insert / rules) with bundled OFL fonts

Getting started

dependencies:
  kashida: ^0.1.2
import 'package:kashida/kashida.dart';

For CLI or server code without Flutter, use kashida_core.

Usage

Justify in a widget

final set = requiredBuiltinPatternSet('arabic-naskh');

KashidaText(
  paragraph,
  patternSet: set,
  width: 320,
  style: const TextStyle(fontSize: 22),
  // justifyLastLine: true, // also stretch the last line of each paragraph
);

Or layout without a widget:

final lines = layoutParagraphStyled(
  paragraph,
  set,
  const TextStyle(fontSize: 22),
  width: 320,
  // justifyLastLine: true,
);

Find points or insert a fixed count

final set = requiredBuiltinPatternSet('arabic-simple');

final found = findKashidaPoints('بيت', set);
for (final point in found.points) {
  print('${point.priority} @ grapheme ${point.index}');
}

// Same tatweel count at every allowed join — not line justification.
print(insertKashida('بيت', set));

KashidaPoint.index is a grapheme index. Do not pass it to substring; use point.endOffsetIn(text) or insertKashidaAt.

Custom rules and the pattern grammar are documented in kashida_core.

Layout behavior

  • Greedy wrap on whitespace; a single over-wide word overflows
  • Leftover space thinner than one tatweel is spread between words (MainAxisAlignment.spaceBetween)
  • Last line of each \n-separated paragraph stays short unless justifyLastLine: true

More detail: doc/design.md.

Example app

cd example
flutter run

Acknowledgements

Port of raqim-kashida by Khaled Hosny and Alif Type. Any mistakes in the Dart implementation are ours.

Additional information

Issues and contributions are welcome. Include a small Arabic or Syriac sample and the pattern set name when reporting matching or justification bugs.

Libraries

kashida
Finding kashida (tatweel) insertion points and priorities, driven by a small pattern language.