Skip to main content

HTML FOR DOCX

Tests PyPI Downloads Version Supported Versions

Convert html to docx, this project is a fork from descontinued pqzx/html2docx.

How install

pip install html-for-docx

Usage

The basic usage

Add HTML-formatted content to an existing .docx document

from html4docx import HtmlToDocx

parser = HtmlToDocx()
html_string = '<h1>Hello world</h1>'
parser.add_html_to_document(html_string, filename_docx)

You can use python-docx to manipulate directly the file, here an example

from docx import Document
from html4docx import HtmlToDocx

document = Document()
parser = HtmlToDocx()

html_string = '<h1>Hello world</h1>'
parser.add_html_to_document(html_string, document)

document.save('your_file_name.docx')

or incrementally add new html to document and save it when finished, new content will always be added at the end

from docx import Document
from html4docx import HtmlToDocx

document = Document()
parser = HtmlToDocx()

for part in ['First', 'Second', 'Third']:
    parser.add_html_to_document(f'<h1>{part} Part</h1>', document)

parser.save('your_file_name.docx')

When you pass a Document object, you can either use document.save() from python-docx or parser.save() from html4docx, both works well.

Both supports saving it in-memory, using BytesIO.

from io import BytesIO
from docx import Document
from html4docx import HtmlToDocx

buffer = BytesIO()
document = Document()
parser = HtmlToDocx()

html_string = '<h1>Hello world</h1>'
parser.add_html_to_document(html_string, document)

# Save the document to the in-memory buffer
parser.save(buffer)

# If you need to read from the buffer again after saving,
# you might need to reset its position to the beginning
buffer.seek(0)

Convert files directly

from html4docx import HtmlToDocx

parser = HtmlToDocx()
parser.parse_html_file(input_html_file_path, output_docx_file_path)
# You can also define a encoding, by default is utf-8
parser.parse_html_file(input_html_file_path, output_docx_file_path, 'utf-8')

Convert files from a string

from html4docx import HtmlToDocx

parser = HtmlToDocx()
docx = parser.parse_html_string(input_html_file_string)

Change table styles

Tables are not styled by default. Use the table_style attribute on the parser to set a table style before convert html. The style is used for all tables.

from html4docx import HtmlToDocx

parser = HtmlToDocx()
parser.table_style = 'Light Shading Accent 4'
docx = parser.parse_html_string(input_html_file_string)

To add borders to tables, use the Table Grid style:

parser.table_style = 'Table Grid'

All table styles we support can be found here.

Options

There is 7 options that you can use to personalize your execution:

  • Disable Images: Ignore all images.
  • Disable Tables: If you do it, it will render just the raw tables content
  • Disable Styles: Ignore all CSS styles. Also disables Style-Map.
  • Disable Fix-HTML: Use BeautifulSoap to Fix possible HTML missing tags.
  • Disable Style-Map: Ignore CSS classes to word styles mapping
  • Disable Tag-Override: Ignore html tag overrides.
  • Disable HTML-Comments: Ignore all "" comments from HTML.

This is how you could disable them if you want:

from html4docx import HtmlToDocx

parser = HtmlToDocx()
parser.options['images'] = False # Default True
parser.options['tables'] = False # Default True
parser.options['styles'] = False # Default True
parser.options['fix-html'] = False # Default True
parser.options['html-comments'] = False # Default False
parser.options['style-map'] = False # Default True
parser.options['tag-override'] = False # Default True
docx = parser.parse_html_string(input_html_file_string)

Extended Styling Features

CSS Class to Word Style Mapping

Map HTML CSS classes to Word document styles:

from html4docx import HtmlToDocx

style_map = {
    'code-block': 'Code Block',
    'numbered-heading-1': 'Heading 1 Numbered',
    'finding-critical': 'Finding Critical'
}

parser = HtmlToDocx(style_map=style_map)
parser.add_html_to_document(html, document)

Tag Style Overrides

Override default tag-to-style mappings:

tag_overrides = {
    'h1': 'Custom Heading 1',  # All <h1> use this style
    'pre': 'Code Block'        # All <pre> use this style
}

parser = HtmlToDocx(tag_style_overrides=tag_overrides)

Custom styles from a Word template: Use a document created from a .docx that already defines the styles (e.g. "Code Block", "Custom Markdown"). Pass that same document to the parser and save it so the custom styles are preserved:

from docx import Document
from html4docx import HtmlToDocx

doc = Document("path/to/template.docx")  # template has Code Block, Custom Markdown, etc.
parser = HtmlToDocx(tag_style_overrides={"code": "Custom Markdown", "pre": "Code Block"})
parser.add_html_to_document(html, doc)
doc.save("output.docx")  # save the template-based doc so custom styles are preserved

