Back to Blog
Python

python python docx images fonts and formatting: A Practical Guide

python python docx images fonts and formatting: Learn to insert images, set fonts, and apply paragraph formatting in Word documents using python-docx, with practical c...

python-docxdocxword-processingimagesfontsformatting
A python-docx generated Word document showing an image, styled text, and formatted paragraphs.

When you need to generate Word documents programmatically, python-docx is the most common library for the job. This article focuses on python python docx images fonts and formatting: how to insert images, control fonts, and apply paragraph formatting reliably. We'll cover the core APIs, common pitfalls, performance considerations, and how to keep your generated documents maintainable.

Setting Up python-docx and Creating a Document

Start by installing the library:

pip install python-docx

Then create a Document object, which represents a .docx file in memory:

from docx import Document doc = Document() doc.save('output.docx')

nThis gives you a blank document with default styles. Most formatting work happens on two levels: paragraphs and runs. A paragraph is a block of text; a run is a contiguous segment of text with identical formatting. Images are added at the paragraph level.

Inserting and Positioning Images

To add an image, use add_picture on the a paragraph or directly on the the document. The simplest form:

doc.add_picture('chart.png')

This places the image in its own paragraph, left-aligned. To control size, pass width or height in EMUs (English Metric Units). python-docx provides Inches and Cm helpers:

from docx.shared import Inches doc.add_picture('chart.png', width=Inches(5))

If you specify only width, height scales proportionally. Specifying both can distort the image, so avoid that unless you intentionally crop or stretch.

To align an image, you need to access the paragraph that contains it. add_picture returns the run, but the paragraph is available via the run's parent. A cleaner approach is to create the paragraph first and then add the picture to it:

from docx.enum.text import WD_ALIGN_PARAGRAPH p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.add_run().add_picture('chart.png', width=Inches(4))

This centers the image. For more control, you can also set the picture's position relative to text, but that requires low-level XML manipulation and is rarely needed for typical reports.

Controlling Fonts and Text Formatting

Fonts are applied to runs, not paragraphs. When you add text with add_paragraph, you get a paragraph with a single run. To set font properties, access the run's font attribute:

p = doc.add_paragraph('Important result') run = p.runs[0] run.font.name = 'Calibri' run.font.size = Pt(14) run.bold = True run.italic = False run.font.color.rgb = RGBColor(0x33, 0x66, 0x99)

Pt and RGBColor come from docx.shared. The name property sets the font family. For complex scripts or East Asian fonts, you may also need to set run.font.element.rPr.rFonts attributes, but for standard Latin text, name works.

To apply different formatting within the same paragraph, create multiple runs:

p = doc.add_paragraph() run1 = p.add_run('Total: ') run1.bold = True run2 = p.add_run('$1,234.56') run2.font.size = Pt(16) run2.font.color.rgb = RGBColor(0xCC, 0x00, 0x00)

This is how you mix bold labels with highlighted values. Remember that formatting is per-run, so if you change a paragraph's text later, you must reapply run formatting.

Paragraph Formatting: Alignment, Spacing, and Indentation

Paragraph-level formatting controls the block layout. Common properties include alignment, line spacing, space before/after, and indentation.

from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.shared import Pt p = doc.add_paragraph('Summary') p.alignment = WD_ALIGN_PARAGRAPH.RIGHT p.paragraph_format.space_before = Pt(12) p.paragraph_format.space_after = Pt(6) p.paragraph_format.line_spacing = 1.5 p.paragraph_format.left_indent = Inches(0.5)

space_before and space_after set spacing outside the paragraph. line_spacing can be a float (multiplier) or a Pt value. Indentation can be set for left, right, and first line. For hanging indents, use first_line_indent with a negative value.

These settings are applied to the paragraph, not to individual runs. If you need different spacing for different sections, create separate paragraphs and set their paragraph_format independently.

Using Styles for Consistent Formatting

Hard-coding fonts and spacing on every paragraph quickly becomes unmaintainable. python-docx supports Word styles, which let you define formatting once and reuse it.

Access a built-in style and modify it:

style = doc.styles['Normal'] style.font.name = 'Arial' style.font.size = Pt(11) style.paragraph_format.space_after = Pt(6)

You can also create custom styles:

from docx.enum.style import WD_STYLE_TYPE custom = doc.styles.add_style('CustomHeading', WD_STYLE_TYPE.PARAGRAPH) custom.base_style = doc.styles['Heading 1'] custom.font.name = 'Georgia' custom.font.size = Pt(20) custom.font.bold = True

Then apply the style to a paragraph:

p = doc.add_paragraph('Chapter 1', style='CustomHeading')

Using styles reduces repetition and makes global changes trivial. When you modify a style, all paragraphs using it update automatically. This is especially useful for long documents where you need consistent headings, body text, and captions.

Performance and Compatibility Considerations

Generating documents with many images or large fonts can affect memory and file size. python-docx loads the entire document into memory, so for very large files, consider streaming or splitting into multiple documents. Images are embedded as-is; a 10 MB image will bloat the output file. Resize images before insertion if possible, using a library like Pillow, and then insert the resized version.

Compatibility is another concern. python-docx only works with .docx (Office Open XML) files, not the older .doc format. If you need .doc support, you must use a different tool or convert via LibreOffice. Also, font availability depends on the system where the document is opened. A font name that exists on your machine may not exist on the recipient's, causing Word to substitute a fallback. For maximum portability, stick to common fonts like Arial, Calibri, or Times New Roman, or embed fonts in the document (which python-docx does not support directly).

Finally, be aware that python-docx does not validate the formatting you set. For example, setting line_spacing to a negative value will produce an invalid document that may not open in Word. Always test the generated file with a real Word processor, especially when you use advanced features like custom styles or complex alignment.

Handling Edge Cases: Images with Captions and Inline Text

A common pattern is to add a caption below an image. You can do this by adding a separate paragraph after the image:

p_img = doc.add_paragraph() p_img.alignment = WD_ALIGN_PARAGRAPH.CENTER p_img.add_run().add_picture('chart.png', width=Inches(4)) p_cap = doc.add_paragraph('Figure 1: Quarterly revenue') p_cap.alignment = WD_ALIGN_PARAGRAPH.CENTER p_cap.runs[0].font.size = Pt(9) p_cap.runs[0].font.italic = True

If you need text to wrap around an image, python-docx does not expose a high-level API. You would have to manipulate the underlying XML to set the image's wrap type. This is rarely worth the effort; a simpler approach is to use a table with two cells: one for the image and one for the text. Tables give you predictable layout without fighting the document model.

Another edge case is inserting an image inside a run that already has text. The add_picture method is available on Run, so you can do:

p = doc.add_paragraph() run = p.add_run('Before ') run.add_picture('icon.png', width=Inches(0.5)) run.add_text(' after')

This places the image inline with the text, which is useful for icons or inline graphics. Note that the run's font formatting does not affect the image, but the image's vertical alignment is based on the run's baseline.

Final Technical Note: Working with Sections and Page Formatting

For complete control over page size, margins, and orientation, you need to work with sections. python-docx creates a default section when you instantiate a Document. You can modify it:

section = doc.sections[0] section.page_width = Inches(8.5) section.page_height = Inches(11) section.left_margin = Inches(1) section.right_margin = Inches(1)

If your document needs different headers or footers for different parts, add new sections with doc.add_section(). Each section can have its own page setup. This is often necessary for reports that mix portrait and landscape pages or that have different margins for appendices.

Combining images, fonts, paragraph formatting, and section properties gives you a complete toolkit for generating professional Word documents. The key is to plan the document structure before writing code: define styles for recurring elements, decide where images go, and set section properties early. This keeps the generation logic clean and the output predictable.

python python docx images fonts and formatting | RYUSLOG DEV