Skip to content

Word Default Template #23

Open
Open

Description

@toxicphreAK

Write a macro adding all default styles to new document and remove whole document content. Save it as default.docx.
Actually it is creating a new document adding all styles by hand and removing content from file as only by adding e.g. a new heading to the doc it will be in the XML (only styles used once are integrated).

Remove author from doc props after creation.

Activity

  1. toxicphreAK commented on Aug 3, 2026

    @toxicphreAK
    OwnerAuthor

    Still open on the 2.0.0 line. main was restarted from upstream v1.2.0, so it currently carries upstream's default template again, not the Word 16 one from 0.9.x.

    Re-applying the Word 16 template is deliberately scheduled last among the code changes, because it alters default-styles.xml and the default document content that a number of tests assert against. Doing it before the feature work would mean repeatedly re-fixing that fallout.

    Adding to the 2.0.0 milestone.

  2. added this to the 2.0.0 milestone on Aug 3, 2026
  3. toxicphreAK commented on Aug 3, 2026

    @toxicphreAK
    OwnerAuthor

    Not done — this one needs Microsoft Word and I have no way to run it. Leaving open, with what I found so a Word session is short.

    Current state. The bundled src/docx/templates/default.docx already defines 164 styles: all nine heading levels and their Char variants, Title, Subtitle, Quote, Intense Quote, the List Bullet/Number/Continue families, Caption, TOC Heading, Book Title, and the full set of built-in table styles. It is not as bare as the issue implies.

    What is actually missing. Comparing the defined styles against the w:latentStyles block in the same part, 12 styles are declared latent but never defined:

    Bibliography, Placeholder Text, Revision, toc 1 … toc 9

    More to the point, several styles the library itself writes by name are absent from the styles part entirely:

    style id written by
    CommentText, CommentReference upstream Comment.add_paragraph()
    FootnoteText, FootnoteReference Footnote.add_paragraph(), Run.add_footnote_reference() (#34)
    Hyperlink Paragraph.add_hyperlink()

    Each of those currently works around the gap: comments and footnotes assign the style id straight to the element, bypassing the styles lookup that would raise, and Paragraph._add_default_hyperlink_style() synthesises a blue underlined character style on first use. Those workarounds are what fixing this would let us delete, so there is a concrete payoff.

    Why I stopped. The remedy is to hand-author w:style definitions into word/styles.xml. Word's built-in style definitions are application defaults, not something ref/xsd/ or the ISO spec pins down, and there is no Word-authored source in the repo to lift them from — doc-word-default-blank.docx and doc-default.docx both define only 4 styles. So I would be guessing at the exact spacing, size and colour values, and a wrong guess silently changes the appearance of every document the library creates. That is not a trade I should make unilaterally on a binary asset.

    Suggested procedure when you next have Word to hand, which is roughly what the issue describes:

    1. New blank document from Normal.dotm.
    2. Apply every style you want materialised at least once — Word only writes a style into styles.xml once it is used or explicitly marked. The 12 latent ones above plus the six in the table are the ones that matter.
    3. Delete all body content, leaving a single empty paragraph.
    4. Clear the author and last-modified-by core properties.
    5. Save as src/docx/templates/default.docx.

    Two things to check afterwards, because they will bite:

    • tests/test_reproducible.py pins byte-identical serialisation; a new template changes the expected bytes.
    • The current template still ships word/stylesWithEffects.xml, a Word 2010 artefact. A Word 16 save will drop it, which is fine, but the README claims "Word 16 (Office 2019) default template" and that claim is not true of the file we ship today — worth making it true in the same change, or dropping the claim.

    I have removed that README line for now, since it described something that has not landed.

  4. toxicphreAK commented on Aug 3, 2026

    @toxicphreAK
    OwnerAuthor

    Status from a survey of the current template while working the 2.0.0 features.

    src/docx/templates/default.docx now carries 164 style definitions (the 0.9.x one had ~39), which resolved #32. But the premise of this issue still holds: Word only writes a style definition once the style has been used, so several built-ins are still latent rather than defined. Confirmed absent from the current template:

    header, footer, caption, heading 6, heading 7, heading 8, heading 9

    Anyone applying one of those by name gets KeyError: no style with name 'header'.

    This still needs the macro described here — create a document, use every built-in once so Word materialises it, delete the content, clear the author from the doc props, save as default.docx — and that needs a real Word install, so I have not attempted it. Leaving open.

    Worth noting a partial workaround now exists for the cross-document case: Styles.copy_style_from() (#87) will lift a missing style out of a template document, with its w:basedOn / w:next / w:link closure. That does not remove the need for a complete default template.

  5. toxicphreAK commented on Aug 4, 2026

    @toxicphreAK
    OwnerAuthor

    Re-analysed the bundled template rather than relying on the earlier notes on this thread, and one of them was wrong.

    Correction

    The comment above listing header, footer, caption and heading 6–heading 9 as absent is not accurate against the current template. All seven are defined, and document.add_paragraph("x", style="Header") works today. That note appears to describe the 0.9.x template. Disregard it.

    Actual inventory of src/docx/templates/default.docx

    164 style definitions — 36 paragraph, 27 character, 100 table, 1 numbering — plus 137 w:lsdException entries.

    Declared latent but never defined, 12:

    toc 1 … toc 9, Placeholder Text, Revision, Bibliography

    Written by the library itself but absent from the styles part, 5:

    Hyperlink, CommentText, CommentReference, FootnoteText, FootnoteReference

    Verified failures on a document straight out of Document():

    >>> document.styles["Hyperlink"]
    KeyError: "no style with name 'Hyperlink'"
    >>> document.add_paragraph("x", style="Bibliography")
    KeyError: "no style with name 'Bibliography'"
    >>> document.add_paragraph("x", style="toc 1")
    KeyError: "no style with name 'toc 1'"

    Three of the five do not need Word after all

    The objection that stalled this issue — that Word's built-in definitions are application defaults with no authoritative source in the repo, so hand-authoring them means guessing — holds for most of the list. It does not hold for these three. The repo already contains Word-authored definitions:

    Hyperlink          features/steps/test_files/comments-rich-para.docx, par-hlink-frags.docx, par-hyperlinks.docx
    CommentText        features/steps/test_files/comments-rich-para.docx
    CommentReference   features/steps/test_files/comments-rich-para.docx
    

    Authentic Word output, already under version control. Lifting them verbatim removes _add_default_hyperlink_style() and its guessed blue-underline character style. Split out as #117.

    Separately: the template carries 447 KB of parts nothing needs

    Turned up while measuring the above. Every document this library creates ships:

    word/stylesWithEffects.xml 438 KB — the Word 2007 back-compat copy of the styles part, reltype schemas.microsoft.com/office/2007/.... Word 2013+ neither reads nor writes it.
    docProps/thumbnail.jpeg 8.3 KB — a preview image of the template's page, shown by file managers as the generated document's thumbnail
    customXml/item1.xml + props + rels the template author's empty b:Sources bibliography store

    A one-paragraph document is 826 KB uncompressed, of which styles.xml is 349 KB and stylesWithEffects.xml another 438 KB. Split out as #113 — also not blocked on Word.

    What stays here

    FootnoteText, FootnoteReference, the endnote pair (#94), toc 1–toc 9, Bibliography, Placeholder Text and Revision. None are in the fixture corpus, so these still want a Word session and the macro procedure described in the issue body, or a Word-authored source document from elsewhere.

    Worth noting #116 makes that easier for end users regardless: bulk import_from() lets anyone pull authentic definitions out of any Word document they have, without waiting on us to ship them.

    Also filed from this

    The wider ask this issue kept bumping into — being able to see and manage what is in a document's styles part at all — is now #114 (usage analysis), #115 (cleanup and pruning), #116 (bulk import/extract) and #118 (an optional CLI).

  6. toxicphreAK commented on Aug 5, 2026

    @toxicphreAK
    OwnerAuthor

    Taking this off the 2.0.0 milestone so the release can go out. Not closing it — the work is still worth doing, it just is not a release blocker.

    Why it does not block 2.0.0. Everything else on the milestone was a correctness or API-shape problem, the kind that is expensive to change after a major release. This is a quality improvement to the shipped template: a document generated today is valid and opens correctly, it just carries fewer built-in style definitions than one Word would produce, so a style has to be added before it can be applied. Landing it in 2.1 breaks nothing that 2.0.0 established.

    Why it is not quick. It needs Word. The task is to author a document containing every built-in style, strip the content back out, and clear the author from the document properties — and there is no way to do that faithfully from this library, which is the whole point of the issue. That is a blocker of a different kind from the rest of the milestone, and it is not one more review pass can clear.

    Related, and worth reading together:

    One thing found while validating 2.0.0 output, recorded here because it belongs to whoever regenerates the template: the current default.docx carries <w:zoom w:val="bestFit"/> in word/settings.xml, which the ECMA-376 1st-edition schema in ref/xsd/ marks invalid — CT_Zoom requires w:percent. It is Word's own output and Word reads it back happily, so it is not a bug to fix here, but a regenerated template should be checked against the schema rather than assumed clean.

  7. removed this from the 2.0.0 milestone on Aug 5, 2026
  8. added a commit that references this issue on Aug 5, 2026
  9. toxicphreAK commented on Aug 5, 2026

    @toxicphreAK
    OwnerAuthor

    Here is the macro. First, though — I measured what is actually missing, and the premise in the issue body is not quite right, which changes what "done" looks like.

    What default.docx already has

    style definitions : 164
    latent entries    : 137
    docDefaults       : present
    

    So it is not the case that only styles used once made it in. All nine headings, the table styles, the list styles, Title, Subtitle, Quote, Caption and the rest are already defined. The gap is narrower and more interesting than "add everything".

    Latent but undefined — 12

    Bibliography      Placeholder Text      Revision
    toc 1  toc 2  toc 3  toc 4  toc 5  toc 6  toc 7  toc 8  toc 9
    

    Absent entirely — not even a latent entry

    Hyperlink              FollowedHyperlink
    annotation text        annotation reference     annotation subject
    footnote text          footnote reference
    endnote text           endnote reference
    

    That second list is the one that matters, because it maps almost exactly onto what 2.0.0 shipped:

    Missing style Feature that needs it
    Hyperlink, FollowedHyperlink Paragraph.add_hyperlink()
    annotation text/reference/subject Document.add_comment() (these are the internal names for Comment Text / Comment Reference / Comment Subject)
    footnote text, footnote reference Document.footnotes, Run.add_footnote_reference()
    toc 1 … toc 9 the TOC field builder
    Placeholder Text content controls (w:sdt)
    Revision tracked changes
    endnote text, endnote reference #94, when endnotes land

    A hyperlink added to a generated document today therefore has no Hyperlink style to reference, so Word renders it as ordinary text rather than blue-and-underlined. Same story for comment and footnote text. That is the concrete user-visible cost of this issue, and it is worth more than the tidiness argument.

    It also subsumes #117, which asked for the Word-authored Hyperlink, CommentText and CommentReference definitions specifically.

    The macro

    Open Word, Alt+F11, Insert > Module, paste, and run GenerateDefaultTemplate. It writes default-generated.docx to your Desktop.

    Option Explicit
    
    ' Generates a style-complete, content-free .docx for use as python-docx-ng's
    ' default template.
    '
    ' Word keeps most built-in styles *latent*: styles.xml gets a w:latentStyles
    ' table listing them, but an actual w:style definition is only written once the
    ' style has been used. This applies every built-in style to throwaway content so
    ' Word materialises the definitions, then removes the content and the personal
    ' metadata, leaving the definitions behind.
    '
    ' Run with `GenerateDefaultTemplate`. Tested on Microsoft 365.
    
    Public Sub GenerateDefaultTemplate()
        Dim doc As Document
        Dim sty As Style
        Dim applied As Long, skipped As Long
    
        Application.ScreenUpdating = False
        Set doc = Documents.Add                     ' -- from Normal.dotm --
    
        For Each sty In doc.Styles
            If sty.BuiltIn Then
                If ApplyStyleOnce(doc, sty) Then
                    applied = applied + 1
                Else
                    skipped = skipped + 1
                End If
            End If
        Next sty
    
        StripContent doc
        StripPersonalMetadata doc
    
        doc.SaveAs2 _
            FileName:=Environ$("USERPROFILE") & "\Desktop\default-generated.docx", _
            FileFormat:=wdFormatXMLDocument, _
            AddToRecentFiles:=False
    
        Application.ScreenUpdating = True
        MsgBox "Applied " & applied & " built-in styles (" & skipped & " skipped)." & vbCrLf & _
               "Saved to Desktop as default-generated.docx", vbInformation
    End Sub
    
    ' Apply `sty` to throwaway content appropriate to its type. Returns True if it
    ' took. Each type needs a different carrier: a character style needs a run of
    ' text, a table style needs a table.
    Private Function ApplyStyleOnce(doc As Document, sty As Style) As Boolean
        Dim rng As Range, tbl As Table, para As Paragraph
    
        On Error GoTo Failed
    
        Select Case sty.Type
            Case wdStyleTypeParagraph, wdStyleTypeParagraphOnly, wdStyleTypeLinked
                Set para = doc.Content.Paragraphs.Add
                para.Range.Text = "x"
                para.Range.Style = sty
    
            Case wdStyleTypeCharacter
                Set para = doc.Content.Paragraphs.Add
                para.Range.Text = "x"
                Set rng = para.Range
                rng.MoveEnd wdCharacter, -1         ' -- exclude the paragraph mark --
                rng.Style = sty
    
            Case wdStyleTypeTable
                Set para = doc.Content.Paragraphs.Add
                Set tbl = doc.Tables.Add(Range:=para.Range, NumRows:=2, NumColumns:=2)
                tbl.Style = sty
    
            Case wdStyleTypeList
                Set para = doc.Content.Paragraphs.Add
                para.Range.Text = "x"
                para.Range.ListFormat.ApplyListTemplateWithLevel _
                    ListTemplate:=ListGalleries(wdNumberGallery).ListTemplates(1)
                para.Range.Style = sty
    
            Case Else
                GoTo Failed
        End Select
    
        ApplyStyleOnce = True
        Exit Function
    
    Failed:
        ' -- a handful of built-ins cannot be applied to ordinary body content;
        ' -- their definitions are not worth failing the run over --
        ApplyStyleOnce = False
    End Function
    
    ' Remove every trace of the throwaway content, leaving one empty paragraph.
    Private Sub StripContent(doc As Document)
        Dim tbl As Table, shp As Shape, i As Long
    
        For i = doc.Tables.Count To 1 Step -1
            doc.Tables(i).Delete
        Next i
        For i = doc.Shapes.Count To 1 Step -1
            doc.Shapes(i).Delete
        Next i
        For i = doc.InlineShapes.Count To 1 Step -1
            doc.InlineShapes(i).Delete
        Next i
    
        doc.Content.Delete
        doc.Content.Style = doc.Styles(wdStyleNormal)   ' -- the surviving paragraph --
    
        ' -- list templates left behind by the list styles would ship numbering.xml
        ' -- entries no generated document references --
        On Error Resume Next
        doc.Content.ListFormat.RemoveNumbers
        On Error GoTo 0
    End Sub
    
    ' Clear author and other identifying metadata. Word stamps these from the
    ' machine that created the file, and they would otherwise ship in every
    ' document generated from this template.
    Private Sub StripPersonalMetadata(doc As Document)
        On Error Resume Next
        With doc
            .BuiltInDocumentProperties(wdPropertyAuthor).Value = ""
            .BuiltInDocumentProperties(wdPropertyLastAuthor).Value = ""
            .BuiltInDocumentProperties(wdPropertyComments).Value = ""
            .BuiltInDocumentProperties(wdPropertyTitle).Value = ""
            .BuiltInDocumentProperties(wdPropertySubject).Value = ""
            .BuiltInDocumentProperties(wdPropertyKeywords).Value = ""
            .BuiltInDocumentProperties(wdPropertyCompany).Value = ""
            .BuiltInDocumentProperties(wdPropertyManager).Value = ""
            ' -- Word 2013+; strips residual personal info Word tracks separately --
            .RemoveDocumentInformation wdRDIDocumentProperties
            .RemoveDocumentInformation wdRDIRemovePersonalInformation
        End With
        On Error GoTo 0
    End Sub

    On macOS, replace the SaveAs2 path with something like
    MacScript("return (path to desktop folder) as string") & "default-generated.docx", or just save through the UI afterwards.

    Verify before committing it

    Word may or may not persist every definition once the content is gone — worth checking rather than assuming. Run this against the generated file:

    import re, zipfile
    
    s = zipfile.ZipFile("default-generated.docx").read("word/styles.xml").decode()
    defined = set(re.findall(r'<w:name w:val="([^"]+)"/>', s))
    latent = set(re.findall(r'<w:lsdException w:name="([^"]+)"', s))
    
    print(f"defined: {len(defined)}  latent: {len(latent)}")
    for want in ("Hyperlink", "annotation text", "annotation reference",
                 "footnote text", "footnote reference", "toc 1", "toc 9",
                 "Placeholder Text", "Revision", "Bibliography"):
        print(f"  {want:<22}", "ok" if want in defined else "STILL MISSING")
    
    from docx import Document
    d = Document("default-generated.docx")
    assert len(d.paragraphs) <= 1, f"content left behind: {[p.text for p in d.paragraphs]}"
    assert not d.core_properties.author, f"author still set: {d.core_properties.author!r}"
    print("content and author clean")

    Three things to check by eye as well:

    1. Size. The current template is 38 KB. More definitions will grow it, and Default template ships 447 KB of parts no generated document needs #113 is about trimming this same file — do not let the two pass each other. Worth comparing before and after.
    2. w:zoom. The current template carries <w:zoom w:val="bestFit"/>, which the ECMA-376 1st-edition schema in ref/xsd/ rejects — CT_Zoom requires w:percent. It is Word's own output and Word reads it back happily, so it is not a bug to fix, but check whether a freshly generated file still has it rather than inheriting the assumption.
    3. Theme part. Style definitions referencing w:asciiTheme resolve against the document's theme. Confirm the generated file ships the same theme the current one does, or theme-font styles will render differently.

    If you would rather not run macros, the fallback is manual and nearly as good: create a document, apply each style from the Styles pane once, delete the content, then File > Info > Check for Issues > Inspect Document to strip personal information. Tedious but it produces the same artefact.

  10. toxicphreAK commented on Aug 5, 2026

    @toxicphreAK
    OwnerAuthor

    Still blocked on having Word available, but two-thirds smaller than it was.

    #139 split out and landed the parts that did not need Word:

    What is left here: FootnoteText, FootnoteReference and the endnote pair EndnoteText/EndnoteReference. None is in the repo's fixture corpus, so they still need a Word session or a Word-authored document from elsewhere. #94 landed endnotes in #139 and assigns EndnoteText/EndnoteReference by id, the same workaround the footnote code already used, so those four are now the whole of the remaining gap.

    Note that #116's Styles.import_from() is a practical route for anyone who has such a document: it will pull authentic definitions into a generated document without anyone hand-authoring w:style elements.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions