www.xbdev.net
xbdev - software development
Sunday April 19, 2026
Home | Contact | Support | Image File Format... Storing Graphics using Bits and Bytes.. | TTF File Format... TTF Image Format.. ..
>>
     
 

TTF File Format...

TTF Image Format.. ..

 


The TTF is the file format for fonts - TTF fonts are so popular as the characters aren
The TTF is the file format for fonts - TTF fonts are so popular as the characters aren't stored as 'pixels' but as a vectors - so they have infinite resolution.


TTF File Format


The TrueType Font (TTF) format is a widely used font file format in digital typography, designed by Apple and Microsoft.

It utilizes quadratic Bézier curves to define glyph shapes, allowing for smooth rendering at various sizes.

TTF files contain data such as glyph outlines, metrics, and hinting instructions, ensuring consistent appearance across different devices and resolutions. Additionally, TrueType fonts support advanced typographic features like ligatures, kerning pairs, and stylistic alternates, making them versatile for various design needs.

TTF files are commonly used on both Windows and macOS platforms, as well as in web typography due to their widespread compatibility and compact file size.


/******************************************************************************/
/*                                                                            */
/*  File: ttf.h                                                               */
/*  Author: bkenwright@xbdev.net                                              */
/*  URL: www.xbdev.net                                                        */
/*                                                                            */
/******************************************************************************/
/*
    ttf font file format reader

    Desc? Well this whole file reads the ttf font file in, parses it and stores
    it in various structures.

    You pass it a stTTF structure, and it fills it in with all the information,
    such as supported characters, glyfs, etc

    File to use:

    void ReadTTF(const char* szFileName, stTTF* ttf)


*/
/******************************************************************************/

#pragma once

#include "dxdebug.h"

const int MAXFLAGS = 1500;

typedef short int        int16;
typedef int                int32;
typedef unsigned int    uint32;
typedef unsigned short  uint16;
typedef unsigned char    uchar8;
typedef unsigned char   uint8;
typedef short            FWord; // - 16-bit signed integer that describes a quantity in FUnits, 
                               //    the smallest measurable distance in em space. 
typedef unsigned int    Fixed;
typedef double            longDateTime;

#pragma pack(1)
struct stOffsetSubTable
{
    uint32 scaler;            // type A tag to indicate the OFA scaler to be used to 
                            // rasterize this font; see the note on the scaler type below for more information. 
    uint16 numTables;        // number of tables 
    uint16 searchRange;        // (maximum power of 2 <= numTables)*16 
    uint16 entrySelector;    // log2(maximum power of 2 <= numTables) 
    uint16 rangeShift;        // numTables*16-searchRange 
};
#pragma pack()


struct stTableDirectory
{
    uint32 tag;                // 4-byte identifier 
    uint32 checkSum;        // checksum for this table 
    uint32 offset;            // offset from beginning of sfnt 
    uint32 length;            // length of this table in byte (actual length not padded length) 
};


struct stFontDirectory
{
    stFontDirectory()  { m_tableDirectory=NULL;        }
    ~stFontDirectory() { delete[] m_tableDirectory; }

    stOffsetSubTable    m_offsetSubTable;
    stTableDirectory*    m_tableDirectory;
};

struct stCMAPTable
{
    uint16 format;        // hopefully 0 for easy fonts
    uint16 length;        // 262 for format 0
    uint16 language;    // language-independent
};

struct stCMAPSubTable
{
    uint16 platformID;            // Platform identifier 
    uint16 platformSpecificID;    // Platform-specific encoding identifier 
    uint32 offset;                // Offset of the mapping table 

    stCMAPTable table;            // Offset points to this
};

struct stCMAP
{
    stCMAP()  
    { 
        subTables=NULL; 

        numGlyphIndexArray=0; glyphIndexArray=NULL;
    }
    ~stCMAP() 
    { 
        if (subTables) delete[] subTables; 
        subTables=NULL; 

        if (glyphIndexArray) delete[] glyphIndexArray; 
        glyphIndexArray=NULL;
    }

    uint16 version;            // Version number (Set to zero) 
    uint16 numberSubtables;    // Number of encoding subtables 

    stCMAPSubTable* subTables;

    int     numGlyphIndexArray;
    uint8*  glyphIndexArray;
};

struct stGlyfDescription
{
    int16 numberOfContours;        // If the number of contours is positive or zero, it is a single glyph;
                                // If the number of contours is -1, the glyph is compound 
    FWord xMin;                    // Minimum x for coordinate data 
    FWord yMin;                    // Minimum y for coordinate data 
    FWord xMax;                    // Maximum x for coordinate data 
    FWord yMax;                    // Maximum y for coordinate data 


