none
メタファイルとユニコード RRS feed

  • 質問

  • VC++2010(MFC+文字セット:UNICODE)で、描画アプリケーションを開発しています。

    描画内容をクリップボードに送るのに、COleServerItem::CopyToClipboard関数を使用しています。
    問題は、このクリップボードに出力された描画データ内の「文字」が正常に出力されません。
    「ああ・・」とか「??ああ??」、または、表示さえもしません。
    描画関数は、ディスプレイに表示されているものと同じものを使用しています(されています)。

    原因がわかりません。

    2017年7月19日 5:51

回答

  • しかし、日本語以外の場合、文字化けがおこります。中国語(簡体)では、文字化けしました。
    ディスプレイ側では、正しく表示されています。

    メタファイルは詳しくありませんが、「内部がマルチバイト文字で扱うからこそ、このような変な引数(マルチバイト文字表現でのバイト数が必要)になる」と仮定すると、異なる文字セット(日本語環境で中国語(簡体))は扱えないということでしょう。
    正確には「Unicode 非対応のプログラムの言語」で選択されている言語に依存すると予想されます。
    The Windows Metafile image format unfortunately does not provide any unicode support と書いているページもありますね)

    上記の情報が正しいと仮定して、出力先がクリップボードと考えると、クリップボードの貼り付け先が、「Unicode 非対応のプログラムの言語」で選択されている言語でメタファイル内の文字列を認識することになりますので、自分のプログラムでどんな工夫をしたとしても、、「Unicode 非対応のプログラムの言語」で日本語を選んでいる状態では、簡体字の文字は扱えません。
    クリップボードを介してメタファイル形式で、異なる文字セットの文字を扱うという仕様がそもそも実現不可能な仕様と言うことになりますので、仕様を見直してください。

    なお、画面に表示できるのは、どちらの文字も表現可能な Unicode で描画しているだけでしょうから、画面の描画と比較することには意味がないと思います。

    2017年7月19日 15:45
    モデレータ

