mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-15 06:52:23 +00:00
* Add craft-sheet: the cell positions, values and tables the spreadsheet formats share craft-xls carried its own CellRef, Range, Value, CellError, Visibility, sheet-name quoting, built-in number format table and indexed palette. craft-xlsx and craft-ods need the same ones, so they move to a crate with no dependencies and craft-xls re-exports them: an application gets one Value and one CellRef whichever file it read. Added on the way: A1 parsing (CellRef::parse, Range::parse), Range::count and a1, CellError::parse and the newer error values SpreadsheetML stores as text, builtin_format_id for writers. * Add craft-xlsx: the part layer of XLSX and XLSB workbooks Package::open finds the workbook part through craft-opc and reads the workbook part, shared strings (runs and phonetic readings), the styles tables as indexed tables, and a worksheet's rows one at a time with the rest of the part as a tree. Binary workbooks give the same workbook, strings, rows and merged cells from their records. Every other part stays reachable through Package::opc. From GridCraft crates/xlsx (7e0d96d7): read.rs, sheet_read.rs, styles.rs, xml.rs (string items, xstring escapes), xlsb.rs and its tests, split from GridCraft's document model. * craft-xlsx: XLSB opens up to 4 million cells, records counted once over the workbook Binary workbooks over 1 million cells were refused, a quarter of what .xls allows, and an allowance of 5 million records per part refused ordinary files: Excel writes four records per non-empty row besides the cells, and one BrtCellBlank for every formatted empty cell. Cells are now 4 million over the workbook (also the most shared strings), text 256 MiB, and records 40 million for every part together, as in craft-xls. Ported from storytold/gridcraft#3807. --------- Co-authored-by: kiwi4boy <kiwi4boy@gmail.com>
51 lines
2.4 KiB
Rust
51 lines
2.4 KiB
Rust
//! Small workbooks built in code, for tests and examples.
|
|
|
|
use craft_opc::{PackageOut, RelsOut};
|
|
|
|
const MAIN_NS: &str = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
|
const R_NS: &str = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
|
|
/// An `.xlsx` with the given sheets, each `(name, the rows inside <sheetData>)`, and shared
|
|
/// strings. Names and strings are written as given: escape them first if they need it.
|
|
pub fn workbook(sheets: &[(&str, &str)], strings: &[&str]) -> Vec<u8> {
|
|
let mut out = PackageOut::default();
|
|
let mut root = RelsOut::default();
|
|
root.add("officeDocument", "xl/workbook.xml", false);
|
|
out.rels("", &root);
|
|
let mut rels = RelsOut::default();
|
|
let mut list = String::new();
|
|
for (i, (name, rows)) in sheets.iter().enumerate() {
|
|
let n = i + 1;
|
|
let part = format!("xl/worksheets/sheet{n}.xml");
|
|
let id = rels.add("worksheet", &format!("worksheets/sheet{n}.xml"), false);
|
|
list.push_str(&format!(r#"<sheet name="{name}" sheetId="{n}" r:id="{id}"/>"#));
|
|
out.xml_part(
|
|
&part,
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
|
|
format!(r#"<worksheet xmlns="{MAIN_NS}"><sheetData>{rows}</sheetData></worksheet>"#).into_bytes(),
|
|
);
|
|
}
|
|
if !strings.is_empty() {
|
|
rels.add("sharedStrings", "sharedStrings.xml", false);
|
|
let items: String = strings.iter().map(|s| format!("<si><t>{s}</t></si>")).collect();
|
|
out.xml_part(
|
|
"xl/sharedStrings.xml",
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
|
|
format!(r#"<sst xmlns="{MAIN_NS}" count="{0}" uniqueCount="{0}">{items}</sst>"#, strings.len()).into_bytes(),
|
|
);
|
|
}
|
|
out.rels("xl/workbook.xml", &rels);
|
|
out.xml_part(
|
|
"xl/workbook.xml",
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
|
format!(r#"<workbook xmlns="{MAIN_NS}" xmlns:r="{R_NS}"><sheets>{list}</sheets></workbook>"#).into_bytes(),
|
|
);
|
|
out.finish().unwrap_or_default()
|
|
}
|
|
|
|
/// A ZIP archive of the given entries, stored as named (no content types are added).
|
|
pub fn zip(entries: &[(&str, &[u8])]) -> Vec<u8> {
|
|
let entries: Vec<(String, Vec<u8>)> = entries.iter().map(|(n, b)| (n.to_string(), b.to_vec())).collect();
|
|
craft_opc::zip_entries(&entries).unwrap_or_default()
|
|
}
|