    // Zero or positive number of contours signals a simple glyph.
    // -1, the glyph is made up of components.
};

struct stGLYFSimple
{
    stGLYFSimple()
    {
        instructions        = NULL;
        endPtsOfContours    = NULL;
        flags                = NULL;
        xCoordinates        = NULL;
        yCoordinates        = NULL;
    }
    ~stGLYFSimple()
    {
        delete[] instructions;            instructions=NULL;
        delete[] endPtsOfContours;        endPtsOfContours=NULL;
        delete[] flags;                    flags=NULL;
        delete[] xCoordinates;            xCoordinates=NULL;
        delete[] yCoordinates;            yCoordinates=NULL;
    }

    uint16* endPtsOfContours;    // [n] Array of last points of each contour; n is the number of contours; array entries are point indices 
    uint16 instructionLength;    // Total number of bytes needed for instructions 
    uint8* instructions;        // [instructionLength] Array of instructions for this glyph 
    int    numFlags;
    uint8* flags;                // [variable] Array of flags 
    int16* xCoordinates;        // uint8 or int16 - Array of x-coordinates; the first is relative to (0,0), others are relative to previous point 
    int16* yCoordinates;        // uint8 or int16 - Array of y-coordinates; the first is relative to (0,0), others are relative to previous point 
};

struct stGLYFCompound
{
    union
    {
        uint16 flags; // Component flag 
        struct
        {
            uint16 ARG_1_AND_2_ARE_WORDS    : 1;    // 0 If set, the arguments are words; 
                                                    // If not set, they are bytes. 
            uint16 ARGS_ARE_XY_VALUES        : 1;    // 1 If set, the arguments are xy values; 
                                                    // If not set, they are points. 
            uint16 ROUND_XY_TO_GRID            : 1;    // 2 If set, round the xy values to grid; 
                                                    // if not set do not round xy values to grid (relevant only to bit 1 is set) 
            uint16 WE_HAVE_A_SCALE            : 1;    // 3 If set, there is a simple scale for the component. 
                                                    // If not set, scale is 1.0. 
                                                    // (this bit is obsolete) 4 (obsolete; set to zero) 
            uint16 MORE_COMPONENTS            : 1;    // 5 If set, at least one additional glyph follows this one. 
            uint16 WE_HAVE_AN_X_AND_Y_SCALE : 1;    // 6 If set the x direction will use a different scale than the y direction. 
            uint16 WE_HAVE_A_TWO_BY_TWO        : 1;    // 7 If set there is a 2-by-2 transformation that will be used to scale the component. 
            uint16 WE_HAVE_INSTRUCTIONS        : 1;    // 8 If set, instructions for the component character follow the last component. 
            uint16 USE_MY_METRICS            : 1;    // 9 Use metrics from this component for the compound glyph. 
            uint16 OVERLAP_COMPOUND            : 1;    // 10 If set, the components of this compound glyph overlap. 
        }flagBits;
    };


    uint16 glyphIndex; // Glyph index of component 
    int32  offset0;
    int32  offset1;
//    int16, uint16, int8 or uint8 argument1 X-offset for component or point number; type depends on bits 0 and 1 in component flags 
//    int16, uint16, int8 or uint8 argument2 Y-offset for component or point number type depends on bits 0 and 1 in component flags 
//    transformation option One of the transformation options from Table 19 
};

struct stGLYFItem
{
    stGLYFItem() { m_glyfSimple=NULL; m_glyfCompound=NULL; }
    ~stGLYFItem() 
    { 
        delete m_glyfSimple;    m_glyfSimple=NULL;
        delete m_glyfCompound;    m_glyfCompound=NULL; 
    }

    stGlyfDescription    m_glyfDescription;
    // (here follow the data for the simple or compound glyph)
    stGLYFSimple*        m_glyfSimple;
    stGLYFCompound*        m_glyfCompound;
};

struct stGLYF
{
    stGLYF() { glyfItem=NULL; numGlyphs=0; }
    ~stGLYF() { if (glyfItem) { delete[] glyfItem; glyfItem=NULL; }; };

    int            numGlyphs;        // Set from 'maxp' table
    stGLYFItem* glyfItem;        // Array
};

struct stHEAD
{
    Fixed version;                // 0x00010000 if (version 1.0) 
    Fixed fontRevision;            // set by font manufacturer 
    uint32 checkSumAdjustment;    // To compute: set it to 0, calculate the checksum for the 'head' table and put it in the table directory, sum the entire font as uint32, then store B1B0AFBA - sum. The checksum for the 'head' table will not be wrong. That is OK. 
    uint32 magicNumber;            // set to 0x5F0F3CF5 
    uint16 flags;                // bit 0 - y value of 0 specifies baseline
                                //    bit 1 - x position of left most black bit is LSB
                                //    bit 2 - scaled point size and actual point size will differ (i.e. 24 point glyph differs from 12 point glyph scaled by factor of 2)
                                //    bit 3 - use integer scaling instead of fractional
                                //    bit 4 - (used by the Microsoft implementation of the TrueType scaler)
                                //    bit 5 - This bit should be set in fonts that are intended to e laid out vertically, and in which the glyphs have been drawn such that an x-coordinate of 0 corresponds to the desired vertical baseline.
                                //    bit 6 - This bit must be set to zero.
                                //    bit 7 - This bit should be set if the font requires layout for correct linguistic rendering (e.g. Arabic fonts).
                                //    bit 8 - This bit should be set for a GX font which has one or more metamorphosis effects designated as happening by default.
                                //    bit 9 - This bit should be set if the font contains any strong right-to-left glyphs.
                                //    bit 10 - This bit should be set if the font contains Indic-style rearrangement effects.
                                //    bits 11-12 - Defined by Adobe. 
    uint16 unitsPerEm;            // range from 64 to 16384 
    longDateTime created;        // international date 
    longDateTime modified;        // international date 
    FWord xMin;                 // for all glyph bounding boxes 
    FWord yMin;                 // for all glyph bounding boxes 
    FWord xMax;                 // for all glyph bounding boxes 
    FWord yMax;                 // for all glyph bounding boxes 
    uint16 macStyle;            // bit 0 bold
                                //    bit 1 italic
                                //    bit 2 underline
                                //    bit 3 outline
                                //    bit 4 shadow
                                //    bit 5 condensed (narrow)
                                //    bit 6 extended 
    uint16 lowestRecPPEM;        // smallest readable size in pixels 
    int16 fontDirectionHint;    // 0 Mixed directional glyphs
                                //    1 Only strongly left to right glyphs
                                //    2 Like 1 but also contains neutrals
                                //    -1 Only strongly right to left glyphs
                                //    -2 Like -1 but also contains neutrals 
    int16 indexToLocFormat;        // 0 for short offsets, 1 for long 
    int16 glyphDataFormat;        // 0 for current format 
};

struct stLOCA
{
    stLOCA()  { glyphOffsets=NULL; }
    ~stLOCA() { delete[] glyphOffsets; glyphOffsets=NULL; }

    int* glyphOffsets;
};

struct stMAXP
{
    Fixed  version;                    // 0x00010000 (1.0) 
    uint16 numGlyphs;                // the number of glyphs in the font 
    uint16 maxPoints;                // points in non-compound glyph 
    uint16 maxContours;                // contours in non-compound glyph 
    uint16 maxComponentPoints;        // points in compound glyph 
    uint16 maxComponentContours;    // contours in compound glyph 
    uint16 maxZones;                // set to 2 
    uint16 maxTwilightPoints;        // points used in Twilight Zone (Z0) 
    uint16 maxStorage;                // number of Storage Area locations 
    uint16 maxFunctionDefs;            // number of FDEFs 
    uint16 maxInstructionDefs;        // number of IDEFs 
    uint16 maxStackElements;        // maximum stack depth 
    uint16 maxSizeOfInstructions;    // byte count for glyph instructions 
    uint16 maxComponentElements;    // number of glyphs referenced at top level 
    uint16 maxComponentDepth;        // levels of recursion, set to 0 if font has only simple glyphs 
};

struct stTTF
{
    stFontDirectory        m_fontDirectory;
    stCMAP                m_cmap;
    stHEAD                m_head;
    stGLYF                m_glyf;
    stLOCA                m_loca;
    stMAXP                m_maxp;
};

inline void SwapEndian(unsigned char *data)
{
    //do nothing, just needed for completeness
}

inline void SwapEndian(char *data)
{
    //do nothing, just needed for completeness
}

inline void SwapEndian(unsigned short *data)
{
    *data=((*data&0x00ff)<<8)
        |((*data&0xff00)>>8);
}

inline void SwapEndian(short *data)
{
    *data=((*data&0x00ff)<<8)
        |((*data&0xff00)>>8);
}

inline void SwapEndian(unsigned int *data)
{
    *data=((*data&0x000000ff)<<24)
        |((*data&0x0000ff00)<< 8)
        |((*data&0x00ff0000)>> 8)
        |((*data&0xff000000)>>24);
}

inline void SwapEndian(int *data)
{
    *data=((*data&0x000000ff)<<24)
        |((*data&0x0000ff00)<< 8)
        |((*data&0x00ff0000)>> 8)
        |((*data&0xff000000)>>24);
}

