{
  "openapi": "3.1.1",
  "info": {
    "title": "Webfont Subsetter (WOFF \u0026 TTF) API",
    "version": "1.0.0",
    "summary": "Shrink a font to the characters your site uses and get a WOFF file with its CSS unicode-range.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://luminafontsubset.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Shrink a font to the characters your site uses and get a WOFF file with its CSS unicode-range.",
        "description": "Upload a TrueType font (TTF or WOFF) and keep only the characters you need: type them, paste a page's text, add Unicode ranges or tick common blocks. Variable fonts can be frozen at one weight. You get a WOFF 1.0 or TTF file with kerning kept as a kern table, the size saving and a ready @font-face rule with unicode-range.\n\nMethodology:\n- The requested code points are the characters of the text, the ranges and the ticked blocks (tab and line breaks ignored). Glyphs are kept for every code point the font maps, plus .notdef and all components of composite glyphs; glyph ids are renumbered densely.\n- The subset keeps head, hhea, maxp, hmtx, loca, glyf, a new cmap (formats 4 and 12), post 3.0, name, OS/2 and hinting tables. GSUB, GPOS, GDEF and variation tables are dropped. With axis values the outlines and advances of that instance are baked in (gvar applied, composites flattened) and OS/2 weight follows wght.\n- Kerning: for subsets up to 400 glyphs every pair runs through the font's own GPOS kern lookups (class kerning included) and the non-zero values go into a legacy kern table (format 0, up to 10900 pairs), which browsers apply when GPOS is absent. Frozen variable instances use the pair list at that location.\n- WOFF 1.0: each table is zlib-compressed when that makes it smaller and 4-byte aligned, with the original checksums; the file is unpacked again and must give the subset font byte for byte. WOFF2 needs a Brotli encoder, which this platform does not have in pure TypeScript, so WOFF2 is not offered.\n\nCheck the font's license before publishing a subset: some licenses forbid modification or reserve the font name. Ligatures, alternates and mark positioning are not kept. CFF (PostScript-outline) fonts are not supported.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_font_subset",
        "x-owner-app": "lumina",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaFontSubsetInput"
              },
              "examples": {
                "example1": {
                  "summary": "Variable starter font frozen at Bold, capitals only",
                  "value": {
                    "font": {
                      "filename": "LuminaStarter-VF.ttf",
                      "contentType": "font/ttf",
                      "contentBase64": "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"
                    },
                    "text": "HOT VOLT",
                    "ranges": "",
                    "presets": [],
                    "format": "woff",
                    "kerning": true,
                    "axes": "wght=700"
                  }
                },
                "example2": {
                  "summary": "Basic Latin subset as TTF",
                  "value": {
                    "font": {
                      "filename": "LuminaStarterLigature.ttf",
                      "contentType": "font/ttf",
                      "contentBase64": "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"
                    },
                    "text": "",
                    "ranges": "U+0020-007E",
                    "presets": [],
                    "format": "ttf",
                    "kerning": true,
                    "axes": ""
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaFontSubsetResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "LuminaFontSubsetInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "font": {
            "title": "Font (TTF or WOFF with TrueType outlines; up to 10 MB; uploaded to the server)",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 9766 KiB. Accepts .ttf,.woff.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "text": {
            "title": "Characters or text to keep",
            "type": "string",
            "maxLength": 20000,
            "default": ""
          },
          "ranges": {
            "title": "Unicode ranges (e.g. U+0020-007E, U+00E9)",
            "type": "string",
            "maxLength": 2000,
            "default": ""
          },
          "presets": {
            "title": "Common character blocks",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "basic-latin",
                "latin-1",
                "latin-ext-a",
                "punctuation",
                "greek",
                "cyrillic",
                "digits"
              ]
            },
            "default": []
          },
          "format": {
            "title": "Output format",
            "type": "string",
            "enum": [
              "woff",
              "ttf"
            ],
            "default": "woff"
          },
          "kerning": {
            "title": "Keep kerning (as a kern table)",
            "type": "boolean",
            "default": true
          },
          "axes": {
            "title": "Freeze a variable font at (e.g. wght=700; empty = keep the default instance)",
            "type": "string",
            "maxLength": 300,
            "default": ""
          }
        },
        "required": [
          "font"
        ]
      },
      "LuminaFontSubsetResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "family",
              "filename",
              "format",
              "bytes",
              "sourceBytes",
              "savedPercent",
              "sha256",
              "glyphs",
              "sourceGlyphs",
              "requested",
              "covered",
              "missing",
              "kerningPairs",
              "kernSource",
              "instance",
              "droppedTables",
              "unicodeRange",
              "reservedName",
              "warnings",
              "artifactUrl",
              "css"
            ],
            "properties": {
              "family": {
                "type": "string"
              },
              "filename": {
                "type": "string"
              },
              "format": {
                "enum": [
                  "woff",
                  "ttf"
                ]
              },
              "bytes": {
                "type": "integer"
              },
              "sourceBytes": {
                "type": "integer"
              },
              "savedPercent": {
                "type": "number"
              },
              "sha256": {
                "type": "string"
              },
              "glyphs": {
                "type": "integer"
              },
              "sourceGlyphs": {
                "type": "integer"
              },
              "requested": {
                "type": "integer"
              },
              "covered": {
                "type": "integer"
              },
              "missing": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "kerningPairs": {
                "type": "integer"
              },
              "kernSource": {
                "enum": [
                  "gpos",
                  "list",
                  "none"
                ]
              },
              "instance": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": {
                  "type": "number"
                }
              },
              "droppedTables": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "unicodeRange": {
                "type": "string"
              },
              "reservedName": {
                "type": "boolean"
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "artifactUrl": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "css": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}