Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

### Fixed

- Spreadsheets: a sheet with a single row now renders that row as data under Excel column letters
(`| A | B |`) instead of as a header with no rows.
- Spreadsheets: `Extraction.title` is filled from document properties (XLSX `docProps/core.xml`,
ODS `meta.xml`, XLS `SummaryInformation`).
- `Format::from_content_type` detects XLSB (`application/vnd.ms-excel.sheet.binary.macroEnabled.12`)
as the new `Format::Xlsb` instead of `Xlsx`; `.xlsb` files and `xl/workbook.bin` containers too.
- XLSX packages missing `_rels/.rels` no longer fail inside calamine.
- Release builds use `panic = "unwind"`, so a parser panic becomes an error instead of aborting the
CLI or the host Python/Node process.
- Indented HTML (most real web pages) now keeps blank lines between blocks; previously
headings, paragraphs, lists and tables after the first ran together with a leading space
(`# A\n B` instead of `# A\n\nB`), which also broke headings and tables as markdown.
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ proptest = "1"
[profile.release]
lto = "thin"
codegen-units = 1
panic = "abort"
# Unwind (not abort): `extract_document` wraps third-party parsers in
# `catch_unwind`, and PyO3/NAPI turn panics into exceptions. Abort would let one
# malformed document kill the CLI or the host Python/Node process.
panic = "unwind"
strip = true

# Browser bundle (crates/markkit-wasm/scripts/build.sh): size over speed.
Expand Down
16 changes: 11 additions & 5 deletions crates/markkit/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ use crate::ir::Document;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[non_exhaustive]
pub struct Extraction {
/// Rendered Markdown.
/// Rendered Markdown. Non-empty output always ends with exactly one `\n`
/// (POSIX text convention, safe to concatenate); empty documents render `""`.
pub markdown: String,
/// Document title when known (HTML `og:title` / JSON-LD / `<title>`;
/// DOCX core properties or `Title` style).
/// Document title when known: HTML `og:title` / JSON-LD / `<title>`; DOCX
/// core properties or `Title` style; XLSX `docProps/core.xml`, ODS
/// `meta.xml`, XLS `SummaryInformation`.
pub title: Option<String>,
/// Format the document was parsed as.
pub format: Format,
Expand Down Expand Up @@ -118,7 +120,8 @@ impl ParsedDocument {
}

/// Run a parser, converting a panic into an error (defence in depth for
/// third-party parsers; only effective when built with `panic = "unwind"`).
/// third-party parsers; only effective when built with `panic = "unwind"`, which
/// markkit's own release profile uses).
#[cfg(any(feature = "docx", feature = "spreadsheet"))]
fn guarded<T>(
what: &str,
Expand Down Expand Up @@ -266,7 +269,9 @@ impl Markkit {
Format::Html => self.parse_html_doc(bytes),
Format::Pdf => self.parse_pdf(bytes),
Format::Docx => self.parse_docx(bytes),
Format::Xlsx | Format::Xls | Format::Ods => self.parse_spreadsheet(bytes),
Format::Xlsx | Format::Xlsb | Format::Xls | Format::Ods => {
self.parse_spreadsheet(bytes)
}
Format::Doc => Err(Error::UnsupportedFormat(
"legacy Word 97-2003 (.doc) documents are not supported".into(),
)),
Expand Down Expand Up @@ -328,6 +333,7 @@ impl Markkit {
let mut doc = out.doc;
self.config.transforms.apply(&mut doc)?;
let mut p = ParsedDocument::new(doc, out.format);
p.title = out.title;
p.warnings = out.warnings;
p.stats.sheets = Some(out.sheets);
p.stats.rows = Some(out.rows);
Expand Down
21 changes: 21 additions & 0 deletions crates/markkit/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub enum Format {
Doc,
/// Office Open XML spreadsheet (`.xlsx`, `.xlsm`, `.xltx`).
Xlsx,
/// Excel binary workbook (`.xlsb`).
Xlsb,
/// Legacy Excel 97-2003 binary (`.xls`).
Xls,
/// OpenDocument spreadsheet (`.ods`).
Expand Down Expand Up @@ -76,6 +78,7 @@ impl Format {
Format::Docx => "docx",
Format::Doc => "doc",
Format::Xlsx => "xlsx",
Format::Xlsb => "xlsb",
Format::Xls => "xls",
Format::Ods => "ods",
Format::Pptx => "pptx",
Expand All @@ -98,6 +101,8 @@ impl Format {
Format::Docx
} else if ct.contains("msword") {
Format::Doc
} else if ct.contains("ms-excel.sheet.binary") {
Format::Xlsb
} else if ct.contains("spreadsheetml") || ct.contains("ms-excel.sheet") {
Format::Xlsx
} else if ct.contains("ms-excel") {
Expand Down Expand Up @@ -125,6 +130,7 @@ impl Format {
"docx" | "docm" | "dotx" | "dotm" => Format::Docx,
"doc" | "dot" => Format::Doc,
"xlsx" | "xlsm" | "xltx" | "xltm" => Format::Xlsx,
"xlsb" => Format::Xlsb,
"xls" | "xlt" => Format::Xls,
"ods" => Format::Ods,
"pptx" | "pptm" | "ppt" => Format::Pptx,
Expand Down Expand Up @@ -198,6 +204,8 @@ fn sniff_zip(bytes: &[u8]) -> Format {
Format::Ods
} else if contains(bytes, b"word/document") {
Format::Docx
} else if contains(bytes, b"xl/workbook.bin") {
Format::Xlsb
} else if contains(bytes, b"xl/workbook.") {
Format::Xlsx
} else if contains(bytes, b"ppt/presentation.") {
Expand Down Expand Up @@ -245,13 +253,26 @@ mod tests {
);
assert_eq!(Format::from_content_type("application/pdf"), Format::Pdf);
assert_eq!(Format::from_content_type("text/plain"), Format::PlainText);
assert_eq!(
Format::from_content_type("application/vnd.ms-excel.sheet.binary.macroEnabled.12"),
Format::Xlsb
);
assert_eq!(
Format::from_content_type("application/vnd.ms-excel.sheet.macroEnabled.12"),
Format::Xlsx
);
assert_eq!(
Format::from_content_type("application/vnd.ms-excel"),
Format::Xls
);
}

#[test]
fn test_extension_detection() {
assert_eq!(Format::from_extension("html"), Format::Html);
assert_eq!(Format::from_extension("PDF"), Format::Pdf);
assert_eq!(Format::from_extension("docx"), Format::Docx);
assert_eq!(Format::from_extension("XLSB"), Format::Xlsb);
}

#[test]
Expand Down
3 changes: 2 additions & 1 deletion crates/markkit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ mod spreadsheet;
#[cfg(feature = "tool")]
pub mod tool;
pub mod transform;
#[cfg(feature = "docx")]
#[cfg(any(feature = "docx", feature = "spreadsheet"))]
#[cfg_attr(not(feature = "docx"), allow(dead_code))] // spreadsheet uses only `find`/`text`
mod xml;

pub use config::MarkkitBuilder;
Expand Down
Loading
Loading