amiga-bootcamp/11_libraries/diskfont.md
2026-04-26 15:29:06 -04:00

27 KiB
Raw Permalink Blame History

← Home · Libraries

diskfont.library — Amiga Bitmap Fonts: Concept, File Format, and Disk Loading

Overview

diskfont.library loads bitmap fonts from disk into the Amiga graphics system. Unlike modern TrueType/OpenType fonts that store mathematical curve descriptions, Amiga fonts are bitmap fonts — each glyph is a hand-drawn grid of pixels at a fixed size. A 14-pixel "Helvetica" and a 24-pixel "Helvetica" are entirely separate files, each hand-designed (or auto-scaled) for that specific pixel height.

This was the dominant font technology of the late 1980s and early 1990s. Before CPUs were fast enough to rasterize Bézier curves on the fly, storing pre-rendered pixel data was the only practical option. The Amiga's bitmap font system was sophisticated for its era — proportional spacing, kerning, algorithmic bold/italic/underline, and color fonts (OS 3.0+) — but it was fundamentally pixel-bound.

Two fonts are built into every Amiga ROM: topaz 8 and topaz 9. Every other font — helvetica, times, courier, garnet, sapphire, and any third-party font — lives on disk under the FONTS: assign and must be loaded via diskfont.library.

Note

This article focuses on the disk side — font file formats, directory structure, loading pipeline, and the conceptual model. For the in-memory TextFont structure, rendering with Text()/SetFont(), and algorithmic styles, see text_fonts.md.


The Bitmap Font Concept — Why Pixels Instead of Curves

How Bitmap Fonts Work

A bitmap font stores each character as a flat pixel grid. The letter "A" at 14 pixels tall might be a 9×14 pixel array:

.........
..#####..
.##...##.
.##...##.
.#######.
.##...##.
.##...##.
.........

Every glyph at every size is pre-drawn. The OS simply copies the glyph's pixel strip to the screen — no math, no rasterization, no hinting engine. This is why Amiga text rendering is so fast: it's just Blitter and BlitMaskBitMapRastPort operations under the hood.

Comparison with Modern TrueType / OpenType

Aspect Amiga Bitmap Fonts TrueType / OpenType
Storage Pixel grid per glyph per size Mathematical curves (Bézier splines)
Scaling Each size is a separate file; scaling creates distortion Arbitrary size rendered from same outlines
File size Small per size, but multiplies by size count One file covers all sizes
Quality at scale Perfect at designed size; jagged/blurry when scaled Smooth at any size (with hinting)
Rendering cost Near-zero — just copy pixels Requires curve rasterization + hinting
Rotation Impossible (pixel grid is axis-aligned) Built-in (curves are coordinate-independent)
Memory Full glyph bitmap in Chip RAM during use Glyph cache; outlines are small
Typographic features Basic: kerning, proportional spacing Rich: ligatures, alternates, feature tables

Why the Amiga Used Bitmap Fonts

  1. CPU limitations: A 7 MHz 68000 cannot rasterize TrueType curves at interactive speeds. Even on 1990s PCs, TrueType rasterization was a noticeable cost. The Amiga solved this by pre-rendering everything.
  2. Memory constraints: A 14-pixel bitmap font for ASCII 32127 fits in roughly 24 KB. Loading and blitting pixels uses zero CPU beyond the initial LoadSeg().
  3. Hardware acceleration: The Blitter can copy glyph pixels directly to the screen's planar bitmap. This hardware-accelerated text path depends on glyphs already being in planar pixel format.
  4. Era conventions: In 1985, bitmap fonts were the industry standard. Apple's Macintosh (1984) used bitmap "suitcase" fonts. Windows 3.1 (1992) defaulted to bitmap system fonts. TrueType didn't become universal until the mid-1990s.

Font File Format — The Complete On-Disk Structure

The Two-File Architecture

Every Amiga bitmap font consists of two kinds of files:

FONTS:
├── helvetica.font              ← Font Contents File (descriptor)
└── helvetica/                  ← Font Data Directory
    ├── 9                       ← Font Descriptor File (size 9)
    ├── 11                      ← Font Descriptor File (size 11)
    ├── 14                      ← Font Descriptor File (size 14)
    ├── 19                      ← Font Descriptor File (size 19)
    └── 24                      ← Font Descriptor File (size 24)
graph TB
    subgraph "Font Contents File<br/>(helvetica.font)"
        HDR["FontContentsHeader"]
        FC1["FontContents[0]: size 9"]
        FC2["FontContents[1]: size 11"]
        FC3["FontContents[2]: size 14"]
        FC4["..."]
    end

    subgraph "Font Descriptor Files<br/>(helvetica/9, 11, 14…)"
        DFH9["DiskFontHeader<br/>+ TextFont<br/>+ glyph bitmaps<br/>(size 9)"]
        DFH11["DiskFontHeader<br/>+ TextFont<br/>+ glyph bitmaps<br/>(size 11)"]
        DFH14["DiskFontHeader<br/>+ TextFont<br/>+ glyph bitmaps<br/>(size 14)"]
    end

    FC1 -.->|"points to"| DFH9
    FC2 -.->|"points to"| DFH11
    FC3 -.->|"points to"| DFH14

    style HDR fill:#e8f4fd,stroke:#2196f3,color:#333
    style DFH9 fill:#fff9c4,stroke:#f9a825,color:#333

1. The Font Contents File (.font)

This is the index file — it lists every available size for a given typeface. Its C structure:

/* libraries/diskfont.h — NDK39 */
#define MAXFONTPATH 256

struct FontContentsHeader {
    UWORD   fch_FileID;        /* $0F00 = FCH_ID, $0F02 = TFCH_ID, $0F03 = scalable */
    UWORD   fch_NumEntries;    /* number of FontContents entries following */
    struct FontContents fch_FC[];  /* variable-length array */
};

struct FontContents {
    char    fc_FileName[MAXFONTPATH];  /* path to size directory, e.g. "helvetica/14" */
    UWORD   fc_YSize;                  /* pixel height */
    UBYTE   fc_Style;                  /* FSF_BOLD, FSF_ITALIC, etc. */
    UBYTE   fc_Flags;                  /* FPF_ROMFONT, FPF_DISKFONT, etc. */
};

File ID Values

ID Name Meaning
$0F00 FCH_ID Standard bitmap font (uses FontContents entries)
$0F02 TFCH_ID Tagged bitmap font (uses TFontContents — supports TA_DeviceDPI tags)
$0F03 Scalable outline font (Compugraphic / IntelliFont, not bitmap)

File on Disk (Binary Layout)

Offset  Size  Field
──────  ────  ──────────────────────────────
$00     2     fch_FileID ($0F00 for bitmap)
$02     2     fch_NumEntries (e.g. 5 sizes)
$04     260   FontContents[0]
$108    260   FontContents[1]
$20C    260   FontContents[2]
...     ...   (NumEntries × 260 bytes each)

Each FontContents entry is exactly MAXFONTPATH + 4 = 260 bytes, regardless of actual string length. The fc_FileName field is null-padded.

Per-Entry Flags

Flag Bit Meaning
FPF_ROMFONT 0 Font is built into ROM (topaz only)
FPF_DISKFONT 1 Font is loaded from disk
FPF_REVPATH 2 Designed for right-to-left text
FPF_TALLDOT 3 Designed for Hires (640-pixel) screen
FPF_WIDEDOT 4 Designed for Lores Interlaced screen
FPF_PROPORTIONAL 5 Character widths are not constant
FPF_DESIGNED 6 Hand-designed at this size (not scaled)
FPF_REMOVED 7 Font has been removed from system list

2. The Font Descriptor File (numeric filename, e.g. 14)

Each numeric file in the font directory is a loadable DOS hunk — it's literally wrapped as a HUNK_CODE so LoadSeg() can load it. The first two longwords contain a MOVEQ #0,D0 : RTS instruction pair to safely exit if the file is accidentally executed.

/* libraries/diskfont.h — NDK39 */
#define MAXFONTNAME 32

struct DiskFontHeader {
    /* The 8 bytes BEFORE this struct (not part of it!) are: */
    /*   ULONG dfh_NextSegment;     // BPTR — filled by LoadSeg */
    /*   ULONG dfh_ReturnCode;      // MOVEQ #0,D0 : RTS       */

    struct Node     dfh_DF;         /* Exec Node — links loaded fonts together */
    UWORD           dfh_FileID;     /* DFH_ID ($0F80) */
    UWORD           dfh_Revision;   /* font revision number */
    LONG            dfh_Segment;    /* segment address after LoadSeg */
    char            dfh_Name[MAXFONTNAME];  /* font name, e.g. "helvetica" */
    struct TextFont dfh_TF;         /* The actual TextFont structure */
    /* Immediately after dfh_TF: glyph bitmap data, char location tables */
};

The dfh_TF is the same struct TextFont described in text_fonts.md. After it in memory come:

Field Description
tf_CharData Bitmap strip containing all glyphs (one row per scanline, tf_Modulo bytes wide)
tf_CharLoc Per-character location table: 2 WORDs per glyph (bit offset, width in pixels)
tf_CharSpace Proportional spacing: 1 WORD per glyph (advance width)
tf_CharKern Kerning table: 1 WORD per glyph (extra space after this character)

The Font Loading Pipeline

sequenceDiagram
    participant APP as Application
    participant DF as diskfont.library
    participant DOS as dos.library
    participant DISK as FONTS: (disk)
    participant GFX as graphics.library

    APP->>DF: OpenDiskFont(&ta)
    DF->>DISK: Open("helvetica.font", MODE_OLDFILE)
    DISK-->>DF: FileHandle
    DF->>DF: Read FontContentsHeader
    DF->>DF: Scan entries for matching YSize & Style
    DF->>DISK: Close(.font file)

    alt Size found
        DF->>DOS: LoadSeg("helvetica/14")
        DOS-->>DF: Segment (DiskFontHeader loaded)
        DF->>DF: Verify dfh_FileID == DFH_ID
        DF->>DF: Build TextFont from DiskFontHeader
        DF->>GFX: Link font into system font list
        DF-->>APP: struct TextFont *
    else Size not found
        DF-->>APP: NULL
    end

    APP->>GFX: SetFont(rp, font)
    APP->>GFX: Text(rp, "Hello", 5)
    APP->>GFX: CloseFont(font)
    GFX->>DF: Unlink & free font memory

Font Request Matching

When OpenDiskFont() receives a struct TextAttr, the library searches the .font file's entries:

  1. Exact YSize match: If the requested size exists as a fc_YSize, that entry is selected.
  2. No match: Returns NULL. Unlike modern font systems, the Amiga does not automatically scale to the nearest size.
  3. Style matching: If the requested style (FSF_BOLD, FSF_ITALIC) doesn't exist at that size, graphics.library can algorithmically generate it (SetSoftStyle).
  4. Scaled fonts (AFF_SCALED): OS 2.0+ can auto-generate intermediate sizes by scaling the nearest designed size. These appear in AvailFonts() with the AFF_SCALED flag. Quality is significantly worse than hand-designed sizes.

How Software Uses Different Fonts

The Standard Pattern

struct Library *DiskfontBase = OpenLibrary("diskfont.library", 0L);

/* 1. Request a specific disk font: */
struct TextAttr ta = {"helvetica.font", 14, 0, 0};
struct TextFont *font = OpenDiskFont(&ta);

