基于纸张大小和允许的最大文本长度计算理想的字体大小 [英] Calculate ideal font size, based on the paper size and maximum allowed text length

查看:179
本文介绍了基于纸张大小和允许的最大文本长度计算理想的字体大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有打印在纸上绘制网格的代码。

网格有4列,它们具有相等的水平长度。单元格的高度是纸张大小的十分之一。总行数是未知的,但我知道至少会有一行。

每个单元具有相同的物理大小 - >宽度是纸的四分之一宽度和高度是纸张高度的十分之一。 可放入单元格的最大字符数为50个。



我所面对的问题是选择合适的字体大小,进入细胞。浏览MSDN文档和 WinAPI 的例子,我看到他们使用 GetTextExtPoint32 用于类似的目的,但是如果字体已经存在并且被选择到设备上下文中,这只适用于,这在这里不是这种情况。 b
$ b

我唯一想到的就是创建虚拟字体,看看示例文本是否可以放入单元格中,然后在测试失败时调整它的大小。我也发现

> double int 我得到了近乎完美的输出结果 - >字符串中的最后一个字母超出了限制。我会继续尝试改进这个公式,因为我相信是有希望的。

编辑完毕



<如果需要进一步的信息/编辑,留下评论,我会尽快作出反应。

解决方案


我看到的最大的问题是在这一行:

  lfHeight * = s.cy /(rcText.bottom  -  rcText.top); 

这些都是整数。在C和C ++中,用整数进行除法会导致向零截断。因此,如果分割的结果是应该是3.7,那么最终会得到3,这可能是一个非常粗略的近似值。

另一个问题是GetTextExtentPoint32会不包装文本,但DrawText呢。所以你正在测量的文本,就像你要打印它作为一个单一的线,而你实际上绘制成多行。



把这些放在一起,就可以像这样测量文本:

  WCHAR szText [] = LХидрогеотермалнаенергијаХидрогеотермалнаенерги; 
RECT rcText;
rcText.left = 0;
rcText.top = 0;
rcText.right = pageWidth / 4;
rcText.bottom = top;
const DWORD选项= DT_CENTER | DT_WORDBREAK | DT_NOCLIP;
DrawTextEx(pdx.hDC,szText,-1,& rcText,options | DT_CALCRECT,NULL);

//因为我们使用了DT_CALCRECT,所以DrawTextEx调用并没有绘制任何东西,
//但是它调整了rcText的底部来考虑实际的高度。
double actual_height = static_cast< double>(rcText.bottom - rcText.top);
double desired_height = pageHeight / 10.0;
double ratio = desired_heigth / actual_height;

//按比例缩放字体高度,并将其舍入为最接近的整数。
lf.lfHeight = static_cast< int>(lf.lfHeight * ratio + 0.5);


I have printing code that draws grid on the paper.

Grid has 4 columns, and they have equal horizontal length. Height of the cell is tenth of the paper size. Total number of rows is unknown but I know for a fact that there will be at least one row.

Each cell has same physical size-> width is quarter of the paper width, and height is one tenth of the paper height. Maximum number of characters that can fit into cell is 50.

The problem I face is choosing proper font size so text of maximum length can fit into cell.

Browsing through MSDN documentation and WinAPI examples, I saw that they use GetTextExtPoint32 for similar purposes, but this works only if font already exists and is selected into device context, which is not the case here.

The only thing that crossed my mind was to create "dummy font", see if the example text can fit into cell, and then adjust it's size if the test fails. I have also found this blog that recommends interesting approach to this problem, but being inexperienced I can't decide if "this is the proper way to go".

Can you recommend a correct solution for my problem?

EDITED on June, 30th 2014:

Below is the sample function that draws grid and paints upper left cell in light gray since that cell will contain sample text. That way we can visually validate the success of our drawing code:

