2016-08-03 31 views
-1

可変幅フォントのテキスト行をTextOutのウィンドウに書き込んで、ユーザーが任意の文字をクリックできるようにするとします。そして、どの部分をクリックしたのかをどのようにして知ることができますか?つまり、クリックのカーソル座標を文字列オフセットに変換するにはどうすればよいですか?Visual Cでカーソル位置をテキスト位置に変換する方法は?

私は正しい文字列を切り捨てるまでGetTextExtentPoint32を呼び出すことでやり遂げることができますが、確かに効率的な方法があります。マイクロソフトのメモ帳プログラムは、ライン上を右矢印で移動するピクセル数を正確に把握していますが、それはどうですか?

+0

「Visual C」とは何ですか、なぜタグがないのですか?とにかく、それは言語の問題ではありません。 – Olaf

+2

どのようにテキストをレンダリングするかによって異なりますが、基本的な考え方です。カーニングがない場合は、文字幅ごとにルックアップテーブルを作成し、オフセットを簡単に計算できます。あなたのレンダリング関数がカーニングを使用している場合、または空白を追加してテキストを正当化する場合は、それを測定する適切なマッチング関数を使用する必要があります。メモ帳は通常、固定幅のフォントを使用していますが、可変長フォントのカーニングは何の疑いもありません。 – Groo

+0

ありがとうGroo - 私は考えを得る。 TextOut(少なくともデフォルトでは)はカーン化していないようですので、私の仕事は比較的簡単です。 GetCharWidth32()を使用してルックアップテーブルを生成することができます(確認する)。 メモ帳に関しては、奇妙なことに、それはカーネルに表示されます(または私のWin7版ではそうです)。 Times New Romanでは、「AV」は、フォントサイズ12で1ピクセル、フォントサイズ18で2ピクセル、24インチで3などの中間オーバーラップを示します。オフセットを測定するためにどのような機能が使用されているのか、それともカーニングがどのように機能するのかはわかりませんが、私のプログラムではカーニングなしで行うことができます。 –

答えて

0

この回答は、マイクロソフトの秘密の文書を巡って試行錯誤してまとめたものです。それはExtTextOutとのDrawText間の選択である(と反射に、私はそれが望ましいと思います)カーニングが必要な場合は

* TextOut, which does not kern 
* ExtTextOut, which can kern if its final parameter is non-null 
* DrawText, which always kerns 

MSDN Cライブラリのテキストを表示するには、以下の機能を提供します。

DrawTextは、Grooによって提案された行に沿った解決策を提供します。それはのように、テキストエリアの周りに描画する ボックスを必要とします。

void textOut(HDC hdc, int x, int y, char *s, int l) 
{ 
    RECT r = {0}; 
    r.left = x; 
    r.right = Ewidth; 
    r.top = y; 
    r.bottom = y+LineHt; 
    DrawText(hdc,s,l,&r,DT_NOPREFIX); 
} 

フォントサイズを設定または変更されている場合は、文字幅ルックアップテーブル「CharW」に構築する必要があります。

ABC CharW[256];  // char-width including leading/trailing space 
    GetCharABCWidths(hdc, 0, 0xff, CharW); 

フォントが変更され、カーニングテーブルを構築する必要がある場合:

KERNINGPAIR *Kern; // pairs of chars and the (usually negative) additional gap between them 
int KernCnt;  // number of same 
KERNINGPAIR *CharK[256];// ptr to first kerning-pair for each char 

    if(!Kern) 
     free(Kern); 
    KernCnt = GetKerningPairs(hdc, -1, 0); 
    Kern = malloc(KernCnt * sizeof(*Kern)); 
    GetKerningPairs(hdc, KernCnt, Kern); 
    { 
     int i; 
     for(i = 0; i < KernCnt; ++i) { 
      KERNINGPAIR *k = Kern+i; 
      if(k->wFirst < 0x100) { 
       KERNINGPAIR **k2 = CharK + k->wFirst; 
       if(!*k2) 
        *k2 = k; 
      } 
     } 
    } 

安全な再生するには、カーニングテーブル「カーン」は(wFirst、wSecond)によってソートされなければならないが、ように見えますクラスタ私のコードはqsortなしで動作します。

次のように我々は、従って、任意のサブ画素幅を算出することができる:

int pixelWidth(char *s, int l) 
{ 
    int x = 0; 
    int i; 
    for(i = 0; i < l; ++i) { 
     char c = s[i]; 
     ABC *w = CharW+c; 
     int wk = 0; 
     if(i > 0) { 
      char b = s[i-1]; 
      KERNINGPAIR *k = CharK[b]; 
      if(k) 
      for(; k < Kern+KernCnt && k->wFirst == b; ++k) 
      if(k->wSecond == c) 
       {wk = k->iKernAmount; break;} 
     } 
     x += wk + w->abcA + (w->abcB) + w->abcC; 
    } 
    return x; 
} 

これはテストされ、x座標フラグが設定されている保持電流が座標ときのDrawTextによって返されると一致しています。

SetTextAlign(hdc,TA_UPDATECP) 

したがって、指定されたピクセル幅に一致する部分文字列長を見つけることは簡単です。しかし、簡単な解決策は、ExtTextOutによって提供される

INT W[512];  // maximum string-length 

void textOut(HDC hdc, int x, int y, char *s, int l) 
{ 
    GCP_RESULTS g={0}; 
    g.lStructSize = sizeof(g); 
    g.lpDx = W; 
    g.nGlyphs = sizeof(W)/sizeof(*W); 
    GetCharacterPlacement(hdc, s, l, sizeof(W), &g, GCP_USEKERNING); 
    ExtTextOut(hdc, x, y, 0, 0, s, l, g.lpDx); 
} 

MSDN関数てGetCharacterPlacement()は、文字列s内の各文字の実際のピクセル幅を有する配列を返します。上記の私のルックアップテーブルCharW、Kern、CharKを置き換えます。マイクロソフトによれば、Uniscribe機能に取って代わられているが、英語のようなヨーロッパ言語でもうまく機能する。

関連する問題