Skip to main content

Convert markdown/text/HTML to PPTX, DOCX, and XLSX using template-driven deterministic rendering with optional LLM normalization.

Project description

Text2OfficeProcessor — Intelligent Document Converter

Convert Markdown / Text / HTML to PPTX, DOCX, and XLSX using template-driven deterministic rendering with optional LLM normalization.

PyPI Python 3.10+ License: MIT


What It Does

Text2OfficeProcessor takes your content in plain text formats and converts it into professionally formatted office documents using your own templates. The tool preserves all template branding, backgrounds, images, and layouts exactly — it only injects your content at the run level.

Input Output
.md .pptx — Template-driven slide decks
.txt .docx — Branded word documents
.html .xlsx — Structured spreadsheets

Core Design Principles

  • Deterministic rendering — no LLM hallucination in the final output. All document construction is 100% programmatic.
  • Template preservation — backgrounds, images, fonts, and layouts are never modified.
  • LLM as assistant, not author — LLM can normalize and tag content, never write documents.
  • Zero magic — every replacement is traceable; every slide is auditable.

Quick Start

Install

pip install text2officeprocessor

The text2officeprocessor command is immediately available. Bundled generic templates are included — no template file needed to get started.

From source (for development or contribution):

git clone https://github.com/sage-khan/text2officeprocessor
cd text2officeprocessor
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt && pip install -e .

Zero-config — try it immediately

No template file needed. The bundled generic template is used automatically:

text2officeprocessor convert --slides-md slides.md --output outputs/presentation.pptx

See what templates are bundled:

text2officeprocessor templates

Generate a PPTX with your own template

text2officeprocessor convert \
  --slides-md slides.md \
  --template  template.pptx \
  --output    outputs/presentation.pptx \
  --type pptx

Generate from raw markdown (full pipeline)

text2officeprocessor convert \
  --input content.md \
  --template template.pptx \
  --output output.pptx \
  --type pptx

Generate DOCX

text2officeprocessor convert \
  --input content.md \
  --template template.docx \
  --output output.docx \
  --type docx

Generate XLSX

text2officeprocessor convert \
  --input report.md \
  --output report.xlsx \
  --type xlsx

Use a custom rules config

text2officeprocessor convert \
  --slides-md slides.md \
  --template template.pptx \
  --output output.pptx \
  --config my-rules.yaml

Batch convert an entire directory

text2officeprocessor batch \
  --input-dir ./content/ \
  --output-dir ./outputs/ \
  --type xlsx

Convert only .md files to PPTX using your own template:

text2officeprocessor batch \
  --input-dir ./slides/ \
  --output-dir ./outputs/ \
  --type pptx \
  --template template.pptx \
  --pattern "*.md"

Embed a draw.io diagram into a slide

In your slides.md:

## SLIDE 3 — template_index: 2 (Single Point)
- placeholder: "SINGLE POINT SLIDE" → "System Architecture"
- diagram: "diagrams/architecture.drawio"

Or export a diagram directly:

text2officeprocessor drawio-export diagrams/architecture.drawio --output outputs/architecture.png
text2officeprocessor drawio-export diagrams/multi-page.drawio --all-pages

Watch mode — auto-regenerate on save

text2officeprocessor watch \
  --input notes.md \
  --output presentation.pptx \
  --type pptx

Every time you save notes.md, the output is regenerated automatically. A 1-second debounce prevents multiple rapid saves from triggering redundant builds. Press Ctrl+C to stop.

text2officeprocessor watch \
  --input slides.md \
  --output presentation.xlsx \
  --type xlsx \
  --debounce 0.5 \
  --no-validate

LLM semantic validation

After rendering, ask an LLM to check the output for truncated text, unreplaced placeholders, content mismatches, and empty sections:

text2officeprocessor convert \
  --slides-md slides.md \
  --output output.pptx \
  --llm ollama --llm-model mistral \
  --llm-validate

The check is non-blocking — if the LLM is unavailable the document is still saved.

Web UI

Install the web extras and launch the browser interface:

pip install text2officeprocessor[web]
text2officeprocessor serve

Open http://127.0.0.1:8000 — drag-and-drop a .md, .txt, or .html file, choose an output format, and download the result.

By default, the UI uses Auto (default local) LLM mode, which resolves to the provider in config/llm_config.yaml (default: Ollama). You can switch to Rule-based only to disable LLM.

text2officeprocessor serve --host 0.0.0.0 --port 8080

Custom validation config

Override the built-in placeholder list, artifact tokens, LLM prompt, and more by pointing to your own YAML:

text2officeprocessor convert --input doc.md --output out.xlsx --type xlsx --config my-rules.yaml

See config/default_rules.yaml for all available keys (validation.known_placeholders, llm_validation.prompt, etc.).

Analyze a template before authoring

text2officeprocessor analyze path/to/template.pptx

Slides Markdown Format

The primary PPTX workflow uses a structured markdown format:

## SLIDE 1 — template_index: 0 (Section Header)
- placeholder: "Section Name Here" → "Getting Started"
- placeholder: "SECTION Number" → "SECTION 1"

---

## SLIDE 2 — template_index: 3 (Multi Point)
- placeholder: "Multi Point Slide" → "Key Points"
- bullets:
  - "First key point goes here"
  - "Second key point goes here"
  - "Third key point goes here"

---

## SLIDE 3 — template_index: 7 (Key Highlights — 4 columns)
- placeholder: "Key Highlights" → "What You Will Learn"
- card_1_title: "Topic One"
- card_1_body: "Short description of topic one"
- card_2_title: "Topic Two"
- card_2_body: "Short description of topic two"

See docs/guide.md for the full format specification.


Architecture

Input (.md / .txt / .html)
    │
    ▼
InputPreprocessor → ParsedDocument
    │
    ▼
LLMNormalizer (optional, fallback: rules) → [(SlideIntent, SlideContent)]
    │
    ▼
ContentPlanner → SlidePlan / DocPlan / SpreadsheetPlan
    │
    ├── PPTXEngine (SlidePart clone + inject)
    ├── DOCXEngine (template body injection)
    └── XLSXEngine (openpyxl structured mapping)
         │
         ▼
    ProgrammaticValidator
         │
         ▼
    Output (.pptx / .docx / .xlsx)

Full architecture diagram: docs/architecture.drawio


LLM Providers

LLM is entirely optional — the tool works fully offline with rule-based normalization.

Provider Flag Required Env Var
Ollama (local) --llm ollama None
vLLM (OpenAI-compatible local/server) --llm vllm Optional VLLM_API_KEY
OpenAI --llm openai OPENAI_API_KEY
Claude --llm claude ANTHROPIC_API_KEY
OpenRouter --llm openrouter OPENROUTER_API_KEY
Groq --llm groq GROQ_API_KEY
text2officeprocessor convert --input content.md --template t.pptx \
  --output out.pptx --type pptx --llm ollama --llm-model mistral

Local 7B model setup (recommended default)

Ollama (preferred):

ollama pull mistral:7b-instruct
ollama serve

vLLM alternative:

python -m vllm.entrypoints.openai.api_server \
  --model mistralai/Mistral-7B-Instruct-v0.2 \
  --port 8000

Docker profile option:

docker compose --profile vllm up -d vllm

One-command backend switch:

scripts/switch-llm-backend.sh ollama
scripts/switch-llm-backend.sh vllm --start

Set provider defaults in config/llm_config.yaml and optional cloud keys in .env (see .env.example).


Tutorial: Converting Your First File

This section walks through the complete workflow for each output format — from zero to a finished document.

Step 1 — Analyse your template

Before writing any content, run analyze on your template to discover the exact placeholder text strings in every shape. These are the strings you will reference in your slides markdown:

text2officeprocessor analyze my-template.pptx

Sample output:

Template: my-template.pptx
Total slides: 13

