Skip to content

feat(text): add shrink_text_to_fit method for dynamic font resizing#1107

Open
BastienGimbert wants to merge 1 commit into
scanny:masterfrom
BastienGimbert:add-shrink_text_to_fit-method-for-dynamic-font-resizing
Open

feat(text): add shrink_text_to_fit method for dynamic font resizing#1107
BastienGimbert wants to merge 1 commit into
scanny:masterfrom
BastienGimbert:add-shrink_text_to_fit-method-for-dynamic-font-resizing

Conversation

@BastienGimbert
Copy link
Copy Markdown

Problem & Solution: Text Autofit in python-pptx

The Problem

When you set text_frame.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE in python-pptx, it only sets the XML flag (a:normAutofit). PowerPoint is supposed to recalculate the font size when opening the file, but it doesn't do it automatically - you have to manually open the file and activate/deactivate the functionality to trigger the resize.

The Solution

I added a new method shrink_text_to_fit() to the TextFrame class that:

  1. Calculates the optimal font size using the existing TextFitter class
  2. Applies that size immediately to all text (including defRPr)
  3. Sets the TEXT_TO_FIT_SHAPE flag so PowerPoint continues to auto-adjust if text is modified later

Usage

text_frame.shrink_text_to_fit(
    font_family="Calibri",
    max_size=24,      # Maximum font size
    min_size=6        # Minimum font size (fallback)
)

Files Modified

File Change
src/pptx/text/text.py Added shrink_text_to_fit() method
src/pptx/text/layout.py Fixed crash when text is too long to fit at any size

I also added an unit test to, that pass.

@mszbot
Copy link
Copy Markdown

mszbot commented May 6, 2026

@BastienGimbert can you share an example of the shrink_text_to_fit() method. How does it work?

@BastienGimbert
Copy link
Copy Markdown
Author

@BastienGimbert can you share an example of the shrink_text_to_fit() method. How does it work?

I'm in holiday rn , i will send and example next week.
If i forget, fill free to remember me.

@MartinPacker
Copy link
Copy Markdown

Comment: I'm not sure I'd even want to use that: Consistency between slides and indeed text shapes suggests to me I wouldn't.

But maybe there's a different perspective.

MHoroszowski added a commit to MHoroszowski/python-pptx that referenced this pull request May 18, 2026
…epic (#59)

Resolves all 11 sub-features of the issue #16 epic on one branch.
Fork-state probe confirmed none existed here (unlike #18's arrowheads);
this is genuinely all-new text-API surface. Maintainer UAT pending.

New API:
- Font.superscript / Font.subscript — a:rPr/@baseline (signed
  ST_Percentage fraction: +0.30 super, -0.25 sub; mutually exclusive).
- Font.strike — MSO_TEXT_STRIKE_TYPE (NONE/SINGLE/DOUBLE ->
  noStrike/sngStrike/dblStrike), a:rPr/@strike.
- Font.highlight — lazy _HighlightColor proxy over a:rPr/a:highlight
  (CT_Color); read-without-mutate, schema-ordered before the typeface
  trio (verified: [solidFill, highlight, latin, ea, cs]).
- Font.character_spacing / Font.kerning — a:rPr/@spc (signed) and
  @kern (non-negative), centipoints like Font.size.
- Font.latin / Font.east_asian / Font.complex_script — a:rPr a:latin/
  a:ea/a:cs trio. Font.name STILL sets ONLY a:latin (backward compat,
  regression-tested).
- TextFrame.columns / TextFrame.column_spacing — a:bodyPr @numcol
  (1..16, ValueError otherwise) / @spcCol (EMU).
- TextFrame.text_direction — MSO_TEXT_DIRECTION enum, a:bodyPr/@Vert.
- Paragraph.rtl — a:pPr/@rtl (Arabic/Hebrew/Persian; PowerPoint shapes).
- TextFrame.will_overflow() / TextFrame.overflow_info() — read-only
  (does NOT mutate txBody or set autofit; ISC-68-verified) structured
  _OverflowInfo report via TextFitter. Closes scanny#1114.
- TextFrame.shrink_text_to_fit() — eager normAutofit fontScale
  (thousandths form, e.g. "11111"); does not rewrite run sz. Adds
  CT_TextNormalAutofit/@lnSpcReduction. Closes scanny#1107.
- fit_text long-word crash fix (scanny#168): _break_line now
  force-accepts the shortest candidate when no line fits a single
  word wider than the frame, instead of returning None and crashing
  _wrap_lines on a None unpack.

New simpletypes (ST_TextPoint/ST_TextNonNegativePoint/
ST_TextColumnCount), enums (MSO_TEXT_STRIKE_TYPE, MSO_TEXT_DIRECTION),
oxml registrations (a:highlight->CT_Color, a:ea/a:cs->CT_TextFont).
All new a:rPr/a:bodyPr/a:pPr children/attrs are XSD-ordered
(dml-main.xsd ground truth); autofit choice exclusivity verified.

Tests: 47 new unit tests in tests/test_issue16_advanced_text.py
(super/sub 5, strike 4, highlight 4, spacing 4, trio 5, columns 4,
direction 4, rtl 4, overflow 5, shrink 3, fit168 3) + 11 behave
scenarios. Per-attr save->reopen round-trip coverage included.

Trinity (verbatim):
  python3 -m pytest tests/ -q            -> 3972 passed (3925 + 47)
  ruff check src tests                   -> All checks passed!
  python3 -m behave features/ --no-color -> 1130 scenarios, 0 failed
                                            (1119 + 11)

UAT: uat/uat_issue16_advanced_text.py runs clean, 15/15 round-trip
checks, exit 0 (fixture includes the scanny#168 long unbreakable word and a
latin+ea+cs+Arabic multi-script run per the UAT-fixture-diversity
discipline). No-repair = directly observed: real PowerPoint loaded the
deck as a named 4-slide presentation with zero modal sheets (no repair
dialog) across multiple clean relaunches, no crash. Visual rendering of
the slides is DEFERRED — PowerPoint's document window would not surface
to screen/AX this session (an intermittent PowerPoint-side environment
wall the #18 screenshot-probe documented; not a file defect). Visual +
PowerPoint-resave preservation are the maintainer's §6a acceptance
surface; not claimed here.

Closes #16
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.

3 participants