轉自:http://blog.csdn.net/harvic880925/article/details/9097319
字體、字體系列基本概念與構造
字體系列
GDI+中將具有相同字樣、包括不同風格的字體稱爲字體系列。字體從其系列中獲得名稱、重量(如粗體、規則、加亮)以及風格。例如Arial字體系列包含了下列4種字體:
Arial Regular(規則)、Arial Bold(黑體)、Arial Italic(斜體)、Arial Bold Italic(粗斜體);
在GDI+中輸出文本之前,需要構造一個FontFamily對象和一個Font對象。FontFamily類的構造函數如下:
-
FontFamily()
-
FontFamily(name, fontCollection)
name:字體系列名。
fontCollection:指向一個FontCollection對象(字體集),表明字體系列所屬的私有字體集,默認爲NULL
示例(定義一個Arial Regular字體對象font)
-
FontFamily fontFamily(L"Arial");
-
Font font(&fontFamily,16,FontStyleRegular,UnitPixel);
看幾個結構類型
1、LOGFONTA和LOGFONTW
兩個都是一樣的,只是一個用於ASCII編碼,一個用於UNICODE編碼
字體大小單位枚舉(Unit)
-
enum Unit{
-
UnitWorld,
-
UnitDisplay,
-
UnitPixel,
-
UnitPoint,
-
UnitInch,
-
UnitDocument,
-
UnitMillimeter
-
};
字體風格枚舉(FontStyle)
-
enum FontStyle{
-
FontStyleRegular = 0,
-
FontStyleBold = 1,
-
FontStyleItalic = 2,
-
FontStyleBoldItalic = 3,
-
FontStyleUnderline = 4,
-
FontStyleStrikeout = 8
-
};
注意:在這幾個樣式中,常規、加粗、傾斜、粗斜不能同時使用。下劃線和強調線風格可以同時使用,也就是說,字體風格由兩部分組成,一部分與字體外形有關,另一部分是附加的線條風格(下劃線和強調線),這兩部分可以配合使用組成字體的整體風格。
例如,要描述一個帶下劃線和強調線的粗斜體風格的字體,可以使用下面的組合來完成:
-
FontStyleBoldItalic |FontStyleUnderline |FontStyleStrikeout
列舉系統目前安裝的字體信息
我們可以通過InstalledFontCollection類的成員函數GetFamilies枚舉安裝在計算機上的所有字體。GetFamilies函數返回的是一個FontFamily對象的數組。所以,在使用GetFamilies前,應該爲FontFamily數組分配足夠的內存空間。GetFamilies函數:
-
Status GetFamilies(
-
INT numSought,
-
FontFamily* gpfamilies,
-
INT* numFound
-
) const;
numSought:[in]字體集中的字體系列總數,通過InstalledFontCollection::GetFamilyCount()獲取
gpfamilies:[out]存儲返回的已安裝的字體系列的數組;
numFound:[out]存儲返回的字體系列總數,數值與numSought一樣;
應用:列舉出系統中的所有字體:
-
SolidBrush solidbrush(Color::Black);
-
FontFamily fontfamily(L"Arial");
-
Font font(&fontfamily,8,FontStyleRegular,UnitPoint);
-
-
int count=0;
-
int found=0;
-
-
WCHAR familyName[100];
-
WCHAR *familyList=NULL;
-
FontFamily pFontFamily[500];
-
-
CRect rect;
-
this->GetClientRect(&rect);
-
RectF r(0,0,rect.Width(),rect.Height());
-
-
InstalledFontCollection installedFontCollection;
-
count=installedFontCollection.GetFamilyCount();
-
installedFontCollection.GetFamilies(count,pFontFamily,&found);
-
-
familyList=new WCHAR[count*sizeof(familyName)];
-
wcscpy(familyList,L"");
-
for(int j=0;j<count;j++){
-
pFontFamily[j].GetFamilyName(familyName);
-
wcscat(familyList,familyName);
-
wcscat(familyList,L",");
-
}
-
graphics.DrawString(familyList,-1,&font,r,NULL,&solidbrush);
-
-
delete[] familyList;
注意:wcscpy\wcscat函數分別實現了對雙字節字符(寬字符)的複製、添加操作,在ASCII碼中對應strcpy、strcat;
字體邊沿平滑處理
大家都知道,當字體過大時,就會出現失真,邊緣會出現鋸齒,GDI+對字體邊緣有一套自己的處理方式,是通過SetTextRenderingHint來實現的;
-
Status SetTextRenderingHint(
-
TextRenderingHint newMode
-
);
其中TextRenderingHint是個枚舉類,枚舉了幾種處理邊緣的方式
-
enum TextRenderingHint{
-
TextRenderingHintSystemDefault = 0,
-
TextRenderingHintSingleBitPerPixelGridFit,
-
TextRenderingHintSingleBitPerPixel,
-
TextRenderingHintAntiAliasGridFit,
-
TextRenderingHintAntiAlias,
-
TextRenderingHintClearTypeGridFit
-
};
看示例:
-
FontFamily fontfamily(L"宋體");
-
Font font(&fontfamily,60,FontStyleRegular,UnitPixel);
-
-
-
graphics.SetTextRenderingHint(TextRenderingHintSystemDefault);
-
graphics.DrawString(L"什麼玩意",-1,&font,PointF(0,0),&SolidBrush(Color::Green));
-
-
-
graphics.TranslateTransform(0,font.GetHeight(0.0f));
-
graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixelGridFit );
-
graphics.DrawString(L"什麼玩意",-1,&font,PointF(0,0),&SolidBrush(Color::Green));
-
-
-
graphics.TranslateTransform(0,font.GetHeight(0.0f));
-
graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixel );
-
graphics.DrawString(L"什麼玩意",-1,&font,PointF(0,0),&SolidBrush(Color::Green));
-
-
-
graphics.TranslateTransform(0,font.GetHeight(0.0f));
-
graphics.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit );
-
graphics.DrawString(L"什麼玩意",-1,&font,PointF(0,0),&SolidBrush(Color::Green));
-
-
-
graphics.TranslateTransform(0,font.GetHeight(0.0f));
-
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias );
-
graphics.DrawString(L"什麼玩意",-1,&font,PointF(0,0),&SolidBrush(Color::Green));
-
-
-
graphics.TranslateTransform(0,font.GetHeight(0.0f));
-
graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit );
-
graphics.DrawString(L"什麼玩意",-1,&font,PointF(0,0),&SolidBrush(Color::Green));
從上面可以看出,列出的這六種邊緣處理方式中,最後一種枚舉成員TextRenderingHintClearTypeGridFit的處理效果最好。這種處理方式使用了Microsoft特有的ClearType技術。是專在液晶顯示器上使用的一種增強字體清晰度的技術。但這種技術有時會出現問題,用投影儀投射到白色牆壁上,會出出字體顯示不正常的情況,而且對於老式的CRT顯示器是根本不支持的。操作系統也必須是XP及以上的系統才行。所以一般用第一個參數,與系統處理方式相同就好。讓系統決定我們適合哪種技術。
FontFamily和Font
在GDI+中輸出文本之前,需要構造一個FontFamily對象和一個Font對象。FontFamily類的構造函數如下:
-
FontFamily()
-
FontFamily(name, fontCollection)
name:字體系列名。
fontCollection:指向一個FontCollection對象(字體集),表明字體系列所屬的字體集,默認爲NULL
再看看Font類的構造函數(先看這四個):
-
Font(hdc)
-
Font(HDC hdc, LOGFONTA* logfont)
-
Font(HDC hdc, LOGFONTW* logfont)
-
Font(hdc, hfont)
這四個都是GDI中的構造函數,這裏就全不講了,只說說最後兩個純正的GDI+中的Font構造函數
-
Font(family, emSize, style, unit) ;
-
Font(familyName, emSize, style, unit, fontCollection) ;
family、familyName:fontFamily對象
style:字體樣式,是規則、粗體、斜體、粗斜體、下劃線、強調線的枚舉類,我們上面說過了
unit:字體大小
fontCollection:字體所屬的字體集,這個是用來添加私有字體集的,下面我們將講私有字體集。
構造私有字體集(PrivateFontCollection)
我們平時使用的字體集,只有在系統中註冊過才能直接調用,但當我們使用一個別人系統中可能不存在的字體集時,我們可以從我們的字體文件中加載這個字體集,然後給用戶使用,從文件中加載系統中沒有註冊的字體集的過程,就是創建私有字體集的過程。創建私有字體集用PrivateFontCollection類來實現,這裏就不講了,想了解的話,看《精通GDI+編程》P139。
Font的構造函數中的最後一個參數fontCollection就是從文件中加載的私有字體集;
字體尺寸、字體系列尺寸
字體尺寸和字體系統尺寸不是同一個概念,我們先看一下他們的共同部分,字體在幾個距離上的概念:
幾個概念:
基線:在英文中,會有基線的概念,這是個抽象的概念,所以GDI+並不支持對基線的設置,是指在四線格中的第三條線,這條線把字母分爲上部和下部,如圖中所示。
上部:是指從基線到上行字母頂部之間的距離。
下部:是從基線到下行字母底部的距離。
行距:是指上下兩行基線之間的距離,包括基線之上的上部值、基線之下的下部值以及在上部之外的一些額外空間(通常用於形成行間距,對於這部分空間,GDI+也不提供函數直接訪問操作。)
字體系列的尺寸(FontFamily)
字體系列尺寸,與實際顯示的字體大小無關,它只與設計時的尺寸有關,獲取函數有:
-
FontFamily::GetEmHeight(style)
-
FontFamily::GetCellAscent(style)
-
FontFamily::GetCellDescent(style)
-
FontFamily::GetLineSpacing(style)
它們返回的並不是一個具體的物理尺寸,這些返回值並不被轉換成具體的長度值。這些函數返回的只是一個抽像的大小,在GDI+中被稱爲設計單位(Desin Units)。對於一個設計單位,它的大小是固定的,它不會隨着字體大小的改變而改變。從名稱上就可以看出,設計單位是字體設計人員在設計字體時所使用的一種相對的座標計量單位。
字體尺寸(Font)
字體尺寸是指在顯示器上實際的尺寸(顯示尺寸),它的操作函數的返回值與字體的大小成正比;獲取函數有:
-
Font::GetHeight(dpi)
-
Font::GetSize();
雖然這裏的API只提供了對字體高度值的獲取,但我們可以通過計算得出上部顯示高度,下部顯示高度、行距;
看獲取上部顯示高度公式,其它都類似;
看代碼示例:
-
SolidBrush solidBrush(Color::Green);
-
PointF pointf(0.0f,0.0f);
-
-
WCHAR infoString[100]=L"";
-
REAL FontHeightPixel;
-
UINT ascent;
-
REAL ascentPixel;
-
UINT desent;
-
REAL descentPixel;
-
UINT lineSpacing;
-
REAL lineSpacingPixel;
-
-
FontFamily fontFamily(L"Arial");
-
Font font(&fontFamily,16,FontStyleRegular,UnitPixel);
-
-
-
swprintf(infoString,L"使用font.GetSize()的返回值爲:%.2fem;",font.GetSize());
-
graphics.DrawString(infoString,wcslen(infoString),&font,pointf,&solidBrush);
-
-
pointf.Y+=font.GetHeight(0.0);
-
swprintf(infoString,L"使用font.GetHeight()的返回值爲:%.2f個像素",font.GetHeight(0.0));
-
graphics.DrawString(infoString,wcslen(infoString),&font,pointf,&solidBrush);
-
-
pointf.Y+=font.GetHeight(0.0);
-
swprintf(infoString,L"使用fontFamily.GetEmHeight()返回的字體高度爲:%d個設計單位。",fontFamily.GetEmHeight(FontStyleRegular));
-
graphics.DrawString(infoString,-1,&font,pointf,&solidBrush);
-
-
pointf.Y+=2.0f*font.GetHeight(0.0f);
-
ascent=fontFamily.GetCellAscent(FontStyleRegular);
-
ascentPixel=font.GetSize()*ascent/fontFamily.GetEmHeight(FontStyleRegular);
-
swprintf(infoString,L"上部距離爲%d個設計單位,%.2f像素。",ascent,ascentPixel);
-
graphics.DrawString(infoString,-1,&font,pointf,&solidBrush);
-
-
pointf.Y+=font.GetHeight(0.0f);
-
desent=fontFamily.GetCellDescent(FontStyleRegular);
-
descentPixel=font.GetSize()*desent/fontFamily.GetEmHeight(FontStyleRegular);
-
swprintf(infoString,L"下部距離爲%d個設計單位,%.2f像素。",desent,descentPixel);
-
graphics.DrawString(infoString,-1,&font,pointf,&solidBrush);
-
-
pointf.Y+=font.GetHeight(0.0f);
-
lineSpacing=fontFamily.GetLineSpacing(FontStyleRegular);
-
lineSpacingPixel=font.GetSize()*lineSpacing/fontFamily.GetEmHeight(FontStyleRegular);
-
swprintf(infoString,L"行距爲%d個設計單位,%.2f像素。",lineSpacing,lineSpacingPixel);
-
graphics.DrawString(infoString,-1,&font,pointf,&solidBrush);
注意:由於在中文中沒有基線的概念,所以本節中提到的文本尺寸不一定適合中文。
寫字
一、測量字符串顯示寬度
在實際繪圖中,有一個問題很值得我們考慮,我們怎麼知道我們指定的RECT區域能否容得下我們要繪製的文字?如何確保我們的文字在所設置的區域內能夠完整的顯示?當然GDI+爲我們提供了測量函數MeasureString,我們看下它的調用格式:
-
MeasureString(string, length, font, layoutRect, boundingBox)
-
MeasureString(string, length, font, layoutRect, stringFormat, boundingBox, codepointsFitted, linesFilled)
-
MeasureString(string, length, font, layoutRectSize, stringFormat, size, codepointsFitted, linesFilled)
-
MeasureString(string, length, font, origin, boundingBox)
-
MeasureString(string, length, font, origin, stringFormat, boundingBox)
參數說明:
string:[in]所在繪製的字符串
length:[in]字符串裏字符個數,-1表示字符串以空白結尾。
font:[in]字符串輸出時使用的字體;
origin:[in]輸出原點
stringFormat: [in]輸出所用格式;
layoutRect、layoutRectSize:[in]指定的文本輸出區域
boundingBox、size:[out]計算後的文本輸出的限制矩形(注意中詳細講解)
linesFilled:[out]計算出的在指定區域中能夠顯示的字符行數;
codepointsFitted:[out]計算出的在指定區域中能夠顯示的字符個數;
注意:boundingBox的計算法則:
1、當layoutRect的width 、height都不爲0時;
boundingBox的值是輸出文本所需要的區域與layoutRect的區域的交集,也就是說,在這種情況下,boundingBox的值始終小於等於layoutRect的值;
2、當layoutRect的width爲0時,height!=0;
boundingBox.height=layoutRect.height;
boundingBox.width的值爲在layoutRect.height;這個高度上,要顯示所有文字所需要的寬度;
3、當layoutRect的width!=0,height==0;
boundingBox.width=layoutRect.width;
boundingBox.height的值爲在layoutRect.width;這個寬度上,要顯示所有文字所需要的高度;
示例:
-
WCHAR string[256]={0};
-
wcscpy(string,L"123456789abcdefg");
-
FontFamily fontfamily(L"Arial");
-
-
Font font1(&fontfamily,30,FontStyleRegular,UnitPixel);
-
Font font2(&fontfamily,18,FontStyleRegular,UnitPixel);
-
-
RectF layoutRect(10,10,130,0);
-
SolidBrush brush(Color::Green);
-
RectF boundRect;
-
INT codePointsFitted=0;
-
INT linesFitted=0;
-
-
int strlen=wcslen(string);
-
graphics.MeasureString(string,strlen,&font1,layoutRect,NULL,&boundRect,&codePointsFitted,&linesFitted);
-
CString tmp;
-
CString s(string);
-
tmp.Format(L"欲輸出的字符串爲\n\"%s\"\n共%d個字符,\n其中,在規定的輸出矩形中,\n只輸出了%d行,共%d個字符",s,s.GetLength(),linesFitted,codePointsFitted);
-
-
graphics.DrawRectangle(&Pen(Color::Red,2),layoutRect);
-
graphics.DrawRectangle(&Pen(Color::Red,2),boundRect);
-
-
graphics.DrawString(string,-1,&font1,boundRect,NULL,&brush);
-
graphics.DrawString(tmp,-1,&font2,PointF(150,10),NULL,&brush);
二、字符串去尾
字符串去尾,是指,當字符串在指定的RECT中無法全部顯示時,如何去文本進行截取,以適應目標區域大小。我們暫時把這種截取操作稱爲“去尾”,去尾方式,是通過StringFormat類中的SetTrimming函數來設置的,其函數格式如下:
-
Status SetTrimming(
-
StringTrimming trimming
-
);
其中StringTrimming是個枚舉類,枚舉了GDI+中所支持的去尾方式;
-
enum StringTrimming{
-
StringTrimmingNone = 0,
-
StringTrimmingCharacter = 1,
-
StringTrimmingWord = 2,
-
StringTrimmingEllipsisCharacter = 3,
-
StringTrimmingEllipsisWord = 4,
-
StringTrimmingEllipsisPath = 5
-
};
看下他們的去尾效果(爲保證效果,把字符串中的six去掉了):
使用方法,代碼示例:
-
SolidBrush solidBrush(Color::Green);
-
FontFamily fontFamily(L"宋體");
-
Font font(&fontFamily,25,FontStyleRegular,UnitPixel);
-
-
Pen pen(Color::YellowGreen,2);
-
-
WCHAR text[]=L"one two three four five seven eight nine ten";
-
-
StringFormat stringformat;
-
stringformat.SetTrimming(StringTrimmingCharacter);
-
graphics.DrawRectangle(&pen,10,30,200,60);
-
graphics.DrawString(text,-1,&font,RectF(10,30,200,60),&stringformat,&solidBrush);
三、格式化文本輸出(StringFormat類)
1、StringFormat::SetFormatFlags()
在使用DrawString函數輸出文本時,可以控制文本輸出的格式。這些輸出格式包括文本對齊方式、文本的輸出方向、文本是否自動換行、使用製表符定製文本列及文本是否剪裁等。這些操作都是使用StringFormat類實現的,先看它其中的一個比較重要的設置文本格式的函數:SetFormatFlags
-
Status SetFormatFlags(
-
INT flags
-
);
其中flags是個枚舉類(可以混合使用),枚舉了幾個格式用於設置,這裏只講解幾個比較常用的,其它就不講解了,如果需要可以查查相關資料;
-
enum StringFormatFlags{
-
StringFormatFlagsDirectionRightToLeft = 0x00000001,
-
StringFormatFlagsDirectionVertical = 0x00000002,
-
StringFormatFlagsNoFitBlackBox = 0x00000004,
-
StringFormatFlagsDisplayFormatControl = 0x00000020,
-
StringFormatFlagsNoFontFallback = 0x00000400,
-
StringFormatFlagsMeasureTrailingSpaces = 0x00000800,
-
StringFormatFlagsNoWrap = 0x00001000,
-
StringFormatFlagsLineLimit = 0x00002000,
-
StringFormatFlagsNoClip = 0x00004000
-
};
看StringFormatFlagsDirectionRightToLeft和StringFormatFlagsDirectionVertical使用示例:
-
CStringW temp(L"人生得意須盡歡,莫使金樽空對月!天生我材必有用,千金散盡還復來。");
-
FontFamily fontfamily(L"幼圓");
-
Font font(&fontfamily,20,FontStyleBold,UnitPixel);
-
SolidBrush brush(Color::Red);
-
-
RectF f(10,10,120,font.GetHeight(0.0f)*8);
-
StringFormat strformat;
-
strformat.SetFormatFlags(StringFormatFlagsDirectionRightToLeft |StringFormatFlagsDirectionVertical );
-
strformat.SetAlignment(StringAlignmentCenter);
-
strformat.SetLineAlignment(StringAlignmentCenter);
-
-
graphics.DrawRectangle(&Pen(Color::Green,2),f);
-
graphics.DrawString(temp,-1,&font,f,&strformat,&brush);
StringFormatFlagsNoClip示例:
-
CStringW temp(L"小白兔,白又白");
-
FontFamily fontfamily(L"幼圓");
-
Font font(&fontfamily,20,FontStyleBold,UnitPixel);
-
SolidBrush brush(Color::Red);
-
StringFormat strformat;
-
-
graphics.DrawRectangle(&Pen(Color::Green,2),RectF(10,10,100,30));
-
graphics.DrawString(temp,-1,&font,RectF(10,10,100,30),&strformat,&brush);
-
-
strformat.SetFormatFlags(StringFormatFlagsNoClip );
-
graphics.DrawRectangle(&Pen(Color::Green,2),RectF(120,10,100,30));
-
graphics.DrawString(temp,-1,&font,RectF(120,10,100,30),&strformat,&brush);
2、設置文本對齊方式(StringFormat::SetAlignment(align))
提起“對齊”,大家可能會馬上聯想到“左對齊”、“右對齊”、“居中”之類的常用對齊方法。GDI+中通過stringFormat類下的下列兩個成員函數來設置文本在水平和垂直方向上的對齊方式:
-
StringFormat::SetAlignment(align)
-
StringFormat::SetLineAlignment(align)
其中align是StringAlignment枚舉類中的成員:
-
enum StringAlignment{
-
StringAlignmentNear = 0,
-
StringAlignmentCenter = 1,
-
StringAlignmentFar = 2
-
};
簡單示例(水平居左,垂直居中):
-
CStringW temp(L"小白兔");
-
FontFamily fontfamily(L"幼圓");
-
Font font(&fontfamily,20,FontStyleBold,UnitPixel);
-
SolidBrush brush(Color::Red);
-
StringFormat strformat;
-
strformat.SetAlignment(StringAlignmentNear);
-
strformat.SetLineAlignment(StringAlignmentCenter );
-
graphics.DrawRectangle(&Pen(Color::Green,2),RectF(10,10,200,130));
-
graphics.DrawString(temp,-1,&font,RectF(10,10,200,130),&strformat,&brush);
最終
由於構建DrawString時用的是brush,這就出現了有趣的地方,由於brush分爲純色畫刷、影線畫刷、紋理畫刷、漸變畫刷,所以我們可以用這幾個畫刷來寫字,寫出的字也具有了畫刷所示的效果,這裏就不再一一列舉了,在《精通GDI+編程》中P178-P182演示了這幾種畫刷寫字的示例,有興趣的可以看看。