DEV Community

CoreNova
CoreNova

Posted on

No LibreOffice, No COM: Three Pure-Python Legacy Office Converters

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 .ppt files 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:

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
Enter fullscreen mode Exit fullscreen mode

Or use the Python API:

from doc2docx import convert

result = convert("input.doc", "output.docx", password="secret")
print(result.report.to_dict())
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
from ppt2pptx import convert

result = convert("protected.ppt", "protected.pptx", password="secret")
print(result.report.to_dict())
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. What can be translated into an editable native OOXML object?
  2. What can be preserved as original data even if it cannot yet be translated?
  3. What must be approximated or omitted?
  4. 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)