yokogaki 0.10.2
yokogaki: ^0.10.2 copied to clipboard
Flutter package for Japanese horizontal text (yokogaki) layout with ruby, kenten, warichu, and rich text support
yokogaki #
Flutter package for Japanese horizontal text (yokogaki - 横書き) layout with advanced typography features including ruby, kenten, warichu, and rich text support.
Features #
-
Kinsoku Processing (禁則処理): Japanese line breaking rules
- Line-start prohibition (行頭禁則, gyoto kinsoku)
- Line-end prohibition (行末禁則, gyomatsu kinsoku)
- Hanging characters (ぶら下げ, burasage)
- Pushing-in characters (追い込み, oikomi)
-
Yakumono Adjustment (約物調整): Fine-tune punctuation positioning
- Half-width yakumono handling
- Gyoto indent for opening brackets
- Consecutive yakumono spacing
-
Ruby Text (ルビ/振り仮名): Furigana support
- Place ruby text above base characters
- Multi-line ruby support (splits across line breaks)
- Customizable ruby style
-
Kenten (圏点): Emphasis marks
- Multiple styles: sesame, circles, triangles, squares, stars, diamonds, X marks
- Place marks above characters for emphasis
- Customizable kenten style
- Combine with ruby text
-
Warichu (割注): Inline annotations
- Two-line inline annotations
- Automatically splits text into two rows
- Displayed inline with main text
- Combine with ruby and kenten
-
Text Decoration (上線/下線): Underline and overline support
- Single line, double line, wavy line, dotted line
- Works with ruby (automatic position adjustment)
-
Text Alignment (地付き): Line-level alignment
TextAlignment.start: Align to left (default)TextAlignment.center: Center alignmentTextAlignment.end(地付き): Align to right
-
Text Indentation (字下げ): Character-based indentation
indent: Indent all lines (全行字下げ)firstLineIndent: Indent first line only (段落字下げ)
-
Rich Text: Multiple styles in one text block
- Span-based architecture for hierarchical text
- Mix multiple colors, fonts, sizes, and weights
- Per-span ruby, kenten, and warichu annotations
- Powerful text composition with
SimpleHorizontalTextSpanandGroupHorizontalTextSpan
-
Text Selection: Interactive text selection
- Tap to select single character
- Drag to select text range
- Draggable selection handles
- Context menu with copy/select all
- Keyboard shortcuts (Ctrl+C, Ctrl+A)
- Customizable selection color
- Full support for all typography features
Related Packages #
This package is part of the Japanese text layout suite:
| Package | Description |
|---|---|
| kinsoku | Core text processing (line breaking, character classification) |
| tategaki | Vertical text layout (縦書き) |
| yokogaki | Horizontal text layout (this package) |
Quick Start #
3ステップで横書きテキストを表示:
// 1. Import
import 'package:yokogaki/yokogaki.dart';
// 2. Widget内で使用
HorizontalText(
text: 'こんにちは、世界!',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 24),
),
)
ルビ付きの例:
HorizontalText(
text: '日本語',
rubyList: const [RubyText(startIndex: 0, length: 3, ruby: 'にほんご')],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 32),
rubyStyle: TextStyle(fontSize: 14),
),
)
Platform Support #
| Platform | Status | Notes |
|---|---|---|
| Android | ✅ Supported | All features |
| iOS | ✅ Supported | All features |
| Web | ✅ Supported | All features |
| Windows | ✅ Supported | All features |
| macOS | ✅ Supported | All features |
| Linux | ✅ Supported | All features |
Requirements:
- Flutter: ≥1.17.0
- Dart SDK: ≥3.10.3
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
yokogaki: ^0.10.1
Then run:
flutter pub get
Usage #
Basic Horizontal Text #
import 'package:flutter/material.dart';
import 'package:yokogaki/yokogaki.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return HorizontalText(
text: 'これは横書きのテストです。',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 24),
),
);
}
}
With Line Breaking #
HorizontalText(
text: '吾輩は猫である。名前はまだ無い。どこで生まれたか頓と見当がつかぬ。',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 24),
),
maxWidth: 300, // Enable line breaking at 300px
)
With Ruby Text (Furigana) #
HorizontalText(
text: '日本語',
rubyList: const [
RubyText(startIndex: 0, length: 3, ruby: 'にほんご'),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 32),
rubyStyle: TextStyle(fontSize: 14, color: Colors.red),
),
)
With Kenten (Emphasis Marks) #
HorizontalText(
text: '重要な部分を強調します。',
kentenList: const [
Kenten(startIndex: 0, length: 2, style: KentenStyle.sesame),
Kenten(startIndex: 5, length: 2, style: KentenStyle.filledCircle),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 28),
),
)
Available kenten styles:
KentenStyle.sesame- ゴマ点 (•)KentenStyle.filledCircle- 黒丸 (●)KentenStyle.circle- 白丸 (○)KentenStyle.filledTriangle- 黒三角 (▲)KentenStyle.triangle- 白三角 (△)KentenStyle.doubleCircle- 二重丸 (◎)KentenStyle.filledSquare- 黒四角 (■)KentenStyle.square- 白四角 (□)KentenStyle.filledDiamond- 黒菱形 (◆)KentenStyle.diamond- 白菱形 (◇)KentenStyle.filledStar- 黒星 (★)KentenStyle.star- 白星 (☆)KentenStyle.x- バツ (×)
With Text Decoration (Underline/Overline) #
HorizontalText(
text: '下線と上線のテストです。',
decorationList: const [
TextDecorationAnnotation(
startIndex: 0,
endIndex: 2,
type: TextDecorationLineType.underline,
),
TextDecorationAnnotation(
startIndex: 3,
endIndex: 5,
type: TextDecorationLineType.overline,
),
TextDecorationAnnotation(
startIndex: 6,
endIndex: 8,
type: TextDecorationLineType.wavyUnderline,
),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 28),
),
)
With Text Alignment (地付き) #
HorizontalText(
text: '右揃えの例です。',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 24),
alignment: TextAlignment.end, // 地付き - align to right
),
maxWidth: 400,
)
With First Line Indent (段落字下げ) #
Traditional Japanese paragraph indentation where only the first line is indented:
HorizontalText(
text: '吾輩は猫である。名前はまだ無い。どこで生まれたか頓と見当がつかぬ。',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 20),
firstLineIndent: 1, // First line indented by 1 character
),
maxWidth: 300,
)
With Warichu (Inline Annotations) #
HorizontalText(
text: '本文(注釈)の例です。',
warichuList: const [
Warichu(startIndex: 3, length: 0, warichu: 'ここに注釈'),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 28),
),
)
Combined Ruby and Kenten #
HorizontalText(
text: '重要な日本語を学びます。',
rubyList: const [
RubyText(startIndex: 0, length: 2, ruby: 'じゅうよう'),
RubyText(startIndex: 3, length: 3, ruby: 'にほんご'),
],
kentenList: const [
Kenten(startIndex: 0, length: 2, style: KentenStyle.filledCircle),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 26),
rubyStyle: TextStyle(fontSize: 12, color: Colors.green),
),
)
Rich Text - Multiple Styles #
HorizontalRichText(
span: GroupHorizontalTextSpan(
children: [
SimpleHorizontalTextSpan(
text: 'これは',
style: TextStyle(fontSize: 24, color: Colors.black),
),
SimpleHorizontalTextSpan(
text: '重要',
style: TextStyle(fontSize: 24, color: Colors.red, fontWeight: FontWeight.bold),
kentenList: const [
Kenten(startIndex: 0, length: 2, style: KentenStyle.filledCircle),
],
),
SimpleHorizontalTextSpan(
text: 'な',
style: TextStyle(fontSize: 24, color: Colors.black),
),
SimpleHorizontalTextSpan(
text: 'テキスト',
style: TextStyle(fontSize: 24, color: Colors.blue, fontStyle: FontStyle.italic),
),
SimpleHorizontalTextSpan(
text: 'です。',
style: TextStyle(fontSize: 24, color: Colors.black),
),
],
),
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 24),
),
)
Selectable Text #
SelectableHorizontalText(
text: 'これは選択可能なテキストです。',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 24),
),
maxWidth: 350,
)
SelectionArea Integration (Selection API) #
Use SelectionAreaHorizontalText inside SelectionArea to enable unified text selection across multiple widgets:
SelectionArea(
child: Column(
children: [
SelectableText('通常のテキスト'),
SelectionAreaHorizontalText(
text: '日本語の横書きテキスト',
rubyList: [
RubyText(startIndex: 0, length: 3, ruby: 'にほんご'),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 24),
rubyStyle: TextStyle(fontSize: 12),
),
),
],
),
)
This allows seamless text selection that spans across multiple selectable widgets.
Debug Grid #
HorizontalText(
text: 'グリッド表示テスト',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 32),
),
showGrid: true, // Show character grid for debugging
)
Example #
See the example directory for a complete demo app showcasing all features.
Use Cases / ユースケース #
ブログ・記事表示 #
一般的なWebコンテンツの表示:
HorizontalText(
text: '今日は天気がよかったので、散歩に出かけました。'
'公園では桜が満開で、とても綺麗でした。',
style: HorizontalTextStyle(
baseStyle: TextStyle(
fontSize: 16,
height: 1.8,
fontFamily: 'NotoSansJP',
),
),
maxWidth: 600,
)
教科書・教材 #
ルビと圏点を使った学習教材:
HorizontalText(
text: '日本国憲法は国民主権を基本原理とする。',
rubyList: const [
RubyText(startIndex: 0, length: 4, ruby: 'にほんこくけんぽう'),
RubyText(startIndex: 5, length: 4, ruby: 'こくみんしゅけん'),
RubyText(startIndex: 10, length: 4, ruby: 'きほんげんり'),
],
kentenList: const [
Kenten(startIndex: 5, length: 4, style: KentenStyle.filledCircle),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 20),
rubyStyle: TextStyle(fontSize: 10, color: Colors.grey),
),
maxWidth: 500,
)
新聞・ニュースアプリ #
注釈付きのニュース記事:
HorizontalText(
text: '政府は新たな経済政策を発表した。',
warichuList: const [
Warichu(startIndex: 7, length: 0, warichu: '経済成長と物価安定を目指す'),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 18),
),
maxWidth: 400,
)
論文・学術文書 #
上線・下線による強調:
HorizontalText(
text: '本研究の結論は以下の通りである。',
decorationList: const [
TextDecorationAnnotation(
startIndex: 4,
endIndex: 6,
type: TextDecorationLineType.underline,
),
],
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 16),
),
)
チャット・メッセージアプリ #
選択可能なメッセージ表示:
SelectableHorizontalText(
text: 'こんにちは!今日の予定はどうですか?',
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 16),
),
maxWidth: 280,
)
リッチテキストエディタ #
複数スタイルの組み合わせ:
HorizontalRichText(
span: GroupHorizontalTextSpan(
children: [
SimpleHorizontalTextSpan(
text: '重要:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
SimpleHorizontalTextSpan(
text: '明日は会議があります。',
style: TextStyle(fontSize: 16),
),
],
),
style: HorizontalTextStyle(
baseStyle: TextStyle(fontSize: 16),
),
)
Performance #
yokogaki v0.6.0+ includes significant performance optimizations:
- LRU Layout Cache: Automatic caching of layout calculations with 100-entry limit
- TextPainter Reuse: Reduced memory allocations by reusing TextPainter instances
- Smart Invalidation: Only recalculates when text, style, or width actually changes
Performance improvements:
- ~70% faster layout calculation for repeated renders
- ~50% fewer TextPainter allocations
- Excellent performance for scrollable lists of Japanese text
Roadmap #
- ✅ Basic horizontal text layout
- ✅ Kinsoku processing
- ✅ Yakumono adjustment
- ✅ Line breaking with kinsoku rules
- ✅ Ruby text (furigana) support
- ✅ Kenten (emphasis marks)
- ✅ Warichu (inline annotations)
- ✅ Rich text with multiple styles
- ✅ Performance optimizations
- ✅ Text selection support
- ✅ Text decoration (underline/overline)
- ✅ Text alignment (地付き)
- ✅ Selection API integration (SelectionArea support)
All planned features are now complete!
License #
MIT License - see LICENSE file for details
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
References #
- JIS X 4051:2004 - Japanese document composition method
- W3C Requirements for Japanese Text Layout (JLREQ)
- CSS Text Module Level 3/4