TrueType字體一般包含在單個TrueType字體文件中,其文件後綴爲.TTF。算法
OpenType字體是以相似於TrueType字體的格式編碼的POSTSCRIPT字體。OPENTYPE字體使用.OTF文件後綴。OPENTYPE還容許把多個OPENTYPE字體組合在一個文件中以利於數據共享。這些字體被稱爲TrueType字體集(TrueType collection),其文件後綴爲.TTC。
TrueType字體用machintosh的輪廓字體資源的格式編碼,有一個惟一的標記名"sfnt"。windows沒有macintosh的位圖字體資源格式,字體目錄包含了字體格式的版本號和幾個表,每一個表都有一個tableentry結構項,tableentry結構包含了資源標記、校驗和、偏移量和每一個表的大小。下面是TrueType字體目錄的c語言定義:windows
typedef sturct { char tag[4]; ULONG checkSum; ULONG offset; ULONG length; }TableEntry; typedef struct { Fixed sfntversion; //0x10000 for version 1.0 USHORT numTables; USHORT searchRange; USHORT entrySelector; USHORT rangeShift; TableEntry entries[1];//variable number of TableEntry }TableDirectory;
TableDirectory結構的最後一個字段是可變長度的tableentry結構的數組,安體中的每一個表對應其中一項。TrueType字體中的每一個表都保存了不一樣的邏輯信息-----如圖元中數據、字符到圖元的映射、字距調整信息等等。有表是必須的,有些是可選的。數組
下表列出了TrueType字體中常見的表。服務器
head | 字體頭 字體的全局信息 |
cmap | 字符代碼到圖元的映射 把字符代碼映射爲圖元索引 |
glyf | 圖元數據 圖元輪廓定義以及網格調整指令 |
maxp | 最大需求表 字體中所需內存分配狀況的彙總數據 |
mmtx | 水平規格 圖元水平規格 |
loca | 位置表索引 把元索引轉換爲圖元的位置 |
name | 命名錶 版權說明、字體名、字體族名、風格名等等 |
hmtx | 水平佈局 字體水平佈局星系:上高、下高、行間距、最大前進寬度、最小左支撐、最小右支撐 |
kerm | 字距調整表 字距調整對的數組 |
post | PostScript信息 全部圖元的PostScript FontInfo目錄項和PostScript名網絡 |
PCLT | PCL 5數據 HP PCL 5Printer Language 的字體信息:字體數、寬度、x高度、風格、記號集等等 |
OS/2 | OS/2和Windows特有的規格 TrueType字體所需的規格集 |
在TableDirectory結構中,全部的TableEntry結構都必須根據它們的標記名排序。好比,cmap必須出如今head前,而head必須在glyf前。可是實際的表能夠出如今TrueType字體文件中的任意位置。
Win32API 提供了一個應用程序可用於查詢原始TrueType字體信息的函數:
DWORD GetFontData(HDC hDC,DWORD dwTable ,DWORD dwOffset, LPVOID lpbBuffer ,DWORD cbData);
GetFontData 函數能夠用於查詢設備上下文中當前邏輯字體所對應的TrueType字體,所以傳遞的不是邏輯字體句柄,而是設備上下文句柄。你能夠查詢整個TrueType文件基是文件中的一個表。要查詢整個文件的話dwTable參數應該爲0;不然,應該傳遞要查詢的表的四字符標記的DWORD格式。參數dwOffset是要查詢的表中的起始偏移,要查詢整個表的話應該爲0;參數 lpbBuffer是緩衝區的地址,cbData是緩衝區的大小。若是最後個參數爲NULL和 0,GetFontData函數返回字體文件或表的大小;就會把到的數據拷貝到應用程序所提供的緩衝區中。
下面的例和查詢整個TrueType字體的原始數據:數據結構
1 TableDirctory * GetTrueTypeFont (HDC hDC ,DWORD &nFontSize) 2 { 3 //query font size 4 nFontSize=GetFontData(hDC,0,0,NULL,0); 5 6 TableDirectory * pFont =(TableDirectory *)new BYTE(nFontSize); 7 if (pFont==NULL) 8 return NULL; 9 GetFontData(hDC,0,0,pFont,nFontSize); 10 11 return pFont; 12 }
GetFontData使得應用程序可以在本身的文檔中內嵌TrueType字體,以確保這些文檔能在沒有相應字體的其餘機器上顯示。它的作法是容許應用程序查詢字體數據,而後寫入到文檔中做爲文檔的一部分,在文檔被打於時再安裝該字體以確保文檔能以建立時一樣的方式顯示。好比,Windows NT/2000的假脫機程序在打印到遠端服務器時會在假脫機文件中內嵌入TrueType字體以保證文檔能在另外一臺機器上正確地打印。
一旦接受到TrueType字體的原始數據,它的頭中的TableDirectory結構很容易分析。須要檢查的只有版本號和表的數目,而後就能夠檢查單個的表。咱們來看一些重要的和有趣的表。less
1.字體頭
字體頭表(head表)中包含了TrueType字體的全局信息。下面是字體頭表的結構。編輯器
typedef sturct { Fixed Table;//x00010000 ro version 1.0 Fixed fontRevision;//Set by font manufacturer. ULONG checkSumAdjustment; ULONG magicNumer; //Set to 0x5f0f3cf5 USHORT flags; USHORT unitsPerEm; //Valid range is from 16 to 16384 longDT created; //International date (8-byte field). longDT modified; //International date (8-byte field). FWord xMin; //For all glyph bounding boxes. FWord yMin; //For all glyph bounding boxes. FWord xMax; //For all glyph bounding boxes. FWord xMax; //For all glyph bounding boxes. USHORT macStyle; USHORT lowestRecPPEM; //Smallest readable size in pixels. SHORT fontDirctionHint; SHORT indexToLocFormat; //0 for short offsets ,1 for long. SHORT glyphDataFormat; //0 for current format. }Table_head;
字體的歷史記錄在三個字段中:字全版本號、字體最初建立時間和字體最後修改時間。有8 個字節用於記錄時間戳,記錄的是從1904年1月1日午夜12:00開始的秒數,所以咱們不用擔憂y2k問題,或是什麼y2m問題。
字體設計時是針對一個參考網格設計的,該網格被稱爲em-square,字體中的圖元用網格中的座標表示。所以em-squrare的大小決定胃該字體的圖元被縮放的方式,同時也反映胃該字體的質量。字體頭中保存了每一個em-square的格數和能包含全部圖元的邊界框。Em-square的有效值是從16到16384,常見的值是204八、4096和8192。好比,Windings字體的em-square的格數是2048,圖元的邊界框是[0,-432,2783,1841]。
字體頭表中的其餘信息包括最小可讀像素大小、字體方向、在位置表中圖元索引的格式和圖元數據格式等等。
最大需求表
TrueType字體是一種很是靈活的數據結構,它能夠包含可變數目的圖元,每一個圖元能夠有不一樣數目的控制點,甚至還能夠有數量可變的圖元指令。最大需求表的目的是告知字體柵格器(rasterizer)對內存的需求,以便在出來字體前分配合適大小的內存。由於性能對字體柵格器很是重要,像MFC的CAarray那樣須要頻繁進行數據拷貝操做的動態增加的數據結構不合要求。下面是maxp表的結構。函數
typedef struct { Fixed Version;//0x00010000 for version 1.0. USHORT numGlypha; //Number of glyphs in the font . USHORT maxPoints; //Max points in noncomposite glyph . RSHORT maxContours; //Max contours in noncomposite glyph. USHORT maxCompositePoints;//Max points in a composite glyph. USHORT maxCompositeContours; //Max contours in a composite glyph. USHORT maxZones;// 1 if not use the twilight zone [Z0],//or 2 if so use Z0;2 in most cases. USHORT max TwilightPoints ;/ Maximum points used in Z0. USHORT maxStorage; //Number of storage area locations. USHORT maxFunctionDefs; //Number of FDEFs. USHORT maxStackElements; //Number of depth. USHORT maxSizeOfInstructions; //Max byte count for glyph inst. USHORT maxComponentElements; //Max number top components refernced. USHORT maxComponentDepth; //Max levels of recursion. }Table_maxp;
numGlyphs字段保存了字體中圖元的總數,這決定了到位置表的圖元索引的數量,能夠用於驗證圖元索引的有效性。TrueType字體中的每一個圖元均可以是合成圖元或簡單圖元。簡單圖元能夠有一條或多條輪廓,套用一些控制點定義。合成圖元用幾個其餘圖元的組合來定義。maxPoints\maxCountors\maxCompositePoints maxCompositeContours這幾個字段說明了圖元定義的複雜度。
除了圖元的定義,TrueType字體還使用了圖元指令用於提示字體掃描器如何對控制點進行調整以獲得更均衡更漂亮的光柵化後的圖元。圖元指令也能夠出如今字體程序表(fpgm表)以及控制值程序表(「prep」)的全局字體層中。TrueType圖元指令是一個僞計算機字節指令,該機相似於Java的虛擬機,這些指令能夠用堆棧計算機執行。MaxStackElements maxSizeOfInstructions兩個字段同志堆棧計算機這些指令的複雜度。
以Windings字體爲例,該字體有226個圖元,圖元最多有47條輪廓線,簡單圖元最多有268個點,合成圖元最多有141個點,合成圖元最多有14條輪廓線,最壞狀況下須要492層堆棧,最長的指令有1119個字節。
字符到圖元索引的映射表(cmap表)定義了從不一樣代碼頁中的字符代碼到圖元索引的映射關係,這是在TrueType字體中存取圖元信息的關鍵。cmap表包含幾個了表以支持不一樣的平臺和不一樣的字符編碼方案。佈局
cmap表
typedef struct { USHORT Platform; //platform ID USHORT EncodingID; //encoding ID ULONG TableOffset; //offset to encoding table ? typedef struct { WCHAR wcLow; USHORT cGlyphs; } typedef struct { DWORD cbThis; //sizeof (GLYPHSET)+sizeof(WCRANGE)+(cRanges-1) DWORD flAccel; DWORD cGlyphsSupported; DWORD cRanges; WCRANGE ranges[1]; //ranges[cRanges] }GLYPHSET; DWORD GetFontUnicodeRanges(HDC hDC,LPGLYPHSET lpgs); DWORD GetGlyphIndices(HDC hDC,LPCTSTR lpstr,int c ,LPWORD pgi,DWORD fl);
一般一種字體只提供UNICODE字符集中的字符的一個子集。這些字符能夠被分組爲多個區域,cmap映射表中就是這麼作的。GetFontUnicodeRanges函數在一個GLYPHSET結構中返回支持的圖元的數量、支持的UNICODE區域的數量以及設備上下文中字體的這些區域的詳細信息。GLYPHSET是一個可變長的結構,其大小取決於所支持的UNICODE區域的數量。所以,和Win32 API中支持可變長結構同樣, GetFontUnicodeRanges函數一般須要調用兩次。第一次調用時獲得以NULL指針做爲最後一莜參數,GDI會返回所需窨的大小。調用者而後分配所需的內存,再次調用以獲得真正的數據。這兩種狀況下,GetFontUnicodeRanges函數都會返回保存整個結構所需的數據大小。MSDN文檔可能仍是錯誤地描述成了若是第二個參數是NULL,GetFontUnicodeRanges函數返回指向GLYPHSET結構的指針。
下面是用於查詢上下文中當前字體GLYPHSET結構的一個簡單函數。
1 GLYPHSET *QueryUnicodeRanges(HDC hDC) 2 { 3 //query for size 4 DWORD size=GetFontUnicodeRanges(hDC,NULL); 5 6 if (size==0) return NULL; 7 GLYPHSET *pGlyphSet=(GLYPHSET *)new BYTE(size); 8 9 //get real data 10 pGlyphSet->cbThis=size; 11 size=GetFontUnicodeRanges(hDC,pGlyphSet); 12 13 return pGlyphSet; 14 }
若是在一些Windows TrueType字體上試着調用GetFontUnicodeRanges函數,你會發現這些字體一般支持1000個以上的圖元,這些圖元被分紅幾百個UNICODE區域。好比,「Times New Roman」有我143個圖元,分佈在145個區域中,和一個區域是0x20到0x7f,便可打印的7位ASCII代碼區域。
GetFontUnicodeRanges函數只使用了TrueType字體「cmap」表的一部分部分信息,即從UNICODE到圖元索引的映射域。GetGlyphIndices函數則能真正使用這些映射關係把一個字符串轉換爲一個圖元索引的數組。它接收一個設備上下文句柄、一個字符串指針、字符串長度、一個WORD數組的指針和一個標誌。生成的圖元索引將保存在WORD數組中。若是標誌爲GGI_MASK_NONEXISTING_GLYPHS,找不到的字符的圖元索引會被標註成0xFFFF。此函數獲得的圖元索引能夠傳給其餘GDI函數,如ExtTextOut函數。
2.位置索引
TrueType字體中最有用的信息是glyf表中的圖元數據。有了圖元索引,要找到相應的圖元,須要表(loca表)索引以把圖元索引轉換爲圖元數據表內的偏移量。
位置索引表中保存了n+1個圖元數據表的索引,其中n是保存在最大需求表中的圖元數量。最後一個額外的偏移量並不指向一個新圖元,而是指向最後一個圖元的偏移量和當前圖元的偏移量和當前圖元的偏移量間的差值獲得圖元的長度。
位置索引表中的每個索引以無符號短整數對齊的,若是使用了短整數格式,索引表實際存儲的是WORD偏移量,而不是BYTE偏移量。這合得短整數格式的位置索引表能支持128KB大小的圖元數據表。
3.圖元數據
圖元數據(glyf表)是TrueType字體的核心信息,所以一般它是最大的表。由於的位置索引是一張單獨的表,圖元數據表就徹底只是圖元的序列而已,每一個圖元以圖元頭結構開始:
1 typedef struct 2 { 3 WORD numberOfContours; //contor number,negative if composite 4 FWord xMin; //Minimum x for coordinate data. 5 FWord yMin; //Minimum y for coordinate data. 6 FWord xMax; //Maximum x for coordinate data. 7 FWord yMax; //Maximum y for coordinate data. 8 }GlyphHeader;
對於簡單圖元,numberOfContours字段中保存的是當前圖元的輪廓線的數量;對於合成圖元,numberOfContours字段是一個負值。後者的輪廓線的總數必須基於組成該合成圖元的全部圖元的數據計算獲得。GlyphHeader結構中後四個字段記錄了圖元的邊界框。
對於簡單圖元,圖元的描述緊跟在 GlyphHeader 結構以後。圖元的描述由幾部分信息組成:全部輪廓線結束點的索引、圖元指令和一系列的控制點。每一個控制點包括一個標誌以 x 和 y 座標。概念上而言,控制所需的信息和 GDI 函數 PolyDraw 函數所需的信息相同:一組標誌和一組點的座標。但 TrueType 字體中的控制點的編碼要複雜得多。下面是圖元描述信息的概述:
USHORT endPtsOfContours[n]; //n=number of contours USHORT instructionlength; BYTE instruction[i]; //i = instruction length BYTE flags[]; //variable size BYTE xCoordinates[]; //variable size BYTE yCoordinates[]; //variable size
圖元能夠包含一條或多條輪廓線。好比,字母"O"有兩條輪廓線,一條是內部的輪廓,另外一條是外部的輪廓。對於每一條輪廓線,endPtsOfContours數組保存了其終點的索引,從該索引中能夠計算出輪廓線中點的數量。好比,endPtsOfContours[0]是第一休輪廓線上點的數量,endPtsOfContours[1] - endPtsOfContours[0] 是第二條輪廓線上點的數量。
終點數組後是圖元指令通知度和圖元指令數組。咱們先跳過它們,先來討論控制點。圖元的控制點保存在三個數組中:標誌符數組、x 座標數組和 y 座標數組。找到標誌數組的起始點很簡單,可是標誌數組沒有相應的長度字,也沒有直接其餘兩個數組的方法,你必須先解碼標誌符數組才能解釋 x 和 y 座標數組。
咱們提到em-square被限制爲最大爲16384個網格,所以一般狀況下須要各兩個字節來表示 x 座標和 y 座標。爲了節省空間,圖元中保存的是相對座標。第一個點的座標是相對(0,0)記錄的,全部隨後的點記錄者是和上一個點的座標差值。有些差值能夠用一個字節表示,有些差值爲0,另一些差值則沒法用單個字節表示。標誌符數組保存了每一個座標的編碼信息以及其餘一些信息。下面是標誌中各個位的含義的總結:
typedef enum { G_ONCURVE = 0x01, // on curve ,off curve G_REPEAT = 0x08, //next byte is flag repeat count G_XMASK = 0x12, G_XADDBYTE = 0x12, //X is positive byte G_XSUBBYTE = 0x12, //X is negative byte G_XSAME = 0x10, //X is same G_XADDINT = 0x00, //X is signed word G_YMASK = 0x24, G_YADDBYTE = 0x24, //Y is positive byte G_YSUBBYTE = 0x04, //Y is negative byte G_YSAME = 0x20, //Y is same G_YADDINT = 0x00, //Y is signed word };
在第8章中咱們討論了直線和曲線,咱們提到了一段三階Bezier曲線有四個控制點定義:位於曲線上(on-curve)的起始點、兩個不在曲線上(off-curve)的控制點和一個曲線上的結束點。TureType字體中的圖元輪廓是用二階Bezier曲線定義的,有三個點:一個曲線上的點,一個曲線外的點和另外一個曲線上的點。多個連續的不在曲線上的點是容許的,但不是用來定義三階或更高階的Bezier曲線,而是爲了減小控制點的數目。好比,對於on-off-off-on模式的四個點,會加入一個隱含的點使之成爲on-off-on-off-on,所以定義的是兩段二階Bezier曲線。
若是設置了G_ONCURVE位,那麼控制點在曲線上,不然不在曲線上。
若是設置了G_REPEAT,標誌數組中的下一字節表示重複次數,當前標誌應該重複指定的次數。所以,標誌數組中實際使用了某種類型的行程編碼。
標誌中的其餘位用於描述相應的x座標和y座標的編碼方式,它們能夠表示當前相尋座標是否和上一個相同、正的單字節值、負的單字節值或有符號兩字節值。
解碼圖元的描述是一個兩次掃描的起始點。而後再遍歷圖元定義中的每個點把它轉換爲更容易管理的格式。程序清單14-2列出瞭解碼TrueType圖元的函數,它是KTrueType類的一個方法。
1 int KTrueType::DecodeGlyph(int index, KCurve& curve, XFORM* xm) const 2 { 3 const GlyphHeader* pHeader = GetGlyph(index); 4 if (pHeader == NULL) 5 { 6 // assert(false); 7 return 0; 8 } 9 10 int nContour = (short)reverse(pHeader->numberOfContours); 11 if (nContour < 0) 12 { 13 return DecodeCompositeGlyph(pHeader+1, curve); // after the header 14 } 15 16 if (nContour == 0) 17 return 0; 18 19 curve.SetBound(reverse((WORD)pHeader->xMin), reverse((WORD)pHeader->yMin), 20 reverse((WORD)pHeader->xMax), reverse((WORD)pHeader->yMax)); 21 22 const USHORT* pEndPoint = (const USHORT *) (pHeader+1); 23 24 int nPoints = reverse(pEndPoint[nContour-1]) + 1; // endpoint of last contour + 1 25 int nInst = reverse(pEndPoint[nContour]); // instructon length 26 27 curve.m_glyphindex = index; 28 curve.m_glyphsize = (int) GetGlyph(index+1) - (int) GetGlyph(index); 29 curve.m_Ascender = m_Ascender; 30 curve.m_Descender = m_Descender; 31 curve.m_LineGap = m_LineGap; 32 33 GetMetrics(index, curve.m_advancewidth, curve.m_lsb); 34 35 if ( curve.m_glyphsize==0 ) 36 return 0; 37 38 curve.m_instrsize = nInst; 39 40 const BYTE * pFlag = (const BYTE *) & pEndPoint[nContour] + 2 + nInst; // first byte in flag 41 const BYTE * pX = pFlag; 42 43 int xlen = 0; 44 45 for (int i=0; i<nPoints; i++, pX++) 46 { 47 int unit = 0; 48 49 switch ( pX[0] & G_XMASK ) 50 { 51 case G_XADDBYTE: 52 case G_XSUBBYTE: 53 unit = 1; 54 break; 55 56 case G_XADDINT: 57 unit = 2; 58 } 59 60 if ( pX[0] & G_REPEAT ) 61 { 62 xlen += unit * (pX[1]+1); 63 64 i += pX[1]; 65 pX ++; 66 } 67 else 68 xlen += unit; 69 } 70 71 const BYTE * pY = pX + xlen; 72 73 int x = 0; 74 }
KTrueType類處理TrueType字體的加載和解碼,隨書光盤中有它的完整源代碼。DecodeGlyph給出圖元索引和可選的變換矩陣,處理的是單個圖元的解碼。參數curve是KCurve類,用於把TrueType圖元定義保存爲32位的點的數組以及一個標誌數組,用GDI進行顯示。這些代碼能夠做爲簡單TrueType字體編輯器的基礎。
代碼中調用了GetGlyph方法,該方法用位置表索引找到該圖元的GlyphHeader結構。從中獲得圖元的輪廓線數目。注意必須反轉該值的字節序,由於TrueType字體用的是Big-Endian字節序。若是該值爲負值,說明這是一個合成圖元,應該轉而調用DecodeCompositeGlyph方法。接下支的代碼定位了endPtsOfContours數組,找出點的總數,而後跳過指令找到標誌數組的起始位置。
接下去須要講到的是x座標數組的始位置和長度,這須要遍歷標誌數組一次。對於每個控制點,它在x座標數組中所佔空間可能爲0到2個字節,這取決於它的相對座標是0、單個字節仍是兩個字節。
根據x座標數組的地址和長度能夠獲得y座標的地址。接下去的代碼遍歷全部的輪廓線,解碼其中的控制點,把相對座標轉換爲絕對座標,而後把它加入到曲線對象中。若是須要的話,會對每一個控制點作變換。
回想一下,TrueType使用的是二階Bezier曲線,容許在兩個曲線上的點之間有多個不在曲線上的點。爲了簡化曲線繪製算法,KCurve::Add方法在每兩個不在曲線上的點之間加入一個額外的在曲線上的點。
處理了簡單圖元以後,咱們來看看合成圖元。合成圖元用一個經變換的圖元序列定義。每一個經變換的圖元的定義包括三個部分:一個標誌、一個圖元索引和一個變換矩陣。標誌字段決定了變換矩陣的編碼方式。編碼的目的也是爲了節省一些空間,加外還說明了是否已到達序列的終點。一個完整的2D affine變換須要6個值。但若是隻是平移的話,只須要兩個值(dx, dy),這兩個值能夠保存爲兩個字節或兩個字。若是 x 和 y 以相同的值縮放,此外還須要一個縮放值。取通常的狀況下仍然須要6個值,可是不少時候能夠節省幾個字節。用於變換的值以2.14的有符號定點格式保存,dx 和 dy 值除外,這兩個值以整數形式保存。獲得合成圖元的過程其實是變換和組合幾個圖元的過程。好比,若是字體中的一個圖元是另外一個圖元的精確鏡像,它只需定義爲一個合成圖元,能夠經過對另外一個圖像作鏡像變換便可。程序清單14-3列出瞭解碼合成圖元的代碼。
1 int KTrueType::DecodeCompositeGlyph(const void* pGlyph, KCurve& curve) const 2 { 3 KDataStream str(pGlyph); 4 unsigned flags; 5 int len = 0; 6 7 do 8 { 9 flags = str.GetWord(); 10 11 unsigned glyphIndex = str.GetWord(); 12 13 // Argument1 and argument2 can be either x and y offsets to be added to the glyph or two point numbers. 14 // In the latter case, the first point number indicates the point that is to be matched to the new glyph. 15 // The second number indicates the new glyph's "matched" point. Once a glyph is added, its point numbers 16 // begin directly after the last glyphs (endpoint of first glyph + 1). 17 18 // When arguments 1 and 2 are an x and a y offset instead of points and the bit ROUND_XY_TO_GRID is set to 1, 19 // the values are rounded to those of the closest grid lines before they are added to the glyph. 20 // X and Y offsets are described in FUnits. 21 22 signed short argument1; 23 signed short argument2; 24 25 if (flags & ARG_1_AND_2_ARE_WORDS) 26 { 27 argument1 = str.GetWord(); // (SHORT or FWord) argument1; 28 argument2 = str.GetWord(); // (SHORT or FWord) argument2; 29 } 30 else 31 { 32 argument1 = (signed char) str.GetByte(); 33 argument2 = (signed char) str.GetByte(); 34 } 35 36 signed short xscale, yscale, scale01, scale10; 37 38 xscale = 1; 39 yscale = 1; 40 scale01 = 0; 41 scale10 = 0; 42 43 if ( flags & WE_HAVE_A_SCALE ) 44 { 45 xscale = str.GetWord(); 46 yscale = xscale; // Format 2.14 47 } 48 else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) 49 { 50 xscale = str.GetWord(); 51 yscale = str.GetWord(); 52 } 53 else if (flags & WE_HAVE_A_TWO_BY_TWO) 54 { 55 xscale = str.GetWord(); 56 scale01 = str.GetWord(); 57 scale10 = str.GetWord(); 58 yscale = str.GetWord(); 59 } 60 61 if (flags & ARGS_ARE_XY_VALUES) 62 { 63 XFORM xm; 64 65 xm.eDx = (float) argument1; 66 xm.eDy = (float) argument2; 67 xm.eM11 = xscale / (float) 16384.0; 68 xm.eM12 = scale01 / (float) 16384.0; 69 xm.eM21 = scale10 / (float) 16384.0; 70 xm.eM22 = yscale / (float) 16384.0; 71 72 len += DecodeGlyph(glyphIndex, curve, & xm); 73 } 74 else 75 assert(false); 76 } 77 while (flags & MORE_COMPONENTS); 78 79 if (flags & WE_HAVE_INSTRUCTIONS) 80 { 81 unsigned numInstr = str.GetWord(); 82 83 for (unsigned i=0; i<numInstr; i++) 84 str.GetByte(); 85 } 86 87 // The purpose of USE_MY_METRICS is to force the lsb and rsb to take on a desired value. 88 // For example, an i-circumflex (Unicode 00ef) is often composed of the circumflex and a dotless-i. 89 // In order to force the composite to have the same metrics as the dotless-i, 90 // set USE_MY_METRICS for the dotless-i component of the composite. Without this bit, 91 // the rsb and lsb would be calculated from the HMTX entry for the composite (or would need to be 92 // explicitly set with TrueType instructions). 93 94 // Note that the behavior of the USE_MY_METRICS operation is undefined for rotated composite components. 95 96 return len; 97 }
對於用三個控制點(p0,p1,p2)定義的二階Bezier曲線,相應的三階Bezier曲線的控制點爲(p0,(p0+2*p1)/3,(2*p1+p2)/3,p2)。
4.圖元指令
程序清單14-2和14-3給人的印象是TrueType字體的柵格器能夠經過掃描和轉換圖元的輪廓來輕鬆地實現,好比,用GDI和StrokeAndFillPath函數來填充圖元輪廓繪製出來的路徑。這種簡單的字體柵格器的實現並非頗有用,除非它只用於高分辨詣的設備如打印機等。
簡單柵格器獲得的圖像筆畫粗細不一,有信息的遺漏,有字符特徵的損失以及不對稱等缺陷。當點陣變小是,狀況不會更糟。總之,簡單字體柵格器在小尺寸時會產生字跡模糊的結果。在大尺寸時會產生很差看的結果,只有在點陣增大時結果纔會改善。
當在大的em-square(典型的是2048)中定義的圖元輪廓縮小爲小得多的網格時(如32*32),不可避免會損失精度並引入偏差。
TrueType解決這個問題的方法是控制圖元輪廓從em-square到柵格網格的縮放過程,使獲得的結果看起來效果更好,和原始圖元的設計儘可能接近。這種技術被稱爲網格調整(grid fitting),它想達到的目標有:
消除網格位置的可能影響,保證筆畫的粗細和網格的相對位置無關。
控制圖元中關鍵位置的尺寸
保持對稱性和襯線等 重要的圖元設計細節。
TrueType字體中網格調整的需求在兩個地方中編碼:控制值表(control value table)和每一個圖元的網格調整指令。
控制值表("cvt"表)用於保存一個數組,這些值被稱爲網格調整指令。好比,對於有襯線的字體,基線、襯線高度、大寫字母筆劃的寬度等值都或以是被控制的值。它們能夠以字體設計者已知的次序保存在控制值表中,而後用它們的索引來引用。在字體光柵化過程當中,控制值表中的值根據點陣的大小縮放。在網絡調整指令中引用這些值能夠保證使用的值與網枸的位置無關。好比,若是水平線[14,0,25,200]能夠用CVT表中的兩個值定義爲[14,0,14+CVT[stem_width],0+CVT[cap_height]],那麼該線的寬度和高度會和所在網格的相對位置無關,保持不變。
每個圖元的定義中附加有一個指令序列,該指令序列被稱爲圖元指令,該背景令序列用於控制此圖元的網格高速。圖元指令線用控制值表中的值,以保證在索引圖元中這些值相同。
圖元指令是一種基於堆棧的僞計算機的指令。堆棧計算機經常使用於計算機語言的解釋性實現。好比,Forth(用於嵌入式系統的一種強大而簡潔的語言)、RPL(HP計算器使用的語言)和Java虛擬機都是堆棧計算機。
堆棧計算機一般沒有寄存器,全部的計算都在堆棧上進行(有些堆棧計算機使用分開的控制堆棧和數據堆棧)。好比,壓入指令把一個值壓入堆棧,彈出指令從堆棧中彈出上面的值,二元加法指令彈出上面的兩個值 ,而後把它們的和壓入堆棧。
轉自: https://blog.csdn.net/g0ose/article/details/81208488