forked from sheetjs/docs.sheetjs.com
		
	maple
This commit is contained in:
		
							parent
							
								
									5bce4b2705
								
							
						
					
					
						commit
						62641b5da6
					
				| @ -354,8 +354,8 @@ required in NodeJS scripts. | ||||
| 
 | ||||
| ### Connecting to S3 | ||||
| 
 | ||||
| The `AWS` module includes a function `S3` that performs the connection. Access | ||||
| keys for an IAM user[^9] must be used: | ||||
| The `aws-sdk` module includes a function `S3` that performs the connection.  | ||||
| Access keys for an IAM user[^9] must be used: | ||||
| 
 | ||||
| ```js | ||||
| /* credentials */ | ||||
|  | ||||
| @ -21,7 +21,7 @@ data from opaque spreadsheets and parse the data from Mathematica. | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested in 2023 August 21 in Mathematica 13.2.1. | ||||
| This demo was last tested by SheetJS users on 2023 August 21 in Mathematica 13. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|  | ||||
| @ -21,7 +21,7 @@ spreadsheets into simple XLSX files for MATLAB. | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested in 2023 September 12 in MATLAB R2023a. | ||||
| This demo was last tested by SheetJS users on 2023 September 12 in MATLAB R2023a. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										209
									
								
								docz/docs/03-demos/32-extensions/12-maple.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										209
									
								
								docz/docs/03-demos/32-extensions/12-maple.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,209 @@ | ||||
| --- | ||||
| title: Modern Spreadsheets in Maple | ||||
| sidebar_label: Maple | ||||
| pagination_prev: demos/cloud/index | ||||
| pagination_next: demos/bigdata/index | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| [Maple](https://www.maplesoft.com/products/Maple/) is a numeric computing | ||||
| platform. It offers a robust C-based extension system. | ||||
| 
 | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| This demo uses SheetJS to pull data from a spreadsheet for further analysis | ||||
| within Maple. We'll create a Maple native extension that loads the | ||||
| [Duktape](/docs/demos/engines/duktape) JavaScript engine and uses the SheetJS | ||||
| library to read data from spreadsheets and converts to a Maple-friendly format. | ||||
| 
 | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|   ofile[(workbook\nXLSB file)] | ||||
|   nfile[(clean file\nXLSX)] | ||||
|   data[[Maple\nTable]] | ||||
|   ofile --> |Maple Extension\nSheetJS + Duktape| nfile | ||||
|   nfile --> |ExcelTools\nImport|data | ||||
| ``` | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested by SheetJS users on 2023 October 3 in Maple 2023. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| Maple has limited support for processing spreadsheets through the `ExcelTools` | ||||
| package[^1]. At the time of writing, it lacked support for XLSB, NUMBERS, and | ||||
| other common spreadsheet formats. | ||||
| 
 | ||||
| SheetJS libraries help fill the gap by normalizing spreadsheets to a form that | ||||
| Maple can understand. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| The current recommendation involves a native plugin that reads arbitrary files | ||||
| and generates clean XLSX files that Maple can import. | ||||
| 
 | ||||
| The extension function ultimately pairs the SheetJS `read`[^2] and `write`[^3] | ||||
| methods to read data from the old file and write a new file: | ||||
| 
 | ||||
| ```js | ||||
| var wb = XLSX.read(original_file_data, {type: "buffer"}); | ||||
| var new_file_data = XLSX.write(wb, {type: "array", bookType: "xlsx"}); | ||||
| ``` | ||||
| 
 | ||||
| The extension function will receive a file name and perform the following steps: | ||||
| 
 | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|   ofile{{File\nName}} | ||||
