craft-xlsx: pictures in cells and dynamic-array metadata * 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: tables (ListObjects) read and written back Table mirrors CT_Table: every attribute, columns with calculated and totals formulas, the style info, autoFilter/sortState/extensions kept as written. Package::tables reads a sheet's table parts; Document worksheets hold them as TableParts, regenerated with their relationships and tableParts entries. Part names a document keeps are reserved before new parts are named, so a new part never takes a name a later sheet keeps. * craft-xlsx: PivotTables (caches, records, tables) read and written Typed pivot cache definitions (source, fields, shared items, grouping), streamed cache records and PivotTable definitions (location, fields and items, axes, page and data fields, style), with every unmodelled child and attribute kept. Workbook::pivot_caches lifts pivotCaches. A Document holds caches and tables and regenerates their parts, relationships and pivotCaches entries; an OLAP cache or a table on one stays a kept part. SharedItems::describe gives Excel's shared-item flags and records_xml writes a records part, after GridCraft's pivot writer. * craft-xlsx: pictures in cells and dynamic-array metadata Package::cell_pictures follows a cell's vm through the value and future metadata to its _localImage rich value and image, bounded and lenient as GridCraft's richdata.rs reads it. Document::add_cell_metadata writes the metadata part (XLDAPR for dynamic arrays, XLRICHVALUE for pictures), the rich-value parts and the images as GridCraft writes them.
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.
Tables: Package::tables reads a sheet's table parts (in tableParts order) into Table
(every CT_Table attribute, TableColumns with calculated and totals formulas,
TableStyleInfo; autoFilter, sortState and extensions kept as written). A Document
worksheet holds them as TableParts, regenerated on writing with their relationships and
tableParts entries; a removed table takes both with it. Every part name a document keeps is
reserved before new parts are named, so a new part never takes a name another sheet's part
keeps.
PivotTables: Workbook::pivot_caches lists the caches by id; Package::pivot_cache reads a cache
definition (PivotCacheDefinition: source, fields with shared items and grouping),
Package::pivot_records streams its records, and Package::pivot_tables reads a sheet's
PivotTables (PivotTableDefinition: location, fields and items, axes, page and data fields,
style) with the cache each uses. SharedItems::describe gives the flags Excel writes for a field's
values, and records_xml writes a records part. A Document holds caches as PivotCacheParts
and a worksheet's tables as PivotTableParts, regenerated with their relationships and
pivotCaches entries; an OLAP cache (and a table on one) stays a kept part.
Cell metadata: Package::cell_pictures (and Document::cell_pictures) follows a cell's vm
through the value and future metadata to its _localImage rich value and image, as
CellPicture (PNG or JPEG, alternative text). Every table is bounded (MAX_RECORDS, MAX_PICTURE,
MAX_PICTURES_TOTAL); what does not resolve is None with a warning, and the cell keeps its saved
value. Document::add_cell_metadata writes the metadata part, the rich-value parts and the images
for new pictures (vm = i + 1) and the XLDAPR dynamic-array metadata (cm = 1).
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.