Description
Since the August 2026 servicing update, GlyphTypeface.ComputeSubset throws FileFormatException for several stock, unmodified Windows system fonts. This breaks all XPS export, XPS-based PDF export and printing for documents that use them.
The fonts are structurally valid — every table offset is in bounds and all checksums are correct. They render perfectly on screen. Only subsetting fails, which makes the failure easy to misdiagnose, since the exception message names the font file and implies it is corrupt.
The trigger is the legacy kern table: in these fonts the subtable's uint16 length field has wrapped, and the new bounds validation rejects the entire font rather than skipping the offending optional table.
Affected: Calibri, Cambria, Constantia and Corbel — 15 faces in total. Calibri is a Microsoft default font, so a stock Windows install plus FontFamily="Calibri" is sufficient to hit this.
Reproduction Steps
Console app, <UseWPF>true</UseWPF>, x64, on Windows with stock Calibri installed:
using System.Reflection;
using System.Windows.Media;
var m = typeof(GlyphTypeface).GetMethod("ComputeSubset",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var gt = new GlyphTypeface(new Uri("file:///C:/Windows/Fonts/calibrib.ttf"));
var glyphs = new List<ushort>();
foreach (var c in "Hello world 0123456789")
if (gt.CharacterToGlyphMap.TryGetValue(c, out var g) && !glyphs.Contains(g))
glyphs.Add(g);
var bytes = (byte[])m.Invoke(gt, new object[] { glyphs }); // throws
Swap calibrib.ttf for calibril.ttf (Calibri Light) and it succeeds — same machine, same process, same code.
The public path reaches the same code: build a FlowDocument with FontFamily="Calibri", then XpsDocument.CreateXpsDocumentWriter(doc).Write(paginator).
Expected behavior
Stock Windows system fonts subset successfully, producing a valid XPS/PDF document, as they did before the update.
Actual behavior
System.IO.FileFormatException: 'file:///C:/WINDOWS/FONTS/CALIBRIB.TTF' file does not conform to the expected file format specification.
No document is produced — the whole serialization call aborts, with no partial output and no font fallback.
at MS.Internal.TrueTypeSubsetter.ComputeSubset(Void* fontData, Int32 fileSize, Uri sourceUri, Int32 directoryOffset, UInt16[] glyphArray)
at MS.Internal.FontFace.TrueTypeFontDriver.ComputeFontSubset(ICollection`1 glyphs)
at System.Windows.Media.GlyphTypeface.ComputeSubset(ICollection`1 glyphs)
at System.Windows.Xps.Serialization.FEMCacheItem.SubSetFont(ICollection`1 glyphs, Stream stream)
at System.Windows.Xps.Serialization.FEMCacheItem.Commit()
at System.Windows.Xps.Serialization.XpsFontSubsetter.CommitFontSubsetsSignal(FontSubsetterCommitPolicies signal)
at System.Windows.Xps.Serialization.XpsSerializationManager.ReleaseXmlWriter(Type writerType)
at System.Windows.Xps.Serialization.ReachHierarchySimulator.SimulateEndFixedDocumentSequence(XmlWriter xmlWriter)
at System.Windows.Xps.Serialization.XpsSerializationManager.SaveAsXaml(Object serializedObject)
On .NET 8/9/10 the message wording differs slightly: File 'file:///C:/Windows/Fonts/calibrib.ttf' has an invalid file format.
Regression?
Yes. Verified by A/B on one machine — same font, same test code, swapping only PresentationCore.dll via DEVPATH:
PresentationCore.dll |
Origin |
Result |
| 4.8.9032.0 (built 2024-04-01) |
pre-update, from WinSxS |
OK — 499,120-byte subset |
| 4.8.9344.0 (built 2026-07-08) |
KB5120708, installed 2026-08-11 |
FAIL |
Our production failure occurred 2026-08-12, the day after KB5120708 installed.
The affected fonts have shipped with an identically sized kern table across Calibri 6.24, 6.26 and 6.27 — all three fail now and all worked before — so the fonts did not change. Only the subsetter's tolerance did.
I do not have an unpatched .NET Core runtime installed to bisect that side, but given the shared TtfDelta sources I would expect 8.0.29 / 9.0.18 / 10.0.10 to succeed.
Known Workarounds
None that are both correct and cost-free.
- Strip the
kern table from the font and load the repaired copy from a private font folder. Verified to fix all 15 faces with no visual or metric change. Requires redistributing a modified Microsoft font binary.
- Patch the fonts in
C:\Windows\Fonts in place. Needs takeown/icacls, a FontCache service stop and a reboot, and is undone by any future font-carrying Windows Update, sfc /scannow or DISM repair.
- Use an unaffected font — Calibri Light, Candara, Arial, Georgia and Consolas all subset fine. Changes document appearance.
- Disable subsetting via
XpsSerializationManager.SetFontSubsettingPolicy(FontSubsetterCommitPolicies.None), embedding the whole font instead. Avoids the crash but replaces a ~500 KB subset with ~1.6 MB of font data per document, which was unacceptable for us.
The intended opt-out switch does not work — details under Other information.
Rolling back the update is not a workaround, as it re-opens CVE-2026-62886 / CVE-2026-62897.
Impact
Radius — any WPF app that subsets fonts: XPS export, XPS-based PDF export, and printing. Triggered by 15 stock faces: Calibri, Cambria, Constantia and Corbel (Regular/Bold/Italic/BoldItalic each, except Cambria which has no affected Regular). No third-party or exotic font is needed.
Intensity — total functional failure rather than degradation. Screen rendering is unaffected, so the app looks healthy right up until export.
Reach — every machine that has taken the August 2026 security update. Because it is security servicing, it deploys broadly and automatically and cannot reasonably be declined.
Concrete example — our production system issues official education diplomas. Diploma generation stopped completely on 2026-08-12, mid-season, with no way for affected students to obtain their documents until we ship a workaround.
Configuration
Reproduces on:
- .NET Framework 4.8.1 —
PresentationCore.dll 4.8.9344.0 (NET481REL1LAST_25H2_B), installed via KB5120708 on 2026-08-11
- .NET 8.0.30, 9.0.19, 10.0.11 (
Microsoft.WindowsDesktop.App)
Does not reproduce on: .NET Framework 4.8.1 with PresentationCore.dll 4.8.9032.0.
OS: Windows 11 Pro 25H2, build 10.0.26200.9168
Architecture: x64. The 32-bit PresentationCore.dll contains the same code; I tested x64 only.
Specific to this configuration? No — it depends on the font file, not the machine, OS version or architecture. Font under test: Calibri Bold 6.27, C:\Windows\Fonts\calibrib.ttf, 1,615,616 bytes.
Other information
Where the problem is
src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ — the TTF_SAFE_CHECKS_ENABLED() validation added by #11642 ("Fix potential overflow issues in WPF native code", merged 2026-05-18), which rewrote 13 files in that directory. Most likely ttfacc.cpp, ttftable.cpp or modtable.cpp, where table lengths are read and bounds-checked.
Why these fonts trip it
The legacy kern subtable header stores its own length in a uint16 (max 65,535), while the table directory stores the real length in a uint32. For calibrib.ttf:
| Field |
Value |
kern length from table directory (uint32) |
160,164 |
Subtable header length at kern+6 (uint16) |
29,088 |
160,164 mod 65,536 = 29,088 — the field has wrapped. This is a decades-old limitation of the kern format, and these fonts have always exceeded it. DirectWrite ignores the table entirely and applies kerning from GPOS, so nobody noticed until the subsetter started validating the field.
Measured across 25 fonts, the correlation is perfect — 15/15 fonts whose kern table exceeds 65,535 bytes fail; 10/10 that don't, succeed:
| Font |
kern bytes |
uint16 field |
Subsets? |
| calibri / calibrib / calibrii / calibriz |
149,796–160,254 |
wrapped |
fail |
| cambriab / cambriai / cambriaz |
190,000–218,000 |
wrapped |
fail |
| constan / constanb / constani / constanz |
219,000–232,000 |
wrapped |
fail |
| corbel / corbelb / corbeli / corbelz |
80,000–100,000 |
wrapped |
fail |
| calibril / calibrili |
12,288 / 12,300 |
valid |
ok |
| corbell / corbelli |
~41,000 |
valid |
ok |
| Candara |
48,792 |
valid |
ok |
| arial / times / verdana |
3,000–5,000 |
valid |
ok |
| consola / georgia |
no kern |
— |
ok |
Suggested fix
An inconsistent legacy kern subtable should cause that table to be skipped or truncated, not the whole font to be rejected. It is optional and unused by the WPF/DirectWrite text stack. I verified that stripping only kern restores subsetting for every affected face with no visual or metric change and zero missing characters (including Danish æøåÆØÅ): calibri 520,552 B, calibrib 513,800 B, calibrii 256,064 B, calibriz 255,208 B, corbel 81,332 B, constan 118,996 B, cambriab 241,084 B.
Second bug: the TtfDelta AppContext switch is not wired up
Switch.MS.Internal.TtfDelta.DisableDirectWriteForwarderBoundsCheckProtection is present as a UTF-16 literal in the shipped binary, and MS.Internal.CoreAppContextSwitches.DisableDirectWriteForwarderBoundsCheckProtection honours it — but enabling it does not change behaviour. Tested on .NET Framework 4.8.9344.0 and .NET 8.0.30:
| Configuration |
AppContext.TryGetSwitch |
CoreAppContextSwitches |
Result |
AppContext.SetSwitch(name, true) |
True |
— |
FAIL |
app.config AppContextSwitchOverrides |
True |
— |
FAIL |
app.config + MediaSystem booted via RenderTargetBitmap |
True |
True |
FAIL |
| All three bounds-check switches enabled |
True |
True |
FAIL |
Reflecting over PresentationCore shows managed setters exist for the sibling mitigations but not for this one:
MS.Win32.Penimc.UnsafeNativeMethods.SetDisablePenImcBoundsCheckProtection(Boolean) — present
MS.Win32.PresentationCore.UnsafeNativeMethods+MilCoreApi.WpfGfx_SetDisableBoundsCheckProtection(Boolean) — present
- TtfDelta equivalent — no managed method, though the native symbol
SetDisableDirectWriteForwarderBoundsCheckProtection and the global g_fDWFBoundsCheckEnabled are both present in the binary.
In #11642 the .NET Core truetype.cpp reads the switch itself via System::AppContext::TryGetSwitch in a static-once block at the top of ComputeSubset; that path appears not to take effect in the shipped builds. Please wire this up — it is the documented escape hatch, and currently the only mitigation that would not require modifying Microsoft's font binaries.
Related
Description
Since the August 2026 servicing update,
GlyphTypeface.ComputeSubsetthrowsFileFormatExceptionfor several stock, unmodified Windows system fonts. This breaks all XPS export, XPS-based PDF export and printing for documents that use them.The fonts are structurally valid — every table offset is in bounds and all checksums are correct. They render perfectly on screen. Only subsetting fails, which makes the failure easy to misdiagnose, since the exception message names the font file and implies it is corrupt.
The trigger is the legacy
kerntable: in these fonts the subtable'suint16length field has wrapped, and the new bounds validation rejects the entire font rather than skipping the offending optional table.Affected: Calibri, Cambria, Constantia and Corbel — 15 faces in total. Calibri is a Microsoft default font, so a stock Windows install plus
FontFamily="Calibri"is sufficient to hit this.Reproduction Steps
Console app,
<UseWPF>true</UseWPF>, x64, on Windows with stock Calibri installed:Swap
calibrib.ttfforcalibril.ttf(Calibri Light) and it succeeds — same machine, same process, same code.The public path reaches the same code: build a
FlowDocumentwithFontFamily="Calibri", thenXpsDocument.CreateXpsDocumentWriter(doc).Write(paginator).Expected behavior
Stock Windows system fonts subset successfully, producing a valid XPS/PDF document, as they did before the update.
Actual behavior
System.IO.FileFormatException: 'file:///C:/WINDOWS/FONTS/CALIBRIB.TTF' file does not conform to the expected file format specification.No document is produced — the whole serialization call aborts, with no partial output and no font fallback.
On .NET 8/9/10 the message wording differs slightly:
File 'file:///C:/Windows/Fonts/calibrib.ttf' has an invalid file format.Regression?
Yes. Verified by A/B on one machine — same font, same test code, swapping only
PresentationCore.dllviaDEVPATH:PresentationCore.dllOur production failure occurred 2026-08-12, the day after KB5120708 installed.
The affected fonts have shipped with an identically sized
kerntable across Calibri 6.24, 6.26 and 6.27 — all three fail now and all worked before — so the fonts did not change. Only the subsetter's tolerance did.I do not have an unpatched .NET Core runtime installed to bisect that side, but given the shared
TtfDeltasources I would expect 8.0.29 / 9.0.18 / 10.0.10 to succeed.Known Workarounds
None that are both correct and cost-free.
kerntable from the font and load the repaired copy from a private font folder. Verified to fix all 15 faces with no visual or metric change. Requires redistributing a modified Microsoft font binary.C:\Windows\Fontsin place. Needstakeown/icacls, a FontCache service stop and a reboot, and is undone by any future font-carrying Windows Update,sfc /scannowor DISM repair.XpsSerializationManager.SetFontSubsettingPolicy(FontSubsetterCommitPolicies.None), embedding the whole font instead. Avoids the crash but replaces a ~500 KB subset with ~1.6 MB of font data per document, which was unacceptable for us.The intended opt-out switch does not work — details under Other information.
Rolling back the update is not a workaround, as it re-opens CVE-2026-62886 / CVE-2026-62897.
Impact
Radius — any WPF app that subsets fonts: XPS export, XPS-based PDF export, and printing. Triggered by 15 stock faces: Calibri, Cambria, Constantia and Corbel (Regular/Bold/Italic/BoldItalic each, except Cambria which has no affected Regular). No third-party or exotic font is needed.
Intensity — total functional failure rather than degradation. Screen rendering is unaffected, so the app looks healthy right up until export.
Reach — every machine that has taken the August 2026 security update. Because it is security servicing, it deploys broadly and automatically and cannot reasonably be declined.
Concrete example — our production system issues official education diplomas. Diploma generation stopped completely on 2026-08-12, mid-season, with no way for affected students to obtain their documents until we ship a workaround.
Configuration
Reproduces on:
PresentationCore.dll4.8.9344.0 (NET481REL1LAST_25H2_B), installed via KB5120708 on 2026-08-11Microsoft.WindowsDesktop.App)Does not reproduce on: .NET Framework 4.8.1 with
PresentationCore.dll4.8.9032.0.OS: Windows 11 Pro 25H2, build 10.0.26200.9168
Architecture: x64. The 32-bit
PresentationCore.dllcontains the same code; I tested x64 only.Specific to this configuration? No — it depends on the font file, not the machine, OS version or architecture. Font under test: Calibri Bold 6.27,
C:\Windows\Fonts\calibrib.ttf, 1,615,616 bytes.Other information
Where the problem is
src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/— theTTF_SAFE_CHECKS_ENABLED()validation added by #11642 ("Fix potential overflow issues in WPF native code", merged 2026-05-18), which rewrote 13 files in that directory. Most likelyttfacc.cpp,ttftable.cppormodtable.cpp, where table lengths are read and bounds-checked.Why these fonts trip it
The legacy
kernsubtable header stores its own length in auint16(max 65,535), while the table directory stores the real length in auint32. Forcalibrib.ttf:kernlength from table directory (uint32)lengthatkern+6(uint16)160,164 mod 65,536 = 29,088 — the field has wrapped. This is a decades-old limitation of the
kernformat, and these fonts have always exceeded it. DirectWrite ignores the table entirely and applies kerning fromGPOS, so nobody noticed until the subsetter started validating the field.Measured across 25 fonts, the correlation is perfect — 15/15 fonts whose
kerntable exceeds 65,535 bytes fail; 10/10 that don't, succeed:kernbyteskernSuggested fix
An inconsistent legacy
kernsubtable should cause that table to be skipped or truncated, not the whole font to be rejected. It is optional and unused by the WPF/DirectWrite text stack. I verified that stripping onlykernrestores subsetting for every affected face with no visual or metric change and zero missing characters (including Danish æøåÆØÅ): calibri 520,552 B, calibrib 513,800 B, calibrii 256,064 B, calibriz 255,208 B, corbel 81,332 B, constan 118,996 B, cambriab 241,084 B.Second bug: the TtfDelta AppContext switch is not wired up
Switch.MS.Internal.TtfDelta.DisableDirectWriteForwarderBoundsCheckProtectionis present as a UTF-16 literal in the shipped binary, andMS.Internal.CoreAppContextSwitches.DisableDirectWriteForwarderBoundsCheckProtectionhonours it — but enabling it does not change behaviour. Tested on .NET Framework 4.8.9344.0 and .NET 8.0.30:AppContext.TryGetSwitchCoreAppContextSwitchesAppContext.SetSwitch(name, true)app.configAppContextSwitchOverridesapp.config+MediaSystembooted viaRenderTargetBitmapReflecting over
PresentationCoreshows managed setters exist for the sibling mitigations but not for this one:MS.Win32.Penimc.UnsafeNativeMethods.SetDisablePenImcBoundsCheckProtection(Boolean)— presentMS.Win32.PresentationCore.UnsafeNativeMethods+MilCoreApi.WpfGfx_SetDisableBoundsCheckProtection(Boolean)— presentSetDisableDirectWriteForwarderBoundsCheckProtectionand the globalg_fDWFBoundsCheckEnabledare both present in the binary.In #11642 the .NET Core
truetype.cppreads the switch itself viaSystem::AppContext::TryGetSwitchin a static-once block at the top ofComputeSubset; that path appears not to take effect in the shipped builds. Please wire this up — it is the documented escape hatch, and currently the only mitigation that would not require modifying Microsoft's font binaries.Related