すべての返信

  • 以下のページの症状と似ていると思うのですが、
    このケースではTextOut()の文字数の指定に誤りがある疑いが指摘されています。

    https://social.msdn.microsoft.com/Forums/ja-JP/2ffcc662-1c7d-4251-abef-f1b039f8d29e/textoutcount?forum=vcgeneralja

    確認してみてはどうでしょう。

    2017年7月19日 6:09
  • 日本語の場合、下記のコードで解決できました。ありがとございました。

    ×:pDC->TextOut( x, y, Strings , lstrlen(Strings) );// ディスプレイ描画では問題ない

    ○:pDC->TextOut( x, y, Strings , lstrlen(Strings) * sizeof(TCHAR) );

    しかし、日本語以外の場合、文字化けがおこります。中国語(簡体)では、文字化けしました。
    ディスプレイ側では、正しく表示されています。


    2017年7月19日 7:37
  • >○:pDC->TextOut( x, y, Strings , lstrlen(Strings) * sizeof(TCHAR) );

    引数の「Strings」が、CString又はCStringWクラスのインスタンスであれば

    CDC::TextOut( int x, int y, const CString &str)

    の方のオーバーロード関数を使用すれば良いだけなのですが、
    別の型の場合は、そのクラスにおいてのUnicodeでの
    「文字数」を算定するメンバ関数の結果をnCountに使用したほうが良いでしょう。

    現在のマニュアルは正しい説明がされています(下記参照)。

    https://msdn.microsoft.com/ja-jp/library/yzabsdzx(v=vs.120).aspx


    2017年7月19日 7:52
  • 上記内容は、「○している部分は、間違っている」と解釈すべきなのでしょうか?
    理解力がなくすみません。。。

    最初にいただいたURLの内容を、私が理解した感じでは、

    クリップボード出力の時は、文字数ではなくて、バイト数が必要となる。と理解しました。

    で、日本語の場合、正しく表示されました。

    ちなみに、Strings は、LPCSTR型で、CString s(Strings); として、下記関数を使用しましたが、
    CDC::TextOut( int x, int y, const CString &str)では、文字化けが起こります。

    2017年7月19日 8:28
  • CDC::TextOut()のーバーロードがうまくいかないとのこと、申し訳ありません。
    COleServerItemのクリップボードの場合でしたね。

    この場合、リンクしたページの質問者さんの発言にある通り
    TextOut()でUnicode文字列を出力する場合に、引数のnCountには、
    「Unicode文字列の文字数ではなく、その文字列をMBCSにしたときのバイト数を渡す」
    と正しく表示されるようです。

    また、同ページには
    「半角と全角が混在した文字列の場合は、lstrlen(Strings)*sizeof(TCHAR)では正しく表示されません。」
    との記述もあり、CT2A()でMBCSに変換後、strlen(・・変換後の文字列・・)の結果をnCountに与えるとうまくいったのことでした。

    又は、
    CStringA  MBCS_String( ・・Unicode文字列・・);  のようにMBCS文字列に変換して、
    nCount = MBCS_String.GetLength();
    としてもうまくいくであろうと予測できます。

    2017年7月19日 9:08
  • しかし、日本語以外の場合、文字化けがおこります。中国語(簡体)では、文字化けしました。
    ディスプレイ側では、正しく表示されています。

    メタファイルは詳しくありませんが、「内部がマルチバイト文字で扱うからこそ、このような変な引数(マルチバイト文字表現でのバイト数が必要)になる」と仮定すると、異なる文字セット(日本語環境で中国語(簡体))は扱えないということでしょう。
    正確には「Unicode 非対応のプログラムの言語」で選択されている言語に依存すると予想されます。
    The Windows Metafile image format unfortunately does not provide any unicode support と書いているページもありますね)

    上記の情報が正しいと仮定して、出力先がクリップボードと考えると、クリップボードの貼り付け先が、「Unicode 非対応のプログラムの言語」で選択されている言語でメタファイル内の文字列を認識することになりますので、自分のプログラムでどんな工夫をしたとしても、、「Unicode 非対応のプログラムの言語」で日本語を選んでいる状態では、簡体字の文字は扱えません。
    クリップボードを介してメタファイル形式で、異なる文字セットの文字を扱うという仕様がそもそも実現不可能な仕様と言うことになりますので、仕様を見直してください。

    なお、画面に表示できるのは、どちらの文字も表現可能な Unicode で描画しているだけでしょうから、画面の描画と比較することには意味がないと思います。

    2017年7月19日 15:45
    モデレータ
  • 元の質問文はメタファイルに関しての話題でしたが、いつの間にかTextOutの話題に変わっています。問題点を明確にした方がいいと思います。

    TextOutで正しく表示されないとのことですが、メタファイルやクリップボードは使っておらず、引数Stringsには正しい文字列が格納されている前提でしょうか?

    2017年7月19日 22:39
  • COleServerItem::CopyToClipboard関数でクリップボードに貼り付けられるときのみ、TextOutで出力される文字列が化けるという現象です。

    CopyToClipboard関数は、WMFを作成するデバイスコンテキストを生成して、OnDraw関数を実行していました。
    ※CMetaFileDC::Create()

    WMFが、Unicode未対応ならばと、EMFを作成するデバイスコンテキストを生成+CDC::TextOut( int x, int y, const CString &str)関数
    で、
    ユニコード文字列の文字化けが無くなりました。
    ※CMetaFileDC::CreateEnhanced()

    現在わかった内容は、次の2点です。

    ・UNICODE版のアプリで、WMFを作成するときのTextOut関数はバイト数が必要。だけど、WMF自体がUNICODE文字列は未対応。

    (ここで疑問なのですが、TextOut関数に渡している文字列は、Unicode文字列で、日本語なら正しく表示される。中国語は文字化けする。中国語OSでは、日本語文字列、中国語文字列両方文字化けする。不思議です。)

    ・EMFを作成する場合では、UNICODE文字列が対応しており、CDC::TextOut( int x, int y, const CString &str)関数も使用できる

    ということが分かってきました。

    とりあえず、UNICODE文字列を使用するには、EMFを使用すると理解します。

    • 編集済み Brillia 2017年7月21日 3:33
    2017年7月20日 1:51
  • 回答ではありません。参考としての発言になってしまいますが、

    1.WMF(Windows Metafile)は1990~1992年(頃)に発行されたフォーマットで、
      16bitのGDI用に設計されたものです。
     アプリ内で作成し、アプリ内で正確に再生することは可能ですが、
     DCへの初期化コマンドなどが含まれないため、
     物理ファイルが作られた時の基本的な初期化条件が不明となってしまい、
     単体では正確に再生することが困難な仕様でした。
     公式文書は発見できませんでしたが、WMFはマイクロソフトが非推奨としたはず。
     という記憶があります。

    2.EMF(Enhanced Metafile)は1993年に発行されたフォーマットで、32bit用にWMFを拡張したものです。
     こちらは初期化に必要な要素も(ほぼ)含まれているため単体で正確に再生することができます。

    そういった経過から、WMFよりEMFの方がややサポートが良いかもしれません。

    ただし、残念ながら両者とも、PostScriptやそれから派生したPDF等にとってかわられてしまい、
    サポートしているアプリケーションが少ないのが現状です。

    2017年7月20日 5:33