/******************************************************************************/

inline int FileSize(FILE *fp)
{
    long pos;
    fseek(fp, 0, SEEK_END);
    pos = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    return pos;
}

/******************************************************************************/

void ReadTableDirectory(FILE* fp, stTableDirectory* td)
{
    fread(&td->tag,                sizeof(td->tag),        1, fp);
    fread(&td->checkSum,        sizeof(td->checkSum),    1, fp);
    fread(&td->offset,            sizeof(td->offset),        1, fp);
    fread(&td->length,            sizeof(td->length),        1, fp);
    //SwapEndian(&td->tag);    // Don't need to flip 4 seperate bytes
    SwapEndian(&td->checkSum);
    SwapEndian(&td->offset);
    SwapEndian(&td->length);

    char* t = (char*)&td->tag;
    dprintf("\t [Tag: %c%c%c%c],", t[0], t[1], t[2], t[3] );
    dprintf(" CheckSum 0x%08X,", td->checkSum);
    dprintf(" Offset % 8d,", td->offset);
    dprintf(" Length: % 6d\n", td->length);
}

void ReadFontDirectory(FILE* fp, stFontDirectory* fd)
{
    dprintf("FontDirectory:\n");
    stOffsetSubTable* ot = &fd->m_offsetSubTable;

    fread(&ot->scaler, sizeof(ot->scaler), 1, fp);
    SwapEndian(&ot->scaler);
    DBG_ASSERT(ot->scaler==0x00010000);

    fread(&ot->numTables,        sizeof(ot->numTables),        1, fp);
    fread(&ot->searchRange,        sizeof(ot->searchRange),    1, fp);
    fread(&ot->entrySelector,    sizeof(ot->entrySelector),    1, fp);
    fread(&ot->rangeShift,        sizeof(ot->rangeShift),        1, fp);
    SwapEndian(&ot->numTables);    
    SwapEndian(&ot->searchRange);        
    SwapEndian(&ot->entrySelector);    
    SwapEndian(&ot->rangeShift);

    dprintf("\tOffsetSubTable\n");
    dprintf("\t Scalar: 0x%08X\n",        ot->scaler);
    dprintf("\t NumTables: %d\n",        ot->numTables);
    dprintf("\t SearchRange: %d\n",        ot->searchRange);
    dprintf("\t EntrySelector: %d\n",   ot->entrySelector);
    dprintf("\t RangeShift: %d\n",        ot->rangeShift);
    

    DBG_ASSERT(ot->numTables<MAXFLAGS); // Sanity check

    fd->m_tableDirectory = new stTableDirectory[ot->numTables];

    dprintf("\n\tTableDirectory\n");
    for (int i=0; i<ot->numTables; i++)
    {
        ReadTableDirectory(fp, &fd->m_tableDirectory[i]);
    }
}

/******************************************************************************/

uchar8* GetTableData(uchar8* data, stTTF* ttf, char* name)
{
    stFontDirectory* fd = &ttf->m_fontDirectory;
    stOffsetSubTable* subTable = &fd->m_offsetSubTable;

    stTableDirectory* tableDirectory = NULL;
    for (int i=0; i<subTable->numTables; i++)
    {
        stTableDirectory* table = &fd->m_tableDirectory[i];
        char* c = (char*)&table->tag;

        char tname[32] = {0};
        strncpy(tname, c, 4);

        if (strcmp(tname, name)==0)
        {
            tableDirectory = table;
            break;
        }

    }
    DBG_ASSERT(tableDirectory);

    uchar8* tableData = &data[tableDirectory->offset];

    return tableData;
}

/******************************************************************************/

