home / uni / gs-pdf-mac-preview GSで作ったPDFをMacのプレビューで表示する

 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.3 2023/11/07 17:05:20 you Exp $