![見出し画像](https://assets.st-note.com/production/uploads/images/57740432/rectangle_large_type_2_fd6d430f00d6d94e3adf0992c9c1c33d.png?width=1200)
M5Paperで縦書きテキストビューワを作るよ!(たいへんだったよ!w)
つまり完全自作セルフ(ビルド)パブリッシング!!
なんちゃってw
本の内容だけじゃなくて物理的に本自体を製本しちゃうのが趣味の人っていますよね。
このノートは、それをなぜかデジタルでやっちゃおうというお話なのであります。
とはいってもハードウェア自体はありものの、最近いじりまくってるこの子を使うますです。
4.7インチの大型電子ペーパー(E-ink)を備えたスゴイヤツ。
この上で動く電子書籍的なビューワプログラムを作っちゃおう。というわけですね。Epubを読めたらサイコーだけれど、そこまでは難しいからまずはテキストファイルを縦書き表示したい。
ただ、これが結構クセのある子なのです。横方向への日本語の文字の描画は
↑こんなかんじでなんとかできたのですが、せっかく日本語が出るようになったんだから縦書きのテキストビューワつくりたいよねー(´・ω・)(・ω・`)ネー。と思うのは人の世の常(なのかな?)、まあそんなわけで、いろいろと作戦を練ってみることにします。
まずは先行事例
まずは誰かもうやっていないかなーと探してみて、
こんなステキなプロジェクトを発見しちゃいました。そして出来がいい!!
すっごいなーっておもってよく見てみたら、これ、単純にテキストを表示させているわけでなく、いったん別のコンピュータで画像として保存したデータを表示するっていう手をつかってるんですね。
あったまいい!!
でも、できればもうちょっと簡単に、テキストファイルを入れたら直接縦書き表示してくれるような仕掛けがほしいなー。
と思っていろいろ探したんですけれど良いのが見つかりませんでした。
うーむ。「しょうがない、無いならつくっちゃおう!」と、例によってやってみたわけです。
とりあえず、SDカードへUTF-8形式で保存されたテキストファイルを縦書きで表示するところまでを目指してやってみます。
そう、やってみた。んですが……。
正直、舐めてました
M5Paperの日本語表示、すべて理解した(`・ω・´)キリッ☆
— 神楽坂らせん@『ちょっと上まで…』10話公開!,,Ծ‸Ծ,,☆ (@auxps) July 21, 2021
(※ただし縦書きをのぞくw)https://t.co/7zUP6OVetS pic.twitter.com/F4bptjzHCp
↑の表示ができた段階で、まあ、これを90度回転させればいいわけでしょ、らくしょー♪
なんて、思っていた時期がワタシにもありました><
いやあ、やってみたらこれが大変大変!><
挑戦と挫けた軌跡(ここからしばらくはよみ飛ばしていいですw)
公式のドライバを探す
M5PaperのEインクディスプレイドライバ、M5EPDのドキュメント(英文)
を読みます。(例によって説明が少ない><)
drawString あたりの関数に「方向指定」があることを夢見て読んでみましたですが、やっぱりというか残念ながらありませんでした。これがあったら簡単だったろうに!!><
SetRotationでは?
では、SetRotationなる呪文はどうか?
M5.EPD.SetRotation(90);
こんな風につかいます。90は普通の縦持ちポジション。0で横持ちポジ。
単純に画面全体にやってしまうと、そのまますべての文字が横倒しになるだけで縦書きにはならないんですが、文字単位にこれができるなら、横倒しにしたい文字だけやればいけそう!(”。”とか”、”とかは縦書きになると位置かわりますよね。それをなんとかしたい)
ちょっと余談
突然ですが、日本語の縦書き規則って変じゃありません??
↑の図をごらんください。
下の横書きの「諸行無常の響きあり。」を、そのまま90度倒す(たてる)とこのようになるんですが、ヨコ文字のエーゴとかじゃないばあいはこういう表示じゃダメですよね。
実際には
こんな風に、「縦に並べる」だけで、文字表示の向き自体はそのままなわけです。
当たり前すぎて、見過ごしてしまいますが、日本語の縦書きをする場合はこのようにしなくちゃいけないのです。
なので、単純に SetRotation では全部なってしまうのでダメ。なわけです。
そこで、「表示の向きを変えたい文字だけをSetRotation」したい。という方針になるわけですね。
1文字だけSetRotationに挑戦・・・。
結論から言うと、できませんでした><
canvasで操作範囲を指定して、Rotationして……なんて考えていましたが、そうは問屋が卸してくれず、Rotationすると画面全部がその場で(過去の表示も含めて)がっつり向き変っちゃいます><
持ち方を変えたらその方向に再表示させるためという理由を考えればあたりまえなのですがねー><
そして、
そもそもRotationだけでは無理?
ということにも気づきます。
だいぶ読みやすくなった。
— 神楽坂らせん@『ちょっと上まで…』10話公開!,,Ծ‸Ծ,,☆ (@auxps) July 23, 2021
「。」「、」と「ー」の処理がめんどくちぃ pic.twitter.com/SfVNi56lHJ
↑これは後述のUTF-8問題を乗り越えてからの画面。
一見ちゃんと縦書きできてますが、「。」や「ー」がそのまま横書き同様の表示になっちゃってますね。
「ー」は、縦書きにしたら「|」のように、単に90度Rotationすればよいだけですが、「。」や「、」は、そのままRotationすると、位置がおかしくなります。
↑の赤丸の中にあるように、90度向きを変えると左上に「。」がくる形になってしまう。
これって、日本語の活字体を作った時のバグなんじゃないのー。って気がしないでもないですが、縦書きの時の「。」はやっぱり右上に描かれていないとしっくりきません><
↑こうじゃないと嫌でしょ?
※「。」や「、」が来た時だけ、表示する場所をちょっとずらして書くこともやってみましたが、隣り合う文字が欠けてしまうのでこれもNGでした><
結局どうしたかというと……
もう、めんどくさいから、「自分で描いちゃいました!」
えーーーーって声が聞こえそうだけれど、いいんです。自分で読みやすければ! ってことで、強行突破ですw
canvas.drawCircle(lx+(pt/1.2), ly+(pt/4), 4, 15);
↑ptはフォントのサイズ、lx,lyは表示している文字の位置。なので、従来の表示位置よりx方向にフォント幅/1.2プラス、y方向にフォント幅/4の位置を原点にした円を半径4で描画。という超無理やりサクの結果が
ハンドメイド電子書籍w
— 神楽坂らせん@『ちょっと上まで…』10話公開!,,Ծ‸Ծ,,☆ (@auxps) July 24, 2021
(やっと呪文じゃなくなってきたw) pic.twitter.com/49CjIqxgeN
↑こんなかんじです。
やっとこさ読みやすくなってきましたねw
※「ー」についても「|」になるよう描画していますw
すんごく無理やりなのでちょっとどうかと自分でもおもいますが、まあ読めるからヨシ!(ほんとかなあw)
このあたりの悪戦苦闘の歴史は
↑GitHubにものこしてありますので
↑の行頭の // ← コメントの多さからでも試行錯誤を偲んでいただければ、と><
UTF-8のこと
それともう一つ、上の方ではもうこの問題をクリアしてからのことを書いてしまっていますが、テキストファイルのエンコードについてもちょっと手こずったのでここに書き残しておきます。
M5Paperの扱っているC++では std::string とゆー文字列クラスが扱えるようになっていて、
String()はStringクラスのインスタンスを生成してくれる。だからそのままつかえるよーん(∩´∀`)∩☆ そしてStringクラスはUTF-8に対応している!
だからなんの心配もいらないっ! (と思っていた時期がワタシにもありました……こればっかや><)
実際に使ってみると、UTF-8文字列は1文字に扱うバイト数が固定ではないのですねぇ><
↑ここら辺参照。
コレが何を意味するかというと、UTF-8でエンコードされたテキストファイルは、そのバイト数から文字数が直接わかるわけではないということです><
なので、ファイルから読み取った時に、その文字列が1行におさまるかどうかは、表示(1文字ずつ判定)してみるまでわからないということ!><
普通の横文字ならprintfなどで表示できて、表示エリアを超えたら自動で改行してくれるんですが、縦文字はそうもいかないので、自前で実装する必要があります。。つまり、1文字1文字「この文字はなんて文字?(何バイト?)」と判定してあげなきゃいけないということ。
うっわーー、めんどくさーい><
でもまあ、ここまで作ったからやるっきゃないかあー。。
と、勢いでガリガリ書いたルーチンがこんなかんじ。(センスないですw)
// UTF-8 Check https://seiai.ed.jp/sys/text/java/utf8table.html 参照
//1バイトなら?
//if(buf.charAt(i)>8 and buf.charAt(i)<128){
if(buf.charAt(i)<0x80){
canvas.drawChar(buf.charAt(i), lx, ly);
Serial.print(buf.charAt(i));
//i+=1;
i -= 2 ; // 3バイト幅なので、次ループ用に2バイト戻しておく
//wc=1;
} else if (buf.charAt(i)>0xC0 and buf.charAt(i)<0xE0) {
// 2バイトなら。。(未テスト)
canvas.drawString(buf.substring(i, i+2), lx, ly);
Serial.print(buf.substring(i, i+2));
i -= 1 ; // 3バイト幅なので、次ループ用に1バイト戻しておく
break;
} else if (buf.charAt(i)>0xF0 and buf.charAt(i)>0xF7 ) {
canvas.drawString(buf.substring(i, i+wc+1), lx, ly); // 4バイト文字
i += 1 ;
} else if (buf.charAt(i)>0xF8 and buf.charAt(i)>0xFB ) {
canvas.drawString(buf.substring(i, i+wc+2), lx, ly); //5バイト文字
i += 2 ;
} else if (buf.charAt(i)>0xFC and buf.charAt(i)>0xFD ) {
canvas.drawString(buf.substring(i, i+wc+3), lx, ly); //6バイト文字
i += 3 ;
} else { // else if (buf.charAt(i)>0xE0 and buf.charAt(i)>0xF0 ) { ※本来3バイト文字は 0xE0~0xF0が開始バイトのようだけれどそうでもない?? ここでは上に該当するもの以外を3バイト文字として表示する。
// ここに来るのは3バイト
//Serial.print(buf.substring(i, i+wc));
//「 。」「、」「ー」の処理。無理やり描画
if(buf.substring(i, i+wc).equals("。")){
canvas.drawCircle(lx+(pt/1.2), ly+(pt/4), 4, 15);
} else if (buf.substring(i, i+wc).equals("、")){
canvas.drawLine(lx+(pt/1.2), ly+(pt/4),lx+(pt/1.2)+4, ly+(pt/4)+4, 4, 15);
} else if (buf.substring(i, i+wc).equals("ー")){
canvas.drawLine(lx+(pt/2), ly+(pt/4),lx+(pt/2), ly+(pt-(pt/4)), 3, 15);
} else {
canvas.drawString(buf.substring(i, i+wc), lx, ly);
}
}
もうif文の嵐ですね>< かっこわるい><
下のほうに「。」「、」「ー」の判定文もはいってます。ここも無理やりですねw
もっといい方法あると思うんですが、まあ動くからいいやー(だんだん投げやりw)
そして、3バイト系の漢字類と1バイトのアルファベット&数字以外現状ノーチェックです。変な文字いれたらおかしくなるかも?><
※ここまで書いてから気が付きましたが、もしかしたらSetTextAreaで1文字幅で縦長のエリアを設定して、一番上から普通にprintfしたら縦長の表示になるんじゃ?? なんておもいましたけど結局「。」等の表示で同じことになっちゃいそうなのでこのまま行きます><
改ページ問題
そして、ようやく縦に文字を並べていけるようになってくると、次に現れるのは改ページの問題です。
一行に何バイト(≒何文字)入るかは書いてみないとわからない、ということは、当然、1ページに何バイトはいるかは入れてみないとわからないわけです。当然、行の途中で改行されちゃう場合もありますしね。
なので、まずいったん読み込んだデータを1ページ分画面に埋めてみて、画面内からあふれる瞬間にそのバイト位置を配列に記憶させておいて、そのページを開くときにはそのページ頭ポイントから1ページ分。というカタチで描画するようにしました。
※ページ位置用の配列はとりあえず1000ページ分確保してあります。
そうすると、SDカードからテキストデータをそのページ位置の所まで読み飛ばして1ページ分読む、という処理になるので、長文テキストでもいちいち全部読まなくて良いために意外に高速なページ遷移になったわん。という副作用もありましたw
ようやくできた!(∩´∀`)∩☆
↑の紆余曲折もなんとか乗り越え、ようやくできたのがこんなかんじ!
ビューワーから完全自作セルパブ(?)でけた!!
— 神楽坂らせん@『ちょっと上まで…』10話公開!,,Ծ‸Ծ,,☆ (@auxps) July 25, 2021
うん、読みやすい☆
(まだちょっと記号とか変なとこあるけどねww) pic.twitter.com/VwusJXo5oH
まだ「」や…の処理できてないけど、ページめくり結構速いよ〜☆
— 神楽坂らせん@『ちょっと上まで…』10話公開!,,Ծ‸Ծ,,☆ (@auxps) July 26, 2021
(本体脇にある物理スイッチでめくってまふ)
このサイズの片手で持てるリーダーってお出かけ用にすごくいいかも!!🤗 pic.twitter.com/hTurgX6FMV
↑にあるように、まだ「。」「、」「ー」以外の記号「…」の回転には対応できてませんが全部に対応するのも大変なのでとりあえず現状で公開!
内部のテキストは拙著
から~,,Ծ‸Ծ,,☆
内容から表示ソフトまで自分で作っちゃったセルフビルド&セルフメイドなセルパブですw パブリッシュできてるかどうかはわかりませんが、未完成なりにパブリックに公開しておきますw
↑かなり大きくなっちゃってるのでソースはこちらからー。
今後
必要な記号をもうちょっと対応するのと、ファイルやフォントを選択できるファイラーを装備したり、フォントサイズや行間の調整などもできるようにしたいですねー。まあ、そこらへんはぼちぼちとやってみようとおもいますー☆
ところで!
こんなん(「。」とか「、」とか)を自前で描いてるのってやっぱり変じゃないですか?? 縦書き用のフォントとかってどうなってるんでしょう?? もうちょっとまともでエレガントな方法が別にあるような気がすっごくする今日この頃です。。
今回は動かすことばっか考えていて情報集めがおろそかだった気がするので、もうすこし別の手段についても調査してみないと…と反省してるところです><
いいなと思ったら応援しよう!
![神楽坂らせん](https://assets.st-note.com/production/uploads/images/26348/profile_1d24f04075547f14101d054656b3146a.jpg?width=600&crop=1:1,smart)