SetTextJustificationのブレーク文字

解決


luna  2007-03-22 23:00:39  No: 98347

VB6の質問です。
文字を均等割付してフォームに描画するため、
下記のコード(サンプル)を記述しています。
ご覧の通り、API関数のSetTextJustificationおよびTextOutを
使用しています。
このコードは、Win XPまでのOSでは正常に機能していましたが、
この度Win VISTAで動作確認をしたところ、一部のフォントにおいて
正常に機能しない(均等割付されない)事が判明しました。
原因を自分なりに調査したところ、どうやら通常Chr(32)であるはずの
ブレーク文字がVISTAのMS UI Gothicでは、Chr(2)になっているようです。
もちろん、ブレーク文字は、使用フォントによって異なりますので、
XPでもChr(32)以外のブレーク文字であるフォントの場合は、
同じ現象が発生するのだろうと推測されるのですが、
これまで使ってきた一般的なフォントでは、ほぼすべて
Chr(32)がブレーク文字であったため、気がつかなかっただけかもしれません。
また、Win VISTAにおいても、ブレーク文字がChr(32)である
フォント(HGSゴシックMなど)であれば、正常に均等割付が行われますが、
使いたいフォントは、MS UI Gothicが主であり、
どうしてもこのフォントに対応せざるを得ません。
そこで質問なのですが、ブレーク文字がChr(32)以外のフォントで、
均等割付を正常に動作させるには、下記コードをどのように
修正すれば良いのでしょうか?
識者の方のお知恵を拝借いたしたく、よろしくお願いいたします。

---(サンプルコードここから)---
'このコードは、Form1に貼り付けるだけで、コントロール類は不要です。

Private Declare Function TextOut Lib "gdi32" Alias "TextOutA" ( _
        ByVal hdc As Long, _
        ByVal X As Long, _
        ByVal Y As Long, _
        ByVal lpString As String, _
        ByVal nCount As Long _
        ) As Long

        
Private Declare Function SetTextJustification Lib "gdi32" ( _
        ByVal hdc As Long, _
        ByVal nBreakExtra As Long, _
        ByVal nBreakCount As Long _
        ) As Long

Private Declare Function GetTextMetrics Lib "gdi32" Alias "GetTextMetricsA" ( _
        ByVal hdc As Long, _
        lpMetrics As TEXTMETRIC _
        ) As Long
   

Private Type TEXTMETRIC
   tmHeight As Long
   tmAscent As Long
   tmDescent As Long
   tmInternalLeading As Long
   tmExternalLeading As Long
   tmAveCharWidth As Long
   tmMaxCharWidth As Long
   tmWeight As Long
   tmOverhang As Long
   tmDigitizedAspectX As Long
   tmDigitizedAspectY As Long
   tmFirstChar As Byte
   tmLastChar As Byte
   tmDefaultChar As Byte
   tmBreakChar As Byte
   tmItalic As Byte
   tmUnderlined As Byte
   tmStruckOut As Byte
   tmPitchAndFamily As Byte
   tmCharSet As Byte
End Type

Private Sub Form_Load()
  
    'AutoRedrawを設定
    Me.AutoRedraw = True
  
  
    'フォームのフォントを設定
    Me.FontName = "MS UI Gothic" '<--- NG (ASCII 2)
'    Me.FontName = "HGSゴシックM" '<--- OK (ASCII 32)
    
    
    '出力文字の決定
    Const strPUTSTRING As String = "テスト"
    
    
    'ブレーク文字の取得
    Dim strBc As String
    
    strBc = BreakChar(Me.hdc)
     
    
    '出力文字の各全角文字の次にブレーク文字を挿入
    Dim strTemp As String
    Dim lngBcCnt As Long
    Dim intI As Integer
    Dim strPutString2 As String
    
    For intI = 1 To Len(strPUTSTRING) - 1
        strTemp = Mid(strPUTSTRING, intI, 1)
        strPutString2 = strPutString2 & strTemp
        
        If LenB(StrConv(strTemp, vbFromUnicode)) = 2 Then
            strPutString2 = strPutString2 & strBc
            lngBcCnt = lngBcCnt + 1
        End If
        
    Next
    
    strPutString2 = strPutString2 & Right(strPUTSTRING, 1)
    
    
    '均等割付のためのスペース幅を計算
    Dim lngPutStringSpaceWidth As Long
    
    lngPutStringSpaceWidth = (Me.ScaleWidth - Me.TextWidth(strPutString2)) / Screen.TwipsPerPixelX
    
    
    '均等割付後に出力
    If SetTextJustification(Me.hdc, lngPutStringSpaceWidth, lngBcCnt) <> 0 Then
        Call TextOut(Me.hdc, 0, 0, strPutString2, LenB(StrConv(strPutString2, vbFromUnicode)))
    End If

