Legacy Microsoft Office files are still everywhere: document archives, accounting systems, government exports, email attachments, and business workflows that have been running for decades.
The request sounds simple:
Convert
.doc,.xls, and.pptfiles into.docx,.xlsx, and.pptx.
But the usual solutions often require Microsoft Office automation, LibreOffice, a platform-specific bridge, or an external conversion service. Those choices can be difficult to deploy in Linux containers, serverless jobs, restricted environments, and offline systems.
I wanted another option: converters that read the legacy binary formats directly and write Office Open XML using Python.
That work has grown into three open-source projects:
-
doc2docx: Word
.docto.docx -
xls2xlsx: Excel
.xlsto.xlsxor.xlsm -
ppt2pptx: PowerPoint
.pptto.pptx
All three use only the Python standard library at runtime. They do not launch Microsoft Office, LibreOffice, COM, Java, or an external conversion process.
Why this is harder than changing a file extension
The old and new Office formats have fundamentally different architectures.
Word 97–2003, Excel 97–2003, and PowerPoint 97–2003 files are binary formats. They are commonly stored in a Compound File Binary (CFB/OLE) container, but each application has its own internal data model:
- Word uses document streams and interconnected tables for text, formatting, sections, drawings, and references.
- Excel uses BIFF records for worksheets, formulas, styles, charts, and workbook state.
- PowerPoint uses a hierarchy of records, persist objects, masters, shapes, and incremental-save history.
Modern Office files are OPC/OOXML packages: ZIP containers made of XML parts, media files, relationships, and content-type declarations.
Conversion therefore means parsing one object model and reconstructing it in another. It is not a byte copy, and the three applications cannot share a single universal conversion engine.
The projects do share the same product-level conventions—similar commands, Python APIs, reports, batch behavior, and exit codes—but each has its own format-specific parser and writer.
The principles behind the projects
1. No third-party runtime dependencies
The conversion path uses the Python standard library. This keeps deployment small and predictable, especially in containers, private networks, CI workers, and environments where installing a full office suite is impractical.
“No runtime dependencies” does not mean that development tools never use other software. For example, optional regression scripts can use Microsoft Office or LibreOffice to compare rendered output. Those tools verify the converter; they are not used by the converter itself.
2. Follow the published specifications
The implementations are based primarily on Microsoft's published specifications, including MS-CFB, MS-DOC, MS-XLS, MS-PPT, and MS-ODRAW, together with the relevant OOXML documentation.
Working from the specifications makes the behavior explainable and testable. It also avoids treating another office application as a black-box conversion engine.
3. Report loss instead of hiding it
Legacy Office formats contain features that do not map cleanly to OOXML. Some objects are also underspecified, vendor-specific, malformed, or dependent on application behavior.
The converters produce structured diagnostics for unsupported, repaired, omitted, or approximated content. A conversion that opens successfully should not automatically be presented as a perfect conversion.
This matters in real migration work: knowing what changed is often as important as producing the new file.
4. Make file handling safe and automation-friendly
Inputs are opened read-only. Outputs and JSON reports are written through temporary files and atomically replaced after successful serialization. The tools reject dangerous source/output collisions, and batch jobs isolate failures so that one bad file does not stop an entire directory migration.
The three converters
| Project | PyPI distribution | Command / import | Conversion | Python |
|---|---|---|---|---|
| doc2docx | msdoc2docx |
doc2docx |
DOC → DOCX | 3.10+ |
| xls2xlsx | msxls2xlsx |
xls2xlsx |
XLS → XLSX/XLSM | 3.10+ |
| ppt2pptx | ppt2pptx |
ppt2pptx |
PPT → PPTX | 3.11+ |
The distinction between the distribution name and command name is intentional. For example, you install msdoc2docx from PyPI and then run the doc2docx command.
doc2docx
doc2docx reads Word 97–2003 binary documents and creates native WordprocessingML.
It currently handles a growing range of document content, including text, common character and paragraph formatting, fonts, styles, lists, tables, sections, headers and footers, notes, comments, bookmarks, fields, pictures, common floating shapes, and confirmed embedded OLE objects.
It can also open XOR-obfuscated, classic RC4, and RC4 CryptoAPI password-protected documents when a password is supplied.
Install and convert:
python -m pip install msdoc2docx
doc2docx input.doc -o output.docx --report report.json
Or use the Python API:
from doc2docx import convert
result = convert("input.doc", "output.docx", password="secret")
print(result.report.to_dict())
xls2xlsx
xls2xlsx parses Excel BIFF workbooks and writes .xlsx or .xlsm output.
Its supported content includes common cell types, formulas and cached values, styles, rich text, merged cells, dimensions, outlines, hyperlinks, comments, conditional formatting, data validation, filters, print settings, images, common charts, basic shapes, OLE payloads, and VBA projects.
If a workbook contains VBA, the default output is .xlsm when VBA preservation is enabled.
This project also takes a second approach to fidelity: in addition to native conversion, it embeds an exact copy of the original .xls file in the output by default. The source can later be recovered and verified using its stored length and SHA-256 digest.
python -m pip install msxls2xlsx
xls2xlsx input.xls -o output.xlsx --report report.json
xls2xlsx recover output.xlsx -o recovered.xls
Python usage follows the same pattern:
from xls2xlsx import convert
result = convert(
"input.xls",
"output.xlsx",
preserve_styles=True,
preserve_vba=True,
preserve_source=True,
)
print(result.report.warnings)
ppt2pptx
ppt2pptx reads the PowerPoint record stream directly and reconstructs slides as a .pptx package.
It preserves slide order and dimensions, master relationships, hidden-slide state, master decorations, editable text and common shapes, pictures, backgrounds, comments, speaker notes, slide numbers, dates, headers, and footers. It also reconstructs legacy rectangle-cell tables as editable DrawingML tables in supported cases.
The parser accounts for PowerPoint's append-only incremental saves, where older document containers may still be present in the file. It resolves current persist objects and the master referenced by each slide instead of assuming that the first master applies everywhere.
Password-protected RC4 CryptoAPI presentations are supported when a password is provided.
python -m pip install ppt2pptx
ppt2pptx presentation.ppt -o presentation.pptx --report report.json
from ppt2pptx import convert
result = convert("protected.ppt", "protected.pptx", password="secret")
print(result.report.to_dict())
A consistent command-line workflow
Each project supports conversion, read-only inspection, recursive batch processing, and structured reports.
# Inspect without creating an output file
doc2docx inspect input.doc --json
xls2xlsx inspect input.xls --json
ppt2pptx inspect input.ppt --json
# Convert directory trees
doc2docx batch ./legacy-docs -o ./modern-docs --recursive --report doc-batch.json
xls2xlsx batch ./legacy-sheets -o ./modern-sheets --recursive --report xls-batch.json
ppt2pptx batch ./legacy-slides -o ./modern-slides --recursive --report ppt-batch.json
The common workflow is useful when the tools are called from shell scripts, migration jobs, APIs, or queues. Exit code 0 means success, 1 represents conversion failure or a partial batch failure, and 2 indicates invalid input or command usage.
Diagnostics are part of the output
A report can identify warnings and errors at useful locations such as a worksheet cell, binary stream offset, slide index, or object type. Reports also include conversion statistics.
A simplified report looks like this:
{
"source": "/data/input.xls",
"destination": "/data/output.xlsx",
"diagnostics": [],
"statistics": {
"sheets": 1,
"cells": 42,
"vba_preserved": false,
"source_archive_preserved": true
}
}
This lets an application distinguish between outcomes such as:
- the file was converted without a known loss;
- the main content was converted, but an advanced object was approximated;
- a feature was deliberately omitted for safety;
- the input was malformed or unsupported;
- one file failed inside an otherwise successful batch.
Where these tools fit
These projects are intended for situations where direct, local, automatable conversion is more valuable than depending on a desktop office application:
- migrating document archives on Linux;
- converting uploads inside an application backend;
- processing files in containers or CI jobs;
- inspecting old Office files without opening them interactively;
- handling documents in offline or restricted networks;
- building conversion pipelines that need machine-readable loss reports.
They are not a claim that every historical Office feature can already be reproduced perfectly. Advanced Word drawing geometry, some Excel charts and controls, and PowerPoint animation, media, SmartArt, and complex grouped objects still have incomplete mappings. The repositories document their current limitations in detail.
If your requirement is “make this file look right in every edge case,” test the output with representative real documents. If your requirement is “quietly process thousands of files,” keep the JSON diagnostics and review the warning distribution instead of checking only whether a .docx, .xlsx, or .pptx file was created.
What I learned
The biggest lesson is that file conversion is not only a parser problem. It is a preservation problem.
A useful converter must answer several questions:
- What can be translated into an editable native OOXML object?
- What can be preserved as original data even if it cannot yet be translated?
- What must be approximated or omitted?
- How can the caller discover those decisions automatically?
That is why diagnostics, atomic writes, source preservation, and regression testing are first-class features rather than afterthoughts.
Try the projects
The three converters are open source under the MIT License:
If you have real legacy files that expose a conversion problem, please open an issue in the relevant repository. A small reproducible sample—or a detailed description when the file cannot be shared—is especially helpful.
Stars are appreciated, but edge cases are even more valuable: old Office files have decades of them.
Top comments (0)