LoginSignup
2
0

htmlに生のbinary dataを埋め込むっぽい方法

Posted at

生のbinaryと言うからにはBase64に変換するような生ぬるい方法ではない。ほぼそのまま埋め込むのだ。ほぼとは何ぞ? と思う読者もいるだろう。1つだけ除外しなければならない文字が存在するのだ。それがUnicode番号13、復帰文字という奴だ。
厄介な事にhtmlに記述される文字の中でも復帰文字はUnicode番号10として認識されてしまう。環境にもよるかもしれないが、少なくともWindows Google Chrome、JavaScriptでは innerText、innerHTML、textContent で得られる値は10である。そのため別の文字に変換した上でescape文字と組にしなければならない。当然だがescape文字自身もescape文字と組にしなければならない。
単純に \ でescapeすれば良いという話ではない。そもそもJavaScriptのように \r と書いても復帰文字が自動召喚されるわけではない(まあJavaScript側でそういう風に処理すればいいのだが…)。
escape文字に適任なのはbinary data中で最も出現数が少ない文字である。その方が膨張率が小さくて済む。
さて、下らないかもしれない前置きはこの辺にして本題とやら話をにすり替えていこう。

制約
こんな馬鹿げた事をする為にはmeta要素で charset=l1 (latin1、iso-8859-1等と同義)を設定しなければならない。UTF8などという符号化法式を選択する余地はないのだ!

html中のどこに仕込むか?

さすがにどこでもいいというわけにはいかない。a要素やp要素では意味不明な文字列がだらだらと丸出しになる。おまけに連続する空白類も1文字分扱いだ。要素内の<!-- -->の区間の文字は全て無視される始末。これらを考慮すると候補は限られる。

  • style
    偶然cssとして解釈されるbinary dataに直面するかもしれない。その対策として type="text/plain" を設定すれば良い
  • script
    type="text/plain" を設定する
  • noscript
    特に欠点が無いかも
  • textarea
    開始tag直後と閉じtag直前の改行が無視される問題がある。加えて数値文字参照と文字実体参照の対策は非常に面倒
  • xmp
    廃止された要素。それを無視すれば使い勝手が良い
  • plaintext
    廃止された要素。最強っぽいけどhtml最後の要素にする必要がある

plaintext以外はbinary data中に閉じtagと認識できる羅列が見つかったら破綻する。その対策として閉じtagとみなせる羅列の途中にescape文字を仕込む。

html編集にはBinary Editor
文字番号0のLatin1文字は普通の文書編集programでは消えていたり別文字に変換されている可能性がある。安全を期するためにもBinary Editorで編集する事を勧める

復元処理

latin1の文書の中でも文字番号128~159(130,143,144,146,158以外)の扱いは特殊である。この連中は charCodeAt で得られる値が変態じみている。詳細は以下の表の通り。

latin1 取得値 latin1 取得値 latin1 取得値
128 376 138 8226 150 8240
129 382 139 8221 151 710
131 339 140 8220 152 8225
132 8250 141 8217 153 8224
133 353 142 8216 154 8230
134 8482 145 381 155 8222
135 732 147 338 156 402
136 8212 148 8249 157 8218
137 8211 149 352 159 8364

これらのひねくれ者的な文字に対しては、文字番号通りの値を得るための特殊な処理が必要だ。例えば以下のようになる。

A=Uint8Array.form(L1txt,(c,i)=>(i=c.charCodeAt()%65533)>>8?129+' \x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C \x8E  \x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C \x9E\x9F'.indexOf(c):i)

\x82\x83...\x9E\x9F の部分は実際には生のLatin1文字でいいのだが、便宜上このように記述。%65533とは何者か? charCodeAtは文字番号0のLatin1文字を65533だ! などと豪語しておられるのだ。その誤解を解くためのまじないである。
連想配列で紐付ける古典的な手法もある。以下の例ではnoscript要素の中身をbinaryに変換して取得している。

<noscript id=s>abc	ABC
!"#$%\~:;</noscript>
<script>
function L1toBin(L1txt){
	var a=256,z=L1txt.length,h={__proto__:null},A=new Uint8Array(z);
	for(;a;)h["\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f".charAt(h[String.fromCharCode(--a)]=a)]=a^128;
	for(;a<z;)A[a]=h[L1txt.charAt(a++)];
	return A
}
A=L1toBin(s.innerText)
</script>

これも\x80\x81...\x9e\x9fの部分は生のLatin1文字列であり、128 bytesとなる。え? charAtは古臭いって? そう、今時は[]で位置を指定できるのだが、古風にこだわってみた。
7行目も今時は以下のように書き換えられる。

for(z of L1txt)A[a++]=h[z];

裏技

実は閉じtag、<!-- -->、復帰文字、escape文字等といったものを考慮せず埋め込む方法がある。htmlをpng形式の画像として再読み込みすればいいのだ。少し特殊でやや複雑な方法であり、制約もある。
htmlの先頭にpngの頭情報を埋め込み、続いて画像本体、htmlのcanvas要素とimg要素を埋め込む。png画像本体は埋め込みたい任意のbinaryから構築する。
img要素のonload属性でアレコレ処理すると、任意のbinary情報を取得できるって寸法だ。実用例としてzpngがある

2
0
0

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
2
0