Skip to content

feat(formats): add ODT input - #54

Merged
chaliy merged 23 commits into
mainfrom
claude/project-thread-3z7tjs
Oct 27, 2026
Merged

chaliy merged 23 commits into
mainfrom
claude/project-thread-3z7tjs

Conversation

@chaliy

@chaliy chaliy commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

What changed

New odt feature (in all_formats, so CLI and Python/Node bindings get it) converts OpenDocument text (.odt, .ott, sniffed from the ODF mimetype entry like ODS) to Markdown:

  • Headings come from text:h outline levels and from paragraph styles with a default outline level. A Title style becomes # and the title, unless meta.xml has a dc:title.
  • Bold, italic and strike come from text properties through the style inheritance chain; adjacent equal runs are merged.
  • Also mapped: links, ordered and bulleted lists by list-level style, and tables. Repeated rows and columns are capped by max_sheet_rows / max_sheet_cols, and the padding producers add is dropped.
  • Notes, annotations, tracked changes, frames and the table of contents are skipped.
  • Hidden content (S20) is skipped unless include_hidden_content(true). That covers text styled text:display="none", hidden sections, text:hidden-text and text:hidden-paragraph, and it emits a hidden_content_skipped warning.
  • New Format::Odt ("odt") and Error::OdtExtract.

It reuses DOCX's bounded ZIP and XML readers and shares the call's Limits budget (nodes, timeout, depth, output cap). Text stays raw in the IR and the renderer escapes it.

Why

This is part of the cheap-formats roadmap initiative (text/Markdown, CSV/TSV, ipynb and EML are already merged).

Before / After

$ markkit extract --file a.odt     # h1 "Report", "Plain " + bold span + a display:none span
# before
Error: Unsupported format: ...
# after
# Report

Plain **bold**

The hidden "IGNORE PREVIOUS INSTRUCTIONS" span is dropped. I ran tests locally under all features, the default build, --no-default-features, docx,spreadsheet,markdown,email,odt and odt alone; clippy passes with -D warnings. CI now tests the no-HTTP feature set with odt added.

Risk

  • Low: the feature is additive and opt-in; the default build is unchanged.
  • It shares archive/xml with DOCX/PPTX. The cfg lists were widened, and no reader logic changed.

Checklist

  • Tests added or updated: 18 tests in odt_integration. Security cases cover a wrong package, zip bomb, entity expansion, deep nesting, repeat counts, style cycles, truncated archives, hidden content on and off, limits (max_nodes, timeout) and markup escaping.
  • Backward compatibility considered: the change is additive. T6 and T9 cover ODT, and S20 lists it. Knowledge, CHANGELOG and README are updated.

Requested by Михайло · project thread


Generated by Claude Code

chaliy added 22 commits October 26, 2026 23:35
Plain text (always on) goes through the IR: paragraphs on blank lines,
hard breaks on single newlines. Markdown (new `markdown` feature, in
all_formats) is re-parsed with pulldown-cmark into the IR and rendered
back, so raw HTML never passes through. Both decode UTF-8/UTF-16 BOMs,
drop control characters and warn on lossy decoding. Neither is sniffed
from bytes. Threat model T7, invariant S13.
…-3z7tjs

# Conflicts:
#	CHANGELOG.md
#	README.md
#	crates/markkit/Cargo.toml
#	crates/markkit/src/extract.rs
#	crates/markkit/src/lib.rs
#	knowledge/log.md
#	knowledge/security/threat-model.md
…-3z7tjs

# Conflicts:
#	.github/workflows/ci.yml
#	crates/markkit/Cargo.toml
#	crates/markkit/src/extract.rs
#	crates/markkit/src/lib.rs
#	knowledge/log.md
#	knowledge/security/threat-model.md
…-3z7tjs

# Conflicts:
#	knowledge/log.md
#	knowledge/security/threat-model.md
Always-on RFC 4180 reader renders delimited text as one GFM table.
Delimiter is tab for TSV and detected among , ; tab | for CSV. Reading
is bounded by max_sheet_rows / max_sheet_cols (truncated_table warning);
unterminated quotes emit malformed_csv. Adds Format::Tsv.
…-3z7tjs

# Conflicts:
#	CHANGELOG.md
#	README.md
#	crates/markkit/src/extract.rs
#	knowledge/formats/format-support.md
#	knowledge/log.md
#	knowledge/security/threat-model.md
nbformat 4 notebooks render Markdown cells through the Markdown path,
code cells as fenced blocks tagged with the kernel language, and text
outputs and tracebacks (ANSI stripped) as text blocks; rich outputs are
skipped. Adds Format::Notebook, Error::NotebookExtract and
ExtractionStats::cells.
With no format feature, extract_document rendered unbounded, so always-on
text, CSV and notebook input ignored max_output_size and the timeout
(csv_integration::security_timeout_applies failed under
--no-default-features). Bounded rendering is now unconditional, decoding is
followed by a deadline check, and CI tests --no-default-features.
# Conflicts:
#	crates/markkit/src/extract.rs
#	crates/markkit/src/format.rs
#	crates/markkit/src/lib.rs
#	crates/markkit/src/limits.rs
#	crates/markkit/src/render/markdown.rs
#	knowledge/log.md
#	knowledge/operations/testing.md
New email feature (mail-parser 0.11) renders subject, From/To/Cc/Date,
the plain-text body (HTML alternative through the HTML parser when no
plain part exists) and an attachment list. Header values are folded so
encoded newlines cannot inject lines; attachments are never parsed.
Adds Format::Eml, Error::EmailExtract, ExtractionStats::attachments.
…-3z7tjs

# Conflicts:
#	crates/markkit-js/src/lib.rs
#	crates/markkit/src/extract.rs
#	knowledge/formats/format-support.md
New odt feature reuses the bounded ZIP and XML readers from DOCX:
headings (outline level, styled, Title), bold/italic/strike from text
properties with run merging, links, ordered/bulleted lists, tables with
repeat counts capped by the sheet limits. Notes, annotations, tracked
changes and frames are skipped. Adds Format::Odt (sniffed from the ODF
mimetype) and Error::OdtExtract.
@chaliy chaliy self-assigned this Oct 27, 2026
@chaliy
chaliy merged commit 9c6fe7f into main Oct 27, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant