なでしこさんで禁則処理付きの行揃え（指定文字数で改行）がしたい！

　日本語組み版の禁則処理について学んだことを書き残しておくための記事。
　あるいは、Unicodeに見た目同じような横棒いっぱいあって目が回る話。ダッシュやハイフン種類多すぎ！

# 行揃えとは
　なでしこｖ１には、「行揃え」という命令があります。
　与えた文字列を指定した桁数で折り返して出力してくれるもので、禁則処理もされています。
　しかし、ｖ３には行揃えがありません。

　まあ、アレだ。ｖ３はＷｅｂ版がメインですから。Ｗｅｂ上で表示するなら、改行で折り返したりするより、CSSの`line-break`とか`word-break`とか`word-wrap`とかとかで折り返しの設定をした方が良いに決まってるんですが。

　でも、行揃えしたい！！！
　用途としては、キャンバスへの文字描画です。
　普通に打った文章を、指定文字数で自動的に折り返して文字描画したいワケですよ。

　決まった文字数で折り返すだけなら別にカンタンです。
　文字数ごとに改行コードを入れてけば良いだけ！
　と思ったら、絵文字の罠にハマってサロゲートペア文字だの異体字セレクタだのに悩まされちゃったりもしましたが💧

https://qiita.com/snowdrops89/items/fd9fb96e5c96290a4c47

　でもま、本来やろうとしてたのは禁則処理ですよ。
　簡単なぶら下げくらいならすぐ出来そうだけど、禁則処理の種類も、禁則文字もいろいろあって、あれこれ調べることになったので、ここに書き残しておく。

# 禁則処理とは
　日本語組版には、行頭・行末に特定の文字を置くことを禁止する規則があります。
　また、ダッシュ、リーダなど連続した同じ文字間や、英字や数字など特定の組み合わせの文字間では分割しないという規則もあります。

　ようするに、行頭に句読点`、。`や終わり括弧`」』）`がきたり、行末に始め括弧`「『（`があったりするとカッコ悪いし、英単語やアラビア数字の途中で改行されると読みづらくなるので、そのような配置を禁止し、そうならないように調整するってコトですね。