=== SLIDE 0 (layout: Section Header) ===
  Shape 1 "Title" para[0] run[0]: 'Section Name Here'
  Shape 2 "Subtitle" para[0] run[0]: 'SECTION Number'

=== SLIDE 3 (layout: Multi Point) ===
  Shape 1 "Title" para[0] run[0]: 'Multi Point Slide'

The string in quotes ('Section Name Here', 'Multi Point Slide', etc.) is what you put as the "old" value in a placeholder: line.


Step 2 — Create your slides markdown

Create a file called slides.md. Each slide block starts with a header that names the template slide to clone and what content to inject:

## SLIDE 1 — template_index: 0 (Section Header)
- placeholder: "Section Name Here" → "Project Overview"
- placeholder: "SECTION Number" → "SECTION 1"

---

## SLIDE 2 — template_index: 3 (Multi Point)
- placeholder: "Multi Point Slide" → "Goals for This Quarter"
- bullets:
  - "Reduce onboarding time by 30%"
  - "Launch the self-service portal"
  - "Complete security audit"

---

## SLIDE 3 — template_index: 7 (Key Highlights — 4 columns)
- placeholder: "Key Highlights" → "Four Pillars"
- placeholder: "Enter your subhead line here" → "Our strategic focus areas"
- card_1_title: "Speed"
- card_1_body: "Faster delivery cycles"
- card_2_title: "Quality"
- card_2_body: "Zero-defect release policy"
- card_3_title: "Scale"
- card_3_body: "Infrastructure for 10x growth"
- card_4_title: "People"
- card_4_body: "Invest in the team first"

---

## SLIDE 4 — template_index: 11 (Excellence Grid)
- placeholder: "EXCELLENCE IN THE" → "Why We Win"
- item_01_title: "Customer Focus"
- item_01_body: "Every decision starts with the customer."
- item_02_title: "Execution"
- item_02_body: "We ship and iterate fast."
- item_03_title: "Ownership"
- item_03_body: "Everyone is accountable."

Key rules:

  • template_index is the zero-based slide number in your template (from analyze output)
  • placeholder: "old" → "new" replaces exact text; old must match the template character for character
  • Separate slides with ---
  • See docs/guide.md for all supported slide types and item keys

Step 3 — Generate the PPTX

text2officeprocessor convert \
  --slides-md slides.md \
  --template  my-template.pptx \
  --output    outputs/presentation.pptx \
  --type pptx

The tool prints a summary and runs the validator automatically:

  Slides to render: 4
  Output written: outputs/presentation.pptx
  Validation: PASSED (0 errors, 0 warnings)

Tutorial: Converting Markdown to DOCX

Point to any .md or .txt file and a .docx template. The full pipeline parses the markdown structure and injects it into the template body:

text2officeprocessor convert \
  --input    report.md \
  --template my-template.docx \
  --output   outputs/report.docx \
  --type docx

The DOCX engine preserves all template headers, footers, logos, and styles. Only the body content is replaced.


Tutorial: Exporting Markdown Tables to XLSX

Any markdown file containing tables is automatically mapped to sheets. No template needed:

Input data.md:

# Q1 Sales Report

## Regional Breakdown

| Region | Revenue | Growth |
|--------|---------|--------|
| North  | 142,000 | +12%   |
| South  | 98,500  | +4%    |
| East   | 203,000 | +21%   |

Command:

text2officeprocessor convert \
  --input  data.md \
  --output outputs/q1-report.xlsx \
  --type   xlsx

Each ## section becomes a sheet. Tables become rows. Bullet lists become single-column rows.


Tutorial: Using a Custom Config for a Different Template

If you have a template with different placeholder text than the defaults, create a my-rules.yaml that overrides just the placeholder_map:

placeholder_map:
  section_header:
    title: "CLICK TO EDIT TITLE"
    number: "00"
  bullets:
    title: "SLIDE TITLE"
  key_highlights:
    title: "HIGHLIGHTS"
    subtitle: "subtitle text"

Then pass it with --config:

