Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Fonts with per-component overrides

Open
#18 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
48/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
python

Research direction

Begin by locating the style defaults, core slide builders, rich-layout text paths, the README style-keys table, and the existing style-defaults key-set test named in the issue. Run that test before changes, then trace the current font-size assignments and rich-layout font handling. Done means the specified family resolution works across components, existing output is unchanged without font keys, tests pass, and the font-availability limitations are documented.

Written by the indexing model from the issue text.

Description

Fonts with per-component overrides

Summary

Today an author can change font sizes but not the typeface — the font family is
either inherited from the blank template (core slides) or hardcoded to Segoe UI
(rich layouts). This enhancement adds a global font_family plus per-component
overrides (title_font_family, body_font_family, …) to the front-matter style:
block, and optional per-component weight/style flags. Builders set each run's font name
alongside the size they already set. It also documents — honestly — the PowerPoint
font-availability limitation (python-pptx cannot embed fonts) and recommends a
safe-fallback strategy. With no font keys set, every existing deck renders identically.

Value & Motivation

Typography is core to brand identity, yet ms-presentations gives authors no control
over the typeface at all
. The style block exposes only sizes — every value is a point
size, a colour, or a number; there is no font-family field. Concretely:

  • Core slides set only the font size, never the typeface name, so they inherit
    whatever font the blank template ships (Calibri).
  • The rich (16:9) layouts hardcode the family to Segoe UI.

The result: a company whose brand font is "Poppins for headings, Inter for body" cannot
express that, and a deck mixes Calibri (core slides) with Segoe UI (rich slides) with no
way to unify them. Authors already tune sizes through style:; the natural, expected
next step is to tune the typeface, globally and per component.

Proposed Solution / Detailed Design

Add font-family resolution to the existing style object with a clean
global → per-component → leave-untouched fallback, and have every slide apply a run's
font name wherever it already applies a font size. Parameterise the rich layouts'
hardcoded Segoe UI through the same style object.

Spec syntax

Nested under the existing front-matter style: block:

---
title: Brand Deck
style:
  font_family: Inter                 # global default for every component
  title_font_family: Poppins         # per-component overrides
  heading_font_family: Poppins
  body_font_family: Inter
  rich_font_family: Inter            # text drawn by the 16:9 rich layouts
  # weight / style (optional)
  title_font_bold: true
  subtitle_font_italic: true
---

Per-component family keys (each falls back to font_family, then to "untouched"):

Key Component(s) Today
font_family global default for all below none
title_font_family title-slide & section-header title Calibri (template)
heading_font_family content / two-column title bar Calibri (template)
subtitle_font_family subtitle text Calibri (template)
body_font_family content bullets Calibri (template)
column_heading_font_family † two-column per-column heading n/a — not currently rendered
column_body_font_family two-column bullets Calibri (template)
name_font_family resource-box name Calibri (template)
url_font_family resource-box URL Calibri (template)
badge_font_family resource-box badge Calibri (template)
rich_font_family all rich-layout text Segoe UI (hardcoded)

† The two-column layout does not currently render a separate per-column heading — the
existing column_heading_font_size key is defined but unused by any builder.
column_heading_font_family is included only for parity with that key and would take
effect if/when such a heading is rendered; it is otherwise a no-op today.

Optional weight/style flags follow the same per-component naming:
*_font_bold (bool), *_font_italic (bool).

Per-component font overrides (e.g. **TitleFont**: Poppins) at the per-slide level are
intentionally out of scope for v1 and tracked in Open Questions.

Behaviour & resolution

For each component, the resolved family is:

  1. the component key (e.g. title_font_family) if non-empty, else
  2. the global font_family if non-empty, else
  3. empty → do not set a font name.

Step 3 is what preserves today's look: when no font keys are set, slides never assign a
name, so core slides keep the template typeface and rich layouts keep Segoe UI via the
existing literal fallback. Resolution happens once when the style is built, yielding a
resolved family per component (a string, possibly empty).

Weight/style flags resolve the same way and map onto the run's bold/italic, which the
rich layouts already apply per run.

Implementation outline

Described by area of change rather than by source location, since exact files and
functions are an implementation-PR concern.

Area Change
Style defaults Add the *_font_family keys (default empty) and a global font_family; resolve each component's family with the global → component → empty fallback. Add *_font_bold / *_font_italic if weight/style is in scope.
Core slide types At each point that sets a font size, also set the font name when the resolved family for that component is non-empty. A single small helper keeps that conditional in one place; the line-break text helper gains a family parameter.
Rich layouts Replace the hardcoded Segoe UI literals with the resolved rich_font_family (threaded in from the style object the rich builders already receive), keeping Segoe UI only as the final fallback default.
Documentation Add the new *_font_family rows to the README style-keys table and a clear note about the font-availability limitation and recommended fallbacks.
python-pptx mechanism

Setting a typeface is straightforward — assign the run's font name:

run.font.name = "Inter"     # or paragraph.font.name for placeholder paragraphs

This writes an <a:latin typeface="Inter"/> into the run/paragraph rPr. The
rich-layout text path already does exactly this; the core slides simply don't, yet. For
placeholder-based core slides the same applies to the paragraph's font.

