From 75697ac27ec3fdc8c78d76572fb7a502675bac84 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Tue, 23 May 2023 14:53:11 -0400 Subject: [PATCH] vba --- docz/docs/07-csf/04-book.md | 2 +- docz/docs/07-csf/07-features/07-vba.md | 285 +++++++++++++++++++++++++ docz/docs/07-csf/07-features/index.md | 68 ------ docz/docusaurus.config.js | 2 +- docz/static/vba/SheetJSVBAFormula.xlsm | Bin 0 -> 14790 bytes docz/static/vba/vbaProject.bin | Bin 0 -> 16384 bytes 6 files changed, 287 insertions(+), 70 deletions(-) create mode 100644 docz/docs/07-csf/07-features/07-vba.md create mode 100644 docz/static/vba/SheetJSVBAFormula.xlsm create mode 100644 docz/static/vba/vbaProject.bin diff --git a/docz/docs/07-csf/04-book.md b/docz/docs/07-csf/04-book.md index c7d3125..095d7a0 100644 --- a/docz/docs/07-csf/04-book.md +++ b/docz/docs/07-csf/04-book.md @@ -85,6 +85,6 @@ discussed in more detail in ["Defined Names"](/docs/csf/features/names) | Key | Description | |:----------------|:-----------------------------------------------------------| -| `CodeName` | [VBA Workbook Name](/docs/csf/features#vba-and-macros) | +| `CodeName` | [VBA Workbook Name](/docs/csf/features/vba) | | `date1904` | epoch: 0/false for 1900 system, 1/true for 1904 | | `filterPrivacy` | Warn or strip personally identifying info on save | diff --git a/docz/docs/07-csf/07-features/07-vba.md b/docz/docs/07-csf/07-features/07-vba.md new file mode 100644 index 0000000..d7178a1 --- /dev/null +++ b/docz/docs/07-csf/07-features/07-vba.md @@ -0,0 +1,285 @@ +--- +sidebar_position: 7 +--- + +import current from '/version.js'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; + +# VBA and Macros + +
+ File Format Support (click to show) + +Note that XLSX does not support macros. The XLSM file format is nearly +identical to XLSX and supports macros. + +| Formats | Basic | Storage Representation | +|:--------|:-----:|:-----------------------------------| +| XLSM | ✔ | `vbaProject.bin` file in container | +| XLSX | * | Not supported in format (use XLSM) | +| XLSB | ✔ | `vbaProject.bin` file in container | +| XLS | ✔ | Intercalated in CFB container | + +Asterisks (*) mark features that are not supported by the file formats. There is +no way to embed VBA in the XLSX format. + +
+ +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. + +:::note + +The `vbaraw` property stores raw bytes. [SheetJS Pro](https://sheetjs.com/pro) +offers a special component for extracting macro text from the VBA blob, editing +the VBA project, and exporting new VBA blobs. + +::: + +## Demos + +The export demos focus on [an example](pathname:///vba/SheetJSVBAFormula.xlsm) +that includes the following user-defined functions: + +```vb +Function GetFormulaA1(Cell As Range) As String + GetFormulaA1 = Cell.Formula +End Function + +Function GetFormulaRC(Cell As Range) As String + GetFormulaRC = Cell.Formula2R1C1 +End Function +``` + + +### Copying Macros + +After downloading the sample file, the demo extracts the VBA blob and creates +a new workbook including the VBA blob. Click the button to create the file and +open in a spreadsheet editor that supports VBA: + + + + +```jsx live +function SheetJSVBAFormula() { return ( ); } +``` + + + + +0) Install the dependencies: + +{`\ +npm init -y +npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} + + +1) Save the following script to `generate_file.js`: + +```js title="generate_file.js" +const XLSX = require("xlsx"); +(async() => { +/* Extract VBA Blob from test file */ +const url = "https://docs.sheetjs.com/vba/SheetJSVBAFormula.xlsm"; +const raw_data = await (await fetch(url)).arrayBuffer(); +const blob = XLSX.read(raw_data, {bookVBA: true}).vbaraw; + +/* generate worksheet and workbook */ +const worksheet = XLSX.utils.aoa_to_sheet([ + ["Cell", "A1", "RC"], + [ + {t:"n", f:"LEN(A1)"}, // A2 + {t:"s", f:"GetFormulaA1(A2)"}, // B2 + {t:"s", f:"GetFormulaRC(A2)"} // C2 + ] +]); +const workbook = XLSX.utils.book_new(); +XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1"); + +/* add VBA blob to new workbook */ +workbook.vbaraw = blob; + +/* create an XLSM file and try to save to SheetJSVBANeu.xlsm */ +XLSX.writeFile(workbook, "SheetJSVBANeu.xlsm", { bookVBA: true }); +})(); +``` + +2) Run the script: + +```bash +node generate_file.js +``` + +This script will generate `SheetJSVBANeu.xlsm`. + + + + +### Extracting VBA Blobs + +To obtain the blob, `bookVBA: 1` must be set in the `read` or `readFile` call. + +The following example extracts the embedded VBA blob in a workbook: + + + + +```jsx live +function SheetJSExtractVBA(props) { + const [msg, setMsg] = React.useState("Select a macro-enabled file"); + return ( <> + {msg}
+ { + /* parse workbook with bookVBA: true */ + const wb = XLSX.read(await e.target.files[0].arrayBuffer(), {bookVBA: true}); + + /* get vba blob */ + if(!wb.vbaraw) return setMsg("No VBA found!"); + const blob = wb.vbaraw; + + /* download to vbaProject.bin */ + setMsg("Attempting to download vbaProject.bin"); + const url = URL.createObjectURL(new Blob([blob])); + const a = document.createElement("a"); + a.download = "vbaProject.bin"; a.href = url; + document.body.appendChild(a); a.click(); + document.body.removeChild(a); + }}/> + ); +} +``` + +
+ + +0) Install the dependencies: + +{`\ +npm init -y +npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} + + +1) Save the following script to `extract_vba.js`: + +```js title="extract_vba.js" +const fs = require("fs"), XLSX = require("xlsx"); +const wb = XLSX.readFile(process.argv[2], { bookVBA: true }); +if(!wb.vbaraw) throw new Error("Could not find VBA blob!"); +fs.writeFileSync("vbaProject.bin", wb.vbaraw); +``` + +2) Run the script: + +```bash +node extract_vba.js SheetJSMacroEnabled.xlsm +``` + +This script will generate `vbaProject.bin`. It can be added to a new workbook. + + +
+ +### Exporting Blobs + +To ensure the writers export the VBA blob: + +- The output format must support VBA (`xlsm` or `xlsb` or `xls` or `biff8`) +- The workbook object must have a valid `vbaraw` field +- The `write` or `writeFile` call must include the option `bookVBA: true` + +This example uses [`vbaProject.bin`](pathname:///vba/vbaProject.bin) from the +[sample file](pathname:///vba/vbaProject.bin): + +```jsx live +function SheetJSVBAPrepared() { return ( ); } +``` + +## Details + +### Code Names + +Excel will use `ThisWorkbook` (or a translation like `DieseArbeitsmappe`) as the +default Code Name for the workbook. Each worksheet will be identified using the +default `Sheet#` naming pattern even if the worksheet names have changed. + +A custom workbook code name will be stored in `wb.Workbook.WBProps.CodeName`. +For exports, assigning the property will override the default value. + +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. + +### Macrosheets + +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"`. + +Under the hood, Excel treats Macrosheets as normal worksheets with special +interpretation of the function expressions. + +#### Detecting Macros in Workbooks + +The `vbaraw` field will only be set if macros are present. Macrosheets will be +explicitly flagged. Combining the two checks yields a simple function: + +```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'); +} +``` diff --git a/docz/docs/07-csf/07-features/index.md b/docz/docs/07-csf/07-features/index.md index 8bc91d9..d292249 100644 --- a/docz/docs/07-csf/07-features/index.md +++ b/docz/docs/07-csf/07-features/index.md @@ -229,71 +229,3 @@ function Visibility(props) { -## VBA and Macros - -
- Format Support (click to show) - -**VBA Modules**: XLSM, XLSB, BIFF8 XLS - -
- -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. - -The `vbaraw` property stores raw bytes. [SheetJS Pro](https://sheetjs.com/pro) -offers a special component for extracting macro text from the VBA blob, editing -the VBA project, and exporting new VBA blobs. - -#### Round-tripping Macro Enabled Files - -In order to preserve macro when reading and writing files, the `bookVBA` option -must be set to true when reading and when writing. In addition, the output file -format must support macros. `XLSX` notably does not support macros, and `XLSM` -should be used in its place: - -```js -/* Reading data */ -var wb = XLSX.read(data, { bookVBA: true }); // read file and distill VBA blob -var vbablob = wb.vbaraw; -``` - -#### Code Names - -Excel will use `ThisWorkbook` (or a translation like `DieseArbeitsmappe`) as the -default Code Name for the workbook. Each worksheet will be identified using the -default `Sheet#` naming pattern even if the worksheet names have changed. - -A custom workbook code name will be stored in `wb.Workbook.WBProps.CodeName`. -For exports, assigning the property will override the default value. - -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. - -#### Macrosheets - -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"`. - -Under the hood, Excel treats Macrosheets as normal worksheets with special -interpretation of the function expressions. - -#### Detecting Macros in Workbooks - -The `vbaraw` field will only be set if macros are present. Macrosheets will be -explicitly flagged. Combining the two checks yields a simple function: - -```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'); -} -``` diff --git a/docz/docusaurus.config.js b/docz/docusaurus.config.js index cf559c7..b64ce49 100644 --- a/docz/docusaurus.config.js +++ b/docz/docusaurus.config.js @@ -142,7 +142,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, - additionalLanguages: [ "swift", "java", "csharp", "perl", "ruby", "cpp", "applescript", "liquid", "rust" ], + additionalLanguages: [ "visual-basic", "swift", "java", "csharp", "perl", "ruby", "cpp", "applescript", "liquid", "rust" ], }, liveCodeBlock: { playgroundPosition: 'top' diff --git a/docz/static/vba/SheetJSVBAFormula.xlsm b/docz/static/vba/SheetJSVBAFormula.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..148b4052bd9d87d1cc0fd460252e977ad0cf0fba GIT binary patch literal 14790 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 literal 0 HcmV?d00001 diff --git a/docz/static/vba/vbaProject.bin b/docz/static/vba/vbaProject.bin new file mode 100644 index 0000000000000000000000000000000000000000..a112350290f016ef109ff88dd6368184be42ed3f GIT binary patch literal 16384 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 Lm+!FLA_@Et1c2mg literal 0 HcmV?d00001