End Sub

Private Function BreakChar(lngHdc As Long) As String

    '指定されたhdcのブレーク文字を取得
    Dim tm As TEXTMETRIC
    Dim ret As Long
 
    If GetTextMetrics(lngHdc, tm) <> 0 Then ' Retrieve information.
        BreakChar = Chr(tm.tmBreakChar)
    Else
        BreakChar = Chr(32)
    End If
    
End Function


K.J.K.  2007-03-23 21:03:39  No: 98348

そのbreak文字の確認は、どのように行いましたか?
その確認したときの手法は用いることができないのでしょうか?


Luna  2007-03-24 00:41:10  No: 98349

K.J.K様
Break文字の確認についてですが、先のコードにあります
GetTextMetrics関数にてTEXTMETRIC構造体を取得しています。
以上、引き続きよろしくお願いいたします。


K.J.K.  2007-03-24 01:21:35  No: 98350

つまり、特定のFontでは、Break文字としてChr$(2)が得られるけれども、
それをBreak文字として利用するとうまく表示できない、ということなの
でしょうか。

現在のTextOutなどを使う場合は、とりあえず、
1,文字列型ではなく、Byte配列に事前に変換したものを渡す。
2,TextOutWなどを用いる。
3,VistaもしくはXP以降の新描画API関数がないか調べてみる。
あたりをチェックしてみるとか。


Luna  2007-03-24 06:49:51  No: 98351

K.J.K.様
コメントありがとうございます。
以下、結果報告です。

1.Byte配列の件
Byte型変数に文字列を代入して試しましたが、結果は変わらずでした。
2.TextOutWについて
文字をユニコードに変換して試しましたが、結果は変わらずでした。
3.使用関数について
TextOut関数以外に、TabbedTextOut関数およびExtTextOut関数を試しましたが、結果は変わらずでした。
ちなみに、マイクロソフトのサイトを見ると、
TextOut関数およびSetTextJustification関数については、
VISTAでもサポートされているようです。
参考URL
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/fontext_5yd0.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/fontext_9lm6.asp

引き続き、皆様のお知恵をお待ちしています。


魔界の仮面弁士  2007-03-24 19:38:28  No: 98352

> 2.TextOutWについて
> 文字をユニコードに変換して試しましたが、結果は変わらずでした。

それは、どのようなコードになりましたか?
VB6 の文字列は、もともと Unicode エンコードされていますので、
「ユニコードに変換する」という作業が発生している時点で、
どこか間違っているような気がするのですが…。


Luna  2007-03-25 00:17:05  No: 98353

魔界の仮面弁士 様

コメントありがとうございます。

先のソースコードのAPI宣言のところで
  ...Alias "TextOutA"...

  ...Alias "TextOutW"...
に変更し、それから
  StrConv(strPutString2, vbUnicode)
にてUnicodeに変換(したつもり?)しています。


魔界の仮面弁士  2007-03-25 22:40:21  No: 98354

>  ...Alias "TextOutW"...
> に変更し、それから
TextOutW の第4引数(文字列データを渡す部分)は、どのなっていますか?

「ByRef x As Byte」「ByRef s As Any」「ByVal l As Long」などの
宣言にしているのであれば問題ありませんが、「ByVal s As String」に
なっているのだとしたら、正しく呼び出すことはできませんよ。

>   StrConv(strPutString2, vbUnicode)
それは大問題。
Unicode 文字列に対して StrConv(x ,vbUnicode) してはいけません。

それだと、Unicodeデータを「Shift_JIS→Unicode」の変換処理に
かけてしまうことになるため、データの一部が破損することがあります。

英語の文章を、(英和ではなく)和英翻訳ソフトに渡した場合のことを
想像してみてください。変換結果は出鱈目なものになってしまいますよね。
それを、逆変換として英和翻訳したとしても、元の英文は復元されません。