|   subgraph JS Operations | ||||
|     ojbuf[(Buffer\nFile Bytes)] | ||||
|     wb(((SheetJS\nWorkbook))) | ||||
|     njbuf[(Buffer\nXLSX bytes)] | ||||
|   end | ||||
|   obuf[(File\nbytes)] | ||||
|   nbuf[(New file\nbytes)] | ||||
|   nfile[(XLSX\nFile)] | ||||
|   ofile --> |C\nRead File| obuf | ||||
|   obuf --> |Duktape\nBuffer Ops| ojbuf | ||||
|   ojbuf --> |SheetJS\n`read`| wb | ||||
|   wb --> |SheetJS\n`write`| njbuf | ||||
|   njbuf --> |Duktape\nBuffer Ops| nbuf | ||||
|   nbuf --> |C\nWrite File| nfile | ||||
| ``` | ||||
| 
 | ||||
| ### C Extensions | ||||
| 
 | ||||
| Maple C extensions are shared libraries or DLLs that use special Maple methods | ||||
| for parsing arguments and returning values. | ||||
| 
 | ||||
| To simplify the flow, the new function will take one argument (the original file | ||||
| name) and return one value (the new file name). | ||||
| 
 | ||||
| The official documentation has a comprehensive list[^4] of methods. For this | ||||
| demo, the following methods are used: | ||||
| 
 | ||||
| - `MapleNumArgs` and `IsMapleString` are used in argument validation. The demo | ||||
| function will raise a Maple exception if no file name is specified. | ||||
| 
 | ||||
| - `MapleRaiseError` and `MapleRaiseError2` programmatically raise errors. | ||||
| 
 | ||||
| - `MapleToString` and `ToMapleString` convert between Maple and C strings. | ||||
| 
 | ||||
| ### Duktape JS Engine | ||||
| 
 | ||||
| This demo uses the [Duktape JavaScript engine](/docs/demos/engines/duktape). The | ||||
| SheetJS + Duktape demo covers engine integration details in more detail. | ||||
| 
 | ||||
| The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone) | ||||
| can be loaded in Duktape by reading the source from the filesystem. | ||||
| 
 | ||||
| ## Complete Demo | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| This demo was tested in Windows x64. The path names and build commands will | ||||
| differ in other platforms and operating systems. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| The [`sheetjs-maple.c`](pathname:///maple/sheetjs-maple.c) extension exports the | ||||
| `SheetToXLSX` Maple method. It takes a file name argument, parses the specified | ||||
| file, exports data to `sheetjsw.xlsx` and returns the string `"sheetjsw.xlsx"`. | ||||
| 
 | ||||
| This can be chained with `Import` from `ExcelTools`: | ||||
| 
 | ||||
| ```maple | ||||
| with(ExcelTools); | ||||
| Import(SheetToXLSX("pres.numbers")) | ||||
| ``` | ||||
| 
 | ||||
| 0) Ensure "Windows Subsystem for Linux" (WSL) and Visual Studio are installed. | ||||
| 
 | ||||
| 1) Open a new "x64 Native Tools Command Prompt" window and create a project | ||||
| folder `c:\sheetjs-maple`: | ||||
| 
 | ||||
| ```powershell | ||||
| cd c:\ | ||||
| mkdir sheetjs-maple | ||||
| cd sheetjs-maple | ||||
| ``` | ||||
| 
 | ||||
| 2) Copy the headers and `lib` files from the Maple folder to the project folder. | ||||
| For example, using Maple 2023 on Windows x64: | ||||
| 
 | ||||
| ```powershell | ||||
| copy "C:\Program Files\Maple 2023\extern\include\"*.h . | ||||
| copy "c:\Program Files\Maple 2023\bin.x86_64_WINDOWS"\*.lib . | ||||
| ``` | ||||
| 
 | ||||
| 3) Run `bash` to enter WSL | ||||
| 
 | ||||
| 4) Within WSL, install Duktape: | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://duktape.org/duktape-2.7.0.tar.xz | ||||
| tar -xJf duktape-2.7.0.tar.xz | ||||
| mv duktape-2.7.0/src/*.{c,h} . | ||||
| ``` | ||||
| 
 | ||||