If you save a different document (for example, by creating a new Document() instead of loading your template), the output file will not contain the template’s custom styles.

If a referenced custom style does not exist in the document at generation time, a warning will be logged to help you detect the missing style.

Default Paragraph Style

Set custom default paragraph style:

# Use 'Body' as default (default behavior)
parser = HtmlToDocx(default_paragraph_style='Body')

# Use Word's default 'Normal' style
parser = HtmlToDocx(default_paragraph_style=None)

CSS

Inline CSS Styles

Full support for inline CSS styles on any element:

<p style="color: red; font-size: 14pt">Red 14pt paragraph</p>
<span style="font-weight: bold; color: blue">Bold blue text</span>

External CSS Styles

Limited support for external CSS styles (local or url):

HTML

<head>
    <title>My page</title>
    <!-- Must contain rel="stylesheet" otherwise will be ignored -->
    <link rel="stylesheet" href="https://my-external-css.domain.com/style.css">
    <!-- OR -->
    <link rel="stylesheet" href="../my_local/css/style.css">
</head>
<body>
    <div class="classFromExternalCss">
        <p>This should be Red and <span class="blueSpan">Blue</span>.<p>
        <p id="specialParagraph" style="font-weight: normal">A very special paragraph.</p>
    </div>
</body>

CSS

#specialParagraph {
  text-decoration: underline solid red;
  color: gray
}

.blueSpan {
  color: blue;
}

p {
  color: red;
  font-weight: bold;
}

Supported CSS properties:

  • color
  • font-size
  • font-weight (bold)
  • font-style (italic)
  • text-decoration (underline, line-through)
  • font-family
  • text-align
  • background-color
  • Border (for tables)
  • Verticial Align (for tables)

!important Flag Support

Proper CSS precedence with !important:

<span style="color: gray">
  Gray text with <span style="color: red !important">red important</span>.
</span>

The !important flag ensures highest priority.

Style Precedence Order

Styles are applied in this order (lowest to highest priority):

  1. Base HTML tag styles (<b>, <em>, <code>)
  2. Parent span styles
  3. CSS class-based styles (from style_map)
  4. External CSS Styles (from style tag or external files)
  5. Inline CSS styles (from style attribute)
  6. !important inline CSS styles (highest priority)

Metadata

You're able to read or set docx metadata:

from docx import Document
from html4docx import HtmlToDocx

document = Document()
parser = HtmlToDocx()
parser.set_initial_attrs(document)
metadata = parser.metadata

# You can get metadata as dict
metadata_json = metadata.get_metadata()
print(metadata_json['author']) # Jane
# or just print all metadata if if you want
metadata.get_metadata(print_result=True)

# Set new metadata
metadata.set_metadata(author="Jane", created="2025-07-18T09:30:00")
document.save('your_file_name.docx')

You can find all available metadata attributes here.

Logging

html4docx uses Python's standard logging module with named, hierarchical loggers (for example, html4docx.h4d). The library never logs directly to the root logger and installs a NullHandler by default, so it remains silent unless your application configures logging.

Silence all html4docx logs:

import logging

logging.getLogger("html4docx").setLevel(logging.ERROR) # suppresses WARNING and below errors

Enable debug logging:

import logging

logging.basicConfig(level=logging.DEBUG)
logging.getLogger("html4docx").setLevel(logging.DEBUG)

Django / framework LOGGING dict — add an entry for the html4docx parent and it applies to all sub-loggers:

LOGGING = {
    "version": 1,
    "loggers": {
        "html4docx": {
            "level": "ERROR",   # suppresses WARNING and below
            "propagate": False,
        },
    },
}

Note: Unrecognised CSS properties (e.g. letter-spacing, margin, padding) are intentionally logged at DEBUG level because they are expected skips for any real-world HTML, not problems. Only genuinely unexpected situations (missing styles, unsupported colour formats, etc.) are logged at WARNING.

Why

My goal in forking and fixing/updating this package was to complete my current task at work, which involves converting HTML to DOCX. The original package lacked a few features and had some bugs, preventing me from completing the task. Instead of creating a new package from scratch, I preferred to update this one.

Differences (fixes and new features)