// hWnd is the window that owns the property sheet.
HRESULT GDI_PRINT(HWND hWnd)
{
    HRESULT hResult;
    PRINTDLGEX pdx = {0};
    LPPRINTPAGERANGE pPageRanges = NULL;

    // Allocate an array of PRINTPAGERANGE structures.
    pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));

    if (!pPageRanges)
        return E_OUTOFMEMORY;

    //  Initialize the PRINTDLGEX structure.
    pdx.lStructSize = sizeof(PRINTDLGEX);
    pdx.hwndOwner = hWnd;
    pdx.hDevMode = NULL;
    pdx.hDevNames = NULL;
    pdx.hDC = NULL;
    pdx.Flags = PD_RETURNDC;
    pdx.Flags2 = 0;
    pdx.ExclusionFlags = 0;
    pdx.nPageRanges = 0;
    pdx.nMaxPageRanges = 10;
    pdx.lpPageRanges = pPageRanges;
    pdx.nMinPage = 1;
    pdx.nMaxPage = 1000;
    pdx.nCopies = 1;
    pdx.hInstance = 0;
    pdx.lpPrintTemplateName = NULL;
    pdx.lpCallback = NULL;
    pdx.nPropertyPages = 0;
    pdx.lphPropertyPages = NULL;
    pdx.nStartPage = START_PAGE_GENERAL;
    pdx.dwResultAction = 0;

    //  Invoke the Print property sheet.

    hResult = PrintDlgEx(&pdx);

    if ( ( hResult == S_OK ) && ( pdx.dwResultAction == PD_RESULT_PRINT ) )
    {

        // User clicked the Print button, 
        // so use the DC and other information returned in the 
        // PRINTDLGEX structure to print the document.

        //======= Various initializations ==========//

        DOCINFO diDocInfo = {0};
        diDocInfo.cbSize = sizeof( DOCINFO ); 
        diDocInfo.lpszDocName = L"Testing printing...";

        int pageWidth = GetDeviceCaps( pdx.hDC, HORZRES ), 
            pageHeight = GetDeviceCaps( pdx.hDC, VERTRES ); 

        //===================== IMPORTANT !!! ==========================//
        //               Must test this on real printer !!!             //
        //         For now testing is done in XPS and MS OneNote2007    //
        //==============================================================//

        //================== end of initialization =====================//

        if( StartDoc( pdx.hDC, &diDocInfo ) > 0 )
        {
            if( StartPage( pdx.hDC ) > 0 )
            {
                //===== creating red pen that will draw grid =====//
                LOGBRUSH lb;
                lb.lbColor = RGB( 255, 0, 0 );
                lb.lbHatch = 0;
                lb.lbStyle = BS_SOLID;

                HPEN hPen = ExtCreatePen( PS_COSMETIC | PS_SOLID, 1, &lb, 0, NULL); 
                HGDIOBJ oldPen = SelectObject( pdx.hDC, hPen );

                // create test font
                HFONT font, oldFont; 

                long lfHeight = -MulDiv( 14,
                    GetDeviceCaps( pdx.hDC, LOGPIXELSY ), 
                    72 );

                font = CreateFont( lfHeight, 0, 0, 0, 
                    FW_BOLD, TRUE, FALSE, FALSE, 
                    0, 0, 0, 
                    0, 0, L"Microsoft Sans Serif" );

                oldFont = SelectFont( pdx.hDC, font );

                SetBkMode( pdx.hDC, TRANSPARENT );
                SetTextColor( pdx.hDC, RGB( 255, 0, 0 ) );

                // testing rectangle -> top left cell of the grid
                RECT rcText;

                rcText.left = 0;
                rcText.top = 0;
                rcText.right = pageWidth / 4;
                rcText.bottom = pageHeight / 10;

                // fill destination rectangle with gray brush
                // so we can visually validate rectangle coordinates 
                FillRect( pdx.hDC, &rcText, (HBRUSH)GetStockObject(LTGRAY_BRUSH) );

                // implement solution mentioned in the comment to this question
                SIZE s;
                ::GetTextExtentPoint32( pdx.hDC, 
                    L"Хидрогеотермална енергија Хидрогеотермална енерги", 
                    wcslen( L"Хидрогеотермална енергија Хидрогеотермална енерги" ), 
                    &s );

                // select old font back and dispose test font
                SelectObject( pdx.hDC, oldFont );
                DeleteObject( font );

                // adjust font height
                lfHeight *= s.cy / ( rcText.bottom - rcText.top );

                // now we can create proper font 
                font = CreateFont( lfHeight, 0, 0, 0, 
                    FW_BOLD, TRUE, FALSE, FALSE, 
                    0, 0, 0, 
                    0, 0, L"Microsoft Sans Serif" );

                oldFont = SelectFont( pdx.hDC, font );

                // draw text in test rectangle 
                DrawTextEx( pdx.hDC,
                    L"Хидрогеотермална енергија Хидрогеотермална енерги", 
                    wcslen( L"Хидрогеотермална енергија Хидрогеотермална енерги" ), 
                    &rcText, DT_CENTER | DT_WORDBREAK | DT_NOCLIP, NULL );

                //============== draw a testing grid ===============//

                // draw vertical lines of the grid
                for( int i = 0; i <= pageWidth; i += pageWidth / 4 )
                {
                    MoveToEx( pdx.hDC, i, 0, NULL );
                    LineTo( pdx.hDC, i, pageHeight );
                }

                // draw horizontal lines of the grid
                for( int j = 0; j <= pageHeight; j += pageHeight / 10 )
                {
                    MoveToEx( pdx.hDC, 0, j, NULL );
                    LineTo( pdx.hDC, pageWidth, j );
                }

                // no need for pen anymore so delete it
                SelectObject( pdx.hDC, oldPen );
                DeleteObject( hPen );

                // no need for font, delete it
                SelectFont( pdx.hDC, oldFont );
                DeleteFont( font );

                if( EndPage( pdx.hDC ) < 0 )
                // for now pop a message box saying something went wrong
                    MessageBox( hWnd, L"EndDoc failed!", L"Error", MB_OK );
            }

            EndDoc( pdx.hDC );
        }
    }

    if (pdx.hDevMode != NULL) 
        GlobalFree(pdx.hDevMode); 

    if (pdx.hDevNames != NULL) 
        GlobalFree(pdx.hDevNames); 

    if (pdx.lpPageRanges != NULL)
        GlobalFree(pPageRanges);

    if (pdx.hDC != NULL) 
        DeleteDC(pdx.hDC);

    return hResult;
}

