A Python-based Word to Markdown converter for Microsoft Word documents.
- Support for heading conversion (H1-H6)
- Support for paragraph text
- Support for bold, italic, underline formatting
- Support for ordered and unordered lists
- Support for table conversion
- Support for image extraction and conversion
- Automatic folder structure creation
- Automatic blank line and format cleanup
- Command line interface
- Batch conversion support
- Smart title handling with proper heading level adjustment
- Intelligent formatting merge (e.g., adjacent underline tags)
- Font-size based heading detection (when no heading styles are present)
- Legacy
.docsupport via LibreOffice conversion
pip install word2mdUpgrade to the latest version:
pip install --upgrade word2mdgit clone https://github.com/HNRobert/word2md.git
cd word2md
pip install -e .Python python-docx cannot read .doc files directly. This project supports .doc by converting it to a temporary .docx using LibreOffice.
- macOS:
brew install --cask libreoffice - Ensure the
sofficecommand is available in yourPATH(LibreOffice installs it). - Alternatively, you can set the
WORD2MD_SOFFICE_PATHenvironment variable to the full path of your LibreOfficesofficeexecutable (useful on Windows or custom installs).
Examples:
- macOS / Linux (bash/zsh):
# export the path to soffice binary
export WORD2MD_SOFFICE_PATH=/Applications/LibreOffice.app/Contents/MacOS/soffice- Windows (PowerShell):
# set environment variable for current session
$env:WORD2MD_SOFFICE_PATH = 'C:\\Program Files\\LibreOffice\\program\\soffice.exe'After installation, you can use the word2md command:
# Convert single file
word2md document.docx
# Convert legacy .doc (requires LibreOffice)
word2md document.doc
# Specify output file
word2md document.docx -o output.md
# Show verbose output
word2md document.docx -v
# Ignore all images and output a single Markdown file
word2md document.docx --ignore-images
# Batch conversion
word2md *.docx -o output_directory/You can also run the converter directly:
# Convert single file to auto-generated folder structure
python main.py document.docx
# Convert legacy .doc (requires LibreOffice)
python main.py document.doc
# Specify output file
python main.py document.docx -o output.md
# Show verbose output
python main.py document.docx -o output.md -v
# Ignore all images and output a single Markdown file
python main.py document.docx --ignore-images# Batch conversion to output directory
python main.py *.docx -o output_directory/
# Output to stdout
python main.py document.docxThe project is now organized as a modular package:
word2md/
├── main.py # Main entry point
├── docx_converter/ # Main package
│ ├── __init__.py # Package initialization
│ ├── cli.py # Command line interface
│ ├── converter.py # Main converter class
│ ├── document_processor.py # Document processing logic
│ ├── paragraph_processor.py # Paragraph processing
│ ├── formatting.py # Text formatting (bold, italic, etc.)
│ ├── list_processor.py # List handling
│ ├── table_processor.py # Table conversion
│ ├── image_processor.py # Image processing in paragraphs
│ ├── image_extractor.py # Image extraction from DOCX
│ └── utils.py # Utility functions
├── assets/
│ └── sample.docx # Sample test file
├── requirements.txt # Dependencies
└── README.md # Documentation
- Bold →
**Bold** - Italic →
*Italic* - Underline →
<u>Underline</u>
The converter supports multiple methods for detecting headings:
- Style-based detection: Converts Word heading styles (Heading 1-6, Title) to Markdown headings
- Font-size based detection: When no heading styles are present, automatically detects headings based on font size hierarchy
- Analyses all paragraphs with uniform font sizes
- Determines the baseline font size (most common size, usually normal text)
- Assigns heading levels to larger font sizes in descending order
- Example: If baseline is 12pt, then 18pt → # (H1), 16pt → ## (H2), 14pt → ### (H3)
- Word heading styles → Markdown headings (# ## ### etc.)
- Smart title handling: When a "Title" style is present, all other headings are automatically adjusted down one level
- Unordered lists (•, -, * etc.) →
- Item - Ordered lists (1., 2., etc.) →
1. Item
- Word tables → Markdown table format
- Automatic extraction of images from DOCX
- Save to
assets/directory under document name folder - Create proper image references in Markdown:
 - Optional
--ignore-images/--no-imagesmode to skip all images
After conversion, the following structure is created:
document_name/
├── document_name.md
└── assets/
├── image_001.jpg
├── image_002.png
└── ...
When using --ignore-images, output is a single Markdown file (no subfolder and no assets/ directory):
document_name.md
A document with the following structure:
- Title style: "TEST DOC"
- Heading 1: "Title 1"
- Heading 2: "Title 2"
- Heading 3: "Title 3"
- Various text formatting including bold, italic, and underlined text
# TEST DOC
## Title 1
### Title 2
#### Title 3
This is a paragraph with **bold text**, _italic text_, and <u>underlined text</u>.
- Unordered list item 1
- Unordered list item 2
1. Ordered list item 1
2. Ordered list item 2
word2md/
├── main.py # Main entry point
├── docx_converter/ # Main package
│ ├── __init__.py # Package initialization
│ ├── cli.py # Command line interface
│ ├── converter.py # Main converter class
│ ├── document_processor.py # Document processing logic
│ ├── paragraph_processor.py # Paragraph processing
│ ├── formatting.py # Text formatting (bold, italic, etc.)
│ ├── list_processor.py # List handling
│ ├── table_processor.py # Table conversion
│ ├── image_processor.py # Image processing in paragraphs
│ ├── image_extractor.py # Image extraction from DOCX
│ └── utils.py # Utility functions
├── assets/
│ └── sample.docx # Sample test file
├── requirements.txt # Dependencies
└── README.md # Documentation
- Modular Design: Each component has a single responsibility
- Easy Testing: Individual modules can be tested independently
- Maintainable: Clear separation of concerns
- Extensible: Easy to add new features or modify existing ones
DocxToMarkdownConverter: Main orchestrator classDocumentProcessor: Handles document-level processing and title detectionParagraphProcessor: Manages paragraph conversion and formattingImageExtractor: Extracts and maps images from DOCX filesListProcessor: Handles ordered and unordered list conversionTableProcessor: Converts Word tables to Markdown formatTextFormatter: Handles text formatting (bold, italic, underline)
The modular structure makes it easy to extend functionality:
Edit docx_converter/formatting.py to add support for new text styles.
Modify docx_converter/list_processor.py to handle different list formats.
Update docx_converter/image_processor.py and docx_converter/image_extractor.py for advanced image handling.
Add new processors in the docx_converter/ directory and integrate them via document_processor.py.
- Install dependencies:
pip install -r requirements.txt - Run tests:
python main.py assets/sample.docx - Add new features in appropriate modules
- Test with various DOCX files
- Update documentation
This repository provides a manual GitHub Action to publish the package to PyPI. The workflow is triggered via the Actions UI (Manual publish to PyPI → Run workflow).
Behaviour:
- It requires a
versioninput (semantic version like1.0.1). - It will update
docx_converter/__init__.pyandsetup.pywith the provided version. - If files change, it commits & pushes the change back to the
mainbranch and optionally creates av<version>tag. - Finally it builds
sdist+wheeland publishes to PyPI using thePYPI_API_TOKENsecret. If you requested a tag (thetaginput), the workflow will also create a GitHub Release (tagv<version>) and upload the generated artifacts fromdist/*to the release.
Set up:
- Add
PYPI_API_TOKENas a repository secret (Repository Settings → Secrets and variables → Actions → New repository secret). - Trigger the workflow via the Actions page and supply
version. To create a tag/release, check thetagcheckbox.
Note: The workflow only runs on manual dispatch to avoid accidental publishes on routine pushes.
- The converter primarily supports basic document formats; complex formatting may require manual adjustment
- Images are automatically extracted and saved to the assets folder
- Complex table layouts may need manual optimization
- Some Word-specific formats have no equivalent in Markdown and will be simplified
MIT License
Issues and Pull Requests are welcome to improve this converter.