text2officeprocessor convert \
  --slides-md slides.md \
  --template  corporate-template.pptx \
  --output    output.pptx \
  --config    my-rules.yaml

The tool reads the overrides at runtime. No Python changes needed.


Tutorial: Running via Docker (no Python required)

Mount your working directory to /data and pass all paths relative to that mount:

docker run --rm \
  -v $(pwd):/data \
  text2officeprocessor convert \
  --slides-md /data/slides.md \
  --template  /data/my-template.pptx \
  --output    /data/output.pptx \
  --type pptx

For LLM-assisted conversion using a local Ollama model:

docker compose run --rm text2officeprocessor convert \
  --input   /data/content.md \
  --template /data/template.pptx \
  --output  /data/output.pptx \
  --type pptx --llm ollama --llm-model mistral

Running Tests

python -m pytest tests/ -v

Expected: 123 tests pass.


Documentation

Document Purpose
docs/guide.md Full usage, architecture, folder structure, extending the tool
docs/architecture.drawio System architecture diagram (open with draw.io)
docs/development/changelog.md All changes with timestamps
docs/development/diagnostics.md Bug fixes and troubleshooting

Project Structure

text2officeprocessor/
├── src/core/
│   ├── models.py              # Domain dataclasses
│   ├── exceptions.py          # Custom exceptions
│   ├── parser/preprocessor.py # md/txt/html → ParsedDocument
│   ├── planner/               # ParsedDocument → SlidePlan/DocPlan/SpreadsheetPlan
│   ├── engines/pptx/          # PPTX engine (SlidePart clone)
│   ├── engines/docx/          # DOCX engine (body injection)
│   ├── engines/xlsx/          # XLSX engine (openpyxl)
│   ├── llm/                   # LLM abstraction layer
│   └── validation/            # Post-render checks
├── src/cli/main.py            # Typer CLI entry point
├── config/                    # YAML configuration
├── templates/                 # Sample templates
├── tests/                     # 38 unit + integration tests
└── docs/                      # Architecture diagram + guide

Contributing

Contributions are welcome. Please read CONTRIBUTING.md before submitting a pull request.

Key contribution areas:

  • New LLM providers (implement LLMProvider base class)
  • New output formats (new engine + plan type)
  • Draw.io → PNG integration for flowchart slides
  • Web UI (Phase 2 roadmap item)
  • Additional template types and placeholder maps

License

MIT License — see LICENSE.

Author

Muhammad Danyal (Sage) Khan

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

text2officeprocessor-0.3.1.tar.gz (220.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

text2officeprocessor-0.3.1-py3-none-any.whl (135.8 kB view details)

Uploaded Python 3

File details

Details for the file text2officeprocessor-0.3.1.tar.gz.

File metadata

  • Download URL: text2officeprocessor-0.3.1.tar.gz
  • Upload date:
  • Size: 220.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for text2officeprocessor-0.3.1.tar.gz
Algorithm Hash digest
SHA256 1f494de1bcc0916bfb5bb887a851d08ef2cf7b5106461cde67dfa49af4ae111a
MD5 d0cb7b4dae6d385e780da35bcfcb4a35
BLAKE2b-256 3d006e52eba87bc9d793624c11cc80fc1bf2cafd203bdf8ccc60ef9a2cf825ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for text2officeprocessor-0.3.1.tar.gz:

Publisher: python-publish.yml on sage-khan/text2officeprocessor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file text2officeprocessor-0.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for text2officeprocessor-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 63185bc83193a298fb8ecd87dd2e1ff977398f42328544f9197f348c3d1e093e
MD5 94ae75f1b57b50166b1c2193d77b3ef8
BLAKE2b-256 b0626112cfba0eb13d449deabb514f3d80c5af075e6fe0e422dd60908cd12c4d

See more details on using hashes here.

Provenance

The following attestation bundles were made for text2officeprocessor-0.3.1-py3-none-any.whl:

Publisher: python-publish.yml on sage-khan/text2officeprocessor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page