if (font)
{
    /* 2. Assign it to the RastPort: */
    SetFont(rp, font);

    /* 3. Position cursor at baseline: */
    Move(rp, 10, 20 + font->tf_Baseline);

    /* 4. Render text: */
    Text(rp, "Disk-loaded font", 16);

    /* 5. Measure text for alignment: */
    UWORD w = TextLength(rp, "Centered", 8);
    Move(rp, (screenWidth - w) / 2, 50 + font->tf_Baseline);
    Text(rp, "Centered", 8);

    /* 6. Release when done: */
    CloseFont(font);
}

CloseLibrary(DiskfontBase);

ROM Font vs Disk Font — When to Use Each

Scenario Use Why
System UI, debug output, quick text OpenFont("topaz.font") — ROM font Always available, zero disk access, zero load time
Application body text, custom UI OpenDiskFont("helvetica.font") — disk font Professional appearance; user expects non-topaz fonts
Word processor, DTP, final output OpenDiskFont() with FPF_DESIGNED Hand-tuned glyphs look best; avoid scaled variants
Memory-critical (games, demos) OpenFont("topaz.font") or embed custom font Disk fonts consume Chip RAM for glyph data
Font enumeration / chooser AvailFonts() + iterate Build a font picker dialog

Getting the User's Preferred Font

Applications should respect the system font preference set in Workbench Preferences:

/* Read the user's font preference: */
struct Preferences *prefs = AllocMem(sizeof(struct Preferences), MEMF_ANY);
GetPrefs(prefs, sizeof(struct Preferences));

struct TextAttr userFont = {
    prefs->FontName,          /* e.g. "helvetica.font" */
    prefs->FontSize,          /* e.g. 14 or YSIZE_DEFAULT */
    FSF_NORMAL,
    FPF_DISKFONT
};

struct TextFont *font = OpenDiskFont(&userFont);
if (!font)
{
    /* Fall back to topaz if user's font is unavailable */
    struct TextAttr fallback = {"topaz.font", 8, 0, FPF_ROMFONT};
    font = OpenFont(&fallback);
}
SetFont(rp, font);

FreeMem(prefs, sizeof(struct Preferences));

Adding New Fonts to Amiga

Manual Installation

  1. Copy the .font file into FONTS::

    Copy MyFont.font FONTS:
    
  2. Copy the font directory into FONTS::

    Copy MyFont FONTS: ALL
    
  3. Verify the structure:

    FONTS:
    ├── MyFont.font          ← font contents (index)
    └── MyFont/
        ├── 11               ← size 11 pixels
        ├── 14               ← size 14 pixels
        └── 18               ← size 18 pixels
    
  4. Re-scan available fonts (or reboot). The AvailFonts() function automatically picks up new fonts on the next call — no reboot needed.

Extending the FONTS: Assign

The FONTS: assign can span multiple directories and volumes:

; Add a floppy disk of fonts to the search path:
Assign FONTS: FontDisk: ADD

; Add fonts from a hard drive subdirectory:
Assign FONTS: Work:MyFonts ADD

; Verify current path:
Assign FONTS:
; Output: FONTS: SYS:Fonts Work:MyFonts FontDisk:

Fonts on any path in the FONTS: assign cascade — if helvetica.font exists in both SYS:Fonts and Work:MyFonts, the first one found is used.

Creating Your Own Fonts — Font Editors

Tool Source Era Notes
FED (Font EDitor) Commodore (shipped with Workbench) 19851990 Basic but functional; shipped with 1.x
Personal Fonts Maker Cloanto 19901994 Professional drawing tools; the standard for custom bitmap fonts
TypeFace 1992+ Supported both bitmap and Compugraphic outline editing
Calligrapher 1991+ Calligraphic stroke-based font design
FontMachine 1993+ AmigaGuide-based font designer

Outline Fonts — The Evolution Beyond Bitmaps

OS 2.0 introduced support for Compugraphic (CG) outline fonts, and OS 3.0 added the Agfa IntelliFont engine (via bullet.library). These are .font files with fch_FileID = $0F03 that point to scalable outline data instead of fixed-size bitmaps.

graph LR
    subgraph "Bitmap (1.x+)"
        B_FONT[".font file<br/>fch_FileID = $0F00"]
        B_DIR["size/14<br/>(DiskFontHeader + glyph pixels)"]
        B_FONT --> B_DIR
    end

    subgraph "Outline (2.0+)"
        O_FONT[".font file<br/>fch_FileID = $0F03"]
        O_ENGINE["bullet.library<br/>(IntelliFont engine)"]
        O_DATA["Outline data<br/>(.otag / .ofont / .type)"]
        O_FONT --> O_ENGINE
        O_ENGINE --> O_DATA
    end

    style B_FONT fill:#fff9c4,stroke:#f9a825
    style O_FONT fill:#e8f5e9,stroke:#4caf50

Outline fonts can scale to any size from a single set of curves, eliminating the need for separate per-size files. However, they require CPU rasterization and were considered slow on 6800068020 systems — bitmap fonts remained the default for interactive applications.


Enumerating All Available Fonts

struct AvailFontsHeader *afh = NULL;
LONG bufSize = 4096;

/* Retry loop — buffer may be too small: */
do {
    if (afh) { FreeMem(afh, bufSize); bufSize += 1024; }
    afh = AllocMem(bufSize, MEMF_ANY | MEMF_CLEAR);
} while (AvailFonts((STRPTR)afh, bufSize, AFF_DISK | AFF_MEMORY | AFF_SCALED) > 0);

struct AvailFonts *af = &afh->afh_AF;

for (LONG i = 0; i < afh->afh_NumEntries; i++)
{
    UWORD type = af[i].af_Type;
    const char *source =
        (type & AFF_MEMORY) ? "ROM" :
        (type & AFF_DISK)   ? "disk" :
        (type & AFF_SCALED) ? "scaled" : "other";

    Printf("%-20s  y=%2ld  %-6s  %s%s%s\n",
           af[i].af_Attr.ta_Name,
           af[i].af_Attr.ta_YSize,
           source,
           (af[i].af_Attr.ta_Style & FSF_BOLD)       ? "B" : " ",
           (af[i].af_Attr.ta_Style & FSF_ITALIC)      ? "I" : " ",
           (af[i].af_Attr.ta_Style & FSF_UNDERLINED)  ? "U" : " ");
}

FreeMem(afh, bufSize);

Memory Considerations for AvailFonts()

The buffer must be in any memory (not Chip RAM). AvailFonts() writes AvailFontsHeader + N × AvailFonts entries. If the buffer is too small, it returns the number of additional bytes needed. The retry loop above handles this correctly.


Color Fonts (OS 3.0+)

OS 3.0 extended bitmap fonts with color support — glyphs with multiple bitplanes:

Traditional font:  1 bitplane  → black or background color
Color font:        up to 8 bitplanes → 256 colors per glyph
/* graphics/text.h — NDK39 */
struct ColorTextFont {
    struct TextFont ctf_TF;          /* standard TextFont */
    UWORD   ctf_Flags;              /* CT_COLORFONT, CT_GREYFONT */
    UBYTE   ctf_Depth;              /* number of bitplanes (18) */
    UBYTE   ctf_FgColor;            /* default foreground pen */
    UBYTE   ctf_Low;                /* lowest color register used */
    UBYTE   ctf_High;               /* highest color register used */
    APTR    ctf_PlanePick;          /* plane selection for rendering */
    APTR    ctf_PlaneOnOff;         /* plane on/off masks */
    struct ColorFontColors *ctf_ColorTable;
    APTR    ctf_CharData[8];        /* per-plane glyph data pointers */
};

Color fonts store each bitplane as a separate glyph bitmap. The ctf_ColorTable maps pen numbers to the screen's actual palette — a font's "red" might be pen 17 on one screen and pen 5 on another.

Warning

Color fonts require Chip RAM for all glyph data planes. A 24-pixel color font at 8 bitplanes uses 8× the memory of a monochrome bitmap font at the same size. On a 512 KB Chip RAM system, this can be prohibitive for multi-font applications.


Historical Context

Competitive Landscape (19851993)

Platform Font System Scalable? Notes
AmigaOS 1.x3.x Bitmap fonts (diskfont.library) OS 2.0+ (Compugraphic) Proportional, kerning, algorithmic styles; color fonts in 3.0
Macintosh System 16 Bitmap "suitcase" fonts + FOND resources No (until TrueType in System 7, 1991) Multiple sizes per family; 72 DPI screen assumption
Windows 1.x3.0 Bitmap .FON files No (until TrueType in 3.1, 1992) Monospaced system font; proportional in 3.0
Atari ST TOS GDOS bitmap fonts (optional) No (until SpeedoGDOS, 1991) 8×8 default system font; GDOS added proportional fonts
X11 (Unix) BDF (Bitmap Distribution Format) No (PostScript via display PostScript) Server-side fonts; XFT + FreeType came much later

The Amiga was ahead of its contemporaries in font quality: proportional spacing, kerning, and algorithmic style generation were available from day one (1985). The Mac didn't get TrueType until 1991; Windows until 1992. But by 1993, the industry had shifted to outline fonts, and AmigaOS's bitmap model was showing its age.

The Compugraphic / IntelliFont Era

Commodore licensed Agfa Compugraphic (CG) outline font technology for AmigaOS 2.0, and later Agfa IntelliFont for OS 3.0. These were real outline fonts (cubic Bézier curves), rendered by bullet.library. The Amiga could do what macOS and Windows did with TrueType — but on slower hardware and with a smaller font library. By the time outline fonts arrived, the Amiga market was already in decline.


Modern Analogies

Amiga Concept Modern Equivalent Where It Matches / Differs
Bitmap .font file + per-size directory Sprite sheets in game engines Both are pre-rendered pixel grids; neither scales well
OpenDiskFont()SetFont()Text() CTFontCreateWithName()CGContextSetFont()CTLineDraw() Same three-step pattern; modern APIs handle scaling transparently
AvailFonts() enumeration NSFontManager.availableFonts / DirectWrite IDWriteFontCollection Same concept — enumerate what's installed
Algorithmic bold/italic NSFontManager.convertWeight(_:of:) Amiga does pixel smearing/shearing; modern does weighted stroke
FONTS: assign $XDG_DATA_DIRS/fonts / Windows C:\Windows\Fonts Same multi-path search concept
Compugraphic outline fonts TrueType (Apple/Microsoft, 1991) Both are Bézier outline formats; CG predates TrueType

Decision Guide — Bitmap vs Outline Fonts on Amiga

flowchart TD
    A["Need a font for<br/>your application?"] --> B{"Target OS version?"}
    B -->|"1.x"| C["✅ Bitmap only<br/>OpenDiskFont()"]
    B -->|"2.x+"| D{"Font quality<br/>critical?"}
    B -->|"3.x+"| D

    D -->|"Yes — DTP, word processor"| E{"CPU?"}
    D -->|"No — UI, debug, utility"| F["✅ Bitmap font<br/>fast, cheap, reliable"]

    E -->|"68020+"| G["Outline font viable<br/>(bullet.library)"]
    E -->|"68000"| H["⚠️ Outline too slow<br/>Use designed bitmap sizes"]

    style C fill:#e8f5e9,stroke:#4caf50
    style F fill:#e8f5e9,stroke:#4caf50
    style G fill:#fff9c4,stroke:#f9a825

