Back to Blog
Python

python python docx paragraphs headings and tables

python python docx paragraphs headings and tables: Learn how to use python-docx to add paragraphs, headings, and tables to Word documents with practical code examples...

python-docxWord documentsdocument automationparagraphsheadingstables
Illustration of a Python script generating a Word document with paragraphs, headings, and a table.

When you need to generate Word documents programmatically, python-docx provides a straightforward way to work with paragraph text, heading styles, and table structures. This article covers the core operations for python python docx paragraphs headings and tables, with code examples you can adapt to your own document generation tasks.

Setting Up the Document and Adding Paragraphs

The first step is to create a Document object. This represents the in-memory Word document that you will later save to a file. The library is not part of the standard library, so you need to install it first with pip install python-docx.

from docx import Document doc = Document()

Every paragraph in a Word document is represented by a Paragraph object. The simplest way to add a paragraph is to call add_paragraph() on the document. This method returns the newly created paragraph, which you can then modify or style.

paragraph = doc.add_paragraph('This is the first paragraph.')

You can also add an empty paragraph and append runs later. A run is a contiguous sequence of text with the same formatting. For example, to create a paragraph with mixed formatting:

p = doc.add_paragraph() run_bold = p.add_run('Bold text') run_bold.bold = True p.add_run(' and normal text')

The add_paragraph() method accepts an optional style argument. This is useful when you want to apply a predefined style, such as a list bullet or a custom style you have defined in the template. If you do not specify a style, the default Normal style is used.

Adding Headings with Built-in Styles

Headings are paragraphs with specific outline levels and formatting. python-docx provides a dedicated add_heading() method that applies the built-in Heading styles. The method takes the heading text and a level parameter, where level 0 is the Title style and levels 1–9 correspond to Heading 1 through Heading 9.

doc.add_heading('Introduction', level=1) doc.add_heading('Background', level=2) doc.add_heading('Methodology', level=3)

Each heading level maps to a named style in the underlying document. The exact formatting—font size, color, spacing—depends on the template you are using. If you create a document from the default template, the headings follow Word's default theme.

You can also apply a heading style to an existing paragraph using the style property:

p = doc.add_paragraph('Chapter 1') p.style = doc.styles['Heading 1']

This is useful when you need to create a heading after the paragraph has been built, or when you want to reuse a paragraph object. Note that add_heading() automatically sets the style and also adds an outline level, which is important for navigation panes and table of contents generation.

Creating and Filling Tables

Tables in python-docx are created with add_table(). You must specify the number of rows and columns, and you can optionally set a table style. The method returns a Table object.

table = doc.add_table(rows=3, cols=3) table.style = 'Light Grid Accent 1'

The style property accepts the name of a built-in table style. If you do not set a style, the table uses the default 'Table Grid' style, which includes visible borders. To access a specific cell, use the cell(row, col) method. You can then set the text of the cell.

cell = table.cell(0, 0) cell.text = 'Name'

A common pattern is to fill a table from a list of data. For example, given a list of tuples, you can iterate and assign values:

rows_data = [ ('Alice', 30, 'Engineer'), ('Bob', 25, 'Designer'), ('Carol', 35, 'Manager') ] for row_idx, row_data in enumerate(rows_data): for col_idx, value in enumerate(row_data): table.cell(row_idx + 1, col_idx).text = str(value)

Note that the first row is often used for headers. You can also access a row object via table.rows and a column via table.columns. The Row and Column objects provide access to their cells, which is convenient for applying uniform formatting.

Styling Paragraphs and Runs

Paragraph-level formatting includes alignment, indentation, spacing, and line breaks. The ParagraphFormat object, accessed via paragraph.paragraph_format, controls these properties.

from docx.enum.text import WD_ALIGN_PARAGRAPH p = doc.add_paragraph('Centered text') p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.paragraph_format.space_before = Pt(12) p.paragraph_format.space_after = Pt(6) p.paragraph_format.line_spacing = 1.5

Run-level formatting affects the font properties of a specific run. You can set the font name, size, bold, italic, underline, and color.

run = p.add_run('Important') run.bold = True run.italic = True run.font.size = Pt(14) run.font.name = 'Arial' run.font.color.rgb = RGBColor(0xFF, 0x00, 0x00)

The Pt and RGBColor classes come from docx.shared. They are used to specify measurement values and colors. Without these imports, you would need to pass integers or strings, which is error-prone.

When you need to apply the same formatting to many paragraphs, it is more maintainable to define a custom style once and reuse it. python-docx allows you to add new styles to the document's style collection. This is particularly useful for headings and body text in long documents.

Working with Table Cells and Merging

Tables often require merging cells to create a header that spans multiple columns or a label that spans multiple rows. The Cell object has a merge() method that takes another cell and returns a merged cell.

merged_cell = table.cell(0, 0).merge(table.cell(0, 2)) merged_cell.text = 'Merged Header'

After merging, the merged cell spans the range of the two original cells. You can also merge vertically by passing a cell from a different row. The merged cell's text property sets the content, but you can also access its paragraphs and runs for more control.

When you merge cells, the resulting cell inherits the formatting of the first cell. If you need to set a background color or vertical alignment, you can access the cell's _tc property (the underlying XML element) and manipulate it directly, though that is more advanced.

Another common operation is adding a row or column after the table has been created. The add_row() and add_column() methods are available on the Table object. They add a row at the end and a column at the end, respectively. To insert a row at a specific position, you need to work with the XML directly, which is beyond the scope of this article.

Performance and Memory Considerations for Large Documents

python-docx loads the entire document into memory as a tree of objects. This means that generating a very large document—for example, a report with thousands of paragraphs and tables—can consume significant RAM. The library is not designed for streaming; it builds the whole document in memory and then writes it out when you call save().

If you are generating a document that is tens of megabytes in size, you may notice high memory usage. In such cases, consider whether you can split the document into multiple smaller files, or whether you can use a lower-level library that writes directly to a file. For most business documents, however, python-docx is sufficient.

Another performance consideration is the repeated use of add_paragraph() and add_table(). Each call creates new objects and appends them to the document body. The overhead is small, but if you are adding hundreds of thousands of elements, you should profile your code. A common optimization is to avoid unnecessary style lookups by caching style objects.

When you save a document, python-docx serializes the entire XML tree. This can be slow for large documents. If you need to save frequently, consider building the document in memory and saving only once at the end.

Common Pitfalls and How to Avoid Them

One frequent mistake is forgetting to save the document. The Document object exists only in memory until you call save(). If your script exits without saving, all your work is lost. Always ensure that the save call is executed, even if an exception occurs, by using a try/finally block or a context manager if you create a wrapper.

Another pitfall is using the wrong style name. Built-in heading styles are named 'Heading 1', 'Heading 2', etc., but the exact capitalization matters. If you use doc.styles['heading 1'], it will raise a KeyError. Similarly, table styles have specific names like 'Table Grid' or 'Light Shading Accent 1'. To see all available styles, you can iterate over doc.styles and print their names.

When working with tables, a common issue is that add_table() creates a table with no borders if no style is set. If you expect visible borders, set table.style = 'Table Grid' explicitly. Also, the cell.text property replaces all existing content in the cell. If you need to append text to a cell that already contains a paragraph, you must access the cell's paragraphs and add runs to the last paragraph.

Finally, be aware that python-docx does not support every Word feature. For example, it cannot create text boxes, shapes, or complex page layouts. If your document requires these features, you may need to manipulate the underlying XML or use a different library. Understanding the limitations of python-docx helps you choose the right tool for your document generation tasks.

python python docx paragraphs headings and tables | RYUSLOG DEV