【发布时间】:2014-08-19 12:54:36
【问题描述】:
我有在纸上绘制网格的打印代码。
网格有 4 列,它们的水平长度相等。单元格的高度是纸张尺寸的十分之一。总行数未知,但我知道至少会有一行。
每个单元格具有相同的物理尺寸-> 宽度是纸张宽度的四分之一,高度是纸张高度的十分之一。 单元格最多可容纳 50 个字符。
我面临的问题是选择正确的字体大小,以便最大长度的文本可以适合单元格。
浏览 MSDN 文档和WinAPI 示例,我看到他们使用GetTextExtPoint32 用于类似目的,但这仅在字体已经存在并且被选择到设备上下文时才有效,这里不是这样。
我唯一想到的是创建“虚拟字体”,查看示例文本是否可以放入单元格,然后在测试失败时调整其大小。我还发现 this blog 推荐了解决此问题的有趣方法,但由于缺乏经验,我无法确定“这是否是正确的方法”。
您能为我的问题推荐一个正确的解决方案吗?
2014 年 6 月 30 日编辑:
以下是绘制网格并将左上单元格绘制为浅灰色的示例函数,因为该单元格将包含示例文本。这样我们就可以直观地验证我们的绘图代码是否成功:
// 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;
}
要使用此功能,只需按下按钮/菜单选择或其他方式启动它。
XPS 中的结果似乎一致,但我在 MS OneNote 2007 中得到了奇怪的结果,如下图所示:
字体大小为 14:
字体大小为 20:
字体大小为 20,但应用了上述功能的缩放:
编辑结束
2014 年 7 月 6 日编辑:
上面编辑的第三张图片是GDI使用默认高度值的结果,因为我对字体高度进行数学调整的结果是0。一旦zero is passed to CreateFont 提到的行为是预期的。
从double 到int 执行正确的转换后,我得到了几乎完美的输出 -> 字符串中的最后一个字母几乎没有 超过了限制。我将继续尝试改进这个公式,因为我相信很有希望。如果有人有其他数学解决方案,请随时发布。
编辑结束
如果需要进一步的信息/编辑,请发表评论,我会尽快做出反应。
【问题讨论】:
-
创建一个虚拟字体并获取最大长度字符串的长度。如果“不合适”,请不要丢弃它——用它来计算更接近的值!如果文本是 n 像素宽并且您的单元格是 m 像素,则将字体大小缩小到 m/n。
-
@Jongware:文本确实适合单元格,但结果不是我所期望的。我用
DrawTextExt画出测试串,测试串有几个字。文本必须垂直居中。虽然整个测试字符串都适合单元格,但垂直居中是不正确的。如果需要,我将使用小型演示 sn-p 更新我的帖子。 也许我误解了你,或者错误地应用了你的建议。最好的问候。 -
听起来好像我误解了您对“选择适当的字体大小以便最大长度的文本可以放入单元格”的解释。我在您的帖子中看不到“垂直居中”的提及,所以也许您应该编辑它。(我也在想,也许您拥有的草图或屏幕截图会有所帮助。)
-
@Jongware:我相信你是对的,但现在已经很晚了。另外,我想真正付出努力,以便我的编辑可以具有建设性,所以我明天会进行更新(包括截图)。最好的问候
-
@Jongware:我已按照您的指示编辑了我的帖子。至于垂直居中,我相信一个简单的数学会帮助我。现在我的主要问题显示在我编辑的最后一张图片中。最好的问候。