0

データをプリンターに出力するアプリケーションがあります。今のところ、フォント サイズを 18 ポイントにハードコーディングし、印刷に関連するすべての座標とオフセットを計算して (特定の 18 ポイント フォントの場合)、印刷しています。これは、アプリケーションを開発するための基礎として行いました。

今、フォントサイズとフェイスに応じて、すべてを動的に調整できるようにしたいと考えています。

フォントの論理サイズを取得するために、エラー チェックを行わずに次のテスト コードを作成しました (C)。

void GetTextSize(char *input, size_t inputSize, char *fontName, size_t fontSize, SIZE *size)
{
    if(input == NULL || size == NULL || fontName == NULL)
    {
        return;
    }
    else
    {
        HFONT hFont = NULL;
        LOGFONT lf;
        HDC hdc = NULL;

        memset(&lf, 0, sizeof(lf));

        // Get the device context of the entire screen
        hdc = GetDC(NULL);

        // Set the face-name
        strcpy(lf.lfFaceName, fontName);

        // Set the font height
        lf.lfHeight = -MulDiv(fontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);

        // Set the font weight
        lf.lfWeight = FW_NORMAL;

        // Create the font
        hFont = CreateFontIndirect(&lf);

        // Re-associate the obtained device context with the font just created
        SelectObject(hdc, hFont);

        // Get the required dimensions for the text
        GetTextExtentPoint32(hdc, input, inputSize, &size);

        // Free resources
        ReleaseDC(NULL, hdc);
        DeleteObject(hFont);
        hFont = NULL;
        hdc = NULL;
    }
}

基本的、

  1. 画面全体のデバイス コンテキストを取得します。
  2. CreateFontIndirectLOGFONT構造体を使用して目的のフォントを作成します。
  3. デバイス コンテキストを作成したフォントに再度関連付けます。
  4. を使用して論理単位でフォント幅を計算しGetTextExtentPoint32ます。

上記のコードにより、size変数に , が含まれcx = 241ますcy = 34。(私のモニターの DPI は 96 です)

これらの値を実際の印刷サイズにマッピングするにはどうすればよいですか? デフォルトのテキスト マッピング モードはであるMM_TEXTため、これらのcxとのcy値はピクセルに対応します。

これを行う必要がある理由は 2 つあります。

  1. 長い行を複数の行に分割する必要があります。ページの幅がインチでわかっているので、必要なのはテキストの幅 (インチ) だけです。
  2. フォント サイズに基づいて印刷を開始する場所を決定する必要があります。

プリンターの仕様によると、1 mm あたりのドット数は 8 です。つまり、DPI は約 203.2 です。

4

1 に答える 1

1

あなたのコードは、必要なものにすでに近づいています。ハンドル リークを修正する必要があります。削除する前に、デバイス コンテキストを復元する必要があります。

    HGDIOBJ hOldFont = SelectObject(hdc, hFont);

    // Get the required dimensions for the text
    GetTextExtentPoint32A(hdc, input, strlen(input), size);

    // Free resources
    SelectObject(hdc, hOldFont);
    ::ReleaseDC(NULL, hdc);
    DeleteObject(hFont);

次に何をするかは、結果をどれだけ正確にしたいかによって大きく異なります。画面上で 15 ポイントのフォントは、紙の上でも 15 ポイントになります。したがって、紙に収まる可能な限り幅の広い文字列を見つけたい場合は、次から取得します。

 int maxWidth = (int)(paperWidthInInches * GetDeviceCaps(hdc, LOGPIXELSX));

DrawTextEx() 関数が、テキストを用紙に収めるという単調な作業の多くをどのように処理できるかに注目してください。用紙サイズに設定した RECT を受け取り、その四角形に収まるようにテキストを自動的にレンダリングします。通常、DT_WORDBREAK オプションを使用して、単語の境界でテキストを自動的に折り返すようにします。DT_CALCRECT オプションを使用して、実際にテキストをレンダリングせずにページ レイアウトを計算します。DRAWTEXTPARAMS.uiLengthDrawn 値が更新され、ページに収まらない場合に文字列の印刷を開始する場所が通知されます。

これで、プリンター デバイス コンテキストを DrawTextEx() に渡すだけで、テキストがプリンターにレンダリングされます。CreateDC() から取得したデバイス コンテキストを渡します。PrintDlg() 関数は、ユーザーがプリンターを選択できるようにするのに便利です。

それは朗報でした。悪いニュースは、GDI が真のデバイスに依存しないテキスト レンダリングを提供しないことです。特にモニターの場合、ピクセル グリッドを固定するために、レンダリングされたテキストの幅が微妙に変更されます。これにより、非常に読みやすいテキストが提供されますが、プリンターなどの DPI が高いデバイスではレイアウトが乱れます。違いは小さく、テキスト行のほんの一握りのピクセルであり、フォントが大きいほど、違いは小さくなります. そして、これらの小さな違いは、ワードラップのために、印刷されたテキストでは大きな違いになる傾向があります.

これを回避するには、プリンター デバイス コンテキストを使用してページ レイアウトを計算する必要があります。1 行に収まる RECT を DrawTextEx() に渡して、各行の終了位置を調べます。今画面にレンダリングしたものは、もちろん完全ではありません。潜在的に幅の広い文字列をレンダリングするには、エルボー ルームが必要になります。DirectWrite は、真にデバイスに依存しないテキスト レンダリングのための API を提供します。

于 2013-06-30T13:42:43.827 に答える