Spire.PDF 12.7.8
Win 11
NET 4.8
# Bug Report: Spire.PDF PDF to DOCX conversion embeds fonts with mismatched obfuscation metadata, causing crashes in downstream OOXML readers
## Summary
When converting a PDF to DOCX using `PdfDocument.ConvertOptions.SetPdfToDocOptions(true, ...)` (PS conversion engine enabled), the resulting `.docx` embeds subset TrueType fonts under `word/fonts/*.odttf`. These font parts are declared in `[Content_Types].xml` with content type `application/vnd.openxmlformats-officedocument.obfuscatedFont`, and `word/fontTable.xml` supplies a `w:fontKey` GUID for each one (used per the OOXML spec to de-obfuscate the font data on read).
However, the embedded font **data itself is not actually obfuscated** — each `.odttf` part begins with the plain, valid TrueType `sfnt` signature (`00 01 00 00`) rather than an XOR-obfuscated header. This is an internal inconsistency in the produced file: it declares the fonts as obfuscated and supplies keys to de-obfuscate them, but the bytes are already plain.
Any OOXML consumer that correctly follows the spec will XOR the (already valid) font header using the supplied `w:fontKey`, corrupting a well-formed font into garbage. Passing that corrupted font data into font-parsing/rasterization code is a common trigger for native buffer-overrun bugs. We can reproduce a hard crash (native stack buffer overrun, `STATUS_STACK_BUFFER_OVERRUN` / `0xc0000409`, faulting in `ucrtbase.dll`) in LibreOffice Writer (`soffice.bin`) when it opens a DOCX produced this way.
## Environment
- Spire.PDF for .NET version: `<FILL IN — e.g. 12.8.0>`
- License type: `<FILL IN — Free / Commercial / Trial>`
- .NET runtime: `<FILL IN — e.g. .NET 8.0>`
- OS: `<FILL IN — e.g. Windows 11 x64>`
- Downstream reader used to confirm the crash: LibreOffice Writer (`soffice.bin`) 26.2.0.0
## Steps to Reproduce
```csharp
using Spire.Pdf;
PdfDocument f = new PdfDocument();
f.LoadFromFile(inputFile); // PDF containing embedded/subset fonts + graphics
f.ConvertOptions.SetPdfToDocOptions(true, false); // usePsMode = true
f.SaveToFile(outputFile, FileFormat.DOCX);
f.Close();
```
1. Convert a PDF containing embedded fonts (in our repro: Corbel, Corbel Bold, Arial, Brush Script MT Italic) and graphics to DOCX using the above call.
2. Inspect the resulting `.docx` as a zip archive.
3. Note `[Content_Types].xml` declares `word/fonts/font1.odttf` (etc.) as `ContentType="application/vnd.openxmlformats-officedocument.obfuscatedFont"`.
4. Note `word/fontTable.xml` supplies a `w:fontKey` GUID for each embedded font, used for de-obfuscation on read (mapping below).
5. Inspect the raw bytes of each `word/fonts/*.odttf` part.
## Technical Evidence
**Font name → fontKey GUID mapping (from `word/fontTable.xml`):**
| Font name | fontKey (rId → GUID) | Part |
|-------------------------|-------------------------------------------|--------------------------|
| Corbel | `61a6d648-0000-0000-0000-000000000000` | `word/fonts/font1.odttf` |
| Brush Script MT Italic | `d96a931c-0000-0000-0000-000000000000` | `word/fonts/font2.odttf` |
| Corbel Bold | `2e04420f-0000-0000-0000-000000000000` | `word/fonts/font3.odttf` |
| Arial | `61790736-0000-0000-0000-000000000000` | `word/fonts/font4.odttf` |
**First 4 bytes of each embedded font part (should be XOR-obfuscated per spec, but are not):**
```
word/fonts/font1.odttf: 00 01 00 00 ← valid, un-obfuscated TrueType sfnt signature
word/fonts/font2.odttf: 00 01 00 00 ← valid, un-obfuscated TrueType sfnt signature
word/fonts/font3.odttf: 00 01 00 00 ← valid, un-obfuscated TrueType sfnt signature
word/fonts/font4.odttf: 00 01 00 00 ← valid, un-obfuscated TrueType sfnt signature
```
`00 01 00 00` is the plain TrueType version number — i.e., this is what a *correctly de-obfuscated* font header should look like, but it's what's stored *before* any de-obfuscation is applied. Since the part is tagged `obfuscatedFont` and a `fontKey` is supplied, a spec-compliant reader will XOR these already-valid header bytes, producing corrupted font data.
**`[Content_Types].xml` (relevant entry):**
```xml
<Default Extension="odttf" ContentType="application/vnd.openxmlformats-officedocument.obfuscatedFont" />
```
**`word/fontTable.xml` (excerpt, one font):**
```xml
<w:font w:name="Corbel">
<w:embedRegular r:id="rId1" w:fontKey="{61a6d648-0000-0000-0000-000000000000}" />
</w:font>
```
## Expected Behavior
Either:
- (a) The embedded font data should actually be obfuscated (XORed) to match the declared `obfuscatedFont` content type and the supplied `w:fontKey`, per the OOXML spec (ECMA-376), **or**
- (b) If font obfuscation is not actually applied, the parts should be declared with a plain (non-obfuscated) content type/extension and no `w:fontKey`/`embedRegular` de-obfuscation instruction should be written, so downstream readers don't attempt to de-obfuscate valid data.
## Actual Behavior
The output DOCX declares fonts as obfuscated (content type + `fontKey`) while storing plain, un-obfuscated font bytes. This mismatch causes spec-compliant OOXML readers to corrupt the font data on open, which in turn caused a native crash (stack buffer overrun) in our downstream reader (LibreOffice `soffice.bin`).
## Impact
Files produced by this conversion path can crash third-party applications that open them, with no error surfaced back to the producing application (Spire.PDF reports conversion success; the failure only appears later, in the consuming reader, as a native process crash).
## Workaround in use
We are currently stripping `word/fonts/*`, the corresponding relationships, and the `w:embedRegular`/`w:fontKey` references from `word/fontTable.xml` post-conversion to avoid the crash, at the cost of losing embedded font fidelity (falls back to substituted system fonts).