在给定目标打印机的DPI的情况下,如何以某种字体计算某些打印文本的大小?

时间:2013-06-07 14:14:57

标签: c algorithm language-agnostic font-size

我有一个将数据打印到打印机的应用程序。现在,我已经将字体大小硬编码为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以逻辑单位计算字体宽度。
  5. 上述代码会导致size变量包含:cx = 241cy = 34。 (我的显示器的DPI是96)

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

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

    1. 我需要将长行分成多行。因为我知道以英寸为单位的页面宽度,所以我需要的是以英寸为单位的文本宽度。
    2. 我需要根据字体大小决定从哪里开始打印。
    3. 根据打印机的规格,每毫米的点数为8;即,DPI约为203.2。

1 个答案:

答案 0 :(得分: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。