forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			702 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			702 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | --- | ||
|  | sidebar_position: 7 | ||
|  | --- | ||
|  | 
 | ||
|  | # Spreadsheet Features
 | ||
|  | 
 | ||
|  | Even for basic features like date storage, the official Excel formats store the | ||
|  | same content in different ways.  The parsers are expected to convert from the | ||
|  | underlying file format representation to the Common Spreadsheet Format.  Writers | ||
|  | are expected to convert from CSF back to the underlying file format. | ||
|  | 
 | ||
|  | ## Formulae
 | ||
|  | 
 | ||
|  | The A1-style formula string is stored in the `f` field.  Even though different | ||
|  | file formats store the formulae in different ways, the formats are translated. | ||
|  | Even though some formats store formulae with a leading equal sign, CSF formulae | ||
|  | do not start with `=`. | ||
|  | 
 | ||
|  | <details> | ||
|  |   <summary><b>Formulae File Format Support</b> (click to show)</summary> | ||
|  | 
 | ||
|  | | Storage Representation | Formats                  | Read  | Write | | ||
|  | |:-----------------------|:-------------------------|:-----:|:-----:| | ||
|  | | A1-style strings       | XLSX                     |   ✔   |   ✔   | | ||
|  | | RC-style strings       | XLML and plain text      |   ✔   |   ✔   | | ||
|  | | BIFF Parsed formulae   | XLSB and all XLS formats |   ✔   |       | | ||
|  | | OpenFormula formulae   | ODS/FODS/UOS             |   ✔   |   ✔   | | ||
|  | | Lotus Parsed formulae  | All Lotus WK_ formats    |   ✔   |       | | ||
|  | 
 | ||
|  | Since Excel prohibits named cells from colliding with names of A1 or RC style | ||
|  | cell references, a (not-so-simple) regex conversion is possible.  BIFF Parsed | ||
|  | formulae and Lotus Parsed formulae have to be explicitly unwound.  OpenFormula | ||
|  | formulae can be converted with regular expressions. | ||
|  | 
 | ||
|  | Shared formulae are decompressed and each cell has the formula corresponding to | ||
|  | its cell.  Writers generally do not attempt to generate shared formulae. | ||
|  | </details> | ||
|  | 
 | ||
|  | ### Single-Cell Formulae
 | ||
|  | 
 | ||
|  | For simple formulae, the `f` key of the desired cell can be set to the actual | ||
|  | formula text.  This worksheet represents `A1=1`, `A2=2`, and `A3=A1+A2`: | ||
|  | 
 | ||
