Task 598: .R8P File Format

Task 598: .R8P File Format

1. List of Properties for .R8P File Format

The .R8P file format is associated with Intellifont PCL 4 Bitmap Font files, used in HP's Printer Command Language (PCL) for bitmap fonts. It is compatible with PCL 4 and described in PCL 5 documentation under Format 0 for backward compatibility. The file contains a font descriptor (header) followed by character definitions (each with a descriptor and raster data). Properties intrinsic to the format (from the font descriptor block) include:

  • Font Descriptor Size: Total size of the descriptor in bytes (UI, 2 bytes).
  • Header Format: Font format value (UB, 1 byte; 0 for bitmap).
  • Font Type: Relation to symbol set and printable codes (UB, 1 byte; e.g., 0=bound 32-127).
  • Style MSB: Most significant byte of style word (UI, 2 bytes; includes posture, width, structure).
  • Baseline Position: Y-position of baseline (UI, 2 bytes; in dots).
  • Cell Width: Width of character cell (UI, 2 bytes; in dots).
  • Cell Height: Height of character cell (UI, 2 bytes; in dots).
  • Orientation: Font orientation (UB, 1 byte; e.g., 0=portrait).
  • Spacing: Spacing type (B/UB, 1 byte; 0=fixed, 1=proportional).
  • Symbol Set: Symbol set ID (UI, 2 bytes; e.g., 8U for Roman-8).
  • Pitch (Default HMI): Horizontal spacing (UI, 2 bytes; in quarter-dots).
  • Height: Vertical height (UI, 2 bytes; in quarter-dots).
  • xHeight: Height of lowercase 'x' (UI, 2 bytes; in quarter-dots).
  • Width Type: Width category (SB, 1 byte; e.g., 0=normal).
  • Style LSB: Least significant byte of style word (UB, 1 byte).
  • Stroke Weight: Stroke thickness (SB, 1 byte; e.g., 0=medium).
  • Typeface LSB: Least significant byte of typeface number (UB, 1 byte).
  • Typeface MSB: Most significant byte of typeface (UB, 1 byte; includes vendor).
  • Serif Style: Serif classification (UB, 1 byte).
  • Quality/Placement: Print quality or baseline placement (UB/SB, 1 byte).
  • Underline Position (Distance): Distance to underline (SB/UI, 1-2 bytes; signed).
  • Underline Thickness (Height): Thickness of underline (UB/UI, 1 byte).
  • Text Height: Optimum inter-line spacing (UI, 2 bytes; in quarter-dots).
  • Text Width: Average lowercase width (UI, 2 bytes; in quarter-dots).
  • First Code: First character code (UI, 2 bytes).
  • Last Code / Number of Characters: Last code or total characters (UI, 2 bytes).
  • Pitch Extended / Height Extended: Extended precision for pitch/height (UB/UI, 1-2 bytes).
  • Cap Height: Capital height (UI, 2 bytes; as % of Em).
  • Font Number: Unique vendor ID (ULI, 4 bytes).
  • Font Name: ASCII font name (ASC16, 16 bytes).
  • X Resolution: Horizontal DPI (UI, 2 bytes).
  • Y Resolution: Vertical DPI (UI, 2 bytes).
  • Scale Factor: Scale factor (UI, 2 bytes; 1 for bitmap).
  • Master Underline Position: Master underline position (SI, 2 bytes; signed).
  • Master Underline Thickness (Height): Master thickness (UI, 2 bytes).
  • Font Scaling Technology: Scaling type (UB, 1 byte; 0 for bitmap).
  • Variety: Font subtype (UB, 1 byte).
  • OR Threshold: Compositing threshold (UI, 2 bytes; 0 for bitmap).
  • Global Italic Angle: Italic slant (SI, 2 bytes; degrees).
  • Global Intellifont Data Size: Size of optional Intellifont data (UI, 2 bytes; 0 for bitmap).
  • Global Intellifont Data: Optional scalable data (variable; empty for bitmap).
  • Character Complement: Array indicating present characters (variable array of UB).
  • Checksum: Header integrity check (UB/UI, 1-2 bytes).
  • Copyright: Optional ASCII copyright notice (variable ASC).
  • Data Segments: Optional segments (variable; e.g., CC for complement, IF for Intellifont).

These properties define the font's structure, metrics, and rendering behavior. Character-level properties (not listed here as they are per-character) include left/top offsets, width/height, delta X, data size, and raster data (monochrome, packed rows).

After extensive searching, no publicly available direct download links for .R8P files were found. This format is obscure and tied to legacy PCL 4 bitmap fonts, with no samples in common archives or font repositories. Related PCL bitmap font samples (often .SFT) exist, but none match the .R8P extension. If needed, PCL font tools can generate compatible files.

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .R8P File Dump

