昨年のアドベントカレンダー、最後の記事がコレでした。
まるっと一年越しのばーじょんあっぷということになるでしょうか。
テキストを一文字ずつ
昨年作成した原稿用紙では、テキスト要素を一行ずつ配置していました。
キャンバスへの描画と違い、SVG画像ではCSSで文字間隔などの指定が行えるので、それさえしっかりと行えば一文字ずつ位置を指定しなくても、きっちりとマス目に文字が収まって描画できる♪ というのがそもそもの発端だったからです。
が! それではシンタックスハイライトは出来ません。
色設定はテキスト要素に設定するので、文字ごとに色を変えるためにはテキストを文字ごとにバラして描画しなくては。
あらかじめ空のテキストを設置した原稿用紙に文章を流し込んでいますので、自在に色を変えるには、完全に一文字ずつにバラす必要があります。
アタマの中だけで考えると、なんかもーうわ~めんどくさ~ てなるよね~💧
いや、賢い人ならぜんぜんそんなことないのカモですが~。
そして、実際やってみたら、そう面倒なことはありませんでした。
だってベツに、自分で植字するわけじゃないんですから!
全部なでしこさんがやってくれるんですから!!
原稿用紙の作成と実際の執筆で、それぞれループが一つ深くなるくらいなもんです。
ていうか、フツーに二次元配列のと同じアレです。
# テキスト
書体にSVG文字フォント設定。
「{マス幅-文字余白}px」にSVG文字サイズ設定
無色にSVG線色設定。筆色にSVG塗り色設定。
行数回:
行=回数-1。
原稿用紙テキスト[行]=空配列。
行文字数+1回:
列=回数-1。
原稿用紙テキスト[行][列]=[(文字描画位置[行][0]),(文字描画位置[行][1]+マス幅*列)]に空をSVG縦書き文字描画。
ぶら下げ禁則の分があるので、20行×21文字で作成しています。
●(Aを)執筆とは:
原稿用紙行数回:
行=回数-1。
もし、回数>(Aの要素数)ならば、此行=空。
違えば、此行=A[行]。
此行は此行を文字列分解。
原稿用紙行文字数回:
列=回数-1。
もし、列≧(此行の要素数)ならば:
原稿用紙テキスト[行][列]に空をテキスト設定。
違えば:
原稿用紙テキスト[行][列]に此行[列]をテキスト設定。
カラーリングする
ここまで来れば、数値や文字列、コメント文など簡単な物は容易に色づけが出来るようになります。
なでしこには、助詞や予約語、システム関数などの一覧を取得する命令もあります。
👉 なでしこ3 マニュアル > plugin_system/デバッグ支援
しかし、これだけでは十分な結果を得られません。
なでしこの命令は、助詞として指定された以外の平仮名を送り仮名として使用出来るようになっているなど、より日本語らしく表記することが出来るよう工夫されており、単純にこの単語はこの色、という割り当ては出来ないんですよね。
そこで!
今年のアドベントカレンダー、最初の記事でご紹介した、TKIさん作の『なでしこスクリプトをhtmlに変換』です。
これがその辺キチンと完備されていて素晴らしい上、MIT Licenseという制限の緩いライセンスで公開されていますので、こちらのスクリプト分割部分をまるっとお借りしてやっていこうと思います。
最初の記事に引き続き、最後の記事も、おんぶに抱っこでスミマセン🙏
構文分割
たとえば、
「こんにちは、なでしこさん」と表示する。というコードを構文分割すると、
[
["コメント","#サンプル"],
["改行","\n"],
["文字列","「こんにちは、なでしこさん」"],
["助詞","と"],
["関数名","表示する"],
["句読点","。"]
]
このような二次元配列が出来ます。
そして、この0列目の区分に沿ってカラーリング設定に指定したHTMLタグを付けて出力していくというものなのですが、原稿用紙の場合にはページの概念があります。
一ページごとにページを進めたり、また戻ったりしなければならないし、関数とかが前後ページにわたって分断されてしまうこともあるでしょう。
あと、原稿用紙側を大幅に変更したくない(めんどい💧)
原稿用紙へ組み込む
とゆうわけで、あんまり賢そうじゃないけど、頁分割前の生原稿を構文分割したあと、頁分割した原稿を1ページずつ文字数を数え! 構文分割後データの文字数を数え!! もし区分がページに跨がっていた場合には同じ区分で分割して、構文分割後データを1ページごとに区切ったハイライト情報を作成。
●(分割後データから)ハイライト情報作成:
文字数データ=空配列。
整形済み執筆用原稿を反復:
pm=0。
対象を反復:
pm=pm+(対象の文字数)
pmを文字数データに配列追加。
P=0。
文字数データを反復:
pm=対象。m=0。シンタックスハイライト情報[P]=空配列。
pm>mの間:
データ=分割後データの0を配列切り取る。
もし、m+(データ[1]の文字数)≦pmならば:
シンタックスハイライト情報[P]にデータを配列追加。
m=m+(データ[1]の文字数)。
違えば、:
残り文字数=pm-m。
[データ[0],(データ[1]の残り文字数だけ文字左部分)]をシンタックスハイライト情報[P]に配列追加。//配列の引数の順番問題!
分割後データの0に[データ[0],データ[1]の1から残り文字数だけ文字削除]を配列挿入。
抜ける。
もし、(分割後データの要素数)=0ならば、抜ける。
P=P+1。
シンタックスハイライト情報を戻す。
そして、後から考えるとシンタックスハイライト情報の中にはすべて文章が入っているんですから、コレだけ参照すれば良いように作ればよかったですが、行を跨いだ時にも分割して 禁則処理と行揃えに沿って改行の情報を入れていくのが面倒&「執筆」を当初の仕様から変更するの面倒&時間が無い! とゆうわけで、文字は当初の通り整形済み原稿から入れていき、シンタックスハイライト情報を参照して文字色を入れていくという形を取っています。
●(ページ数を)執筆とは:
原稿=整形済み執筆用原稿[ページ数]。
種別=シンタックスハイライト情報[ページ数]を配列複製。
原稿用紙行数回:
行=回数-1。
もし、回数>(原稿の要素数)ならば、此行=空。
違えば、此行=原稿[行]。
此行は此行を文字列分解。
原稿用紙行文字数+1回:
列=回数-1。
もし、列≧(此行の要素数)ならば:
原稿用紙テキスト[行][列]に空をテキスト設定。
違えば:
原稿用紙テキスト[行][列]の「塗り色」にカラーリング設定[種別[0][0]]をSVG属性設定。
原稿用紙テキスト[行][列]に此行[列]をテキスト設定。
種別[0][1]=種別[0][1]の1から1だけ文字削除。
もし、(種別[0][1]の文字数)=0ならば、:
種別の0を配列削除。
できました!
なんかいろいろ雑でスミマセンが、想定通りにカラーリング出来たので満足!!
すべてのページのダウンロード
昨年、『「すべてのページ」を押すと、zip化などされることなくページ数分バラでダウンロードされますので、押す時は十分気をつけましょう』なんて書いたものですが、それ以前のバグがありました😱
なんと、zip化などされることなく、ページ数分の1ページ目がバラでダウンロードされてくるという。さいてー😓(※すでに修正済みです)
でもま、誰も全てのページをダウンロードなんて、してないことでしょう。ワタクシだってしてません(ダメじゃん💧)
今年は、zip化しますよ☆
といっても、これまた他人のふんどしというヤツ。
てぃふと@うぇいくさん作の『電子書籍(epub3)の作成』
epub3電子書籍は、ざっくり言うとXML文書と画像・音声・フォントの類いをEPUB3の仕様に沿ってzipで圧縮したものってことらしく、こちらのコードを頑張って眺めると、なでしこさんでjszipを使う術がバッチリ分かるのでした!
動的インポートの術も素晴らしいですね✨ てんさいだね‼️
というわけで、こちらはより緩いCC0で公開して下さっておりますので、お借りしましょう。
必要っぽい関数群をありがたーくコピペさせていただきまして、コンテンツBLOB生成は、mimeTypeをapplication/zip
に変え、圧縮されるようcompression: "DEFLATE"
を追加しました。
で、こんな感じ?
原稿用紙全ダウンロードボタンをクリックした時には、:
JSZIP作成して、zipに代入。
ダウンロードファイル名作成して、ファイル名に代入。
頁数は、1。
原稿用紙総頁数回:
頁数-1を執筆。
原稿用紙現在頁表示ラベルに「{頁数}ページ/全{原稿用紙総頁数}ページ 」をテキスト設定。
zipの「{ファイル名}_{頁数}.svg」に原稿用紙SVG["outerHTML"]を空でJSZIPファイル作成。
頁数を1増やす。
BLOB=zipからコンテンツBLOB生成。
URL=「URL.createObjectURL」を[BLOB]でJS関数実行。
A=「a」のDOM部品作成。# 仮のaタグ。
Aの「download」にファイル名をDOM属性設定。
Aの「href」にURLをDOM属性設定。
Aの「click」を空でJSメソッド実行。
DOM親要素からAをDOM子要素削除。# 消して終了。
「window.URL.revokeObjectURL」を[URL]でJS関数実行。# 解放して終了。
#------------------
ZIPとは変数=NULL。
URL_JSZIPとは定数=「https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js」
ライブラリ準備。
●ライブラリ準備とは
URL_JSZIPを動的インポートをAWAIT
ZIPはWINDOW["JSZip"]
ここまで。
●JSZIP作成とは
『(function(JSZip){return new JSZip();})』を[ZIP]でJS関数実行で戻る
ここまで
●(ZIPのNAMEにDATAをOPTSで)JSZIPファイル作成とは
ZIPの"file"を[NAME, DATA, OPTS]でJSメソッド実行
ここまで
●(zipから)コンテンツBLOB生成とは
OPTSとは定数={
type:"blob",
mimeType: "application/zip",
compression: "DEFLATE",
}
contentとは変数=zipからOPTSでJSZIP非同期生成をAWAIT
contentで戻る
ここまで
●(ZIPからOPTSで)JSZIP非同期生成とは
ZIPの"generateAsync"を[OPTS]でJSメソッド実行で戻る
ここまで
●(Sを)動的インポートとは
『(function(s) {return import(s);})』を[S]でJS関数実行で戻る
ここまで
無事、全ページがzipに梱包されてダウンロードされました!
つかいかた
プログラム冒頭で取り込んでください。
!『拡張プラグイン:genko.nako3』を取り込む。
そして、プログラムを入力します。
連文や敬語文法など駆使して、なるべく日本語らしいコードにすると楽しいです。
実行すると普通にプログラムが走りますが、「原稿用紙作成」「設定」のボタンが作成されています。
「設定」 画面はムダに頑張って作っているので、ぜひお気に入りの原稿用紙を作ってみて下さい。
「原稿用紙作成」 ボタンを押すと、カラフルなシンタックスハイライト付きでエディタのコードが原稿用紙に描画されます✨
カラフルですよぉ~🎵🎶
やったね‼️
おわります
トリの記事でしたが、昨年の二番煎じ&他人におんぶに抱っことゆうアレでスミマセンでした🙏
でも、いい感じの原稿用紙が出来上がり、なんとか25日中に投稿出来そうなので良かったです✨
今年はなでしこさん、特になんのアニバーサリーでもなさそうだから、ワタクシぼちぼちやっていく~とか言っていましたが、皆さんのご尽力により今年もカレンダーを全て埋めることが出来ました!
参加して下さった方も、読んで下さった方も、本当にありがとうございました!!