## 禁則文字一覧
　[ここ](https://www.w3.org/TR/jlreq/#character_classes)を参考にしています。
　同じ半角記号も追加しています。
　
### 行頭禁則
- **終わり括弧類**
```
』」）〕］｝〉》】｠〙〗»〟’”)]}｣
```
- **句読点類**
```
。．、，､｡,.
```
- **ハイフン類**
```
‐〜゠–
```
- **区切り約物**
```
！？‼⁇⁈⁉!?
```
- **中点類**
```
・：；･:;
```
- **繰返し記号**
```
ヽヾゝゞ々〻
```
- **長音記号**
```
ーｰ
```
- **小書き仮名**
```
ぁぃぅぇぉァィゥェォっゃゅょゎゕゖッャュョヮヵヶㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿｧｨｩｪｫｬｭｮｯ
```
### 行末禁則
- **始め括弧類**
```
『「（〔［｛〈《【｟〘〖«〝‘“([{｢
```
### 分割禁止
- **分離禁止文字**
　連続した、` —…‥ `の間。
　縦書きの場合、` 〳〴 `と` 〵 `の間。
- **英単語**
　ただし、行末にハイフンを付けることで分割可能。
- **連数字**
　連続したアラビア数字の間。
- **前置省略記号**
```
￥＄￡＃€№
```
　と、その後ろにくるアラビア数字・漢数字との間。
- **後置省略記号**
```
°′″℃￠％‰㏋ℓ㌃㌍㌔㌘㌢㌣㌦㌧㌫㌶㌻㍉㍊㍍㍑㍗㎎㎏㎜㎝㎞㎡㏄
```
　と、その前にくるアラビア数字・漢数字との間。

### ハイフンとダッシュの罠
　似たような横線っぽいのいっぱいあるのねん💧
|字形|Unicode|名称|通用名称|備考|
|:--:|:----:|:---|:------|:---|
|‐|2010|HYPHEN|ハイフン（四分）|002Dとは別の本当の（？）ハイフン。「ハイフン類」|
|‑|2011|Non-Breaking Hyphen||折り返しをしないハイフン。（参考）|
|‒|2012|Figure Dash||数字と同じ幅のダッシュ。（参考）|
|–|2013|EN DASH|二分ダーシ，ダッシュ（二分）|nと同じ横幅のダッシュ。「ハイフン類」|
|—|2014|EM DASH|ダッシュ（全角）|mと同じ横幅のダッシュ。「分離禁止文字」|
|―|2015|HORIZONTAL BAR|クォーテーションダッシュ|普通に変換されるダッシュ。SJISの全角ダッシュ(815C)|
|-|002D|HYPHEN-MINUS|ハイフンマイナス|キーボードから打てる普通のハイフン|
|－|FF0D|FULLWIDTH HYPHEN-MINUS|全角ハイフンマイナス|普通に変換される全角ハイフン|
|ー|30FC|KATAKANA-HIRAGANA PROLONGED SOUND MARK|長音記号|
|ｰ|ff70|HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK|半角長音記号|
|─|2500|BOX DRAWINGS LIGHT HORIZONTAL|横細線素片|罫線|

　長音記号もフォントによってはただの横棒ですよねー。
　なでしこの変数名付けたりする時に、長音記号のつもりで全角ハイフン使ってしまうとエラーなるのでご注意です。過去の質問に、それが原因でうまくいってなかった件ありました。
　それはともかく。
　参考サイトで「ハイフン類」とされているハイフンは、普通にキーボードから打てるハイフンではありませんでした。
　`-`や`－`は正確にはハイフンマイナスと言って、ハイフンでもありマイナスでもあるとゆう記号なので、日本語組み版としてはマイナスの意味を持たない本当のハイフン`‐`となってると言うのは分かりますが、nダッシュ`–`がハイフン類なんだ・・・
　なんにしろ、そんな凝った記号を（ワタシが）使うわけないので、ふつーのハイフンも入れとかないと意味ない～。
　そして、「分離禁止文字」に入ってるダッシュはmダッシュ`—`でしたが、ATOKさんがダッシュとして変換してくれるのは、HORIZONTAL BAR`―`の方なんですよ。
　そもそもnダッシュとmダッシュは、フォントによっては入っていないし、シフトJISの全角ダッシュ(0x815C)はHORIZONTAL BARの方に変換されてるっぽい。なんか、この辺、いろいろコンランがあるみたい。参考サイトでも「HORIZONTAL BARにも同様の振る舞いを実装しているものもある」とか書いてあるしね。
　ややこやしい！
　なんにしろnダッシュやmダッシュはシフトJISでは表示できずｖ１のnakopadでも「?」になっちゃうんだからこれまで何も考えず使ってきたのは間違いなくHORIZONTAL BARの全角ダッシュなわけよね。
　あと、関係ないけどたまーにダッシュのつもりで罫線`─`で打ってくる人もいるんだよねーｗ

## 禁則処理の種類
- **追い出し禁則**
　行末禁則文字を次行の先頭に移動する。
　行頭禁則文字を直前の文字と合わせて次行に移動する。
　分割禁止文字列が行を跨ぐ場合、その全てを次行に移動する。
　字間を広げて表示域に割り付ける。
- **追い込み禁則**
　行頭禁則文字を前の行の末尾に移動する。
　分割禁止文字列が行を跨ぐ場合、その全てを前の行の末尾に移動する。
　字間を詰めて表示域を超えないようにする。
- **ぶら下げ禁則**
　句読点を前の行の末尾に移動するが、字間の調整を行わず、表示域からはみ出させて表示させる。
　適用されるのは句読点のみ。


　行末禁則を追い込みで行うことは普通無いですよね。
　行末禁則は、始め括弧だけなので、前の行の一文字を追い込んだってやっぱりカッコ悪い。
　あと、字間を詰めて追い込める文字数には自ずと限界があるものと思う。
　字間の調整は表示する時の処理なので、ここでは、ぶら下げ禁則は追い込みと同じと考えられます。

# 行揃えの禁則処理は？
　なでしこｖ１にてお試ししたところ、こんな感じでしたー。
　なでしこの行揃えは、文字数ではなくバイト数での指定なので、`10で行揃え`しています。
```
# 終わり括弧
『あいうえお」
あいうえお」」」』を10で行揃えして表示。
　↓
あいうえお」
あいうえ
お」」」
```
```
# 句読点類
『かきくけこ。
かきくけこ。。。』を10で行揃えして表示。
　↓
かきくけこ。
かきくけ
こ。。。
```
```
# ハイフン類
『さしすせそ－
さしすせそ－－』を10で行揃えして表示。
　↓
さしすせそ
－
さしすせそ
－－
```
```
# 区切り約物
『たちつてと！
たちつてと！！』を10で行揃えして表示。
　↓
たちつてと！
たちつて
と！！
```
```
# 中点類
『なにぬねの・
なにぬねの・・・』を10で行揃えして表示。
　↓
なにぬねの・
なにぬね
の・・・
```
```
# 繰返し記号
『はひふへほ々
はひふへほ々々』を10で行揃えして表示。
　↓
はひふへほ々
はひふへ
ほ々々
```
```
# 長音類
『まみむめもー
まみむめもーー』を10で行揃えして表示。
　↓
まみむめもー
まみむめ
もーー
```
```
# 小書き仮名
『みゃみゅみょ』を10で行揃えして表示。
　↓
みゃみゅみ
ょ
```
```
# 分離禁止
『やゆよよ……』を10で行揃えして表示。
　↓
やゆよよ…
…
```
```
# 始め括弧
『らりるれ「ろ』を10で行揃えして表示。
　↓
らりるれ「
ろ
```
```
# 半角濁点・半濁点
『ﾊﾟﾋﾟﾌﾟﾍﾟｯﾎﾟ』を10で行揃えして表示。
　↓
ﾊﾟﾋﾟﾌﾟﾍﾟｯﾎﾟ
```
```
# 行文字数を超えて禁則文字が続いた場合
『あいう
。。。。。。。。。。』を10で行揃えして表示。
　↓
あいう。
。。。。。。
。。。
```

　基本、一文字だったら追い込んで、禁則文字が連続した場合は追い出しているみたいね。
　ハイフン類と小書き仮名は、行頭禁則に入っていないラシイ。
　そしてなんと、行末禁則は行われていない！　いままでずっと気づかんかった～。
　分割禁止も行われていないようですね。
　行文字数を超えて禁則文字が続いた場合どうなるのかなー？　と、素朴な疑問で試してみたら、一行だけの時には一見良いのですが、前の行があると改行されていても一文字追い込まれてしまうみたい・・・

　まあ、無理に全部同じにしなくてもいいんだけど、とりあえずこんな感じ。
　ってか、ハイフン類と分割禁止が行われないなら、横線っぽいのの数々についての考察は別にいらなかった:joy_cat:

# なでしこさんで行揃え

　とりあえず、できたような？
```
!『https://n3s.nadesi.com/plain/mojisyuPlus.nako3』を取り込む。//サロゲートペア文字や異体字セレクタを判定。
＃ 禁則処理
＃＃ 禁則文字一覧
変数 禁則文字一覧＝{
  // ・行頭禁則
    「終わり括弧類」:「』」&『」）〕］｝〉》】｠〙〗»〟’”』&『)]}｣』,
    「句読点類」:『。．、，』&『､｡,.』,
    「ハイフン類」:『‐〜゠–』&『-－』,
    「区切り約物」:『！？』&『‼⁇⁈⁉』&『!?』,
    「中点類」:『・：；』&『･:;』,
    「繰返し記号」:『ヽヾゝゞ々〻』,
    「長音記号」:『ー』&『ｰ』,
    「小書き仮名」:『ぁぃぅぇぉァィゥェォっゃゅょゎゕゖッャュョヮヵヶ』&『ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ』&『ｧｨｩｪｫｬｭｮｯ』,
  // ・行末禁則
    「始め括弧類」:「『」&『「（〔［｛〈《【｟〘〖«〝‘“』&『([{｢』,
  // ・分割禁止
    「分離禁止文字」:『—…‥〳〴〵』&『―』,
    「前置省略記号」:『￥＄￡＃€№』,
    「後置省略記号」:『°′″℃￠％‰㏋ℓ㌃㌍㌔㌘㌢㌣㌦㌧㌫㌶㌻㍉㍊㍍㍑㍗㎎㎏㎜㎝㎞㎡㏄』,
  // ・仮名と分離禁止
    「濁点半濁点」:『゛゜』&『ﾞﾟ』
}

＃＃ 禁則文字指定
変数 行頭禁則文字＝禁則文字一覧["終わり括弧類"]&禁則文字一覧["句読点類"]&禁則文字一覧["区切り約物"]&禁則文字一覧["中点類"]&禁則文字一覧["繰返し記号"]&禁則文字一覧["長音記号"]&禁則文字一覧["濁点半濁点"]&「—…―～」&異体字セレクタ一覧取得。
変数 行末禁則文字＝禁則文字一覧["始め括弧類"]。

＃＃ 一字追い込み
# 次行行頭が行頭禁則文字ならば、一字だけ追い込む。
●(対象行の行を)一字追い込み処理
    次行行頭＝対象行の1だけ文字左部分。
    此行末尾＝行の1だけ文字右部分。
    もし、(((行頭禁則文字の0から次行行頭を文字検索)＞0)かつ((対象行の文字数)≠0))ならば、:
        行＝行&(対象行の1から1を文字抜き出す)。
        対象行＝対象行の1から1を文字削除。
    [対象行,行]で戻る。
ここまで。

＃＃ 追い出し禁則
# 行末禁則（この行の末尾が行末禁則文字ならば、それを次の行に送る）
# 行頭禁則（次行行頭が行頭禁則文字ならば、この行の末尾を次の行に送る）
# 禁則文字が連続する間、再帰で処理する。
●(対象行の行を行文字数で)追い出し禁則処理
    次行行頭＝対象行の1だけ文字左部分。
    此行末尾＝行の1だけ文字右部分。
    # 行の全てが同じジャンルの禁則文字だったら処理しない。
    もし、((行を「[{行頭禁則文字の『]』を『\]』に置換}]{波カッコ}{行文字数},{波カッコ閉じ}」で正規表現マッチ)＝NULL)かつ　＿
    ((行を「[{行末禁則文字}]{波カッコ}{行文字数},{波カッコ閉じ}」で正規表現マッチ)＝NULL)ならば、:
        もし、(((行の文字数)≧行文字数)かつ((行末禁則文字の0から此行末尾を文字検索)＞0))または　＿
        (((行頭禁則文字の0から次行行頭を文字検索)＞0)かつ((対象行の文字数)≠0))ならば、:
            対象行＝(行の(行の文字数)から1を文字抜き出す)&対象行。
            行＝行の(行の文字数)から1を文字削除。
            対象行の行を行文字数-1で追い出し禁則処理。# 再帰
        違えば、:
            [対象行,行]で戻る。
    違えば、:
        空で戻る。# 禁則文字が行文字数以上連続した場合
ここまで。

＃ 行揃え
●(原稿を行文字数で)行揃え
    変数 対象行＝空。変数 行＝空。変数 変換済み原稿＝空。
    変数 次行行頭＝空。変数 此行末尾＝空。
    原稿＝原稿を改行で区切る。# 本文の行単位で処理する。
    原稿を反復:
        対象行＝対象。
        もし、対象行＝空ならば、　# 空行の場合。
            変換済み原稿＝変換済み原稿に空を一行追加。
        違えば、
            (対象行の文字数)≠0の間:
                F=いいえ。
                抜き出し文字数＝対象行から行文字数で抜き出し文字数カウント。
                行＝対象行の1から抜き出し文字数を文字抜き出す。
                対象行＝対象行の1から抜き出し文字数を文字削除。
                # 一字追い込み
                対象行の行を一字追い込み処理。
                対象行,行＝それ。
                # 追い出し禁則
                対象行の行を行文字数で追い出し禁則処理。
                もし、それ==空でなければ、:
                    対象行,行＝それ。
                変換済み原稿＝変換済み原稿に行を一行追加。
        ここまで。
    変換済み原稿で戻る。
ここまで。

●(対象行から字数で抜き出し文字数を)抜き出し文字数カウント
        もし、抜き出し文字数＝空ならば、抜き出し文字数＝字数。
        行＝対象行の1から字数を文字抜き出す。
        対象行＝対象行の1から字数を文字削除。
        特殊文字個数＝(行の上位サロゲート個数)＋(行の異体字セレクタ個数)＋(行の半角文字個数)/2。
        抜き出し文字数＝抜き出し文字数＋特殊文字個数。
        もし、特殊文字個数＝０ならば、
            抜き出し文字数で戻る。
        違えば、もし、特殊文字個数＜１ならば、
            抜き出し文字数＋１で戻る。
        違えば、
            対象行から特殊文字個数で抜き出し文字数を抜き出し文字数カウント。
        ここまで。
ここまで。
```
　ｖ１と違い、文字数での指定にしました。（毎回間違っては「あれっ？」てなってたのねん💧）
　半角文字は一文字で二文字抜き出してるので、ｖ１と結果は変わらない・・・ハズ。

　禁則文字は、ｖ１同様ハイフン類と小書き仮名は禁則文字に含めず、分割禁止処理も行っていません。でも、三点リーダと全角ダッシュとmダッシュと波ダッシュは、行頭禁則に含めました（結果的に分離はされなくなる）。
　始め括弧の行末禁則は入れました。あと、 行文字数を超えて禁則文字が続いた場合は処理を行わないことにしました。
　必要に応じて前置省略記号や後置省略記号の一部も加えるかも、ってかよく考えたら禁則文字は変数にしたんだから、文書によって変えたい放題だった。

　禁則処理は、先に一字だけ追い込み、その後禁則文字が連続していた場合は再帰で連続している限り追い出しています。
　サロゲートペア文字などを数えて抜き出し文字数をプラスする処理も再帰で地道にやっていてあんまり賢そうでは無い＞＜
　異体字セレクタは、行頭禁則にすることで分離禁止しています。
　サロゲートペア文字や異体字セレクタの判定（mojisyuPlus.nako3）については、前の記事を参照。

https://qiita.com/snowdrops89/items/fd9fb96e5c96290a4c47

# 動作確認

　できました！

https://n3s.nadesi.com/id.php?1891

　一応、意図したとおりに禁則処理されていると思います☆
　絵文字も、異体字セレクタ付きの絵文字も、ちゃんと一文字として処理されてますね♪（絵文字は禁則対象ではありません。きちんと見た目通りの字数で改行して文字化けもしないのを確認）

　~~でもこれ、サロゲートペア文字や異体字セレクタを判定したり数えたりするやつをプラグインとして取り込んでいるんだけど、これをさらに他のプログラムから取り込んで使ってみようとしてみたら、これのプラグイン名がメインになって、テストが表示されちゃった💧
　取り込むは多重になっちゃいけないのかな？:thinking:
　とりあえず、プラグインにするなら必要な命令は全部貼っ付けて取り込むは使わないか、取り込むならテストは別っこにするかのどっちかでだいじょぶでした。~~
　・・・とかぼやいていたら、v3.3.78で修正していただけました！
　プラグインの機能が一段と便利になりました！！！
　ありがたや～:bow: