windows客戶端開發--如何測量一個字串顯示的物理長度
首先需要說明的是,我所說的字串的長度,不是string的length,也不是string的size。我指的是顯示的長度,即物理長度。
緣由:
之所以要提到這個,是因為遇到了一些問題。
再使用duilib進行開發時,發現label控制元件不能自適應寬度。
思考:
這顯示是這個庫的一個不足,但是我們可以試圖去修改一下這個庫。但是本著開源的精神,或許這個庫的設計初衷就是label不能自適應字串的寬度呢?
所有隻有走第二條路,我們首先獲得要顯示字串的寬度width值,然後把這個label的寬度設定為width。
實現:
現在的問題就是在windows上,如何獲得一個字串的寬度呢?
這個時候一定想到了使用裝置描述表,HDC。
如何獲得DC以及如何釋放DC,這裡就不再贅述了,之前的部落格有過講解。
這個時候你又會問,我怎麼確定一個字串的顯示長度呢?
對於同一個字串,不同大小的字型,顯示也是不一樣的啊。
所以,我們肯定也需要用到字型。
這裡有個函式:
HFONT hFont = CreateFont(27, 0, 0, 0, FW_DONTCARE, 0, 0, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, NULL);
看看這個函式,引數很多:
cHeight是字型的高度。
cWidth是字型的寬度。
cEscapement是字型的傾斜角。
cOrientation是字型的傾斜角。
cWeight是字型的粗細。
bItalic是字型是否斜體。
bUnderline是字型是否有下劃線。
bStrikeOut是字型是否有刪除線。
iCharSet是字型使用的字符集。
iOutPrecision是指定如何選擇合適的字型。
iClipPrecision是用來確定裁剪的精度。
iQuality是怎麼樣跟選擇的字型相符合。
iPitchAndFamily是間距標誌和屬性標誌。
pszFaceName是字型的名稱。
函式功能:該函式建立一種有特殊性的邏輯字型,此邏輯字型可以在後面被任何裝置選擇。
函式原型:HFONT CreateFont(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, DWORD fdwltalic, DWORD fdwUnderline, DWORD fdwStrikeOut, DWORD
fdwCharSet, DWORD fdwOutputPrecision, DWORD fdwClipPrecision, DWORD fdwQuality, DWORD fdwPitchAndFamily, LPCTSTR lpszFace);
引數:
nHeight:指定字型的字元單元或字元的邏輯單位高度,字元的高度值(也被稱為em高度)是指字元單元高度值減去內部標頭值。字型對映器以如下方式解釋nHeight指定的值,各值含義
為:
0:字型對映器轉換這個值以裝置單位,並和已有字型的單元高度相匹配。
0:字型對映器轉換在選擇匹配時用一個預設的高度值。
<0:字型對映器轉換這個值到裝置單位,並將它的絕對值和已有字型的字元高度相匹配。
比較所有的高度,字型對映器選擇不超過要求大小的最大字型。
此對映當字型第一次被使用時發生。
對於MM_TEXT對映方式,可以用下面的公式為一種指定了點大小的字型確定高度:
nHeight=-MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY),72)
nWidth:指定所要求字型的字元的邏輯單位的平均寬度。如果此值為0,字型對映器選擇一個closest match值,closest match值是由比較當前裝置的特徵係數與可使用字型的數字化特徵係數之差的絕對值而確定的。
nEscapement:指定移位向量和裝置X軸之間的一個角度,以十分之一度為單位。移位向量平行於正文行的基線。
Windows NT:當圖形裝置設定為GM_ADVANCED時,可以不依賴字串的字元的定位角而指定字串的移位角。
當圖形模式被設定為GM_COMPATIBLE時,nEscapement同時指定移位角和定位角,可以設定nEscapement和nOrientation為相同的值。
Windows 95:nEscapement同時指定移位角和定位角,可設定nEscapement和nOrientation為相同的值。
nOrientation:指定每個字元的基線和裝置X軸之間的角度。
FnWeight:在0到1000之間指定字型的權值,如400表示標準體,700表示黑(粗)體,如果此值為0,則使用預設的權值。
為方便定義,可使用如下值:
FW_DONTCARE:0;FW_THIN;100;FW_EXTRALIGHT;200;FW_ULTRALIGHT;200;FW_LIGHT;300;
FW_NORMAL:400;FW_REGULAR;400;FW_MEDIUM;500;FW_SEMIBOLD;600;FW_DEMIBOLD;600;
FW_BOLD:700;FW_EXTRABOLD;800;FW_ULTRABOLD;800;FW_HEAVY;900;FW_BLACK;900。
fdwItalic:如果設定為TRUE則指定斜體。
fdwUnderline:如果設定為TRUE,則指定加下劃線的字全。
fdwStrikeOut:如果設定為TRUE,則strikeout指定字型。
fdwCharSet:指定字符集,下列值是預定義的:
ANSI_CHARSET; BALTIC_CHARSET; CHINESEBIG5_CHARSET; DEFAULT_CHARSET;
EASTEUROPE_CHARSET; GB2312_CHARSET; GREEK_CHARSET; HANGUL_CHARSET; MAC_CHARSET; OEM_CHARSET; RUSSIAN_CHARSET; SHIFTJIS_CHARSET;
SYMBOL_CHARSET; TURKISH_CHARSET。
韓國Windows:JOHAB_CHARSET;
中東地區Windows:HEBREW_CHARSSET; ARABIC_CHARSET
泰國Windows:THAI_CHARSET
OEM_CHARSET指定的字符集與作業系統有關。
可以使用DEFAULT_CHARSET值來允許字型的名字和大小來充分描述邏輯字型。如果指定的字型名不存在,任何字符集的字型都可以替代指定的字型,所以應該小心地用DEFAULT_CHARSET來避免不期望的結果出現。
作業系統中存在其他字符集的字型。如果一個應用程式用一種未知字符集的字型,則應用程式不會試圖去翻譯或解釋用那種字型寫出來的字串。
在字型對映過程中此引數很重要。為確保獲得一致的結果,指定一個特殊的字符集。如果在lpszFace引數中指定了一個字型名,確定fdwCharSet值與由lpszFace指定的字型字符集是否匹配。
fdwOutputPrecision:指定輸出精度,輸出精度義輸出與要求的字型高度、寬度、字元定位、移位、字元間距和字元型別的匹配程式,它可取下列值之一:
OUT_CHARACTER_PRECIS;未用。
OUT_DEFAULT_PRECIS:指定預設的字型對映器狀態。
OUT_DEVICE_PRECIS:指示字型對映器在當系統裡有多種字型使用同一個字型使用同一個名字時選擇一種裝置字型。
OUT_OUTLINE_PRCIS:在Windows NT中此值指示字型對映器從TrueType和其他基於邊框的字型中選擇。
OUT_RASTER_PRECIS:指示字型對映器在當系統裡有多種字型使用同一個名字時選擇一種光柵字型。
OUT_STRING_PRECIS:此值沒有被字全對映器使用,但是當掃描字型被列舉時作為返回值。
OUT_STROKE_PRECIS:在Windows NT中此值沒有被字型對映器使用,但是當TrueType字型、其他基於邊框的字型和向量字型被列舉時,作為返回值。
Windows 95:此值沒有被字型對映器使用,但是當TrueType字型或向量字型被列舉時,作為返回值。
OUT_TT_ONLY_PRECIS:指示字型對映器僅從TrueType字型中選擇,如果系統中沒有安裝TrueType字型,則字型對映返回預設狀態。、
OUT_TT_PRECIS:指示字型對映器在當系統裡有多種同名的字型時選擇一種TrueType字型。
當作業系統含有多種與指定名字同名的字型時,應用程式可以使用OUT_DEVICE_PRECIS,OUT_RASTER_PRECIS和OUT_TT_PRECIS值來控制字型對映器如何選擇一種字型,例如,如果作業系統含有名字Symbol的光柵和TrueType兩種字型,指定OUT_TT_PRECIS使字型對映器選擇TrueType方式。指定OUT_TT_ONLY_PRECIS使字型對映器選擇一種TrueType字型,儘管這會給TrueType字型換一個名字。
fdwClipPrecision;指定裁剪精度,裁剪精度定義如何裁剪部分超出裁剪區的字元,它可取一個或多個下列值:
CLIP_DEFAULT_PRECIS:指定預設裁剪狀態。CLIP_CHARACTER_PRECIS:未用。
CLIP_STROKE_PRECIS:未被字型對映器使用,但是當光柵字型、向量字型或TrueType字型被列舉時作為返回值。在Windows環境下,為保證相容性,當列舉字型時這個值總被返回。
CLIP_MASK:未用。CLIP_EMBEDDED:要使用嵌入式只讀字型必須使用此標誌。
CLIP_LH_ANGLES:當此值被使用時,所有字型的旋轉依賴於座標系統的定位是朝左的還是朝右的。
如果未使用此值,裝置字型總是逆時針方向旋轉,但其他字型的旋轉依賴於座標系統的定向。要得到更多關於座標系統定向的資訊,參見引數orientation。
CLIP_TT_ALWAYS:未用。
fdwQuality:指向輸出質量,輸出質量定義GDI如何仔細地將邏輯字型屬性與實際物理字型屬性相匹配。它可取下列值之一:
DEFAULT_QUALITY:字型的外觀不重要。
DRAFT_QUALITY:字型外觀的重要性次於使用PROOF_QUALITY時,對GDI光柵字型,縮放比例是活動的,這意味著多種字型大小可供選擇,但質量可能不高,如果有必要,粗體、斜體、下劃線、strikeout字型可被綜合起來使用。
PROOF_QUALITY:字元質量比精確匹配邏輯字型字型屬性更重要。對GDI掃描字型,縮放比例是活動的,並選擇最接近的大小。儘管當使用PROOF_QUALITY時,選擇字型大小並不完
全匹配,但字型的質量很高,並沒有外觀上的變形。如果有必要,粗體、斜體、下劃線、strikeout字型可被綜合起來使用。
fdwPitchAndFamily:指定字型間距和字型族,低端二位指定字型的字元間距,它可取下列值之一:
DEFAULT_PITCH;FIXED_PITCH; VARIABLE_PITCH
高階四位指定字型族,可取下列值之一:
FF_DECORATIVE:新奇的字型,如老式英語(Old English)。FF_DONTCARE:不關心或不知道。
FF_MDERN:筆劃寬度固定的字型,有或者無襯線。如Pica、Elite和Courier New。
FF_ROMAN:筆劃寬度變動的字型,有襯線。如MS Serif。
FF_SCRIPT:設計成看上去象手寫體的字型。如Script和Cursive。
FF_SWISS:筆劃寬度變動的字型,無斜線。如MS Sans Serif。
應用程式可以用運算子OR將字元間距和字型族組合起來給fdwPitchAndFamily賦值。
字型族描述一種字型的普通外觀,當所有的精確字樣都不能使用時,可用它們來指定字型。
lpszface:指向指定字型的字樣名的、以/0結束的字串指標,字串的長度不能超過32個字元(包括字元/0),函式EnumFontFamilies可用來列舉所有當前可用字型的字樣名。
如果lpszFace為NULL或指向一個空串,GDI使用能匹配其他屬性的第一種字型。
返回值:如果函式呼叫成功,返回值是一種邏輯字型控制代碼;如果函式呼叫失敗,返回值為NULL。
Windows NT:若想獲得更多錯誤資訊,請呼叫GetLastError函式。
備註:當一種字型不再使用時,可用DeleteObject來刪除。
為保護那些提供字型給Windows和Windows NT的賣主的版權,基於Win32的應用程式總是列出所選擇字型的準確名字。由於不同的系統會使用不同的字型,不要認為所選擇字型就是要
求的字型。例如,如果要求名叫Palatino的字型,但系統沒提供那樣一種字型,則字型對映器將會以一種不同名但有相似屬性的字型取而代之。系統總是將使用者選擇的字型名報告出來。
接下來就是利用SelectObject()函式,選入hdc和字型,這裡SelectObject函式也不做過多介紹。
下面就要開始整體了,我們有兩種方法。
1使用函式GetTextExtentPoint32
函式功能:該函式計算指定的正文字串的高度和寬度。
函式原型:BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int cbString, LPSIZE lpSize);
引數:
hdc:裝置環境控制代碼。
lpString:指向正文字串的指標。此字串不必以\0結束,因為cbString指定了字串的長度。
cbString:指向字串中的字元數。
lpSize:指向SIZE結構的指標,該結構中字串的尺寸將被返回。
返回值:如果函式呼叫成功,返回值是非零值,如果函式呼叫失敗,返回值是0。
2使用函式DrawText
函式原型
int DrawText(
HDC hDC, // 裝置描述表控制代碼
LPCTSTR lpString, // 將要繪製的字串
int nCount, // 字串的長度
LPRECT lpRect, // 指向矩形結構RECT的指標
UINT uFormat // 正文的繪製選項
);
引數
hdc:
[輸入]裝置環境控制代碼。
lpString:
[輸入]指向將被寫入的字串的指標,如果引數nCount是-1,則字串必須是以\0結束的。 如果uFormat包含DT_MODIFYSTRING,則函式可為此字串增加4個字元,存放字串的緩衝區必須足夠大,能容納附加的字元。
nCount:
[輸入]指向字串中的字元數。如果nCount為-1,則lpString指向的字串被認為是以\0結束的,DrawText會自動計算字元數。
lpRect:
[輸入/輸出]指向結構RECT的指標,其中包含文字將被置於其中的矩形的資訊(按邏輯座標)。
uFormat:
[輸入]指定格式化文字的方法。它可以下列值的任意組合,各值描述如下:
DT_CALCRECT:這個引數比較重要,可以使DrawText函式計算出輸出文字的尺寸。如果輸出文字有多行,DrawText函式使用lpRect定義的矩形的寬度,並擴充套件矩形的底部以容納輸出文字的最後一行。如果輸出文字只有一行,則DrawText函式改變矩形的右邊界,以容納下正文行的最後一個字元。出現上述任何一種情況,DrawText函式將返回格式化文字的高度,而不是繪製文字。
DT_CENTER:指定文字水平居中顯示。
DT_VCENTER:指定文字垂直居中顯示。該標記只在單行文字輸出時有效,所以它必須與DT_SINGLELINE結合使用。
DT_SINGLELINE:單行顯示文字,回車和換行符都不斷行。
最後,獻上程式碼:
int CalWstringWidth(const std::wstring & name)
{
HDC hDC = ::GetDC(NULL);
HFONT hFont = CreateFont(27, 0, 0, 0, FW_DONTCARE, 0, 0, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, NULL);
SelectObject(hDC, hFont);
LPCTSTR string = name.c_str();
SIZE size = { 0 };
GetTextExtentPoint32(hDC, string, _tcslen(string), &size);
RECT rect = { 0 };
//::DrawText(hDC, string, _tcslen(string), &rect, DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE);
DeleteDC(hDC);
//int str_width = std::abs(rect.right - rect.left);
return size.cx;
}