To use this function, just launch it on button press/menu selection or whatever.

The results in XPS seem consistent, but I get strange results in MS OneNote 2007 which following images illustrate:

Font size is 14 :

Font size is 20 :

Font size is 20, but scaling from the above function was applied :

END OF EDIT

EDITED on July, 6th 2014:

The third picture from above edit was the result of GDI using default height value because the result of my mathematical adjustment for font height was 0. Once zero is passed to CreateFont mentioned behavior is expected.

After performing proper casting from double to int I got nearly perfect output -> last letter in the string barely exceeds the limit. I will continue to try improving this formula since I believe is promising. If anybody has another mathematical solution feel free to post it.

END OF EDIT

If further info / edit is required, leave a comment and I will react as soon as possible.

解决方案

There are multiple issues involved.

The biggest problem I see is in this line:

lfHeight *= s.cy / ( rcText.bottom - rcText.top );

These are all integers. In C and C++, division with integers results in truncation toward zero. So if the result of the division "should" be 3.7, you'll end up with 3, which can be a pretty crude approximation.

Another problem is that GetTextExtentPoint32 does not wrap text, but DrawText does. So you're measuring the text as though you're going to print it as a single line, and you actually draw it as multiple lines. Instead of using GetTextExtendPoint32, you can measure the height with DrawText by DT_CALCRECT flag.

Putting these together, you want to measure your text like this:

WCHAR szText[] = L"Хидрогеотермална енергија Хидрогеотермална енерги";
RECT rcText;
rcText.left = 0;
rcText.top = 0;
rcText.right = pageWidth / 4;
rcText.bottom = top;
const DWORD options = DT_CENTER | DT_WORDBREAK | DT_NOCLIP;
DrawTextEx( pdx.hDC, szText, -1, &rcText,  options | DT_CALCRECT, NULL);

// Because we used DT_CALCRECT, the DrawTextEx call didn't draw anything,
// but it did adjust the bottom of rcText to account for the actual height.
double actual_height = static_cast<double>(rcText.bottom - rcText.top);
double desired_height = pageHeight / 10.0;
double ratio = desired_heigth / actual_height;

// Scale the font height by the ratio, and round it off to the nearest int.
lf.lfHeight = static_cast<int>(lf.lfHeight * ratio + 0.5);

这篇关于基于纸张大小和允许的最大文本长度计算理想的字体大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