void ReadCMAP(uchar8* data, stTTF* ttf, stCMAP* cmap)
{
    uchar8* cmapData = GetTableData(data, ttf, "cmap");
    DBG_ASSERT(cmapData);
    
    uchar8* startCMAPData = cmapData;

    cmap->version            = *((uint16*)cmapData);        cmapData+=2;        SwapEndian(&cmap->version);
    cmap->numberSubtables    = *((uint16*)cmapData);        cmapData+=2;        SwapEndian(&cmap->numberSubtables);


    int numSubTables = cmap->numberSubtables;
    DBG_ASSERT(numSubTables>0 && numSubTables<1000); // Sanity check
    cmap->subTables = new stCMAPSubTable[numSubTables];
    DBG_ASSERT(cmap->subTables);

    for (int i=0; i<numSubTables; i++)
    {
        stCMAPSubTable* subTable = &cmap->subTables[i];

        subTable->platformID            = *((uint16*)cmapData);        cmapData+=2;    SwapEndian(&subTable->platformID);
        subTable->platformSpecificID    = *((uint16*)cmapData);        cmapData+=2;    SwapEndian(&subTable->platformSpecificID);
        subTable->offset                = *((uint32*)cmapData);        cmapData+=4;    SwapEndian(&subTable->offset);


        stCMAPTable* table = &subTable->table;
        uchar8* tableOffset = startCMAPData + subTable->offset;

        table->format        = *((uint16*)tableOffset);        tableOffset+=2;    SwapEndian(&table->format);
        table->length        = *((uint16*)tableOffset);        tableOffset+=2;    SwapEndian(&table->length);
        table->language        = *((uint16*)tableOffset);        tableOffset+=2;    SwapEndian(&table->language);

        switch (table->format)
        {
            case 0: // Table follows as 256 glyph indices
            {        // Character codes and glyph indices that are restricted to a single byte
                DBG_ASSERT(table->length == 262);

                DBG_ASSERT(cmap->glyphIndexArray==NULL);
                if (cmap->glyphIndexArray==NULL)
                {
                    cmap->glyphIndexArray = new uint8[256];
                    cmap->numGlyphIndexArray = 256;
                    
                    uint8* glyphIndexArray = cmap->glyphIndexArray;

                    for (int k=0; k<256; k++)
                    {
                        glyphIndexArray[k] = *((uint8*)tableOffset);    tableOffset+=1;
                    }
                }
            }
            break;

            default:
            {
                //DBG_HALT;
            }
        }
    }
}

/******************************************************************************/

int ReadCoords(uint8* data, int numFlags, uint8* flags, int16* coords, bool isX)
{
    uint8* startAddr = data;

    int point = 0;
    const int MAXPOINTS = MAXFLAGS;
    int points[MAXPOINTS];
    int numPoints = 0;
    for (int i=0; i<numFlags; i++)
    {
        DBG_ASSERT(numPoints<MAXPOINTS);
        uint8 flag = flags[i];

        bool isByte = false;
        bool isSame = false;
        
        if (isX)
        {
            isByte = (flag&2)  > 0 ? true : false;
            isSame = (flag&16) > 0 ? true : false;
        }
        else
        {
            isByte = (flag&4)  > 0 ? true : false;
            isSame = (flag&32) > 0 ? true : false;
        }

        if (isByte)
        {
            uint8 coord = *((uint8*)data);            data+=1;                    SwapEndian(&coord);
            int16 scoord = coord;
            if (!isSame)
                scoord = -scoord;
            point += scoord;
        }
        else
        {
            if (isSame)
            {
                // same as last point
            }
            else
            {
                int16 coord = *((int16*)data);            data+=2;                    SwapEndian(&coord);
                point += coord;
            }
        }
        points[numPoints++] = point;
    }

    DBG_ASSERT(numPoints == numFlags);
    for (int i=0; i<numPoints; i++)
    {
        coords[i] = points[i];
    }

    int bytesRead = (int)(data - startAddr);
    return bytesRead;
}

/******************************************************************************/

void ReadGLYFSimple(uchar8* data, stGLYFItem* glyfItem, int indx)
{
    //int numPoints = indx;     // index refers to last point.

    int numPoints = -1;

    glyfItem->m_glyfSimple = new stGLYFSimple;
    stGLYFSimple* glyfSimple = glyfItem->m_glyfSimple;

    int numberOfContours = glyfItem->m_glyfDescription.numberOfContours;
    DBG_ASSERT(numberOfContours>0 && numberOfContours<10000);

    glyfSimple->endPtsOfContours        = new uint16[numberOfContours];
    for (int i=0; i<numberOfContours; i++)
    {
        glyfSimple->endPtsOfContours[i]    = *((uint16*)data);        data+=2;        SwapEndian(&glyfSimple->endPtsOfContours[i]);

        if (glyfSimple->endPtsOfContours[i] > numPoints)
        {
            numPoints = glyfSimple->endPtsOfContours[i];
        }
    }

    DBG_ASSERT(numPoints>=0);
    DBG_ASSERT(numPoints<2000);


    glyfSimple->instructionLength        = *((uint16*)data);        data+=2;        SwapEndian(&glyfSimple->instructionLength);
///    DBG_ASSERT(glyfSimple->instructionLength>0 && glyfSimple->instructionLength<10000);

    glyfSimple->instructions = new uint8[ glyfSimple->instructionLength ];

    for (int i=0; i<glyfSimple->instructionLength; i++)
    {
        glyfSimple->instructions[i]        = *((uint8*)data);        data+=1;        SwapEndian(&glyfSimple->instructions[i]);
    }



    uint8 flags[MAXFLAGS];


    numPoints++;
    int numFlags = 0;
    while (numFlags < numPoints)
    {
        DBG_ASSERT(numFlags < MAXFLAGS);

        uint8 flag =  *((uint8*)data);            data+=1;                    SwapEndian(&flag);
        
        flags[numFlags] = flag;
        numFlags++;

        uint8 repeat = (flag & 0x8); // Get Repeat Flag

        if (repeat)
        {
            ///????ERROR???...should repeat and call itself?
            uint8 numRepeats = *((uint8*)data);        data+=1;                    SwapEndian(&numRepeats);
            for (int i=0; i<numRepeats; i++)
            {
                flags[numFlags] =  flag;
                numFlags++;
            }
        }
    }

    if (indx==68)
    {
        int check = 0;
    }

    glyfItem->m_glyfSimple->numFlags = numFlags;
    glyfItem->m_glyfSimple->flags = new uint8[numFlags];
    for (int i=0; i<numFlags; i++)
    {
        glyfItem->m_glyfSimple->flags[i] = flags[i];
    }

    glyfItem->m_glyfSimple->xCoordinates = new int16[numFlags];
    glyfItem->m_glyfSimple->yCoordinates = new int16[numFlags];

    data += ReadCoords(data, numFlags, flags, glyfItem->m_glyfSimple->xCoordinates, true); // x coords


    if (indx==68)
    {
        int check = 0;
    }


    data += ReadCoords(data, numFlags, flags, glyfItem->m_glyfSimple->yCoordinates, false); // y coords

}

