yokogaki 0.9.0 copy "yokogaki: ^0.9.0" to clipboard
yokogaki: ^0.9.0 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

    • 6 types of emphasis marks (sesame, circles, triangles)
    • 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
  • 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 SimpleHorizontalTextSpan and GroupHorizontalTextSpan
  • Text Selection: Interactive text selection

    • Tap to select single character
    • Drag to select text range
    • Long press to copy text to clipboard
    • Customizable selection color
    • Full support for all typography features

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  yokogaki: ^0.7.0

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 Kinsoku Processing #

HorizontalText(
  text: '日本語の横書きレイアウトを実装しました。禁則処理も対応しています。',
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 20),
    enableKinsoku: true,  // Enable kinsoku processing (default: true)
  ),
  maxWidth: 350,
)

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),
  ),
)

Ruby with Line Breaking #

HorizontalText(
  text: '漢字はとても難しいです。',
  rubyList: const [
    RubyText(startIndex: 0, length: 2, ruby: 'かんじ'),
    RubyText(startIndex: 5, length: 2, ruby: 'むずか'),
  ],
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 24),
    rubyStyle: TextStyle(fontSize: 12, color: Colors.blue),
  ),
  maxWidth: 300,
)

With Kenten (Emphasis Marks) #

HorizontalText(
  text: '重要な部分を強調します。',
  kentenList: const [
    Kenten(startIndex: 0, length: 2, type: KentenType.sesame),
    Kenten(startIndex: 5, length: 2, type: KentenType.filledCircle),
  ],
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 28),
  ),
)

Kenten Types #

HorizontalText(
  text: 'ゴマ 白丸 黒丸 三角 黒三角 二重丸',
  kentenList: const [
    Kenten(startIndex: 0, length: 2, type: KentenType.sesame),
    Kenten(startIndex: 3, length: 2, type: KentenType.circle),
    Kenten(startIndex: 6, length: 2, type: KentenType.filledCircle),
    Kenten(startIndex: 9, length: 2, type: KentenType.triangle),
    Kenten(startIndex: 12, length: 3, type: KentenType.filledTriangle),
    Kenten(startIndex: 16, length: 3, type: KentenType.doubleCircle),
  ],
  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, type: KentenType.filledCircle),
  ],
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 26),
    rubyStyle: TextStyle(fontSize: 12, color: Colors.green),
  ),
)

With Warichu (Inline Annotations) #

HorizontalText(
  text: '本文(注釈)の例です。',
  warichuList: const [
    Warichu(startIndex: 3, length: 0, warichu: 'ここに注釈'),
  ],
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 28),
  ),
)

All Features Combined #

HorizontalText(
  text: '重要(注)な文章です。',
  rubyList: const [
    RubyText(startIndex: 0, length: 2, ruby: 'じゅうよう'),
  ],
  kentenList: const [
    Kenten(startIndex: 0, length: 2, type: KentenType.sesame),
  ],
  warichuList: const [
    Warichu(startIndex: 3, length: 0, warichu: 'ちゅう'),
  ],
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 26),
    rubyStyle: TextStyle(fontSize: 12, color: Colors.purple),
  ),
)

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, type: KentenType.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),
  ),
)

Rich Text - With Ruby #

HorizontalRichText(
  span: GroupHorizontalTextSpan(
    children: [
      SimpleHorizontalTextSpan(
        text: '日本語',
        style: TextStyle(fontSize: 28, color: Colors.black),
        rubyList: const [
          RubyText(startIndex: 0, length: 3, ruby: 'にほんご'),
        ],
      ),
      SimpleHorizontalTextSpan(
        text: 'は',
        style: TextStyle(fontSize: 28, color: Colors.black),
      ),
      SimpleHorizontalTextSpan(
        text: '美しい',
        style: TextStyle(fontSize: 28, color: Colors.pink, fontWeight: FontWeight.bold),
        rubyList: const [
          RubyText(startIndex: 0, length: 3, ruby: 'うつく'),
        ],
      ),
      SimpleHorizontalTextSpan(
        text: 'です。',
        style: TextStyle(fontSize: 28, color: Colors.black),
      ),
    ],
  ),
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 28),
    rubyStyle: TextStyle(fontSize: 14, color: Colors.deepOrange),
  ),
)

Selectable Text #

SelectableHorizontalText(
  text: 'これは選択可能なテキストです。',
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 24),
  ),
  maxWidth: 350,
)

Selectable Text - With Ruby #

SelectableHorizontalText(
  text: '日本語の選択可能なテキスト',
  rubyList: const [
    RubyText(startIndex: 0, length: 3, ruby: 'にほんご'),
    RubyText(startIndex: 4, length: 4, ruby: 'せんたくかのう'),
  ],
  style: HorizontalTextStyle(
    baseStyle: TextStyle(fontSize: 28),
    rubyStyle: TextStyle(fontSize: 14, color: Colors.blue),
  ),
)

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.

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

All planned features are now complete!

  • kinsoku: Japanese text processing library (line breaking, character classification)
  • tategaki: Vertical Japanese text layout

License #

MIT License - see LICENSE file for details

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

References #

1
likes
0
points
237
downloads

Publisher

verified publisherpub.nyapic.com

Weekly Downloads

Flutter package for Japanese horizontal text (yokogaki) layout with ruby, kenten, warichu, and rich text support

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, kinsoku

More

Packages that depend on yokogaki