Skip to content

Conversation

@EwoutH
Copy link

@EwoutH EwoutH commented Nov 10, 2025

Builds on #227 and #317.

Summary

This PR introduces first-class support for Word Alt Text in python-docx. Users can now set and read both the Alt Text Title and Alt Text Description (as shown in Word’s Accessibility pane) directly through the API when inserting or editing pictures.

Motive

Previously, python-docx did not expose a clean API for working with image alternative text. Users had to manually manipulate XML to set or retrieve <wp:docPr> attributes (title, descr). This enhancement improves accessibility workflows and enables automated tools (e.g., alt-text generators) to embed compliant descriptions directly into Word documents.

Implementation

  • CT_NonVisualDrawingProps (oxml/shape.py): Added optional attributes title and descr.
  • CT_Inline.new() / new_pic_inline(): Accept and apply title / descr to <wp:docPr>.
  • StoryPart.new_pic_inline(): Added title and descr parameters and forwarded them to CT_Inline.new_pic_inline().
  • Run.add_picture() and Document.add_picture(): Updated to accept and pass through title and descr for inline pictures.
  • InlineShape (shape.py): Added new alt_text and alt_title properties for convenient read/write access to docPr.descr and docPr.title.

Usage Examples

from docx import Document
from docx.shared import Inches

doc = Document()

# Insert an image with alt text
doc.add_picture(
    "chart.png",
    width=Inches(5),
    descr="Line chart showing sales growth from 2020 to 2024.",
    title="Sales Growth Chart"
)

# Access or modify alt text of an existing shape
shape = doc.inline_shapes[0]
print(shape.alt_text)      # "Line chart showing sales growth from 2020 to 2024."
shape.alt_text = "Updated alt text description."
shape.alt_title = "Updated title."
doc.save("report_with_alt_text.docx")

Additional Notes

  • Verified functionality using a test script that inserted an image, saved, reloaded, and confirmed alt_title and alt_text persisted correctly.
  • No breaking API changes; title and descr are optional.
  • Works across all story parts (document, headers, footers).
  • Backwards compatible with existing documents.
  • Enables accessibility tooling, automated captioning, and alt-text validation in Word documents.

…ingProps

Expose the `<wp:docPr>` attributes used for image title and alt text in Word. These correspond to the "Alt Text (Title)" and "Alt Text (Description)" fields shown in the Word UI.

Based on the approach from PR python-openxml#227 ("Add support for image title and alt text"), limited here to adding OptionalAttribute definitions in the OXML layer.
- Extend StoryPart.new_pic_inline() with optional `title` and `descr` parameters.
- Pass these through to CT_Inline.new_pic_inline() for setting <wp:docPr> attributes.
- Update Run.add_picture() to accept and forward `title` and `descr` to StoryPart.

Part of the effort to enable setting image alternative text (accessibility title/description) when adding pictures.
Expose read/write access to image alternative text stored in <wp:docPr> via new `InlineShape.alt_text` (description) and `InlineShape.alt_title` (title) properties.
@EwoutH
Copy link
Author

EwoutH commented Nov 10, 2025

While adding alt-text, I ran into an API/compatibility issue.

I’d like to let users set alt text at insertion time via:

  • Document.add_picture(...)
  • Run.add_picture(...)

These are public APIs. Internally they call:

  • StoryPart.new_pic_inline(...)
  • CT_Inline.new_pic_inline(...)

However, the existing API and tests assume a 3-arg positional call chain:

document.add_picture(path, width, height)
run.add_picture(path, width, height)
part.new_pic_inline(image, width, height)

If we extend this with positional title / descr, we either:

  • break those expectations and risk user code,
  • or start passing extra None arguments around, which is brittle and obscures intent.

I think there are three options:

  1. Keep add_picture as-is; only add:

    • InlineShape.alt_text
    • InlineShape.alt_title
      Users set alt text after insertion:
    shape = doc.add_picture("img.png")
    shape.alt_text = "..."
  2. Extend via keyword-only args (backwards compatible):

    • add_picture(..., *, title=None, descr=None)
    • Plumb these through internally as keyword args only.
  3. Internal-only clean break (future-proof core, public stays stable)

    • Keep public APIs as-is (users can optionally pass title/descr as kwargs).
    • Make internal constructors keyword-only and clearer:
      • StoryPart.new_inline_picture(..., *, width=None, height=None, title=None, descr=None)
        (optionally keep new_pic_inline(...) as a shim that calls new_inline_picture(...) and deprecate it)
      • CT_Inline.new_pic_inline(..., *, title=None, descr=None) and CT_Inline.new(..., *, title=None, descr=None)
    • Run.add_picture(...) forwards only provided kwargs to new_inline_picture(...).
    • This gives us a clean, extensible internal surface (ready for future options like wrapping/hyperlink) without changing public behavior. Guidance requested on whether to include the shim + deprecation, or go straight to the new names.

@scanny Which option would you prefer?

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