|  | ```js | ||
|  | var worksheet = { | ||
|  |   "!ref": "A1:A3", | ||
|  |   A1: { t:'n', v:1 }, | ||
|  |   A2: { t:'n', v:2 }, | ||
|  |   A3: { t:'n', v:3, f:'A1+A2' } | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | Utilities like `aoa_to_sheet` will accept cell objects in lieu of values: | ||
|  | 
 | ||
|  | ```js | ||
|  | var worksheet = XLSX.utils.aoa_to_sheet([ | ||
|  |   [ 1 ], // A1 | ||
|  |   [ 2 ], // A2 | ||
|  |   [ {t: "n", v: 3, f: "A1+A2"} ] // A3 | ||
|  | ]); | ||
|  | ``` | ||
|  | 
 | ||
|  | Cells with formula entries but no value will be serialized in a way that Excel | ||
|  | and other spreadsheet tools will recognize.  This library will not automatically | ||
|  | compute formula results!  For example, the following worksheet will include the | ||
|  | `BESSELJ` function but the result will not be available in JavaScript: | ||
|  | 
 | ||
|  | ```js | ||
|  | var worksheet = XLSX.utils.aoa_to_sheet([ | ||
|  |   [ 3.14159, 2 ], // Row "1" | ||
|  |   [ { t:'n', f:'BESSELJ(A1,B1)' } ] // Row "2" will be calculated on file open | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | If the actual results are needed in JS, [SheetJS Pro](https://sheetjs.com/pro) | ||
|  | offers a formula calculator component for evaluating expressions, updating | ||
|  | values and dependent cells, and refreshing entire workbooks. | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Array Formulae
 | ||
|  | 
 | ||
|  | _Assign an array formula_ | ||
|  | 
 | ||
|  | ```js | ||
|  | XLSX.utils.sheet_set_array_formula(worksheet, range, formula); | ||
|  | ``` | ||
|  | 
 | ||
|  | Array formulae are stored in the top-left cell of the array block.  All cells | ||
|  | of an array formula have a `F` field corresponding to the range.  A single-cell | ||
|  | formula can be distinguished from a plain formula by the presence of `F` field. | ||
|  | 
 | ||
|  | For example, setting the cell `C1` to the array formula `{=SUM(A1:A3*B1:B3)}`: | ||
|  | 
 | ||
|  | ```js | ||
|  | // API function | ||
|  | XLSX.utils.sheet_set_array_formula(worksheet, "C1", "SUM(A1:A3*B1:B3)"); | ||
|  | 
 | ||
|  | // ... OR raw operations | ||
|  | worksheet['C1'] = { t:'n', f: "SUM(A1:A3*B1:B3)", F:"C1:C1" }; | ||
|  | ``` | ||
|  | 
 | ||
|  | For a multi-cell array formula, every cell has the same array range but only the | ||
|  | first cell specifies the formula.  Consider `D1:D3=A1:A3*B1:B3`: | ||
|  | 
 | ||
|  | ```js | ||
|  | // API function | ||
|  | XLSX.utils.sheet_set_array_formula(worksheet, "D1:D3", "A1:A3*B1:B3"); | ||
|  | 
 | ||
|  | // ... OR raw operations | ||
|  | worksheet['D1'] = { t:'n', F:"D1:D3", f:"A1:A3*B1:B3" }; | ||
|  | worksheet['D2'] = { t:'n', F:"D1:D3" }; | ||
|  | worksheet['D3'] = { t:'n', F:"D1:D3" }; | ||
|  | ``` | ||
|  | 
 | ||
|  | Utilities and writers are expected to check for the presence of a `F` field and | ||
|  | ignore any possible formula element `f` in cells other than the starting cell. | ||
|  | They are not expected to perform validation of the formulae! | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Dynamic Arrays
 | ||
|  | 
 | ||
|  | _Assign a dynamic array formula_ | ||
|  | 
 | ||
|  | ```js | ||
|  | XLSX.utils.sheet_set_array_formula(worksheet, range, formula, true); | ||
|  | ``` | ||
|  | 
 | ||
|  | Released in 2020, Dynamic Array Formulae are supported in the XLSX/XLSM and XLSB | ||
|  | file formats.  They are represented like normal array formulae but have special | ||
|  | cell metadata indicating that the formula should be allowed to adjust the range. | ||
|  | 
 | ||
|  | An array formula can be marked as dynamic by setting the cell's `D` property to | ||
|  | true.  The `F` range is expected but can be the set to the current cell: | ||
|  | 
 | ||
|  | ```js | ||
|  | // API function | ||
|  | XLSX.utils.sheet_set_array_formula(worksheet, "C1", "_xlfn.UNIQUE(A1:A3)", 1); | ||
|  | 
 | ||
|  | // ... OR raw operations | ||
|  | worksheet['C1'] = { t: "s", f: "_xlfn.UNIQUE(A1:A3)", F:"C1", D: 1 }; // dynamic | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Localization
 | ||
|  | 
 | ||
|  | SheetJS operates at the file level.  Excel stores formula expressions using the | ||
|  | English (United States) function names.  For non-English users, Excel uses a | ||
|  | localized set of function names. | ||
|  | 
 | ||
|  | For example, when the computer language and region is set to French (France), | ||
|  | Excel interprets `=SOMME(A1:C3)` as if `SOMME` is the `SUM` function.  However, | ||
|  | in the actual file, Excel stores `SUM(A1:C3)`. | ||
|  | 
 | ||
|  | **Prefixed "Future Functions"** | ||
|  | 
 | ||
|  | Functions introduced in newer versions of Excel are prefixed with `_xlfn.` when | ||
|  | stored in files.  When writing formula expressions using these functions, the | ||
|  | prefix is required for maximal compatibility: | ||
|  | 
 | ||
|  | ```js | ||
|  | // Broadest compatibility | ||
|  | XLSX.utils.sheet_set_array_formula(worksheet, "C1", "_xlfn.UNIQUE(A1:A3)", 1); | ||
|  | 
 | ||
|  | // Can cause errors in spreadsheet software | ||
|  | XLSX.utils.sheet_set_array_formula(worksheet, "C1", "UNIQUE(A1:A3)", 1); | ||
|  | ``` | ||
|  | 
 | ||
|  | When reading a file, the `xlfn` option preserves the prefixes. | ||
|  | 
 | ||
|  | <details> | ||
|  |   <summary><b> Functions requiring `_xlfn.` prefix</b> (click to show)</summary> | ||
|  | 
 | ||
|  | This list is growing with each Excel release. | ||
|  | 
 | ||
|  | ``` | ||
|  | ACOT | ||
|  | ACOTH | ||
|  | AGGREGATE | ||
|  | ARABIC | ||
|  | BASE | ||
|  | BETA.DIST | ||
|  | BETA.INV | ||
|  | BINOM.DIST | ||
|  | BINOM.DIST.RANGE | ||
|  | BINOM.INV | ||
|  | BITAND | ||
|  | BITLSHIFT | ||
|  | BITOR | ||
|  | BITRSHIFT | ||
|  | BITXOR | ||
|  | BYCOL | ||
|  | BYROW | ||
|  | CEILING.MATH | ||
|  | CEILING.PRECISE | ||
|  | CHISQ.DIST | ||
|  | CHISQ.DIST.RT | ||
|  | CHISQ.INV | ||
|  | CHISQ.INV.RT | ||
|  | CHISQ.TEST | ||
|  | COMBINA | ||
|  | CONFIDENCE.NORM | ||
|  | CONFIDENCE.T | ||
|  | COT | ||
|  | COTH | ||
|  | COVARIANCE.P | ||
|  | COVARIANCE.S | ||
|  | CSC | ||
|  | CSCH | ||
|  | DAYS | ||
|  | DECIMAL | ||
|  | ERF.PRECISE | ||
|  | ERFC.PRECISE | ||
|  | EXPON.DIST | ||
|  | F.DIST | ||
|  | F.DIST.RT | ||
|  | F.INV | ||
|  | F.INV.RT | ||
|  | F.TEST | ||
|  | FIELDVALUE | ||
|  | FILTERXML | ||
|  | FLOOR.MATH | ||
|  | FLOOR.PRECISE | ||
|  | FORMULATEXT | ||
|  | GAMMA | ||
|  | GAMMA.DIST | ||
|  | GAMMA.INV | ||
|  | GAMMALN.PRECISE | ||
|  | GAUSS | ||
|  | HYPGEOM.DIST | ||
|  | IFNA | ||
|  | IMCOSH | ||
|  | IMCOT | ||
|  | IMCSC | ||
|  | IMCSCH | ||
|  | IMSEC | ||
|  | IMSECH | ||
|  | IMSINH | ||
|  | IMTAN | ||
|  | ISFORMULA | ||
|  | ISOMITTED | ||
|  | ISOWEEKNUM | ||
|  | LAMBDA | ||
|  | LET | ||
|  | LOGNORM.DIST | ||
|  | LOGNORM.INV | ||
|  | MAKEARRAY | ||
|  | MAP | ||
|  | MODE.MULT | ||
|  | MODE.SNGL | ||
|  | MUNIT | ||
|  | NEGBINOM.DIST | ||
|  | NORM.DIST | ||
|  | NORM.INV | ||
|  | NORM.S.DIST | ||
|  | NORM.S.INV | ||
|  | NUMBERVALUE | ||
|  | PDURATION | ||
|  | PERCENTILE.EXC | ||
|  | PERCENTILE.INC | ||
|  | PERCENTRANK.EXC | ||
|  | PERCENTRANK.INC | ||
|  | PERMUTATIONA | ||
|  | PHI | ||
|  | POISSON.DIST | ||
|  | QUARTILE.EXC | ||
|  | QUARTILE.INC | ||
|  | QUERYSTRING | ||
|  | RANDARRAY | ||
|  | RANK.AVG | ||
|  | RANK.EQ | ||
|  | REDUCE | ||
|  | RRI | ||
|  | SCAN | ||
|  | SEC | ||
|  | SECH | ||
|  | SEQUENCE | ||
|  | SHEET | ||
|  | SHEETS | ||
|  | SKEW.P | ||
|  | SORTBY | ||
|  | STDEV.P | ||
|  | STDEV.S | ||
|  | T.DIST | ||
|  | T.DIST.2T | ||
|  | T.DIST.RT | ||
|  | T.INV | ||
|  | T.INV.2T | ||
|  | T.TEST | ||
|  | UNICHAR | ||
|  | UNICODE | ||
|  | UNIQUE | ||
|  | VAR.P | ||
|  | VAR.S | ||
|  | WEBSERVICE | ||
|  | WEIBULL.DIST | ||
|  | XLOOKUP | ||
|  | XOR | ||
|  | Z.TEST | ||
|  | ``` | ||
|  | 
 | ||
|  | </details> | ||
|  | 
 | ||
|  | ## 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's "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's "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 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 MDW 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> | ||
|  | 
 | ||
|  | ## Number Formats
 | ||
|  | 
 | ||
|  | The `cell.w` formatted text for each cell is produced from `cell.v` and `cell.z` | ||
|  | format.  If the format is not specified, the Excel `General` format is used. | ||
|  | The format can either be specified as a string or as an index into the format | ||
|  | table.  Parsers are expected to populate `workbook.SSF` with the number format | ||
|  | table.  Writers are expected to serialize the table. | ||
|  | 
 | ||
|  | Custom tools should ensure that the local table has each used format string | ||
|  | somewhere in the table.  Excel convention mandates that the custom formats start | ||
|  | at index 164.  The following example creates a custom format from scratch: | ||
|  | 
 | ||
|  | <details> | ||
|  |   <summary><b>New worksheet with custom format</b> (click to show)</summary> | ||
|  | 
 | ||
|  | ```js | ||
|  | var wb = { | ||
|  |   SheetNames: ["Sheet1"], | ||
|  |   Sheets: { | ||
|  |     Sheet1: { | ||
|  |       "!ref":"A1:C1", | ||
|  |       A1: { t:"n", v:10000 },                    // <-- General format | ||
|  |       B1: { t:"n", v:10000, z: "0%" },           // <-- Builtin format | ||
|  |       C1: { t:"n", v:10000, z: "\"T\"\ #0.00" }  // <-- Custom format | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | ``` | ||
|  | </details> | ||
|  | 
 | ||
|  | The rules are slightly different from how Excel displays custom number formats. | ||
|  | In particular, literal characters must be wrapped in double quotes or preceded | ||
|  | by a backslash. For more info, see the Excel documentation article | ||
|  | `Create or delete a custom number format` or ECMA-376 18.8.31 (Number Formats) | ||
|  | 
 | ||
|  | 
 | ||
|  | <details> | ||
|  |   <summary><b>Default Number Formats</b> (click to show)</summary> | ||
|  | 
 | ||
|  | The default formats are listed in ECMA-376 18.8.30: | ||
|  | 
 | ||
|  | | ID | Format                     | | ||
|  | |---:|:---------------------------| | ||
|  | |  0 | `General`                  | | ||
|  | |  1 | `0`                        | | ||
|  | |  2 | `0.00`                     | | ||
|  | |  3 | `#,##0`                    | | ||
|  | |  4 | `#,##0.00`                 | | ||
|  | |  9 | `0%`                       | | ||
|  | | 10 | `0.00%`                    | | ||
|  | | 11 | `0.00E+00`                 | | ||
|  | | 12 | `# ?/?`                    | | ||
|  | | 13 | `# ??/??`                  | | ||
|  | | 14 | `m/d/yy` (see below)       | | ||
|  | | 15 | `d-mmm-yy`                 | | ||
|  | | 16 | `d-mmm`                    | | ||
|  | | 17 | `mmm-yy`                   | | ||
|  | | 18 | `h:mm AM/PM`               | | ||
|  | | 19 | `h:mm:ss AM/PM`            | | ||
|  | | 20 | `h:mm`                     | | ||
|  | | 21 | `h:mm:ss`                  | | ||
|  | | 22 | `m/d/yy h:mm`              | | ||
|  | | 37 | `#,##0 ;(#,##0)`           | | ||
|  | | 38 | `#,##0 ;[Red](#,##0)`      | | ||
|  | | 39 | `#,##0.00;(#,##0.00)`      | | ||
|  | | 40 | `#,##0.00;[Red](#,##0.00)` | | ||
|  | | 45 | `mm:ss`                    | | ||
|  | | 46 | `[h]:mm:ss`                | | ||
|  | | 47 | `mmss.0`                   | | ||
|  | | 48 | `##0.0E+0`                 | | ||
|  | | 49 | `@`                        | | ||
|  | 
 | ||
|  | </details> | ||
|  | 
 | ||
|  | Format 14 (`m/d/yy`) is localized by Excel: even though the file specifies that | ||
|  | number format, it will be drawn differently based on system settings.  It makes | ||
|  | sense when the producer and consumer of files are in the same locale, but that | ||
|  | is not always the case over the Internet.  To get around this ambiguity, parse | ||
|  | functions accept the `dateNF` option to override the interpretation of that | ||
|  | specific format string. | ||
|  | 
 | ||
|  | ## Hyperlinks
 | ||
|  | 
 | ||
|  | <details> | ||
|  |   <summary><b>Format Support</b> (click to show)</summary> | ||
|  | 
 | ||
|  | **Cell Hyperlinks**: XLSX/M, XLSB, BIFF8 XLS, XLML, ODS | ||
|  | 
 | ||
|  | **Tooltips**: XLSX/M, XLSB, BIFF8 XLS, XLML | ||
|  | 
 | ||
|  | </details> | ||
|  | 
 | ||
|  | Hyperlinks are stored in the `l` key of cell objects.  The `Target` field of the | ||
|  | hyperlink object is the target of the link, including the URI fragment. Tooltips | ||
|  | are stored in the `Tooltip` field and are displayed when you move your mouse | ||
|  | over the text. | ||
|  | 
 | ||
|  | For example, the following snippet creates a link from cell `A3` to | ||
|  | <https://sheetjs.com> with the tip `"Find us @ SheetJS.com!"`: | ||
|  | 
 | ||
|  | ```js | ||
|  | ws['A1'].l = { Target:"https://sheetjs.com", Tooltip:"Find us @ SheetJS.com!" }; | ||
|  | ``` | ||
|  | 
 | ||
|  | Note that Excel does not automatically style hyperlinks -- they will generally | ||
|  | be displayed as normal text. | ||
|  | 
 | ||
|  | _Remote Links_ | ||
|  | 
 | ||
|  | HTTP / HTTPS links can be used directly: | ||
|  | 
 | ||
|  | ```js | ||
|  | ws['A2'].l = { Target:"https://docs.sheetjs.com/#hyperlinks" }; | ||
|  | ws['A3'].l = { Target:"http://localhost:7262/yes_localhost_works" }; | ||
|  | ``` | ||
|  | 
 | ||
|  | Excel also supports `mailto` email links with subject line: | ||
|  | 
 | ||
|  | ```js | ||
|  | ws['A4'].l = { Target:"mailto:ignored@dev.null" }; | ||
|  | ws['A5'].l = { Target:"mailto:ignored@dev.null?subject=Test Subject" }; | ||
|  | ``` | ||
|  | 
 | ||
|  | _Local Links_ | ||
|  | 
 | ||
|  | Links to absolute paths should use the `file://` URI scheme: | ||
|  | 
 | ||
|  | ```js | ||
|  | ws['B1'].l = { Target:"file:///SheetJS/t.xlsx" }; /* Link to /SheetJS/t.xlsx */ | ||
|  | ws['B2'].l = { Target:"file:///c:/SheetJS.xlsx" }; /* Link to c:\SheetJS.xlsx */ | ||
|  | ``` | ||
|  | 
 | ||
|  | Links to relative paths can be specified without a scheme: | ||
|  | 
 | ||
|  | ```js | ||
|  | ws['B3'].l = { Target:"SheetJS.xlsb" }; /* Link to SheetJS.xlsb */ | ||
|  | ws['B4'].l = { Target:"../SheetJS.xlsm" }; /* Link to ../SheetJS.xlsm */ | ||
|  | ``` | ||
|  | 
 | ||
|  | Relative Paths have undefined behavior in the SpreadsheetML 2003 format.  Excel | ||
|  | 2019 will treat a `..\` parent mark as two levels up. | ||
|  | 
 | ||
|  | _Internal Links_ | ||
|  | 
 | ||
|  | Links where the target is a cell or range or defined name in the same workbook | ||
|  | ("Internal Links") are marked with a leading hash character: | ||
|  | 
 | ||
|  | ```js | ||
|  | ws['C1'].l = { Target:"#E2" }; /* Link to cell E2 */ | ||
|  | ws['C2'].l = { Target:"#Sheet2!E2" }; /* Link to cell E2 in sheet Sheet2 */ | ||
|  | ws['C3'].l = { Target:"#SomeDefinedName" }; /* Link to Defined Name */ | ||
|  | ``` | ||
|  | 
 | ||
|  | ## Cell Comments
 | ||
|  | 
 | ||
|  | Cell comments are objects stored in the `c` array of cell objects.  The actual | ||
|  | contents of the comment are split into blocks based on the comment author.  The | ||
|  | `a` field of each comment object is the author of the comment and the `t` field | ||
|  | is the plain text representation. | ||
|  | 
 | ||
|  | For example, the following snippet appends a cell comment into cell `A1`: | ||
|  | 
 | ||
|  | ```js | ||
|  | if(!ws.A1.c) ws.A1.c = []; | ||
|  | ws.A1.c.push({a:"SheetJS", t:"I'm a little comment, short and stout!"}); | ||
|  | ``` | ||
|  | 
 | ||
|  | Note: XLSB enforces a 54 character limit on the Author name.  Names longer than | ||
|  | 54 characters may cause issues with other formats. | ||
|  | 
 | ||
|  | To mark a comment as normally hidden, set the `hidden` property: | ||
|  | 
 | ||
|  | ```js | ||
|  | if(!ws.A1.c) ws.A1.c = []; | ||
|  | ws.A1.c.push({a:"SheetJS", t:"This comment is visible"}); | ||
|  | 
 | ||
|  | if(!ws.A2.c) ws.A2.c = []; | ||
|  | ws.A2.c.hidden = true; | ||
|  | ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"}); | ||
|  | ``` | ||
|  | 
 | ||
|  | 
 | ||
|  | _Threaded Comments_ | ||
|  | 
 | ||
|  | Introduced in Excel 365, threaded comments are plain text comment snippets with | ||
|  | author metadata and parent references. They are supported in XLSX and XLSB. | ||
|  | 
 | ||
|  | To mark a comment as threaded, each comment part must have a true `T` property: | ||
|  | 
 | ||
|  | ```js | ||
|  | if(!ws.A1.c) ws.A1.c = []; | ||
|  | ws.A1.c.push({a:"SheetJS", t:"This is not threaded"}); | ||
|  | 
 | ||
|  | if(!ws.A2.c) ws.A2.c = []; | ||
|  | ws.A2.c.hidden = true; | ||
|  | ws.A2.c.push({a:"SheetJS", t:"This is threaded", T: true}); | ||
|  | ws.A2.c.push({a:"JSSheet", t:"This is also threaded", T: true}); | ||
|  | ``` | ||
|  | 
 | ||
|  | There is no Active Directory or Office 365 metadata associated with authors in a thread. | ||
|  | 
 | ||
|  | ## Sheet Visibility
 | ||
|  | 
 | ||
|  | Excel enables hiding sheets in the lower tab bar.  The sheet data is stored in | ||
|  | the file but the UI does not readily make it available.  Standard hidden sheets | ||
|  | are revealed in the "Unhide" menu.  Excel also has "very hidden" sheets which | ||
|  | cannot be revealed in the menu.  It is only accessible in the VB Editor! | ||
|  | 
 | ||
|  | The visibility setting is stored in the `Hidden` property of sheet props array. | ||
|  | 
 | ||
|  | <details> | ||
|  |   <summary><b>More details</b> (click to show)</summary> | ||
|  | 
 | ||
|  | | Value | Definition  | | ||
|  | |:-----:|:------------| | ||
|  | |   0   | Visible     | | ||
|  | |   1   | Hidden      | | ||
|  | |   2   | Very Hidden | | ||
|  | 
 | ||
|  | With <https://rawgit.com/SheetJS/test_files/HEAD/sheet_visibility.xlsx>: | ||
|  | 
 | ||
|  | ```js | ||
|  | > wb.Workbook.Sheets.map(function(x) { return [x.name, x.Hidden] })
 | ||
|  | [ [ 'Visible', 0 ], [ 'Hidden', 1 ], [ 'VeryHidden', 2 ] ] | ||
|  | ``` | ||
|  | 
 | ||
|  | Non-Excel formats do not support the Very Hidden state.  The best way to test | ||
|  | if a sheet is visible is to check if the `Hidden` property is logical truth: | ||
|  | 
 | ||
|  | ```js | ||
|  | > wb.Workbook.Sheets.map(function(x) { return [x.name, !x.Hidden] })
 | ||
|  | [ [ 'Visible', true ], [ 'Hidden', false ], [ 'VeryHidden', false ] ] | ||
|  | ``` | ||
|  | </details> | ||
|  | 
 | ||
|  | ## VBA and Macros
 | ||
|  | 
 | ||
|  | VBA Macros are stored in a special data blob that is exposed in the `vbaraw` | ||
|  | property of the workbook object when the `bookVBA` option is `true`.  They are | ||
|  | supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats.  The supported format | ||
|  | writers automatically insert the data blobs if it is present in the workbook and | ||
|  | associate with the worksheet names. | ||
|  | 
 | ||
|  | <details> | ||
|  | 	<summary><b>Custom Code Names</b> (click to show)</summary> | ||
|  | 
 | ||
|  | The workbook code name is stored in `wb.Workbook.WBProps.CodeName`.  By default, | ||
|  | Excel will write `ThisWorkbook` or a translated phrase like `DieseArbeitsmappe`. | ||
|  | Worksheet and Chartsheet code names are in the worksheet properties object at | ||
|  | `wb.Workbook.Sheets[i].CodeName`.  Macrosheets and Dialogsheets are ignored. | ||
|  | 
 | ||
|  | The readers and writers preserve the code names, but they have to be manually | ||
|  | set when adding a VBA blob to a different workbook. | ||
|  | 
 | ||
|  | </details> | ||
|  | 
 | ||
|  | <details> | ||
|  | 	<summary><b>Macrosheets</b> (click to show)</summary> | ||
|  | 
 | ||
|  | Older versions of Excel also supported a non-VBA "macrosheet" sheet type that | ||
|  | stored automation commands.  These are exposed in objects with the `!type` | ||
|  | property set to `"macro"`. | ||
|  | 
 | ||
|  | </details> | ||
|  | 
 | ||
|  | <details> | ||
|  | 	<summary><b>Detecting macros in workbooks</b> (click to show)</summary> | ||
|  | 
 | ||
|  | The `vbaraw` field will only be set if macros are present, so testing is simple: | ||
|  | 
 | ||
|  | ```js | ||
|  | function wb_has_macro(wb/*:workbook*/)/*:boolean*/ { | ||
|  | 	if(!!wb.vbaraw) return true; | ||
|  | 	const sheets = wb.SheetNames.map((n) => wb.Sheets[n]); | ||
|  | 	return sheets.some((ws) => !!ws && ws['!type']=='macro'); | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | </details> | ||
|  | 
 |