「PDFCreatorの文字化け問題-修正箇所発見」のやすさんのコメント欄からの記事になります。
PDFCreatorは家使いなのでそこまで使いきっている感じでもなく、これまで私は素通りしていましたが。PDFCreatorは印刷時のPDF情報を設定する際、日本語が混じっているとその部分はスルーする、という現象があるのですね。日本語を入れるとはじかれる、無視される…。
私の場合、家にはVB6.0環境がないので、とりあえず、やすさんのコメント欄とソースコードを頼りにロジックのみ考えてみました。
ちなみに問題のモジュールを含む本家オリジナルのソースはこちら。
(1)ReplaceEncodingCharsについて
やすさんの修正案のコードの全貌。
Public Function ReplaceEncodingChars(Str1 As String) As String '---ErrPtnr-OnError-START--- DO NOT MODIFY ! --- On Error GoTo ErrPtnr_OnError '---ErrPtnr-OnError-END--- DO NOT MODIFY ! --- 50010 Dim i As Integer, tStr As String 50011 Dim bStr() As Byte 50020 tStr = "" 50030 ' First we look for oct encoding chars 50040 For i = 127 To 255 50050 Str1 = Replace$(Str1, "\" & Oct$(i), Chr$(i)) 50060 Next i 50070 ReplaceEncodingChars = Str1 50080 ' Second we look for hex encoding chars 50090 If Len(Str1) >= 4 Then 50100 If Mid$(Str1, 1, 1) = "<" And Mid$(Str1, Len(Str1), 1) = ">" Then 50110 If Len(Str1) Mod 2 = 0 Then 50111 ReDim bStr((Len(Str1) - 2) / 2) 50120 For i = 2 To Len(Str1) - 1 Step 2 50130 If IsNumeric("&H" & Mid$(Str1, i, 2)) = True Then 50140 If CByte("&H" & Mid$(Str1, i, 2)) > 255 Then 50150 Exit Function 50160 Else '50170 tStr = tStr & Chr$(CByte("&H" & Mid$(Str1, i, 2))) 50170 bStr((i - 2) / 2) = CByte("&H" & Mid$(Str1, i, 2)) 50180 End If 50190 Else 50200 Exit Function 50210 End If 50220 Next i 50221 tStr = StrConv(bStr, vbUnicode) 50230 End If 50240 End If 50250 End If 50260 If Len(tStr) > 0 Then 50270 ReplaceEncodingChars = tStr 50280 End If '---ErrPtnr-OnError-START--- DO NOT MODIFY ! --- Exit Function ErrPtnr_OnError: '---ErrPtnr-OnError-END--- DO NOT MODIFY ! --- End Function
ReplaceEncodingCharsの役割としては、
- 引数には、バイナリの文字列が渡される。
たとえば「<82E282B782B382F182B182F182CE82F182CD8142>」こんな文字列を引き渡される。 - 返り値としては、日本語の文字列を返せばOK。
という関数なわけですね?(コードから推測…)
だとすると、やすさんの修正案通り、バイトの配列を一端作る流れが妥当だと私も思います。
いまのところ、敢えて修正案を出すなら
50130 If IsNumeric("&H" & Mid$(Str1, i, 2)) = True Then 50140 If CByte("&H" & Mid$(Str1, i, 2)) > 255 Then 50150 Exit Function 50160 Else
あたりの分岐はコメントアウトしちゃっていいんじゃないの?と思ったくらいでしょうか。「なんかもー、どうせバイト変換できないときに抜けちゃうなら、エラー処理でいいんじゃないの?」と思うので…。
それにしてもオリジナルのコードは明らかにおかしいですよね。2桁ずつゲットして文字変換されても、マルチバイトな言語では明らかに困るだろう。(;´Д`)
(2)EncodeCharsについて
やすさんの修正案のコードの全貌。
Public Function EncodeChars(ByVal CodePage As eCodePage, ByVal Str1 As String) As String ' UTF-16, UTF-8 conversion '---ErrPtnr-OnError-START--- DO NOT MODIFY ! --- On Error GoTo ErrPtnr_OnError '---ErrPtnr-OnError-END--- DO NOT MODIFY ! --- 50010 Dim i As Long, tStr As String, Size As Long, buffer() As Byte, c As Long, tL As Long 50020 50030 If LenB(Str1) = 0 Then 50040 Exit Function 50050 End If 50061 Select Case CodePage Case eCodePage.CP_NoEncoding 50080 EncodeChars = "(" & Str1 & ")" 50090 Case eCodePage.CP_UTF8 50100 For i = 1 To Len(Str1) 50110 c = AscW(Mid$(Str1, i, 1)) 50121 Select Case c Case 0 To &H7F& 50140 tStr = tStr & String(2 - Len(Hex(c)), "0") & Hex(c) 50150 Case &H80& To &H7FF& 50160 tL = &HC0& Or ((c And &H3FC0&) \ &H40&) 50170 tStr = tStr & String(2 - Len(Hex(tL)), "0") & Hex(tL) 50180 tL = &H80& Or (c And &H3F&) 50190 tStr = tStr & String(2 - Len(Hex(tL)), "0") & Hex(tL) 50200 Case &H800& To &HFFFF& 50210 tL = &HE0& Or ((c And &HF000&) \ &H1000&) 50220 tStr = tStr & String(2 - Len(Hex(tL)), "0") & Hex(tL) 50230 tL = &H80& Or ((c And &HFC0&) \ &H40&) 50240 tStr = tStr & String(2 - Len(Hex(tL)), "0") & Hex(tL) 50250 tL = &H80& Or (c And &H3F&) 50260 tStr = tStr & String(2 - Len(Hex(tL)), "0") & Hex(tL) 50270 End Select 50280 Next i 50290 EncodeChars = "<BFBBEF" & tStr & ">" 50540 Case eCodePage.CP_UTF16 50550 For i = 1 To Len(Str1) 50560 c = AscW(Mid$(Str1, i, 1)) '50570 tStr = tStr & String(4 - Len(Hex(c)), "0") & Hex(c) 50570 tStr = tStr & Right("0000" & Hex(c), 4) 50580 Next i 50590 EncodeChars = "<FEFF" & tStr & ">" 50600 End Select '---ErrPtnr-OnError-START--- DO NOT MODIFY ! --- Exit Function ErrPtnr_OnError: '---ErrPtnr-OnError-END--- DO NOT MODIFY ! --- End Function
引数でエンコーディング、及び文字列を渡して、引数で決めたエンコーディングの16進を返すファンクションですね(eCodePageはEnumで、CP_NoEncoding/CP_UTF8/CP_UTF16の三択です)。
AscWが負の値のとき(たとえば「髙」とか)、オリジナルのコードだとHex(c)に「FFFF9AD9」などアタマ4桁に余計なコードがついて16進数を返せなくなくなりますね。36行目を
50560 c = CLng("&H" & Hex(AscW(Mid$(Str1, i, 1))))
このように修正すれば、どうにか…、という感じですけど。
やすさんのRight関数を使っての4桁取得が妥当ですよね。エレガントだし、これはこれで完成っぽいですね。
やすさんへ
いろいろネタ的には初めて尽くしで、すごく勉強になりました!ありがとうございます。
(1)の方は、根本的にこれ以上のロジックが私のアタマで浮かぶか怪しいですが、しばらく考えてみます。
もともとどこかでbyteの配列をゲットしているチャンスがあるんだから、そのままどーにかならなかったの?とか思わなくもありませんが。そこまで手を広げていくと、収拾つかなくなっていきそうですよね…。
いやー、それにしてもこうしてみてみると、つくづくと面白いですね!(←だんだんノってきた) +。:.゚ヽ(*´∀`)ノ゚.:。+゚
4 件のコメント:
こんばんわ、じょに さん。
PDFCreatorも昔(0.9.2?)と比べると、Vista対応のためか、かなり変更されているようですが、ところどころ?と思う処理が残ってますね。
修正しだすと、止め処なく修正が必要になりそうなので、そこはグッとガマンしています。
これからも じょに さんの記事を楽しく拝見させて頂きます。
Vista対応ですか。PDFCreatorは、このままVB6.0で目指すということなんでしょうかねー。
ところで日本語対応ですが、やすさんの修正案を反映させて動かしてみたところ、
タイトル等はキレイに日本語表示でフォームに入るようになりましたが、
いざ[保存]ボタンを押すと、保存できませんよね?
[保存]を押した後、イマイチどこで抜けちゃうのか追いきれず & PDFについての知識もかなり浅いので、その辺の知識も漁る必要がありそうな予感が…。
.NetではActiveReportを使ったり、iText.NETをいじったりしたことはあるものの、そのテのもので「お任せ」状態で実は「そもそも」がわかっていなかったりするので。
道のりは遠い…。(^^;)
じょに さん、こんにちは。
日本語で保存できないとのこと、こちらでは問題なく保存できているのですが?
もしかしたら modPDF.ReplaceEncodingChars で8進数指定の考慮(修正)漏れが見つかったので、文字に依存しているのかもしれません。こちらの修正は20行程度追加してしまいました。
どのような文字を指定した場合に保存できないのか教えて頂いたら調べてみます。
一時的ですが、修正版を置きました。
https://www.webfile.jp/dl.php?i=533572&s=64da0d2645003a937846
PDFCreator_0516.zip 834,618byte
(MD5 : 91ae1b27a964be1cfdbd111b286e445f)
Version 0.9.8_090516.zip 4,211,740byte
(MD5 : db8944afaa2e672b31d9f66a8a473060)
やすさん、こんばんわ。
前回の修正のみでは、タイトルに日本語は表示されるものの、「保存」をクリックすると、もう一度、最初の画面が再表示され、保存できなかったのです。日本語を少しでも混ぜて[保存]をクリック→再表示するだけ、の永久ループだったわけですね。アルファベット英数のみの文字列にするまでこのループからは抜け出せませんでした。
でも今回やすさんがアップロードしてくれたexeだと確かにうまく動きますね(DLしました。ありがとうございます)。すごーい!!
修正コードも例によってエディタで確認しました。
8進数の変換部分をを全面的に書き変えたわけですね。現状、引数のStr1に入るバリエーションが把握しきれていなくて、イマヒトツ、ピンときていなかった私はこの部分から目をそらしていたわけですが…。(^^;
保存できない永久ループの原因はここにあったのですね。
元ネタと修正案の差は、入り口と出口の関係がよくわかっていないのでピンときていないところがあるのですが(修正案はUnicodeで変換しているところがポイント???)、平日、またちょっとずつ見比べて見たいと思います。(・・)ゞ
コメントを投稿