The honest limitation — python-pptx cannot embed fonts. Setting a font name only
writes the name into the file; it does not bundle the font. If the viewing
machine lacks that typeface, PowerPoint substitutes another and the deck looks wrong.
PowerPoint's own "Embed fonts in the file" feature writes <p:embeddedFontLst> font
parts into the package — python-pptx exposes no API for this, and doing it by
hand means adding binary font parts plus relationships, which is brittle and raises font
licensing concerns. Therefore:

  • v1 sets font names only. Embedding is explicitly out of scope.
  • Recommend a safe-fallback bias toward widely-available fonts: Arial is the
    safest truly cross-platform choice; Calibri ships with Microsoft Office on both
    Windows and macOS; Segoe UI is reliable only on Windows/Office — it is not a
    macOS system font, which is why the rich layouts' current Segoe UI default already
    substitutes on a Mac without Office installed. Document that any custom or web/Google
    font (Inter, Poppins, …) must be installed on every viewing machine, or the author
    must run PowerPoint's File → Options → Save → Embed fonts on the generated deck.
  • "Weight" is not a numeric axis. OOXML/PowerPoint express weight via the bold
    toggle or via a weight-named family (e.g. Inter SemiBold), not a CSS-style numeric
    weight. The doc and README should say so to avoid surprise.
Google / web font sourcing guidance

The README note should give authors a short recipe for non-system fonts:

  1. Download the family (e.g. from Google Fonts) and install it locally before
    building, so any preview on the author's machine renders correctly.
  2. Reference it by its exact installed family name in style:
    (body_font_family: Inter).
  3. Ensure viewers also have it installed, or embed fonts via PowerPoint after
    generation (and confirm the font's licence permits embedding).
  4. When in doubt, choose a near-equivalent system font to minimise substitution drift.

Rationale

Extending the existing style object — rather than inventing a new mechanism — is the
right call for three reasons that map to the motivation:

  1. Authors already think in style:. They tune *_font_size there today; adding
    *_font_family beside it is the smallest possible conceptual step and immediately
    discoverable. The global→component fallback mirrors how CSS-minded users expect
    cascades to work.
  2. It unifies the split typography in one move. Routing both the core slides and the
    rich layouts' hardcoded Segoe UI through the same style field is what lets a single
    font_family: Inter make the whole deck consistent — the exact pain today.
  3. Honesty over magic. The embedding limitation is real and unavoidable in
    python-pptx. Documenting it plainly (names-only + fallback strategy) is more
    valuable than a fragile, license-risky embedding hack that would surprise users when
    it breaks.
Alternatives considered
  • OOXML font embedding (<p:embeddedFontLst>) — rejected for v1. No python-pptx
    support, binary part wrangling, and font-licensing risk. Listed as possible future
    work.
  • Theme-level fonts (<a:fontScheme> major/minor) on the slide master — deferred
    as a Phase-2 complement.
    Cleaner for blanket placeholder text and a single global
    typeface, but doesn't give per-component control and doesn't reach the rich layouts'
    explicitly-built runs. Could later back the global font_family while per-component
    keys stay run-level.
  • Per-run only, no style-object change — rejected. Gives no single global knob and
    forces every slide to grow its own font argument plumbing.
  • A separate fonts: top-level block — rejected. Splits styling across two places;
    style: already owns typography (sizes), so families belong there too.

Impact on Existing Product

  • Backwards compatibility. Fully additive. When every family key is empty, slides
    never set a font name, so core slides keep the template typeface and rich layouts keep
    Segoe UI (via the literal fallback). Existing decks render identically.
  • Migration. None required; opt-in via new keys.
  • Interaction with existing size keys. Families are orthogonal to the existing
    *_font_size keys; size behaviour is unchanged.
  • Performance. Negligible — a few extra attribute assignments per run; no I/O.
  • Documentation. The README style-keys table grows; add a "Fonts & availability"
    callout covering the embedding limitation, safe fallbacks, and the weight caveat.
  • Tests. Adding keys to the style defaults will break the existing style-defaults
    test that asserts the exact set of known style keys; its expected-key set must be
    updated in the same change.

Risks & Mitigations

Risk Likelihood / Impact Mitigation
Chosen font absent on viewer machine → substitution High / High Document plainly; bias defaults to cross-platform fonts; advise install-or-embed; manual verification step.
Authors expect numeric font weights Med / Low Document that weight = bold flag or weight-named family, not a numeric axis.
Forgetting to update the style-defaults key-set test Med / High (red CI) Flagged in Impact → Tests and Acceptance Criteria.
Per-component plumbing touches many call sites Med / Med Centralise in one font-applying helper; cover with the no-font regression test.
Font licence forbids embedding Low / Med v1 doesn't embed; Phase-2 embedding gated on licence guidance.

Open Questions

  • Should v1 include the weight/style (*_font_bold / *_font_italic) flags, or ship
    family-only first and add weight/style in a follow-up?
  • Should the global font_family be backed by a theme <a:fontScheme> now (cleaner for
    placeholders) or kept purely run-level for v1?
  • Do we want experimental font embedding at all, given the licensing exposure?
  • Per-slide font overrides: ship alongside the 0001
    slide-level override generalisation, or defer entirely?
Dominant language
Python
Stars
11
Forks
3
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/presentations

All issues in microsoft/presentations

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.