The Font Set Format is a JSON-based specification for defining collections of font configurations as unions of cross-products. It’s useful for automated testing, automated asset building, automated sample generation, and any scenario requiring systematic font property exploration.
Key Features:
Use Cases:
{
"fontSets": [
{
"name": "Optional descriptive name",
"density": [1.0, 2.0],
"families": ["Arial", "Georgia"],
"styles": ["normal", "italic", "oblique"],
"weights": ["normal", "bold", [100, 900, 100]],
"sizes": [[12, 24, 0.5], 48]
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
fontSets |
Array | Yes | Array of font set definitions (see below) |
Each object in the fontSets array defines a cross-product of font properties.
| Field | Type | Required | Description |
|---|---|---|---|
name |
String | No | Optional descriptive name for documentation |
density |
Array | Yes | Pixel densities (e.g., [1.0], [1.0, 2.0]) |
families |
Array | Yes | Font family names (e.g., ["Arial", "Courier New"]) |
styles |
Array | Yes | Font styles: "normal", "italic", "oblique" |
weights |
Array | Yes | Font weights (see Weight Values below) |
sizes |
Array | Yes | Font sizes in CSS pixels (numbers or ranges) |
Arrays can contain three types of values:
1.0, "Arial", "normal"[1.0, 2.0], ["Arial", "Georgia"][start, stop, step]Range Format: Three-element array [start, stop, step] where:
start: Starting value (inclusive)stop: Ending value (inclusive)step: Increment stepRange Examples:
[12, 24, 0.5] → [12, 12.5, 13, 13.5, ..., 24]
[100, 900, 100] → [100, 200, 300, 400, 500, 600, 700, 800, 900]
[1.0, 2.0, 0.5] → [1.0, 1.5, 2.0]
1.0 (standard), 2.0 (Retina/HiDPI)[1.0], [1.0, 2.0], [1.0, 1.5, 2.0]["Arial"], ["Arial", "Georgia", "Times New Roman"]"normal", "italic", "oblique"["normal"], ["normal", "italic"], ["normal", "italic", "oblique"]"normal", "bold", "bolder", "lighter"100, 200, 300, 400, 500, 600, 700, 800, 900"100", "200", …, "900"["normal"]
["normal", "bold"]
[400, 700]
["normal", [400, 900, 100]]
[12, 14, 16, 18] // Explicit sizes
[[12, 24, 0.5]] // Range: 12 to 24 with 0.5 steps
[[12, 24, 1], 48, 64] // Range plus specific sizes
Each font set generates a cross-product of all its properties:
Formula: density_count × families_count × styles_count × weights_count × sizes_count
Example:
{
"density": [1.0, 2.0], // 2 values
"families": ["Arial"], // 1 value
"styles": ["normal", "italic"], // 2 values
"weights": ["normal", "bold"], // 2 values
"sizes": [[12, 14, 1]] // 3 values (12, 13, 14)
}
Total: 2 × 1 × 2 × 2 × 3 = 24 font configurations
Multiple font sets are combined via union (not cross-product between sets).
Example:
{
"fontSets": [
{
"name": "Arial Small Sizes",
"density": [1.0],
"families": ["Arial"],
"styles": ["normal"],
"weights": ["normal"],
"sizes": [[8, 12, 1]] // 5 sizes: 8, 9, 10, 11, 12
},
{
"name": "Georgia Large Sizes",
"density": [1.0],
"families": ["Georgia"],
"styles": ["normal"],
"weights": ["normal"],
"sizes": [[18, 24, 2]] // 4 sizes: 18, 20, 22, 24
}
]
}
Total: 5 + 4 = 9 font configurations
This allows different font families to have different size ranges or weight options.
Simple testing of one font family across size range:
{
"fontSets": [
{
"name": "Arial Size Testing",
"density": [1.0],
"families": ["Arial"],
"styles": ["normal"],
"weights": ["normal"],
"sizes": [[12, 24, 0.5]]
}
]
}
Total: 1 × 1 × 1 × 1 × 25 = 25 configurations
Testing font families with different styles:
{
"fontSets": [
{
"name": "Serif and Sans-Serif Styles",
"density": [1.0, 2.0],
"families": ["Arial", "Georgia", "Courier New"],
"styles": ["normal", "italic"],
"weights": ["normal"],
"sizes": [14, 16, 18, 20]
}
]
}
Total: 2 × 3 × 2 × 1 × 4 = 48 configurations
Testing all numeric weights for a font:
{
"fontSets": [
{
"name": "Arial Weight Spectrum",
"density": [1.0],
"families": ["Arial"],
"styles": ["normal"],
"weights": [[100, 900, 100]],
"sizes": [16]
}
]
}
Total: 1 × 1 × 1 × 9 × 1 = 9 configurations
Comprehensive specification with different requirements per font:
{
"fontSets": [
{
"name": "UI Fonts - Standard Sizes",
"density": [1.0, 2.0],
"families": ["Arial", "Helvetica"],
"styles": ["normal", "italic"],
"weights": ["normal", "bold", [400, 700, 100]],
"sizes": [[12, 18, 2]]
},
{
"name": "Monospace - Code Editor Sizes",
"density": [1.0],
"families": ["Courier New", "Monaco"],
"styles": ["normal"],
"weights": ["normal"],
"sizes": [10, 11, 12, 13, 14, 16]
},
{
"name": "Display Fonts - Large Sizes",
"density": [2.0],
"families": ["Georgia", "Times New Roman"],
"styles": ["normal", "italic"],
"weights": ["normal", "bold"],
"sizes": [[24, 48, 4]]
}
]
}
Totals:
Grand Total: 516 configurations
Creating assets for both standard and high-DPI displays:
{
"fontSets": [
{
"name": "Cross-Platform Font Assets",
"density": [1.0, 2.0],
"families": ["Arial", "Roboto", "Open Sans"],
"styles": ["normal", "italic", "oblique"],
"weights": ["normal", "bold", [300, 700, 100]],
"sizes": [[8, 32, 0.5]]
}
]
}
Total: 2 × 3 × 3 × 7 × 49 = 6,174 configurations
Creating the BitmapTextInvariant font for special Unicode font-invariant characters:
{
"fontSets": [
{
"name": "BitmapTextInvariant Monospaced Symbols",
"density": [1.0, 2.0],
"families": ["BitmapTextInvariant"],
"styles": ["normal"],
"weights": ["normal"],
"sizes": [[9, 72, 0.5]]
}
]
}
Details:
Character Set Selection (hardcoded in implementation):
fontFamily === 'BitmapTextInvariant': Uses predefined font-invariant characterssrc/builder/create-glyphs.js and cannot be overridden via configurationTotal: 2 × 1 × 1 × 1 × 128 = 256 configurations
See README.md for API usage examples and docs/ARCHITECTURE.md for implementation details.
Basic Usage:
// Load the JSON specification
const spec = {
fontSets: [
{
density: [1.0],
families: ["Arial"],
styles: ["normal"],
weights: ["normal"],
sizes: [[12, 24, 1]]
}
]
};
// Create generator
const generator = new FontSetGenerator(spec);
// Get count without generating instances
console.log(`Total fonts: ${generator.getCount()}`);
// Iterate over font configurations
for (const fontProps of generator.iterator()) {
console.log(fontProps.idString);
// Use fontProps for testing, asset building, sample generation, etc.
}
The FontSetGenerator validates specifications and throws descriptive errors:
fontSets arrayfontSets cannot be emptynormal, italic, obliqueExample error messages:
"Font set specification must contain 'fontSets' array"
"Set 1: Missing required field 'families'"
"Set 2: Field 'sizes' cannot be empty"
"Invalid range: start (24) > stop (12)"
"Invalid fontWeight: 1000 - must be one of: normal, bold, ..."
The FontSetGenerator is designed for memory efficiency:
Memory usage: O(total_range_values) not O(total_configurations)
Example:
{
"sizes": [[8, 100, 0.5]] // 185 size values
}
This allows generation of massive font sets (10,000+ configurations) without memory issues.
name field for documentation and debugginggetCount() before iteration to verify expectations