如何精确计算单个字符在的宽高.

xiaohuh421 2014-11-24 12:50:34
加精
使用一般的方法, 比如DrawText 的DT_CALCRECT参数或者GetTextExtentPoint32系列的函数获取的字符宽高, 会包含空间区域.

这个区域也是可以理解的, 不同的字符要相同字体下, 所使用的点阵需要的空间是不同的.

空白区域如下图所示:

在文字的上, 和下都有一部分空白区域, 并且文字越大, 这个空白区域越大.

有没有其它方法, 能得到实际的像素填充区域大小? 或者怎么得到上下空白区域的大小?

谢谢大家


...全文
1683 28 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
bigeng1 2014-12-24
  • 打赏
  • 举报
回复
这个程序还不错
beyondcj 2014-12-03
  • 打赏
  • 举报
回复
bcrun 2014-12-02
  • 打赏
  • 举报
回复
这种需求在WORD中输出文字中应该也常见吧,不知国内做WPS的是怎么实现的
xiaohuh421 2014-11-28
  • 打赏
  • 举报
回复
赵老师的方法确实靠谱, 简单又方便. 并可直接获得实际输出位置.
效果如图:
xiaohuh421 2014-11-28
  • 打赏
  • 举报
回复
引用 21 楼 fandyvon 的回复:
[quote=引用 20 楼 fandyvon 的回复:] [quote=引用 19 楼 fandyvon 的回复:] [quote=引用 4 楼 schlafenhamster 的回复:] GetTextExtentPoint32 function The GetTextExtentPoint32 function computes the width and height of the specified string of text.
喔喔? [/quote]喔喔?[/quote] 喔喔[/quote] 这种方法不行, 得到的结果就是如我帖子中的样子.
引用 15 楼 hhhh63 的回复:
dc.GetGlyphOutline,象AV两字可以更紧。
这种方法也试过了, 宽度和高度都会有所偏差. 按MSDN说明应该是可以的, 但是不知道为什么实际效果不如人意. GLYPHMETRICS和TEXTMETRIC GetGlyphOutline如何获取汉字的点阵数据
xiaohuh421 2014-11-28
  • 打赏
  • 举报
回复
引用 18 楼 zhao4zhong1 的回复:
BeginPath DrawText EndPath PathToRegion GetRgnBox 此为最终正解!如假包换!!
不愧是赵老师, 这个方法应该比较靠普.
赵4老师 2014-11-28
  • 打赏
  • 举报
回复
引用 24 楼 xiaohuh421 的回复:
赵老师的方法确实靠谱, 简单又方便. 并可直接获得实际输出位置. 效果如图:
知道差距了吧!(偶尔个人膨胀一下,感觉真好!!)
赵4老师 2014-11-27
  • 打赏
  • 举报
回复
BeginPath DrawText EndPath PathToRegion GetRgnBox 此为最终正解!如假包换!!
xiaohuh421 2014-11-26
  • 打赏
  • 举报
回复
先放一个根据点阵计算文字轮廓的代码的简单实现, 继续等其它方法.

void CalcCharSize(HDC hDC, TCHAR ch, SIZE &siSize, SIZE *pTB)
{
	GetTextExtentPoint32(hDC, &ch, 1, &siSize);
	int iWidth = siSize.cx;
	int iHeight = siSize.cy;

	//设备无关位图
	CONST int BIT_COUNT24 = 24;
	BITMAPINFO bmpInfo={0};
	int bmpWidthBytes =0;
	bmpWidthBytes= iWidth*BIT_COUNT24;//4字节对齐,位图每一行数据的大小.
	bmpWidthBytes+=31;
	bmpWidthBytes/=32;
	bmpWidthBytes*=4;
	ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
	bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	bmpInfo.bmiHeader.biBitCount=BIT_COUNT24;
	bmpInfo.bmiHeader.biCompression=BI_RGB;
	bmpInfo.bmiHeader.biPlanes=1;
	bmpInfo.bmiHeader.biWidth=iWidth;
	bmpInfo.bmiHeader.biHeight=-iHeight;
	bmpInfo.bmiHeader.biSizeImage = bmpWidthBytes * iHeight;
	BYTE *pBmpBits=NULL;
	HBITMAP hBmp=::CreateDIBSection(NULL,&bmpInfo,0,(void**)&pBmpBits,NULL,0);

	HDC hMemDC = CreateCompatibleDC(hDC);
	HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hBmp);

	SetBkMode(hMemDC, TRANSPARENT);
	//设置目标颜色, 方便下面计算
	SetTextColor(hMemDC, RGB(255,255,255));
	//统一字体
	HFONT hFont = (HFONT)::GetCurrentObject(hDC, OBJ_FONT);
	HFONT hOldFont = (HFONT)::SelectObject(hMemDC, hFont); 
	RECT rcRect = {0,0,siSize.cx, siSize.cy};
	DrawText(hMemDC, &ch, 1, &rcRect, DT_LEFT|DT_TOP);
	::SelectObject(hMemDC, hOldFont);

	//找top
	int top = 0;
	for(int y=0; y<iHeight; ++y)
	{
		for(int x=0; x<iWidth; ++x)
		{
			if(pBmpBits[y*bmpWidthBytes+x*3] > 0)
			{
				top = y;
				y = iHeight;
				break;
			}
		}
	}

	//找bottom
	int bottom = 0;
	for(int y=iHeight-1; y>0; --y)
	{
		for(int x=0; x<iWidth; ++x)
		{
			if(pBmpBits[y*bmpWidthBytes+3*x] > 0)
			{
				bottom = y;
				y = 0;
				break;
			}
		}
	}

	if(pTB)
	{
		pTB->cx = top;
		pTB->cy = siSize.cy-bottom-1;
	}
	siSize.cy = bottom-top+1;
	::SelectObject(hMemDC, hOldBmp);

	::DeleteObject(hBmp);

	::DeleteDC(hMemDC);



}
xiaohuh421 2014-11-26
  • 打赏
  • 举报
