How to obtain head.ChecksumAdjustment?

34 Views Asked by At

How to calculate the checksumAdjustment(ms, apple) field in the head table of a TTF file? Here is my method of calculation.

package main

import (
    "encoding/binary"
    "os"
)

func CalcCheckSum(data []byte) uint32 {
    var sum uint32
    step := 3
    for _, c := range data {
        sum += uint32(c) << ((step & 3) * 8)
        step--
    }
    return sum
}

func main() {
    var bs []byte
    bs, _ = os.ReadFile("myFont.ttf")
    // Handle TableHeader
    // ...
    // Handle TableRecord
    // ...
    var headOffset uint32 // Assume it is provided by `TableRecord["head"].Offset` and it's correct.

    binary.BigEndian.PutUint32(bs[headOffset+8:headOffset+12], 0) // 8~12 is checksumAdjustment: https://learn.microsoft.com/en-us/typography/opentype/spec/head
    sum := CalcCheckSum(bs)
    checksumAdjustment := 0xB1B0AFBA - sum
    binary.BigEndian.PutUint32(bs[headOffset+8:headOffset+12], checksumAdjustment)
}

In some fonts (I am sorry for not being able to provide details due to licensing issues), I used this calculation method. When I submit the resulting font file for verification to FontVal-2.1.2

It complains that head.ChecksumAdjustment calculation is incorrect. I'm going crazy, please help me.

1

There are 1 best solutions below

0
Carson On BEST ANSWER

you can't directly read all data content for calculation.

Reason: If some tables need to be padded with zeros, then there will be a difference between doing it separately and doing it all at once. Doing it all at once will only add zeros at the end of the data, but doing it separately will check each piece of data to determine if zeros need to be added, resulting in differences. see below:

func CalcCheckSum(data []byte) uint32 {
    var sum uint32
    step := 3
    for _, c := range data {
        sum += uint32(c) << ((step & 3) * 8)
        step--
    }
    return sum
}

func Test(t *testing.T) {
    // Doing it separately
    allData := []byte{0x01, 0x03}
    v1 := CalcCheckSum(allData)      // 0x0103_0000
    v2 := CalcCheckSum(allData[0:1]) // 0x0100_0000
    v3 := CalcCheckSum(allData[1:])  // 0x0300_0000
    fmt.Printf("%08X, %08X, %08X\n", v1, v2, v3)
    if v1 != v2+v3 {
        fmt.Println("test1")
    }

    // Doing it all at once. If the data does not need to be padded with zeros, then there is no difference between the two methods.
    allData = []byte{1, 2, 3, 4, 5, 6, 7, 8}
    v1 = CalcCheckSum(allData)      // 0x01020304 + 0x05060708 = 0x0608_0A0C
    v2 = CalcCheckSum(allData[0:4]) // 0x01020304
    v3 = CalcCheckSum(allData[4:])  // 0x05060708
    fmt.Printf("%08X, %08X, %08X\n", v1, v2, v3)
    if v1 == v2+v3 {
        fmt.Println("test2")
    }

    // Output:
    // test1
    // test2
}

playground

so you should do it separately:

0xB1B0AFBA - CalcCheckSum(bs[:recordsOffset]) + (CalcCheckSum(table1Bytes) + CalcCheckSum(table2Bytes) + ... + CalcCheckSum[tableNBytes])

The following code for detailed process.

Note, the following code assumes that the CheckSum of each TableRecord is correct.

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "os"
)

func CalcCheckSum(data []byte) uint32 {
    var sum uint32
    step := 3
    for _, c := range data {
        sum += uint32(c) << ((step & 3) * 8)
        step--
    }
    return sum
}

type TableHeader struct {
    SFNTVersion   uint32
    NumTables     uint16
    SearchRange   uint16
    EntrySelector uint16
    RangeShift    uint16
}

type TableRecord struct {
    Tag      [4]byte
    CheckSum uint32
    Offset   uint32
    Length   uint32
}

type TTFont struct {
    TableHeader
    TableRecords []TableRecord
}

func main() {
    var bs []byte
    var font TTFont
    bs, _ = os.ReadFile("test.ttf")
    r := bytes.NewReader(bs)
    _ = binary.Read(r, binary.BigEndian, &font.TableHeader)
    font.TableRecords = make([]TableRecord, font.NumTables)
    _ = binary.Read(r, binary.BigEndian, &font.TableRecords)

    /*
        set head.ChecksumAdjustment = 0
        and Compile each table...
        update each TableRecords...
    */

    var sum uint32
    recordsOffset := 12 /*header size*/ + font.NumTables*16
    sum += CalcCheckSum(bs[:recordsOffset])

    for _, record := range font.TableRecords {
        sum += record.CheckSum
    }
    checksumAdjustment := 0xB1B0AFBA - sum
    fmt.Printf("0x%X", checksumAdjustment)
}