-//\r
-// TTF.CPP - The TrueType class\r
-// by James L. Hammons\r
-// (C) 2005 Underground Software\r
-//\r
-// This class encapsulates all the complexity of a TrueType Font File\r
-// database. Included are functions to load, save, & initialize the\r
-// TTF database, move, add, & delete points & glyphs, i.e. manipulate\r
-// a TTF file in just about any way imaginable!\r
-//\r
-// JLH = James L. Hammons <jlhamm@acm.org>\r
-//\r
-// Who When What\r
-// --- ---------- -------------------------------------------------------------\r
-// JLH ??/??/199? Created this file\r
-//\r
-//\r
-// STILL TO BE DONE:\r
-//\r
-// - Eliminate ALL references to BYTE, WORD, SBYTE, SWORD, etc.\r
-//\r
-\r
-#include <stdio.h> // For file handling, etc. //\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include "charnames.h"\r
-#include "ttf.h"\r
-\r
-#define TTFDEBUG\r
-\r
-#ifdef TTFDEBUG\r
-#include "debug.h"\r
-#endif\r
-\r
-#define NUMTABS 24 // Number of distinct tables\r
-\r
-\r
-/*void fskip(HANDLE file, uint32 bytesToSkip)\r
-{\r
- SetFilePointer(file, (LONG)bytesToSkip, NULL, FILE_CURRENT);\r
-}*/\r
-\r
-//\r
-// Get a BYTE from the current file...\r
-//\r
-uint8 ReadByte(FILE * file)\r
-{\r
- return (uint8)fgetc(file);\r
-}\r
-\r
-// The following routines get and put WORDs and DWORDs in little endian\r
-// format, so they should work no matter what endianess the platform that\r
-// holds the TTF object.\r
-\r
-//\r
-// Get a WORD from the current file...\r
-//\r
-uint16 ReadWord(FILE * file)\r
-{\r
- uint16 word = (uint16)fgetc(file) << 8;\r
- word |= (uint16)fgetc(file);\r
-\r
- return word;\r
-}\r
-\r
-//\r
-// Get a double WORD from the current file...\r
-//\r
-uint32 ReadDWord(FILE * file)\r
-{\r
- uint32 dword = 0;\r
-\r
- for(int i=0; i<4; i++)\r
- {\r
- dword <<= 8;\r
- dword |= (uint8)fgetc(file);\r
- }\r
-\r
- return dword;\r
-}\r
-\r
-//\r
-// Write a WORD to the current file...\r
-//\r
-void WriteWord(FILE * file, uint16 word)\r
-{\r
- fputc(word >> 8, file); // Hi byte\r
- fputc(word & 0xFF, file); // Lo byte\r
-}\r
-\r
-//\r
-// Write a double WORD to the current file...\r
-//\r
-void WriteDWord(FILE * file, uint32 dword)\r
-{\r
- for(int i=0; i<4; i++)\r
- {\r
- fputc((char)(dword >> 24), file);\r
- dword <<= 8;\r
- }\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Table extraction helper routines (inline 'em?)\r
-////////////////////////////////////////////////////////////////////////////\r
-\r
-//\r
-// Return a BYTE from a BYTE based table\r
-//\r
-uint8 GetByte(uint8 * table, uint32 &ptr)\r
-{\r
- return table[ptr++];\r
-}\r
-\r
-//\r
-// Return a WORD from a BYTE based table\r
-//\r
-uint16 GetWord(uint8 * table, uint32 &ptr)\r
-{\r
- uint16 hi = table[ptr++];\r
- uint16 lo = table[ptr++];\r
-\r
- return (uint16)((hi<<8) | lo);\r
-}\r
-\r
-//\r
-// Return a double WORD from a BYTE based table\r
-//\r
-uint32 GetDWord(uint8 * table, uint32 &ptr)\r
-{\r
- uint32 hi1 = table[ptr++];\r
- uint32 lo1 = table[ptr++];\r
- uint32 hi2 = table[ptr++];\r
- uint32 lo2 = table[ptr++];\r
-\r
- return (uint32)((hi1 << 24) | (lo1 << 16) | (hi2 << 8) | lo2);\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Table storage helper routines (inline 'em?)\r
-////////////////////////////////////////////////////////////////////////////\r
-\r
-//\r
-// Store a BYTE in a BYTE based table\r
-//\r
-void SetByte(uint8 * table, uint32 &ptr, uint8 data)\r
-{\r
- table[ptr++] = data;\r
-}\r
-\r
-//\r
-// Store a WORD in a BYTE based table\r
-//\r
-void SetWord(uint8 * table, uint32 &ptr, uint16 data)\r
-{\r
- table[ptr++] = data>>8; table[ptr++] = data&0xFF;\r
-}\r
-\r
-//\r
-// Store a DWORD in a BYTE based table\r
-//\r
-void SetDWord(uint8 * table, uint32 &ptr, uint32 data)\r
-{\r
- table[ptr++] = (uint8)(data >> 24); table[ptr++] = (uint8)(data >> 16);\r
- table[ptr++] = (uint8)(data >> 8); table[ptr++] = (uint8)(data & 0xFF);\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Fixed point to float (& vice versa) conversions\r
-/////////////////////////////////////////////////////////////////////////////\r
-float FixedToFloat(int16 fixed)\r
-{\r
- return (float)fixed / 16384.0f;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// TTF Constructor\r
-/////////////////////////////////////////////////////////////////////////////\r
-TTF::TTF(void)\r
-{\r
- loaded = false; // Set font initializer\r
- isDirty = false; // Glyphs are clean\r
- numberOfPolys = 0; // Set reasonable values\r
- isCompositeGlyph = false; // No composite glyph (yet)\r
-\r
- parray[0] = &EBDT; parray[1] = &EBLC; parray[2] = &EBSC; // Init pointer\r
- parray[3] = <SH; parray[4] = &OS_2; parray[5] = &PCLT; // array...\r
- parray[6] = &VDMX; parray[7] = &cmap; parray[8] = &cvt; \r
- parray[9] = &fpgm; parray[10] = &gasp; parray[11] = &glyf;\r
- parray[12] = &hdmx; parray[13] = &head; parray[14] = &hhea;\r
- parray[15] = &hmtx; parray[16] = &kern; parray[17] = &loca;\r
- parray[18] = &maxp; parray[19] = &name; parray[20] = &post;\r
- parray[21] = &prep; parray[22] = &vhea; parray[23] = &vmtx;\r
-\r
- larray[0] = &EBDT_len; larray[1] = &EBLC_len; larray[2] = &EBSC_len;\r
- larray[3] = <SH_len; larray[4] = &OS_2_len; larray[5] = &PCLT_len;\r
- larray[6] = &VDMX_len; larray[7] = &cmap_len; larray[8] = &cvt_len;\r
- larray[9] = &fpgm_len; larray[10] = &gasp_len; larray[11] = &glyf_len;\r
- larray[12] = &hdmx_len; larray[13] = &head_len; larray[14] = &hhea_len;\r
- larray[15] = &hmtx_len; larray[16] = &kern_len; larray[17] = &loca_len;\r
- larray[18] = &maxp_len; larray[19] = &name_len; larray[20] = &post_len;\r
- larray[21] = &prep_len; larray[22] = &vhea_len; larray[23] = &vmtx_len;\r
-\r
- for(uint32 i=0; i<NUMTABS; i++)\r
- *parray[i] = NULL; // Init pointers...\r
-\r
- for(uint32 i=0; i<MAXGLYPHS; i++)\r
- glyph[i] = NULL;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// TTF Destructor\r
-/////////////////////////////////////////////////////////////////////////////\r
-TTF::~TTF()\r
-{\r
- ClearTables(); // This should handle deallocation correctly...\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: void Init(void)\r
-//\r
-// This function initializes the TTF object, setting reasonable values for\r
-// the various internal variables used by the object.\r
-// [distinct from New?]\r
-/////////////////////////////////////////////////////////////////////////////\r
-void TTF::Init(void)\r
-{\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Clear the tables so that file can be reloaded\r
-/////////////////////////////////////////////////////////////////////////////\r
-void TTF::ClearTables(void)\r
-{\r
- for(uint32 i=0; i<NUMTABS; i++)\r
- {\r
- if ((*parray[i]) != NULL)\r
- {\r
- free(*parray[i]);\r
- *parray[i] = NULL;\r
- *larray[i] = 0;\r
- }\r
- } \r
-\r
- for(uint32 i=0; i<MAXGLYPHS; i++)\r
- {\r
- if (glyph[i] != NULL)\r
- {\r
- free(glyph[i]);\r
- glyph[i] = NULL;\r
- }\r
- }\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL Load(const char * filename)\r
-//\r
-// This loads the TTF database from an external file. Returns 'true' if\r
-// successful.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::Load(const char * filename)\r
-{\r
-// unsigned char ch; // Temp variable\r
-// UINT num_tabs; // Number of tables\r
- uint32 offset[NUMTABS], length[NUMTABS]; // Temporary storage...\r
- char names[NUMTABS][5];\r
- char narray[NUMTABS][5] = { "EBDT", "EBLC", "EBSC", "LTSH", "OS/2", "PCLT",\r
- "VDMX", "cmap", "cvt ", "fpgm", "gasp", "glyf", "hdmx", "head", "hhea",\r
- "hmtx", "kern", "loca", "maxp", "name", "post", "prep", "vhea", "vmtx" };\r
-\r
- //loaded = false;\r
-// file.open(filename, ios::binary|ios::in); // Open the file\r
- FILE * file = fopen(filename, "rb");\r
-\r
- if (file == NULL)\r
-#ifdef TTFDEBUG\r
-{\r
-WriteLogMsg("Failed to open file!\n");\r
-#endif\r
- return false;\r
-#ifdef TTFDEBUG\r
-}\r
-#endif\r
-\r
- if (ReadDWord(file) != 0x00010000)\r
-#ifdef TTFDEBUG\r
-{\r
-WriteLogMsg("File was NOT a TTF file!\n");\r
-#endif\r
- return false; // Not a TTF file...\r
-#ifdef TTFDEBUG\r
-}\r
-#endif\r
-\r
- uint32 num_tabs = ReadWord(file); // Get # of tables\r
-#ifdef TTFDEBUG\r
-WriteLogMsg("Number of tables is %u...\n", num_tabs);\r
-#endif\r
-// fskip(file, 6); // Skip this shiat...\r
- fseek(file, 6, SEEK_CUR);\r
-\r
-#ifdef TTFDEBUG\r
-WriteLogMsg("Reading names of tables...\n");\r
-#endif\r
- for(uint32 i=0; i<num_tabs; i++)\r
- {\r
-// ReadFile(file, names[i], 4, &bytesRead, NULL);\r
- fread(names[i], 1, 4, file);\r
- names[i][4] = 0;\r
-// fskip(file, 4); // Checksum\r
- fseek(file, 4, SEEK_CUR); // Checksum\r
- offset[i] = ReadDWord(file); // Offset from start of file\r
- length[i] = ReadDWord(file); // Length of table\r
- }\r
-\r
-#ifdef TTFDEBUG\r
-WriteLogMsg("Reading tables...\n");\r
-#endif\r
- for(uint32 i=0; i<num_tabs; i++)\r
- {\r
- for(uint32 j=0; j<NUMTABS; j++)\r
- {\r
- if ((strcmp(names[i], narray[j])) == 0) // Found a match...\r
- {\r
-// *parray[j] = (uint8 *)GlobalAlloc(GMEM_FIXED, length[i]); // Allocate space\r
- *parray[j] = (uint8 *)malloc(length[i]); // Alloc space\r
-\r
- if (*parray[j] == NULL)\r
- return false; // Bail out if nothing allocated\r
-\r
- *larray[j] = length[i]; // Set its length...\r
-// SetFilePointer(file, (LONG)offset[i], NULL, FILE_BEGIN);\r
-// ReadFile(file, *parray[j], length[i], &bytesRead, NULL);\r
- fseek(file, offset[i], SEEK_SET);\r
- fread(*parray[j], 1, length[i], file);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- fclose(file);\r
-\r
-// This shouldn't be necessary, since it's irrelevant (loaded flag)\r
-// loaded = true; // Set 'loaded' flag...\r
- isDirty = false; // Glyphs are clean\r
- ExtractTables(); // Move table data to my structs\r
- // & ignore return val\r
- // (should handle errors here)\r
- return true;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL Save(const char * filename)\r
-//\r
-// Save the TT font currently in the object\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::Save(const char * filename)\r
-{\r
-// fstream file;\r
-// ULONG offset = 12;\r
- uint32 numtabs = 0;\r
- char padding[3] = { 0, 0, 0 };\r
- // Convert this to a table of ULONGs to decrease complexity...\r
-//wouldn't be endian safe then...\r
- char narray[NUMTABS][5] = { "EBDT", "EBLC", "EBSC", "LTSH", "OS/2", "PCLT",\r
- "VDMX", "cmap", "cvt ", "fpgm", "gasp", "glyf", "hdmx", "head", "hhea",\r
- "hmtx", "kern", "loca", "maxp", "name", "post", "prep", "vhea", "vmtx" };\r
-\r
- if (isDirty)\r
- EncodeGlyph(currentGlyph);\r
-\r
- BuildTables(); // Ignore return value... \r
-\r
- for(uint32 i=0; i<NUMTABS; i++) // Figure out how many tables there are\r
- if ((*parray[i]) != NULL)\r
- numtabs++;\r
-\r
- uint32 offset = 12 + (numtabs * 16); // Calc correct offset to start of data\r
-\r
-// HANDLE file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,\r
-// NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r
- FILE * file = fopen(filename, "wb");\r
-\r
- WriteDWord(file, 0x00010000); // Write header...\r
- WriteWord(file, numtabs);\r
- WriteWord(file, 0); // SearchRange (Max power of 2 <= numTables) x 16.\r
- WriteWord(file, 0); // entrySelector Log2(max power of 2 <= numTables).\r
- WriteWord(file, 0); // NumTables x 16 - searchRange.\r
-\r
- for(uint32 i=0; i<NUMTABS; i++) // Write out table directory...\r
- {\r
- if ((*parray[i]) != NULL)\r
- {\r
-// for(int j=0; j<4; j++)\r
-// file.put(narray[i][j]); // Name\r
-// WriteFile(file, narray[i], 4, &bytesWritten, NULL);\r
- fwrite(narray[i], 1, 4, file); // Name\r
-\r
- WriteDWord(file, 0); // Checksum\r
- WriteDWord(file, offset); // Offset\r
- WriteDWord(file, (*larray[i])); // Length\r
-\r
- offset += (((*larray[i]) + 3) & ~3); // Pad out to 4-uint8 boundary...\r
- }\r
- }\r
-\r
- for(uint32 i=0; i<NUMTABS; i++) // Write out the tables...\r
- {\r
- if ((*parray[i]) != NULL)\r
- {\r
-// for(uint32 j=0; j<(*larray[i]); j++)\r
-// file.put((*parray[i])[j]);\r
-// WriteFile(file, *parray[i], *larray[i], &bytesWritten, NULL);\r
- fwrite(*parray[i], 1, *larray[i], file);\r
-\r
- uint32 remainder = ((*larray[i]) & 0x3);\r
- if (remainder) // i.e., it's not evenly div by 4\r
-// for(j=remainder; j<4; j++)\r
-// file.put((char)0); // pad it!\r
-// WriteFile(file, padding, 4 - remainder, &bytesWritten, NULL);\r
- fwrite(padding, 1, 4 - remainder, file);\r
- }\r
- }\r
-\r
- fclose(file);\r
-\r
- return true; // Whether or not it was successful...\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL EncodeGlyph(int glyphnum)\r
-//\r
-// This function encodes the glyph data and stores it to the 'glyf' table.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::EncodeGlyph(uint16 glyphnum)\r
-{\r
- bool retval = false; // Assume failure\r
- uint8 flag, xbuf[4000], ybuf[4000], fbuf[2000];\r
- uint32 xp = 0, yp = 0, fp = 0; // Table pointers\r
-\r
- if (glyphnum < myMaxp.numGlyphs) // numofgls is 1 based ind, glyph# 0 based\r
- {\r
- // need to add composite encoding...\r
- int lastx = 0, lasty = 0;\r
-\r
- for(uint32 i=0; i<numberOfPoints; i++)\r
- {\r
- flag = 0;\r
- int curx = gx[i] - lastx, cury = gy[i] - lasty;\r
-\r
- if (onCurve[i])\r
- flag |= 0x01; // Set on curve info\r
-\r
- if (curx)\r
- {\r
- if ((curx > 255) || (curx < -255)) // I.e., it's 2 uint8 value\r
- SetWord(xbuf, xp, curx);\r
- else\r
- {\r
- if (curx & 0x100)\r
- {\r
- flag |= 0x02; // I.e., it's negative\r
- curx *= -1; // Flip it so correct value is stored\r
- }\r
- else\r
- flag |= 0x12;\r
-\r
- SetByte(xbuf, xp, curx); // Automagically strips neg bit\r
- }\r
- }\r
- else\r
- flag |= 0x10; // X-coord is same...\r
- \r
- if (cury)\r
- {\r
- if ((cury > 255) || (cury < -255)) // I.e., it's 2 uint8 value\r
- SetWord(ybuf, yp, cury);\r
- else\r
- {\r
- if (cury & 0x100)\r
- {\r
- flag |= 0x04; // I.e., it's negative\r
- cury *= -1;\r
- }\r
- else\r
- flag |= 0x24;\r
- \r
- SetByte(ybuf, yp, cury);\r
- }\r
- }\r
- else\r
- flag |= 0x20; // Y-coord is same...\r
- \r
- fbuf[i] = flag;\r
- lastx = gx[i]; lasty = gy[i];\r
- }\r
-\r
- // Now crunch flags... ugly, ugly, ugly.\r
-/*\r
- fbuf[numberOfPoints] = 0; // Set sentinel value (ugly way to do this)\r
- for(i=0; i<numberOfPoints; i++);\r
- {\r
- if (fbuf[i] == fbuf[i+1]) // \r
- {\r
- uint8 count = 0; // Sentinel takes care of check for end of flags...\r
- while (fbuf[i] == fbuf[++i]) count++; // Count number of repeats\r
- i--;\r
- fbuf[fp++] = fbuf[i] | 0x08; // Set repeat flag\r
- fbuf[fp++] = count; // & number of repeats\r
- }\r
- else fbuf[fp++] = fbuf[i]; // Otherwise, just copy...\r
- }\r
-*/\r
- fp = numberOfPoints;\r
- // Find length of glyph and reallocate space if necessary\r
-\r
- uint32 newLength = 12 + numberOfPolys*2 + numberOfHints + fp + xp + yp;\r
-\r
- if (newLength & 0x03)\r
- newLength += (4 - newLength & 0x03);\r
-\r
- if (glyphLen[glyphnum] != newLength)\r
- {\r
- glyph[glyphnum] = (uint8 *)realloc(glyph[glyphnum], newLength);\r
- glyphLen[glyphnum] = newLength;\r
- }\r
-\r
- // And finally, store it!\r
-\r
- uint32 gp = 0; // Glyph pointer...\r
- SetWord(glyph[glyphnum], gp, numberOfPolys);\r
- SetWord(glyph[glyphnum], gp, llx);\r
- SetWord(glyph[glyphnum], gp, lly);\r
- SetWord(glyph[glyphnum], gp, urx);\r
- SetWord(glyph[glyphnum], gp, ury);\r
- for(uint32 i=0; i<numberOfPolys; i++)\r
- SetWord(glyph[glyphnum], gp, poly[i]);\r
- SetWord(glyph[glyphnum], gp, numberOfHints);\r
- for(uint32 i=0; i<numberOfHints; i++)\r
- SetByte(glyph[glyphnum], gp, hint[i]);\r
- for(uint32 i=0; i<fp; i++)\r
- SetByte(glyph[glyphnum], gp, fbuf[i]);\r
- for(uint32 i=0; i<xp; i++)\r
- SetByte(glyph[glyphnum], gp, xbuf[i]);\r
- for(uint32 i=0; i<yp; i++)\r
- SetByte(glyph[glyphnum], gp, ybuf[i]);\r
-\r
- retval = true; // Successfully encoded!\r
- }\r
-\r
- return retval;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL DecodeGlyph(int glyphnum)\r
-//\r
-// This function decodes the glyph data and stores it to the object's\r
-// internal array.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::DecodeGlyph(uint16 glyphnum)\r
-{\r
- bool retval = false;\r
- uint32 i, dp;\r
-\r
- // glyphnum is 0 based index, while numGlyphs is 1 based\r
- if (glyphnum >= myMaxp.numGlyphs)\r
- return false; // Invalid char #\r
-\r
- if (!glyphLen[glyphnum])\r
- {\r
- numberOfPoints = numberOfPolys = 0;\r
- return true; // Zero length IS valid...\r
- }\r
-// else // Get character data...\r
-// {\r
- // Now get the character data...\r
- dp = 0; // Reset data pointer\r
- isCompositeGlyph = false; // Default is no\r
- numberOfPolys = GetWord(glyph[glyphnum], dp); // # of polygons\r
-\r
- llx = (int16)GetWord(glyph[glyphnum], dp); // Lower left X\r
- lly = (int16)GetWord(glyph[glyphnum], dp); // Lower left Y\r
- urx = (int16)GetWord(glyph[glyphnum], dp); // Upper right X\r
- ury = (int16)GetWord(glyph[glyphnum], dp); // Upper right Y\r
-\r
- // Need to handle composite glyphs better here. The ways things\r
- // are set now is a recipe for disaster...\r
-\r
- if (numberOfPolys == 0xFFFF)\r
- {\r
- isCompositeGlyph = true;\r
- numberOfPolys = 0; // Set for no points\r
-\r
- Composite cmpst;\r
-\r
- while (!compositeList.IsEmpty()) // Empty composite list...\r
- compositeList.GetFront();\r
-\r
- do\r
- {\r
- cmpst.flags = GetWord(glyph[glyphnum], dp);\r
- cmpst.glyphIndex = GetWord(glyph[glyphnum], dp);\r
- cmpst.arg1 = (cmpst.flags & 0x01 ? (int16)GetWord(glyph[glyphnum], dp) : (int8)GetByte(glyph[glyphnum], dp));\r
- cmpst.arg2 = (cmpst.flags & 0x01 ? (int16)GetWord(glyph[glyphnum], dp) : (int8)GetByte(glyph[glyphnum], dp));\r
-\r
- if (cmpst.flags & 0x08)\r
- cmpst.xScale = cmpst.yScale = FixedToFloat((int16)GetWord(glyph[glyphnum], dp));\r
- else if (cmpst.flags & 0x40)\r
- cmpst.xScale = FixedToFloat((int16)GetWord(glyph[glyphnum], dp)),\r
- cmpst.yScale = FixedToFloat((int16)GetWord(glyph[glyphnum], dp));\r
- else if (cmpst.flags & 0x80)\r
- cmpst.xScale = FixedToFloat((int16)GetWord(glyph[glyphnum], dp)),\r
- cmpst.scale01 = FixedToFloat((int16)GetWord(glyph[glyphnum], dp)),\r
- cmpst.scale10 = FixedToFloat((int16)GetWord(glyph[glyphnum], dp)),\r
- cmpst.yScale = FixedToFloat((int16)GetWord(glyph[glyphnum], dp));\r
-\r
- compositeList.AddAtRear(cmpst);\r
- }\r
- while (cmpst.flags & 0x20); //*/\r
-\r
- return true;\r
- }\r
-\r
-//do {\r
-// USHORT flags;\r
-// USHORT glyphIndex;\r
-// if ( flags & ARG_1_AND_2_ARE_uint16S) {\r
-// (SHORT or Fuint16) argument1;\r
-// (SHORT or Fuint16) argument2;\r
-// } else {\r
-// USHORT arg1and2; /* (arg1 << 8) | arg2 */\r
-// }\r
-// if ( flags & WE_HAVE_A_SCALE ) {\r
-// F2Dot14 scale; /* Format 2.14 */\r
-// } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) {\r
-// F2Dot14 xscale; /* Format 2.14 */\r
-// F2Dot14 yscale; /* Format 2.14 */\r
-// } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) {\r
-// F2Dot14 xscale; /* Format 2.14 */\r
-// F2Dot14 scale01; /* Format 2.14 */\r
-// F2Dot14 scale10; /* Format 2.14 */\r
-// F2Dot14 yscale; /* Format 2.14 */\r
-// }\r
-//} while ( flags & MORE_COMPONENTS ) \r
-//if (flags & WE_HAVE_INSTR){\r
-// USHORT numInstr\r
-// uint8 instr[numInstr]\r
-//\r
-//Flags Bit Description \r
-//ARG_1_AND_2_ARE_uint16S 0 If this is set, the arguments are uint16s;\r
-// otherwise, they are uint8s.\r
-//ARGS_ARE_XY_VALUES 1 If this is set, the arguments are xy values;\r
-// otherwise, they are points.\r
-//ROUND_XY_TO_GRID 2 For the xy values if the preceding is true.\r
-//WE_HAVE_A_SCALE 3 This indicates that there is a simple scale\r
-// for the component. Otherwise, scale = 1.0.\r
-//RESERVED 4 This bit is reserved. Set it to 0. \r
-//MORE_COMPONENTS 5 Indicates at least one more glyph after this\r
-// one.\r
-//WE_HAVE_AN_X_AND_Y_SCALE 6 The x direction will use a different scale\r
-// from the y direction.\r
-//WE_HAVE_A_TWO_BY_TWO 7 There is a 2 by 2 transformation that will\r
-// be used to scale the component.\r
-//WE_HAVE_INSTRUCTIONS 8 Following the last component are\r
-// instructions for the composite character.\r
-//USE_MY_METRICS 9 If set, this forces the aw and lsb (and rsb)\r
-// for the composite to be equal to those from\r
-// this original glyph. This works for hinted\r
-// and unhinted characters.\r
-\r
- for(i=0; i<numberOfPolys; i++)\r
- poly[i] = GetWord(glyph[glyphnum], dp);\r
-\r
- numberOfHints = GetWord(glyph[glyphnum], dp);\r
-\r
- for(i=0; i<numberOfHints; i++)\r
- hint[i] = GetByte(glyph[glyphnum], dp);\r
-\r
- // Decode the dots...\r
-\r
- uint32 num_pts = poly[numberOfPolys-1] + 1;\r
- numberOfPoints = num_pts; // necessary??\r
- uint32 xptr, yptr; // pointers to beginning of coord data\r
- uint32 numXs = 0;\r
- int xx = 0, yy = 0, repeat;\r
- uint32 numTokens = num_pts, k, numRep;\r
-\r
- // We make an educated guess that num_pts = num_tokens; but if there\r
- // is repeated data in the tokens, then we need to adjust the # of\r
- // tokens down appropriately.\r
-\r
- for(uint32 i=0; i<numTokens; i++)\r
- {\r
- uint8 token = glyph[glyphnum][dp+i]; uint32 rpts = 1;\r
-\r
- if (token & 0x08) // Repeated token?\r
- {\r
- i++; // Yes, bump pointer to # of times to repeat\r
- numRep = glyph[glyphnum][dp+i];\r
- rpts += numRep;\r
-\r
- if (numRep > 1) // Do we need to adjust numTokens?\r
- // this is bad, modifying numTokens while it's used as a compare value\r
- // in the above loop...\r
- // Is it necessary to do so in order to keep from going too far?\r
- numTokens -= (numRep - 1); // Yes, adjust.\r
- }\r
-\r
- // 10 = same x(0uint8s), 00 = 2uint8s, 02&12 = 1uint8\r
- if ((token & 0x02) == 0x02)\r
- numXs += rpts;\r
-\r
- if ((token & 0x12) == 0x00)\r
- numXs += (rpts*2);\r
- }\r
-\r
- xptr = dp + numTokens;\r
- yptr = dp + numTokens + numXs;\r
-\r
- // & continue decoding X/Y-coords...\r
-\r
- k = 0; // Index to the point array\r
-\r
- for(uint32 i=0; i<numTokens; i++)\r
- {\r
- uint8 token = glyph[glyphnum][dp+i];\r
- repeat = 1;\r
-\r
- if (token & 0x08)\r
- {\r
- i++;\r
- repeat += glyph[glyphnum][dp+i]; // Set repeat data...\r
- }\r
-\r
- while (repeat--)\r
- {\r
- if ((token & 0x12) == 0x12)\r
- xx += GetByte(glyph[glyphnum], xptr);\r
-\r
- if ((token & 0x12) == 0x02)\r
- xx -= GetByte(glyph[glyphnum], xptr);\r
-\r
- if ((token & 0x12) == 0x00)\r
- xx += (int16)GetWord(glyph[glyphnum], xptr);\r
-\r
- gx[k] = xx; // Store x-coordinate\r
-\r
- if ((token & 0x24) == 0x24)\r
- yy += GetByte(glyph[glyphnum], yptr);\r
-\r
- if ((token & 0x24) == 0x04)\r
- yy -= GetByte(glyph[glyphnum], yptr);\r
-\r
- if ((token & 0x24) == 0x00)\r
- yy += (int16)GetWord(glyph[glyphnum], yptr);\r
-\r
- gy[k] = yy; // Store y-coordinate\r
-\r
- onCurve[k++] = (token & 0x01 ? true : false); // If bit 0 set, then it's on curve\r
- }\r
- }\r
-\r
- retval = true; // Hmm. Successfully decoded a glyph...\r
-// }\r
-\r
- isDirty = false; // do it here?\r
-\r
- return retval;\r
-}\r
-\r
-/*****************************************************************************\r
- Member function:\r
-\r
- This function decodes the 'glyf' data for a non-composite (atomic) glyph and\r
- returns it as a GlyphPoints object. Helper function.\r
- *****************************************************************************/\r
-GlyphPoints TTF::GetGlyphPoints(uint16 glyphNum)\r
-{\r
- if (DecodeGlyph(glyphNum))\r
- return GlyphPoints(numberOfPoints, numberOfPolys, gx, gy, onCurve, poly);\r
-\r
- return GlyphPoints();\r
-}\r
-\r
-/*****************************************************************************\r
- Member function:\r
-\r
- This function decodes the 'glyf' data for a composite glyph and returns\r
- it as a GlyphPoints object.\r
- *****************************************************************************/\r
-GlyphPoints TTF::GetAllCompositePoints(uint16 glyphNum)\r
-{\r
-// int tmpGlyph = currentGlyph;\r
- \r
- DecodeGlyph(glyphNum); // Check for composite-ness\r
-\r
- if (!isCompositeGlyph)\r
- return GlyphPoints();\r
-\r
-/*\r
- for(int i=1; i<=pDoc->m_myFont.compositeList.Length(); i++)\r
- {\r
- TComposite cmpst = fnt->compositeList.PeekPosition(i);\r
- fnt->SetGlyph(cmpst.glyphIndex);\r
- if (cmpst.flags & 0x0002)\r
- m_nXOffset = cmpst.arg1, m_nYOffset = cmpst.arg2;\r
- ScanConvertSingleGlyph(pDC, nScanlines, nHorzLines, nXOffset, nYOffset);\r
- }\r
- fnt->SetGlyph(pDoc->character_num);\r
-*/\r
-\r
- GlyphPoints retVal;\r
-\r
- for(int i=1; i<=compositeList.Length(); i++)\r
- {\r
- Composite cmpst = compositeList.PeekPosition(i);\r
- \r
-// SetGlyph(cmpst.glyphIndex);\r
-// if (cmpst.flags & 0x0002)\r
-// m_nXOffset = cmpst.arg1, m_nYOffset = cmpst.arg2;\r
-// ScanConvertSingleGlyph(pDC, nScanlines, nHorzLines, nXOffset, nYOffset);\r
- GlyphPoints gp = GetGlyphPoints(cmpst.glyphIndex);\r
-\r
- if (cmpst.flags & 0x0002)\r
- gp.OffsetPoints(cmpst.arg1, cmpst.arg2);\r
-\r
- retVal = retVal + gp;\r
- // NOTE: May have to adjust scanlines as per 0x0002 above...\r
- }\r
-\r
-// SetGlyph(tmpGlyph);\r
- DecodeGlyph(currentGlyph); // Reset needed here...\r
-\r
- return retVal;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: void GetCharName(int cNum, uint8 * buf)\r
-//\r
-// This function returns the character name of the glyph number passed in.\r
-/////////////////////////////////////////////////////////////////////////////\r
-void TTF::GetCharName(int cNum, uint8 * buf)\r
-{\r
- buf[0] = 0; // Set failure as default condition\r
- \r
- if (!post) // PS names are here...\r
- return;\r
-\r
- if (post[1] != 0x02 || post[2] != 0x00) // i.e., it's NOT a V2.0 table\r
- return;\r
-\r
- uint8 * pTab = NULL;\r
- uint32 tabOff = 34, numGlyphs = (uint32)((post[32] << 8) | post[33]);\r
- uint32 index = (uint32)((post[tabOff + cNum * 2] << 8) | post[tabOff + cNum * 2 + 1]);\r
- uint32 nInd2;\r
-\r
- if (index > 257)\r
- {\r
- index -= 258;\r
- nInd2 = tabOff + (2 * numGlyphs);\r
- pTab = post;\r
- }\r
- else\r
- {\r
- nInd2 = 0;\r
- pTab = macStdNames;\r
- }\r
-\r
- for(uint32 i=0; i<index; i++)\r
- nInd2 = nInd2 + pTab[nInd2] + 1; // 1st uint8 is length of string + that uint8\r
-\r
- uint8 len = pTab[nInd2];\r
- nInd2++;\r
-\r
- for(uint8 i=0; i<len; i++)\r
- buf[i] = pTab[nInd2 + i];\r
-\r
- buf[len] = 0;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL ExtractTables(void)\r
-//\r
-// This function extracts the data from the various tables and puts them in\r
-// various structs for easier handling of the font data.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::ExtractTables(void)\r
-{\r
- if (head_len != 54)\r
- {\r
-#ifdef TTFDEBUG\r
-WriteLogMsg("Bad HEAD header: Expected 54, found %u...\n", head_len);\r
-#endif\r
- return false; // Corrupt data?\r
- }\r
-\r
- uint32 tp = 0;\r
- myHead.version = GetDWord(head, tp);\r
- myHead.fontRevision = GetDWord(head, tp);\r
- myHead.checkSumAdjustment = GetDWord(head, tp);\r
- myHead.magicNumber = GetDWord(head, tp);\r
- myHead.flags = GetWord(head, tp);\r
- myHead.unitsPerEm = GetWord(head, tp);\r
- myHead.createdh = GetDWord(head, tp);\r
- myHead.createdl = GetDWord(head, tp);\r
- myHead.modifiedh = GetDWord(head, tp);\r
- myHead.modifiedl = GetDWord(head, tp);\r
- myHead.xMin = GetWord(head, tp);\r
- myHead.yMin = GetWord(head, tp);\r
- myHead.xMax = GetWord(head, tp);\r
- myHead.yMax = GetWord(head, tp);\r
- myHead.macStyle = GetWord(head, tp);\r
- myHead.lowestRecPPEM = GetWord(head, tp);\r
- myHead.fontDirectionHint = (int16)GetWord(head, tp);\r
- myHead.indexToLocFormat = (int16)GetWord(head, tp);\r
- myHead.glyphDataFormat = (int16)GetWord(head, tp);\r
-\r
- if (maxp_len != 32)\r
- {\r
-#ifdef TDEBUG\r
-WriteLogMsg("Bad MAXP header: Expected 32, found %u...\n", maxp_len);\r
-#endif\r
- return false; // Corrupt data?\r
- }\r
-\r
- tp = 0; // Reset table pointer\r
- myMaxp.version = GetDWord(maxp, tp);\r
- myMaxp.numGlyphs = GetWord(maxp, tp);\r
- myMaxp.maxPoints = GetWord(maxp, tp);\r
- myMaxp.maxContours = GetWord(maxp, tp);\r
- myMaxp.maxCompositePoints = GetWord(maxp, tp);\r
- myMaxp.maxCompositeContours = GetWord(maxp, tp);\r
- myMaxp.maxZones = GetWord(maxp, tp);\r
- myMaxp.maxTwilightPoints = GetWord(maxp, tp);\r
- myMaxp.maxStorage = GetWord(maxp, tp);\r
- myMaxp.maxFunctionDefs = GetWord(maxp, tp);\r
- myMaxp.maxInstructionDefs = GetWord(maxp, tp);\r
- myMaxp.maxStackElements = GetWord(maxp, tp);\r
- myMaxp.maxSizeOfInstructions = GetWord(maxp, tp);\r
- myMaxp.maxComponentElements = GetWord(maxp, tp);\r
- myMaxp.maxComponentDepth = GetWord(maxp, tp);\r
-\r
- tp = 0; // Reset table pointer\r
- uint32 start = (myHead.indexToLocFormat ? GetDWord(loca, tp) : GetWord(loca, tp) << 1);\r
-\r
- for(uint32 i=0; i<myMaxp.numGlyphs; i++)\r
- {\r
- uint32 end = (myHead.indexToLocFormat ? GetDWord(loca, tp) : GetWord(loca, tp) << 1);\r
- uint32 length = end - start;\r
- glyphLen[i] = length; // Lengths are saved 'cause malloc is sloppy\r
-\r
- if (length) // loca+start? pointer arithmetic?\r
- {\r
- glyph[i] = (uint8 *)malloc(length); // Allocate space,\r
- memcpy(glyph[i], glyf+start, length); // and move it!\r
- }\r
- else\r
- glyph[i] = NULL;\r
-\r
- start = end; // Reset start value\r
- }\r
-\r
- return true;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL BuildTables(void)\r
-//\r
-// This function builds the various TTF tables using info in the various\r
-// structs so that a TTF file can be written out to disk.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::BuildTables(void)\r
-{\r
- uint32 i, tp, start;\r
-\r
- myHead.indexToLocFormat = 1; // We don't bother with [uint16s*2] format...\r
-\r
- // Build HEAD table\r
-\r
- tp = 0; // Reset table pointer\r
- SetDWord(head, tp, myHead.version);// = GetDWord(head, tp);\r
- SetDWord(head, tp, myHead.fontRevision);// = GetDWord(head, tp);\r
- SetDWord(head, tp, myHead.checkSumAdjustment);// = GetDWord(head, tp);\r
- SetDWord(head, tp, myHead.magicNumber);// = GetDWord(head, tp);\r
- SetWord(head, tp, myHead.flags);// = GetWord(head, tp);\r
- SetWord(head, tp, myHead.unitsPerEm);// = GetWord(head, tp);\r
- SetDWord(head, tp, myHead.createdh);// = GetDWord(head, tp);\r
- SetDWord(head, tp, myHead.createdl);// = GetDWord(head, tp);\r
- SetDWord(head, tp, myHead.modifiedh);// = GetDWord(head, tp);\r
- SetDWord(head, tp, myHead.modifiedl);// = GetDWord(head, tp);\r
- SetWord(head, tp, myHead.xMin);// = GetWord(head, tp);\r
- SetWord(head, tp, myHead.yMin);// = GetWord(head, tp);\r
- SetWord(head, tp, myHead.xMax);// = GetWord(head, tp);\r
- SetWord(head, tp, myHead.yMax);// = GetWord(head, tp);\r
- SetWord(head, tp, myHead.macStyle);// = GetWord(head, tp);\r
- SetWord(head, tp, myHead.lowestRecPPEM);// = GetWord(head, tp);\r
- SetWord(head, tp, myHead.fontDirectionHint);// = (int16)GetWord(head, tp);\r
- SetWord(head, tp, myHead.indexToLocFormat);// = (int16)GetWord(head, tp);\r
- SetWord(head, tp, myHead.glyphDataFormat);// = (int16)GetWord(head, tp);\r
-\r
- // Build MAXP table\r
-\r
- tp = 0; // Reset table pointer\r
- SetDWord(maxp, tp, myMaxp.version);// = GetDWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.numGlyphs);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxPoints);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxContours);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxCompositePoints);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxCompositeContours);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxZones);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxTwilightPoints);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxStorage);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxFunctionDefs);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxInstructionDefs);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxStackElements);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxSizeOfInstructions);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxComponentElements);// = GetWord(maxp, tp);\r
- SetWord(maxp, tp, myMaxp.maxComponentDepth);// = GetWord(maxp, tp);\r
-\r
- // Build LOCA & GLYF tables\r
-\r
- loca_len = (myMaxp.numGlyphs+1) * 4; // Set size of table\r
-\r
- if (loca)\r
- free(loca); // And create/reallocate it...\r
-\r
- loca = (uint8 *) malloc(loca_len);\r
-\r
- glyf_len = 0; // Refigure glyf table length\r
-\r
- for(i=0; i<myMaxp.numGlyphs; i++)\r
- glyf_len += glyphLen[i];\r
-\r
- if (glyf)\r
- free(glyf);\r
-\r
- glyf = (uint8 *) malloc(glyf_len);\r
-\r
- start = tp = 0; // Reset table pointer\r
-\r
- for(i=0; i<myMaxp.numGlyphs; i++)\r
- {\r
- SetDWord(loca, tp, start); // Store glyph start address\r
-\r
- if (glyphLen[i])\r
- memcpy(glyf+start, glyph[i], glyphLen[i]);\r
-\r
- start += glyphLen[i];\r
- }\r
-\r
- SetDWord(loca, tp, start); // Finally, store end of glyphs+1\r
-\r
- return true;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL SetGlyph(int glyphnum)\r
-//\r
-// This function decodes the glyph data and stores the points in its own\r
-// internal array. If the flag isDirty is set, it also encodes the internal\r
-// array and stores it to the 'glyf' table.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::SetGlyph(uint16 glyphnum)\r
-{\r
- bool retval = false; // Set failure as default\r
-\r
- if (isDirty)\r
- EncodeGlyph(currentGlyph);\r
-\r
- isDirty = false;\r
-\r
- if (glyphnum < myMaxp.numGlyphs) // numofgls is 1 based ind, glyph# 0 based\r
- {\r
- currentGlyph = glyphnum;\r
- DecodeGlyph(currentGlyph);\r
- retval = true;\r
- }\r
-\r
- return retval;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL AddGlyph(int glyphnum)\r
-//\r
-// This function adds a glyph of zero size at position glyphnum. If glyphnum\r
-// is greater than the number of glyphs, glyph is added at end of list. This\r
-// glyph then becomes the current glyph.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::AddGlyph(uint16 glyphnum)\r
-{\r
- // incomplete: JLH\r
- bool retval = false;\r
-\r
- if (isDirty)\r
- EncodeGlyph(currentGlyph);\r
-\r
- isDirty = false;\r
-\r
- return retval;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Member function: BOOL DeleteGlyph(int glyphnum)\r
-//\r
-// This function deletes the glyph at position glyphnum. All glyphs after\r
-// this glyph are moved down in position.\r
-/////////////////////////////////////////////////////////////////////////////\r
-bool TTF::DeleteGlyph(uint16 glyphnum)\r
-{\r
- // incomplete: JLH\r
- bool retval = false;\r
-\r
- return retval;\r
-}\r
-\r
-//\r
-// Various & sundry member functions implementation\r
-//\r
-// NOTE: For error handling, should throw exceptions--not return values...! !!! FIX !!!\r
-//\r
-\r
-Box TTF::GetBoundingBox(void)\r
-{\r
- return Box(llx, lly, urx, ury);\r
-}\r
-\r
-GlyphPt TTF::GetPoint(uint16 pointno)\r
-{\r
- GlyphPt p;\r
-\r
- if (pointno >= MAXPOINTS)\r
- return p; // Fix to make invalid\r
-\r
- p.x = gx[pointno], p.y = gy[pointno], p.onCurve = onCurve[pointno];\r
- return p;\r
-}\r
-\r
-uint16 TTF::GetNumberOfPolys(void)\r
-{\r
- return numberOfPolys;\r
-}\r
-\r
-uint16 TTF::GetPolyEnd(uint16 polynum)\r
-{\r
- if (polynum >= numberOfPolys)\r
- return 0;\r
-\r
- return poly[polynum];\r
-}\r
-\r
-int TTF::GetPointX(uint16 pointno)\r
-{\r
- if (pointno >= MAXPOINTS)\r
- return 0;\r
-\r
- return gx[pointno];\r
-}\r
-\r
-int TTF::GetPointY(uint16 pointno)\r
-{\r
- if (pointno >= MAXPOINTS)\r
- return 0;\r
-\r
- return gy[pointno];\r
-}\r
-\r
-bool TTF::GetOnCurve(uint16 pointno)\r
-{\r
- if (pointno >= MAXPOINTS)\r
- return true;\r
-\r
- return onCurve[pointno];\r
-}\r
-\r
-bool TTF::SetOnCurve(uint16 pointno, bool state)\r
-{\r
- if (pointno >= numberOfPoints)\r
- return false;\r
-\r
- onCurve[pointno] = state;\r
- isDirty = true;\r
-\r
- return true;\r
-}\r
-\r
-bool TTF::MovePoint(uint16 pointno, int x, int y)\r
-{\r
- if (pointno >= numberOfPoints)\r
- return false;\r
-\r
- gx[pointno] = x; gy[pointno] = y;\r
- isDirty = true;\r
-\r
- return true;\r
-}\r
-\r
-bool TTF::MovePoint(uint16 pointno, GlyphPt p)\r
-{\r
- if (pointno >= numberOfPoints)\r
- return false;\r
-\r
- gx[pointno] = p.x; gy[pointno] = p.y; onCurve[pointno] = p.onCurve;\r
- isDirty = true;\r
-\r
- return true;\r
-}\r
-\r
-bool TTF::IsCompositeGlyph(void)\r
-{\r
- return isCompositeGlyph;\r
-}\r
-\r
+//
+// TTF.CPP - The TrueType class
+// by James L. Hammons
+// (C) 2005 Underground Software
+//
+// This class encapsulates all the complexity of a TrueType Font File
+// database. Included are functions to load, save, & initialize the
+// TTF database, move, add, & delete points & glyphs, i.e. manipulate
+// a TTF file in just about any way imaginable!
+//
+// JLH = James L. Hammons <jlhamm@acm.org>
+//
+// Who When What
+// --- ---------- -----------------------------------------------------------
+// JLH ??/??/199? Created this file
+//
+//
+// STILL TO BE DONE:
+//
+// - Eliminate ALL references to BYTE, WORD, SBYTE, SWORD, etc.
+//
+
+#include <stdio.h> // For file handling, etc.
+#include <stdlib.h>
+#include <string.h>
+#include "charnames.h"
+#include "ttf.h"
+
+#define TTFDEBUG
+
+#ifdef TTFDEBUG
+#include "debug.h"
+#endif
+
+#define NUMTABS 24 // Number of distinct tables
+
+
+/*void fskip(HANDLE file, uint32_t bytesToSkip)
+{
+ SetFilePointer(file, (LONG)bytesToSkip, NULL, FILE_CURRENT);
+}*/
+
+//
+// Get a BYTE from the current file...
+//
+uint8_t ReadByte(FILE * file)
+{
+ return (uint8_t)fgetc(file);
+}
+
+// The following routines get and put WORDs and DWORDs in little endian
+// format, so they should work no matter what endianess the platform that
+// holds the TTF object.
+
+//
+// Get a WORD from the current file...
+//
+uint16_t ReadWord(FILE * file)
+{
+ uint16_t word = (uint16_t)fgetc(file) << 8;
+ word |= (uint16_t)fgetc(file);
+
+ return word;
+}
+
+//
+// Get a double WORD from the current file...
+//
+uint32_t ReadDWord(FILE * file)
+{
+ uint32_t dword = 0;
+
+ for(int i=0; i<4; i++)
+ {
+ dword <<= 8;
+ dword |= (uint8_t)fgetc(file);
+ }
+
+ return dword;
+}
+
+//
+// Write a WORD to the current file...
+//
+void WriteWord(FILE * file, uint16_t word)
+{
+ fputc(word >> 8, file); // Hi byte
+ fputc(word & 0xFF, file); // Lo byte
+}
+
+//
+// Write a double WORD to the current file...
+//
+void WriteDWord(FILE * file, uint32_t dword)
+{
+ for(int i=0; i<4; i++)
+ {
+ fputc((char)(dword >> 24), file);
+ dword <<= 8;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Table extraction helper routines (inline 'em?)
+////////////////////////////////////////////////////////////////////////////
+
+//
+// Return a BYTE from a BYTE based table
+//
+uint8_t GetByte(uint8_t * table, uint32_t &ptr)
+{
+ return table[ptr++];
+}
+
+//
+// Return a WORD from a BYTE based table
+//
+uint16_t GetWord(uint8_t * table, uint32_t &ptr)
+{
+ uint16_t hi = table[ptr++];
+ uint16_t lo = table[ptr++];
+
+ return (uint16_t)((hi<<8) | lo);
+}
+
+//
+// Return a double WORD from a BYTE based table
+//
+uint32_t GetDWord(uint8_t * table, uint32_t &ptr)
+{
+ uint32_t hi1 = table[ptr++];
+ uint32_t lo1 = table[ptr++];
+ uint32_t hi2 = table[ptr++];
+ uint32_t lo2 = table[ptr++];
+
+ return (uint32_t)((hi1 << 24) | (lo1 << 16) | (hi2 << 8) | lo2);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Table storage helper routines (inline 'em?)
+////////////////////////////////////////////////////////////////////////////
+
+//
+// Store a BYTE in a BYTE based table
+//
+void SetByte(uint8_t * table, uint32_t &ptr, uint8_t data)
+{
+ table[ptr++] = data;
+}
+
+//
+// Store a WORD in a BYTE based table
+//
+void SetWord(uint8_t * table, uint32_t &ptr, uint16_t data)
+{
+ table[ptr++] = data>>8; table[ptr++] = data&0xFF;
+}
+
+//
+// Store a DWORD in a BYTE based table
+//
+void SetDWord(uint8_t * table, uint32_t &ptr, uint32_t data)
+{
+ table[ptr++] = (uint8_t)(data >> 24); table[ptr++] = (uint8_t)(data >> 16);
+ table[ptr++] = (uint8_t)(data >> 8); table[ptr++] = (uint8_t)(data & 0xFF);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Fixed point to float (& vice versa) conversions
+/////////////////////////////////////////////////////////////////////////////
+float FixedToFloat(int16_t fixed)
+{
+ return (float)fixed / 16384.0f;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// TTF Constructor
+/////////////////////////////////////////////////////////////////////////////
+TTF::TTF(void)
+{
+ loaded = false; // Set font initializer
+ isDirty = false; // Glyphs are clean
+ numberOfPolys = 0; // Set reasonable values
+ isCompositeGlyph = false; // No composite glyph (yet)
+
+ parray[0] = &EBDT; parray[1] = &EBLC; parray[2] = &EBSC; // Init pointer
+ parray[3] = <SH; parray[4] = &OS_2; parray[5] = &PCLT; // array...
+ parray[6] = &VDMX; parray[7] = &cmap; parray[8] = &cvt;
+ parray[9] = &fpgm; parray[10] = &gasp; parray[11] = &glyf;
+ parray[12] = &hdmx; parray[13] = &head; parray[14] = &hhea;
+ parray[15] = &hmtx; parray[16] = &kern; parray[17] = &loca;
+ parray[18] = &maxp; parray[19] = &name; parray[20] = &post;
+ parray[21] = &prep; parray[22] = &vhea; parray[23] = &vmtx;
+
+ larray[0] = &EBDT_len; larray[1] = &EBLC_len; larray[2] = &EBSC_len;
+ larray[3] = <SH_len; larray[4] = &OS_2_len; larray[5] = &PCLT_len;
+ larray[6] = &VDMX_len; larray[7] = &cmap_len; larray[8] = &cvt_len;
+ larray[9] = &fpgm_len; larray[10] = &gasp_len; larray[11] = &glyf_len;
+ larray[12] = &hdmx_len; larray[13] = &head_len; larray[14] = &hhea_len;
+ larray[15] = &hmtx_len; larray[16] = &kern_len; larray[17] = &loca_len;
+ larray[18] = &maxp_len; larray[19] = &name_len; larray[20] = &post_len;
+ larray[21] = &prep_len; larray[22] = &vhea_len; larray[23] = &vmtx_len;
+
+ for(uint32_t i=0; i<NUMTABS; i++)
+ *parray[i] = NULL; // Init pointers...
+
+ for(uint32_t i=0; i<MAXGLYPHS; i++)
+ glyph[i] = NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// TTF Destructor
+/////////////////////////////////////////////////////////////////////////////
+TTF::~TTF()
+{
+ ClearTables(); // This should handle deallocation correctly...
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: void Init(void)
+//
+// This function initializes the TTF object, setting reasonable values for
+// the various internal variables used by the object.
+// [distinct from New?]
+/////////////////////////////////////////////////////////////////////////////
+void TTF::Init(void)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Clear the tables so that file can be reloaded
+/////////////////////////////////////////////////////////////////////////////
+void TTF::ClearTables(void)
+{
+ for(uint32_t i=0; i<NUMTABS; i++)
+ {
+ if ((*parray[i]) != NULL)
+ {
+ free(*parray[i]);
+ *parray[i] = NULL;
+ *larray[i] = 0;
+ }
+ }
+
+ for(uint32_t i=0; i<MAXGLYPHS; i++)
+ {
+ if (glyph[i] != NULL)
+ {
+ free(glyph[i]);
+ glyph[i] = NULL;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL Load(const char * filename)
+//
+// This loads the TTF database from an external file. Returns 'true' if
+// successful.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::Load(const char * filename)
+{
+// unsigned char ch; // Temp variable
+// UINT num_tabs; // Number of tables
+ uint32_t offset[NUMTABS], length[NUMTABS]; // Temporary storage...
+ char names[NUMTABS][5];
+ char narray[NUMTABS][5] = { "EBDT", "EBLC", "EBSC", "LTSH", "OS/2", "PCLT",
+ "VDMX", "cmap", "cvt ", "fpgm", "gasp", "glyf", "hdmx", "head", "hhea",
+ "hmtx", "kern", "loca", "maxp", "name", "post", "prep", "vhea", "vmtx" };
+
+ //loaded = false;
+// file.open(filename, ios::binary|ios::in); // Open the file
+ FILE * file = fopen(filename, "rb");
+
+ if (file == NULL)
+#ifdef TTFDEBUG
+{
+WriteLogMsg("Failed to open file!\n");
+#endif
+ return false;
+#ifdef TTFDEBUG
+}
+#endif
+
+ if (ReadDWord(file) != 0x00010000)
+#ifdef TTFDEBUG
+{
+WriteLogMsg("File was NOT a TTF file!\n");
+#endif
+ return false; // Not a TTF file...
+#ifdef TTFDEBUG
+}
+#endif
+
+ uint32_t num_tabs = ReadWord(file); // Get # of tables
+#ifdef TTFDEBUG
+WriteLogMsg("Number of tables is %u...\n", num_tabs);
+#endif
+// fskip(file, 6); // Skip this shiat...
+ fseek(file, 6, SEEK_CUR);
+
+#ifdef TTFDEBUG
+WriteLogMsg("Reading names of tables...\n");
+#endif
+ for(uint32_t i=0; i<num_tabs; i++)
+ {
+// ReadFile(file, names[i], 4, &bytesRead, NULL);
+ fread(names[i], 1, 4, file);
+ names[i][4] = 0;
+// fskip(file, 4); // Checksum
+ fseek(file, 4, SEEK_CUR); // Checksum
+ offset[i] = ReadDWord(file); // Offset from start of file
+ length[i] = ReadDWord(file); // Length of table
+ }
+
+#ifdef TTFDEBUG
+WriteLogMsg("Reading tables...\n");
+#endif
+ for(uint32_t i=0; i<num_tabs; i++)
+ {
+ for(uint32_t j=0; j<NUMTABS; j++)
+ {
+ if ((strcmp(names[i], narray[j])) == 0) // Found a match...
+ {
+// *parray[j] = (uint8_t *)GlobalAlloc(GMEM_FIXED, length[i]); // Allocate space
+ *parray[j] = (uint8_t *)malloc(length[i]); // Alloc space
+
+ if (*parray[j] == NULL)
+ return false; // Bail out if nothing allocated
+
+ *larray[j] = length[i]; // Set its length...
+// SetFilePointer(file, (LONG)offset[i], NULL, FILE_BEGIN);
+// ReadFile(file, *parray[j], length[i], &bytesRead, NULL);
+ fseek(file, offset[i], SEEK_SET);
+ fread(*parray[j], 1, length[i], file);
+ break;
+ }
+ }
+ }
+
+ fclose(file);
+
+// This shouldn't be necessary, since it's irrelevant (loaded flag)
+// loaded = true; // Set 'loaded' flag...
+ isDirty = false; // Glyphs are clean
+ ExtractTables(); // Move table data to my structs
+ // & ignore return val
+ // (should handle errors here)
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL Save(const char * filename)
+//
+// Save the TT font currently in the object
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::Save(const char * filename)
+{
+// fstream file;
+// ULONG offset = 12;
+ uint32_t numtabs = 0;
+ char padding[3] = { 0, 0, 0 };
+ // Convert this to a table of ULONGs to decrease complexity...
+//wouldn't be endian safe then...
+ char narray[NUMTABS][5] = { "EBDT", "EBLC", "EBSC", "LTSH", "OS/2", "PCLT",
+ "VDMX", "cmap", "cvt ", "fpgm", "gasp", "glyf", "hdmx", "head", "hhea",
+ "hmtx", "kern", "loca", "maxp", "name", "post", "prep", "vhea", "vmtx" };
+
+ if (isDirty)
+ EncodeGlyph(currentGlyph);
+
+ BuildTables(); // Ignore return value...
+
+ for(uint32_t i=0; i<NUMTABS; i++) // Figure out how many tables there are
+ if ((*parray[i]) != NULL)
+ numtabs++;
+
+ uint32_t offset = 12 + (numtabs * 16); // Calc correct offset to start of data
+
+// HANDLE file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+// NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ FILE * file = fopen(filename, "wb");
+
+ WriteDWord(file, 0x00010000); // Write header...
+ WriteWord(file, numtabs);
+ WriteWord(file, 0); // SearchRange (Max power of 2 <= numTables) x 16.
+ WriteWord(file, 0); // entrySelector Log2(max power of 2 <= numTables).
+ WriteWord(file, 0); // NumTables x 16 - searchRange.
+
+ for(uint32_t i=0; i<NUMTABS; i++) // Write out table directory...
+ {
+ if ((*parray[i]) != NULL)
+ {
+// for(int j=0; j<4; j++)
+// file.put(narray[i][j]); // Name
+// WriteFile(file, narray[i], 4, &bytesWritten, NULL);
+ fwrite(narray[i], 1, 4, file); // Name
+
+ WriteDWord(file, 0); // Checksum
+ WriteDWord(file, offset); // Offset
+ WriteDWord(file, (*larray[i])); // Length
+
+ offset += (((*larray[i]) + 3) & ~3); // Pad out to 4-uint8_t boundary...
+ }
+ }
+
+ for(uint32_t i=0; i<NUMTABS; i++) // Write out the tables...
+ {
+ if ((*parray[i]) != NULL)
+ {
+// for(uint32_t j=0; j<(*larray[i]); j++)
+// file.put((*parray[i])[j]);
+// WriteFile(file, *parray[i], *larray[i], &bytesWritten, NULL);
+ fwrite(*parray[i], 1, *larray[i], file);
+
+ uint32_t remainder = ((*larray[i]) & 0x3);
+ if (remainder) // i.e., it's not evenly div by 4
+// for(j=remainder; j<4; j++)
+// file.put((char)0); // pad it!
+// WriteFile(file, padding, 4 - remainder, &bytesWritten, NULL);
+ fwrite(padding, 1, 4 - remainder, file);
+ }
+ }
+
+ fclose(file);
+
+ return true; // Whether or not it was successful...
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL EncodeGlyph(int glyphnum)
+//
+// This function encodes the glyph data and stores it to the 'glyf' table.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::EncodeGlyph(uint16_t glyphnum)
+{
+ bool retval = false; // Assume failure
+ uint8_t flag, xbuf[4000], ybuf[4000], fbuf[2000];
+ uint32_t xp = 0, yp = 0, fp = 0; // Table pointers
+
+ if (glyphnum < myMaxp.numGlyphs) // numofgls is 1 based ind, glyph# 0 based
+ {
+ // need to add composite encoding...
+ int lastx = 0, lasty = 0;
+
+ for(uint32_t i=0; i<numberOfPoints; i++)
+ {
+ flag = 0;
+ int curx = gx[i] - lastx, cury = gy[i] - lasty;
+
+ if (onCurve[i])
+ flag |= 0x01; // Set on curve info
+
+ if (curx)
+ {
+ if ((curx > 255) || (curx < -255)) // I.e., it's 2 uint8_t value
+ SetWord(xbuf, xp, curx);
+ else
+ {
+ if (curx & 0x100)
+ {
+ flag |= 0x02; // I.e., it's negative
+ curx *= -1; // Flip it so correct value is stored
+ }
+ else
+ flag |= 0x12;
+
+ SetByte(xbuf, xp, curx); // Automagically strips neg bit
+ }
+ }
+ else
+ flag |= 0x10; // X-coord is same...
+
+ if (cury)
+ {
+ if ((cury > 255) || (cury < -255)) // I.e., it's 2 uint8_t value
+ SetWord(ybuf, yp, cury);
+ else
+ {
+ if (cury & 0x100)
+ {
+ flag |= 0x04; // I.e., it's negative
+ cury *= -1;
+ }
+ else
+ flag |= 0x24;
+
+ SetByte(ybuf, yp, cury);
+ }
+ }
+ else
+ flag |= 0x20; // Y-coord is same...
+
+ fbuf[i] = flag;
+ lastx = gx[i]; lasty = gy[i];
+ }
+
+ // Now crunch flags... ugly, ugly, ugly.
+/*
+ fbuf[numberOfPoints] = 0; // Set sentinel value (ugly way to do this)
+ for(i=0; i<numberOfPoints; i++);
+ {
+ if (fbuf[i] == fbuf[i+1]) //
+ {
+ uint8_t count = 0; // Sentinel takes care of check for end of flags...
+ while (fbuf[i] == fbuf[++i]) count++; // Count number of repeats
+ i--;
+ fbuf[fp++] = fbuf[i] | 0x08; // Set repeat flag
+ fbuf[fp++] = count; // & number of repeats
+ }
+ else fbuf[fp++] = fbuf[i]; // Otherwise, just copy...
+ }
+*/
+ fp = numberOfPoints;
+ // Find length of glyph and reallocate space if necessary
+
+ uint32_t newLength = 12 + numberOfPolys*2 + numberOfHints + fp + xp + yp;
+
+ if (newLength & 0x03)
+ newLength += (4 - newLength & 0x03);
+
+ if (glyphLen[glyphnum] != newLength)
+ {
+ glyph[glyphnum] = (uint8_t *)realloc(glyph[glyphnum], newLength);
+ glyphLen[glyphnum] = newLength;
+ }
+
+ // And finally, store it!
+
+ uint32_t gp = 0; // Glyph pointer...
+ SetWord(glyph[glyphnum], gp, numberOfPolys);
+ SetWord(glyph[glyphnum], gp, llx);
+ SetWord(glyph[glyphnum], gp, lly);
+ SetWord(glyph[glyphnum], gp, urx);
+ SetWord(glyph[glyphnum], gp, ury);
+ for(uint32_t i=0; i<numberOfPolys; i++)
+ SetWord(glyph[glyphnum], gp, poly[i]);
+ SetWord(glyph[glyphnum], gp, numberOfHints);
+ for(uint32_t i=0; i<numberOfHints; i++)
+ SetByte(glyph[glyphnum], gp, hint[i]);
+ for(uint32_t i=0; i<fp; i++)
+ SetByte(glyph[glyphnum], gp, fbuf[i]);
+ for(uint32_t i=0; i<xp; i++)
+ SetByte(glyph[glyphnum], gp, xbuf[i]);
+ for(uint32_t i=0; i<yp; i++)
+ SetByte(glyph[glyphnum], gp, ybuf[i]);
+
+ retval = true; // Successfully encoded!
+ }
+
+ return retval;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL DecodeGlyph(int glyphnum)
+//
+// This function decodes the glyph data and stores it to the object's
+// internal array.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::DecodeGlyph(uint16_t glyphnum)
+{
+ bool retval = false;
+ uint32_t i, dp;
+
+ // glyphnum is 0 based index, while numGlyphs is 1 based
+ if (glyphnum >= myMaxp.numGlyphs)
+ return false; // Invalid char #
+
+ if (!glyphLen[glyphnum])
+ {
+ numberOfPoints = numberOfPolys = 0;
+ return true; // Zero length IS valid...
+ }
+// else // Get character data...
+// {
+ // Now get the character data...
+ dp = 0; // Reset data pointer
+ isCompositeGlyph = false; // Default is no
+ numberOfPolys = GetWord(glyph[glyphnum], dp); // # of polygons
+
+ llx = (int16_t)GetWord(glyph[glyphnum], dp); // Lower left X
+ lly = (int16_t)GetWord(glyph[glyphnum], dp); // Lower left Y
+ urx = (int16_t)GetWord(glyph[glyphnum], dp); // Upper right X
+ ury = (int16_t)GetWord(glyph[glyphnum], dp); // Upper right Y
+
+ // Need to handle composite glyphs better here. The ways things
+ // are set now is a recipe for disaster...
+
+ if (numberOfPolys == 0xFFFF)
+ {
+ isCompositeGlyph = true;
+ numberOfPolys = 0; // Set for no points
+
+ Composite cmpst;
+
+ while (!compositeList.IsEmpty()) // Empty composite list...
+ compositeList.GetFront();
+
+ do
+ {
+ cmpst.flags = GetWord(glyph[glyphnum], dp);
+ cmpst.glyphIndex = GetWord(glyph[glyphnum], dp);
+ cmpst.arg1 = (cmpst.flags & 0x01 ? (int16_t)GetWord(glyph[glyphnum], dp) : (int8_t)GetByte(glyph[glyphnum], dp));
+ cmpst.arg2 = (cmpst.flags & 0x01 ? (int16_t)GetWord(glyph[glyphnum], dp) : (int8_t)GetByte(glyph[glyphnum], dp));
+
+ if (cmpst.flags & 0x08)
+ cmpst.xScale = cmpst.yScale = FixedToFloat((int16_t)GetWord(glyph[glyphnum], dp));
+ else if (cmpst.flags & 0x40)
+ cmpst.xScale = FixedToFloat((int16_t)GetWord(glyph[glyphnum], dp)),
+ cmpst.yScale = FixedToFloat((int16_t)GetWord(glyph[glyphnum], dp));
+ else if (cmpst.flags & 0x80)
+ cmpst.xScale = FixedToFloat((int16_t)GetWord(glyph[glyphnum], dp)),
+ cmpst.scale01 = FixedToFloat((int16_t)GetWord(glyph[glyphnum], dp)),
+ cmpst.scale10 = FixedToFloat((int16_t)GetWord(glyph[glyphnum], dp)),
+ cmpst.yScale = FixedToFloat((int16_t)GetWord(glyph[glyphnum], dp));
+
+ compositeList.AddAtRear(cmpst);
+ }
+ while (cmpst.flags & 0x20); //*/
+
+ return true;
+ }
+
+//do {
+// USHORT flags;
+// USHORT glyphIndex;
+// if ( flags & ARG_1_AND_2_ARE_uint16_tS) {
+// (SHORT or Fuint16_t) argument1;
+// (SHORT or Fuint16_t) argument2;
+// } else {
+// USHORT arg1and2; /* (arg1 << 8) | arg2 */
+// }
+// if ( flags & WE_HAVE_A_SCALE ) {
+// F2Dot14 scale; /* Format 2.14 */
+// } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) {
+// F2Dot14 xscale; /* Format 2.14 */
+// F2Dot14 yscale; /* Format 2.14 */
+// } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) {
+// F2Dot14 xscale; /* Format 2.14 */
+// F2Dot14 scale01; /* Format 2.14 */
+// F2Dot14 scale10; /* Format 2.14 */
+// F2Dot14 yscale; /* Format 2.14 */
+// }
+//} while ( flags & MORE_COMPONENTS )
+//if (flags & WE_HAVE_INSTR){
+// USHORT numInstr
+// uint8_t instr[numInstr]
+//
+//Flags Bit Description
+//ARG_1_AND_2_ARE_uint16_tS 0 If this is set, the arguments are uint16_ts;
+// otherwise, they are uint8_ts.
+//ARGS_ARE_XY_VALUES 1 If this is set, the arguments are xy values;
+// otherwise, they are points.
+//ROUND_XY_TO_GRID 2 For the xy values if the preceding is true.
+//WE_HAVE_A_SCALE 3 This indicates that there is a simple scale
+// for the component. Otherwise, scale = 1.0.
+//RESERVED 4 This bit is reserved. Set it to 0.
+//MORE_COMPONENTS 5 Indicates at least one more glyph after this
+// one.
+//WE_HAVE_AN_X_AND_Y_SCALE 6 The x direction will use a different scale
+// from the y direction.
+//WE_HAVE_A_TWO_BY_TWO 7 There is a 2 by 2 transformation that will
+// be used to scale the component.
+//WE_HAVE_INSTRUCTIONS 8 Following the last component are
+// instructions for the composite character.
+//USE_MY_METRICS 9 If set, this forces the aw and lsb (and rsb)
+// for the composite to be equal to those from
+// this original glyph. This works for hinted
+// and unhinted characters.
+
+ for(i=0; i<numberOfPolys; i++)
+ poly[i] = GetWord(glyph[glyphnum], dp);
+
+ numberOfHints = GetWord(glyph[glyphnum], dp);
+
+ for(i=0; i<numberOfHints; i++)
+ hint[i] = GetByte(glyph[glyphnum], dp);
+
+ // Decode the dots...
+
+ uint32_t num_pts = poly[numberOfPolys-1] + 1;
+ numberOfPoints = num_pts; // necessary??
+ uint32_t xptr, yptr; // pointers to beginning of coord data
+ uint32_t numXs = 0;
+ int xx = 0, yy = 0, repeat;
+ uint32_t numTokens = num_pts, k, numRep;
+
+ // We make an educated guess that num_pts = num_tokens; but if there
+ // is repeated data in the tokens, then we need to adjust the # of
+ // tokens down appropriately.
+
+ for(uint32_t i=0; i<numTokens; i++)
+ {
+ uint8_t token = glyph[glyphnum][dp+i]; uint32_t rpts = 1;
+
+ if (token & 0x08) // Repeated token?
+ {
+ i++; // Yes, bump pointer to # of times to repeat
+ numRep = glyph[glyphnum][dp+i];
+ rpts += numRep;
+
+ if (numRep > 1) // Do we need to adjust numTokens?
+ // this is bad, modifying numTokens while it's used as a compare value
+ // in the above loop...
+ // Is it necessary to do so in order to keep from going too far?
+ numTokens -= (numRep - 1); // Yes, adjust.
+ }
+
+ // 10 = same x(0uint8_ts), 00 = 2uint8_ts, 02&12 = 1uint8_t
+ if ((token & 0x02) == 0x02)
+ numXs += rpts;
+
+ if ((token & 0x12) == 0x00)
+ numXs += (rpts*2);
+ }
+
+ xptr = dp + numTokens;
+ yptr = dp + numTokens + numXs;
+
+ // & continue decoding X/Y-coords...
+
+ k = 0; // Index to the point array
+
+ for(uint32_t i=0; i<numTokens; i++)
+ {
+ uint8_t token = glyph[glyphnum][dp+i];
+ repeat = 1;
+
+ if (token & 0x08)
+ {
+ i++;
+ repeat += glyph[glyphnum][dp+i]; // Set repeat data...
+ }
+
+ while (repeat--)
+ {
+ if ((token & 0x12) == 0x12)
+ xx += GetByte(glyph[glyphnum], xptr);
+
+ if ((token & 0x12) == 0x02)
+ xx -= GetByte(glyph[glyphnum], xptr);
+
+ if ((token & 0x12) == 0x00)
+ xx += (int16_t)GetWord(glyph[glyphnum], xptr);
+
+ gx[k] = xx; // Store x-coordinate
+
+ if ((token & 0x24) == 0x24)
+ yy += GetByte(glyph[glyphnum], yptr);
+
+ if ((token & 0x24) == 0x04)
+ yy -= GetByte(glyph[glyphnum], yptr);
+
+ if ((token & 0x24) == 0x00)
+ yy += (int16_t)GetWord(glyph[glyphnum], yptr);
+
+ gy[k] = yy; // Store y-coordinate
+
+ onCurve[k++] = (token & 0x01 ? true : false); // If bit 0 set, then it's on curve
+ }
+ }
+
+ retval = true; // Hmm. Successfully decoded a glyph...
+// }
+
+ isDirty = false; // do it here?
+
+ return retval;
+}
+
+/*****************************************************************************
+ Member function:
+
+ This function decodes the 'glyf' data for a non-composite (atomic) glyph and
+ returns it as a GlyphPoints object. Helper function.
+ *****************************************************************************/
+GlyphPoints TTF::GetGlyphPoints(uint16_t glyphNum)
+{
+ if (DecodeGlyph(glyphNum))
+ return GlyphPoints(numberOfPoints, numberOfPolys, gx, gy, onCurve, poly);
+
+ return GlyphPoints();
+}
+
+/*****************************************************************************
+ Member function:
+
+ This function decodes the 'glyf' data for a composite glyph and returns
+ it as a GlyphPoints object.
+ *****************************************************************************/
+GlyphPoints TTF::GetAllCompositePoints(uint16_t glyphNum)
+{
+// int tmpGlyph = currentGlyph;
+
+ DecodeGlyph(glyphNum); // Check for composite-ness
+
+ if (!isCompositeGlyph)
+ return GlyphPoints();
+
+/*
+ for(int i=1; i<=pDoc->m_myFont.compositeList.Length(); i++)
+ {
+ TComposite cmpst = fnt->compositeList.PeekPosition(i);
+ fnt->SetGlyph(cmpst.glyphIndex);
+ if (cmpst.flags & 0x0002)
+ m_nXOffset = cmpst.arg1, m_nYOffset = cmpst.arg2;
+ ScanConvertSingleGlyph(pDC, nScanlines, nHorzLines, nXOffset, nYOffset);
+ }
+ fnt->SetGlyph(pDoc->character_num);
+*/
+
+ GlyphPoints retVal;
+
+ for(int i=1; i<=compositeList.Length(); i++)
+ {
+ Composite cmpst = compositeList.PeekPosition(i);
+
+// SetGlyph(cmpst.glyphIndex);
+// if (cmpst.flags & 0x0002)
+// m_nXOffset = cmpst.arg1, m_nYOffset = cmpst.arg2;
+// ScanConvertSingleGlyph(pDC, nScanlines, nHorzLines, nXOffset, nYOffset);
+ GlyphPoints gp = GetGlyphPoints(cmpst.glyphIndex);
+
+ if (cmpst.flags & 0x0002)
+ gp.OffsetPoints(cmpst.arg1, cmpst.arg2);
+
+ retVal = retVal + gp;
+ // NOTE: May have to adjust scanlines as per 0x0002 above...
+ }
+
+// SetGlyph(tmpGlyph);
+ DecodeGlyph(currentGlyph); // Reset needed here...
+
+ return retVal;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: void GetCharName(int cNum, uint8_t * buf)
+//
+// This function returns the character name of the glyph number passed in.
+/////////////////////////////////////////////////////////////////////////////
+void TTF::GetCharName(int cNum, uint8_t * buf)
+{
+ buf[0] = 0; // Set failure as default condition
+
+ if (!post) // PS names are here...
+ return;
+
+ if (post[1] != 0x02 || post[2] != 0x00) // i.e., it's NOT a V2.0 table
+ return;
+
+ uint8_t * pTab = NULL;
+ uint32_t tabOff = 34, numGlyphs = (uint32_t)((post[32] << 8) | post[33]);
+ uint32_t index = (uint32_t)((post[tabOff + cNum * 2] << 8) | post[tabOff + cNum * 2 + 1]);
+ uint32_t nInd2;
+
+ if (index > 257)
+ {
+ index -= 258;
+ nInd2 = tabOff + (2 * numGlyphs);
+ pTab = post;
+ }
+ else
+ {
+ nInd2 = 0;
+ pTab = macStdNames;
+ }
+
+ for(uint32_t i=0; i<index; i++)
+ nInd2 = nInd2 + pTab[nInd2] + 1; // 1st uint8_t is length of string + that uint8_t
+
+ uint8_t len = pTab[nInd2];
+ nInd2++;
+
+ for(uint8_t i=0; i<len; i++)
+ buf[i] = pTab[nInd2 + i];
+
+ buf[len] = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL ExtractTables(void)
+//
+// This function extracts the data from the various tables and puts them in
+// various structs for easier handling of the font data.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::ExtractTables(void)
+{
+ if (head_len != 54)
+ {
+#ifdef TTFDEBUG
+WriteLogMsg("Bad HEAD header: Expected 54, found %u...\n", head_len);
+#endif
+ return false; // Corrupt data?
+ }
+
+ uint32_t tp = 0;
+ myHead.version = GetDWord(head, tp);
+ myHead.fontRevision = GetDWord(head, tp);
+ myHead.checkSumAdjustment = GetDWord(head, tp);
+ myHead.magicNumber = GetDWord(head, tp);
+ myHead.flags = GetWord(head, tp);
+ myHead.unitsPerEm = GetWord(head, tp);
+ myHead.createdh = GetDWord(head, tp);
+ myHead.createdl = GetDWord(head, tp);
+ myHead.modifiedh = GetDWord(head, tp);
+ myHead.modifiedl = GetDWord(head, tp);
+ myHead.xMin = GetWord(head, tp);
+ myHead.yMin = GetWord(head, tp);
+ myHead.xMax = GetWord(head, tp);
+ myHead.yMax = GetWord(head, tp);
+ myHead.macStyle = GetWord(head, tp);
+ myHead.lowestRecPPEM = GetWord(head, tp);
+ myHead.fontDirectionHint = (int16_t)GetWord(head, tp);
+ myHead.indexToLocFormat = (int16_t)GetWord(head, tp);
+ myHead.glyphDataFormat = (int16_t)GetWord(head, tp);
+
+ if (maxp_len != 32)
+ {
+#ifdef TDEBUG
+WriteLogMsg("Bad MAXP header: Expected 32, found %u...\n", maxp_len);
+#endif
+ return false; // Corrupt data?
+ }
+
+ tp = 0; // Reset table pointer
+ myMaxp.version = GetDWord(maxp, tp);
+ myMaxp.numGlyphs = GetWord(maxp, tp);
+ myMaxp.maxPoints = GetWord(maxp, tp);
+ myMaxp.maxContours = GetWord(maxp, tp);
+ myMaxp.maxCompositePoints = GetWord(maxp, tp);
+ myMaxp.maxCompositeContours = GetWord(maxp, tp);
+ myMaxp.maxZones = GetWord(maxp, tp);
+ myMaxp.maxTwilightPoints = GetWord(maxp, tp);
+ myMaxp.maxStorage = GetWord(maxp, tp);
+ myMaxp.maxFunctionDefs = GetWord(maxp, tp);
+ myMaxp.maxInstructionDefs = GetWord(maxp, tp);
+ myMaxp.maxStackElements = GetWord(maxp, tp);
+ myMaxp.maxSizeOfInstructions = GetWord(maxp, tp);
+ myMaxp.maxComponentElements = GetWord(maxp, tp);
+ myMaxp.maxComponentDepth = GetWord(maxp, tp);
+
+ tp = 0; // Reset table pointer
+ uint32_t start = (myHead.indexToLocFormat ? GetDWord(loca, tp) : GetWord(loca, tp) << 1);
+
+ for(uint32_t i=0; i<myMaxp.numGlyphs; i++)
+ {
+ uint32_t end = (myHead.indexToLocFormat ? GetDWord(loca, tp) : GetWord(loca, tp) << 1);
+ uint32_t length = end - start;
+ glyphLen[i] = length; // Lengths are saved 'cause malloc is sloppy
+
+ if (length) // loca+start? pointer arithmetic?
+ {
+ glyph[i] = (uint8_t *)malloc(length); // Allocate space,
+ memcpy(glyph[i], glyf+start, length); // and move it!
+ }
+ else
+ glyph[i] = NULL;
+
+ start = end; // Reset start value
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL BuildTables(void)
+//
+// This function builds the various TTF tables using info in the various
+// structs so that a TTF file can be written out to disk.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::BuildTables(void)
+{
+ uint32_t i, tp, start;
+
+ myHead.indexToLocFormat = 1; // We don't bother with [uint16_ts*2] format...
+
+ // Build HEAD table
+
+ tp = 0; // Reset table pointer
+ SetDWord(head, tp, myHead.version);// = GetDWord(head, tp);
+ SetDWord(head, tp, myHead.fontRevision);// = GetDWord(head, tp);
+ SetDWord(head, tp, myHead.checkSumAdjustment);// = GetDWord(head, tp);
+ SetDWord(head, tp, myHead.magicNumber);// = GetDWord(head, tp);
+ SetWord(head, tp, myHead.flags);// = GetWord(head, tp);
+ SetWord(head, tp, myHead.unitsPerEm);// = GetWord(head, tp);
+ SetDWord(head, tp, myHead.createdh);// = GetDWord(head, tp);
+ SetDWord(head, tp, myHead.createdl);// = GetDWord(head, tp);
+ SetDWord(head, tp, myHead.modifiedh);// = GetDWord(head, tp);
+ SetDWord(head, tp, myHead.modifiedl);// = GetDWord(head, tp);
+ SetWord(head, tp, myHead.xMin);// = GetWord(head, tp);
+ SetWord(head, tp, myHead.yMin);// = GetWord(head, tp);
+ SetWord(head, tp, myHead.xMax);// = GetWord(head, tp);
+ SetWord(head, tp, myHead.yMax);// = GetWord(head, tp);
+ SetWord(head, tp, myHead.macStyle);// = GetWord(head, tp);
+ SetWord(head, tp, myHead.lowestRecPPEM);// = GetWord(head, tp);
+ SetWord(head, tp, myHead.fontDirectionHint);// = (int16_t)GetWord(head, tp);
+ SetWord(head, tp, myHead.indexToLocFormat);// = (int16_t)GetWord(head, tp);
+ SetWord(head, tp, myHead.glyphDataFormat);// = (int16_t)GetWord(head, tp);
+
+ // Build MAXP table
+
+ tp = 0; // Reset table pointer
+ SetDWord(maxp, tp, myMaxp.version);// = GetDWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.numGlyphs);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxPoints);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxContours);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxCompositePoints);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxCompositeContours);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxZones);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxTwilightPoints);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxStorage);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxFunctionDefs);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxInstructionDefs);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxStackElements);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxSizeOfInstructions);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxComponentElements);// = GetWord(maxp, tp);
+ SetWord(maxp, tp, myMaxp.maxComponentDepth);// = GetWord(maxp, tp);
+
+ // Build LOCA & GLYF tables
+
+ loca_len = (myMaxp.numGlyphs+1) * 4; // Set size of table
+
+ if (loca)
+ free(loca); // And create/reallocate it...
+
+ loca = (uint8_t *) malloc(loca_len);
+
+ glyf_len = 0; // Refigure glyf table length
+
+ for(i=0; i<myMaxp.numGlyphs; i++)
+ glyf_len += glyphLen[i];
+
+ if (glyf)
+ free(glyf);
+
+ glyf = (uint8_t *) malloc(glyf_len);
+
+ start = tp = 0; // Reset table pointer
+
+ for(i=0; i<myMaxp.numGlyphs; i++)
+ {
+ SetDWord(loca, tp, start); // Store glyph start address
+
+ if (glyphLen[i])
+ memcpy(glyf+start, glyph[i], glyphLen[i]);
+
+ start += glyphLen[i];
+ }
+
+ SetDWord(loca, tp, start); // Finally, store end of glyphs+1
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL SetGlyph(int glyphnum)
+//
+// This function decodes the glyph data and stores the points in its own
+// internal array. If the flag isDirty is set, it also encodes the internal
+// array and stores it to the 'glyf' table.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::SetGlyph(uint16_t glyphnum)
+{
+ bool retval = false; // Set failure as default
+
+ if (isDirty)
+ EncodeGlyph(currentGlyph);
+
+ isDirty = false;
+
+ if (glyphnum < myMaxp.numGlyphs) // numofgls is 1 based ind, glyph# 0 based
+ {
+ currentGlyph = glyphnum;
+ DecodeGlyph(currentGlyph);
+ retval = true;
+ }
+
+ return retval;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL AddGlyph(int glyphnum)
+//
+// This function adds a glyph of zero size at position glyphnum. If glyphnum
+// is greater than the number of glyphs, glyph is added at end of list. This
+// glyph then becomes the current glyph.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::AddGlyph(uint16_t glyphnum)
+{
+ // incomplete: JLH
+ bool retval = false;
+
+ if (isDirty)
+ EncodeGlyph(currentGlyph);
+
+ isDirty = false;
+
+ return retval;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Member function: BOOL DeleteGlyph(int glyphnum)
+//
+// This function deletes the glyph at position glyphnum. All glyphs after
+// this glyph are moved down in position.
+/////////////////////////////////////////////////////////////////////////////
+bool TTF::DeleteGlyph(uint16_t glyphnum)
+{
+ // incomplete: JLH
+ bool retval = false;
+
+ return retval;
+}
+
+//
+// Various & sundry member functions implementation
+//
+// NOTE: For error handling, should throw exceptions--not return values...! !!! FIX !!!
+//
+
+Box TTF::GetBoundingBox(void)
+{
+ return Box(llx, lly, urx, ury);
+}
+
+GlyphPt TTF::GetPoint(uint16_t pointno)
+{
+ GlyphPt p;
+
+ if (pointno >= MAXPOINTS)
+ return p; // Fix to make invalid
+
+ p.x = gx[pointno], p.y = gy[pointno], p.onCurve = onCurve[pointno];
+ return p;
+}
+
+uint16_t TTF::GetNumberOfPolys(void)
+{
+ return numberOfPolys;
+}
+
+uint16_t TTF::GetPolyEnd(uint16_t polynum)
+{
+ if (polynum >= numberOfPolys)
+ return 0;
+
+ return poly[polynum];
+}
+
+int TTF::GetPointX(uint16_t pointno)
+{
+ if (pointno >= MAXPOINTS)
+ return 0;
+
+ return gx[pointno];
+}
+
+int TTF::GetPointY(uint16_t pointno)
+{
+ if (pointno >= MAXPOINTS)
+ return 0;
+
+ return gy[pointno];
+}
+
+bool TTF::GetOnCurve(uint16_t pointno)
+{
+ if (pointno >= MAXPOINTS)
+ return true;
+
+ return onCurve[pointno];
+}
+
+bool TTF::SetOnCurve(uint16_t pointno, bool state)
+{
+ if (pointno >= numberOfPoints)
+ return false;
+
+ onCurve[pointno] = state;
+ isDirty = true;
+
+ return true;
+}
+
+bool TTF::MovePoint(uint16_t pointno, int x, int y)
+{
+ if (pointno >= numberOfPoints)
+ return false;
+
+ gx[pointno] = x; gy[pointno] = y;
+ isDirty = true;
+
+ return true;
+}
+
+bool TTF::MovePoint(uint16_t pointno, GlyphPt p)
+{
+ if (pointno >= numberOfPoints)
+ return false;
+
+ gx[pointno] = p.x; gy[pointno] = p.y; onCurve[pointno] = p.onCurve;
+ isDirty = true;
+
+ return true;
+}
+
+bool TTF::IsCompositeGlyph(void)
+{
+ return isCompositeGlyph;
+}
+