Repository navigation
Decide whether a standard-14 face constant should resolve to the face it names #451
Description
Activity
Working on this in #479. Two corrections to the inventory above, both measured by rendering the whole example catalogue and comparing every page.
Four of the five library sites are already correct.
HeadingBarStyle:21,TimelineBuilder:69,TimelineMarker:74andModernProfessional:134each set.decoration(BOLD)beside the alias, so they render bold today. The alias is redundant there, not broken.ChartDefaults:101is the one that sets no decoration, and it is the only place in the library where the form produces the wrong output.So the blast radius is narrower than "every chart axis label, heading bar, timeline marker and those preset headings": fixing
ChartDefaultschanges three documents — the engine deck (page 4), the feature catalogue (page 3) and the chart showcase (page 5). The axis labels are unaffected; they nameHELVETICAalready.A second source looked bigger and turned out to be a dead declaration. Four
Typographyfactories (modernProfessional,editorialBlue,invoiceModern,proposalModern) declare a face constant asheadlineFont, which reads like a systematic version of the same bug. It is not: those presets style their headlines themselves and never reachBrandTheme.headlineStyle(). Patching that style toBOLDchanges nine documents —classic-serif,minimal-underlined,boxed-sections,centered-headline,blue-banner— none of them the four themes, and all of them intentionally regular. Rewriting the four tokens to their base family moves nothing.#479 takes neither Option 1 nor Option 3. It fixes the one real site, rewrites the sixteen redundant ones to name their family (render-neutral, verified by the same sweep), and adds the Option 2 signal: one log line per distinct face constant
FontLibraryrewrites — per name, not per lookup.Option 1 stays open and is now cheaper to decide.
FontFaceResolutionTestpins today's answer against the resolvedPDFont, includingHELVETICA_BOLDwithdecoration(ITALIC)→Helvetica-Oblique. Making the alias a real face becomes an edit to a named assertion rather than a silent change, and the precedence rule that case needs can be settled then. The cost is unchanged: 97 example sites use the form without a decoration.- changed the title
[-]fontName(HELVETICA_BOLD) selects the family, not the weight — bold text renders regular[/-][+]Decide whether a standard-14 face constant should resolve to the face it names[/+]on Aug 7, 2026 - addedenhancementNew feature or requestNew feature or requestand removedbugSomething isn't workingSomething isn't working
on Aug 7, 2026 Not touched by the RTL/fonts work that just landed (#535/#534/#536/#538/#539/#540 —
FontLibrary.javahas zero commits from it, theFONT_ALIASESrewrite and the warning are
exactly as this issue describes them). But that work supplies an argument for the open
decision, so noting it here rather than leaving it to be rediscovered.The custom-font path already implements the fallback this issue is weighing, and it is
now load-bearing.FontFamilyDefinition.Builder.build():FontBinarySource resolvedBold = bold != null ? bold : regular; FontBinarySource resolvedItalic = italic != null ? italic : regular; FontBinarySource resolvedBoldItalic = boldItalic != null ? boldItalic : (bold != null ? bold : (italic != null ? italic : regular));
Requested face if registered, nearest otherwise — with a ladder that keeps the weight when
the slant is unavailable. The families added in this cycle lean on it for real: David Libre
ships no italic, Noto Sans Georgian and Armenian are single variable-font instances, and
Gothic A1 has a drawn bold but no italic.BundledFamiliesTestpins each collapse.So the library currently answers the same question two ways: a custom family resolves
requested, else nearest, while a standard-14 face constant is rewritten to its base
before any lookup, which is why the bold program thatDefaultFontsregisters via
FontFamilyDefinition.standard14("HELVETICA", "HELVETICA_BOLD", …)exists but is
unreachable. Option 1 would make the two paths agree rather than introduce a new behaviour.It also settles the precedence question this issue flags, in the direction the custom path
already went: there, the explicitly registered face wins and the fallback only fires on
absence. Applied toHELVETICA_BOLD+decoration(ITALIC), the consistent reading is that
the decoration is the face selector and the constant contributes nothing once the family is
known — i.e. today'sHelvetica-Oblique, whichFontFaceResolutionTestalready pins. That
keeps one rule for both paths instead of a special case for the standard 14.Still open, still a decision — just with one fewer unknown.
Where this stands
fontName(FontName.HELVETICA_BOLD)selects the Helvetica family. The face comesfrom the style's
decoration, so a style that names a face and sets no decorationrenders regular — and is measured with regular metrics. The same holds for all nine
standard-14 style variants (
TIMES_BOLD,COURIER_OBLIQUE,HELVETICA_BOLD_OBLIQUE, …).Two pieces, each individually reasonable:
FontLibrary.resolveBaseFontmaps every standard-14 face constant onto its regular base through
FONT_ALIASES—before any lookup, so it is a rewrite rather than a fallback.
Font.fontType(TextDecoration)then picks the real face from the span's decoration.
So the design is name selects the family, decoration selects the face, and
.fontName(HELVETICA_BOLD)expresses the family twice and the face never. The workingform is:
What has already landed
#479 and #486 took Option 2 — make the no-op visible — and cleared the call sites.
Nothing in this repository renders the wrong weight any more.
ChartDefaults.DONUT_CENTER_TEXT_STYLEnamesHELVETICAand setsdecoration(BOLD). The four other sites in the original inventory —HeadingBarStyle,TimelineBuilder,TimelineMarker,ModernProfessional— eachalready set the decoration beside the alias and were never wrong; the alias was
redundant there, not broken.
FontLibrary.warnOnceAboutFaceAliaslogs one line perdistinct face constant the library rewrites — per name, not per lookup, so a document
that uses the form a thousand times says so once.
docs/font-coverage.md, section "The name picks thefamily, the decoration picks the face", and the
fontName@paramonDocumentTextStyle.FontFaceResolutionTestasserts the resolved
PDFontfor the family + decoration matrix, for a face constantwith no decoration, and for
HELVETICA_BOLDwithdecoration(ITALIC)→Helvetica-Oblique.examples. A grepfor the face constants across every non-test main source now returns only the
FONT_ALIASEStable, theDefaultFontsfamily declarations and Javadoc — zero callsites.
What is still open
Whether the alias should become a real fallback: look the requested variant up
first, and fall back to the base family only when nothing is registered. The
standard-14 families already declare all four faces (
DefaultFontspasses"HELVETICA_BOLD"and friends toFontFamilyDefinition.standard14), so the boldprogram is available — it is only unreachable.
Deciding it needs a precedence rule for the case the pinning test already names: a
style that names
HELVETICA_BOLDand setsdecoration(ITALIC). Today the decorationwins and the result is
Helvetica-Oblique. The candidates are decoration-wins(unchanged), name-wins, or combine into bold-oblique. Whichever is chosen, it lands as
an edit to a named assertion rather than as a silent change of behaviour.
The cost argument has changed since this was filed. The original reason to defer was
blast radius: every layout snapshot and pixel baseline covering an alias site would
move, and 97 example sites used the form without a decoration. Those sites no longer
exist, so making the alias a real face now moves nothing in this repository. The change
is visible only to consumer code that writes the form — which is exactly where it would
read as a fix rather than a regression. It is still a behavioural change to published
API and wants a
@sincenote and a CHANGELOG entry, but it no longer carries asnapshot-churn bill.
The remaining alternative is to leave the rewrite as the rule and treat the warning plus
the two documented statements as the answer. That is a decision to record here and
close on, not a silent status quo.
Also update
docs/font-coverage.mdand theDocumentTextStyle@param, both of which currentlystate the rewrite as the rule, if the rule changes.
docs/architecture/backend-capability-matrix.mdonly if resolution ends up differingper backend — the PPTX backend takes the face from the decoration since #450, so the two
agree today.