Page 1 of 1

Reading a TrueType File

Posted: Fri Oct 16, 2015 10:30 pm
by chris319
Here is a little program to obtain some of the metrics from a truetype file. It reads the .ttf file directly, not relying on functions specific to any operating system. This program finds the ascender, descender and line gap (external leading) of a font. In this demo both times and arial are available.

Code: Select all

;Font metrics
;Updated on 10/18/2015 by chris319
OpenConsole()

pointSize = 72
;internal leading ;accents

    Procedure.q EndianQ(val.q)
      Protected addr.l = @val
      EnableASM
      MOV edx, addr
      MOV eax, [edx + 4]
      MOV edx, [edx]
      BSWAP eax
      BSWAP edx
      DisableASM
      ProcedureReturn
    EndProcedure

Procedure.l EndianL(val.l);change endian of 32-bit variable
    !MOV Eax,dword[p.v_val]
    !BSWAP Eax
    !MOV dword[p.v_val],Eax
    ProcedureReturn val.l
EndProcedure

Structure fontHeader
tableVersionNumber.l
fontRevision.l
checkSumAdjustment.l
magicNumber.l
flags.u
unitsPerEm.u
 created.q
 modified.q
 xMin.w
 yMin.w
 xMax.w
 yMax.w
; USHORT 	macStyle
; USHORT 	lowestRecPPEM
; SHORT 	fontDirectionHint
; SHORT 	indexToLocFormat
; SHORT 	glyphDataFormat
EndStructure
fontHeader.fontHeader

;Structure Size_Metrics
;    x_ppem.u;      /* horizontal pixels per EM               */
;    y_ppem.u;      /* vertical pixels per EM                 */

;     FT_Fixed   x_scale;     /* scaling values used to convert font    */
;     FT_Fixed   y_scale;     /* units to 26.6 fractional pixels        */
;     FT_Pos     ascender;    /* ascender in 26.6 frac. pixels          */
;     FT_Pos     descender;   /* descender in 26.6 frac. pixels         */
;     FT_Pos     height;      /* text height in 26.6 frac. pixels       */
;     FT_Pos     max_advance; /* max horizontal advance, in 26.6 pixels */
;EndStructure
;sizeMetrics.Size_Metrics

Structure  TT_HoriHeader
    Version.l
;    Version.u
    Ascender.w
    Descender.w
    lineGap.w
EndStructure
hh.TT_HoriHeader

Structure tagTT_OFFSET_TABLE
    MajorVersion.u
    uMinorVersion.u
    numTables.u
    uSearchRange.u
    uEntrySelector.u
    uRangeShift.u
EndStructure
offsetTable.tagTT_OFFSET_TABLE

Structure tagTT_TABLE_DIRECTORY
    szTag.a[4] ;table name
    CheckSum.l ;Checksum
    Offset.l ;Offset from beginning of file
    Length.l ;length of the table in bytes
EndStructure
tableDirectory.tagTT_TABLE_DIRECTORY
szTag.s

Structure _tagTT_NAME_TABLE_HEADER ;Header of names table
    uFSelector.u ;format selector. Always 0
    uNRCount.u ;Name Records count
    uStorageOffset.u ;Offset for strings storage from start of the table
EndStructure

Structure tagTT_NAME_RECORD ;Record in names table
    uPlatformID.u
    uEncodingID.u
    uLanguageID.u
    uNameID.u
    uStringLength.u
    uStringOffset.u  ;from start of storage area
EndStructure

;fontName$ = "arial.ttf"
fontName$ = "times.ttf"
;fontName$ = "verdana.ttf"
result = ReadFile(1,fontName$)
If result = 0:Debug "Error opening font file":EndIf
PrintN(fontName$)
LoadFont(1,"times new roman",72)

