* craft-xlsx: read parts with canonical prefixes so kept trees can be written back The part layer read every part by local name, so extLst children (x14:, xr:, xm:), mc:AlternateContent and r:id lost their namespaces and could not be written back. Parts are now read with one fixed prefix per namespace (the main namespace unprefixed), mc:Ignorable and mc:Choice/@Requires are rewritten to match, and ns::declare prepares a kept tree for writing. craft-xml: a namespace listed with the empty prefix reads unprefixed. * craft-xlsx: write SpreadsheetML workbooks (Document read and write) Document holds a whole workbook: the workbook part, shared strings, styles, each worksheet's rows and the rest of its part, and every other part kept as bytes. write regenerates the workbook part, worksheets, shared strings and styles from the model, keeps unknown children in place (schema order for regenerated ones), keeps part names and relationship ids, and writes kept parts unchanged. Writers: Styles::to_xml and to_el for each table entry, StringItem::to_el (phonetic runs checked as on reading), SheetPart::to_xml with rows streamed, Workbook::to_el, XlsxKind. Row and DefinedName keep their other attributes; CellStyle reads iLevel. craft-opc: LazyPackage::canonical gives a part's stored name. * craft-xlsx: typed worksheet features and comments, written back SheetPart::features lifts sheet views, print setup, data validation, conditional formats, hyperlinks and the legacy drawing into types that mirror their elements (optional attributes, unnamed ones kept) and writes them in schema order. Comments come from the comments part, note shapes from the VML drawing; a sheet that gains comments without a VML drawing gets one. A hyperlink to a new URL gets its own relationship. craft-sheet: Range derives Default. * craft-xlsx: drawings, charts and the theme on craft-drawingml Drawing reads a sheet's xdr:wsDr into anchors with typed placements and objects kept as written; Anchor gives the object's kind, properties, chart or image relationship and xfrm. Package::drawing, Package::chart (c: and cx: parts through craft-drawingml) and Package::theme. Document worksheets hold their drawing part, regenerated with its relationships kept by id; add_chart and add_picture add new chart parts (chartEx behind mc:AlternateContent) and images with their anchors. More drawing namespaces get prefixes (a16, asvg, aink, am3d, cx1-cx4, dgm).
craft-xlsx
Excel workbooks: .xlsx, .xlsm, .xltx, .xltm (SpreadsheetML in an OPC package,
transitional and strict) and .xlsb (the same package with binary parts). Clean-room, from
ECMA-376 Part 1 (SpreadsheetML), [MS-XLSB] and observed files. No unsafe.
This is the part layer. Package::open opens the container through craft-opc and reads:
| Call | Gives |
|---|---|
workbook() |
Workbook: sheets in tab order (name, id, visibility, kind, part), the 1904 date flag, defined names, the active tab, and the whole part as a tree |
shared_strings() |
StringItems: text, formatted runs, phonetic readings |
styles() |
Styles: number formats, fonts, fills, borders, cell and cell-style formats, named styles, differential formats, the indexed palette, as indexed tables |
sheet(entry, each) |
each Row of Cells to a callback, then a SheetPart: the rest of the worksheet as a tree, its relationships, columns and merged cells |
opc(), read_xml(), optional_xml(), rels() |
any other part |
Every part is read with fixed prefixes (ns::CANONICAL): the SpreadsheetML main namespace
(transitional or strict) unprefixed, the others by ns::NAMESPACES (r:id, mc:AlternateContent,
x14:sparklineGroups, xm:sqref), and the prefixes in mc:Ignorable and mc:Choice/@Requires
rewritten to match. A tree kept from a part goes back out with ns::declare, which declares what
it uses. A name in a namespace that is not listed reads as ?:name and is not written.
A cell keeps the file's terms: a shared-string index, a format index, the formula text as the
file spells it, value and cell metadata indexes. CellValue::resolve gives the value it shows.
Shared formulas are not expanded and formulas are not parsed: that needs a formula grammar.
Writing
Document::read holds a whole SpreadsheetML workbook: the Workbook, shared strings, Styles,
each worksheet's Rows and SheetPart, and every other part as a KeptPart (bytes and content
type: drawings, charts, the theme, document properties, a VBA project, their relationships).
Document::write regenerates the workbook part (sheets, defined names, date system, active tab),
the worksheets (dimension, columns, merged cells, rows), the shared strings and the styles tables
from those fields, keeps the rest of each part's tree in place, and writes the kept parts as they
were. Part names and relationship ids are kept, so a file read and written without changes points
where it did, and writing it twice gives the same bytes. The calculation chain is dropped (Excel
rebuilds it). XlsxKind is the workbook part's content type (.xlsx, .xlsm, .xltx, .xltm).
Typed worksheet features (SheetPart::features), each mirroring its element with optional
attributes and the unnamed ones kept: sheet views (panes, selections), print setup (options,
margins, page setup, header and footer, page breaks), data validation (also read from the x14
extension), conditional formats (colour scales, data bars, icon sets, rule extensions kept),
hyperlinks (with their URLs), the legacy drawing. Comments (Comments, from the comments part)
and the note shapes of the VML drawing (NoteShape). A sheet that gains comments and has no VML
drawing gets one (notes_vml, in a shape block no kept drawing uses); an existing VML drawing is
kept as read. Extensions in extLst are carried through unchanged.
Drawings and charts, on craft-drawingml: Package::drawing reads a sheet's xdr:wsDr into a
Drawing of Anchors, each with a typed Placement (two-cell, one-cell, absolute, with EMU
offsets) and its object kept as written; Anchor names the object's kind, its cNvPr, its chart
or image relationship and its a:xfrm, and craft_drawingml::read reads its fill, line and text.
mc:AlternateContent items (Office 2016 charts, ink) are kept in place, and
Drawing::alternate_anchors reads the anchors inside them. Package::chart reads a chart part
(c: or cx:) into craft_drawingml::chart::Chart; Package::theme reads the theme. A
Document worksheet holds its DrawingPart (regenerated, its relationships kept by id);
Document::add_chart and Document::add_picture add a chart part written by craft-drawingml
(a chartEx part behind mc:AlternateContent for the Office 2016 types) or an image, with its
anchor. Chart parts and media are kept parts.
The parts this crate writes use the transitional namespaces; a strict file's kept parts stay as they were. A binary workbook is not written.
A binary workbook gives the same Workbook, strings, rows and merged cells, with saved values
only: its formulas are token streams that are not decoded, and its styles are not read (each
cell keeps its format index).
Hostile input
Limits.package: ZIP entries, bytes per part and in all (craft-opc).Limits.xml: one XML part. Rows and shared strings are streamed, so this bounds one row, one string and what is left of a part, not a sheet's length.Limits.binary_records,binary_cells,binary_text: a binary workbook, over all its parts (40 million records, 4 million cells and as many shared strings, 256 MiB of text, ascraft-xlsallows). Fixed caps: 1024 sheets, 100,000 merged ranges per sheet, 64 nested extension blocks.- Cells and rows outside the 1,048,576 × 16,384 grid are dropped and counted.
SpreadsheetML is read leniently: a damaged optional part is a warning, a damaged worksheet keeps the rows before the damage. A binary part that does not parse fails the read, since skipping a record would lose data without a trace.
Shared with craft-xls and craft-ods through craft-sheet: CellRef, Range, Value,
CellError, Visibility, sheet-name quoting, built-in number formats and the palette.