UTF-8な日本語を埋め込んだPostScriptファイルを作り、GhostscriptでPDFにすると、Acrobat Readerでは読めるのに、Macのプレビューでは読めないPDFができあがる。 フォントの埋め込みとか色々やってみてもどうにもならない。 EUCでは表示できるので、もしやと思ってUTF-16にしてみたら表示できた。
Acrobat Readerで表示できて、プレビューで表示できない現象は、だいぶ前から気づいていた。 最初に気づいたのはPostScriptで書いた電子デート印を久しぶりに使おうと思った時で、なぜ電子デート印が出てきたかというと武漢でアウトブレークした例のやつが、世界中に広まったから。 つまり、2020年には気がついていたので、未だ直っていないということはAppleも気づいてないか、直す気がないかのどちらかなのだろう・・・。 PSファイル自体をUTF-16にしてしまうと、PostScriptコードもUTF-16になってしまうし・・・。
ということで、UTF-8→UTF-16変換してshowに突っ込むことにする。 UTF-8→UTF-16変換は機械的にできる。 まず、UTF8→コードポイントに変換。
結果は0x110000未満である必要がある。 これをUTF-16として出力する。
/convertUTF8toUTF16 { 1 dict begin /s exch def /cnt 0 def /u 0 def /a [ s { /c exch def c 16#80 lt { % 0... .... 0 c /cnt 0 def } { c 16#c0 lt { % 10.. .... cnt 0 gt { /u u 64 mul c 16#3f and add def /cnt cnt 1 sub def cnt 0 eq { u 16#10000 lt { u -8 bitshift 16#ff and u 16#ff and } { u 16#110000 lt { u 16#10000 sub dup -18 bitshift 16#03 and 16#d8 or exch dup -10 bitshift 16#ff and exch dup -8 bitshift 16#03 and 16#dc or exch 16#ff and } if } ifelse } if } if } { c 16#e0 lt { % 110. ...., 2 bytes /cnt 1 def /u c 16#1f and def } { c 16#f0 lt { % 1110 ...., 3 bytes /cnt 2 def /u c 16#0f and def } { c 16#f8 lt { % 1111 0..., 4 bytes /cnt 3 def /u c 16#07 and def } { /cnt 0 def } ifelse } ifelse } ifelse } ifelse } ifelse } forall ] def /out a length string def 0 1 out length 1 sub { /i exch def out i a i get put } for out end } bind def
コードの構造としては、入力文字列を
forall
して、UTF-16に変換したデータをスタック上に置き去りにしている。
これを
forall
ごと
[ ]
で囲むことで一度配列にする。
この段階で出力文字列の長さが分かるので、出力文字列を作って、配列のデータを
put
で押し込んでいる。
配列のインデックスが必要なので
forall
では処理できず、
for
を使っている。
終値から1引かないといけないのが面倒なんだよなー。
変換の本体は
cnt
が状態変数になっており、第1バイトでUTF-8の1文字のバイト数(から1を引いたもの)を設定している。
ループが進むごとに1ずつ引かれていき、0になったときにコードポイントをUTF-16に変換して出力している。
コードポイントに変換できなかったバイトシーケンスは単に無視される。
また、UTF-8はなるべく短いバイト数でエンコードされているべきで、例えば0xf0 0x00 0x00 0x3a
は0x3aにデコードしようと思えばできるが、これは不正なバイトシーケンスである。
昔、これでブラウザのセキュリティチェックをすり抜ける脆弱性があった。
上記のコードはそのあたりのチェックはしていない。
UNICODEは漢字の字体とか半角チルダ・全角チルダ問題とか、色々と問題が多くてあまり好きではないが、UTF-8のエンコードスキームだけは非常によくできていると思う。 1バイト見ただけでそれが第一バイトかそうではないかが確実に分かり、1文字のバイト数も確定するのがコードを書く上で非常に重要である。 漢字などは3バイトになってしまう欠点があるが、それを補って余りある利点だと思っている。 SJISはこの辺の取り扱いが非常に面倒。 EUCはその中間。
使い方はこんな感じ。
文字列をconvertUTF8toUTF16
に渡してから
show
に渡せばよい。
/IPAex-Mincho-UniJIS-UTF8-H findfont 48 scalefont setfont 10 20 moveto (ABCxyzあいうワヲン漢字) show /IPAex-Mincho-UniJIS-UTF16-H findfont 48 scalefont setfont 10 78 moveto (ABCxyzあいうワヲン漢字) convertUTF8toUTF16 show
こんななんの変哲もないPostScriptファイルをPDFにして、プレビューとAcrobat Readerで表示してみるとこうなる。
上がAcrobat Reader、下がプレビューである。 2行のテキストは上がUTF-16、下がUTF-8である。 プレビューのUTF-8は見事に豆腐になっている。
Copyright (C) 2023 akamoz.jp
$Id: mac-pdf-utf8.htm,v 1.4 2024/02/19 04:52:46 you Exp $