ReadData(1,@offsetTable,SizeOf(offsetTable))
;FileSeek(1,SizeOf(offsetTable))
test$ = ""
While test$ <> "hhea"
  ReadData(1,@tableDirectory,SizeOf(tableDirectory))
  test$ = PeekS(@tableDirectory\szTag[0], 4, #PB_Ascii)
Wend

tableDirectory\Offset = EndianL(tableDirectory\Offset)
;EnableASM:ROL tableDirectory\Offset,8:DisableASM ;swap bytes
FileSeek(1,tableDirectory\Offset.l)
ReadData(1,@hh,SizeOf(TT_HoriHeader))

EnableASM:ROL hh\Ascender,8:DisableASM ;swap bytes
;Debug hh\ascender
floatAscender.f = hh\Ascender

EnableASM:ROL hh\Descender,8:DisableASM ;swap bytes
;Debug hh\Descender
floatDescender.f = hh\Descender

EnableASM:ROL hh\lineGap,8:DisableASM ;swap bytes
;Debug hh\lineGap
floatLineGap.f = hh\lineGap

;EnableASM:ROL hh\Version,16:DisableASM ;swap bytes
;hh\Version = EndianL(hh\Version)

FileSeek(1,0)
ReadData(1,@offsetTable,SizeOf(offsetTable))
FileSeek(1,SizeOf(offsetTable))

test$ = ""
Repeat
  ReadData(1,@tableDirectory,SizeOf(tableDirectory))
  test$ = PeekS(@tableDirectory\szTag[0], 4, #PB_Ascii)
Until test$ = "head"
tableDirectory\Offset = EndianL(tableDirectory\Offset);CONVERT TO LITTLE ENDIAN
FileSeek(1,tableDirectory\Offset)

ReadData(1,@fontHeader,SizeOf(fontHeader))
;Debug Bin(fontHeader\tableVersionNumber)
;EnableASM:ROL fontHeader\magicNumber,16:DisableASM ;swap bytes
;Debug Hex(fontHeader\magicNumber,#PB_Long)

EnableASM:ROL fontHeader\unitsPerEm,8:DisableASM ;swap bytes
PrintN("Units per em:  "+Str(fontHeader\unitsPerEm))

scale.f = pointSize/fontHeader\unitsPerEm

;Debug floatAscender *scale
;Debug floatDescender *scale
;Debug floatLineGap *scale
;Debug floatLineGap *scale

floatAscender * scale
floatDescender * scale
floatLineGap * scale
PrintN("Ascender  "+StrF(floatAscender,0) + "")
PrintN("Descender  "+StrF(floatDescender,0) + "")
PrintN("Line gap (external leading)  "+StrF(floatLineGap,0) + "")

emHeight = fontHeader\unitsPerEm/pointSize
PrintN("em Height:  "+Str(emHeight))

internalLeading = (floatAscender) - (floatDescender) - emHeight
PrintN("Internal leading:  "+Str(internalLeading))

CloseFile(1)

OpenWindow(1,500,150,600,400,"",#PB_Window_BorderLess)
StartDrawing(WindowOutput(1))
DrawingFont(FontID(1))
DrawText(50,0,"ghi"+Chr(202),#White,#Red)
;LineXY(0,72-floatAscender*scale,600,72-floatAscender*scale,#White)
;LineXY(0,floatDescender*scale,600,floatDescender*scale,#White)
;LineXY(0,pointSize,600,pointSize,#White)
;LineXY(0,emHeight,600,emHeight,#White);emHeight
;Debug (floatAscender - floatDescender)+internalLeading;emHeight
baseLine=(floatAscender - floatDescender)+emHeight
LineXY(0,baseLine,600,baseLine,#Black);base line
;LineXY(0,(baseLine + emHeight)- floatlineGap,600,(baseLine + emHeight)- floatlineGap,#Green)
LineXY(0,(baseLine + emHeight),600,(baseLine + emHeight),#Yellow)

lineSpace=baseLine + emHeight

DrawText(50,lineSpace,Chr(202)+"ight",#White,#Red)

LineXY(0,emHeight+floatDescender+floatLineGap,600,emHeight+floatDescender+floatLineGap,#Blue)
;LineXY(0,baseLine-internalLeading,600,baseLine-internalLeading,#Blue)
StopDrawing()

PrintN(Chr(10)+"Press any key to exit")
While Inkey() = ""
Delay(100)
Wend
End

Re: Reading a TrueType File

Posted: Sun Oct 18, 2015 5:29 pm
by chris319
The program has been updated to remove bugs contained in the previous version.

This code is needed to draw lines of text with the correct amount of spacing between rows. It was desired to have a solution which works on any OS.

Truetype has some truly weird things going on. The variables are big endian and must be converted to little endian in addition to converting from Truetype's own variable types to ones compatible with PureBasic. The code could be cleaned up in places but I'll leave that to you.