{"slug": "what-the-fstype-field-in-a-font-means-and-how-to-read-it-with-fonttools", "title": "What the fsType field in a font means, and how to read it with fontTools", "summary": "A developer documented how to read the fsType embedding-permission field in the OS/2 table of TrueType and OpenType fonts using fontTools, showing that the low four bits hold a single usage value (0, 2, 4 or 8) while bits 8 and 9 are separate no-subsetting and bitmap-only flags. Scanning fonts installed on a Mac turned up only values 0, 4 and 8, and extracting fonts from a PDF with PyMuPDF revealed that Type 1 fonts such as PlantinMTStd have no OS/2 table, so the field reads as None rather than zero.", "body_md": "Font tooling keeps mentioning `fsType`, and it is easy to treat it as a magic number where 0 is fine and anything else is a problem. The spec says something more specific, and a few things in it are easy to misread. So I read the field on fonts installed on my Mac, then on the fonts inside a PDF, and wrote down what each reading means.\n\n`fsType` lives in the OS/2 table of a TrueType or OpenType font. The OpenType spec (the OS/2 page on learn.microsoft.com, read on 2026-10-28) splits the 16 bits into parts. Bits 0 to 3 hold the usage permission. Bit 8 means the font must not be subsetted. Bit 9 means only bitmap embedding is allowed. The rest are reserved.\n\nThe permission part is not a set of independent switches. It holds one value, and the valid ones are 0, 2, 4 and 8. Zero is installable embedding, 2 is restricted license embedding, 4 is preview and print embedding, and 8 is editable embedding. Value 2 is the most restrictive of the four.\n\nThis is the part I had to slow down for. The usage value 8 is what you get when bit 3 is set, so in binary it is `0000000000001000`. Bit 8 is a different thing entirely: it is the value 256, `0000000100000000`, and it says \"no subsetting\". Bit 9 is 512, \"bitmap only\". If you only look at the decimal number and never at the binary form, 8, 256 and 512 all feel like the same kind of flag, and they are not.\n\nThat is why the snippet below prints the binary string and splits the value with masks instead of comparing against a list of numbers. `v & 0xF` isolates the permission part, and `v & 0x100` and `v & 0x200` test the two flags.\n\nThe script takes font paths and prints one line per font. It handles `.ttc` collections by opening the first font inside, so that a collection does not need special handling. The names are placeholders for my own files: A is Noto Serif SC, B is Arial, C is Palatino, D is Helvetica.\n\n``` python\npython3 - 'FONT-A.otf' 'FONT-B.ttf' 'FONT-C.ttc' 'FONT-D.ttc' <<'P'\nimport sys\nfrom fontTools.ttLib import TTFont, TTCollection\nfor p in sys.argv[1:]:\n    f = TTCollection(p, lazy=True).fonts[0] if p.endswith('.ttc') else TTFont(p, lazy=True)\n    v = f['OS/2'].fsType\n    print(f\"{f['name'].getBestFullName():<18} fsType={v:<4} {v:016b}  usage={v & 0xF} no_subset={bool(v & 0x100)} bitmap_only={bool(v & 0x200)}\")\nP\n\nNoto Serif SC      fsType=0    0000000000000000  usage=0 no_subset=False bitmap_only=False\nArial              fsType=8    0000000000001000  usage=8 no_subset=False bitmap_only=False\nPalatino           fsType=4    0000000000000100  usage=4 no_subset=False bitmap_only=False\nHelvetica          fsType=0    0000000000000000  usage=0 no_subset=False bitmap_only=False\n```\n\nAcross the fonts I scanned on this one machine, the values were only ever 0, 4 or 8. Pacifico, Liberation Sans, Noto Sans, Inter, Helvetica, Times and Menlo gave 0. Palatino, Avenir Book and PingFang SC gave 4. Arial, Georgia and Impact gave 8. I found no installed font with 2, 256 or 512. Anything I say below about those three values comes from copies where I changed the field myself, not from fonts I found in the wild.\n\nAsk the same question of a PDF and there is one more trap. A PDF usually carries a subset of each font, and PyMuPDF can pull those bytes back out with `extract_font`, after which fontTools reads the OS/2 table as above. My sample was the first four pages of the \"Evaluating Wikipedia\" brochure from Wiki Education Foundation and others, licensed CC BY-SA 3.0. Its embedded fonts were NewsGothicBT in three weights with fsType 4, ArialMT with 8, and PlantinMTStd, which printed `None`.\n\nThat `None` is not a zero and it does not mean \"no restriction\". PlantinMTStd is a Type 1 font, and Type 1 fonts have no OS/2 table at all, so there is no field to read. If the script treats `None` as 0, it claims a permission the file never stated. In my version I only call fontTools when the extracted font is a TrueType or OpenType file, and everything else stays `None` on purpose. A Type 1 font in a PDF therefore tells you nothing about license through this route, so any restriction on it would have to be checked somewhere else, for example in the foundry's terms for that typeface.\n\nThe field is a statement of the font's license. What software does with it varies, and I only observed two cases. Chromium 149 printing to PDF, using my modified copies of Liberation Sans, with each value: for 2 and 512 it did not embed the font and wrote the glyphs as Type 3 outlines. For 256 it embedded the whole font without subsetting, and the PDF grew from 85,024 to 502,702 bytes. For 4 and 8 it embedded a normal subset. That is one machine and two TrueType fonts (Liberation Sans and Pacifico), and I did not read Chromium's source to see why.\n\nI also ran the PDFs through ImgIng ([https://imging.ai/](https://imging.ai/)), a Beta tool that runs in a desktop browser and processes files locally. Its output turned TrueType subsets into inlined woff fonts, drew Type 3 glyphs as paths, and fell back to a system font for the Type 1 face. How it makes those choices internally I don't know, and I would not claim it reads fsType to do it.", "url": "https://wpnews.pro/news/what-the-fstype-field-in-a-font-means-and-how-to-read-it-with-fonttools", "canonical_source": "https://dev.to/token111/what-the-fstype-field-in-a-font-means-and-how-to-read-it-with-fonttools-3063", "published_at": "2026-10-29 08:07:31+00:00", "updated_at": "2026-10-29 08:17:27.158164+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["fontTools", "PyMuPDF", "Noto Serif SC", "Arial", "Palatino", "Helvetica", "Wiki Education Foundation"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/what-the-fstype-field-in-a-font-means-and-how-to-read-it-with-fonttools", "markdown": "https://wpnews.pro/news/what-the-fstype-field-in-a-font-means-and-how-to-read-it-with-fonttools.md", "text": "https://wpnews.pro/news/what-the-fstype-field-in-a-font-means-and-how-to-read-it-with-fonttools.txt", "jsonld": "https://wpnews.pro/news/what-the-fstype-field-in-a-font-means-and-how-to-read-it-with-fonttools.jsonld"}}