/******************************************************************************/

void ReadGLYFCompound(uchar8* data, stGLYFItem* glyfItem, int indx)
{

    const int ARG_1_AND_2_ARE_WORDS        = 1<<0;        // If set, the arguments are words; 
                                                    // If not set, they are bytes. 
    const int ARGS_ARE_XY_VALUES        = 1<<1;        // If set, the arguments are xy values; 
                                                    // If not set, they are points. 
    const int ROUND_XY_TO_GRID            = 1<<2;        // If set, round the xy values to grid; 
                                                    // if not set do not round xy values to grid (relevant only to bit 1 is set) 
    const int WE_HAVE_A_SCALE            = 1<<3;        // If set, there is a simple scale for the component. 
                                                    // If not set, scale is 1.0. 
                                                    // (this bit is obsolete) 4 (obsolete; set to zero) 
    const int MORE_COMPONENTS            = 1<<5;        // If set, at least one additional glyph follows this one. 
    const int WE_HAVE_AN_X_AND_Y_SCALE    = 1<<6;     // If set the x direction will use a different scale than the y direction. 
    const int WE_HAVE_A_TWO_BY_TWO        = 1<<7;        // If set there is a 2-by-2 transformation that will be used to scale the component. 
    const int WE_HAVE_INSTRUCTIONS        = 1<<8;        // If set, instructions for the component character follow the last component. 
    const int USE_MY_METRICS            = 1<<9;        // Use metrics from this component for the compound glyph. 
    const int OVERLAP_COMPOUND            = 1<<10;    // If set, the components of this compound glyph overlap. 


    glyfItem->m_glyfCompound = new stGLYFCompound;
    stGLYFCompound* glyfCompound = glyfItem->m_glyfCompound;


    uint16 flagbyte = 0;
    uint16 glyphIndex = 0;
    do 
    {
        flagbyte    = *((uint16*)data);        data+=2;    SwapEndian(&flagbyte);
        glyphIndex    = *((uint16*)data);        data+=2;    SwapEndian(&glyphIndex);

        if ( (flagbyte & ARGS_ARE_XY_VALUES) == 0 )
        {
            DBG_HALT; // index
        }
        else
        {
            if ( flagbyte & ARG_1_AND_2_ARE_WORDS )
            {
                uint16 arg0 = *((uint16*)data);            data+=2;                    SwapEndian(&arg0);
                uint16 arg1 = *((uint16*)data);            data+=2;                    SwapEndian(&arg1);
            }
            else
            {
                uint8 arg0 = *((uint8*)data);            data+=1;                    SwapEndian(&arg0);
                uint8 arg1 = *((uint8*)data);            data+=1;                    SwapEndian(&arg1);
            }
        }

        if (flagbyte & WE_HAVE_A_TWO_BY_TWO)
        {
            uint16 a = *((uint16*)data);                data+=2;                    SwapEndian(&a);
            uint16 b = *((uint16*)data);                data+=2;                    SwapEndian(&b);
            uint16 c = *((uint16*)data);                data+=2;                    SwapEndian(&c);
            uint16 d = *((uint16*)data);                data+=2;                    SwapEndian(&d);
        }
        else if (flagbyte & WE_HAVE_AN_X_AND_Y_SCALE)
        {
            uint16 a = *((uint16*)data);                data+=2;                    SwapEndian(&a);
            0;
            0;
            uint16 d = *((uint16*)data);                data+=2;                    SwapEndian(&d);
        }
        else if ( flagbyte & WE_HAVE_A_SCALE )
        {
            uint16 s = *((uint16*)data);                data+=2;                    SwapEndian(&s);
            s;
            0;
            0;
            s;
        }
        else
        {
            1;
            0;
            0;
            1;
        }

    } while (flagbyte & MORE_COMPONENTS);

}

