Create Office Documents from Markdown
create_from_markdown writes a brand-new DOCX, XLSX, or PPTX from a single Markdown string. One Markdown source, three target formats. Markdown is the natural sink for LLM-generated content: have the model emit Markdown — far more reliable than asking it to emit Office XML directly — then materialize the right Office format on demand.
Internally the Markdown is parsed into Office Oxide’s structured DocumentIR and rendered to the target format, so you get the same deterministic, format-agnostic mapping. The IR itself is primarily an extraction and inspection surface (see to_ir()); for creation, Markdown is the public entry point in every binding.
What is the signature for create_from_markdown?
The function takes a Markdown string, a target format ("docx", "xlsx", or "pptx", case-insensitive), and an output path. It returns nothing on success and raises/returns an error on failure.
| Binding | Signature |
|---|---|
| Python | def create_from_markdown(markdown: str, format: str, path, /) -> None |
| Rust | pub fn create_from_markdown(markdown: &str, format: DocumentFormat, path: impl AsRef<Path>) -> Result<()> |
| Go | func CreateFromMarkdown(markdown, format, path string) error |
| JavaScript | createFromMarkdown(markdown, format, path) |
| C# | static void Document.CreateFromMarkdown(string markdown, string format, string path) |
How do I create a DOCX, XLSX, or PPTX from Markdown?
Write your content once as Markdown, then switch the format argument to choose the target.
Rust
use office_oxide::create::create_from_markdown;
use office_oxide::format::DocumentFormat;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let markdown = "\
# Quarterly Report
Generated automatically from Markdown using Office Oxide.
## Highlights
- Revenue grew by **32%** year-over-year
- Customer satisfaction: 4.8 / 5.0
## Financial Summary
| Category | Q3 2025 | Q4 2025 |
|------------|---------|---------|
| Revenue | $1.2M | $1.6M |
| Net Profit | $0.4M | $0.7M |
";
create_from_markdown(markdown, DocumentFormat::Docx, "report.docx")?;
create_from_markdown(markdown, DocumentFormat::Xlsx, "report.xlsx")?;
create_from_markdown(markdown, DocumentFormat::Pptx, "report.pptx")?;
Ok(())
}
Python
from office_oxide import create_from_markdown
markdown = """\
# Quarterly Report
Generated automatically from Markdown using Office Oxide.
## Highlights
- Revenue grew by **32%** year-over-year
- Customer satisfaction: 4.8 / 5.0
- New products launched: Widget Pro, Widget Lite
## Financial Summary
| Category | Q3 2025 | Q4 2025 |
|------------|---------|---------|
| Revenue | $1.2M | $1.6M |
| Expenses | $0.8M | $0.9M |
| Net Profit | $0.4M | $0.7M |
"""
# One Markdown source, three target formats:
create_from_markdown(markdown, "docx", "report.docx")
create_from_markdown(markdown, "xlsx", "report.xlsx")
create_from_markdown(markdown, "pptx", "report.pptx")
JavaScript
import { createFromMarkdown } from 'office-oxide';
const markdown = `# Quarterly Report
Generated automatically from Markdown using Office Oxide.
## Highlights
- Revenue grew by **32%** year-over-year
- Customer satisfaction: 4.8 / 5.0
## Financial Summary
| Category | Q3 2025 | Q4 2025 |
|------------|---------|---------|
| Revenue | $1.2M | $1.6M |
| Net Profit | $0.4M | $0.7M |
`;
for (const fmt of ['docx', 'xlsx', 'pptx']) {
createFromMarkdown(markdown, fmt, `report.${fmt}`);
}
Go
package main
import (
"log"
oo "github.com/yfedoseev/office_oxide/go"
)
func main() {
markdown := `# Quarterly Report
Generated automatically from Markdown using Office Oxide.
## Highlights
- Revenue grew by **32%** year-over-year
- Customer satisfaction: 4.8 / 5.0
## Financial Summary
| Category | Q3 2025 | Q4 2025 |
|------------|---------|---------|
| Revenue | $1.2M | $1.6M |
| Net Profit | $0.4M | $0.7M |
`
for _, fmt := range []string{"docx", "xlsx", "pptx"} {
if err := oo.CreateFromMarkdown(markdown, fmt, "report."+fmt); err != nil {
log.Fatalf("CreateFromMarkdown(%s): %v", fmt, err)
}
}
}
C#
using OfficeOxide;
const string markdown = """
# Quarterly Report
Generated automatically from Markdown using Office Oxide.
## Highlights
- Revenue grew by **32%** year-over-year
- Customer satisfaction: 4.8 / 5.0
## Financial Summary
| Category | Q3 2025 | Q4 2025 |
|------------|---------|---------|
| Revenue | $1.2M | $1.6M |
| Net Profit | $0.4M | $0.7M |
""";
foreach (var fmt in new[] { "docx", "xlsx", "pptx" })
{
Document.CreateFromMarkdown(markdown, fmt, $"report.{fmt}");
}
C
#include <stdio.h>
#include "office_oxide.h"
int main(void) {
const char *markdown =
"# Quarterly Report\n\n"
"Generated automatically from Markdown using Office Oxide.\n\n"
"## Highlights\n\n"
"- Revenue grew by **32%** year-over-year\n"
"- Customer satisfaction: 4.8 / 5.0\n\n"
"## Financial Summary\n\n"
"| Category | Q3 2025 | Q4 2025 |\n"
"|------------|---------|---------|\n"
"| Revenue | $1.2M | $1.6M |\n"
"| Net Profit | $0.4M | $0.7M |\n";
/* format: "docx" | "xlsx" | "pptx" (case-insensitive) */
const char *formats[] = { "docx", "xlsx", "pptx" };
for (int i = 0; i < 3; i++) {
int err = 0;
char path[32];
snprintf(path, sizeof(path), "report.%s", formats[i]);
if (office_create_from_markdown(markdown, formats[i], path, &err) != 0) {
fprintf(stderr, "create_from_markdown(%s) failed: code=%d\n", formats[i], err);
return 1;
}
}
return 0;
}
How does each target format render the Markdown?
The Markdown is parsed into Office Oxide’s structured IR, then each format renders that structure deterministically:
| Markdown construct | DOCX | XLSX | PPTX |
|---|---|---|---|
# heading |
Paragraph with Heading{level} style |
Bold cell at the top of a section | Slide title placeholder |
| Paragraph text | Body paragraph | Cells in successive rows | Body text placeholder |
- / 1. list |
Bulleted or numbered list | Cells in column A | Body bullets |
| Table | Word table | Cells starting at the next free row | PowerPoint table |
**bold** / *italic* |
Inline run formatting | Plain cell text | Inline run formatting |
The mapping is conservative: every Markdown construct produces deterministic output, but each Office format has features Markdown doesn’t model (XLSX cell number formats, PPTX animations, DOCX comment threads). For richer output, build the file and then edit it with the per-format editor, or drop into the format-specific writers.
How do I read, modify, and write back across formats?
Pair extraction with creation to do content edits that survive across formats. Read the source as Markdown, modify the Markdown, and write a fresh file:
Python
from office_oxide import Document, create_from_markdown
with Document.open("legacy.doc") as doc:
markdown = doc.to_markdown()
# Append a new section
markdown += "\n## Appendix\n\nEffective 2026-06-22.\n"
create_from_markdown(markdown, "docx", "modernized.docx")
C
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "office_oxide.h"
int main(void) {
int err = 0;
/* Read the source as Markdown */
OfficeDocumentHandle *doc = office_document_open("legacy.doc", &err);
if (!doc) { fprintf(stderr, "open failed: code=%d\n", err); return 1; }
char *md = office_document_to_markdown(doc, &err);
office_document_free(doc);
if (!md) { fprintf(stderr, "to_markdown failed: code=%d\n", err); return 1; }
/* Append a new section */
const char *appendix = "\n## Appendix\n\nEffective 2026-06-22.\n";
char *modified = malloc(strlen(md) + strlen(appendix) + 1);
strcpy(modified, md);
strcat(modified, appendix);
office_oxide_free_string(md);
/* Write a fresh file */
office_create_from_markdown(modified, "docx", "modernized.docx", &err);
free(modified);
return err;
}
This is also a clean path for legacy → OOXML modernization (DOC → DOCX, XLS → XLSX, PPT → PPTX) when you only need the textual structure. For a structure-preserving conversion that keeps the original layout, use save_as() instead, which round-trips through the IR. See Conversion: legacy → OOXML.
Why generate Office files from Markdown?
- Format-agnostic templates. Generate the same content as DOCX (for Word users), XLSX (for analysts), or PPTX (for presentations) with no rewriting — just change the
formatargument. - LLM-native. Have the model produce Markdown; materialize the file. Markdown is what models emit most reliably.
- Diffable. Markdown is plain text; commit it. The Office file becomes a build artifact.
- Fast. Office Oxide’s Rust core writes files in milliseconds, with a 100% pass rate across the benchmark corpus.
Advanced: build directly from a DocumentIR (Rust)
The Rust crate additionally exposes create_from_ir for callers that want to construct or transform the structured IR by hand instead of going through Markdown:
use office_oxide::create::create_from_ir;
use office_oxide::format::DocumentFormat;
use office_oxide::ir::DocumentIR;
// Build or transform a DocumentIR however you like, then render it:
let ir: DocumentIR = DocumentIR::from_markdown("# Title\n\nBody.\n", DocumentFormat::Docx);
create_from_ir(&ir, DocumentFormat::Docx, "report.docx")?;
pub fn create_from_ir(ir: &DocumentIR, format: DocumentFormat, path: impl AsRef<Path>) -> Result<()>
In the Python, Go, JavaScript, and C# bindings the IR is read-only — produce it with to_ir() for inspection and pipelines, and create files with create_from_markdown.
Limitations
- Cell formatting (number formats, currency, custom styles) isn’t expressed in Markdown. Add it via the per-format editor after creation.
- Images, charts, smart art, and embedded objects aren’t carried by the Markdown path — use the format-specific writers (
XlsxWriter,PptxWriter, or the DOCX builder) for those. - XLSX merges, PPTX animations, and DOCX comment threads have no Markdown equivalent.
For everything beyond Markdown’s expressive range, use the format-specific writers. They give full access to OOXML features at the cost of being format-specific.
FAQ
What formats can create_from_markdown produce?
DOCX, XLSX, and PPTX. The format argument is "docx", "xlsx", or "pptx" (case-insensitive). Legacy binary formats (DOC, XLS, PPT) are read-only and cannot be created.
Is there a create_from_ir function in Python or other bindings?
No. create_from_ir is exposed only in the Rust crate. In Python, Go, JavaScript, and C#, create files with create_from_markdown; the IR (to_ir/to_ir_json) is an extraction-side, read-only concept.
How do I round-trip an existing document while keeping its layout?
Use save_as() for a structure-preserving conversion. Use to_markdown() + create_from_markdown() when you only need the textual structure rebuilt cleanly.
How fast is document creation? Office Oxide’s Rust core writes Office files in milliseconds, with a 100% pass rate across the benchmark corpus — fast enough to generate documents on demand inside a request handler.
See also
- Structured IR — the IR schema in detail (extraction side)
- Conversion: legacy → OOXML — modernize DOC/XLS/PPT
- Editing overview — modify existing files in place