Here's a self-contained HTML page with JavaScript for drag-and-drop support. It reads the .R8P file as binary, parses the font descriptor based on the properties, and dumps them to the screen.

.R8P File Parser
Drag and drop .R8P file here

Note: This parses the core fixed header (up to ~80 bytes); variable segments require additional logic based on descriptor size.

4. Python Class for .R8P Handling

import struct
import sys

class R8PHandler:
    def __init__(self, filename):
        self.filename = filename
        self.properties = {}
        self.data = None

    def read(self):
        with open(self.filename, 'rb') as f:
            self.data = f.read()
        self.decode()

    def decode(self):
        if not self.data:
            raise ValueError("No data to decode")
        offset = 0
        self.properties['Font Descriptor Size'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Header Format'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Font Type'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Style MSB'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Baseline Position'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Cell Width'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Cell Height'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Orientation'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Spacing'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Symbol Set'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Pitch (Default HMI)'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Height'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['xHeight'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Width Type'] = struct.unpack_from('>b', self.data, offset)[0]; offset += 1
        self.properties['Style LSB'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Stroke Weight'] = struct.unpack_from('>b', self.data, offset)[0]; offset += 1
        self.properties['Typeface LSB'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Typeface MSB'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Serif Style'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Quality/Placement'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Underline Position (Distance)'] = struct.unpack_from('>h', self.data, offset)[0]; offset += 2
        self.properties['Underline Thickness (Height)'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Text Height'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Text Width'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['First Code'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Last Code / Number of Characters'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Pitch Extended / Height Extended'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Cap Height'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Font Number'] = struct.unpack_from('>I', self.data, offset)[0]; offset += 4
        self.properties['Font Name'] = self.data[offset:offset+16].decode('ascii', errors='ignore').rstrip('\x00'); offset += 16
        self.properties['X Resolution'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Y Resolution'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Scale Factor'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Master Underline Position'] = struct.unpack_from('>h', self.data, offset)[0]; offset += 2
        self.properties['Master Underline Thickness (Height)'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Font Scaling Technology'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['Variety'] = struct.unpack_from('>B', self.data, offset)[0]; offset += 1
        self.properties['OR Threshold'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        self.properties['Global Italic Angle'] = struct.unpack_from('>h', self.data, offset)[0]; offset += 2
        self.properties['Global Intellifont Data Size'] = struct.unpack_from('>H', self.data, offset)[0]; offset += 2
        # Variable fields omitted for brevity; extend as needed.

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

    def write(self, new_filename=None):
        if not self.data:
            raise ValueError("No data to write")
        filename = new_filename or self.filename
        with open(filename, 'wb') as f:
            f.write(self.data)

# Example usage:
# handler = R8PHandler('example.r8p')
# handler.read()
# handler.print_properties()
# handler.write('output.r8p')

5. Java Class for .R8P Handling

import java.io.*;
import java.nio.*;
import java.nio.file.*;

public class R8PHandler {
    private String filename;
    private byte[] data;
    private java.util.Map<String, Object> properties = new java.util.HashMap<>();

    public R8PHandler(String filename) {
        this.filename = filename;
    }

    public void read() throws IOException {
        data = Files.readAllBytes(Paths.get(filename));
        decode();
    }

    private void decode() {
        ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
        properties.put("Font Descriptor Size", buffer.getShort());
        properties.put("Header Format", buffer.get());
        properties.put("Font Type", buffer.get());
        properties.put("Style MSB", buffer.getShort());
        properties.put("Baseline Position", buffer.getShort());
        properties.put("Cell Width", buffer.getShort());
        properties.put("Cell Height", buffer.getShort());
        properties.put("Orientation", buffer.get());
        properties.put("Spacing", buffer.get());
        properties.put("Symbol Set", buffer.getShort());
        properties.put("Pitch (Default HMI)", buffer.getShort());
        properties.put("Height", buffer.getShort());
        properties.put("xHeight", buffer.getShort());
        properties.put("Width Type", buffer.get());
        properties.put("Style LSB", buffer.get());
        properties.put("Stroke Weight", buffer.get());
        properties.put("Typeface LSB", buffer.get());
        properties.put("Typeface MSB", buffer.get());
        properties.put("Serif Style", buffer.get());
        properties.put("Quality/Placement", buffer.get());
        properties.put("Underline Position (Distance)", buffer.getShort());
        properties.put("Underline Thickness (Height)", buffer.get());
        properties.put("Text Height", buffer.getShort());
        properties.put("Text Width", buffer.getShort());
        properties.put("First Code", buffer.getShort());
        properties.put("Last Code / Number of Characters", buffer.getShort());
        properties.put("Pitch Extended / Height Extended", buffer.getShort());
        properties.put("Cap Height", buffer.getShort());
        properties.put("Font Number", buffer.getInt());
        byte[] fontNameBytes = new byte[16];
        buffer.get(fontNameBytes);
        properties.put("Font Name", new String(fontNameBytes).trim());
        properties.put("X Resolution", buffer.getShort());
        properties.put("Y Resolution", buffer.getShort());
        properties.put("Scale Factor", buffer.getShort());
        properties.put("Master Underline Position", buffer.getShort());
        properties.put("Master Underline Thickness (Height)", buffer.getShort());
        properties.put("Font Scaling Technology", buffer.get());
        properties.put("Variety", buffer.get());
        properties.put("OR Threshold", buffer.getShort());
        properties.put("Global Italic Angle", buffer.getShort());
        properties.put("Global Intellifont Data Size", buffer.getShort());
        // Variable fields omitted.

    }

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

    public void write(String newFilename) throws IOException {
        if (data == null) {
            throw new IllegalStateException("No data to write");
        }
        Files.write(Paths.get(newFilename != null ? newFilename : filename), data);
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     R8PHandler handler = new R8PHandler("example.r8p");
    //     handler.read();
    //     handler.printProperties();
    //     handler.write("output.r8p");
    // }
}

6. JavaScript Class for .R8P Handling

class R8PHandler {
    constructor(filename) {
        this.filename = filename;
        this.properties = {};
        this.data = null;
    }

    async read() {
        // Assuming Node.js with fs module
        const fs = require('fs');
        this.data = fs.readFileSync(this.filename);
        this.decode();
    }

    decode() {
        const dataView = new DataView(this.data.buffer);
        let offset = 0;
        this.properties['Font Descriptor Size'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Header Format'] = dataView.getUint8(offset); offset += 1;
        this.properties['Font Type'] = dataView.getUint8(offset); offset += 1;
        this.properties['Style MSB'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Baseline Position'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Cell Width'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Cell Height'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Orientation'] = dataView.getUint8(offset); offset += 1;
        this.properties['Spacing'] = dataView.getUint8(offset); offset += 1;
        this.properties['Symbol Set'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Pitch (Default HMI)'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Height'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['xHeight'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Width Type'] = dataView.getInt8(offset); offset += 1;
        this.properties['Style LSB'] = dataView.getUint8(offset); offset += 1;
        this.properties['Stroke Weight'] = dataView.getInt8(offset); offset += 1;
        this.properties['Typeface LSB'] = dataView.getUint8(offset); offset += 1;
        this.properties['Typeface MSB'] = dataView.getUint8(offset); offset += 1;
        this.properties['Serif Style'] = dataView.getUint8(offset); offset += 1;
        this.properties['Quality/Placement'] = dataView.getUint8(offset); offset += 1;
        this.properties['Underline Position (Distance)'] = dataView.getInt16(offset, false); offset += 2;
        this.properties['Underline Thickness (Height)'] = dataView.getUint8(offset); offset += 1;
        this.properties['Text Height'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Text Width'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['First Code'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Last Code / Number of Characters'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Pitch Extended / Height Extended'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Cap Height'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Font Number'] = dataView.getUint32(offset, false); offset += 4;
        this.properties['Font Name'] = this.getAsciiString(dataView, offset, 16); offset += 16;
        this.properties['X Resolution'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Y Resolution'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Scale Factor'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Master Underline Position'] = dataView.getInt16(offset, false); offset += 2;
        this.properties['Master Underline Thickness (Height)'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Font Scaling Technology'] = dataView.getUint8(offset); offset += 1;
        this.properties['Variety'] = dataView.getUint8(offset); offset += 1;
        this.properties['OR Threshold'] = dataView.getUint16(offset, false); offset += 2;
        this.properties['Global Italic Angle'] = dataView.getInt16(offset, false); offset += 2;
        this.properties['Global Intellifont Data Size'] = dataView.getUint16(offset, false); offset += 2;
        // Variable fields omitted.

    }

    getAsciiString(dataView, offset, length) {
        let str = '';
        for (let i = 0; i < length; i++) {
            const char = dataView.getUint8(offset + i);
            if (char === 0) break;
            str += String.fromCharCode(char);
        }
        return str;
    }

    printProperties() {
        for (const [key, value] of Object.entries(this.properties)) {
            console.log(`${key}: ${value}`);
        }
    }

    write(newFilename = null) {
        if (!this.data) {
            throw new Error('No data to write');
        }
        const fs = require('fs');
        fs.writeFileSync(newFilename || this.filename, this.data);
    }
}

// Example usage:
// const handler = new R8PHandler('example.r8p');
// await handler.read();
// handler.printProperties();
// handler.write('output.r8p');

7. C Class for .R8P Handling

Note: In C, we use structs for "class-like" behavior. Here's a simple implementation.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <arpa/inet.h> // For htons, etc.

typedef struct {
    char *filename;
    uint8_t *data;
    size_t data_size;
    // Properties as a simple array for demo; use a map lib for full.
    char *prop_keys[40];
    char prop_values[40][64]; // Stringified values
    int prop_count;
} R8PHandler;

R8PHandler* create_r8p_handler(const char *filename) {
    R8PHandler *handler = malloc(sizeof(R8PHandler));
    handler->filename = strdup(filename);
    handler->data = NULL;
    handler->data_size = 0;
    handler->prop_count = 0;
    return handler;
}

void read_r8p(R8PHandler *handler) {
    FILE *f = fopen(handler->filename, "rb");
    if (!f) {
        perror("Failed to open file");
        return;
    }
    fseek(f, 0, SEEK_END);
    handler->data_size = ftell(f);
    fseek(f, 0, SEEK_SET);
    handler->data = malloc(handler->data_size);
    fread(handler->data, 1, handler->data_size, f);
    fclose(f);
    decode_r8p(handler);
}

void decode_r8p(R8PHandler *handler) {
    uint8_t *data = handler->data;
    int offset = 0;
    char buf[64];

    uint16_t val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Font Descriptor Size";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    uint8_t val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Header Format";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    // Repeat for other fields similarly...
    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Font Type";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Style MSB";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Baseline Position";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Cell Width";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Cell Height";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Orientation";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Spacing";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Symbol Set";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Pitch (Default HMI)";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Height";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "xHeight";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    int8_t val_s8 = *(int8_t*)(data + offset); offset += 1;
    snprintf(buf, sizeof(buf), "%d", val_s8);
    handler->prop_keys[handler->prop_count] = "Width Type";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Style LSB";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val_s8 = *(int8_t*)(data + offset); offset += 1;
    snprintf(buf, sizeof(buf), "%d", val_s8);
    handler->prop_keys[handler->prop_count] = "Stroke Weight";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Typeface LSB";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Typeface MSB";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Serif Style";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Quality/Placement";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    int16_t val_s16 = ntohs(*(int16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%d", val_s16);
    handler->prop_keys[handler->prop_count] = "Underline Position (Distance)";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Underline Thickness (Height)";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Text Height";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Text Width";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "First Code";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Last Code / Number of Characters";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Pitch Extended / Height Extended";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Cap Height";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    uint32_t val32 = ntohl(*(uint32_t*)(data + offset)); offset += 4;
    snprintf(buf, sizeof(buf), "%u", val32);
    handler->prop_keys[handler->prop_count] = "Font Number";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    char font_name[17];
    memcpy(font_name, data + offset, 16); font_name[16] = '\0';
    handler->prop_keys[handler->prop_count] = "Font Name";
    strcpy(handler->prop_values[handler->prop_count++], font_name); offset += 16;

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "X Resolution";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Y Resolution";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Scale Factor";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val_s16 = ntohs(*(int16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%d", val_s16);
    handler->prop_keys[handler->prop_count] = "Master Underline Position";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Master Underline Thickness (Height)";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Font Scaling Technology";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val8 = data[offset]; offset += 1;
    snprintf(buf, sizeof(buf), "%u", val8);
    handler->prop_keys[handler->prop_count] = "Variety";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "OR Threshold";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val_s16 = ntohs(*(int16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%d", val_s16);
    handler->prop_keys[handler->prop_count] = "Global Italic Angle";
    strcpy(handler->prop_values[handler->prop_count++], buf);

    val16 = ntohs(*(uint16_t*)(data + offset)); offset += 2;
    snprintf(buf, sizeof(buf), "%u", val16);
    handler->prop_keys[handler->prop_count] = "Global Intellifont Data Size";
    strcpy(handler->prop_values[handler->prop_count++], buf);
    // Variable fields omitted.

}

void print_properties(R8PHandler *handler) {
    for (int i = 0; i < handler->prop_count; i++) {
        printf("%s: %s\n", handler->prop_keys[i], handler->prop_values[i]);
    }
}

void write_r8p(R8PHandler *handler, const char *new_filename) {
    if (!handler->data) {
        fprintf(stderr, "No data to write\n");
        return;
    }
    const char *fname = new_filename ? new_filename : handler->filename;
    FILE *f = fopen(fname, "wb");
    if (!f) {
        perror("Failed to write file");
        return;
    }
    fwrite(handler->data, 1, handler->data_size, f);
    fclose(f);
}

void destroy_r8p_handler(R8PHandler *handler) {
    free(handler->data);
    free(handler->filename);
    free(handler);
}

// Example usage:
// int main() {
//     R8PHandler *handler = create_r8p_handler("example.r8p");
//     read_r8p(handler);
//     print_properties(handler);
//     write_r8p(handler, "output.r8p");
//     destroy_r8p_handler(handler);
//     return 0;
// }

Note: The C implementation uses big-endian conversion (ntohs/ntohl) assuming network byte order; adjust if file is little-endian. Variable fields are omitted for brevity in all codes.