Skip to content

Add public theme font and style inheritance APIs - #1612

Draft
pseudosavant wants to merge 4 commits into
python-openxml:masterfrom
pseudosavant:codex/theme-font-api
Draft

pseudosavant wants to merge 4 commits into
python-openxml:masterfrom
pseudosavant:codex/theme-font-api

Conversation

@pseudosavant

Copy link
Copy Markdown

Setting a heading style's font.name can leave its existing theme references in place. In Word, the heading can then render in the theme font instead of the requested typeface. Applying a literal font to every run also prevents later theme changes from updating that text.

This adds public APIs for editing the document's Latin theme fonts and binding styles to those fonts. For example, a document can use Aptos Display for headings and Aptos for body text, with both families reflected in Word's theme settings and inherited by new paragraphs.

Proposed API

from docx import Document

document = Document()
document.theme_fonts.name = "Document fonts"
document.theme_fonts.major_latin = "Aptos Display"
document.theme_fonts.minor_latin = "Aptos"

document.styles.default_font.theme_font = "minor"
document.styles["Normal"].font.theme_font = "minor"

for level in range(1, 7):
    style = document.styles[f"Heading {level}"]
    style.font.theme_font = "major"
    if style.linked_style is not None:
        style.linked_style.font.theme_font = "major"

document.add_heading("Heading", level=1)
document.add_paragraph("Body text")
document.save("example.docx")
  • Document.theme_fonts exposes the scheme name and major/minor Latin typefaces.
  • Font.theme_font accepts "major", "minor", or None. Assigning a family removes conflicting literal Latin names. Assigning None removes local Latin theme references while preserving literal names. The getter reads local references and returns None for absent or mixed families.
  • Styles.default_font exposes document-wide character defaults.
  • Read-only Style.linked_style exposes the linked style, allowing callers to configure the character style associated with a paragraph style.

Existing Font.name assignment behavior is preserved. Theme edits preserve East Asian, complex-script, and supplemental fonts, colors, and effects. Access creates a complete default theme when none exists. Malformed or incomplete theme font schemes raise ValueError. Untouched theme parts retain their original bytes.

Validation

  • 31 new unit cases cover saved-package round trips, partial changes, preservation, missing and malformed themes, font references, linked styles, and default formatting.
  • The full suite passes with 1,640 unit tests and 651 acceptance scenarios.
  • The same library changes pass Python 3.9 through 3.13 and Windows checks in the fork CI run.
  • Downstream integration checks verify actual PDF fonts using Microsoft Word with Aptos/Aptos Display and LibreOffice with Liberation fonts. Changing only the theme updates the rendered fonts while leaving document content and styles unchanged. Those layout checks live in the downstream project and do not require Office for this repository's tests.

Feedback requested

This is a draft for feedback on the public API shape before finalizing the contribution. In particular, feedback on the theme_fonts entry point, the theme_font property name, string family values versus an enumeration, and the handling of missing or incomplete themes would be welcome.

The branch contains the library implementation, documentation, unit tests, and acceptance coverage. It is based directly on upstream master and is independent of the existing hyperlink authoring PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant