Task 181: .EOT File Format

Task 181: .EOT File Format

.EOT File Format Specifications

The .EOT (Embedded OpenType) file format is a proprietary font format developed by Microsoft for embedding fonts in web pages. It wraps an OpenType or TrueType font with additional metadata and optional compression. The format is defined in the W3C submission document, which describes the EMBEDDEDFONT structure. There are three versions: 0x00010000, 0x00020001, and 0x00020002, with later versions adding fields for root string URL checking and EUDC (End User Defined Characters) support. The structure consists of a header with fixed and variable-length fields, followed by the font data.

1. List of Properties Intrinsic to the .EOT File Format

The properties refer to the fields in the EMBEDDEDFONT structure. These are intrinsic to the file format and include metadata about the font, embedding permissions, and the embedded font data itself. The fields are listed in order for the most comprehensive version (0x00020002), noting which are conditional on version. Sizes are in bytes; variable sizes depend on content. Data types are as specified in the format.

  • EOTSize: Unsigned long (4 bytes) - Total length of the structure in bytes, including strings and font data.
  • FontDataSize: Unsigned long (4 bytes) - Length of the embedded OpenType font data in bytes.
  • Version: Unsigned long (4 bytes) - Format version (0x00010000, 0x00020001, or 0x00020002).
  • Flags: Unsigned long (4 bytes) - Processing flags (e.g., compression type, subsetting).
  • FontPANOSE: Byte array (10 bytes) - PANOSE classification for the font.
  • Charset: Byte (1 byte) - Character set of the font (e.g., 0x01 for default).
  • Italic: Byte (1 byte) - Italic flag (0x01 if italic).
  • Weight: Unsigned long (4 bytes) - Font weight value.
  • fsType: Unsigned short (2 bytes) - Embedding permission flags.
  • MagicNumber: Unsigned short (2 bytes) - Magic number (0x504C) for validation.
  • UnicodeRange1: Unsigned long (4 bytes) - Unicode range bits 0-31.
  • UnicodeRange2: Unsigned long (4 bytes) - Unicode range bits 32-63.
  • UnicodeRange3: Unsigned long (4 bytes) - Unicode range bits 64-95.
  • UnicodeRange4: Unsigned long (4 bytes) - Unicode range bits 96-127.
  • CodePageRange1: Unsigned long (4 bytes) - Code page range bits 0-31.
  • CodePageRange2: Unsigned long (4 bytes) - Code page range bits 32-63.
  • CheckSumAdjustment: Unsigned long (4 bytes) - Checksum adjustment from the font header.
  • Reserved1: Unsigned long (4 bytes) - Reserved (must be 0).
  • Reserved2: Unsigned long (4 bytes) - Reserved (must be 0).
  • Reserved3: Unsigned long (4 bytes) - Reserved (must be 0).
  • Reserved4: Unsigned long (4 bytes) - Reserved (must be 0).
  • Padding1: Unsigned short (2 bytes) - Padding for alignment (0x0000).
  • FamilyNameSize: Unsigned short (2 bytes) - Size of FamilyName array in bytes.
  • FamilyName: UTF-16 string (variable) - English font family name.
  • Padding2: Unsigned short (2 bytes) - Padding for alignment (0x0000).
  • StyleNameSize: Unsigned short (2 bytes) - Size of StyleName array in bytes.
  • StyleName: UTF-16 string (variable) - English font style name.
  • Padding3: Unsigned short (2 bytes) - Padding for alignment (0x0000).
  • VersionNameSize: Unsigned short (2 bytes) - Size of VersionName array in bytes.
  • VersionName: UTF-16 string (variable) - English font version name.
  • Padding4: Unsigned short (2 bytes) - Padding for alignment (0x0000).
  • FullNameSize: Unsigned short (2 bytes) - Size of FullName array in bytes.
  • FullName: UTF-16 string (variable) - English full font name.
  • RootStringSize: Unsigned short (2 bytes) - Size of RootString array in bytes (present if Version >= 0x00020001).
  • RootString: UTF-16 string (variable) - Root strings for URL binding (present if Version >= 0x00020001).
  • RootStringCheckSum: Unsigned long (4 bytes) - Checksum for RootString (present if Version == 0x00020002).
  • EUDCCodePage: Unsigned long (4 bytes) - EUDC code page (present if Version == 0x00020002).
  • Padding5: Unsigned short (2 bytes) - Padding for alignment (0x0000, present if Version == 0x00020002).
  • SignatureSize: Unsigned short (2 bytes) - Size of Signature array in bytes (present if Version == 0x00020002).
  • Signature: Byte array (variable) - Signature data (present if Version == 0x00020002).
  • EUDCFlags: Unsigned long (4 bytes) - EUDC flags (present if Version == 0x00020002).
  • EUDCFontSize: Unsigned long (4 bytes) - Size of EUDCFontData in bytes (present if Version == 0x00020002).
  • EUDCFontData: Byte array (variable) - EUDC font data (present if Version == 0x00020002).
  • FontData: Byte array (variable) - The embedded font data (FontDataSize bytes), potentially compressed.

3. Ghost Blog Embedded HTML/JavaScript for Drag and Drop .EOT File Parsing

The following is an embedded HTML snippet with JavaScript that can be used in a Ghost blog post. It creates a drag-and-drop area where a .EOT file can be dropped, parses the file using DataView, and displays all properties on the screen. It assumes the file is in version 0x00020002 for completeness but can handle earlier versions by skipping conditional fields.

Drag and drop .EOT file here

4. Python Class for .EOT File Handling

The following Python class uses the struct module to decode and encode .EOT files. It can read a file, parse the properties, print them to console, and write a new file with the same or modified properties. It handles version 0x00020002 for completeness.

import struct
import os

class EOTHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}
        self.read_file()

    def read_file(self):
        with open(self.filepath, 'rb') as f:
            data = f.read()
        offset = 0
        self.properties['EOTSize'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['FontDataSize'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['Version'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['Flags'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['FontPANOSE'] = list(struct.unpack_from('10B', data, offset)); offset += 10
        self.properties['Charset'] = struct.unpack_from('B', data, offset)[0]; offset += 1
        self.properties['Italic'] = struct.unpack_from('B', data, offset)[0]; offset += 1
        self.properties['Weight'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['fsType'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['MagicNumber'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['UnicodeRange1'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['UnicodeRange2'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['UnicodeRange3'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['UnicodeRange4'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['CodePageRange1'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['CodePageRange2'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['CheckSumAdjustment'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['Reserved1'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['Reserved2'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['Reserved3'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['Reserved4'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
        self.properties['Padding1'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['FamilyNameSize'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['FamilyName'] = data[offset:offset + self.properties['FamilyNameSize']].decode('utf-16le'); offset += self.properties['FamilyNameSize']
        self.properties['Padding2'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['StyleNameSize'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['StyleName'] = data[offset:offset + self.properties['StyleNameSize']].decode('utf-16le'); offset += self.properties['StyleNameSize']
        self.properties['Padding3'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['VersionNameSize'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['VersionName'] = data[offset:offset + self.properties['VersionNameSize']].decode('utf-16le'); offset += self.properties['VersionNameSize']
        self.properties['Padding4'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['FullNameSize'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
        self.properties['FullName'] = data[offset:offset + self.properties['FullNameSize']].decode('utf-16le'); offset += self.properties['FullNameSize']
        if self.properties['Version'] >= 0x00020001:
            self.properties['RootStringSize'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
            self.properties['RootString'] = data[offset:offset + self.properties['RootStringSize']].decode('utf-16le'); offset += self.properties['RootStringSize']
            if self.properties['Version'] == 0x00020002:
                self.properties['RootStringCheckSum'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
                self.properties['EUDCCodePage'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
                self.properties['Padding5'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
                self.properties['SignatureSize'] = struct.unpack_from('<H', data, offset)[0]; offset += 2
                self.properties['Signature'] = list(data[offset:offset + self.properties['SignatureSize']]); offset += self.properties['SignatureSize']
                self.properties['EUDCFlags'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
                self.properties['EUDCFontSize'] = struct.unpack_from('<I', data, offset)[0]; offset += 4
                self.properties['EUDCFontData'] = list(data[offset:offset + self.properties['EUDCFontSize']]); offset += self.properties['EUDCFontSize']
        self.properties['FontData'] = list(data[offset:offset + self.properties['FontDataSize']])

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write_file(self, output_path):
        data = b''
        data += struct.pack('<I', self.properties['EOTSize'])
        data += struct.pack('<I', self.properties['FontDataSize'])
        data += struct.pack('<I', self.properties['Version'])
        data += struct.pack('<I', self.properties['Flags'])
        data += struct.pack('10B', *self.properties['FontPANOSE'])
        data += struct.pack('B', self.properties['Charset'])
        data += struct.pack('B', self.properties['Italic'])
        data += struct.pack('<I', self.properties['Weight'])
        data += struct.pack('<H', self.properties['fsType'])
        data += struct.pack('<H', self.properties['MagicNumber'])
        data += struct.pack('<I', self.properties['UnicodeRange1'])
        data += struct.pack('<I', self.properties['UnicodeRange2'])
        data += struct.pack('<I', self.properties['UnicodeRange3'])
        data += struct.pack('<I', self.properties['UnicodeRange4'])
        data += struct.pack('<I', self.properties['CodePageRange1'])
        data += struct.pack('<I', self.properties['CodePageRange2'])
        data += struct.pack('<I', self.properties['CheckSumAdjustment'])
        data += struct.pack('<I', self.properties['Reserved1'])
        data += struct.pack('<I', self.properties['Reserved2'])
        data += struct.pack('<I', self.properties['Reserved3'])
        data += struct.pack('<I', self.properties['Reserved4'])
        data += struct.pack('<H', self.properties['Padding1'])
        data += struct.pack('<H', self.properties['FamilyNameSize'])
        data += self.properties['FamilyName'].encode('utf-16le')
        data += struct.pack('<H', self.properties['Padding2'])
        data += struct.pack('<H', self.properties['StyleNameSize'])
        data += self.properties['StyleName'].encode('utf-16le')
        data += struct.pack('<H', self.properties['Padding3'])
        data += struct.pack('<H', self.properties['VersionNameSize'])
        data += self.properties['VersionName'].encode('utf-16le')
        data += struct.pack('<H', self.properties['Padding4'])
        data += struct.pack('<H', self.properties['FullNameSize'])
        data += self.properties['FullName'].encode('utf-16le')
        if self.properties['Version'] >= 0x00020001:
            data += struct.pack('<H', self.properties['RootStringSize'])
            data += self.properties['RootString'].encode('utf-16le')
            if self.properties['Version'] == 0x00020002:
                data += struct.pack('<I', self.properties['RootStringCheckSum'])
                data += struct.pack('<I', self.properties['EUDCCodePage'])
                data += struct.pack('<H', self.properties['Padding5'])
                data += struct.pack('<H', self.properties['SignatureSize'])
                data += bytes(self.properties['Signature'])
                data += struct.pack('<I', self.properties['EUDCFlags'])
                data += struct.pack('<I', self.properties['EUDCFontSize'])
                data += bytes(self.properties['EUDCFontData'])
        data += bytes(self.properties['FontData'])
        with open(output_path, 'wb') as f:
            f.write(data)

# Example usage:
# handler = EOTHandler('example.eot')
# handler.print_properties()
# handler.write_file('output.eot')

5. Java Class for .EOT File Handling

The following Java class uses ByteBuffer to decode and encode .EOT files. It can read a file, parse the properties, print them to console, and write a new file.

import java.io.*;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class EOTHandler {
    private Map<String, Object> properties = new HashMap<>();
    private String filepath;

    public EOTHandler(String filepath) {
        this.filepath = filepath;
        readFile();
    }

    private void readFile() {
        try (FileInputStream fis = new FileInputStream(filepath)) {
            byte[] data = fis.readAllBytes();
            ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
            int offset = 0;

            properties.put("EOTSize", buffer.getInt(offset)); offset += 4;
            properties.put("FontDataSize", buffer.getInt(offset)); offset += 4;
            properties.put("Version", buffer.getInt(offset)); offset += 4;
            properties.put("Flags", buffer.getInt(offset)); offset += 4;
            byte[] panose = new byte[10];
            buffer.position(offset); buffer.get(panose); offset += 10;
            properties.put("FontPANOSE", panose);
            properties.put("Charset", buffer.get(offset)); offset += 1;
            properties.put("Italic", buffer.get(offset)); offset += 1;
            properties.put("Weight", buffer.getInt(offset)); offset += 4;
            properties.put("fsType", buffer.getShort(offset)); offset += 2;
            properties.put("MagicNumber", buffer.getShort(offset)); offset += 2;
            properties.put("UnicodeRange1", buffer.getInt(offset)); offset += 4;
            properties.put("UnicodeRange2", buffer.getInt(offset)); offset += 4;
            properties.put("UnicodeRange3", buffer.getInt(offset)); offset += 4;
            properties.put("UnicodeRange4", buffer.getInt(offset)); offset += 4;
            properties.put("CodePageRange1", buffer.getInt(offset)); offset += 4;
            properties.put("CodePageRange2", buffer.getInt(offset)); offset += 4;
            properties.put("CheckSumAdjustment", buffer.getInt(offset)); offset += 4;
            properties.put("Reserved1", buffer.getInt(offset)); offset += 4;
            properties.put("Reserved2", buffer.getInt(offset)); offset += 4;
            properties.put("Reserved3", buffer.getInt(offset)); offset += 4;
            properties.put("Reserved4", buffer.getInt(offset)); offset += 4;
            properties.put("Padding1", buffer.getShort(offset)); offset += 2;
            short familyNameSize = buffer.getShort(offset); offset += 2;
            properties.put("FamilyNameSize", familyNameSize);
            byte[] familyNameBytes = new byte[familyNameSize];
            buffer.position(offset); buffer.get(familyNameBytes); offset += familyNameSize;
            properties.put("FamilyName", new String(familyNameBytes, StandardCharsets.UTF_16LE));
            properties.put("Padding2", buffer.getShort(offset)); offset += 2;
            short styleNameSize = buffer.getShort(offset); offset += 2;
            properties.put("StyleNameSize", styleNameSize);
            byte[] styleNameBytes = new byte[styleNameSize];
            buffer.position(offset); buffer.get(styleNameBytes); offset += styleNameSize;
            properties.put("StyleName", new String(styleNameBytes, StandardCharsets.UTF_16LE));
            properties.put("Padding3", buffer.getShort(offset)); offset += 2;
            short versionNameSize = buffer.getShort(offset); offset += 2;
            properties.put("VersionNameSize", versionNameSize);
            byte[] versionNameBytes = new byte[versionNameSize];
            buffer.position(offset); buffer.get(versionNameBytes); offset += versionNameSize;
            properties.put("VersionName", new String(versionNameBytes, StandardCharsets.UTF_16LE));
            properties.put("Padding4", buffer.getShort(offset)); offset += 2;
            short fullNameSize = buffer.getShort(offset); offset += 2;
            properties.put("FullNameSize", fullNameSize);
            byte[] fullNameBytes = new byte[fullNameSize];
            buffer.position(offset); buffer.get(fullNameBytes); offset += fullNameSize;
            properties.put("FullName", new String(fullNameBytes, StandardCharsets.UTF_16LE));
            int version = (int) properties.get("Version");
            if (version >= 0x00020001) {
                short rootStringSize = buffer.getShort(offset); offset += 2;
                properties.put("RootStringSize", rootStringSize);
                byte[] rootStringBytes = new byte[rootStringSize];
                buffer.position(offset); buffer.get(rootStringBytes); offset += rootStringSize;
                properties.put("RootString", new String(rootStringBytes, StandardCharsets.UTF_16LE));
                if (version == 0x00020002) {
                    properties.put("RootStringCheckSum", buffer.getInt(offset)); offset += 4;
                    properties.put("EUDCCodePage", buffer.getInt(offset)); offset += 4;
                    properties.put("Padding5", buffer.getShort(offset)); offset += 2;
                    short signatureSize = buffer.getShort(offset); offset += 2;
                    properties.put("SignatureSize", signatureSize);
                    byte[] signature = new byte[signatureSize];
                    buffer.position(offset); buffer.get(signature); offset += signatureSize;
                    properties.put("Signature", signature);
                    properties.put("EUDCFlags", buffer.getInt(offset)); offset += 4;
                    int eudcFontSize = buffer.getInt(offset); offset += 4;
                    properties.put("EUDCFontSize", eudcFontSize);
                    byte[] eudcFontData = new byte[eudcFontSize];
                    buffer.position(offset); buffer.get(eudcFontData); offset += eudcFontSize;
                    properties.put("EUDCFontData", eudcFontData);
                }
            }
            int fontDataSize = (int) properties.get("FontDataSize");
            byte[] fontData = new byte[fontDataSize];
            buffer.position(offset); buffer.get(fontData); offset += fontDataSize;
            properties.put("FontData", fontData);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void printProperties() {
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }

    public void writeFile(String outputPath) {
        try (FileOutputStream fos = new FileOutputStream(outputPath)) {
            ByteBuffer buffer = ByteBuffer.allocate((int) properties.get("EOTSize")).order(ByteOrder.LITTLE_ENDIAN);
            int offset = 0;

            buffer.putInt(offset, (int) properties.get("EOTSize")); offset += 4;
            buffer.putInt(offset, (int) properties.get("FontDataSize")); offset += 4;
            buffer.putInt(offset, (int) properties.get("Version")); offset += 4;
            buffer.putInt(offset, (int) properties.get("Flags")); offset += 4;
            buffer.position(offset); buffer.put((byte[]) properties.get("FontPANOSE")); offset += 10;
            buffer.put(offset, (byte) properties.get("Charset")); offset += 1;
            buffer.put(offset, (byte) properties.get("Italic")); offset += 1;
            buffer.putInt(offset, (int) properties.get("Weight")); offset += 4;
            buffer.putShort(offset, (short) properties.get("fsType")); offset += 2;
            buffer.putShort(offset, (short) properties.get("MagicNumber")); offset += 2;
            buffer.putInt(offset, (int) properties.get("UnicodeRange1")); offset += 4;
            buffer.putInt(offset, (int) properties.get("UnicodeRange2")); offset += 4;
            buffer.putInt(offset, (int) properties.get("UnicodeRange3")); offset += 4;
            buffer.putInt(offset, (int) properties.get("UnicodeRange4")); offset += 4;
            buffer.putInt(offset, (int) properties.get("CodePageRange1")); offset += 4;
            buffer.putInt(offset, (int) properties.get("CodePageRange2")); offset += 4;
            buffer.putInt(offset, (int) properties.get("CheckSumAdjustment")); offset += 4;
            buffer.putInt(offset, (int) properties.get("Reserved1")); offset += 4;
            buffer.putInt(offset, (int) properties.get("Reserved2")); offset += 4;
            buffer.putInt(offset, (int) properties.get("Reserved3")); offset += 4;
            buffer.putInt(offset, (int) properties.get("Reserved4")); offset += 4;
            buffer.putShort(offset, (short) properties.get("Padding1")); offset += 2;
            buffer.putShort(offset, (short) properties.get("FamilyNameSize")); offset += 2;
            buffer.position(offset); buffer.put(((String) properties.get("FamilyName")).getBytes(StandardCharsets.UTF_16LE)); offset += (short) properties.get("FamilyNameSize");
            buffer.putShort(offset, (short) properties.get("Padding2")); offset += 2;
            buffer.putShort(offset, (short) properties.get("StyleNameSize")); offset += 2;
            buffer.position(offset); buffer.put(((String) properties.get("StyleName")).getBytes(StandardCharsets.UTF_16LE)); offset += (short) properties.get("StyleNameSize");
            buffer.putShort(offset, (short) properties.get("Padding3")); offset += 2;
            buffer.putShort(offset, (short) properties.get("VersionNameSize")); offset += 2;
            buffer.position(offset); buffer.put(((String) properties.get("VersionName")).getBytes(StandardCharsets.UTF_16LE)); offset += (short) properties.get("VersionNameSize");
            buffer.putShort(offset, (short) properties.get("Padding4")); offset += 2;
            buffer.putShort(offset, (short) properties.get("FullNameSize")); offset += 2;
            buffer.position(offset); buffer.put(((String) properties.get("FullName")).getBytes(StandardCharsets.UTF_16LE)); offset += (short) properties.get("FullNameSize");
            int version = (int) properties.get("Version");
            if (version >= 0x00020001) {
                buffer.putShort(offset, (short) properties.get("RootStringSize")); offset += 2;
                buffer.position(offset); buffer.put(((String) properties.get("RootString")).getBytes(StandardCharsets.UTF_16LE)); offset += (short) properties.get("RootStringSize");
                if (version == 0x00020002) {
                    buffer.putInt(offset, (int) properties.get("RootStringCheckSum")); offset += 4;
                    buffer.putInt(offset, (int) properties.get("EUDCCodePage")); offset += 4;
                    buffer.putShort(offset, (short) properties.get("Padding5")); offset += 2;
                    buffer.putShort(offset, (short) properties.get("SignatureSize")); offset += 2;
                    buffer.position(offset); buffer.put((byte[]) properties.get("Signature")); offset += (short) properties.get("SignatureSize");
                    buffer.putInt(offset, (int) properties.get("EUDCFlags")); offset += 4;
                    buffer.putInt(offset, (int) properties.get("EUDCFontSize")); offset += 4;
                    buffer.position(offset); buffer.put((byte[]) properties.get("EUDCFontData")); offset += (int) properties.get("EUDCFontSize");
                }
            }
            buffer.position(offset); buffer.put((byte[]) properties.get("FontData"));
            fos.write(buffer.array());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Example usage:
    // public static void main(String[] args) {
    //     EOTHandler handler = new EOTHandler("example.eot");
    //     handler.printProperties();
    //     handler.writeFile("output.eot");
    // }
}

6. JavaScript Class for .EOT File Handling

The following JavaScript class uses DataView to parse .EOT files. It requires Node.js with fs for file I/O. It can read a file, parse the properties, print them to console, and write a new file.

const fs = require('fs');

class EOTHandler {
  constructor(filepath) {
    this.filepath = filepath;
    this.properties = {};
    this.readFile();
  }

  readFile() {
    const data = fs.readFileSync(this.filepath);
    const view = new DataView(data.buffer);
    let offset = 0;

    this.properties.EOTSize = view.getUint32(offset, true); offset += 4;
    this.properties.FontDataSize = view.getUint32(offset, true); offset += 4;
    this.properties.Version = view.getUint32(offset, true); offset += 4;
    this.properties.Flags = view.getUint32(offset, true); offset += 4;
    this.properties.FontPANOSE = [];
    for (let i = 0; i < 10; i++) {
      this.properties.FontPANOSE.push(view.getUint8(offset)); offset += 1;
    }
    this.properties.Charset = view.getUint8(offset); offset += 1;
    this.properties.Italic = view.getUint8(offset); offset += 1;
    this.properties.Weight = view.getUint32(offset, true); offset += 4;
    this.properties.fsType = view.getUint16(offset, true); offset += 2;
    this.properties.MagicNumber = view.getUint16(offset, true); offset += 2;
    this.properties.UnicodeRange1 = view.getUint32(offset, true); offset += 4;
    this.properties.UnicodeRange2 = view.getUint32(offset, true); offset += 4;
    this.properties.UnicodeRange3 = view.getUint32(offset, true); offset += 4;
    this.properties.UnicodeRange4 = view.getUint32(offset, true); offset += 4;
    this.properties.CodePageRange1 = view.getUint32(offset, true); offset += 4;
    this.properties.CodePageRange2 = view.getUint32(offset, true); offset += 4;
    this.properties.CheckSumAdjustment = view.getUint32(offset, true); offset += 4;
    this.properties.Reserved1 = view.getUint32(offset, true); offset += 4;
    this.properties.Reserved2 = view.getUint32(offset, true); offset += 4;
    this.properties.Reserved3 = view.getUint32(offset, true); offset += 4;
    this.properties.Reserved4 = view.getUint32(offset, true); offset += 4;
    this.properties.Padding1 = view.getUint16(offset, true); offset += 2;
    this.properties.FamilyNameSize = view.getUint16(offset, true); offset += 2;
    this.properties.FamilyName = new TextDecoder('utf-16le').decode(data.slice(offset, offset + this.properties.FamilyNameSize)); offset += this.properties.FamilyNameSize;
    this.properties.Padding2 = view.getUint16(offset, true); offset += 2;
    this.properties.StyleNameSize = view.getUint16(offset, true); offset += 2;
    this.properties.StyleName = new TextDecoder('utf-16le').decode(data.slice(offset, offset + this.properties.StyleNameSize)); offset += this.properties.StyleNameSize;
    this.properties.Padding3 = view.getUint16(offset, true); offset += 2;
    this.properties.VersionNameSize = view.getUint16(offset, true); offset += 2;
    this.properties.VersionName = new TextDecoder('utf-16le').decode(data.slice(offset, offset + this.properties.VersionNameSize)); offset += this.properties.VersionNameSize;
    this.properties.Padding4 = view.getUint16(offset, true); offset += 2;
    this.properties.FullNameSize = view.getUint16(offset, true); offset += 2;
    this.properties.FullName = new TextDecoder('utf-16le').decode(data.slice(offset, offset + this.properties.FullNameSize)); offset += this.properties.FullNameSize;
    if (this.properties.Version >= 0x00020001) {
      this.properties.RootStringSize = view.getUint16(offset, true); offset += 2;
      this.properties.RootString = new TextDecoder('utf-16le').decode(data.slice(offset, offset + this.properties.RootStringSize)); offset += this.properties.RootStringSize;
      if (this.properties.Version === 0x00020002) {
        this.properties.RootStringCheckSum = view.getUint32(offset, true); offset += 4;
        this.properties.EUDCCodePage = view.getUint32(offset, true); offset += 4;
        this.properties.Padding5 = view.getUint16(offset, true); offset += 2;
        this.properties.SignatureSize = view.getUint16(offset, true); offset += 2;
        this.properties.Signature = [];
        for (let i = 0; i < this.properties.SignatureSize; i++) {
          this.properties.Signature.push(view.getUint8(offset)); offset += 1;
        }
        this.properties.EUDCFlags = view.getUint32(offset, true); offset += 4;
        this.properties.EUDCFontSize = view.getUint32(offset, true); offset += 4;
        this.properties.EUDCFontData = [];
        for (let i = 0; i < this.properties.EUDCFontSize; i++) {
          this.properties.EUDCFontData.push(view.getUint8(offset)); offset += 1;
        }
      }
    }
    this.properties.FontData = [];
    for (let i = 0; i < this.properties.FontDataSize; i++) {
      this.properties.FontData.push(view.getUint8(offset)); offset += 1;
    }
  }

  printProperties() {
    console.log(this.properties);
  }

  writeFile(outputPath) {
    const buffer = new ArrayBuffer(this.properties.EOTSize);
    const view = new DataView(buffer);
    let offset = 0;

    view.setUint32(offset, this.properties.EOTSize, true); offset += 4;
    view.setUint32(offset, this.properties.FontDataSize, true); offset += 4;
    view.setUint32(offset, this.properties.Version, true); offset += 4;
    view.setUint32(offset, this.properties.Flags, true); offset += 4;
    this.properties.FontPANOSE.forEach(b => { view.setUint8(offset, b); offset += 1; });
    view.setUint8(offset, this.properties.Charset); offset += 1;
    view.setUint8(offset, this.properties.Italic); offset += 1;
    view.setUint32(offset, this.properties.Weight, true); offset += 4;
    view.setUint16(offset, this.properties.fsType, true); offset += 2;
    view.setUint16(offset, this.properties.MagicNumber, true); offset += 2;
    view.setUint32(offset, this.properties.UnicodeRange1, true); offset += 4;
    view.setUint32(offset, this.properties.UnicodeRange2, true); offset += 4;
    view.setUint32(offset, this.properties.UnicodeRange3, true); offset += 4;
    view.setUint32(offset, this.properties.UnicodeRange4, true); offset += 4;
    view.setUint32(offset, this.properties.CodePageRange1, true); offset += 4;
    view.setUint32(offset, this.properties.CodePageRange2, true); offset += 4;
    view.setUint32(offset, this.properties.CheckSumAdjustment, true); offset += 4;
    view.setUint32(offset, this.properties.Reserved1, true); offset += 4;
    view.setUint32(offset, this.properties.Reserved2, true); offset += 4;
    view.setUint32(offset, this.properties.Reserved3, true); offset += 4;
    view.setUint32(offset, this.properties.Reserved4, true); offset += 4;
    view.setUint16(offset, this.properties.Padding1, true); offset += 2;
    view.setUint16(offset, this.properties.FamilyNameSize, true); offset += 2;
    new TextEncoder('utf-16le').encode(this.properties.FamilyName).forEach(b => { view.setUint8(offset, b); offset += 1; });
    view.setUint16(offset, this.properties.Padding2, true); offset += 2;
    view.setUint16(offset, this.properties.StyleNameSize, true); offset += 2;
    new TextEncoder('utf-16le').encode(this.properties.StyleName).forEach(b => { view.setUint8(offset, b); offset += 1; });
    view.setUint16(offset, this.properties.Padding3, true); offset += 2;
    view.setUint16(offset, this.properties.VersionNameSize, true); offset += 2;
    new TextEncoder('utf-16le').encode(this.properties.VersionName).forEach(b => { view.setUint8(offset, b); offset += 1; });
    view.setUint16(offset, this.properties.Padding4, true); offset += 2;
    view.setUint16(offset, this.properties.FullNameSize, true); offset += 2;
    new TextEncoder('utf-16le').encode(this.properties.FullName).forEach(b => { view.setUint8(offset, b); offset += 1; });
    if (this.properties.Version >= 0x00020001) {
      view.setUint16(offset, this.properties.RootStringSize, true); offset += 2;
      new TextEncoder('utf-16le').encode(this.properties.RootString).forEach(b => { view.setUint8(offset, b); offset += 1; });
      if (this.properties.Version === 0x00020002) {
        view.setUint32(offset, this.properties.RootStringCheckSum, true); offset += 4;
        view.setUint32(offset, this.properties.EUDCCodePage, true); offset += 4;
        view.setUint16(offset, this.properties.Padding5, true); offset += 2;
        view.setUint16(offset, this.properties.SignatureSize, true); offset += 2;
        this.properties.Signature.forEach(b => { view.setUint8(offset, b); offset += 1; });
        view.setUint32(offset, this.properties.EUDCFlags, true); offset += 4;
        view.setUint32(offset, this.properties.EUDCFontSize, true); offset += 4;
        this.properties.EUDCFontData.forEach(b => { view.setUint8(offset, b); offset += 1; });
      }
    }
    this.properties.FontData.forEach(b => { view.setUint8(offset, b); offset += 1; });
    fs.writeFileSync(outputPath, new Uint8Array(buffer));
  }
}

// Example usage:
// const handler = new EOTHandler('example.eot');
// handler.printProperties();
// handler.writeFile('output.eot');

7. C++ Class for .EOT File Handling

The following C++ class uses binary file I/O to decode and encode .EOT files. It can read a file, parse the properties, print them to console, and write a new file. Note: "c class" is interpreted as C++ class, as C does not have classes in the same way.

#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
#include <cstring>

class EOTHandler {
private:
    std::string filepath;
    struct Properties {
        uint32_t EOTSize;
        uint32_t FontDataSize;
        uint32_t Version;
        uint32_t Flags;
        uint8_t FontPANOSE[10];
        uint8_t Charset;
        uint8_t Italic;
        uint32_t Weight;
        uint16_t fsType;
        uint16_t MagicNumber;
        uint32_t UnicodeRange1;
        uint32_t UnicodeRange2;
        uint32_t UnicodeRange3;
        uint32_t UnicodeRange4;
        uint32_t CodePageRange1;
        uint32_t CodePageRange2;
        uint32_t CheckSumAdjustment;
        uint32_t Reserved1;
        uint32_t Reserved2;
        uint32_t Reserved3;
        uint32_t Reserved4;
        uint16_t Padding1;
        uint16_t FamilyNameSize;
        std::wstring FamilyName;
        uint16_t Padding2;
        uint16_t StyleNameSize;
        std::wstring StyleName;
        uint16_t Padding3;
        uint16_t VersionNameSize;
        std::wstring VersionName;
        uint16_t Padding4;
        uint16_t FullNameSize;
        std::wstring FullName;
        uint16_t RootStringSize;
        std::wstring RootString;
        uint32_t RootStringCheckSum;
        uint32_t EUDCCodePage;
        uint16_t Padding5;
        uint16_t SignatureSize;
        std::vector<uint8_t> Signature;
        uint32_t EUDCFlags;
        uint32_t EUDCFontSize;
        std::vector<uint8_t> EUDCFontData;
        std::vector<uint8_t> FontData;
    } props;

public:
    EOTHandler(const std::string& fp) : filepath(fp) {
        readFile();
    }

    void readFile() {
        std::ifstream file(filepath, std::ios::binary | std::ios::ate);
        std::streamsize size = file.tellg();
        file.seekg(0, std::ios::beg);
        std::vector<char> buffer(size);
        file.read(buffer.data(), size);
        char* data = buffer.data();
        size_t offset = 0;

        std::memcpy(&props.EOTSize, data + offset, 4); offset += 4;
        std::memcpy(&props.FontDataSize, data + offset, 4); offset += 4;
        std::memcpy(&props.Version, data + offset, 4); offset += 4;
        std::memcpy(&props.Flags, data + offset, 4); offset += 4;
        std::memcpy(props.FontPANOSE, data + offset, 10); offset += 10;
        std::memcpy(&props.Charset, data + offset, 1); offset += 1;
        std::memcpy(&props.Italic, data + offset, 1); offset += 1;
        std::memcpy(&props.Weight, data + offset, 4); offset += 4;
        std::memcpy(&props.fsType, data + offset, 2); offset += 2;
        std::memcpy(&props.MagicNumber, data + offset, 2); offset += 2;
        std::memcpy(&props.UnicodeRange1, data + offset, 4); offset += 4;
        std::memcpy(&props.UnicodeRange2, data + offset, 4); offset += 4;
        std::memcpy(&props.UnicodeRange3, data + offset, 4); offset += 4;
        std::memcpy(&props.UnicodeRange4, data + offset, 4); offset += 4;
        std::memcpy(&props.CodePageRange1, data + offset, 4); offset += 4;
        std::memcpy(&props.CodePageRange2, data + offset, 4); offset += 4;
        std::memcpy(&props.CheckSumAdjustment, data + offset, 4); offset += 4;
        std::memcpy(&props.Reserved1, data + offset, 4); offset += 4;
        std::memcpy(&props.Reserved2, data + offset, 4); offset += 4;
        std::memcpy(&props.Reserved3, data + offset, 4); offset += 4;
        std::memcpy(&props.Reserved4, data + offset, 4); offset += 4;
        std::memcpy(&props.Padding1, data + offset, 2); offset += 2;
        std::memcpy(&props.FamilyNameSize, data + offset, 2); offset += 2;
        props.FamilyName = std::wstring(reinterpret_cast<const wchar_t*>(data + offset), props.FamilyNameSize / 2); offset += props.FamilyNameSize;
        std::memcpy(&props.Padding2, data + offset, 2); offset += 2;
        std::memcpy(&props.StyleNameSize, data + offset, 2); offset += 2;
        props.StyleName = std::wstring(reinterpret_cast<const wchar_t*>(data + offset), props.StyleNameSize / 2); offset += props.StyleNameSize;
        std::memcpy(&props.Padding3, data + offset, 2); offset += 2;
        std::memcpy(&props.VersionNameSize, data + offset, 2); offset += 2;
        props.VersionName = std::wstring(reinterpret_cast<const wchar_t*>(data + offset), props.VersionNameSize / 2); offset += props.VersionNameSize;
        std::memcpy(&props.Padding4, data + offset, 2); offset += 2;
        std::memcpy(&props.FullNameSize, data + offset, 2); offset += 2;
        props.FullName = std::wstring(reinterpret_cast<const wchar_t*>(data + offset), props.FullNameSize / 2); offset += props.FullNameSize;
        if (props.Version >= 0x00020001) {
            std::memcpy(&props.RootStringSize, data + offset, 2); offset += 2;
            props.RootString = std::wstring(reinterpret_cast<const wchar_t*>(data + offset), props.RootStringSize / 2); offset += props.RootStringSize;
            if (props.Version == 0x00020002) {
                std::memcpy(&props.RootStringCheckSum, data + offset, 4); offset += 4;
                std::memcpy(&props.EUDCCodePage, data + offset, 4); offset += 4;
                std::memcpy(&props.Padding5, data + offset, 2); offset += 2;
                std::memcpy(&props.SignatureSize, data + offset, 2); offset += 2;
                props.Signature.assign(data + offset, data + offset + props.SignatureSize); offset += props.SignatureSize;
                std::memcpy(&props.EUDCFlags, data + offset, 4); offset += 4;
                std::memcpy(&props.EUDCFontSize, data + offset, 4); offset += 4;
                props.EUDCFontData.assign(data + offset, data + offset + props.EUDCFontSize); offset += props.EUDCFontSize;
            }
        }
        props.FontData.assign(data + offset, data + offset + props.FontDataSize);
    }

    void printProperties() {
        std::cout << "EOTSize: " << props.EOTSize << std::endl;
        std::cout << "FontDataSize: " << props.FontDataSize << std::endl;
        std::cout << "Version: " << props.Version << std::endl;
        std::cout << "Flags: " << props.Flags << std::endl;
        std::cout << "FontPANOSE: ";
        for (int i = 0; i < 10; i++) std::cout << (int)props.FontPANOSE[i] << " ";
        std::cout << std::endl;
        std::cout << "Charset: " << (int)props.Charset << std::endl;
        std::cout << "Italic: " << (int)props.Italic << std::endl;
        std::cout << "Weight: " << props.Weight << std::endl;
        std::cout << "fsType: " << props.fsType << std::endl;
        std::cout << "MagicNumber: " << props.MagicNumber << std::endl;
        std::cout << "UnicodeRange1: " << props.UnicodeRange1 << std::endl;
        std::cout << "UnicodeRange2: " << props.UnicodeRange2 << std::endl;
        std::cout << "UnicodeRange3: " << props.UnicodeRange3 << std::endl;
        std::cout << "UnicodeRange4: " << props.UnicodeRange4 << std::endl;
        std::cout << "CodePageRange1: " << props.CodePageRange1 << std::endl;
        std::cout << "CodePageRange2: " << props.CodePageRange2 << std::endl;
        std::cout << "CheckSumAdjustment: " << props.CheckSumAdjustment << std::endl;
        std::cout << "Reserved1: " << props.Reserved1 << std::endl;
        std::cout << "Reserved2: " << props.Reserved2 << std::endl;
        std::cout << "Reserved3: " << props.Reserved3 << std::endl;
        std::cout << "Reserved4: " << props.Reserved4 << std::endl;
        std::cout << "Padding1: " << props.Padding1 << std::endl;
        std::cout << "FamilyNameSize: " << props.FamilyNameSize << std::endl;
        std::wcout << L"FamilyName: " << props.FamilyName << std::endl;
        std::cout << "Padding2: " << props.Padding2 << std::endl;
        std::cout << "StyleNameSize: " << props.StyleNameSize << std::endl;
        std::wcout << L"StyleName: " << props.StyleName << std::endl;
        std::cout << "Padding3: " << props.Padding3 << std::endl;
        std::cout << "VersionNameSize: " << props.VersionNameSize << std::endl;
        std::wcout << L"VersionName: " << props.VersionName << std::endl;
        std::cout << "Padding4: " << props.Padding4 << std::endl;
        std::cout << "FullNameSize: " << props.FullNameSize << std::endl;
        std::wcout << L"FullName: " << props.FullName << std::endl;
        if (props.Version >= 0x00020001) {
            std::cout << "RootStringSize: " << props.RootStringSize << std::endl;
            std::wcout << L"RootString: " << props.RootString << std::endl;
            if (props.Version == 0x00020002) {
                std::cout << "RootStringCheckSum: " << props.RootStringCheckSum << std::endl;
                std::cout << "EUDCCodePage: " << props.EUDCCodePage << std::endl;
                std::cout << "Padding5: " << props.Padding5 << std::endl;
                std::cout << "SignatureSize: " << props.SignatureSize << std::endl;
                std::cout << "Signature: ";
                for (auto b : props.Signature) std::cout << (int)b << " ";
                std::cout << std::endl;
                std::cout << "EUDCFlags: " << props.EUDCFlags << std::endl;
                std::cout << "EUDCFontSize: " << props.EUDCFontSize << std::endl;
                std::cout << "EUDCFontData size: " << props.EUDCFontData.size() << std::endl; // Truncated
            }
        }
        std::cout << "FontData size: " << props.FontData.size() << std::endl; // Truncated
    }

    void writeFile(const std::string& outputPath) {
        std::ofstream file(outputPath, std::ios::binary);
        file.write(reinterpret_cast<const char*>(&props.EOTSize), 4);
        file.write(reinterpret_cast<const char*>(&props.FontDataSize), 4);
        file.write(reinterpret_cast<const char*>(&props.Version), 4);
        file.write(reinterpret_cast<const char*>(&props.Flags), 4);
        file.write(reinterpret_cast<const char*>(props.FontPANOSE), 10);
        file.write(reinterpret_cast<const char*>(&props.Charset), 1);
        file.write(reinterpret_cast<const char*>(&props.Italic), 1);
        file.write(reinterpret_cast<const char*>(&props.Weight), 4);
        file.write(reinterpret_cast<const char*>(&props.fsType), 2);
        file.write(reinterpret_cast<const char*>(&props.MagicNumber), 2);
        file.write(reinterpret_cast<const char*>(&props.UnicodeRange1), 4);
        file.write(reinterpret_cast<const char*>(&props.UnicodeRange2), 4);
        file.write(reinterpret_cast<const char*>(&props.UnicodeRange3), 4);
        file.write(reinterpret_cast<const char*>(&props.UnicodeRange4), 4);
        file.write(reinterpret_cast<const char*>(&props.CodePageRange1), 4);
        file.write(reinterpret_cast<const char*>(&props.CodePageRange2), 4);
        file.write(reinterpret_cast<const char*>(&props.CheckSumAdjustment), 4);
        file.write(reinterpret_cast<const char*>(&props.Reserved1), 4);
        file.write(reinterpret_cast<const char*>(&props.Reserved2), 4);
        file.write(reinterpret_cast<const char*>(&props.Reserved3), 4);
        file.write(reinterpret_cast<const char*>(&props.Reserved4), 4);
        file.write(reinterpret_cast<const char*>(&props.Padding1), 2);
        file.write(reinterpret_cast<const char*>(&props.FamilyNameSize), 2);
        file.write(reinterpret_cast<const char*>(props.FamilyName.data()), props.FamilyNameSize);
        file.write(reinterpret_cast<const char*>(&props.Padding2), 2);
        file.write(reinterpret_cast<const char*>(&props.StyleNameSize), 2);
        file.write(reinterpret_cast<const char*>(props.StyleName.data()), props.StyleNameSize);
        file.write(reinterpret_cast<const char*>(&props.Padding3), 2);
        file.write(reinterpret_cast<const char*>(&props.VersionNameSize), 2);
        file.write(reinterpret_cast<const char*>(props.VersionName.data()), props.VersionNameSize);
        file.write(reinterpret_cast<const char*>(&props.Padding4), 2);
        file.write(reinterpret_cast<const char*>(&props.FullNameSize), 2);
        file.write(reinterpret_cast<const char*>(props.FullName.data()), props.FullNameSize);
        if (props.Version >= 0x00020001) {
            file.write(reinterpret_cast<const char*>(&props.RootStringSize), 2);
            file.write(reinterpret_cast<const char*>(props.RootString.data()), props.RootStringSize);
            if (props.Version == 0x00020002) {
                file.write(reinterpret_cast<const char*>(&props.RootStringCheckSum), 4);
                file.write(reinterpret_cast<const char*>(&props.EUDCCodePage), 4);
                file.write(reinterpret_cast<const char*>(&props.Padding5), 2);
                file.write(reinterpret_cast<const char*>(&props.SignatureSize), 2);
                file.write(reinterpret_cast<const char*>(props.Signature.data()), props.SignatureSize);
                file.write(reinterpret_cast<const char*>(&props.EUDCFlags), 4);
                file.write(reinterpret_cast<const char*>(&props.EUDCFontSize), 4);
                file.write(reinterpret_cast<const char*>(props.EUDCFontData.data()), props.EUDCFontSize);
            }
        }
        file.write(reinterpret_cast<const char*>(props.FontData.data()), props.FontDataSize);
    }
};

// Example usage:
// int main() {
//     EOTHandler handler("example.eot");
//     handler.printProperties();
//     handler.writeFile("output.eot");
//     return 0;
// }