Skip to content

TTC to TTF File Converter

TTC files are TrueType Collection files that contain the data needed to represent multiple font styles and font weights; this is a collection. On the other hand, TTF files are TrueType Font files, that contains the data required to work with only one font style or one font weight (for example: Regular, Bold or Light).

There are a lot of free websites you can use to extract the individual styles and weights from a .ttc file and create .ttf files. But why not do this with Xojo, just for fun … or because I need it! Keep reading and I will show you how.

Extracting the available styles and weights from TTC files requires parsing its data to find the data chunks required to compose the TTF. That is what we are going to do. Start by creating a new Desktop project (you can adapt it later for any supported Xojo targets like Web or Mobile).

Building the TTC to TTF Converter

All the work is going to be done via a method; but because it will be dealing with font files, it would be a good idea to start by creating our own FontUtilities module. So, add a new module to the project and name it “FontUtilities” in the Inspector Panel.

Next, with FontUtilities selected in the Navigator, add a new method to it using the following signature:

  • Method Name: ExtractTTFStylesFromTTC
  • Parameters: inputFile As FolderItem, outputFolder As FolderItem
  • Scope: Public

And add the following code into the associated Code Editor:

#Pragma DisableBackgroundTasks
#Pragma DisableBoundsChecking
#Pragma StackOverflowChecking False

If inputFile = Nil Or Not inputFile.Exists Then Return
If outputFolder = Nil Or Not outputFolder.IsFolder Or Not outputFolder.IsWriteable Then Return

Var mb As MemoryBlock

Var rb As BinaryStream = BinaryStream.Open(inputFile)
mb = rb.Read(inputFile.Length)
mb.LittleEndian = False
rb.Close

If mb <> Nil Then
  
  Var OutputBuffers() As MemoryBlock
  
  // Let's make sure this is a .ttc file by checking the first four bytes of the header
  
  If mb.StringValue(0, 4) <> "ttcf" Then Return
  
  // offset for the total of faces/styles in the ttc file
  Var totalFaces As Integer = mb.UInt32Value(8)
  
  Var tableHeaderOffset As Integer
  Var tableCount As UInt16
  Var headerLength, tLength, tableLength, totalLength, currentOffset As Integer
  
  Var outputBuffer As MemoryBlock
  
  For n As Integer = 0 To totalFaces - 1
    
    // 0x0c is the first possition of the tableHeader offsets
    tableHeaderOffset = mb.UInt32Value(12 + (n * 4))
    
    // offset to the table count from the current table header offset
    tableCount = mb.UInt16Value(tableHeaderOffset+4) 
    
    headerLength = 12 + tableCount * 16
    
    tableLength = 0 
    
    For j As Integer = 0 To tableCount - 1
      
      tLength = mb.UInt32Value(tableHeaderOffset+12+12+(j*16))
      tableLength = tableLength + Bitwise.BitAnd(tLength + 3, Bitwise.OnesComplement(3)) // next value multiple of four
    Next
    
    totalLength = headerLength + tableLength
    
    outputBuffer = New MemoryBlock(totalLength)
    outputBuffer.LittleEndian = False
    
    //Copy table to the buffer for the new file
    outputBuffer.StringValue(0,headerLength - 1) = mb.StringValue(tableHeaderOffset, headerLength - 1)
    
    currentOffset = headerLength
    
    Var tOffset, tOtherLength As Integer
    
    For j As Integer = 0 To tableCount - 1
      tOffset = mb.UInt32Value(tableHeaderOffset + 12 + 8 + (j * 16))
      tOtherLength = mb.UInt32Value(tableHeaderOffset + 12 + 12 + (j * 16))
      outputBuffer.UInt32Value(12 + 8 + (j * 16)) = currentOffset
      
      // Copy data to the buffer for the new file
      outputBuffer.StringValue(currentOffset, tOtherLength - 1) = mb.StringValue(tOffset, tOtherLength - 1)
      
      outputBuffer.UInt32Value(currentOffset) = mb.UInt32Value(tOffset)
      currentOffset = currentOffset + Bitwise.BitAnd(tOtherLength + 3, Bitwise.OnesComplement(3)) // next value multiple of four
    Next
    outputBuffers.Add(outputBuffer)
  Next
  
  // Let's create the .ttf files in the output folder
  Var max As Integer = outputBuffers.Count - 1
  Var outputFile As FolderItem
  
  For n As Integer = 0 To max
    outputFile = outputFolder.Child(inputFile.Name.NthField(".", 1) + " " + n.ToString + ".ttf")
    Var ob As BinaryStream = BinaryStream.Create(outputFile)
    ob.Write(outputBuffers(n))
    ob.Close
  Next
End If

Testing the TTC to TTF Converter

Now, let’s create a minimal user interface in order to test it. Select the Window1 window in the Navigator in order to bring up the Layout Editor. Next, drag a DesktopButton from the Library and drop it inside the window area in the Layout Editor. Use the alignment guides to help you to leave enough room between the button and the window margins.

Double click the button in the Layout Editor to open the Add Event Handler dialog box. The Pressed Event Handler should be selected already, so simply confirm it clicking on the “OK” button.

The Pressed Event Handler has been added under Button1 in the Navigator and with its associated Code Editor visible in the main area, type the following lines of code:

Var inputFontFile As FolderItem = FolderItem.ShowOpenFileDialog(".ttc")
Var outputFolder As FolderItem = FolderItem.ShowSelectFolderDialog

FontUtilities.ExtractTTFStylesFromTTC(inputFontFile, outputFolder)

The first line of code will ask the user to select the “.ttc” font file to process, and the second line will ask for the destination folder where the output (the converted .ttf font files) will be stored. Finally, the third line of code is the one calling our ExtractTFFStylesFromTTC method passing along both the file to process and the output folder.

That’s all! Click on the Run button in the main toolbar of the Xojo IDE. Once the app is running, click on the button to select any “.ttc” file you want to process. Next, the app will ask you to select the output folder and you will be able to see the extracted files inside the selected output folder.

Paul learned to program in BASIC at age 13 and has programmed in more languages than he remembers, with Xojo being an obvious favorite. When not working on Xojo, you can find him talking about retrocomputing at Goto 10 and on Mastodon @lefebvre@hachyderm.io.