5
2

なでしこさんのコードをカラフルなシンタックスハイライト付きで原稿用紙に描画したい?!

Posted at

 昨年のアドベントカレンダー、最後の記事がコレでした。

 まるっと一年越しのばーじょんあっぷということになるでしょうか。

テキストを一文字ずつ

 昨年作成した原稿用紙では、テキスト要素を一行ずつ配置していました。
 キャンバスへの描画と違い、SVG画像ではCSSで文字間隔などの指定が行えるので、それさえしっかりと行えば一文字ずつ位置を指定しなくても、きっちりとマス目に文字が収まって描画できる♪ というのがそもそもの発端だったからです。
 が! それではシンタックスハイライトは出来ません。
 色設定はテキスト要素に設定するので、文字ごとに色を変えるためにはテキストを文字ごとにバラして描画しなくては。

 あらかじめ空のテキストを設置した原稿用紙に文章を流し込んでいますので、自在に色を変えるには、完全に一文字ずつにバラす必要があります。
 アタマの中だけで考えると、なんかもーうわ~めんどくさ~:confounded: てなるよね~💧
 いや、賢い人ならぜんぜんそんなことないのカモですが~。

 そして、実際やってみたら、そう面倒なことはありませんでした。
 だってベツに、自分で植字するわけじゃないんですから!
 全部なでしこさんがやってくれるんですから!!
 原稿用紙の作成と実際の執筆で、それぞれループが一つ深くなるくらいなもんです。
 ていうか、フツーに二次元配列のと同じアレです。

    # テキスト
    書体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
        対象反復:
            
pmpm+(対象文字数)
        pm文字数データ配列追加
    P0
    文字数データ反復:
        
pm対象m0シンタックスハイライト情報[P]=空配列
        pmm:
            
データ分割後データ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執筆
        原稿用紙現在頁表示ラベル「{頁数}ページ/全{原稿用紙総頁数}ページ  」テキスト設定
        zip「{ファイル名}_{頁数}.svg」原稿用紙SVG["outerHTML"]JSZIPファイル作成
        頁数1増やす
    BLOBzipからコンテンツBLOB生成
    URL「URL.createObjectURL」[BLOB]JS関数実行
    A「a」DOM部品作成# 仮のaタグ。
    A「download」ファイル名DOM属性設定
    A「href」URLDOM属性設定
    A「click」JSメソッド実行
    DOM親要素からADOM子要素削除# 消して終了。
    「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
 ZIPWINDOW["JSZip"]
ここまで

●JSZIP作成とは
 『(function(JSZip){return new JSZip();})』[ZIP]JS関数実行戻る
ここまで

●(ZIPのNAMEにDATAをOPTSで)JSZIPファイル作成とは
 ZIP"file"[NAMEDATAOPTS]JSメソッド実行
ここまで

●(zipから)コンテンツBLOB生成とは
 OPTSとは定数{
  type:"blob",
  mimeType"application/zip",
  compression"DEFLATE",
 }
 contentとは変数zipからOPTSJSZIP非同期生成AWAIT
 content戻る
ここまで

●(ZIPからOPTSで)JSZIP非同期生成とは
 ZIP"generateAsync"[OPTS]JSメソッド実行戻る
ここまで

●(Sを)動的インポートとは
 『(function(s) {return import(s);})』[S]JS関数実行戻る
ここまで

 無事、全ページがzipに梱包されてダウンロードされました!
 

つかいかた

 プログラム冒頭で取り込んでください。

!『拡張プラグイン:genko.nako3』を取り込む。

 そして、プログラムを入力します。
 連文や敬語文法など駆使して、なるべく日本語らしいコードにすると楽しいです。

 実行すると普通にプログラムが走りますが、「原稿用紙作成」「設定」のボタンが作成されています。

 「設定」 画面はムダに頑張って作っているので、ぜひお気に入りの原稿用紙を作ってみて下さい。
Screenshot 2023-12-25 at 12-12-00 🍯 なでしこ3貯蔵庫.png

 「原稿用紙作成」 ボタンを押すと、カラフルなシンタックスハイライト付きでエディタのコードが原稿用紙に描画されます✨

Screenshot 2023-12-24 at 16-14-56 🍯 なでしこ3貯蔵庫.png

 カラフルですよぉ~🎵🎶
 やったね‼️

おわります

 トリの記事でしたが、昨年の二番煎じ&他人におんぶに抱っことゆうアレでスミマセンでした🙏
 でも、いい感じの原稿用紙が出来上がり、なんとか25日中に投稿出来そうなので良かったです✨

 今年はなでしこさん、特になんのアニバーサリーでもなさそうだから、ワタクシぼちぼちやっていく~とか言っていましたが、皆さんのご尽力により今年もカレンダーを全て埋めることが出来ました! 
 参加して下さった方も、読んで下さった方も、本当にありがとうございました!!

5
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2