| 5) Still within WSL, download SheetJS scripts and the test file. | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js | ||||
| curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js | ||||
| curl -LO https://sheetjs.com/pres.numbers`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| 6) Still within WSL, download the extension C code | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://docs.sheetjs.com/maple/sheetjs-maple.c | ||||
| ``` | ||||
| 
 | ||||
| 7) Exit WSL by running `exit`. The window will return to the command prompt. | ||||
| 
 | ||||
| 8) Build the extension DLL: | ||||
| 
 | ||||
| ```powershell | ||||
| cl -Gz sheetjs-maple.c duktape.c /EHsc -link -dll -out:sheetjs-maple.dll maplec.lib | ||||
| ``` | ||||
| 
 | ||||
| 9) Close and re-open Maple, then create a new Maple Worksheet or Document | ||||
| 
 | ||||
| 10) Run the following command in Maple to change the working directory: | ||||
| 
 | ||||
| ```maple | ||||
| currentdir("c:\\sheetjs-maple"); | ||||
| ``` | ||||
| 
 | ||||
| 11) Load the `SheetToXLSX` method from the extension: | ||||
| 
 | ||||
| ```maple | ||||
| with(ExternalCalling): | ||||
| dll:=ExternalLibraryName("sheetjs-maple"): | ||||
| SheetToXLSX:=DefineExternal("SheetToXLSX",dll): | ||||
| ``` | ||||
| 
 | ||||
| 12) Read the `pres.numbers` test file: | ||||
| 
 | ||||
| ```maple | ||||
| with(ExcelTools); | ||||
| Import(SheetToXLSX("pres.numbers")) | ||||
| ``` | ||||
| 
 | ||||
| The result will show the data from `pres.numbers` | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| [^1]: See ["ExcelTools"](https://www.maplesoft.com/support/help/Maple/view.aspx?path=ExcelTools) in the Maple documentation. | ||||
| [^2]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^3]: See [`write` in "Writing Files"](/docs/api/write-options) | ||||
| [^4]: See ["C OpenMaple and ExternalCalling Application Program Interface (API)"](https://www.maplesoft.com/support/help/maple/view.aspx?path=OpenMaple%2FC%2FAPI) in the Maple documentation. | ||||
							
								
								
									
										
											BIN
										
									
								
								docz/static/maple/maple.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								docz/static/maple/maple.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										115
									
								
								docz/static/maple/sheetjs-maple.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										115
									
								
								docz/static/maple/sheetjs-maple.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include "maplec.h" | ||||
| #include "duktape.h" | ||||
| 
 | ||||
| /* --- EXPORT_DECL macro from official example --- */ | ||||
| 
 | ||||
| #if !defined(EXPORT_DECL) | ||||
| #ifdef _MSC_VER | ||||
| #define EXPORT_DECL __declspec(dllexport) | ||||
| #else | ||||
| #define EXPORT_DECL | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| /* --- the SheetJS + Duktape demo cover these machinations --- */ | ||||
| 
 | ||||
| #define FAIL_LOAD { \ | ||||
|   duk_push_undefined(ctx); \ | ||||
|   perror("Error in load_file"); \ | ||||
|   return 1; \ | ||||
| } | ||||
| 
 | ||||
| static char *read_file(const char *filename, size_t *sz) { | ||||
|   FILE *f = fopen(filename, "rb"); | ||||
|   if(!f) return NULL; | ||||
|   long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); } | ||||
|   char *buf = (char *)malloc(fsize * sizeof(char)); | ||||
|   *sz = fread((void *) buf, 1, fsize, f); | ||||
|   fclose(f); | ||||
|   return buf; | ||||
| } | ||||
| 
 | ||||
| static duk_int_t eval_file(duk_context *ctx, const char *filename) { | ||||
|   size_t len; char *buf = read_file(filename, &len); | ||||
|   if(!buf) FAIL_LOAD | ||||
| 
 | ||||
|   duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len); | ||||
|   duk_int_t retval = duk_peval(ctx); | ||||
|   duk_pop(ctx); | ||||
|   return retval; | ||||
| } | ||||
| 
 | ||||
| static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) { | ||||
|   size_t len; char *buf = read_file(filename, &len); | ||||
|   if(!buf) FAIL_LOAD | ||||
| 
 | ||||
|   duk_push_external_buffer(ctx); | ||||
|   duk_config_buffer(ctx, -1, buf, len); | ||||
|   duk_put_global_string(ctx, var); | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) { | ||||
|   duk_get_global_string(ctx, var); | ||||
|   duk_size_t sz; | ||||
|   char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz); | ||||
| 
 | ||||
|   if(!buf) return 1; | ||||
|   FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f); | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| #define FAIL_DUK(cmd) { \ | ||||
|   const char *errmsg = duk_safe_to_string(ctx, -1); \ | ||||
|   duk_destroy_heap(ctx); \ | ||||
|   MapleRaiseError2(kv, "error in %1 : %2", ToMapleString(kv, cmd), ToMapleString(kv, errmsg)); \ | ||||
|   return NULL; \ | ||||
| } | ||||
| 
 | ||||
| #define DOIT(cmd) duk_eval_string_noresult(ctx, cmd); | ||||
| 
 | ||||
| /* SheetToXLSX function */ | ||||
| EXPORT_DECL ALGEB M_DECL SheetToXLSX( MKernelVector kv, ALGEB *args ) { | ||||
|   duk_int_t res = 0; | ||||
| 
 | ||||
|   /* get filename */ | ||||
|   if(MapleNumArgs(kv, (ALGEB)args) != 1) { | ||||
|     MapleRaiseError(kv, "must specify a filename"); | ||||
|     return NULL; | ||||
|   } | ||||
|   if(!IsMapleString(kv, args[1])) { | ||||
|     MapleRaiseError(kv, "filename must be a string"); | ||||
|     return NULL; | ||||
|   } | ||||
|   const char *filename = MapleToString(kv, args[1]); | ||||
| 
 | ||||
|   /* initialize duktape */ | ||||
|   duk_context *ctx = duk_create_heap_default(); | ||||
|   /* duktape does not expose a standard "global" by default */ | ||||
|   DOIT("var global = (function(){ return this; }).call(null);"); | ||||
| 
 | ||||
|   /* load SheetJS library */ | ||||
|   res = eval_file(ctx, "shim.min.js"); | ||||
|   if(res != 0) FAIL_DUK("shim load") | ||||
|   res = eval_file(ctx, "xlsx.full.min.js"); | ||||
|   if(res != 0) FAIL_DUK("library load") | ||||
| 
 | ||||
|   /* read file */ | ||||
|   res = load_file(ctx, filename, "buf"); | ||||
|   if(res != 0) FAIL_DUK("file load") | ||||
|   printf("Loaded file %s\n", filename); | ||||
| 
 | ||||
|   /* parse workbook and write to XLSX */ | ||||
|   DOIT("wb = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});"); | ||||
|   DOIT("newbuf = (XLSX.write(wb, {type:'array', bookType:'xlsx'}));");\ | ||||
| 
 | ||||
|   /* write file */ | ||||
|   res = save_file(ctx, "sheetjsw.xlsx", "newbuf");\ | ||||
|   if(res != 0) FAIL_DUK("save sheetjsw.xlsx") | ||||
| 
 | ||||
|   /* return filename */ | ||||
|   return ToMapleString(kv, "sheetjsw.xlsx"); | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user