This commit is contained in:
SheetJS 2023-09-20 17:53:18 -04:00
parent 7a1b75d50b
commit 8e39ab8f33
12 changed files with 789 additions and 131 deletions

@ -37,7 +37,7 @@ The ["Demo"](#demo) creates an app that looks like the screenshots below:
</td></tr></tbody></table>
### Integration Details
## Integration Details
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
from the main entrypoint or any script in the project.
@ -91,7 +91,7 @@ async function updateFile(v) { try {
} catch(e) { console.log(e); } }
```
#### Writing data
### Writing data
Starting from an array of objects, the SheetJS `json_to_sheet` method[^5]
generates a SheetJS worksheet object. The `book_append_sheet` and `book_new`
@ -141,7 +141,7 @@ window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
});
```
### Demo
## Demo
:::note

@ -179,14 +179,11 @@ ws["!margins"]={left:0.25,right:0.25,top:0.75,bottom:0.75,header:0.3,footer:0.3}
In addition to the aforementioned sheet keys, worksheets also add:
- `ws['!cols']`: array of column properties objects. Column widths are actually
stored in files in a normalized manner, measured in terms of the "Maximum
Digit Width" (the largest width of the rendered digits 0-9, in pixels). When
parsed, the column objects store the pixel width in the `wpx` field, character
width in the `wch` field, and the maximum digit width in the `MDW` field.
- `ws['!cols']`: [array of column objects](/docs/csf/features/colprops).
Each column object encodes properties including level, width and visibility.
- `ws['!rows']`: array of row properties objects as explained later in the docs.
Each row object encodes properties including row height and visibility.
- `ws['!rows']`: [array of row objects](/docs/csf/features/rowprops).
Each row object encodes properties including level, height and visibility.
- `ws['!merges']`: array of range objects corresponding to the merged cells in
the worksheet. Plain text formats do not support merge cells. CSV export

@ -0,0 +1,361 @@
---
title: Row Properties
sidebar_position: 8
---
<details>
<summary><b>File Format Support</b> (click to show)</summary>
By default, all rows in a workbook are "Visible" and have a standard height.
| Formats | Height | Hidden Rows | Outline Level |
|:-----------------|:------:|:-----------:|:-------------:|
| XLSX/XLSM | ✔ | ✔ | ✔ |
| XLSB | ✔ | ✔ | ✔ |
| XLML | ✔ | ✔ | ✕ |
| BIFF8 XLS | R | R | R |
| BIFF5 XLS | R | R | R |
| SYLK | ✔ | * | ✕ |
| ODS / FODS / UOS | + | + | + |
Asterisks (*) mark formats that represent hidden rows with zero height. For
example, there is no way to specify a custom row height and mark that the row is
hidden in the SYLK format.
Plus (+) marks formats with limited support. ODS supports specifying row heights
in many units of measure. SheetJS supports some but not all ODS units.
X (✕) marks features that are not supported by the file formats. For example,
the SpreadsheetML 2003 (XLML) file format does not support outline levels.
</details>
Many spreadsheet tools support adjusting row heights to accommodate multiple
lines of data or varying text sizes.
Some tools additionally support row grouping or "outlining". Excel displays row
outline levels to the left of the grid.
SheetJS worksheet objects store row properties in the `!rows` field. It is
expected to be an array of row metadata objects.
## Demo
This example creates a workbook that includes custom row heights, hidden rows,
and row outline levels.
<table><thead><tr>
<th>Excel for Windows</th>
<th>Excel for Mac</th>
</tr></thead><tbody><tr><td>
![Excel for Windows](pathname:///rowprops/win.png)
</td><td>
![Excel for Mac](pathname:///rowprops/mac.png)
</td></tr></tbody></table>
<details><summary><b>Export Demo</b> (click to show)</summary>
The table lists the assigned heights, outline levels and visibility settings.
```jsx live
function SheetJSRowProps() {
const [ws, setWS] = React.useState();
const [__html, setHTML] = React.useState("");
const fmt = React.useRef(null);
/* when the page is loaded, create worksheet and show table */
React.useEffect(() => {
/* Create worksheet from simple data */
const data = [
{ Height: 20, Unit: "px", Level: 0 }, { Height: 25, Unit: "pt", Level: 1 },
{ Height: 30, Unit: "px", Level: 2 }, { Height: 35, Unit: "pt", Level: 3 },
{ Height: 25, Unit: "pt", Level: 3 }, { Height: 15, Unit: "px", Level: 1 },
{ Height: 10, Unit: "pt", Level: 0 }, { Hidden: true }
];
const ws = XLSX.utils.json_to_sheet(data);
/* set row metadata */
ws["!rows"] = [];
data.forEach((row, i) => {
const r = {};
if(row.Level) (ws["!rows"][i+1] = r).level = row.Level;
if(row.Unit == "px") (ws["!rows"][i+1] = r).hpx = row.Height || 0;
if(row.Unit == "pt") (ws["!rows"][i+1] = r).hpt = row.Height || 0;
if(row.Hidden) (ws["!rows"][i+1] = r).hidden = true;
});
/* save worksheet object for the export */
setWS(ws);
/* generate the HTML table */
setHTML(XLSX.utils.sheet_to_html(ws));
}, []);
const xport = (fmt) => {
/* Export to file (start a download) */
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Formats");
XLSX.writeFile(wb, `SheetJSRowProps.${fmt}`, {cellStyles: true});
};
const fmts = ["xlsx", "xlsb", "xls", "slk", "ods"];
return ( <>
<b>File format: </b>
<select ref={fmt}>{fmts.map(f=>(<option value={f}>{f}</option>))}</select>
<br/><button onClick={()=>xport(fmt.current.value)}><b>Export!</b></button>
<div dangerouslySetInnerHTML={{__html}}/>
</> );
}
```
</details>
## Functions
:::caution pass
**Row processing must be explicitly enabled!**
:::
Functions creating worksheet objects are not guaranteed to generate the `!rows`
array. Writers are not guaranteed to export row metadata.
#### Reading Files
[`read` and `readFile`](/docs/api/parse-options) accept an options argument. The
`cellStyles` option must be set to `true` to generate row properties:
```js
var wb = XLSX.read(data, {/* ... other options , */ cellStyles: true});
```
#### Writing Files
[`write` and `writeFile`](/docs/api/write-options) accept an options argument.
The `cellStyles` option must be set to `true` to export row properties:
```js
XLSX.writeFile(wb, "SheetJSRowProps.xlsx", {/* ...opts , */ cellStyles: true});
```
#### Importing HTML Tables
[`table_to_book` and `table_to_sheet`](/docs/api/utilities/html#html-table-input)
process HTML DOM TABLE elements.
Individual table rows (`TR` elements) can be marked as hidden by setting the CSS
`display` property to `none`.
By default, hidden rows are imported and appropriately marked as hidden:
```js
/* generate worksheet from first table, preserving hidden rows */
var tbl = document.getElementsByTagName("TABLE")[0];
var ws = XLSX.utils.table_to_sheet(tbl);
```
If the `display` option is set to `true`, hidden rows will be skipped:
```js
/* generate worksheet from first table, omitting hidden rows */
var tbl = document.getElementsByTagName("TABLE")[0];
var ws = XLSX.utils.table_to_sheet(tbl, {display: true})
```
#### Exporting Data
[`sheet_to_csv`](/docs/api/utilities/csv#delimiter-separated-output) and
[`sheet_to_json`](/docs/api/utilities/array#array-output) accept options. If the
`skipHidden` option is set to true, hidden rows will not be exported:
```js
var ws = wb.Sheets[wb.SheetNames[0]]; // first worksheet
var csv = XLSX.utils.sheet_to_csv(ws, {/* ...opts, */ skipHidden: true});
```
## Storage
The `!rows` property in a sheet object stores row-level metadata. If present, it
is expected to be an array of row objects.
:::info pass
As explained in ["Addresses and Ranges"](/docs/csf/general#rows), SheetJS uses
zero-indexed rows. The row metadata for Excel row 20 is stored at index 19 of
the `!rows` array.
:::
When performing operations, it is strongly recommended to test for the existence
of the row structure.
This snippet checks the `!rows` array and the specific row object, creating them
if they do not exist, before setting the `hidden` property of the third row:
```js
/* Excel third row -> SheetJS row index 3 - 1 = 2 */
var ROW_INDEX = 2;
/* create !rows array if it does not exist */
if(!ws["!rows"]) ws["!rows"] = [];
/* create row metadata object if it does not exist */
if(!ws["!rows"][ROW_INDEX]) ws["!rows"][ROW_INDEX] = {hpx: 20};
/* set row to hidden */
ws["!rows"][ROW_INDEX].hidden = true;
```
### Row Heights
Row heights can be specified in two ways:
| Property | Description |
|:---------|:------------------------|
| `hpx` | Height in screen pixels |
| `hpt` | Height in points |
The following snippet sets the height of the third row to 50 pixels:
```js
const ROW_HEIGHT = 50;
/* Excel third row -> SheetJS row index 3 - 1 = 2 */
const ROW_INDEX = 2;
/* create !rows array if it does not exist */
if(!ws["!rows"]) ws["!rows"] = [];
/* create row metadata object if it does not exist */
if(!ws["!rows"][ROW_INDEX]) ws["!rows"][ROW_INDEX] = {hpx: ROW_HEIGHT};
/* set row height */
ws["!rows"][ROW_INDEX].hpx = ROW_HEIGHT;
```
### Row Visibility
The `hidden` property controls visibility.
The following snippet hides the fourth row:
```js
/* Excel fourth row -> SheetJS row index 4 - 1 = 3 */
var ROW_INDEX = 3;
/* create !rows array if it does not exist */
if(!ws["!rows"]) ws["!rows"] = [];
/* create row metadata object if it does not exist */
if(!ws["!rows"][ROW_INDEX]) ws["!rows"][ROW_INDEX] = {hpx: 20};
/* set row to hidden */
ws["!rows"][ROW_INDEX].hidden = true;
```
### Outline Levels
The `level` property controls outline level / grouping. It is expected to be a
number between `0` and `7` inclusive.
:::note pass
The Excel UI displays outline levels next to the column labels. The base level
shown in the application is `1`.
SheetJS is zero-indexed: the default (base) level is `0`.
:::
The following snippet sets the level of the sixth row to Excel 2 / SheetJS 1:
```js
/* Excel level 2 -> SheetJS level 2 - 1 = 1 */
var LEVEL = 1;
/* Excel sixth row -> SheetJS row index 6 - 1 = 5 */
var ROW_INDEX = 2;
/* create !rows array if it does not exist */
if(!ws["!rows"]) ws["!rows"] = [];
/* create row metadata object if it does not exist */
if(!ws["!rows"][ROW_INDEX]) ws["!rows"][ROW_INDEX] = {hpx: 20};
/* set level */
ws["!rows"][ROW_INDEX].level = LEVEL;
```
### Grouping Rows
Applications treat consecutive rows with the same level as part of a "group".
The "Group" command typically increments the level of each row in the range:
```js
/* start_row and end_row are SheetJS 0-indexed row indices */
function gruppieren(ws, start_row, end_row) {
/* create !rows array if it does not exist */
if(!ws["!rows"]) ws["!rows"] = [];
/* loop over every row index */
for(var i = start_row; i <= end_row; ++i) {
/* create row metadata object if it does not exist */
if(!ws["!rows"][i]) ws["!rows"][i] = {hpx: 20};
/* increment level */
ws["!rows"][i].level = 1 + (ws["!rows"][i].level || 0);
}
}
```
The "Ungroup" command typically decrements the level of each row in the range:
```js
/* start_row and end_row are SheetJS 0-indexed row indices */
function dissocier(ws, start_row, end_row) {
/* create !rows array if it does not exist */
if(!ws["!rows"]) ws["!rows"] = [];
/* loop over every row index */
for(var i = start_row; i <= end_row; ++i) {
/* if row metadata does not exist, the level is zero -> skip */
if(!ws["!rows"][i]) continue;
/* if row level is not specified, the level is zero -> skip */
if(!ws["!rows"][i].level) continue;
/* decrement level */
--ws["!rows"][i].level;
}
}
```
#### Grouping Symbol
By default, Excel displays the group collapse button on the row after the data.
In the UI, this is adjusted by the option "Summary rows below detail".
SheetJS exposes this option in the `above` property of the `"!outline"` property
of worksheet objects. Setting this property to `true` effectively "unchecks" the
"Summary rows below detail" option in Excel:
```js
if(!ws["outline"]) ws["!outline"] = {};
ws["!outline"].above = true; // show summary rows above detail
```
## Implementation Details
<details><summary><b>Details</b> (click to show)</summary>
Excel internally stores row heights in points. The default resolution is 72 DPI
or 96 PPI, so the pixel and point size should agree. For different resolutions
they may not agree, so the library separates the concepts.
Even though all of the information is made available, writers are expected to
follow the priority order:
1) use `hpx` pixel height if available
2) use `hpt` point height if available
</details>

@ -0,0 +1,418 @@
---
title: Column Properties
sidebar_position: 9
---
<details>
<summary><b>File Format Support</b> (click to show)</summary>
By default, all columns in a workbook are "Visible" and have a standard width.
| Formats | Width | Hidden Cols | Outline Level |
|:-----------------|:-----:|:-----------:|:-------------:|
| XLSX/XLSM | ✔ | ✔ | ✔ |
| XLSB | ✔ | ✔ | ✔ |
| XLML | ✔ | ✔ | ✕ |
| BIFF8 XLS | ✔ | ✔ | ✔ |
| BIFF5 XLS | R | R | R |
| SYLK | ✔ | * | ✕ |
Asterisks (*) mark formats that represent hidden columns with zero width. For
example, there is no way to specify a custom column width and mark the column as
hidden in the SYLK format.
X (✕) marks features that are not supported by the file formats. For example,
the SpreadsheetML 2003 (XLML) file format does not support outline levels.
</details>
Many spreadsheet tools support adjusting column widths to accommodate longer
formatted data or varying text sizes.
Some tools additionally support column grouping or "outlining". Excel displays
outline levels above the grid.
SheetJS worksheet objects store column properties in the `!cols` field. It is
expected to be an array of column metadata objects.
:::warning Excel Bugs
For most common formats (XLSX, XLS), widths are tied to font metrics, which are
tied to Windows Scaling settings. In Windows 11, the Scale factor settings are
found in "System" > "Display" > "Scale"
**Column widths may appear different on other machines due to scaling.**
**This is an issue with Excel.**
:::
## Demo
This example creates a workbook that includes custom column widths, hidden
columns, and column outline levels.
<table><thead><tr>
<th>Excel for Windows</th>
<th>Excel for Mac</th>
</tr></thead><tbody><tr><td>
![Excel for Windows](pathname:///colprops/win.png)
</td><td>
![Excel for Mac](pathname:///colprops/mac.png)
</td></tr></tbody></table>
<details><summary><b>Export Demo</b> (click to show)</summary>
The table lists the assigned widths, outline levels and visibility settings.
```jsx live
function SheetJColProps() {
const [ws, setWS] = React.useState();
const [__html, setHTML] = React.useState("");
const fmt = React.useRef(null);
/* when the page is loaded, create worksheet and show table */
React.useEffect(() => {
/* Create worksheet from simple data */
const data = [
[ "Width" , 10, 20, 30, 40, 50, 20, 20, ],
[ "Level" , 0, 1, 2, 3, 3, 1, 0, ],
[ "Hidden" , 0, 0, 0, 0, 0, 0, 0, 1 ]
];
const ws = XLSX.utils.aoa_to_sheet(data);
/* set column metadata */
ws["!cols"] = [];
for(let i = 1; i <= 8; ++i) {
const r = {};
if(data[0][i] != null) (ws["!cols"][i] = r).wpx = data[0][i];
if(data[1][i] != null) (ws["!cols"][i] = r).level = data[1][i];
if(data[2][i] != null) (ws["!cols"][i] = r).hidden = data[2][i];
}
/* save worksheet object for the export */
setWS(ws);
/* generate the HTML table */
setHTML(XLSX.utils.sheet_to_html(ws));
}, []);
const xport = (fmt) => {
/* Export to file (start a download) */
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Formats");
XLSX.writeFile(wb, `SheetJSColProps.${fmt}`, {cellStyles: true});
};
const fmts = ["xlsx", "xlsb", "xls", "slk"];
return ( <>
<b>File format: </b>
<select ref={fmt}>{fmts.map(f=>(<option value={f}>{f}</option>))}</select>
<br/><button onClick={()=>xport(fmt.current.value)}><b>Export!</b></button>
<div dangerouslySetInnerHTML={{__html}}/>
</> );
}
```
</details>
## Functions
:::caution pass
**Column processing must be explicitly enabled!**
:::
Functions creating worksheet objects are not guaranteed to generate the `!cols`
array. Writers are not guaranteed to export column metadata.
#### Reading Files
[`read` and `readFile`](/docs/api/parse-options) accept an options argument. The
`cellStyles` option must be set to `true` to generate column properties:
```js
var wb = XLSX.read(data, {/* ... other options , */ cellStyles: true});
```
#### Writing Files
[`write` and `writeFile`](/docs/api/write-options) accept an options argument.
The `cellStyles` option must be set to `true` to export column properties:
```js
XLSX.writeFile(wb, "SheetSColProps.xlsx", {/* ...opts , */ cellStyles: true});
```
#### Exporting Data
[`sheet_to_csv`](/docs/api/utilities/csv#delimiter-separated-output) and
[`sheet_to_json`](/docs/api/utilities/array#array-output) accept options. If the
`skipHidden` option is set to true, hidden columns will not be exported:
```js
var ws = wb.Sheets[wb.SheetNames[0]]; // first worksheet
var csv = XLSX.utils.sheet_to_csv(ws, {/* ...opts, */ skipHidden: true});
```
## Storage
The `!cols` property in a sheet object stores column-level metadata. If present,
it is expected to be an array of column objects.
:::info pass
As explained in ["Addresses and Ranges"](/docs/csf/general#columns), SheetJS uses
zero-indexed columns. The column metadata for Excel column "T" is stored at index
19 of the `!cols` array.
:::
When performing operations, it is strongly recommended to test for the existence
of the column structure.
This snippet checks the `!cols` array and the specific column object, creating
them if they do not exist, before setting the `hidden` property of column "C":
```js
/* Excel column "C" -> SheetJS column index 2 == XLSX.utils.decode_col("C") */
var COL_INDEX = 2;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set column to hidden */
ws["!cols"][COL_INDEX].hidden = true;
```
### Column Widths
Column widths can be specified in three ways:
| Property | Description | Excel UI |
|:---------|:------------------------|:---------|
| `wpx` | Width in screen pixels | Pixels |
| `wch` | "inner width" in MDW ** | Width |
| `width` | "outer width" in MDW ** | |
:::note pass
When resizing a column, Excel will show a tooltip:
![Resize tooltip](pathname:///colprops/xlwidth.png)
`wpx` stores the "pixels" field (`65` in the diagram) for certain computer and
font settings.
:::
<details><summary><b>MDW (Max Digit Width)</b> (click to show)</summary>
**`MDW`**
"MDW" stands for "Max Digit Width", the maximum width of the numeric characters
(`0`, `1`, ..., `9`) using the first font specified in the file. For most common
fonts and text scaling settings, this is the width of `0` measured in pixels.
Parsers will save the estimated pixel width of the `0` digit to the `MDW`
property of the column object. It is always a positive integer.
**`width`**
`width` is the distance from "gridline before the current column" to "gridline
before the next column" divided by MDW and rounded to the nearest `1/256`.
**`wch`**
Table cells in Excel include 2 pixels of padding on each side. The vertical
gridline is one pixel wide. In total, the `width` includes 5 pixels of padding.
`wch` is the "inner width", calculated by subtracting the 5 pixels from `width`.
`wch` is also measured in MDW units rounded to the nearest `1/256`.
**Diagram**
The following diagram depicts the Excel box model and the relationship between
`width`, `wpx`, `MDW` and the displayed grid:
![Box diagram](pathname:///colprops/xlbox.png)
The distance between the two red lines is `width * MDW = 15` pixels. That span
includes one gridline width (1 pixel) and two padding blocks (2 pixels each).
The space available for content is `wch * MDW = 15 - 5 = 10` pixels.
</details>
The following snippet sets the width of column "C" to 50 pixels:
```js
const COL_WIDTH = 50;
/* Excel column "C" -> SheetJS column index 2 == XLSX.utils.decode_col("C") */
var COL_INDEX = 2;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set column width */
ws["!cols"][COL_INDEX].wpx = COL_WIDTH;
```
### Column Visibility
The `hidden` property controls visibility.
The following snippet hides column "D":
```js
/* Excel column "D" -> SheetJS column index 3 == XLSX.utils.decode_col("D") */
var COL_INDEX = 3;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set column to hidden */
ws["!cols"][COL_INDEX].hidden = true;
```
### Outline Levels
The `level` property controls outline level / grouping. It is expected to be a
number between `0` and `7` inclusive.
:::note pass
The Excel UI displays outline levels above the row labels. The base level
shown in the application is `1`.
SheetJS is zero-indexed: the default (base) level is `0`.
:::
The following snippet sets the level of column "F" to Excel 2 / SheetJS 1:
```js
/* Excel level 2 -> SheetJS level 2 - 1 = 1 */
var LEVEL = 1;
/* Excel column "F" -> SheetJS column index 5 == XLSX.utils.decode_col("F") */
var COL_INDEX = 5;
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* create column metadata object if it does not exist */
if(!ws["!cols"][COL_INDEX]) ws["!cols"][COL_INDEX] = {wch: 8};
/* set level */
ws["!cols"][COL_INDEX].level = LEVEL;
```
### Grouping Columns
Applications treat consecutive columns with the same level as part of a "group".
The "Group" command typically increments the level of each column in the range:
```js
/* start_col and end_col are SheetJS 0-indexed column indices */
function grouper(ws, start_col, end_col) {
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* loop over every column index */
for(var i = start_col; i <= end_col; ++i) {
/* create column metadata object if it does not exist */
if(!ws["!cols"][i]) ws["!cols"][i] = {wch: 8};
/* increment level */
ws["!cols"][i].level = 1 + (ws["!cols"][i].level || 0);
}
}
```
The "Ungroup" command typically decrements the level of each column in the range:
```js
/* start_col and end_col are SheetJS 0-indexed column indices */
function aufheben(ws, start_col, end_col) {
/* create !cols array if it does not exist */
if(!ws["!cols"]) ws["!cols"] = [];
/* loop over every column index */
for(var i = start_col; i <= end_col; ++i) {
/* if column metadata does not exist, the level is zero -> skip */
if(!ws["!cols"][i]) continue;
/* if column level is not specified, the level is zero -> skip */
if(!ws["!cols"][i].level) continue;
/* decrement level */
--ws["!cols"][i].level;
}
}
```
#### Grouping Symbol
By default, Excel displays the group collapse button on the column after the
data. In the UI, this option is named "Summary columns to right of detail".
SheetJS exposes this option in the `left` property of the `"!outline"` property
of worksheet objects. Setting this property to `true` effectively "unchecks" the
"Summary columns to right of detail" option in Excel:
```js
if(!ws["outline"]) ws["!outline"] = {};
ws["!outline"].left = true; // show summary to left of detail
```
## Implementation Details
<details><summary><b>Details</b> (click to show)</summary>
**Three Width Types**
There are three different width types corresponding to the three different ways
spreadsheets store column widths:
SYLK and other plain text formats use raw character count. Contemporaneous tools
like Visicalc and Multiplan were character based. Since the characters had the
same width, it sufficed to store a count. This tradition was continued into the
BIFF formats.
SpreadsheetML (2003) tried to align with HTML by standardizing on screen pixel
count throughout the file. Column widths, row heights, and other measures use
pixels. When the pixel and character counts do not align, Excel rounds values.
XLSX internally stores column widths in a nebulous "Max Digit Width" form. The
Max Digit Width is the width of the largest digit when rendered (generally the
"0" character is the widest). The internal width must be an integer multiple of
the width divided by 256. ECMA-376 describes a formula for converting between
pixels and the internal width. This represents a hybrid approach.
Read functions attempt to populate all three properties. Write functions will
try to cycle specified values to the desired type. In order to avoid potential
conflicts, manipulation should delete the other properties first. For example,
when changing the pixel width, delete the `wch` and `width` properties.
**Column Width Priority**
Even though all of the information is made available, writers are expected to
follow the priority order:
1) use `width` field if available
2) use `wpx` pixel width if available
3) use `wch` character count if available
</details>

@ -17,124 +17,3 @@ The following topics are covered in sub-pages:
<a href={item.href}>{item.label}</a>{cP?.summary && (" - " + cP.summary)}
</li> );
})}</ul>
## Row and Column Properties
<details>
<summary><b>Format Support</b> (click to show)</summary>
**Row Properties**: XLSX/M, XLSB, BIFF8 XLS, XLML, SYLK, DOM, ODS
**Column Properties**: XLSX/M, XLSB, BIFF8 XLS, XLML, SYLK, DOM
</details>
Row and Column properties are not extracted by default when reading from a file
and are not persisted by default when writing to a file. The option
`cellStyles: true` must be passed to the relevant read or write function.
_Column Properties_
The `!cols` array in each worksheet, if present, is a collection of `ColInfo`
objects which have the following properties:
```typescript
type ColInfo = {
/* visibility */
hidden?: boolean; // if true, the column is hidden
/* column width is specified in one of the following ways: */
wpx?: number; // width in screen pixels
width?: number; // width in Excel "Max Digit Width", width*256 is integral
wch?: number; // width in characters
/* other fields for preserving features from files */
level?: number; // 0-indexed outline / group level
MDW?: number; // Excel "Max Digit Width" unit, always integral
};
```
_Row Properties_
The `!rows` array in each worksheet, if present, is a collection of `RowInfo`
objects which have the following properties:
```typescript
type RowInfo = {
/* visibility */
hidden?: boolean; // if true, the row is hidden
/* row height is specified in one of the following ways: */
hpx?: number; // height in screen pixels
hpt?: number; // height in points
level?: number; // 0-indexed outline / group level
};
```
_Outline / Group Levels Convention_
The Excel UI displays the base outline level as `1` and the max level as `8`.
Following JS conventions, SheetJS uses 0-indexed outline levels wherein the base
outline level is `0` and the max level is `7`.
<details>
<summary><b>Why are there three width types?</b> (click to show)</summary>
There are three different width types corresponding to the three different ways
spreadsheets store column widths:
SYLK and other plain text formats use raw character count. Contemporaneous tools
like Visicalc and Multiplan were character based. Since the characters had the
same width, it sufficed to store a count. This tradition was continued into the
BIFF formats.
SpreadsheetML (2003) tried to align with HTML by standardizing on screen pixel
count throughout the file. Column widths, row heights, and other measures use
pixels. When the pixel and character counts do not align, Excel rounds values.
XLSX internally stores column widths in a nebulous "Max Digit Width" form. The
Max Digit Width is the width of the largest digit when rendered (generally the
"0" character is the widest). The internal width must be an integer multiple of
the width divided by 256. ECMA-376 describes a formula for converting between
pixels and the internal width. This represents a hybrid approach.
Read functions attempt to populate all three properties. Write functions will
try to cycle specified values to the desired type. In order to avoid potential
conflicts, manipulation should delete the other properties first. For example,
when changing the pixel width, delete the `wch` and `width` properties.
</details>
<details>
<summary><b>Implementation details</b> (click to show)</summary>
_Row Heights_
Excel internally stores row heights in points. The default resolution is 72 DPI
or 96 PPI, so the pixel and point size should agree. For different resolutions
they may not agree, so the library separates the concepts.
Even though all of the information is made available, writers are expected to
follow the priority order:
1) use `hpx` pixel height if available
2) use `hpt` point height if available
_Column Widths_
Given the constraints, it is possible to determine the `MDW` without actually
inspecting the font! The parsers guess the pixel width by converting from width
to pixels and back, repeating for all possible `MDW` and selecting the value
that minimizes the error. XLML actually stores the pixel width, so the guess
works in the opposite direction.
Even though all of the information is made available, writers are expected to
follow the priority order:
1) use `width` field if available
2) use `wpx` pixel width if available
3) use `wch` character count if available
</details>

@ -141,6 +141,7 @@ The function takes an options argument:
|`sheetStubs` | false | Create cell objects of type `z` for `null` values |
|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
|`UTC` | false | If true, dates are interpreted using UTC methods ** |
|`dense` | false | Emit [dense sheet object](docs/csf/sheet#dense-mode) |
[UTC option is explained in "Dates"](/docs/csf/features/dates#utc-option)
@ -279,6 +280,7 @@ default column order is determined by the first appearance of the field using
|`skipHeader` | false | If true, do not include header row in output |
|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
|`UTC` | false | If true, dates are interpreted using UTC methods ** |
|`dense` | false | Emit [dense sheet object](docs/csf/sheet#dense-mode) |
[UTC option is explained in "Dates"](/docs/csf/features/dates#utc-option)
@ -524,6 +526,7 @@ an options argument:
|`dateNF` | FMT 14 | Use specified date format in string output |
|`defval` | | Use specified value in place of null or undefined |
|`blankrows` | ** | Include blank lines in the output ** |
|`skipHidden` | false | Do not generate objects for hidden rows/columns |
|`UTC` | false | If true, dates will be correct in UTC ** |
- `raw` only affects cells which have a format code (`.z`) field or a formatted

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB