青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

TrueType漢字字庫中的筆劃是怎么存放的


TrueType字體通常包含在單個(gè)TrueType字體文件中,其文件后綴為.TTF。OpenType字體是以類似   于TrueType字體的格式編碼的POSTSCRIPT字體。OPENTYPE字體使用.OTF文件后綴。OPENTYPE還允許把多個(gè)OPENTYPE字體組合在一個(gè)文件中以利于數(shù)據(jù)共享。這些字體被稱為TrueType字體集(TrueType   colleCTion),其文件后綴為.TTC。  
        TrueType字體用machintosh的輪廓字體資源的格式編碼,有一個(gè)唯一的標(biāo)記名"sfnt"。windows沒有macintosh的位圖字體資源格式,字體目錄   包含了字體格式的版本號和幾個(gè)表,每個(gè)表都有一個(gè)tableentry結(jié)構(gòu)項(xiàng),tableentry結(jié)構(gòu)包含了資源標(biāo)記、校驗(yàn)和、偏移量和每個(gè)表的大小。下面是TrueType字體目錄的c語言定義:  
  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;  
   
          TrueType   字體中的所有數(shù)據(jù)都使用big-endian編碼,最高位字節(jié)在最前面(因?yàn)門rueType字體最初是由apple公司定義的,而apple公司的os運(yùn)行在motorola的cpu上)。如果一人TrueType字體以00   01   00   00   ,00   17開頭,我們就可以知道它的格式是輪廓字體資源("sfnt")版本1.0的格式,有23個(gè)表。  
   
          TableDirectory結(jié)構(gòu)的最后一個(gè)字段是可變長度的tableentry結(jié)構(gòu)的數(shù)組,安體中的每個(gè)表對應(yīng)其中一項(xiàng)。TrueType字體中的每個(gè)表都保存了不同的邏輯信息-----如圖元中數(shù)據(jù)、字符到圖元的映射、字距調(diào)整信息等等。有表是必須的,有些是可選的。下表列出了TrueType字體中常見的表。  
     
  head           字體頭                                           字體的全局信息  
  cmap           字符代碼到圖元的映射               把字符代碼映射為圖元索引  
  glyf           圖元數(shù)據(jù)                                       圖元輪廓定義以及網(wǎng)格調(diào)整指令  
  maxp           最大需求表                                   字體中所需內(nèi)存分配情況的匯總數(shù)據(jù)  
  mmtx           水平規(guī)格                                       圖元水平規(guī)格  
  loca           位置表索引                                   把元索引轉(zhuǎn)換為圖元的位置  
  name           命名表                                           版權(quán)說明、字體名、字體族名、風(fēng)格名等等  
  hmtx           水平布局                                       字體水平布局星系:上高、下高、行間距、最大前進(jìn)寬度、最小左支撐、最小右支撐  
  kerm           字距調(diào)整表                                   字距調(diào)整對的數(shù)組  
  post           PostScript信息                           所有圖元的PostScript   FontInfo目錄項(xiàng)和PostScript名  
  PCLT           PCL   5數(shù)據(jù)                                     HP   PCL   5Printer   Language   的字體信息:字體數(shù)、寬度、x高度、風(fēng)格、記號集等等  
  OS/2           OS/2和Windows特有的規(guī)格         TrueType字體所需的規(guī)格集  
          在TableDirectory結(jié)構(gòu)中,所有的TableEntry結(jié)構(gòu)都必須根據(jù)它們的標(biāo)記名排序。比如,cmap必須出現(xiàn)在head前,而head必須在glyf前。但是實(shí)際的表可以出現(xiàn)在TrueType字體文件中的任意位置。  
          Win32API   提供了一個(gè)應(yīng)用程序可用于查詢原始TrueType字體信息的函數(shù):  
  DWORD   GetFontData(HDC   hDC,DWORD   dwTable   ,DWORD   dwOffset,   LPVOID   lpbBuffer   ,DWORD   cbData);  
          GetFontData函數(shù)可以用于查詢設(shè)備上下文中當(dāng)前邏輯字體所對應(yīng)的TrueType字體,因此傳遞的不是邏輯字體句柄,而是設(shè)備上下文句柄。你可以查詢整個(gè)TrueType文件基是文件中的一個(gè)表。要查詢整個(gè)文件的話dwTable參數(shù)應(yīng)該為0;否則,應(yīng)該傳遞要查詢的表的四字符標(biāo)記的DWORD格式。參數(shù)dwOffset是要查詢的表中的起始偏移,要查詢整個(gè)表的話應(yīng)該為0;參數(shù);pvBuffer是緩沖區(qū)的地址,cbData是緩沖區(qū)的大小。如果最后個(gè)參數(shù)為NULL和0,GetFontData函數(shù)返回字體文件或表的大小;就會把到的數(shù)據(jù)拷貝到應(yīng)用程序所提供的緩沖區(qū)中。  
  下面的例和查詢整個(gè)TrueType字體的原始數(shù)據(jù):  
    TableDirctory   *   GetTrueTypeFont   (HDC   hDC   ,DWORD   &nFontSize)  
  {  
  //query   font   size  
  nFontSize=GetFontData(hDC,0,0,NULL,0);  
   
  TableDirectory   *   pFont   =(TableDirectory   *)new   BYTE(nFontSize);  
  if   (pFont==NULL)  
  return     NULL;  
  GetFontData(hDC,0,0,pFont,nFontSize);  
   
  return   pFont;  
  }  
          GetFontData使得應(yīng)用程序能夠在自己的文檔中內(nèi)嵌TrueType字體,以確保這些文檔能在沒有相應(yīng)字體的其他機(jī)器上顯示。它的做法是允許應(yīng)用程序查詢字體數(shù)據(jù),然后寫入到文檔中作為文檔的一部分,在文檔被打于時(shí)再安裝該字體以確保文檔能以創(chuàng)建時(shí)同樣的方式顯示。比如,Windows   NT/2000的假脫機(jī)程序在打印到遠(yuǎn)端服務(wù)器時(shí)會在假脫機(jī)文件中內(nèi)嵌入TrueType字體以保證文檔能在另一臺機(jī)器上正確地打印。  
  一旦接受到TrueType字體的原始數(shù)據(jù),它的頭中的TableDirectory結(jié)構(gòu)很容易分析。需要檢查的只有版本號和表的數(shù)目,然后就可以檢查單個(gè)的表。我們來看一些重要的和有趣的表。  
  1.字體頭  
      字體頭表(head表)中包含了TrueType字體的全局信息。下面是字體頭表的結(jié)構(gòu)。  
  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;  
   
          字體的歷史記錄在三個(gè)字段中:字全版本號、字體最初創(chuàng)建時(shí)間和字體最后修改時(shí)間。有8   個(gè)字節(jié)用于記錄時(shí)間戳,記錄的是從1904年1月1日午夜12:00開始的秒數(shù),因此我們不用擔(dān)心y2k問題,或是什么y2m問題。  
          字體設(shè)計(jì)時(shí)是針對一個(gè)參考網(wǎng)格設(shè)計(jì)的,該網(wǎng)格被稱為em-square,字體中的圖元用網(wǎng)格中的坐標(biāo)表示。因此em-squrare的大小決定胃該字體的圖元被縮放的方式,同時(shí)也反映胃該字體的質(zhì)量。字體頭中保存了每個(gè)em-square的格數(shù)和能   包含所有圖元的邊界框。Em-square的有效值是從16到16384,常見的值是2048、4096和8192。比如,Windings字體的em-square的格數(shù)是2048,圖元的邊界框是[0,-432,2783,1841]。  
          字體頭表中的其他信息包括最小可讀像素大小、字體方向、在位置表中圖元索引的格式和圖元數(shù)據(jù)格式等等。  
  最大需求表  
          TrueType字體是一種非常靈活的數(shù)據(jù)結(jié)構(gòu),它可以包含可變數(shù)目的圖元,每個(gè)圖元可以有不同數(shù)目的控制點(diǎn),甚至還可以有數(shù)量可變的圖元指令。最大需求表的目的是告知字體柵格器(rasterizer)對內(nèi)存的需求,以便   在出來字體前分配合適大小的內(nèi)存。因?yàn)樾阅軐ψ煮w柵格器非常重要,像MFC的CAarray那樣需要頻繁進(jìn)行數(shù)據(jù)拷貝操作的動態(tài)增長的數(shù)據(jù)結(jié)構(gòu)不合要求。下面是maxp表的結(jié)構(gòu)。  
  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字段保存了字體中圖元的總數(shù),這決定了到位置表的圖元索引的數(shù)量,可以用于嚴(yán)正圖元索引的有效性。TrueType字體中的每個(gè)圖元都可以是合成圖元或簡單圖元。簡單圖元可以有一條或多大體上輪廓中國,條用一些控制點(diǎn)定義。合成圖元用幾個(gè)其他圖元的組合來定義。maxPoints\maxCountors\maxCompositePoints   maxCompositeContours這幾個(gè)字段說明了圖元定義的復(fù)雜度。  
          除了圖元的定義,TrueType字體還使用了圖元指令用于提示字體掃描器如何對控制點(diǎn)進(jìn)行調(diào)整以得到更均衡更漂亮的光柵化后的圖元。圖元指令也可以出現(xiàn)在字體程序表(fpgm表)以及控制值程序表(“prep”)的全局字體層中。TrueType圖元指令是一個(gè)偽計(jì)算機(jī)字節(jié)指令,該機(jī)類似于Java的虛擬機(jī),這些指令可以用堆棧計(jì)算機(jī)執(zhí)行。MaxStackElements   maxSizeOfInstructions兩個(gè)字段同志堆棧計(jì)算機(jī)這些指令的復(fù)雜度。  
          以Windings字體為例,該字體有226個(gè)圖元,圖元最多有47條輪廓線,簡單圖元最多有268個(gè)點(diǎn),合成圖元最多有141個(gè)點(diǎn),合成圖元最多有14條輪廓線,最壞情況下需要492層堆棧,最長的指令有1119個(gè)字節(jié)。  
  字符到圖元索引的映射表(cmap表)定義了從不同代碼頁中的字符   代碼到圖元索引的映射關(guān)系,這是在TrueType字體中存取圖元信息的關(guān)鍵。cmap表包含幾個(gè)了表以支持不同的平臺和不同的字符編碼方案。  
   
  下面是cmap表的結(jié)構(gòu)。  
   
    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字符集中的字符的一個(gè)子集。這些字符可以被分組為多個(gè)區(qū)域,cmap映射表中就是這么做的。GetFontUnicodeRanges函數(shù)在一個(gè)GLYPHSET結(jié)構(gòu)中返回支持的圖元的數(shù)量、支持的UNICODE區(qū)域的數(shù)量以及設(shè)備上下文中字體的這些區(qū)域的詳細(xì)信息。GLYPHSET是一個(gè)可變長的結(jié)構(gòu)   ,其大小取決于所支持的UNICODE區(qū)域的數(shù)量。因此,和Win32   API中支持可變長結(jié)構(gòu)一樣,       GetFontUnicodeRanges函數(shù)通常需要調(diào)用兩   次。第一次調(diào)用時(shí)得到以NULL指針作為最后一莜參數(shù),GDI會返回所需窨的大小。調(diào)用者然后分配所需的內(nèi)存,再次調(diào)用以得到真正的數(shù)據(jù)。這兩   種情況下,GetFontUnicodeRanges函數(shù)都會返回保存整個(gè)結(jié)構(gòu)所需的數(shù)據(jù)大小。MSDN文檔可能還是錯誤地描述成了如果第二個(gè)參數(shù)是NULL,GetFontUnicodeRanges函數(shù)返回指向GLYPHSET結(jié)構(gòu)的指針。  
  下面是用于查詢上下文中當(dāng)前字體GLYPHSET結(jié)構(gòu)的一個(gè)簡單函數(shù)。  
  GLYPHSET   *QueryUnicodeRanges(HDC   hDC)  
  {  
  //query   for   size  
  DWORD   size=GetFontUnicodeRanges(hDC,NULL);  
   
  if   (size==0)   return   NULL;  
  GLYPHSET   *pGlyphSet=(GLYPHSET   *)new   BYTE(size);  
   
  //get   real   data  
  pGlyphSet->cbThis=size;  
  size=GetFontUnicodeRanges(hDC,pGlyphSet);  
   
  return   pGlyphSet;  
  }  
   
   
          如果在一些Windows   TrueType字體上試著調(diào)用GetFontUnicodeRanges函數(shù),你會發(fā)現(xiàn)這些字體通常支持1000個(gè)以上的圖元,這些圖元被分成幾百個(gè)UNICODE區(qū)域。比如,“Times   New   Roman”有我143個(gè)圖元,分布在145個(gè)區(qū)域中,和一個(gè)區(qū)域是0x20到0x7f,即可打印的7位ASCII代碼區(qū)域。  
  GetFontUnicodeRanges函數(shù)只使用了TrueType字體“cmap”表的一部分部分信息,即從UNICODE到圖元索引的映射域。GetGlyphIndices函數(shù)則能真正使用這些映射關(guān)系把一個(gè)字符串轉(zhuǎn)換為一個(gè)圖元索引的數(shù)組。它接收一個(gè)設(shè)備上下文句柄、一個(gè)字符串指針、字符串長度、一個(gè)WORD數(shù)組的指針和一個(gè)標(biāo)志。生成的圖元索引將保存在WORD數(shù)組中。如果標(biāo)志為GGI_MASK_NONEXISTING_GLYPHS,找不到的字符的圖元索引會被標(biāo)注成0xFFFF。此函數(shù)得到的圖元索引可以傳給其他GDI函數(shù),如ExtTextOut函數(shù)。  
   
  2.位置索引  
   
          TrueType字體中最有用的信息是glyf表中的圖元數(shù)據(jù)。有了圖元索引,要找到相應(yīng)的圖元,需要表(loca表)索引以把圖元索引轉(zhuǎn)換為圖元數(shù)據(jù)表內(nèi)的偏移量。  
  位置索引表中保存了n+1個(gè)圖元數(shù)據(jù)表的索引,其中n是保存在最大需求表中的圖元數(shù)量。最后一個(gè)額外   的偏移量并不指向一個(gè)新圖元,而是指向最后一個(gè)圖元的偏移量和當(dāng)前圖元的偏移量和當(dāng)前圖元的偏移量間的差值得到圖元的長度。  
  位置索引表中的每一個(gè)索引以無符號短整數(shù)對齊的,如果使用了短整數(shù)格式,索引表實(shí)際存儲的是WORD偏移量,而不是BYTE偏移量。這合得短整數(shù)格式的位置索引表能   支持128KB大小的圖元數(shù)據(jù)表。  
   
  3.圖元數(shù)據(jù)  
        圖元數(shù)據(jù)(glyf表)是TrueType字體的核心信息,因此通常它是最大的表。因?yàn)榈奈恢盟饕且粡垎为?dú)的表,圖元數(shù)據(jù)表就完全只是圖元的序列而已,每個(gè)圖元以圖元頭結(jié)構(gòu)開始:  
  typedef   struct    
  {  
  WORD   numberOfContours;   //contor   number,negative   if   composite  
  FWord   xMin;       //Minimum   x   for   coordinate   data.  
  FWord   yMin;       //Minimum   y   for   coordinate   data.  
  FWord   xMax;       //Maximum   x   for   coordinate   data.  
  FWord   yMax;       //Maximum   y   for   coordinate   data.  
  }GlyphHeader;  
   
          對于簡單圖元,numberOfContours字段中保存的是當(dāng)前圖元的輪廓線的樹木;對于合成圖元,numberOfContours字段是一個(gè)負(fù)值。后者的輪廓線的總數(shù)必須基于組成該合成圖元的所有圖元的數(shù)據(jù)計(jì)算得到。GlyphHeader結(jié)構(gòu)中后四個(gè)字段記錄了圖元的邊界框。  
        對于簡單圖元,圖元的描述緊跟在GlyphHeader結(jié)構(gòu)之后。圖元的描述由幾部分信息組成:所有輪廓線結(jié)束點(diǎn)的索引、圖元指令和一系列的控制點(diǎn)。每個(gè)控制點(diǎn)包括一個(gè)標(biāo)志以x和y坐標(biāo)。概念上而言,控制所需的信息和GDI函數(shù)PolyDraw函數(shù)所需的信息相同:一組標(biāo)志和一組點(diǎn)的坐標(biāo)。但TrueType字體中的控制點(diǎn)的編碼要復(fù)雜得多。下面是圖元描述信息的概述:  
  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"有兩   條輪廓線,一條是內(nèi)部的輪廓,另一條是外部的輪廓。對于每一條輪廓線,endPtsOfContours數(shù)組保存了其終點(diǎn)的索引,從該索引中可以計(jì)算出輪廓線中點(diǎn)的數(shù)量。比如,endPtsOfContours[0]是第一休輪廓線上點(diǎn)的數(shù)量,endPtsOfContours[1]-endPtsOfContours[0]是第二條輪廓線上點(diǎn)的數(shù)量。  
          終點(diǎn)數(shù)組后是圖元指令通知度和圖元指令數(shù)組。我們先跳過它們,先來討論冬至點(diǎn)。圖元的控制點(diǎn)保存在三個(gè)數(shù)組中:標(biāo)志獲得組、x坐標(biāo)數(shù)組和y坐標(biāo)數(shù)組。找到標(biāo)志數(shù)組的起始點(diǎn)很簡單,但是標(biāo)志數(shù)組沒有相應(yīng)的長度字,也沒有直接其他兩個(gè)數(shù)組的方法,你必須先解碼標(biāo)志數(shù)組才能解釋x和y坐標(biāo)數(shù)組。  
          我們提到棕em-square被限制為最大為16384個(gè)網(wǎng)格,因此通常情況下需要各兩個(gè)字節(jié)來表示x坐標(biāo)和y坐標(biāo)。為了節(jié)省空間,圖元中保存的是相對坐標(biāo)。第一個(gè)點(diǎn)的坐標(biāo)是相對(0,0)記錄的,所有隨后的點(diǎn)記錄者是和上一個(gè)點(diǎn)的坐標(biāo)差值。有些差值可以用一個(gè)字節(jié)表示,有些差值為0,另外一些差值則無法用耽擱字節(jié)表示。標(biāo)志數(shù)組保存了每個(gè)坐標(biāo)的編碼信息以及其他一些信息。下面是標(biāo)志中各個(gè)位的含義的總結(jié):  
   
  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曲線有四個(gè)控制點(diǎn)定義:位于曲線上(on-curve)的起始點(diǎn)、兩個(gè)不在曲線上(off-curve)的控制點(diǎn)和一個(gè)曲線上的結(jié)束點(diǎn)。TureType字體中的圖元輪廓是用二階Bezier曲線定義的,有三個(gè)點(diǎn):一個(gè)曲線上的點(diǎn),一個(gè)曲線外的點(diǎn)和另一個(gè)曲線上的點(diǎn)。多個(gè)連續(xù)的不在曲線上的點(diǎn)是允許的,但不是用來定義三階或更高階的Bezier曲線,而是為了減少控制點(diǎn)的數(shù)目。比如,對于on-off-off-on模式的四個(gè)點(diǎn),會加入一個(gè)隱含的點(diǎn)使之成為on-off-on-off-on,因此定義的是兩段二階Bezier曲線。  
          如果設(shè)置了G_ONCURVE位,那么控制點(diǎn)在曲線上,否則不在曲線上。如果設(shè)置了G_REPEAT,標(biāo)志數(shù)組中的下一字節(jié)表示重復(fù)次數(shù),當(dāng)前標(biāo)志應(yīng)該重復(fù)指定的次數(shù)。因此,標(biāo)志數(shù)組中實(shí)際使用了某種類型的行程編碼。標(biāo)志中的其他位用于描述相應(yīng)   的x坐標(biāo)和y坐標(biāo)的編碼方式,它們可以表示當(dāng)前相尋坐標(biāo)是否和上一個(gè)相同、正的單字節(jié)值、負(fù)的單字節(jié)值或有符號兩字節(jié)值。  
          解碼圖元的描述是一個(gè)兩次掃描的起始點(diǎn)。然后再遍歷圖元定義中的每一個(gè)點(diǎn)把它轉(zhuǎn)換為更容易管理的格式。程序清單14-2列出了解碼TrueType圖元的函數(shù),它是KTrueType類的一個(gè)方法。  
   
  int   KTrueType::DecodeGlyph(int   index,   KCurve   &   curve,   XFORM   *   xm)   const  
  {  
  const   GlyphHeader   *   pHeader   =   GetGlyph(index);  
   
  if   (   pHeader==NULL   )  
  {  
  // assert(false);  
  return   0;  
  }  
   
  int nContour   =   (short)   reverse(pHeader->numberOfContours);  
   
  if   (   nContour<0   )  
  {  
  return   DecodeCompositeGlyph(pHeader+1,   curve);   //   after   the   header  
  }  
   
  if   (   nContour==0   )  
  return   0;  
   
  curve.SetBound(reverse((WORD)pHeader->xMin),   reverse((WORD)pHeader->yMin),    
                        reverse((WORD)pHeader->xMax),   reverse((WORD)pHeader->yMax));  
   
  const   USHORT   *   pEndPoint   =   (const   USHORT   *)   (pHeader+1);  
   
  int   nPoints   =   reverse(pEndPoint[nContour-1])   +   1;     //   endpoint   of   last   contour   +   1  
  int   nInst       =   reverse(pEndPoint[nContour]);       //   instructon   length  
   
  curve.m_glyphindex   =   index;  
  curve.m_glyphsize     =   (int)   GetGlyph(index+1)   -   (int)   GetGlyph(index);  
  curve.m_Ascender       =   m_Ascender;  
  curve.m_Descender     =   m_Descender;  
  curve.m_LineGap         =   m_LineGap;  
   
  GetMetrics(index,   curve.m_advancewidth,   curve.m_lsb);  
   
  if   (   curve.m_glyphsize==0   )  
  return   0;  
   
  curve.m_instrsize     =   nInst;  
   
  const   BYTE   *   pFlag   =   (const   BYTE   *)   &   pEndPoint[nContour]   +   2   +   nInst; //   first   byte   in   flag  
  const   BYTE   *   pX         =   pFlag;  
   
  int   xlen   =   0;  
   
  for   (int   i=0;   i<nPoints;   i++,   pX++)  
  {  
  int   unit   =   0;  
   
  switch   (   pX[0]   &   G_XMASK   )  
  {  
  case   G_XADDBYTE:  
  case   G_XSUBBYTE:  
  unit   =   1;  
  break;  
   
  case   G_XADDINT:  
  unit   =   2;  
  }  
   
  if   (   pX[0]   &   G_REPEAT   )  
  {  
  xlen   +=   unit   *   (pX[1]+1);    
   
  i   +=   pX[1];  
  pX   ++;  
  }  
  else  
  xlen   +=   unit;  
  }  
   
  const   BYTE   *   pY   =   pX   +   xlen;  
   
  int   x   =   0;  
KTrueType類處理TrueType字體的裝入和解碼,隨書光盤中有它的完整源代碼。DecodeGlyph給出圖元索引和可選的變換矩陣,處理的是單個(gè)圖元的解碼。參數(shù)curve是KCurve類,用于把TrueType圖元定義保存為32位的點(diǎn)的贖罪以及一個(gè)標(biāo)志數(shù)組,以梗用GDI進(jìn)行顯示。這些代碼可以作為簡單TrueType字體編輯器的基礎(chǔ)。  
          代碼中調(diào)用了GetGlyph方法,該方法用位置表索引找到該圖元的GlyphHeader結(jié)構(gòu)。從中得到圖元的輪廓線數(shù)目。注意必須反轉(zhuǎn)該值的字節(jié)序,因?yàn)門rueType字體用的是Big-Endian字節(jié)序。如果該值為負(fù)值,說明這是一個(gè)合成圖元,應(yīng)該轉(zhuǎn)而調(diào)用DecodeCompositeGlyph方法。接下支的代碼定位了endPtsOfContours數(shù)組,找出點(diǎn)的總數(shù),然后跳過指令找到標(biāo)志數(shù)組的起始位置。  
          接下去需要長到的是x坐標(biāo)數(shù)組的始位置和長度,這需要遍歷標(biāo)志數(shù)組一次。對于每一個(gè)控制點(diǎn),它在x坐標(biāo)數(shù)組中所占空間可能為0到2個(gè)字節(jié),這取決于它的相對坐標(biāo)是0、單個(gè)字節(jié)還是兩個(gè)字節(jié)。  
          根據(jù)x坐標(biāo)數(shù)組的地址和長度可以得到y(tǒng)坐標(biāo)的地址。接下去的代碼遍歷所有的輪廓線,解碼其中的控制點(diǎn),把相對坐標(biāo)轉(zhuǎn)換為絕對坐標(biāo),然后把它加入到曲線對象中。如果需要的話,會對每個(gè)控制點(diǎn)做變換。  
  回想一下,TrueType使用的是二階Bezier曲線,允許在兩個(gè)曲線上的點(diǎn)之間有多個(gè)不在曲線上的點(diǎn)。為了簡化曲線繪制算法,KCurve::Add方法在每兩個(gè)不在曲線上的點(diǎn)之間加入一個(gè)額外的在曲線上的點(diǎn)。  
   
          處理了簡單圖元之后,我們來看看合成圖元。合成圖元用一個(gè)經(jīng)變換的圖元序列定義。每個(gè)經(jīng)變換的圖元的定義包括三個(gè)部分:一個(gè)標(biāo)志、一個(gè)圖元索引和一個(gè)變換矩陣。標(biāo)志字段決定了變換矩陣的編碼方式。編碼的目的也是為了節(jié)省一些空間,加外還說明了是否已到達(dá)序列的終點(diǎn)。一個(gè)完整的2D   affine變換需要6個(gè)值。但如果只是平移的話,只需要兩個(gè)值(dx,dy),這兩個(gè)值可以保存為兩個(gè)字節(jié)或兩個(gè)字。如果x和y以相同的值縮放,加外還需要一個(gè)縮放值。取一般的情況下仍然需要6個(gè)值,但是很多時(shí)候可以節(jié)省幾個(gè)字節(jié)。用于變換的值以2.14的有符號定點(diǎn)格式保存,dx和dy值除外,這兩個(gè)值以整數(shù)形式保存。得到合成圖元的過程實(shí)際上是變換和組合幾個(gè)圖元的過程。比如,如果字體中的一個(gè)圖元是另一個(gè)圖元的精確鏡像,它只需定義為一個(gè)合成圖元,可以通過對另一個(gè)圖像做鏡像變換即可。程序清單14-3列出了解碼合成圖元的代碼。  
   
   
  int   KTrueType::DecodeCompositeGlyph(const   void   *   pGlyph,   KCurve   &   curve)   const  
  {  
  KDataStream   str(pGlyph);  
   
  unsigned   flags;  
   
  int   len   =   0;  
   
  do    
  {  
  flags             =   str.GetWord();  
   
  unsigned   glyphIndex   =   str.GetWord();  
   
  //   Argument1   and   argument2   can   be   either   x   and   y   offsets   to   be   added   to   the   glyph   or   two   point   numbers.    
  //   In   the   latter   case,   the   first   point   number   indicates   the   point   that   is   to   be   matched   to   the   new   glyph.    
  //   The   second   number   indicates   the   new   glyph's   "matched"   point.   Once   a   glyph   is   added,   its   point   numbers    
  //   begin   directly   after   the   last   glyphs   (endpoint   of   first   glyph   +   1).    
   
  //   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,    
  //   the   values   are   rounded   to   those   of   the   closest   grid   lines   before   they   are   added   to   the   glyph.    
  //   X   and   Y   offsets   are   described   in   FUnits.    
   
  signed   short   argument1;  
  signed   short   argument2;  
   
  if   (   flags   &   ARG_1_AND_2_ARE_WORDS   )  
  {  
  argument1   =   str.GetWord();   //   (SHORT   or   FWord)   argument1;  
  argument2   =   str.GetWord();   //   (SHORT   or   FWord)   argument2;  
  }    
  else    
  {  
  argument1   =   (signed   char)   str.GetByte();  
  argument2   =   (signed   char)   str.GetByte();  
  }  
   
  signed   short   xscale,   yscale,   scale01,   scale10;  
   
  xscale     =   1;  
  yscale     =   1;  
  scale01   =   0;  
  scale10   =   0;  
   
  if   (   flags   &   WE_HAVE_A_SCALE   )  
  {  
  xscale   =   str.GetWord();  
  yscale   =   xscale; //   Format   2.14    
  }    
  else   if   (   flags   &   WE_HAVE_AN_X_AND_Y_SCALE   )    
  {  
  xscale   =   str.GetWord();  
  yscale   =   str.GetWord();  
  }    
  else   if   (   flags   &   WE_HAVE_A_TWO_BY_TWO   )    
  {  
  xscale     =   str.GetWord();  
  scale01   =   str.GetWord();  
  scale10   =   str.GetWord();  
  yscale     =   str.GetWord();  
  }  
   
  if   (   flags   &   ARGS_ARE_XY_VALUES   )  
  {  
  XFORM   xm;  
   
  xm.eDx     =   (float)   argument1;  
  xm.eDy     =   (float)   argument2;  
  xm.eM11   =   xscale     /   (float)   16384.0;  
  xm.eM12   =   scale01   /   (float)   16384.0;  
  xm.eM21   =   scale10   /   (float)   16384.0;  
  xm.eM22   =   yscale     /   (float)   16384.0;  
   
  len   +=   DecodeGlyph(glyphIndex,   curve,   &   xm);  
  }  
  else  
  assert(false);  
  }    
  while   (   flags   &   MORE_COMPONENTS   );  
   
  if   (   flags   &   WE_HAVE_INSTRUCTIONS   )  
  {  
  unsigned   numInstr   =   str.GetWord();  
   
  for   (unsigned   i=0;   i<numInstr;   i++)  
  str.GetByte();  
  }  
   
  //   The   purpose   of   USE_MY_METRICS   is   to   force   the   lsb   and   rsb   to   take   on   a   desired   value.    
  //   For   example,   an   i-circumflex   (Unicode   00ef)   is   often   composed   of   the   circumflex   and   a   dotless-i.    
  //   In   order   to   force   the   composite   to   have   the   same   metrics   as   the   dotless-i,    
  //   set   USE_MY_METRICS   for   the   dotless-i   component   of   the   composite.   Without   this   bit,    
  //   the   rsb   and   lsb   would   be   calculated   from   the   HMTX   entry   for   the   composite   (or   would   need   to   be    
  //   explicitly   set   with   TrueType   instructions).    
   
  //   Note   that   the   behavior   of   the   USE_MY_METRICS   operation   is   undefined   for   rotated   composite   components.  
   
  return   len;  
  }  
   
   
   
          DecodeCompositeGlyph方法解碼每個(gè)圖元的標(biāo)志、圖元索引和變換矩陣,然后調(diào)用DecodeGlypgh方法進(jìn)行解碼。注意,對DecodeGlyph方法的調(diào)用包含一個(gè)有效的變換矩陣參數(shù)。當(dāng)MORE_COMPONENTS標(biāo)志結(jié)束時(shí),該方法隨之結(jié)束。隨書光盤中有該方法完整的源代碼。  
          解碼后的TrueType字體的圖元要用GDI繪制還有一個(gè)小問題需要處理。GDI只繪制三階Bezier曲線,因此從圖元表解碼所得的二階Bezier曲線的控制點(diǎn)需要轉(zhuǎn)換為三階Bezier曲線的控制點(diǎn)。通過對Bezier曲線原始數(shù)學(xué)定義的研究,可以得到如下用GDI繪制二階Bezier曲線的簡單例程。  
   
  //draw   a   2nd-degree   Bezier   curve   segment  
  BOOL   Bezier2(HDC   hDC,int   &   x0,int   &   y0,   int   x1,   int   y1,   int   x2   ,int   y2)  
  {  
  //   p0   p1   p2   -   >   p0   (p0   +   2p1)/3   (2p1+p2)/3,   p2  
   
  POINT   P[3]   =   {   {   (x0+2*x1)/3,(y0+2*y1)/3},  
                                {(2*x1+x2)/3,(2*y1+y2)/3},  
                                {x2,y2}   };  
  x0=x2;y0=y2;  
  return   PolyBezierTo(hDC,P,3);  
  }  
   
          對于用三個(gè)控制點(diǎn)(p0,p1,p2)定義的二階Bezier曲線,相應(yīng)的三階Bezier曲線的控制點(diǎn)為(p0,(p0+2*p1)/3,(2*p1+p2)/3,p2)。  
   
   
  4.圖元指令  
          程序清單14-2和14-3給人的印象是TrueType字體的柵格器可以通過掃描和轉(zhuǎn)換圖元的輪廓來輕松地實(shí)現(xiàn),比如,用GDI和StrokeAndFillPath函數(shù)來填充圖元輪廓繪制出來的路徑。這種簡單的字體柵格器的實(shí)現(xiàn)并不是很有用,除非它只用于高分辨詣的設(shè)備如打印機(jī)等。  
          簡單柵格器得到的圖像筆畫粗細(xì)不一,有信息的遺漏,有字符特征的損失以及不對稱等缺陷。當(dāng)點(diǎn)陣變小是,情況不會更糟。總之,簡單字體柵格器在小尺寸時(shí)會產(chǎn)生字跡模糊的結(jié)果。在大尺寸時(shí)會產(chǎn)生不好看的結(jié)果,只有在點(diǎn)陣增大時(shí)結(jié)果才會改善。  
          當(dāng)在大的em-square(典型的是2048)中定義的圖元輪廓縮小為小得多的網(wǎng)格時(shí)(如32*32),不可避免會損失精度并引入誤差。  
   
          TrueType解決這個(gè)問題的方法是控制圖元輪廓從em-square到柵格網(wǎng)格的縮放過程,使得到的結(jié)果看起來效果更好,和原始圖元的設(shè)計(jì)盡量接近。這種技術(shù)被稱為網(wǎng)格調(diào)整(grid   fitting),它想達(dá)到的目標(biāo)有:  
          消除網(wǎng)格位置的可能影響,保證筆畫的粗細(xì)和網(wǎng)格的相對位置無關(guān)。  
          控制圖元中關(guān)鍵位置的尺寸  
          保持對稱性和襯線等   重要的圖元設(shè)計(jì)細(xì)節(jié)。  
       
            TrueType字體中網(wǎng)格調(diào)整的需求在兩個(gè)地方中編碼:控制值表(control   value   table)和每個(gè)圖元的網(wǎng)格調(diào)整指令。  
          控制值表("cvt"表)用于保存一個(gè)數(shù)組,這些值被稱為網(wǎng)格調(diào)整指令。比如,對于有襯線的字體,基線、襯線高度、大寫字母筆劃的寬度等值都或以是被控制的值。它們可以以字體設(shè)計(jì)者已知的次序保存在控制值表中,然后用它們的索引來引用。在字體光柵化過程中,控制值表中的值根據(jù)點(diǎn)陣的大小縮放。在網(wǎng)絡(luò)調(diào)整指令中引用這些值   可以保證使用的值與網(wǎng)枸的位置無關(guān)。比如,如果水平線[14,0,25,200]可以用CVT表中的兩個(gè)值定義為[14,0,14+CVT[stem_width],0+CVT[cap_height]],那     么該線的寬度和高度會和所在網(wǎng)格的相對位置無關(guān),保持不變。  
          每一個(gè)圖元的定義中附加有一個(gè)指令序列,該指令序列被稱為圖元指令,該背景令序列用于控制此圖元的網(wǎng)格高速。圖元指令線用控制值表中的值,以保證在索引圖元中這些值相同。  
          圖元指令是一種基于堆棧的偽計(jì)算機(jī)的指令。堆棧計(jì)算機(jī)常用于計(jì)算機(jī)語言的解釋性實(shí)現(xiàn)。比如,F(xiàn)orth(用于嵌入式系統(tǒng)的一種強(qiáng)大而簡潔的語言)、RPL(HP計(jì)算器使用的語言)和Java虛擬機(jī)都是堆棧計(jì)算機(jī)。  
          堆棧計(jì)算機(jī)通常沒有寄存器,所有的計(jì)算都在堆棧上進(jìn)行(有些堆棧計(jì)算機(jī)使用分開的控制堆棧和數(shù)據(jù)堆棧)。比如,壓入指令把一個(gè)值壓入堆棧,彈出指令從堆棧中彈出上面的值,二元加法指令彈出上面的兩   個(gè)值   ,然后把它們的和壓入堆棧。


