PPTX 嵌入字型:LibreOffice 採用的一種格式,及默默忽略的兩種格式
Embedding a font in a PPTX: the file that LibreOffice used, and the two it silently ignored
PPTX 匯出實驗顯示,LibreOffice 26.8.0.3 只有在 fntdata 使用 Flags 0 的 EOT,並設定 embedTrueTypeFonts="1" 時,才會載入嵌入字型;原始 TTF 或缺少該屬性均被忽略。
I've been writing a PPTX exporter, and I wanted the font to travel with the file. When the person opening it doesn't have the font installed, the text falls back to some default and the line breaks drift.
PPTX can carry fonts. The part is called a fntdata file, and from the spec and a few open source implementations I pieced together that it should be an EOT wrapper (Embedded OpenType) around the TTF. Before writing any real code I ran one slide through four variants to see which one the renderer would honor.
The setup: one chart slide that uses VT323, a font I did not have installed on this computer. I rendered each variant to PDF with LibreOffice 26.8.0.3 and ran pdffonts to see which font actually got drawn.
| What I put in the file | Font in the PDF |
|---|---|
| Nothing | Fallback fonts (FrankRuhlHofshi-Bold, LinuxLibertineG) |
EOT with Flags 0, plus embedTrueTypeFonts="1" on the presentation |
VT323-Regular |
The raw TTF as the fntdata, plus embedTrueTypeFonts="1"
|
Fallback, silently ignored |
EOT with Flags 0, no embedTrueTypeFonts attribute |
Fallback, silently ignored |
Only the second row worked, the one where both pieces were present. In the other two attempts the text fell back to a substitute font, the same outcome as embedding nothing at all.
A few details that cost me time:
- The EOT header is the version 0x00020002 layout from the W3C submission. Flags 0 means no MicroType Express compression and no XOR. I couldn't find a public compressor for MicroType Express, so uncompressed it is.
- LibreOffice's own PPTX export writes the same thing (EOT, Flags 0, TTF appended as is), and it also writes
embedTrueTypeFonts="1". Its import code only reads the embedded font list when that attribute is true, and always treats the data as EOT. That matches what the table shows. - I opened a real fntdata from Apache POI's test data. Its header says Flags 0x5, which is subset plus compressed, so whatever Office writes is not what I'm writing. I can't prove that file came from PowerPoint itself, so I'm only using it as a header reference.
- Variable fonts need care. I pinned the weight to make a static instance, then rewrote the name table so the family name in the font, the family name in the EOT header and the
typefaceattribute all matched.
On size, the font is the expensive part. Pretendard Regular after deflate, which is roughly what the zip does to it:
| Characters kept | Size |
|---|---|
| Whole font | 1,117,566 bytes |
| All 11,172 Hangul syllables plus ASCII and Latin-1 | 931,592 bytes |
| The 2,350 common Hangul syllables plus ASCII and Latin-1 | 233,993 bytes |
| Only the 30 characters used on the slide | 4,619 bytes |
I went with the 2,350 set. On a test over 41 themes with 177 layouts each, the subset lost zero characters out of 64,465 (font, character) pairs. The catch is that if the recipient types a syllable outside those 2,350, only that character falls back to the default font. Microsoft's documentation says the same about subset embedding, that it limits editing.
Here is what I did not check. I don't have PowerPoint on this computer, so everything above is LibreOffice behavior. I don't know whether PowerPoint accepts an uncompressed EOT. Comments in one open source project say a raw TTF makes Windows PowerPoint fall back to Calibri, and the same comments mention PowerPoint rejecting a whole file as corrupt after an XOR obfuscation attempt on a font part. Those are their observations, not mine, so I'm keeping the feature off by default until someone opens the file in PowerPoint on a machine without the font. Keynote and PowerPoint for the web are also untested.
If you've opened an uncompressed (Flags 0) EOT fntdata in Windows or Mac PowerPoint on a machine without the font, did it load the font, or did it quietly fall back?
This post was written with AI assistance and checked by me against my experiment notes.
來源:DEV Community · dev.to