Best Practices

  1. Always check OpenDiskFont() return value — the font might not be installed on the user's system; fall back to topaz
  2. Use FPF_DESIGNED sizes — auto-scaled (AFF_SCALED) fonts look jagged; prefer hand-designed bitmap sizes
  3. Close fonts when doneCloseFont() decrements the accessor count; leaking fonts wastes Chip RAM
  4. Cache opened fonts — opening the same font repeatedly reads from disk each time; keep a pointer if you'll use it again
  5. Respect the user's font preference — read GetPrefs() and use the system font for UI elements
  6. Use topaz for debug/development output — no disk dependency, always 8 or 9 pixels, predictable width
  7. Don't mix bitmap and outline font APIsOpenFont() and OpenDiskFont() return the same TextFont* type, but outline fonts interact with bullet.library internally

Antipatterns

Antipattern Why It's Wrong Correct Approach
The Hardcoded Helvetica Assuming "helvetica.font/14" exists → NULL on minimal Workbench installs Always fall back to topaz if OpenDiskFont() returns NULL
The Leaked Accessor Calling OpenDiskFont() in a loop without CloseFont() → font never freed Pair every OpenDiskFont() with a CloseFont()
The AFF_SCALED Surprise Using AvailFonts() without checking AFF_SCALED flag → user picks a blurry scaled size Filter out or visually flag AFF_SCALED entries in font pickers
The Baseline Mishandling Move(rp, x, y) without adding font->tf_Baseline → text renders at the wrong vertical position Always: Move(rp, x, y + font->tf_Baseline)
The Missing FONTS: Assign Relying on FONTS: being available on a minimal boot → OpenDiskFont() fails silently Verify FONTS: assign exists, or open from an explicit path

Pitfalls

1. AvailFonts() Buffer Management

Bad — single-shot allocation, might overflow:

APTR buf = AllocMem(4096, MEMF_ANY);
AvailFonts(buf, 4096, AFF_DISK);  /* may return shortfall > 0 — data truncated! */

Good — retry loop until buffer is large enough:

LONG size = 4096;
struct AvailFontsHeader *afh = NULL;
LONG shortfall;
do {
    if (afh) { FreeMem(afh, size); size += shortfall; }
    afh = AllocMem(size, MEMF_ANY | MEMF_CLEAR);
} while ((shortfall = AvailFonts((STRPTR)afh, size, AFF_DISK | AFF_MEMORY)) > 0);

2. Font Name Includes .font Suffix

OpenDiskFont() expects the full filename including the .font extension:

/* WRONG: */
struct TextAttr ta = {"helvetica", 14, 0, 0};
OpenDiskFont(&ta);  /* → NULL */

/* CORRECT: */
struct TextAttr ta = {"helvetica.font", 14, 0, 0};
OpenDiskFont(&ta);  /* → works */

3. DiskFont Must Come from OpenDiskFont(), Not OpenFont()

OpenFont() searches only already-loaded (ROM/memory) fonts. OpenDiskFont() triggers the disk search pipeline:

/* WRONG for disk fonts: */
struct TextFont *f = OpenFont(&ta);   /* only finds ROM fonts! */

/* CORRECT: */
struct TextFont *f = OpenDiskFont(&ta);  /* searches FONTS: on disk */

4. Font Not Found After Installation Without Restart

Adding fonts via Assign FONTS: NewPath: ADD takes effect immediately. But fonts are cached after first load — if a font was previously opened and is still in memory, the new version won't be seen until the old one is CloseFont()'d by all users.


References

NDK Headers

  • libraries/diskfont.hFontContentsHeader, FontContents, DiskFontHeader, AvailFontsHeader
  • graphics/text.hTextFont, TextAttr, ColorTextFont, style/flags constants
  • preferences.hstruct Preferences (font preference fields)

ADCD 2.1 / ROM Kernel Manual

  • Libraries Manual — Chapter 29: "Graphics Library and Text" — section "Composition of a Bitmap Font on Disk"
  • diskfont.library Autodocs — OpenDiskFont(), AvailFonts()

External References

Cross-References in This Knowledge Base