Skip to content

Commit 60c1f62

Browse files
authored
docs(pptx): surface font substitution and say how to avoid it (#452)
* docs(pptx): surface font substitution and say how to avoid it The backend already warns once per family whenever a deck will reference a font instead of carrying it, and each message names what to do. The examples silenced com.demcha.compose outright, so a reader running a deck example saw none of it — the warnings fired into a muted logger. Raising that logger to WARN costs 14 lines across all 87 generated documents, because every warning is already deduplicated per family or per kind. The module page said substitution was logged but not what it costs or how to avoid it. It now states the three cases a font can land in — a standard-14 name travelling as a metric-compatible viewer font with identical widths and different letterforms, an embeddable binary family travelling as itself, and a name-only family that depends on the viewer having it installed — so a reader who needs glyph identity knows to use a bundled family or register their own. * docs(changelog): state the dedup rule instead of a line count The entry pinned two numbers and both were wrong: the suite writes 93 documents, not 87, and emits 18 warning lines, not 14 — the earlier tally matched on the render./docx. prefixes and missed the glyph.missing lines and one node-drop warning. Correcting them would only reset the clock, since adding one example moves both. The entry now describes the property that keeps the output short, and states the two dedup scopes accurately: a font substitution is deduplicated per family per render, a capability note per kind for the process.
1 parent 83ca902 commit 60c1f62

3 files changed

Lines changed: 49 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ shade), while others lose something the format cannot carry — see Known limita
182182

183183
### Documentation and examples
184184

185+
- **The examples now show the engine's warnings.** Their logging config silenced
186+
`com.demcha.compose` entirely, so a reader running a deck example never saw which
187+
fonts the viewer would substitute or which capability had degraded — the warnings
188+
fired into a muted logger. The output stays short because every warning is
189+
deduplicated before it is logged: a font substitution once per family per render, a
190+
capability note once per kind for the process.
191+
- **`render-pptx` documents how to get a glyph-identical deck.** Geometry matches the
192+
PDF by construction, but glyphs are drawn by the viewer; the page now states the
193+
three cases — a standard-14 name travels as a metric-compatible viewer font with
194+
identical widths and different letterforms, an embeddable binary family travels as
195+
itself, and a name-only family depends on the viewer having it installed.
185196
- **`TwinOutputExample`** — a single 16:9 page written once and emitted twice from the
186197
same session. The README gains a dual-output section showing the PDF render beside
187198
PowerPoint's own export of the generated slide, plus the deck open in PowerPoint with

examples/src/main/resources/logback.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<logger name="org.apache.pdfbox" level="WARN"/>
99
<logger name="org.apache.fontbox" level="WARN"/>
10-
<logger name="com.demcha.compose" level="OFF"/>
10+
<logger name="com.demcha.compose" level="WARN"/>
1111

1212
<root level="ERROR">
1313
<appender-ref ref="CONSOLE"/>

render-pptx/README.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ the slide simply takes that size.
6767
[#413](https://github.com/DemchaAV/GraphCompose/issues/413).
6868
- **Glyphs are drawn by the viewer, not by GraphCompose.** Shape frames, line
6969
boxes and positions are fixed by the layout graph, but the actual glyph
70-
rasterisation depends on the fonts installed on the viewing machine. Fonts are
71-
embedded where the font's licensing bits allow it; otherwise PowerPoint
72-
substitutes, and the backend logs which family was substituted.
70+
rasterisation depends on the fonts available to the viewing machine. See
71+
[Getting glyph-identical decks](#getting-glyph-identical-decks) for what that
72+
costs and how to avoid it.
7373
- **Byte-for-byte reproducibility is opt-in.** The default path streams the deck
7474
with live timestamps. Pass
7575
`PptxFixedLayoutBackend.builder().deterministic(instant)` to pin the core
@@ -93,6 +93,40 @@ the slide simply takes that size.
9393
- **Multi-section documents** render into one deck; the sections must share a
9494
slide size.
9595

96+
## Getting glyph-identical decks
97+
98+
Geometry is identical to the PDF by construction — both backends consume the
99+
same resolved layout, and every line is pre-wrapped and absolutely placed, so
100+
PowerPoint can never reflow content. **Glyphs are the one thing PPTX cannot
101+
guarantee on its own**, because a deck names its fonts and the viewer draws
102+
them. Which of the three cases you land in depends on how the font reached the
103+
document, and the backend warns once per family for the two that are not exact:
104+
105+
| How the font reached the document | In the deck | Glyphs |
106+
|---|---|---|
107+
| A standard-14 name (`FontName.HELVETICA`, `TIMES`, `COURIER`) | referenced as its metric-compatible viewer font — Arial, Times New Roman, Courier New | **widths identical, letterforms differ** |
108+
| A binary family whose licensing bits allow embedding | embedded in the deck | **identical everywhere** |
109+
| A family registered without binary sources, or one whose bits refuse embedding | referenced by name only | identical **if** the viewer has that font installed, otherwise the viewer substitutes |
110+
111+
So for a deck that matches the PDF glyph for glyph, use a real font program
112+
rather than a PDF built-in — either a bundled family from `graph-compose-fonts`,
113+
or your own file registered on the session:
114+
115+
```java
116+
try (DocumentSession document = GraphCompose.document()
117+
.pageSize(DocumentPageSize.SLIDE_16_9)
118+
.create()) {
119+
document.registerFontFamily(myFamily); // embedded when licensing allows
120+
compose(document);
121+
document.buildPptx(Path.of("deck.pptx"));
122+
}
123+
```
124+
125+
Every substitution is announced at `WARN` under the key
126+
`render.pptx.font.substitution`, once per family per render, naming the font the
127+
document asked for and what the deck will reference instead. Embedded families
128+
log nothing — silence means the deck carries its own glyphs.
129+
96130
## When to depend on it
97131

98132
Opt-in, at compile scope, when you want PowerPoint output. It is not pulled in

0 commit comments

Comments
 (0)