/******************************************************************************/

void ReadGLYF(uchar8* data, stTTF* ttf, stGLYF* glyf)
{
    uchar8* glyfData = GetTableData(data, ttf, "glyf");
    DBG_ASSERT(glyfData);


    int numGlyphs = ttf->m_maxp.numGlyphs;
    DBG_ASSERT(numGlyphs>0 && numGlyphs<10000); // Sanity check

    stLOCA* loca = &ttf->m_loca;
    int* glyphOffsets = loca->glyphOffsets;
    DBG_ASSERT(glyphOffsets);

    glyf->numGlyphs = numGlyphs;
    glyf->glyfItem = new stGLYFItem[numGlyphs];

    for (int i=0; i<numGlyphs; i++)
    {
        stGLYFItem* glyfItem = &glyf->glyfItem[i];
        int glyfOffset    = glyphOffsets[i];
        uchar8* itemData = &glyfData[ glyfOffset ];

        stGlyfDescription* glyfDesc = &glyfItem->m_glyfDescription;
        glyfDesc->numberOfContours        = *((int16*)itemData);        itemData+=2;        SwapEndian(&glyfDesc->numberOfContours);
        glyfDesc->xMin                    = *((FWord*)itemData);        itemData+=2;        SwapEndian(&glyfDesc->xMin);
        glyfDesc->yMin                    = *((FWord*)itemData);        itemData+=2;        SwapEndian(&glyfDesc->yMin);
        glyfDesc->xMax                    = *((FWord*)itemData);        itemData+=2;        SwapEndian(&glyfDesc->xMax);
        glyfDesc->yMax                    = *((FWord*)itemData);        itemData+=2;        SwapEndian(&glyfDesc->yMax);


        // Only Simple Glyphs at the moment
        DBG_ASSERT(glyfDesc->numberOfContours!=0);

        if (glyfDesc->numberOfContours>0)
        {
            ReadGLYFSimple(itemData, glyfItem, i);
        }
        
        if (glyfDesc->numberOfContours<0)
        {
            DBG_ASSERT(glyfDesc->numberOfContours==-1);
            ReadGLYFCompound(itemData, glyfItem, i);
        }
    }

    int done = 0;

}



/******************************************************************************/

void ReadLOCA(uchar8* data, stTTF* ttf, stLOCA* loca)
{
    uchar8* locaData = GetTableData(data, ttf, "loca");
    DBG_ASSERT(locaData);

    int numGlyphs = ttf->m_maxp.numGlyphs;

    DBG_ASSERT(numGlyphs>0 && numGlyphs<5000); // Sanity Check
    loca->glyphOffsets = new int[numGlyphs];

    DBG_ASSERT(ttf->m_head.indexToLocFormat==0 || ttf->m_head.indexToLocFormat==1);
    bool shortOffsets = ttf->m_head.indexToLocFormat==0 ? true : false;


    for (int i=0; i<numGlyphs; i++)
    {
        if (shortOffsets)
        {
            uint16 offset        = *((uint16*)locaData);        locaData+=2;    SwapEndian(&offset);
            loca->glyphOffsets[i]    = offset;
        }
        else
        {
            uint32 offset        = *((uint32*)locaData);        locaData+=4;    SwapEndian(&offset);
            loca->glyphOffsets[i]    = offset;
        }
    }

}


/******************************************************************************/