回复
引用 10 楼 hhhh63 的回复:
不好意思,刚才服务器出错,发了两条,实测了一下 宋体,100像素高,英文字母j下面到底,簧字上面到顶,代码如下:
void CdlgDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		
		CFont ft;
		ft.CreateFont(
		100,                   // nHeight
		0,                   // nWidth
		0,                         // nEscapement
		0,                         // nOrientation
		0,                   // nWeight
		FALSE,                     // bItalic
		FALSE,                     // bUnderline
		0,                         // cStrikeOut
		ANSI_CHARSET,              // nCharSet
		OUT_DEFAULT_PRECIS,        // nOutPrecision
		CLIP_DEFAULT_PRECIS,       // nClipPrecision
		DEFAULT_QUALITY,           // nQuality
		DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
		L"宋体");


		CPaintDC dc(this); // 用于绘制的设备上下文
		dc.SelectObject( ft );
		CString str = L"jklHJK弹簧";
		CRect rc;
		dc.DrawText( str, rc, DT_CALCRECT );
		rc.OffsetRect( 10, 10 );
		dc.Rectangle(rc);
		dc.DrawText( str, rc, DT_LEFT);


	}
}
你的代码中哪里是体现 基线,中线,底线,顶线 对齐的呢? DrawText( str, rc, DT_CALCRECT ); 实际测试, 这个计算出来的区域也是包含空白区域的. 而不是文字轮廓区域. 难道真的只有去读取点阵中的文字轮廓?
xiaohuh421 2014-11-26
  • 打赏
  • 举报
回复
引用 6 楼 chenlycly 的回复:
可以参考“MeasureString测量文字注意事项 ”:http://blog.csdn.net/cometnet/article/details/37992627
这个也试过了, 同样计算的高度包括了空白.
那一抹斜阳 2014-11-26
  • 打赏
  • 举报
回复
我其实是来打酱油的,
xiaohuh421 2014-11-26
  • 打赏
  • 举报
回复
感谢hhhh63的回答. 我又学到新的api 百度后, 找到这个例子, 讲得不错. 用GetGlyphOutline搞字模 看这个帖子中的描述,似乎能符合.等我试过了再回来说
hhhh63 2014-11-26
  • 打赏
  • 举报
回复
不好意思,刚才服务器出错,发了两条,实测了一下

宋体,100像素高,英文字母j下面到底,簧字上面到顶,代码如下:
void CdlgDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{

CFont ft;
ft.CreateFont(
100, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
0, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
L"宋体");


CPaintDC dc(this); // 用于绘制的设备上下文
dc.SelectObject( ft );
CString str = L"jklHJK弹簧";
CRect rc;
dc.DrawText( str, rc, DT_CALCRECT );
rc.OffsetRect( 10, 10 );
dc.Rectangle(rc);
dc.DrawText( str, rc, DT_LEFT);


}
}
hhhh63 2014-11-26
  • 打赏
  • 举报
回复
英文字符有基线,中线,底线,顶线,就象过去用的写英文的作业本,字体类中有这些参数,用这些数据应该接近一些。 更精确的,要获得每一个英文字符轮廓占的位置,只有取字符的矢量数据,然后取包围它的矩形,不同的字符轮占的大小不一样。
hhhh63 2014-11-26
  • 打赏
  • 举报
回复
英文字符有基线,中线,底线,顶线,就象过去用的写英文的作业本,字体类中有这些参数,用这些数据应该接近一些。 更精确的,要获得每一个英文字符轮廓占的位置,只有取字符的矢量数据,然后取包围它的矩形,不同的字符轮占的大小不一样。
xiaohuh421 2014-11-26
  • 打赏
  • 举报
