Skip to main content

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

Unified job config — one file instead of a long flag list

--config accepts a single YAML file that can supply everything convert / batch / watch would otherwise need as separate flags — input/template/output paths, output type, LLM provider selection (including an inline API key or a local endpoint's base_url), and the validate/overflow-strategy switches — under job: and llm: keys:

# job.yaml
job:
  input: content.md
  template: templates/my-template.pptx
  output: outputs/deck.pptx
  type: pptx
  validate: true
  overflow_strategy: auto

llm:
  default_provider: claude
  providers:
    claude:
      model: claude-3-5-sonnet-20241022
      api_key: sk-ant-...          # optional; falls back to ANTHROPIC_API_KEY
    ollama:                        # kept around so `--llm ollama` still works without editing this file
      base_url: http://localhost:11434
      model: llama3.2:1b
text2officeprocessor convert --config job.yaml

Explicit CLI flags always win over the file, so --output other.pptx on top of --config job.yaml only overrides the output path. The same file may also carry the rules sections described below (validation, sanitization, placeholder_map, ...) — one file covers both concerns. batch uses job.input_dir / job.output_dir / job.pattern / job.fail_fast in place of job.input / job.output.

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

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/changelog.md All changes with timestamps
docs/diagnostics.md Bug fixes and troubleshooting
docs/architecture.md How the extraction/generation pipeline is built
docs/feature.md New features, one dated section per feature

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

Metadata

Release files for text2officeprocessor 0.5.3

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

Source distribution (sdist)

Source distribution for text2officeprocessor 0.5.3
File Size Uploaded
text2officeprocessor-0.5.3.tar.gz 287.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for text2officeprocessor 0.5.3
File Interpreter ABI Platform
text2officeprocessor-0.5.3-py3-none-any.whl Python 3 none any Details

Total release size: 475.0 kB

Release files / text2officeprocessor-0.5.3.tar.gz

Download URL text2officeprocessor-0.5.3.tar.gz
Size 287.0 kB
Tags Source
SHA-256 checksum
How to use checksums
226261b03193430d974d2b0ad099430fb760a9c9cf78be4df9c5862fb645e702
BLAKE2b-256 checksum
How to use checksums
6c51bb8675b3a02b9aa6cf2067ced69c7d0952ddf43304b9aa5c7c434db3d877
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 Aug 21, 2026.

Transparency log

Release files / text2officeprocessor-0.5.3-py3-none-any.whl

Download URL text2officeprocessor-0.5.3-py3-none-any.whl
Size 188.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b7226287644a18f2d39d0f15301d9fdeaa023cb6d1dbfe791c4bb0d529c3837c
BLAKE2b-256 checksum
How to use checksums
bbdcbc198d256d266d4562401a959541551808bfcff9d9ab670e64366cf5a997
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 Aug 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.3 This release

2 release files

0.5.1

2 release files

0.3.1

2 release files

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