Fixes

  • Fix table_style not working | Dfop02 from Issue
  • Handle missing run for leading br tag | dashingdove from PR
  • Fix base64 images | djplaner from Issue
  • Handle img tag without src attribute | johnjor from PR
  • Fix bug when any style has !important | Dfop02
  • Fix 'style lookup by style_id is deprecated.' | Dfop02
  • Fix background-color not working | Dfop02
  • Fix crashes when img or bookmark is created without paragraph | Dfop02
  • Fix Ordered and Unordered Lists | TaylorN15 from PR
  • Fixed styles was only being applied to span tag. | Dfop02 from Issue
  • Fixed bug on styles parsing when style contains multiple colon. | Dfop02
  • Fixed highlighting a single word | Lynuxen
  • Fix color parsing failing due to invalid colors, falling back to black. | dfop02 from Issue
  • Fix logging noise: replace root-logger calls with named module loggers so consumers can silence or configure html4docx output independently. | dfop02 from Issue
  • Fixed non-breaking spaces (&nbsp;) being collapsed into ordinary spaces, which let Word break lines inside amounts like 1 000,00 € | Lynuxen

New Features

  • Add Witdh/Height style to images | maifeeulasad from PR
  • Support px, cm, pt, in, rem, em, mm, pc and % units for styles | Dfop02
  • Improve performance on large tables | dashingdove from PR
  • Support for HTML Pagination | Evilran from PR
  • Support Table style | Evilran from PR
  • Support alternative encoding | HebaElwazzan from PR
  • Support colors by name | Dfop02
  • Support font_size when text, ex.: small, medium, etc. | Dfop02
  • Support to internal links (Anchor) | Dfop02
  • Support to rowspan and colspan in tables. | Dfop02 from Issue
  • Support to 'vertical-align' in table cells. | Dfop02
  • Support to metadata | Dfop02
  • Add support to table cells style (border, background-color, width, height, margin) | Dfop02
  • Being able to use inline images on same paragraph. | Dfop02
  • Refactory Tests to be more consistent and less 'human validation' | Dfop02
  • Support for common CSS properties for text | Lynuxen
  • Support for CSS classes to Word Styles | raithedavion
  • Support for HTML tag style overrides | raithedavion
  • Support style tag and external CSS styles | Dfop02

To-Do

These are the ideas I'm planning to work on in the future to make this project even better:

  • No ideas for now.

Known Limitations

Lists

  • Maximum Nesting Depth: Ordered lists support up to 3 nested levels. Any additional depth beyond level 3 will be treated as level 3.
  • Counter Reset Behavior:
    • At level 1, starting a new ordered list will reset the counter.
    • At levels 2 and 3, the counter will continue from the previous item unless explicitly reset.

CSS

  1. Complex Selectors:

    • Do not support complex selector like div > p, h1 + h2.
    • Complex selectors are simplified to root tag.
  2. Pseudo-classes:

    • Do not support :hover, :first-child, etc.
    • They're all removed during CSS Parsing
  3. Media Queries:

    • Do not support @media queries.
    • Would be necessary a responsive CSS, that's not applied to Docx.
  4. @import:

    • Do not support @import em CSS.
    • We can try implement it in future if necessary.

Project Guidelines

This project is primarily designed for compatibility with Microsoft Word, but it currently works well with LibreOffice and Google Docs, based on our testing. The goal is to maintain this cross-platform harmony while continuing to implement fixes and updates.

⚠️ However, please note that Microsoft Word is the priority. Bugs or issues specific to other editors (e.g., LibreOffice or Google Docs) may be considered, but fixing them is secondary to maintaining full compatibility with Word.

License

This project is licensed under the MIT License - see the LICENSE file for details

Metadata

Release files for html-for-docx 1.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for html-for-docx 1.2.1
File Size Uploaded Provenance
html_for_docx-1.2.1.tar.gz 91.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for html-for-docx 1.2.1
File Interpreter ABI Platform
html_for_docx-1.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 179.7 kB

Release files / html_for_docx-1.2.1.tar.gz

Download URL html_for_docx-1.2.1.tar.gz
Size 91.1 kB
Tags Source
SHA-256 checksum
How to use checksums
abfe1295b0d23dfa2251ca6d7f2e0f645f5e72843d00a62ab370418307c30e21
BLAKE2b-256 checksum
How to use checksums
051aabc35d2524a583be0927c0bf2ef2dc7727edbaec706527119f5cbeb3eafc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / html_for_docx-1.2.1-py3-none-any.whl

Download URL html_for_docx-1.2.1-py3-none-any.whl
Size 88.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c680f3f0465b4f6cb2e01718013dc6b720bda83209cece056e61cfb4d8177226
BLAKE2b-256 checksum
How to use checksums
47b3d1cee667a54f9430347c325769eacace5d0a7738039a25c1e62032819832
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for html-for-docx 1.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for html_for_docx-1.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page