void ReadMAXP(uchar8* data, stTTF* ttf, stMAXP* maxp)
{
    uchar8* maxpData = GetTableData(data, ttf, "maxp");
    DBG_ASSERT(maxpData);

    maxp->version                = *((Fixed*)maxpData);        maxpData+=4;    SwapEndian(&maxp->version);
    maxp->numGlyphs                = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->numGlyphs);
    maxp->maxPoints                = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxPoints);
    maxp->maxContours            = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxContours);

    maxp->maxComponentPoints    = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxComponentPoints);
    maxp->maxComponentContours    = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxComponentContours);
    maxp->maxZones                = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxZones);
    maxp->maxTwilightPoints        = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxTwilightPoints);
    maxp->maxStorage            = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxStorage);

    maxp->maxFunctionDefs        = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxFunctionDefs);
    maxp->maxInstructionDefs    = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxInstructionDefs);
    maxp->maxStackElements        = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxStackElements);

    maxp->maxSizeOfInstructions    = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxSizeOfInstructions);
    maxp->maxComponentElements    = *((uint16*)maxpData);        maxpData+=2;    SwapEndian(&maxp->maxComponentElements);

    DBG_ASSERT(maxp->version == 0x00010000);                    // 0x00010000 if (version 1.0)
    DBG_ASSERT(maxp->maxZones == 2);                            // set to 2
}

/******************************************************************************/

void ReadHEAD(uchar8* data, stTTF* ttf, stHEAD* head)
{
    uchar8* headData = GetTableData(data, ttf, "head");
    DBG_ASSERT(headData);

    head->version                = *((Fixed*)headData);        headData+=4;    SwapEndian(&head->version);
    head->fontRevision            = *((Fixed*)headData);        headData+=4;    SwapEndian(&head->fontRevision);
    head->checkSumAdjustment    = *((uint32*)headData);        headData+=4;    SwapEndian(&head->checkSumAdjustment);
    head->magicNumber            = *((uint32*)headData);        headData+=4;    SwapEndian(&head->magicNumber);
    head->flags                    = *((uint16*)headData);        headData+=2;    SwapEndian(&head->flags);
    head->unitsPerEm            = *((uint16*)headData);        headData+=2;    SwapEndian(&head->unitsPerEm);

    DBG_ASSERT(sizeof(longDateTime)==8);
    head->created                = *((uint16*)headData);        headData+=8;    //SwapEndian(&head->created);
    head->modified                = *((uint16*)headData);        headData+=8;    //SwapEndian(&head->modified);

    head->xMin                    = *((FWord*)headData);        headData+=2;    SwapEndian(&head->xMin);
    head->yMin                    = *((FWord*)headData);        headData+=2;    SwapEndian(&head->yMin);
    head->xMax                    = *((FWord*)headData);        headData+=2;    SwapEndian(&head->xMax);
    head->yMax                    = *((FWord*)headData);        headData+=2;    SwapEndian(&head->yMax);
    head->macStyle                = *((uint16*)headData);        headData+=2;    SwapEndian(&head->macStyle);

    head->lowestRecPPEM            = *((uint16*)headData);        headData+=2;    SwapEndian(&head->lowestRecPPEM);
    head->fontDirectionHint        = *((int16*)headData);        headData+=2;    SwapEndian(&head->fontDirectionHint);

    head->indexToLocFormat        = *((int16*)headData);        headData+=2;    SwapEndian(&head->indexToLocFormat);
    head->glyphDataFormat        = *((int16*)headData);        headData+=2;    SwapEndian(&head->glyphDataFormat);

    DBG_ASSERT(head->version == 0x00010000);                            // 0x00010000 if (version 1.0)
    DBG_ASSERT(head->magicNumber == 0x5F0F3CF5);                        // set to 0x5F0F3CF5 
    DBG_ASSERT(head->indexToLocFormat==0 || head->indexToLocFormat==1);    // 0 for short offsets, 1 for long 
}

/******************************************************************************/

void ReadTTF(const char* szFileName, stTTF* ttf)
{
    dprintf("Reading TTF File :%s\n", szFileName);

    FILE * fp = fopen(szFileName, "rb");
    DBG_ASSERT(fp);

    int fileSize  = FileSize(fp);
    dprintf("FileSize: %d Bytes\n", fileSize);

    ReadFontDirectory(fp, &ttf->m_fontDirectory);

    uchar8* data = new uchar8[fileSize + 100]; //+100 is safetly buffer
    DBG_ASSERT(data);
    fseek(fp, 0, SEEK_SET);
    fread(data, fileSize, 1, fp);
    fclose (fp);

    ReadCMAP(data, ttf, &ttf->m_cmap);
    ReadHEAD(data, ttf, &ttf->m_head);
    ReadMAXP(data, ttf, &ttf->m_maxp);
    ReadLOCA(data, ttf, &ttf->m_loca);
    ReadGLYF(data, ttf, &ttf->m_glyf);


    //DebugDrawGlyf(ttf);

    delete[] data;
}

/******************************************************************************/


Resources & Links


• WebGPU version Loading/Tesselating and Visualizing TTF Font (LINK)

• C++ Vanilla Code for Loading/Parsing TTF Files (as shown above - complete code) (Download)






























 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2026 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.