本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/clq271520093/archive/2008/01/17/2048257.aspx

posted on 2010-07-30 13:59 wrh 閱讀(1660) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


導(dǎo)航

<2025年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

統(tǒng)計(jì)

常用鏈接

留言簿(19)

隨筆檔案

文章檔案

收藏夾

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国内外成人免费激情在线视频网站 | 亚洲大胆人体视频| 艳女tv在线观看国产一区| 伊人成人在线视频| 亚洲精品自在在线观看| 99精品福利视频| 亚洲女人天堂成人av在线| 欧美亚洲视频| 欧美激情综合色| 一本综合久久| 香蕉久久夜色精品国产| 噜噜噜噜噜久久久久久91| 国产精品视频区| 欧美午夜久久| 国产日韩亚洲欧美| 亚洲第一伊人| 亚洲视频久久| 久久综合给合久久狠狠色| 亚洲国产人成综合网站| 亚洲综合导航| 欧美成人一区在线| 国产麻豆视频精品| 亚洲精选在线观看| 久久久久国产精品午夜一区| 最新日韩在线视频| 久久aⅴ乱码一区二区三区| 欧美电影在线观看完整版| 国产精品三级视频| 日韩午夜电影在线观看| 久久伊伊香蕉| 亚洲一级在线观看| 欧美精品v国产精品v日韩精品| 国产欧美日本一区二区三区| 亚洲免费电影在线观看| 欧美一区国产二区| 99精品视频免费全部在线| 久久中文字幕一区| 国产一区二区视频在线观看| 亚洲午夜一级| 亚洲欧洲日本mm| 久久米奇亚洲| 国产综合视频| 欧美主播一区二区三区美女 久久精品人 | 日韩视频在线观看免费| 美女在线一区二区| 国产亚洲人成a一在线v站| 亚洲欧美在线高清| 一区二区三区黄色| 欧美另类videos死尸| 亚洲福利视频三区| 欧美成人蜜桃| 美日韩在线观看| 亚洲国产日韩一区二区| 老司机成人网| 久久综合综合久久综合| 伊人久久大香线蕉综合热线| 久久久久久久久久看片| 欧美一区国产在线| 国产主播一区二区三区| 久久久噜噜噜久久中文字免| 久久超碰97中文字幕| 极品少妇一区二区三区精品视频| 久久天堂av综合合色| 久久一区二区精品| 亚洲精品在线电影| 99国产精品| 国产精品一页| 乱中年女人伦av一区二区| 久久天堂国产精品| 欧美视频中文一区二区三区在线观看 | 伊人男人综合视频网| 夜夜嗨网站十八久久| 亚洲一区二区三区高清不卡| 欧美日韩精品欧美日韩精品| 国产精品日韩一区二区三区| 9久草视频在线视频精品| 亚洲黄色大片| 欧美日韩精品综合| 久久不射电影网| 久久这里只有| 亚洲人被黑人高潮完整版| 日韩手机在线导航| 国产精品视频网站| 久久综合色88| 欧美激情中文不卡| 亚洲一区精彩视频| 久久黄色网页| 日韩一二在线观看| 午夜电影亚洲| 日韩一区二区久久| 欧美一区二区三区免费大片| 亚洲福利电影| 亚洲欧美日韩在线一区| 亚洲国产精品高清久久久| 亚洲色图制服丝袜| 亚洲国产精品久久91精品| 亚洲一区二区三区成人在线视频精品 | 欧美调教vk| 久久综合中文色婷婷| 欧美日韩国产三区| 久久综合色婷婷| 国产精品视频观看| 亚洲三级电影全部在线观看高清| 国产欧美日韩精品丝袜高跟鞋| 欧美成人69av| 国产日韩免费| 一区二区三区日韩精品视频| 亚洲国产91精品在线观看| 在线一区二区日韩| 亚洲国产日韩一区二区| 欧美亚洲免费| 亚洲欧美在线看| 欧美日韩亚洲国产一区| 美女脱光内衣内裤视频久久网站| 国产精品国产一区二区| 亚洲精品国产精品乱码不99| 国产精品扒开腿做爽爽爽软件| 亚洲高清av在线| 国产精品一区二区三区久久久| 亚洲精品1区2区| 在线观看国产一区二区| 亚洲香蕉在线观看| 亚洲午夜极品| 欧美日韩一区二区高清| 欧美国产三级| 亚洲国产精品一区| 久久久久久电影| 久久综合99re88久久爱| 国产婷婷色综合av蜜臀av| 亚洲尤物视频在线| 亚洲欧美日韩一区二区三区在线观看 | 欧美黄色一级视频| 欧美**字幕| 伊人一区二区三区久久精品| 久久国产手机看片| 久久免费视频观看| 在线观看视频亚洲| 麻豆9191精品国产| 欧美国产国产综合| 亚洲美女免费精品视频在线观看| 欧美sm视频| 99国产精品国产精品毛片| 一区二区三区四区五区精品| 欧美视频免费看| 亚洲自拍都市欧美小说| 久久久久久有精品国产| 亚洲国产精品成人va在线观看| 猛男gaygay欧美视频| 亚洲欧洲精品一区二区三区 | 亚洲国产精品尤物yw在线观看| 亚洲欧洲一区| 欧美日韩福利| 亚洲天堂网站在线观看视频| 欧美一级视频一区二区| 国产在线观看一区| 免费在线日韩av| 日韩网站在线观看| 欧美一区二区在线看| 伊人春色精品| 欧美日韩在线精品一区二区三区| 亚洲一区二区三区视频| 免费在线欧美黄色| 亚洲婷婷综合色高清在线| 国产欧美亚洲日本| 免费美女久久99| 亚洲欧美日韩精品久久| 免费永久网站黄欧美| 国产精品99久久99久久久二8| 国产精品在线看| 另类图片综合电影| 亚洲一区日韩在线| 亚洲电影免费观看高清完整版在线观看| 99在线热播精品免费| 国产亚洲一区精品| 欧美三级视频在线观看| 久久久久久久性| 噜噜噜躁狠狠躁狠狠精品视频 | 国产在线不卡视频| 欧美精品二区| 久久黄金**| 亚洲性色视频| 亚洲国产精品悠悠久久琪琪 | 亚洲伦理久久| 美女主播一区| 欧美一区二区三区免费观看| 亚洲人午夜精品免费| 国产麻豆精品视频| 欧美日韩精品欧美日韩精品一 | 国产亚洲欧美一区| 欧美午夜理伦三级在线观看| 久久亚洲一区二区| 欧美在线看片a免费观看| 99天天综合性| 亚洲啪啪91| 欧美成人国产一区二区| 久久久久久亚洲精品杨幂换脸| 亚洲女人天堂成人av在线| 亚洲视频日本| 亚洲一区二区精品在线| 在线一区二区三区四区五区|