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. CreateFontIndirect使用和LOGFONT结构创建所需的字体。
  3. 将设备上下文与创建的字体重新关联。
  4. 使用 以逻辑单位计算字体宽度GetTextExtentPoint32

上面的代码使size变量包含:cx = 241, cy = 34. (我显示器的 DPI 是 96)

我将如何将这些值映射到实际打印尺寸?由于默认文本映射模式是MM_TEXT,因此这些cxcy值对应于像素。

我需要这样做有两个原因:

  1. 我需要将长行分成多行。因为我知道以英寸为单位的页面宽度,所以我只需要以英寸为单位的文本宽度。
  2. 我需要根据字体大小决定从哪里开始打印。

根据打印机的规格,每毫米点数为 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 的设备(如您的打印机)上抛出布局。差异很小,只有一行文本上的几个像素,字体越大,差异就越小。由于换行,这些微小的差异往往会在打印文本上变成很大的差异。

为避免这种情况,您需要使用打印机设备上下文来计算页面布局。通过将 RECT 传递给仅适合单行的 DrawTextEx() 来找出每行的结束位置。当然,您现在渲染到屏幕上的内容并不完美,您需要一些肘部空间来渲染可能更宽的字符串。DirectWrite 为真正的设备无关文本渲染提供了一个 API。

于 2013-06-30T13:42:43.827 回答