# Formats: what anydoc converts and what GFM you get
This reference documents every input format the pinned CLI (`@firecrawl/anydoc`
v0.2.4) accepts, the GitHub-Flavored Markdown each one produces, and the
fidelity caveats you must know before trusting the output. Every claim below
was verified by running the real CLI against the committed fixtures in
`fixtures/` (see [sources.md](sources.md) for provenance and the verification
procedure).
## Coverage: 8 families / 21 extensions / 12 parsers
| Family | Extensions | Canonical parser |
| --- | --- | --- |
| Word | `.doc`, `.docx`, `.docm` | `doc` (legacy OLE) / `docx` (`.docm` aliases to `docx`) |
| PowerPoint | `.ppt`, `.pps`, `.pot`, `.pptx`, `.pptm`, `.ppsx`, `.ppsm` | `ppt` (`.pps`, `.pot` alias to `ppt`) / `pptx` (`.pptm`, `.ppsx`, `.ppsm` alias to `pptx`) |
| Excel | `.xls`, `.xlsx`, `.xlsm`, `.xlsb` | `xlsx` (all four; calamine reads both OLE and ZIP) |
| OpenDocument | `.odt`, `.ods`, `.odp` | `odt`, `ods`, `odp` |
| Rich Text Format | `.rtf` | `rtf` |
| EPUB | `.epub` | `epub` |
| CSV | `.csv` | `csv` |
| PDF | `.pdf` | `pdf` |
That is **8 families, 21 extensions, 12 canonical parsers**: `doc, docx, odt,
pdf, ppt, pptx, rtf, epub, xlsx, ods, odp, csv`. These 12 names are also the
values accepted by `--format`; extension aliases resolve through the same
mapping (verified: `--format xls` and `--format docm` are accepted).
Format detection reads the file *bytes* first (PDF header, RTF open group, OLE
stream names, ZIP mimetype/content types). CSV has no content signature, so it
falls back to the extension or to an explicit `--format`.
## Shared output behavior
All document formats flow through one shared document model and one GFM
serializer, so identical logical structure yields near-identical Markdown
across formats. Behaviors you can rely on everywhere:
- Headings render as `#`โ`######` with anchors.
- Inline runs preserve **bold**, *italic*, ~~strike~~, `` `code` ``, and lists
(bullet, numbered, nested, roman).
- GFM tables with header rows; merged cells render as **empty covered spans**.
- Footnotes/endnotes: `[^n]` reference inline, with `[^n]: ...` definition
lines at the end of the document.
- Markdown specials in source text are escaped (`\*stars*`, `\| pipe`).
- Embedded images render as their **alt text only** โ raw image bytes never
survive into Markdown.
- Bookmarks/anchor targets render as raw `` markers.
## Word (`.doc`, `.docx`, `.docm`)
Expected output: `#` title, `##`/`###` section headings, inline emphasis,
GFM tables, `[^n]` footnotes. DOCX, DOC, ODT, and RTF all share this document
shape; the same fixture converted as `.doc`, `.odt`, and `.rtf` produced
near-identical markdown.
Real conversion of `fixtures/fixture-handmade-outline.docx`:
```markdown
## Style heading stays a heading
### Direct level overrides the style
Direct nine turns the style heading off
# Direct outline without a style
Child style nine stops inheritance
```
Headings come from Word styles and direct formatting; `#`โ`######` levels map
onto heading levels. Real conversion of `fixtures/text.doc` shows the full
document shape:
```markdown
# Fixture Document
Plain paragraph with **bold**, *italic*, and ~~struck~~ runs.
## Table
| | | |
| --- | --- | --- |
| Wide head | | End |
| Tall | B2 | C2 |
| | B3 | C3 |
## Notes and special text
Music clef ๐ appears before this footnote[^1] reference.
[^1]: Footnote after an astral character.
```
Caveats:
- **Merged cells** in Word tables render as empty covered spans (the covered
cells are blank, not repeated or filled).
- **Nested tables** flatten into a single cell (GFM cannot nest tables) โ a
known limitation of the library.
- Legacy `.doc` (OLE) converts through the same document serializer with the
same shape; only the relative-link target rendering differs cosmetically
between sources.
- Fillable-form controls (DOCX content controls) lose their field layer;
labels and underline glyphs survive.
## PowerPoint (`.ppt`, `.pps`, `.pot`, `.pptx`, `.pptm`, `.ppsx`, `.ppsm`)
Expected output: **slide titles as plain paragraphs** (never markdown
headings), bullet lists, speaker notes as `>` blockquotes, and โ for PPTX and
ODP โ slide tables as proper GFM tables. Legacy `.ppt` flattens tables to bare
text lines (see caveat).
Real conversion of `fixtures/pres.pptx`:
```markdown
Deck Title Slide
- Top level point
- Nested detail
- Second point with emphasis
> Speaker note for the intro slide.
Numbers Slide
| Region | Total |
| --- | --- |
| North | 42 |
Grouped shapes below.
```
Caveat โ **legacy `.ppt` flattens tables to bare text lines.** The same deck
converted from `fixtures/pres.ppt` renders the Numbers Slide table as plain
lines with no `|` table syntax:
```markdown
Numbers Slide
Region
Total
North
42
```
If the presentation's tables matter, use PPTX or ODP and verify the `|` rows
survived (see [workflows.md](workflows.md), "Output verification").
## Excel (`.xls`, `.xlsx`, `.xlsm`, `.xlsb`)
Expected output: each worksheet becomes a `## ` heading followed by
a GFM table; the first row is used as the table header when it looks
label-like.
Real conversion of `fixtures/sheet.xlsx` (first table):
```markdown
## Values
| Kind | Value | Note |
| --- | --- | --- |
| Percent | 0.155 | fifteen and a half |
| Currency | 1234.5 | dollars |
| Thousands | 9876543 | grouped |
| Date | 2026-03-15 | ides of March |
| Duration | 26:30:15 | over a day |
| Tiny | 0.0000004 | four ten-millionths |
| Boolean | TRUE | yes |
```
Caveats:
- **XLS/XLSX drop number formats (issue #27).** Cells carry their *raw*
values, not the formatted display values: `Percent โ 0.155` (not `15.5%`),
`Currency โ 1234.5` (not `$1,234.50`), thousands `9876543`. A percentage
reading as a raw fraction is wrong by 100x in meaning โ warn consumers and
sanity-check spreadsheets. Dates survive as ISO strings (`2026-03-15`).
- **ODS is the contrast case:** it keeps the formatted display values
(`15.5%`, `$1,234.50`, `9,876,543`) on the same logical content. If display
values matter, prefer ODS or a CSV export.
- **Merged cells render as empty covered spans** within the populated range
only. Real conversion of `fixtures/handmade-merged.xlsx`:
```markdown
| | | |
| --- | --- | --- |
| Merged across | | padded |
| tall | b2 | 3.5 |
| | b3 | |
```
- Hidden rows and columns are treated as visible and appear in the output
(known limitation) โ check for hidden template or calculation content before
feeding output to an LLM.
## OpenDocument (`.odt`, `.ods`, `.odp`)
- `.odt`: same document shape as DOCX/DOC/RTF โ `#`/`##` headings, GFM
tables, `[^n]` footnotes. Real conversion of `fixtures/text.odt` matches the
`text.doc` output structure line-for-line (only relative-link targets differ
in depth).
- `.ods`: same spreadsheet shape as XLSX (`## Values` + GFM table) but with
**formatted display values preserved** โ the Excel number-format caveat does
not apply. Real conversion of `fixtures/sheet.ods`:
```markdown
## Values
| Kind | Value | Note |
| --- | --- | --- |
| Percent | 15.5% | fifteen and a half |
| Currency | $1,234.50 | dollars |
| Thousands | 9,876,543 | grouped |
```
- `.odp`: **same slide serializer as PPTX** โ slide titles as plain
paragraphs, speaker notes as blockquotes, and GFM tables **kept** (unlike
legacy `.ppt`). Real conversion of `fixtures/pres.odp`:
```markdown
Deck Title Slide
- Top level point
- - Nested detail
- Second point with emphasis
> Speaker note for the intro slide.
Numbers Slide
| Region | Total |
| --- | --- |
| North | 42 |
```
One cosmetic difference vs PPTX: a nested bullet renders as `- - Nested
detail` on one line rather than as an indented sub-list. The table,
blockquote notes, and paragraph titles are identical in shape to PPTX.
## Rich Text Format (`.rtf`)
Expected output: the same document shape as DOCX/ODT (`# Fixture Document`,
`##` sections, GFM tables, `[^n]` footnote definitions). Real conversion of
`fixtures/text.rtf` matches `text.odt` structure; the one notable difference is
that relative link targets render with a `file:///` absolute path, e.g.
`[a sibling file](file:///anydoc/tests/fixture-src/sibling.odt)`, instead of a
relative path โ a known cosmetic quirk.
## EPUB (`.epub`)
Expected output: `#` chapter headings (plus the book metadata title), GFM
tables, preserved inline emphasis/code, and **internal anchor links resolved to
fragments**. Real conversion of `fixtures/book.epub`:
```markdown
# Fixture Book
# Fixture Book
anydoc tests
# Chapter One
Opening paragraph with **bold**, *italic*, and `code` runs.
See [Chapter Two](#epub-text-ch002-xhtml-chapter-two) for the table, or jump straight to [the marked paragraph](#epub-text-ch002-xhtml-markpoint).
# Chapter Two
| Name | Qty |
| --- | --- |
| Bolts | 12 |
| Nuts | 30 |
```
Notes: the book title may appear twice (metadata title + injected title);
internal links keep working as `[text](#fragment)` links; external links stay
as normal markdown links.
## CSV (`.csv`)
Expected output: the file renders as **one GFM table**. The first row is
**promoted to the header row** when it looks like labels (โฅ 2 columns,
non-empty, non-numeric, distinct fields) โ this behavior ships in 0.2.4.
Quoted fields with embedded commas and newlines are preserved.
Real conversion of `fixtures/fixture-handmade-quoted.csv`:
```markdown
| name | desc | qty |
| --- | --- | --- |
| padded | comma, inside | 3 |
| plain | multi line | 4 |
```
Also verified:
- **Delimiter sniffing** โ a semicolon-delimited file with decimal commas
splits on `;` and keeps `1,5` intact (real output of
`fixtures/fixture-handmade-semicolon.csv`):
```markdown
| a | b | c |
| --- | --- | --- |
| 1,5 | 2,5 | x |
| 3,0 | y | z |
```
- **UTF-16 (with BOM)** decodes to correct Unicode (real output of
`fixtures/fixture-handmade-utf16.csv`):
```markdown
| col1 | col2 |
| --- | --- |
| naรฏve | cafรฉ |
| ฮฮธฮฎฮฝฮฑ | ๆฐๆฎ |
```
CSV has no content signature, so **`--format csv` is required when reading CSV
from stdin** (see [cli-reference.md](cli-reference.md)).
## PDF (`.pdf`) โ the lower-fidelity pipeline
Text-based PDFs convert **locally** through a separate pipeline (`pdf-inspector`)
that emits Markdown directly โ PDF has no document model, so only Markdown
output exists. Real conversion of `fixtures/fixture-text.pdf`:
```markdown
# Fixture Document
Plain paragraph with **bold**, *italic*, and struck runs. **Style-bold paragraph with a** NotBold-styled span **inside.**
## Lists
1.First numbered
2.Second numbered
a)Alpha sub one
b)Alpha sub two
i.Roman sub sub
3.Third numbered Interrupting paragraph between lists.
## Table
Wide head End Tall B2 C2 B3 C3
```
**Fidelity caveats (verified on the real output):**
- **No GFM tables.** Table cell text flattens into a plain paragraph run
(`Wide head End Tall B2 C2 B3 C3`) โ there is no `|` table.
- **No `[^n]` footnotes.** Footnote markers degrade to inline superscript
glyphs (`ยน`) and the note bodies drop into the flow; there is no `[^1]:`
definition block.
- **Links are not emitted as markdown links.** They degrade to `underlined
text`.
- Numbered/bulleted list structure compresses (markers inline), and some
Unicode degrades (e.g. emoji without ZWJ).
### Scanned or image-only PDFs โ explicit hosted OCR
A PDF with **no extractable text layer** fails as `unsupported` with this exact
message (exit code 1):
```
anydoc: unsupported input: PDF has no extractable text (Scanned, 1 pages): OCR is required
```
The local default does not perform OCR. When this message fires, report the
exact error and either route the file to local OCR tooling or, only after explicit
authorization, rerun with `--ocr hosted --allow-hosted-upload`. Hosted mode sends
the whole document to Firecrawl Parse because page selection is unavailable. Do
not select hosted mode implicitly or claim hosted accuracy from the upstream
announcement. See [errors.md](errors.md) for routing guidance.
## Formats anydoc does NOT support
- HTML/SingleFile (open feature request only) โ not an input format.
- Images (`.png`, `.jpg`, ...) โ no image-to-text conversion.
- Password-protected/encrypted documents โ fail with
`anydoc: document is encrypted` (see [errors.md](errors.md)).
- Anything without a recognized signature and extension โ fails as
`unsupported input: unrecognized file content and extension: `.
## Output-shape invariants to remember
1. One serializer: the same logical structure yields near-identical Markdown
across docx/odt/rtf โ do not re-test each office format for the same
feature.
2. Spreadsheets: expect `## ` + GFM tables; warn that xlsx/xls
drop number formats (issue #27) while ODS keeps display values.
3. Legacy `.ppt` and all PDFs lose tabular structure โ add a
"verify the table survived" step or use PPTX/ODP and text PDFs.
4. Images never survive as bytes in Markdown โ only alt text.