//------------------------------------------------ //--- 010 Editor v2.0 Binary Template // // File: TTF.bt // Authors: James Newton of massmind.org and Alex McDonnell of Cisco Systems // Version: 0.8 // Purpose: Template to parse TTF (TrueType) fonts. // Category: Font // File Mask: *.ttf,*.ttc // ID Bytes: 00 01 00 00 00 // History: // 0.8 2020-03-30 darknesswind: Add support for ttc files. // 0.7 2017-05-30 AM: So many changes for readability, style, and error checking. // 0.6 2016-01-28 SweetScape: Updated header for repository submission. // 0.5 JN: added GSUB and GPOS tables. // 0.4 JN: added prep and DSIG tables // fixed serious bug with offset and length from wrong // table being used in table structures. // 0.32 JN: reads simple glyphs with the points in CORRECT order // 0.31 JN: reads simple glyphs with the points in order // 0.3 JN: actually reads simple glyphs // 0.2 JN: reads some tables // 0.1 JN: reads the offsetTable //------------------------------------------------ BigEndian(); local int maxGlyfRead = 9; // set to zero to read all the glyph, but very slow :) //the following locals are masks for "flags" 010 v2 doesn't support #define local USHORT ARG_1_AND_2_ARE_WORDS = 1<<0; // If this is set, the arguments are words; otherwise, they are bytes. local USHORT ARGS_ARE_XY_VALUES = 1<<1; // If this is set, the arguments are xy values; otherwise, they are points. local USHORT ROUND_XY_TO_GRID = 1<<2; // For the xy values if the preceding is true. local USHORT WE_HAVE_A_SCALE = 1<<3; // This indicates that there is a simple scale for the component. Otherwise, scale = 1.0. local USHORT RESERVED = 1<<4; // This bit is reserved. Set it to 0. local USHORT MORE_COMPONENTS = 1<<5; // Indicates at least one more glyph after this one. local USHORT WE_HAVE_AN_X_AND_Y_SCALE = 1<<6; // The x direction will use a different scale from the y direction. local USHORT WE_HAVE_A_TWO_BY_TWO = 1<<7; // There is a 2 by 2 transformation that will be used to scale the component. local USHORT WE_HAVE_INSTRUCTIONS = 1<<8; // Following the last component are instructions for the composite character. local USHORT USE_MY_METRICS = 1<<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. local USHORT OVERLAP_COMPOUND = 1<<10; // Used by Apple in GX fonts. local USHORT SCALED_COMPONENT_OFFSET = 1<<11; // Composite designed to have the component offset scaled (designed for Apple rasterizer). local USHORT UNSCALED_COMPONENT_OFFSET = 1<<12; // Composite designed not to have the component offset scaled (designed for the Microsoft TrueType rasterizer). // these flags tell us: // bit name description local UBYTE ON_CURVE = 1<<0; // If set, the point is on the curve; otherwise, it is off the curve. local UBYTE X_BYTE = 1<<1; // aka "X-Short" If set, the corresponding x-coordinate is 1 byte long. If not set, 2 bytes. local UBYTE Y_BYTE = 1<<2; // aka "Y-Short" If set, the corresponding y-coordinate is 1 byte long. If not set, 2 bytes. //I believe the name x-Short was intended to mean "small" rather than the type SHORT. //I've changed the names to X_BYTE and Y_BYTE since they are BYTES if set to 1 local UBYTE REPEAT = 1<<3; // If set, the next byte specifies the number of additional times this set of flags is to be repeated. // In this way, the number of flags listed can be smaller than the number of points in a character. local UBYTE X_TYPE = 1<<4; // This flag has two meanings, depending on how the x-Short flag is set. // If x-Short is set, this bit describes the sign of the value, // with 1 equalling positive and 0 negative. // If the x-Short bit is not set and this bit is set, then // the current x-coordinate is the same as the previous x-coordinate. // If the x-Short bit is not set and this bit is also not set, // the current x-coordinate is a signed 16-bit delta vector. local UBYTE Y_TYPE = 1<<5; // This flag has two meanings, depending on how the y-Short Vector flag is set. // If y-Short Vector is set, this bit describes the sign of the value, // with 1 equalling positive and 0 negative. // If the y-Short Vector bit is not set and this bit is set, then // the current y-coordinate is the same as the previous y-coordinate. // If the y-Short Vector bit is not set and this bit is also not set, // the current y-coordinate is a signed 16-bit delta vector. local UBYTE RES_1 = 1<<6; // 6 Res This bit is reserved. Set it to zero. local UBYTE RES_2 = 1<<7; // 7 Res This bit is reserved. Set it to zero. typedef signed long TT_Fixed; /* Signed Fixed 16.16 Float */ typedef signed short TT_FWord; /* Distance in FUnits */ typedef unsigned short TT_UFWord; /* Unsigned distance */ typedef signed short TT_Short; typedef unsigned short TT_UShort; typedef signed long TT_Long; typedef unsigned long TT_ULong; typedef unsigned long TT_Offset; typedef OLETIME LONGDATETIME; typedef signed short F2Dot14; typedef struct tOffsetTable { TT_Fixed SFNT_Ver; //sfnt version 0x00010000 for version 1.0. USHORT numTables; //Number of tables. USHORT searchRange; //(Maximum power of 2 <= numTables) x 16. USHORT entrySelector; // Log2(maximum power of 2 <= numTables). USHORT rangeShift; // NumTables x 16-searchRange. }; typedef struct tTable { union { char asChar[4]; // 4 -byte identifier. ULONG asLong; } Tag; ULONG checkSum; // CheckSum for this table. ULONG offset; // Offset from beginning of TrueType font file. ULONG length; // Length of this table. }; string tTableRead( tTable &d ) { char s[50]; SPrintf(s, "Table %s at offset: %u with size: %u", d.Tag.asChar, d.offset, d.length); return s; } typedef struct tcmap_format0 { cmap_subtable = FTell(); USHORT format; // Format number is set to 0. USHORT length; // This is the length in bytes of the subtable. USHORT language; // Please see "Note on the language field in 'cmap' subtables" in this document. BYTE glyphIdArray[256]; // An array that maps character codes to glyph index values. }; typedef struct SubHeader { uint16 firstCode;// First valid low byte for this SubHeader. uint16 entryCount;// Number of valid low bytes for this SubHeader. int16 idDelta; uint16 idRangeOffset; local quad curPos = FTell(); FSeek(curPos + idRangeOffset - 2); USHORT glyphIdArray[entryCount]; FSeek(curPos); }; typedef struct tcmap_format2 { cmap_subtable = FTell(); USHORT format; // Format number is set to 0. USHORT length; // This is the length in bytes of the subtable. USHORT language; USHORT subHeaderKeys[256];// Array that maps high bytes to subHeaders: value is subHeader index . local int maxidx = 0, i = 0; for (i = 0; i < 256; ++i) if (subHeaderKeys[i] > maxidx) maxidx = subHeaderKeys[i]; maxidx = maxidx / 8; SubHeader subHeaders[maxidx]; }; typedef struct tcmap_format4 { cmap_subtable = FTell(); USHORT format; // Format number is set to 4. USHORT length; // This is the length in bytes of the subtable. USHORT language; // Please see "Note on the language field in 'cmap' subtables" in this document. USHORT segCountX2; // 2 x segCount. USHORT searchRange; // 2 x (2**floor(log2(segCount))) USHORT entrySelector; // log2(searchRange/2) USHORT rangeShift; // 2 x segCount - searchRange USHORT endCount[segCountX2 / 2]; // End characterCode for each segment, last=0xFFFF. USHORT reservedPad; // Set to 0. USHORT startCount[segCountX2 / 2]; // Start character code for each segment. SHORT idDelta[segCountX2 / 2]; // Delta for all character codes in segment. USHORT idRangeOffset[segCountX2 / 2]; // Offsets into glyphIdArray or 0 USHORT glyphIdArray[(length-(FTell()-cmap_subtable))/2 ]; // Glyph index array (arbitrary length) }; typedef struct tcmap_format6 { cmap_subtable = FTell(); USHORT format; // Format number is set to 6. USHORT length; // This is the length in bytes of the subtable. USHORT language; // Please see "Note on the language field in 'cmap' subtables" in this document. USHORT firstCode; // First character code of subrange. USHORT entryCount; // Number of character codes in subrange. USHORT glyphIdArray [entryCount]; // Array of glyph index values for character codes in the range. }; typedef struct tcmap_format8 { cmap_subtable = FTell(); USHORT format; // Subtable format; set to 8. USHORT reserved; // Reserved; set to 0 ULONG length; // Byte length of this subtable (including the header) ULONG language; // Please see "Note on the language field in 'cmap' subtables" in this document. BYTE is32[8192]; // Tightly packed array of bits (8K bytes total) indicating whether the particular 16-bit (index) value is the start of a 32-bit character code ULONG nGroups; // Number of groupings which follow struct { ULONG startCharCode; // First character code in this group; note that if this group is for one or more 16-bit character codes (which is determined from the is32 array), this 32-bit value will have the high 16-bits set to zero ULONG endCharCode; // Last character code in this group; same condition as listed above for the startCharCode ULONG startGlyphID; // Glyph index corresponding to the starting character code } groupings[nGroups]; }; typedef struct tcmap_format12 { USHORT format; // Subtable format; set to 12. USHORT reserved; // Reserved; set to 0 ULONG length; // Byte length of this subtable (including the header) ULONG language; // Please see "Note on the language field in 'cmap' subtables" in this document. ULONG nGroups; // Number of groupings which follow struct { ULONG startCharCode; // First character code in this group ULONG endCharCode; // Last character code in this group ULONG startGlyphID; // Glyph index corresponding to the starting character code } groupings[nGroups]; }; typedef struct tcmap { local quad cmap_table; local quad next_cmap_record; cmap_table = FTell(); USHORT version; // Table version number (0). USHORT numTables; // Number of encoding tables that follow. struct tEncodingRecord { local quad cmap_subtable; local quad cmap_record; cmap_record = FTell(); USHORT platformID; // Platform ID. USHORT encodingID; // Platform-specific encoding ID. ULONG offset; // Byte offset from beginning of table to the subtable for this encoding. next_cmap_record = FTell(); FSeek(cmap_table+offset); switch (ReadUShort(FTell())) { case 0: struct tcmap_format0 format0; break; case 2: struct tcmap_format2 format2; break; case 4: struct tcmap_format4 format4; break; case 6: struct tcmap_format6 format6; break; case 8: struct tcmap_format8 format8; break; //case 10: struct tcmap_format10 format10; break; //TODO case 12: struct tcmap_format12 format12; break; } FSeek(next_cmap_record); } EncodingRecord[numTables] ; }; typedef struct thdmx { USHORT version; // Table version number (0) SHORT numRecords; // Number of device records. LONG sizeDeviceRecord; // Size of a device record, long aligned. struct { local quad hdmx_DeviceRecord = FTell(); UBYTE pixelSize; // Pixel size for following widths (as ppem). UBYTE maxWidth; // Maximum width. local quad numGlyphs = (sizeDeviceRecord - (FTell() - hdmx_DeviceRecord)) / 1; UBYTE widths[numGlyphs]; // Array of widths (numGlyphs is from the 'maxp' table). } DeviceRecord[numRecords] ; }; string thdmxRead(thdmx &d) { string s; SPrintf(s, "Version %i %u records %u bytes", d.version, d.numRecords, d.sizeDeviceRecord); return s; } typedef struct thead { ttfId++; TT_Fixed version; //Table version number 0x00010000 for version 1.0. TT_Fixed fontRevision; // Set by font manufacturer. ULONG checkSumAdjustment; // To compute: set it to 0, sum the entire font as ULONG, then store 0xB1B0AFBA - sum. ULONG magicNumber; // Set to 0x5F0F3CF5. USHORT flags; // lots of flags... /* Bit 0: Baseline for font at y=0; Bit 1: Left sidebearing point at x=0; Bit 2: Instructions may depend on point size; Bit 3: Force ppem to integer values for all internal scaler math; may use fractional ppem sizes if this bit is clear; Bit 4: Instructions may alter advance width (the advance widths might not scale linearly); Bits 5-10: These should be set according to Apple's specification . However, they are not implemented in OpenType. Bit 11: Font data is 'lossless,' as a result of having been compressed and decompressed with the Agfa MicroType Express engine. Bit 12: Font converted (produce compatible metrics) Bit 13: Font optimised for ClearType Bit 14: Reserved, set to 0 Bit 15: Reserved, set to 0 */ USHORT unitsPerEm; // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines. LONGDATETIME created; // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer LONGDATETIME modified; // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer SHORT xMin; // For all glyph bounding boxes. SHORT yMin; // For all glyph bounding boxes. SHORT xMax; // For all glyph bounding boxes. SHORT yMax; // For all glyph bounding boxes. USHORT macStyle; // /* Bit 0: Bold (if set to 1); Bit 1: Italic (if set to 1) Bit 2: Underline (if set to 1) Bit 3: Outline (if set to 1) Bit 4: Shadow (if set to 1) Bit 5: Condensed (if set to 1) Bit 6: Extended (if set to 1) Bits 7-15: Reserved (set to 0). */ USHORT lowestRecPPEM; //Smallest readable size in pixels. SHORT fontDirectionHint; // /* 0: Fully mixed directional glyphs; 1: Only strongly left to right; 2: Like 1 but also contains neutrals; -1: Only strongly right to left; -2: Like -1 but also contains neutrals. 1 */ SHORT indexToLocFormat; // 0 for short offsets, 1 for long. SHORT glyphDataFormat; // 0 for current format. }; typedef struct thhea { TT_Fixed version; // Table version number 0x00010000 for version 1.0. TT_FWord Ascender; // Typographic ascent. (Distance from baseline of highest ascender) TT_FWord Descender; // Typographic descent. (Distance from baseline of lowest descender) TT_FWord LineGap; // Typographic line gap. //Negative LineGap values are treated as zero //in Windows 3.1, System 6, and //System 7. TT_UFWord advanceWidthMax; // Maximum advance width value in 'hmtx' table. TT_FWord minLeftSideBearing; // Minimum left sidebearing value in 'hmtx' table. TT_FWord minRightSideBearing; // Minimum right sidebearing value; calculated as Min(aw - lsb - (xMax - xMin)). TT_FWord xMaxExtent; // Max(lsb + (xMax - xMin)). SHORT caretSlopeRise; // Used to calculate the slope of the cursor (rise/run); 1 for vertical. SHORT caretSlopeRun; // 0 for vertical. SHORT caretOffset; // The amount by which a slanted highlight on a glyph needs to be shifted to produce the best appearance. Set to 0 for non-slanted fonts SHORT reserved; // set to 0 SHORT reserved; // set to 0 SHORT reserved; // set to 0 SHORT reserved; // set to 0 SHORT metricDataFormat; // 0 for current format. USHORT numberOfHMetrics; // Number of hMetric entries in 'hmtx' table }; string thheaRead( thhea &d ) { string s; SPrintf(s, "v%.2f %u hmtx records", d.version/65535.0, d.numberOfHMetrics); return s; } typedef struct tlongHorMetric { USHORT advanceWidth; SHORT lsb; }; typedef struct thmtx { local ulong numberOfHMetrics = ttf[ttfId - 1].hhea.numberOfHMetrics; struct tlongHorMetric hMetrics[numberOfHMetrics]; // Paired advance width and left side bearing values for each glyph. The value numOfHMetrics comes from the 'hhea' table. // If the font is monospaced, only one entry need be in the array, but that entry is required. The last entry applies to all subsequent glyphs. local ulong numLeftSideBearing = ( curTblLength - (FTell()-curTblOffset) )/2; if(numLeftSideBearing){ SHORT leftSideBearing[ numLeftSideBearing ]; } // Here the advanceWidth is assumed to be the same as the advanceWidth for the last entry above. // The number of entries in this array is derived from numGlyphs (from 'maxp' table) minus numberOfHMetrics. // This generally is used with a run of monospaced glyphs (e.g., Kanji fonts or Courier fonts). // Only one run is allowed and it must be at the end. This allows a monospaced font to vary the left side bearing values for each glyph. }; string thmtxRead(thmtx &d) { string s; SPrintf(s, "%u HMetrics %u leftSideBearing", d.numberOfHMetrics, d.numLeftSideBearing); return s; } typedef struct tmaxp { TT_Fixed version; //Table version number 0x00005000 for version 0.5 //(Note the difference in the representation of a non-zero fractional part, in Fixed numbers.) if (version == 0x00005000) { USHORT numGlyphs; // The number of glyphs in the font. } else { USHORT numGlyphs; // The number of glyphs in the font. USHORT maxPoints; // Maximum points in a non-composite glyph. USHORT maxContours; // Maximum contours in a non-composite glyph. USHORT maxCompositePoints; // Maximum points in a composite glyph. USHORT maxCompositeContours; // Maximum contours in a composite glyph. USHORT maxZones; // 1 if instructions do not use the twilight zone (Z0), or 2 if instructions do use Z0; should be set to 2 in most cases. USHORT maxTwilightPoints; // Maximum points used in Z0. USHORT maxStorage; // Number of Storage Area locations. USHORT maxFunctionDefs; // Number of FDEFs. USHORT maxInstructionDefs; // Number of IDEFs. USHORT maxStackElements; // Maximum stack depth2. USHORT maxSizeOfInstructions; // Maximum byte count for glyph instructions. USHORT maxComponentElements; // Maximum number of components referenced at "top level" for any composite glyph. USHORT maxComponentDepth; // Maximum levels of recursion; 1 for simple components. } }; string tmaxpRead(tmaxp &d) { string s; if (d.version == 0x00005000) { SPrintf( s, "v%.2f %u glyphs", d.version/65535.0, d.numGlyphs); } else { SPrintf( s, "v%.2f %u glyphs %u points %u contours", d.version/65535.0, d.numGlyphs,d.maxPoints,d.maxContours); } return s; } typedef struct tname { // http://www.microsoft.com/typography/OTSPEC/name.htm local quad name_table = FTell(); USHORT format; // Format selector (=0). USHORT count; // Number of name records. USHORT stringOffset; // Offset to start of string storage (from start of table). local quad NextNameRecord; struct tNameRecord { USHORT platformID; // Platform ID. USHORT encodingID; // Platform-specific encoding ID. USHORT languageID; // Language ID. USHORT nameID; // Name ID. USHORT length; // String length (in bytes). USHORT offset; // String offset from start of storage area (in bytes). NextNameRecord = FTell(); FSeek(name_table + stringOffset + offset); char name[length]; FSeek(NextNameRecord); } NameRecord[count] ; }; string NameRecordRead( tNameRecord &d ) { string s; SPrintf( s, "p%i E%i L%i %s", d.platformID, d.encodingID, d.languageID, d.name ); return s; } string tnameRead(tname &name) { string s; SPrintf(s, "%li Names", name.count); return s; } typedef struct tOS_2 { // http://www.microsoft.com/typography/OTSPEC/os2.htm USHORT version; // 0x0003 SHORT xAvgCharWidth; USHORT usWeightClass; USHORT usWidthClass; USHORT fsType; SHORT ySubscriptXSize; SHORT ySubscriptYSize; SHORT ySubscriptXOffset; SHORT ySubscriptYOffset; SHORT ySuperscriptXSize; SHORT ySuperscriptYSize; SHORT ySuperscriptXOffset; SHORT ySuperscriptYOffset; SHORT yStrikeoutSize; SHORT yStrikeoutPosition; SHORT sFamilyClass; struct tpanose { UBYTE bFamilyType; UBYTE bSerifStyle; UBYTE bWeight; UBYTE bProportion; UBYTE bContrast; UBYTE bStrokeVariation; UBYTE bArmStyle; UBYTE bLetterform; UBYTE bMidline; UBYTE bXHeight; } panose; ULONG ulUnicodeRange1; // Bits 0-31 ULONG ulUnicodeRange2; // Bits 32-63 ULONG ulUnicodeRange3; // Bits 64-95 ULONG ulUnicodeRange4; // Bits 96-127 CHAR achVendID[4]; USHORT fsSelection; USHORT usFirstCharIndex; USHORT usLastCharIndex; SHORT sTypoAscender; SHORT sTypoDescender; SHORT sTypoLineGap; USHORT usWinAscent; USHORT usWinDescent; ULONG ulCodePageRange1; // Bits 0-31 ULONG ulCodePageRange2; // Bits 32-63 SHORT sxHeight; SHORT sCapHeight; USHORT usDefaultChar; USHORT usBreakChar; USHORT usMaxContext; }; string tOS_2Read(tOS_2 &d) { string s; SPrintf(s, "v%i chars %i to %i from %4s", d.version, d.usFirstCharIndex, d.usLastCharIndex, d.achVendID); return s; } typedef struct tpost { local quad post = FTell(); TT_Fixed version; TT_Fixed italicAngle; // Italic angle in counter-clockwise degrees from the vertical. Zero for upright text, negative for text that leans to the right (forward). TT_FWord underlinePosition; // This is the suggested distance of the top of the underline from the baseline (negative values indicate below baseline). TT_FWord underlineThickness; // Suggested values for the underline thickness. ULONG isFixedPitch; // Set to 0 if the font is proportionally spaced, non-zero if the font is not proportionally spaced (i.e. monospaced). ULONG minMemType42; // Minimum memory usage when an OpenType font is downloaded. ULONG maxMemType42; // Maximum memory usage when an OpenType font is downloaded. ULONG minMemType1; // Minimum memory usage when an OpenType font is downloaded as a Type 1 font. ULONG maxMemType1; // Maximum memory usage when an OpenType font is downloaded as a Type 1 font. if (version == 0x00020000) { USHORT numberOfGlyphs; // Number of glyphs (this should be the same as numGlyphs in 'maxp' table). local ushort numGlyphs = numberOfGlyphs; //? Assumption TODO: Verify. Seems to work USHORT glyphNameIndex[numGlyphs]; //This is not an offset, but is the ordinal number of the glyph in 'post' string tables. local ushort numberNewGlyphs = numberOfGlyphs; //? Assumption FALSE TODO: FIX local quad end_name = post+curTblLength; local quad next_name = FTell(); while (next_name < end_name) { struct tpostName { UBYTE length; CHAR text[length]; } name ; next_name = FTell(); } // Glyph names with length bytes [variable] (a Pascal string). } if (version == 0x00025000) { USHORT numberOfGlyphs; // Number of glyphs (this should be the same as numGlyphs in 'maxp' table). local ushort numGlyphs = numberOfGlyphs; //? Assumption TODO: Verify. Seems to work USHORT offset[numGlyphs]; //This is not an offset, but is the ordinal number of the glyph in 'post' string tables. } }; string tpostNameRead( tpostName &d ) { return d.text; //use this instead to get the Unicode, etc... characters. } string tpostRead( tpost &d ) { string s; if ((d.version == 0x00020000) || (d.version == 0x00025000)){ SPrintf( s, "version %.2f %u glyphs", d.version/65535.0, d.numberOfGlyphs); } else { SPrintf(s, "version %.2f", d.version/65535.0); } if (d.isFixedPitch) { s += " fixed pitch"; } else { s += " proportional"; } return s; }; typedef struct tcvt { local quad n = curTblLength / sizeof(TT_FWord); TT_FWord value[ n ]; // List of n values referenceable by instructions. n is the number of FWORD items that fit in the size of the table. }; typedef struct tfpgm { local quad n = curTblLength / sizeof(UBYTE); UBYTE bytecode[n]; // Instructions. n is the number of BYTE items that fit in the size of the table. }; typedef struct tloca { local ulong n = ttf[ttfId-1].maxp.numGlyphs + 1; local short format = ttf[ttfId-1].head.indexToLocFormat; if (format == 0) { USHORT offsets[n]; } else { ULONG offsets[n]; } }; string tlocaRead(tloca &d) { string s; if (d.format == 0) { SPrintf(s, "%u short offsets", d.n); } else { SPrintf(s, "%u long offsets", d.n); } return s; } typedef struct tSimpleGlyphPoints { local ulong i; local quad xStart, xLast; xStart = xLast = FTell(); local quad yStart, yLast; yStart = FTell(); // Printf("xStart: %u\n",yStart); yLast = 0; local byte xLastByte = 0; local byte yLastByte = 0; for (i = 0; i < numPoints; i++) { if ((flag[i] & X_BYTE) && !(flag[i] & X_TYPE)) { yStart++; if (!(flag[i] & X_BYTE)) { yStart++; } // when flag bit 3 is not set, the x coordinate for that point is another byte long } } // Now we can decode points in pairs... for (i = 0; i < numPoints; i++) { if( !(flag[i] & X_BYTE) && (flag[i] & X_TYPE) ) FSeek(xLast); else FSeek(xStart); struct tPoints{ if (flag[i] & X_BYTE) { xLast = FTell(); xLastByte=1; UBYTE xDelta; xStart = FTell(); } else { if ((flag[i] & X_TYPE)) { FSeek(xLast); if (xLast>0) { if (xLastByte) { UBYTE xDeltaRepeat; } else { SHORT xDeltaRepeat; } } } else { xLast = FTell(); xLastByte=0; SHORT xDelta; xStart = FTell(); } } FSeek(yStart); if (flag[i] & Y_BYTE) { yLast = FTell(); yLastByte=1; UBYTE yDelta; yStart = FTell(); } else { if ((flag[i] & Y_TYPE)) { FSeek(yLast); if (yLast>0) { if (yLastByte) { UBYTE yDeltaRepeat; } else { SHORT yDeltaRepeat; } } } else { yLast = FTell(); yLastByte=0; SHORT yDelta; yStart = FTell(); } } FSeek(xStart); //First coordinates relative to (0,0); others are relative to previous point. } points ; } }; string tPointsRead ( tSimpleGlyphPoints &d ) { string s; s="hello"; return s; } typedef struct tSimpleGlyphFlags { typedef UBYTE bitFlag; local bitFlag flag_repeat=0; if (flag_repeat) { UBYTE count; flag_repeat = 0; } else { BitfieldRightToLeft(); bitFlag onCurve :1; bitFlag xByte :1; bitFlag yByte :1; bitFlag repeat :1; bitFlag xType :1; bitFlag yType :1; if (repeat) flag_repeat = 1; } }; string tSimpleGlyphFlagsRead (tSimpleGlyphFlags &d) { string s; if (exists(d.count)) { SPrintf(s, " repeat %u times", d.count); } else { s="Point"; if (d.onCurve) s += " OnCurve"; if (!d.xByte) {s += " X"; if (d.xType) s += "R";} if (d.xByte) {s += " x"; if (d.xType) s += "+"; else s += "-";} if (!d.yByte) {s += " Y"; if (d.yType) s += "R";} if (d.yByte) {s += " y"; if (d.yType) s += "+"; else s += "-";} if (d.repeat) s += " REPEAT:"; } return s; } typedef struct tSimpleGlyph { SHORT numberOfContours; SHORT xMin; // Minimum x for coordinate data. SHORT yMin; // Minimum y for coordinate data. SHORT xMax; // Maximum x for coordinate data. SHORT yMax; // Maximum y for coordinate data. USHORT endPtsOfContours[numberOfContours]; // Array of last points of each contour; n is the number of contours. USHORT instructionLength; // Total number of bytes for instructions. if (instructionLength > 0) { UBYTE instructions[instructionLength]; // Array of instructions for each glyph } local USHORT numPoints; numPoints = endPtsOfContours[numberOfContours-1]; // Unpack the compressed flags table local quad glyf_flag_table = FTell(); local quad glyf_flag_index = FTell(); local ushort i; local ubyte repeat = 0; local ubyte flag[numPoints]; // we have to do this in a local, 'cause of the run length compression from the "repeat" flag local ubyte flag_value; for (i = 0; i < numPoints; i++) { if (repeat > 0) { repeat--; } else { flag_value = ReadUByte(glyf_flag_index++); //only increment the pointer to the number of flags if the count on a repeat is 0 if (flag[i] & 8) { repeat = ReadUByte(glyf_flag_index++); } } flag[i] = flag_value; } local ushort numFlags = glyf_flag_index - glyf_flag_table; struct { tSimpleGlyphFlags flag[numFlags] ; } compressedFlags; // Array of flags for each coordinate in outline; n is the number of flags. //`----------------------------------- struct tSimpleGlyphPoints contours; } ; typedef struct tglyf { local quad glyf_table = FTell(); local quad glyf_offset; //The indexToLoc table stores the offsets to the locations of the glyphs in the font, // relative to the beginning of the glyphData table. //In order to compute the length of the last glyph element, there is an extra entry // after the last valid index. local ulong n; //The (maximum) value of n is numGlyphs + 1. The value for numGlyphs is found in the 'maxp' table. if (maxGlyfRead == 0) maxGlyfRead = ttf[ttfId-1].maxp.numGlyphs; for (n = 0; n <= maxGlyfRead; n++) { glyf_offset = ttf[ttfId - 1].loca.offsets[n]; //The actual local offset is stored, assuming the long version //The version is specified by 'indexToLocFormat' in the head table. 0 for Short, 1 for Long. if (ttf[ttfId-1].head.indexToLocFormat == 0) { glyf_offset *= 2; } //In the short version, the actual local offset divided by 2 is stored. FSeek(glyf_table + glyf_offset); if (ReadShort(FTell()) > 0) { // If the number of contours is greater than or equal to zero, this is a single glyph; struct tSimpleGlyph SimpleGlyph ; } if (ReadShort(FTell()) < 0 & 0==1) { // UNTESTED CODE. TODO: test // If the number of contours is negative, this is a composite glyph. //------------------------------- SHORT numberOfContours; SHORT xMin; // Minimum x for coordinate data. SHORT yMin; // Minimum y for coordinate data. SHORT xMax; // Maximum x for coordinate data. SHORT yMax; // Maximum y for coordinate data. do { USHORT flags; struct tGlyph { USHORT glyphIndex; if ( flags & ARG_1_AND_2_ARE_WORDS) { SHORT argument1; SHORT 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 */ } if (flags & WE_HAVE_INSTRUCTIONS) { USHORT numInstr; BYTE instr[numInstr]; } } Glyph; } while (flags & MORE_COMPONENTS); //------------------------------- } } }; string tSimpleGlyphRead( tSimpleGlyph &d ) { string s; SPrintf( s, "%u contours %u insts %u flags %u points", d.numberOfContours, d.instructionLength, d.numFlags, d.numPoints); return s; } typedef struct tGDEF { TT_Fixed Version; // Version of the GDEF table-initially 0x00010000 TT_Offset GlyphClassDef; // Offset to class definition table for glyph type-from beginning of GDEF header (may be NULL) TT_Offset AttachList; // Offset to list of glyphs with attachment points-from beginning of GDEF header (may be NULL) TT_Offset LigCaretList; // Offset to list of positioning points for ligature carets-from beginning of GDEF header (may be NULL) TT_Offset MarkAttachClassDef; // Offset to class definition table for mark attachment type-from beginning of GDEF header (may be NULL) }; typedef struct tprep { local quad n = curTblLength / sizeof(UBYTE); UBYTE bytecode[n]; // Instructions. n is the number of BYTE items that fit in the size of the table. }; typedef struct tDSIG { local quad DSIG_table = FTell(); local quad nextSig ; ULONG ulVersion; // Version number of the DSIG table (0x00000001) USHORT usNumSigs; // Number of signatures in the table USHORT usFlag; // permission flags //Bit 0: cannot be resigned //Bits 1-7: Reserved (Set to 0) struct { nextSig = FTell(); FSeek(nextSig); ULONG ulFormat; //format of the signature ULONG ulLength; //Length of signature in bytes ULONG ulOffset; //Offset to the signature block from the beginning of the table nextSig = FTell(); FSeek(DSIG_table + ulOffset); USHORT usReserved1; // Reserved for later use; 0 for now USHORT usReserved2; // Reserved for later use; 0 for now ULONG cbSignature; // Length (in bytes) of the PKCS#7 packet in pbSignature UBYTE bSignature[cbSignature]; // PKCS#7 packet } Sigs[usNumSigs] ; }; string tDSIGRead( tDSIG &d ) { string s; SPrintf( s, "v%u %u signature(s)", d.ulVersion, d.usNumSigs); return s; } typedef struct tLangSysTable{ USHORT LookupOrder; // = NULL (reserved for an offset to a reordering table) uint16 ReqFeatureIndex; // Index of a feature required for this language system- if no required features = 0xFFFF uint16 FeatureCount; // Number of FeatureIndex values for this language system-excludes the required feature uint16 FeatureIndex[FeatureCount]; // Array of indices into the FeatureList-in arbitrary order }; typedef struct tLangSysRecord { char LangSysTag[4]; // 4-byte LangSysTag identifier USHORT Offset; // LangSys Offset to LangSys table-from beginning of Script table local quad next = FTell(); FSeek(ScriptTable_table + Offset); local quad LangSys=FTell(); tLangSysTable LangSysTable; FSeek(next); }; string tLangSysRecordRead (tLangSysRecord &d ) { return d.LangSysTag; } typedef struct tScriptRecord { char ScriptTag[4]; //4-byte ScriptTag identifier USHORT Offset; // to Script table-from beginning of ScriptList local quad next = FTell(); FSeek(ScriptList + Offset); local quad ScriptTable_table=FTell(); struct { USHORT DefaultLangSys; // Offset to DefaultLangSys table-from beginning of Script table-may be NULL uint16 LangSysCount; // Number of LangSysRecords for this script-excluding the DefaultLangSys if (LangSysCount){ tLangSysRecord LangSysRecord[LangSysCount] ; } //Array of LangSysRecords-listed alphabetically by LangSysTag } ScriptTable; FSeek(ScriptTable_table + ScriptTable.DefaultLangSys); tLangSysTable DefaultLangSysTable; FSeek(next); }; string tScriptRecordRead (tScriptRecord &d) { return d.ScriptTag; } typedef struct tScriptList { USHORT Offset; //Offset to ScriptList table-from beginning of GSUB table local quad next = FTell(); FSeek(GSUBorGPOS_table + Offset); local quad ScriptList=FTell(); uint16 ScriptCount; //Number of ScriptRecords tScriptRecord ScriptRecord[ScriptCount]; //Array of ScriptRecords -listed alphabetically by ScriptTag FSeek(next); }; string tScriptListRead (tScriptList &d) { string s; SPrintf( s, "%u scripts", d.ScriptCount); return s; } typedef struct tFeatureRecord { char FeatureTag[4]; //4-byte FeatureTag identifier USHORT Offset; // to Feature table-from beginning of FeatureList local quad next = FTell(); FSeek(FeatureList + Offset); local quad FeatureTable_table=FTell(); struct { uint16 FeatureParams; //reserved null uint16 LookupListCount; // Number of LangSysRecords for this script-excluding the DefaultLangSys uint16 LookupListIndex[LookupListCount] ; } FeatureTable ; FSeek(next); }; string tFeatureRecordRead (tFeatureRecord &d) { return d.FeatureTag; } typedef struct tFeatureList { USHORT Offset; //Offset to FeatureList table-from beginning of GSUB table local quad next = FTell(); FSeek(GSUBorGPOS_table + Offset); local quad FeatureList=FTell(); uint16 FeatureCount; //Number of FeatureRecords tFeatureRecord FeatureRecord[FeatureCount] ; //Array of FeatureRecords -listed alphabetically by FeatureTag FSeek(next); }; string tFeatureListRead (tFeatureList &d) { string s; SPrintf( s, "%u Features", d.FeatureCount); return s; } typedef struct tLookupRecord { USHORT Offset; //Offset to LookupList table-from beginning of GSUB table local quad next = FTell(); FSeek(LookupList_table + Offset); uint16 LookupType; //Different enumerations for GSUB and GPOS uint16 LookupFlag; //Lookup qualifiers uint16 SubTableCount; //Number of SubTables for this lookup USHORT SubTable[SubTableCount]; //Array of offsets to SubTables-from beginning of Lookup table FSeek(next); }; typedef struct tLookupList { USHORT Offset; //Offset to LookupList table-from beginning of GSUB table local quad next = FTell(); FSeek(GSUBorGPOS_table + Offset); local quad LookupList_table=FTell(); uint16 LookupCount; //Number of FeatureRecords tLookupRecord LookupRecord[LookupCount] ; //Array of LookupRecords -listed alphabetically by LookupTag FSeek(next); }; typedef struct tGSUBorGPOS { local quad GSUBorGPOS_table = FTell(); TT_Fixed Version; //Version of the GSUB table-initially set to 0x00010000 tScriptList ScriptList ; //Offset to ScriptList table-from beginning of GSUB table tFeatureList FeatureList ; //Offset to FeatureList table-from beginning of GSUB table tLookupList LookupList; //Offset to LookupList table-from beginning of GSUB table //LookupType Enumeration table for glyph substitution //Value Type Description //1 LookupType_Single Replace one glyph with one glyph //2 LookupType_Multiple Replace one glyph with more than one glyph //3 LookupType_Alternate Replace one glyph with one of many glyphs //4 LookupType_Ligature Replace multiple glyphs with one glyph //5 LookupType_Context Replace one or more glyphs in context //6 LookupType_Chaining Context Replace one or more glyphs in chained context //7 LookupType_Extension Substitution Extension mechanism for other substitutions (i.e. this excludes the Extension type substitution itself) //8 LookupType_Reverse chaining context single Applied in reverse order, replace single glyph in chaining context //9+ Reserved For future use }; string tGSUBorGPOSRead( tGSUBorGPOS &d ) { string s; SPrintf( s, "v%.2f", d.Version/65535.0); return s; } typedef struct tLTSH { USHORT ver; USHORT numGlyphs; UBYTE yPels[numGlyphs]; }; typedef struct tRatioRange { unsigned BYTE bCharSet; unsigned BYTE xRatio; unsigned BYTE yStartRatio; unsigned BYTE yEndRatio; } RatioRange; typedef struct tVDMXRecord { USHORT yPelHeight; SHORT yMax; SHORT yMin; } VDMXRecord; typedef struct tVDMXGroup { USHORT recs; BYTE startsz; BYTE endsz; VDMXRecord entry[recs]; } VDMXGroup; typedef struct tVDMX { // header USHORT version; USHORT numRecs; USHORT numRatios; RatioRange ratRange[numRatios]; USHORT offset[numRatios]; VDMXGroup groups[numRatios]; }; struct GaspBehavior { uint16 gridfit : 1; uint16 doGray : 1; uint16 symMetricGridfit : 1; uint16 symMetricSmoothing : 1; uint16 Reserved : 12; }; struct tgaspRange { uint16 rangeMaxPPEM; GaspBehavior rangeGaspBehavior; }; struct tgasp { uint16 version; uint16 numRanges; tgaspRange gaspRanges[numRanges]; }; //================================== local ULONG curTblOffset; local ULONG curTblLength; local int64 nextTTFRec = 0; struct ttfFile { struct tOffsetTable OffsetTable; struct tTable Table[OffsetTable.numTables] ; nextTTFRec = FTell(); //Required Tables if (findTable(this, "cmap")) {struct tcmap cmap; }; if (findTable(this, "head")) {struct thead head;}; if (findTable(this, "hhea")) {struct thhea hhea ; }; if (findTable(this, "hmtx")) {struct thmtx hmtx ; }; if (findTable(this, "maxp")) {struct tmaxp maxp ; }; if (findTable(this, "name")) {struct tname name ; }; if (findTable(this, "OS/2")) {struct tOS_2 OS_2 ; }; if (findTable(this, "post")) {struct tpost post ; }; //Other Tables if (findTable(this, "cvt ")) {struct tcvt cvt ; }; if (findTable(this, "fpgm")) {struct tfpgm fpgm; }; if (findTable(this, "loca")) {struct tloca loca ; }; if (findTable(this, "glyf")) {struct tglyf glyf; }; if (findTable(this, "prep")) {struct tprep prep;}; if (findTable(this, "GDEF")) {struct tGDEF GDEF; }; if (findTable(this, "GSUB")) {struct tGSUBorGPOS GSUB ; }; if (findTable(this, "GPOS")) {struct tGSUBorGPOS GPOS ; }; if (findTable(this, "DSIG")) {struct tDSIG DSIG ; }; if (findTable(this, "hdmx")) {struct thdmx hdmx ; }; if (findTable(this, "LTSH")) {struct tLTSH LTSH; }; if (findTable(this, "VDMX")) {struct tVDMX VDMX; }; if (findTable(this, "gasp")) {struct tgasp gasp; }; //if (findTable(this, "JSTF")) {struct tJSTF JSTF }; //if (findTable(this, "PCLT")) {struct tPCLT PCLT }; //if (findTable(this, "kern")) {struct tkern kern }; FSeek(nextTTFRec); }; int findTable( ttfFile& file, char tag[] ) { //search the Table[] array for a matching character table tag // set curTblOffset, curTblLength and FSeek curTblOffset // return the table index+1 or 0 if no table found. local int i=0; for( i = 0; i < file.OffsetTable.numTables; i++ ) { if ( Strncmp(file.Table[i].Tag.asChar, tag, 4)==0 ) { curTblOffset = file.Table[i].offset; curTblLength = file.Table[i].length; FSeek(file.Table[i].offset); return i+1; } } return 0; } struct TTCHeader { char tag[4]; uint16 majorVersion; uint16 minorVersion; uint32 numFonts; uint32 offsetTable[numFonts]; if (majorVersion == 2) { char dsigTag[4]; uint32 dsigLength; uint32 dsigOffset; } }; local uint32 ttfId = 0; local uint32 numFonts = 1; if (ReadInt() == 0x74746366) // big-endian "ttcf" { struct TTCHeader ttc; numFonts = ttc.numFonts; } struct ttfFile ttf[numFonts];