Luna  2007-03-26 11:14:11  No: 98355

魔界の仮面弁士 様

なるほどです^^;
つまりこうすれば良いということでしょうか?

Private Declare Function TextOut Lib "gdi32" Alias "TextOutW" ( _
        ByVal hdc As Long, _
        ByVal x As Long, _
        ByVal y As Long, _
        ByRef lpString As Byte, _
        ByVal nCount As Long _
        ) As Long
    .
    .
    .
bytPutString2 = strPutString2
Call TextOut(Me.hdc, 0, 0, bytPutString2(0), Len(strPutString2))
    .
    .
    .
しかし、状況は変わりません。


K.J.K.  2007-03-26 21:53:59  No: 98356

GetTextMetricsの呼び出しもUnicode対応にしたのでしょうか?
GetTextMetricsWでBreak文字を取得するとどうなっていますか?


Luna  2007-03-27 07:13:06  No: 98357

K.J.K 様

引き続きありがとうございます。

ご指摘の通り、GetTextMetricsA のままでした。
さっそくGetTextMetricsW に変更してブレーク文字を取得したところ、
文字コードが255と返ってきました。
strBC=ChrW(255)で文字列に代入し、TextOutに文字型を渡す方法とバイト型を渡す方法の2通りを試しましたが、状況はかわらずです。

何か頭が混乱気味ですToT


Luna  2007-03-27 07:39:12  No: 98358

追記です。

GetTextMetricsWの戻り値(TEXTMETRIC)を確認したところ、
tmFirstChar や tmLastChar におかしな値(両方0とか)が入ってくるので、
これが問題なのかもしれません。
GetTextMetricsWの戻り値はTrueなのですが・・・
ひょっとして、TEXTMETRIC構造体が、AとWで違うのかな?
少し調べて試してみます。


Luna  2007-03-27 09:28:26  No: 98359

資料を見つけられなかったので、いろいろと試行錯誤した結果、
TEXTMETRIC構造体を、下記のように修正すれば、
GetTextMetricsWにて正常な値が取れるようでした。
(Unicode文字=2バイトの文字を取得できるように、
文字がらみのByte型を2バイト分配列で確保しています。)

Private Type TEXTMETRIC
   tmHeight As Long
   tmAscent As Long
   tmDescent As Long
   tmInternalLeading As Long
   tmExternalLeading As Long
   tmAveCharWidth As Long
   tmMaxCharWidth As Long
   tmWeight As Long
   tmOverhang As Long
   tmDigitizedAspectX As Long
   tmDigitizedAspectY As Long
   tmFirstChar(1) As Byte
   tmLastChar(1) As Byte
   tmDefaultChar(1) As Byte
   tmBreakChar(1) As Byte
   tmItalic As Byte
   tmUnderlined As Byte
   tmStruckOut As Byte
   tmPitchAndFamily As Byte
   tmCharSet As Byte
End Type

しかし、この結果得られたのは、
  tmBreakChar(0)=2
  tmBreakChar(1)=0
でやはり”2”が返ってきます。

また振り出しです^^;

引き続き、お知恵を拝借できれば幸いです。


K.J.K.  2007-03-27 18:33:37  No: 98360

となると、文字列が描画されるときの幅を取得して、自分で
配置していくしかなさそうですね。


Luna  2007-03-28 01:37:33  No: 98361

K.J.K 様

その後のテストでは、メイリオフォントを使った場合に、
ブレイク文字が”32”であってもSetTextJustification関数が
反映されない事もわかりました。
HG系のフォント(ブレイク文字=32)ではきちんと反映されるのですが・・・
どうやら、本件は、ブレイク文字だけの問題ではないようですね。

しかし、メイリオやMS UI Gothicでこんな状況となると、
VistaではSetTextJustification関数を使えないってことに
なるのかな。

私もなかば諦めかけていたので、自力で配置していく方法に
切り替えて行こうと思います。

なお、解決のチェックはもうしばらく保留しておきますので、
何か情報がありましたら、引き続きよろしくお願いいたします。


Luna  2007-03-31 00:54:34  No: 98362

結局、SetTextJustification関数の使用は諦め、
均等割りするための文字間を自分で計算して、
ExtTextOutにて出力するようにコードを変更しました。

K.J.K.様、魔界の仮面弁士様、お付き合い頂いてありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加