PostScript でShift-JISの1バイト文字、2バイト文字判定

先の「文字列が枠内におさまるように自動改行しながら印刷する(PostScript)」はEUC-JPバージョンでした。これを Shift-JIS で行うには1文字と残りの文字列に分けるための判定方法を変更する必要があります。
Shift-JISの半角文字は0x80〜0x9F、0xE0〜0xFF を使わないようになっています。この範囲の値のバイトに合えば全角文字ですから2バイト文字と判断して次のバイトと組み合わせて1文字とする必要があります。

%!PS-Adobe-3.0

% 漢字 (2byte) :上位1バイト  0x81〜0x9f、 0xe0〜0xef 
/isKANJI     { dup is0x81_0x9f exch is0xe0_0xef or } def  % スタックの値は0x81〜0x9f または 0xe0〜0xef
/is0x81_0x9f { dup 16#81 ge exch 16#9f le and }      def  % スタックの値は0x81〜0x9f
/is0xe0_0xef { dup 16#e0 ge exch 16#ef le and }      def  % スタックの値は0xe0〜0xef

%% ------------------------------------------------------
(=== Test is0x81_0x9f ===\n) print
16#80 is0x81_0x9f ==  % false
16#81 is0x81_0x9f ==  % true
16#82 is0x81_0x9f ==  % true
16#9f is0x81_0x9f ==  % true
16#a0 is0x81_0x9f ==  % false

(=== Test is0xe0_0xef ===\n) print
16#df is0xe0_0xef ==  % false
16#e0 is0xe0_0xef ==  % true
16#e1 is0xe0_0xef ==  % true
16#ef is0xe0_0xef ==  % true
16#f0 is0xe0_0xef ==  % false

(=== Test isKANJI ===\n) print
16#80 isKANJI ==  % false
16#81 isKANJI ==  % true
16#82 isKANJI ==  % true
16#9f isKANJI ==  % true
16#a0 isKANJI ==  % false
16#df isKANJI ==  % false
16#e0 isKANJI ==  % true
16#e1 isKANJI ==  % true
16#ef isKANJI ==  % true
16#f0 isKANJI ==  % false
  • 文字列が枠内におさまるように自動改行しながら印刷する(Shift-JIS バージョン)
%!PS-Adobe-3.0
2.834645669 2.834645669 scale
newpath 

/MS-Mincho-90ms-RKSJ-H findfont 10 scalefont setfont

/vPosition   297 20 sub def  % A4 297mm
/Space 3 def
/CellW 100 def
/ADJW CellW Space 2 mul sub def
0.3 setlinewidth
/Left 20 def
/newLineHight 12 def

/hPosition Left Space add def

hPosition vPosition moveto
CellW       0 rlineto
0        newLineHight 8 mul neg rlineto
CellW neg   0 rlineto
closepath stroke

/newline { /vPosition vPosition newLineHight sub def
            hPosition Space add vPosition moveto } def 

% 先頭から指定長の文字列を取得して返す。
/Head { 0 exch getinterval} def

% 指定位置より後の文字列を返す。
% 参考:ローカル変数に値を渡す方法
% http://www.cs.kyoto-wu.ac.jp/~konami/documents/ps/psmemo.html#subsec6:5
/ldict 1 dict def
/Tail {
    ldict begin
    /start exch def
    /str   exch def
    /len str length start sub def
    str start len getinterval
    end
} def

% 漢字 (2byte) :上位1バイト  0x81〜0x9f、 0xe0〜0xef 
/isKANJI     { dup is0x81_0x9f exch is0xe0_0xef and } def
/is0x81_0x9f { dup 16#81 ge exch 16#9f le and }       def
/is0xe0_0xef { dup 16#e0 ge exch 16#ef le and }       def

% スタックにある文字列の最初の文字が1バイト文字、2バイト文字であるかを判定し、
% 残りの文字をスタックに積み、1文字をスタックに積
/sdict 1 dict def
/Split {
    sdict begin
    /str   exch def
    str 0 get isKANJI { str 2 Tail  str 2 Head}         % 2byte 文字
                      { str 1 Tail  str 1 Head} ifelse  % 1byte 文字
    end
} def

% 残りの文字列が 0 より長いときは foldString を再帰する
/ifFoldString { dup length 0 gt { foldString } if } def

%  指定位置で折り返しながら文字列を表示。
/foldString {
     Split
     dup stringwidth pop     % 1文字を複製し、幅を求める
     currentpoint pop        % 現在の座標取得。Y座標は捨てる
     add                     % 現在位置 + 文字列の幅
     ADJW hPosition add      % 終点
     le { show  ifFoldString }  % 終点より座標が少ないときは1文字表示。残り文字があれば再帰。
                                % 改行して1文字表示。残り文字があれば再帰。
        { newline show ifFoldString }ifelse                 %
  } def

hPosition Space add vPosition moveto newline
(本日は晴天なり本日は晴天なり。我輩は猫である。名前はまだない。Hello,world! 枠内に入るよう横幅を調整しています。) foldString
showpage