回复
引用 4 楼 schlafenhamster 的回复:
GetTextExtentPoint32 function The GetTextExtentPoint32 function computes the width and height of the specified string of text.
这种方法是不行的, 我使用过这种方式. 请看图. 这种方式获取的高度中包括了空白区域, 不仅是文字像素区域. 并且空白区域会随字体的变大, 而变大. 对于显示多行, 显然是不行的
myhaikuotiankong 2014-11-26
  • 打赏
  • 举报
回复
引用 14 楼 xiaohuh421 的回复:
先放一个根据点阵计算文字轮廓的代码的简单实现, 继续等其它方法.

void CalcCharSize(HDC hDC, TCHAR ch, SIZE &siSize, SIZE *pTB)
{
	GetTextExtentPoint32(hDC, &ch, 1, &siSize);
	int iWidth = siSize.cx;
	int iHeight = siSize.cy;

	//设备无关位图
	CONST int BIT_COUNT24 = 24;
	BITMAPINFO bmpInfo={0};
	int bmpWidthBytes =0;
	bmpWidthBytes= iWidth*BIT_COUNT24;//4字节对齐,位图每一行数据的大小.
	bmpWidthBytes+=31;
	bmpWidthBytes/=32;
	bmpWidthBytes*=4;
	ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
	bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	bmpInfo.bmiHeader.biBitCount=BIT_COUNT24;
	bmpInfo.bmiHeader.biCompression=BI_RGB;
	bmpInfo.bmiHeader.biPlanes=1;
	bmpInfo.bmiHeader.biWidth=iWidth;
	bmpInfo.bmiHeader.biHeight=-iHeight;
	bmpInfo.bmiHeader.biSizeImage = bmpWidthBytes * iHeight;
	BYTE *pBmpBits=NULL;
	HBITMAP hBmp=::CreateDIBSection(NULL,&bmpInfo,0,(void**)&pBmpBits,NULL,0);

	HDC hMemDC = CreateCompatibleDC(hDC);
	HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hBmp);

	SetBkMode(hMemDC, TRANSPARENT);
	//设置目标颜色, 方便下面计算
	SetTextColor(hMemDC, RGB(255,255,255));
	//统一字体
	HFONT hFont = (HFONT)::GetCurrentObject(hDC, OBJ_FONT);
	HFONT hOldFont = (HFONT)::SelectObject(hMemDC, hFont); 
	RECT rcRect = {0,0,siSize.cx, siSize.cy};
	DrawText(hMemDC, &ch, 1, &rcRect, DT_LEFT|DT_TOP);
	::SelectObject(hMemDC, hOldFont);

	//找top
	int top = 0;
	for(int y=0; y<iHeight; ++y)
	{
		for(int x=0; x<iWidth; ++x)
		{
			if(pBmpBits[y*bmpWidthBytes+x*3] > 0)
			{
				top = y;
				y = iHeight;
				break;
			}
		}
	}

	//找bottom
	int bottom = 0;
	for(int y=iHeight-1; y>0; --y)
	{
		for(int x=0; x<iWidth; ++x)
		{
			if(pBmpBits[y*bmpWidthBytes+3*x] > 0)
			{
				bottom = y;
				y = 0;
				break;
			}
		}
	}

	if(pTB)
	{
		pTB->cx = top;
		pTB->cy = siSize.cy-bottom-1;
	}
	siSize.cy = bottom-top+1;
	::SelectObject(hMemDC, hOldBmp);

	::DeleteObject(hBmp);

	::DeleteDC(hMemDC);



}
同意
hhhh63 2014-11-26
  • 打赏
  • 举报
回复
引用 13 楼 xiaohuh421 的回复:
你的代码中哪里是体现 基线,中线,底线,顶线 对齐的呢? DrawText( str, rc, DT_CALCRECT ); 实际测试, 这个计算出来的区域也是包含空白区域的. 而不是文字轮廓区域. 难道真的只有去读取点阵中的文字轮廓?
上面的代码中没有用基线对齐。不知道楼主的实际需求是什么?下面估计几种需求情况说明一下: 1. 如果是做一般的显示和排版,LZ的测试的字符有局限性,test这几个字符都在基线以上,所以下面有空白,请看上图我做的测试,显示 j 时下面就充满了,同理,字符上面的空白,有些字符或汉字也会充满。所以对一般排版用DrawText( str, rc, DT_CALCRECT );就行,取的是最大可能的尺寸,象“一”字很小,“簧”字很大,即使取一的尺寸,也按最大的取。 2. 如果需求是一个一个字符精确排版,上下左右不留缝,那就只有取每个字符的点阵或矢量数据了,dc.GetGlyphOutline,象AV两字可以更紧。 3. 有一个变通方法,判断如果都是testabcd这样上半部分的字符,就把下面的内容提高一些,把空白盖上,简单实用。 说了这么多,也不知道有没有用,祝好运
dvlinker 2014-11-24
  • 打赏
  • 举报
回复
可以参考“MeasureString测量文字注意事项 ”:http://blog.csdn.net/cometnet/article/details/37992627
加载更多回复(5)

15,980

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