1.0.0: a new hope
This commit is contained in:
		
						commit
						40c6036085
					
				
							
								
								
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| github: SheetJSDev | ||||
| custom: https://sheetjs.com | ||||
| open_collective: s5s | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| fonts/ | ||||
| node_modules/ | ||||
| *.[wWeE][mM][fF] | ||||
| *.[pP][nN][gG] | ||||
							
								
								
									
										201
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										201
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,201 @@ | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
| 
 | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
| 
 | ||||
|    1. Definitions. | ||||
| 
 | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
| 
 | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
| 
 | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
| 
 | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
| 
 | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
| 
 | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
| 
 | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
| 
 | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
| 
 | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
| 
 | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
| 
 | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
| 
 | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
| 
 | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
| 
 | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
| 
 | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
| 
 | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
| 
 | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
| 
 | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
| 
 | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
| 
 | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
| 
 | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
| 
 | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
| 
 | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
| 
 | ||||
|    END OF TERMS AND CONDITIONS | ||||
| 
 | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
| 
 | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "{}" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
| 
 | ||||
|    Copyright (C) 2020-present   SheetJS LLC | ||||
| 
 | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
| 
 | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
							
								
								
									
										18
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										18
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| .PHONY: all | ||||
| all: node browser | ||||
| 
 | ||||
| .PHONY: src/index.ts | ||||
| js/index.js: src/index.ts | ||||
| 	#tsc src/index.ts | ||||
| 	tsc | ||||
| 
 | ||||
| node: js/index.js | ||||
| 	webpack-cli --config misc/webpack.node.config.js | ||||
| 
 | ||||
| browser: js/index.js | ||||
| 	webpack-cli --config misc/webpack.browser.config.js | ||||
| 
 | ||||
| .PHONY: dist prod | ||||
| dist prod: js/index.js | ||||
| 	webpack-cli --mode=production --config misc/webpack.node.config.js | ||||
| 	webpack-cli --mode=production --config misc/webpack.browser.config.js | ||||
							
								
								
									
										92
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										92
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | ||||
| # js-wmf | ||||
| 
 | ||||
| Processor for Windows MetaFile (WMF) files in JS (for the browser and nodejs). | ||||
| 
 | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| With [npm](https://www.npmjs.org/package/wmf): | ||||
| 
 | ||||
| ```bash | ||||
| $ npm install wmf | ||||
| ``` | ||||
| 
 | ||||
| In the browser: | ||||
| 
 | ||||
| ```html | ||||
| <script src="wmf.js"></script> | ||||
| ``` | ||||
| 
 | ||||
| The browser exposes a variable `WMF`. | ||||
| 
 | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| The `data` argument is expected to be an `ArrayBuffer`, `Uint8Array` or `Buffer` | ||||
| 
 | ||||
| - `WMF.image_size(data)` extracts the image offset and extents, returns an Array | ||||
|   `[width, height]` where both metrics are measured in pixels. | ||||
| 
 | ||||
| - `WMF.draw_canvas(data, canvas)` parses the WMF and draws to a `Canvas`. | ||||
| 
 | ||||
| ### Notes | ||||
| 
 | ||||
| - The library assumes the global `ImageData` is available.  For nodejs-powered | ||||
|   canvas implementations, a shim must be exposed as a global. Using the `canvas` | ||||
|   npm package: | ||||
| 
 | ||||
| ```js | ||||
| const { createImageData } = require("canvas"); | ||||
| global.ImageData = createImageData; | ||||
| ``` | ||||
| 
 | ||||
| - `OffscreenCanvas` in Chrome and some other Canvas implementations require | ||||
|   the dimensions in the constructor: | ||||
| 
 | ||||
| ```js | ||||
| const size = WMF.image_size(data); | ||||
| const canvas = new OffscreenCanvas(size[0], size[1]); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Examples | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>Browser Fetch into canvas</b> (click to show)</summary> | ||||
| 
 | ||||
| ```js | ||||
| // assume `canvas` is a DOM element | ||||
| (async() => { | ||||
|   const res = await fetch("url/for/image.wmf"); | ||||
|   const ab = await res.arrayBuffer(); | ||||
|   WMF.draw_canvas(ab, document.getElementById("canvas")); | ||||
| })(); | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>NodeJS (using `canvas` npm module)</b> (click to show)</summary> | ||||
| 
 | ||||
| ```js | ||||
| const { createCanvas, createImageData } = require("canvas"); | ||||
| global.ImageData = createImageData; | ||||
| 
 | ||||
| const size = WMF.image_size(data); | ||||
| const canvas = createCanvas(size[0], size[1]); | ||||
| WMF.draw_canvas(data, canvas); | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| Please consult the attached LICENSE file for details.  All rights not explicitly | ||||
| granted by the Apache 2.0 License are reserved by the Original Author. | ||||
| 
 | ||||
| 
 | ||||
| ## References | ||||
| 
 | ||||
|  - [MS-WMF]: Windows Metafile Format | ||||
| 
 | ||||
							
								
								
									
										2
									
								
								dist/wmf.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								dist/wmf.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								dist/wmf.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								dist/wmf.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/wmf.node.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								dist/wmf.node.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								dist/wmf.node.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								dist/wmf.node.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										94
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										94
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- wmf.js (C) 2020-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html> | ||||
| <head> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <title>wmf.js Live Demo</title> | ||||
| <style> | ||||
| #drop{ | ||||
| 	border:2px dashed #bbb; | ||||
| 	-moz-border-radius:5px; | ||||
| 	-webkit-border-radius:5px; | ||||
| 	border-radius:5px; | ||||
| 	padding:25px; | ||||
| 	text-align:center; | ||||
| 	font:20pt bold,"Vollkorn";color:#bbb | ||||
| } | ||||
| #b64data{ | ||||
| 	width:100%; | ||||
| } | ||||
| a { text-decoration: none } | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <b><a href="http://sheetjs.com">SheetJS wmf.js Preview Live Demo</a></b> | ||||
| 
 | ||||
| <a href="https://github.com/SheetJS/js-wmf">Source Code Repo</a> | ||||
| <a href="https://github.com/SheetJS/js-wmf/issues">Issues?  Something look weird?  Click here and report an issue</a> | ||||
| <div id="drop">Drop a WMF File here to Preview data</div> | ||||
| <input type="file" name="xlfile" id="xlf" /> ... or click here to select a file | ||||
| 
 | ||||
| <pre id="out"></pre> | ||||
| <canvas id="htmlout"></canvas> | ||||
| <br /> | ||||
| <script src="dist/wmf.js"></script> | ||||
| <script> | ||||
| /*jshint browser:true */ | ||||
| /* eslint-env browser */ | ||||
| /*global Uint8Array, console */ | ||||
| /*global WMF */ | ||||
| /* exported b64it, setfmt */ | ||||
| /* eslint no-use-before-define:0 */ | ||||
| var HTMLOUT = document.getElementById('htmlout'); | ||||
| 
 | ||||
| var do_file = function do_file(files) { | ||||
| 	var f = files[0]; | ||||
| 	var reader = new FileReader(); | ||||
| 	reader.onload = function(e) { | ||||
| 		var data = e.target.result; | ||||
| 		WMF.draw_canvas(data, HTMLOUT); | ||||
| 	}; | ||||
| 	reader.readAsArrayBuffer(f); | ||||
| }; | ||||
| 
 | ||||
| (function() { | ||||
| 	var drop = document.getElementById('drop'); | ||||
| 	if(!drop.addEventListener) return; | ||||
| 
 | ||||
| 	function handleDrop(e) { | ||||
| 		e.stopPropagation(); | ||||
| 		e.preventDefault(); | ||||
| 		do_file(e.dataTransfer.files); | ||||
| 	} | ||||
| 
 | ||||
| 	function handleDragover(e) { | ||||
| 		e.stopPropagation(); | ||||
| 		e.preventDefault(); | ||||
| 		e.dataTransfer.dropEffect = 'copy'; | ||||
| 	} | ||||
| 
 | ||||
| 	drop.addEventListener('dragenter', handleDragover, false); | ||||
| 	drop.addEventListener('dragover', handleDragover, false); | ||||
| 	drop.addEventListener('drop', handleDrop, false); | ||||
| })(); | ||||
| 
 | ||||
| (function() { | ||||
| 	var xlf = document.getElementById('xlf'); | ||||
| 	if(!xlf.addEventListener) return; | ||||
| 	function handleFile(e) { do_file(e.target.files); } | ||||
| 	xlf.addEventListener('change', handleFile, false); | ||||
| })(); | ||||
| 	var _gaq = _gaq || []; | ||||
| 	_gaq.push(['_setAccount', 'UA-36810333-1']); | ||||
| 	_gaq.push(['_trackPageview']); | ||||
| 
 | ||||
| 	(function() { | ||||
| 		var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | ||||
| 		ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | ||||
| 		var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | ||||
| 	})(); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										37
									
								
								js/Records.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										37
									
								
								js/Records.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| "use strict"; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| /* 2.1.1.1 RecordType Enumeration */ | ||||
| exports.WMFRecords = { | ||||
|     0x0000: { n: "META_EOF" }, | ||||
|     0x0626: { n: "META_ESCAPE" }, | ||||
|     0x0940: { n: "META_DIBBITBLT" }, | ||||
|     0x0B41: { n: "META_DIBSTRETCHBLT" }, | ||||
|     0x0A32: { n: "META_EXTTEXTOUT" }, | ||||
|     0x0325: { n: "META_POLYLINE" }, | ||||
|     0x0324: { n: "META_POLYGON" }, | ||||
|     0x0538: { n: "META_POLYPOLYGON" }, | ||||
|     0x02FC: { n: "META_CREATEBRUSHINDIRECT" }, | ||||
|     0x02FB: { n: "META_CREATEFONTINDIRECT" }, | ||||
|     0x02FA: { n: "META_CREATEPENINDIRECT" }, | ||||
|     0x01F0: { n: "META_DELETEOBJECT" }, | ||||
|     0x012C: { n: "META_SELECTCLIPREGION" }, | ||||
|     0x012D: { n: "META_SELECTOBJECT" }, | ||||
|     0x0416: { n: "META_INTERSECTCLIPRECT" }, | ||||
|     0x0035: { n: "META_REALIZEPALETTE" }, | ||||
|     0x0127: { n: "META_RESTOREDC" }, | ||||
|     0x001E: { n: "META_SAVEDC" }, | ||||
|     0x0102: { n: "META_SETBKMODE" }, | ||||
|     0x0103: { n: "META_SETMAPMODE" }, | ||||
|     0x0037: { n: "META_SETPALENTRIES" }, | ||||
|     0x0106: { n: "META_SETPOLYFILLMODE" }, | ||||
|     0x0107: { n: "META_SETSTRETCHBLTMODE" }, | ||||
|     0x012E: { n: "META_SETTEXTALIGN" }, | ||||
|     0x0209: { n: "META_SETTEXTCOLOR" }, | ||||
|     0x020C: { n: "META_SETWINDOWEXT" }, | ||||
|     0x020B: { n: "META_SETWINDOWORG" }, | ||||
|     0xFFFF: { n: "META_SHEETJS" } | ||||
| }; | ||||
| exports.WMFEscapes = { | ||||
|     0x000F: { n: "META_ESCAPE_ENHANCED_METAFILE" } | ||||
| }; | ||||
| //# sourceMappingURL=Records.js.map
 | ||||
							
								
								
									
										1
									
								
								js/Records.js.map
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								js/Records.js.map
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| {"version":3,"file":"Records.js","sourceRoot":"","sources":["../src/Records.ts"],"names":[],"mappings":";;AASA,oCAAoC;AACvB,QAAA,UAAU,GAA+B;IACrD,MAAM,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE;IACzB,MAAM,EAAE,EAAE,CAAC,EAAE,aAAa,EAAE;IAE5B,MAAM,EAAE,EAAE,CAAC,EAAE,gBAAgB,EAAE;IAC/B,MAAM,EAAE,EAAE,CAAC,EAAE,oBAAoB,EAAE;IAEnC,MAAM,EAAE,EAAE,CAAC,EAAE,iBAAiB,EAAE;IAChC,MAAM,EAAE,EAAE,CAAC,EAAE,eAAe,EAAE;IAC9B,MAAM,EAAE,EAAE,CAAC,EAAE,cAAc,EAAE;IAC7B,MAAM,EAAE,EAAE,CAAC,EAAE,kBAAkB,EAAE;IAEjC,MAAM,EAAE,EAAE,CAAC,EAAE,0BAA0B,EAAE;IACzC,MAAM,EAAE,EAAE,CAAC,EAAE,yBAAyB,EAAE;IACxC,MAAM,EAAE,EAAE,CAAC,EAAE,wBAAwB,EAAE;IACvC,MAAM,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE;IAClC,MAAM,EAAE,EAAE,CAAC,EAAE,uBAAuB,EAAE;IACtC,MAAM,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE;IAElC,MAAM,EAAE,EAAE,CAAC,EAAE,wBAAwB,EAAE;IACvC,MAAM,EAAE,EAAE,CAAC,EAAE,qBAAqB,EAAE;IACpC,MAAM,EAAE,EAAE,CAAC,EAAE,gBAAgB,EAAE;IAC/B,MAAM,EAAE,EAAE,CAAC,EAAE,aAAa,EAAE;IAC5B,MAAM,EAAE,EAAE,CAAC,EAAE,gBAAgB,EAAE;IAC/B,MAAM,EAAE,EAAE,CAAC,EAAE,iBAAiB,EAAE;IAChC,MAAM,EAAE,EAAE,CAAC,EAAE,oBAAoB,EAAE;IACnC,MAAM,EAAE,EAAE,CAAC,EAAE,sBAAsB,EAAE;IACrC,MAAM,EAAE,EAAE,CAAC,EAAE,wBAAwB,EAAE;IACvC,MAAM,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE;IAClC,MAAM,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE;IAClC,MAAM,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE;IAClC,MAAM,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE;IAElC,MAAM,EAAE,EAAE,CAAC,EAAE,cAAc,EAAE;CAC7B,CAAC;AAEW,QAAA,UAAU,GAA+B;IACrD,MAAM,EAAE,EAAE,CAAC,EAAE,+BAA+B,EAAE;CAC9C,CAAC"} | ||||
							
								
								
									
										112
									
								
								js/canvas.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										112
									
								
								js/canvas.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| "use strict"; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| var util_1 = require("./util"); | ||||
| var wmf_1 = require("./wmf"); | ||||
| exports.css_color = function (clr) { return "#" + (clr & 0xFF).toString(16).padStart(2, "0") + ((clr >> 8) & 0xFF).toString(16).padStart(2, "0") + ((clr >> 16) & 0xFF).toString(16).padStart(2, "0"); }; | ||||
| exports.set_ctx_state = function (ctx, state) { | ||||
|     if (!state) | ||||
|         return; | ||||
|     var font = ""; | ||||
|     if (state.Font) { | ||||
|         if (state.Font.Italic) | ||||
|             font += " italic"; | ||||
|         if (state.Font.Weight) | ||||
|             font += " " + (state.Font.Weight == 700 ? "bold" : state.Font.Weight == 400 ? "" : state.Font.Weight); | ||||
|         if (state.Font.Height < 0) | ||||
|             font += " " + -state.Font.Height + "px"; | ||||
|         else if (state.Font.Height > 0) | ||||
|             font += " " + state.Font.Height + "px"; | ||||
|         var name_1 = state.Font.Name || ""; | ||||
|         if (name_1 == "System") | ||||
|             name_1 = "Calibri"; // TODO: default sys font is Segoe UI
 | ||||
|         if (name_1) | ||||
|             font += " '" + name_1 + "', sans-serif"; | ||||
|         ctx.font = font.trim(); | ||||
|     } | ||||
| }; | ||||
| // TODO: DIB BIT ORDER?
 | ||||
| exports.render_actions_to_context = function (out, ctx) { | ||||
|     out.forEach(function (act) { | ||||
|         ctx.save(); | ||||
|         exports.set_ctx_state(ctx, act.s); | ||||
|         switch (act.t) { | ||||
|             case "poly": | ||||
|                 ctx.beginPath(); | ||||
|                 ctx.moveTo(act.p[0][0], act.p[0][1]); | ||||
|                 act.p.slice(1).forEach(function (_a) { | ||||
|                     var x = _a[0], y = _a[1]; | ||||
|                     ctx.lineTo(x, y); | ||||
|                 }); | ||||
|                 if (act.g) | ||||
|                     ctx.closePath(); | ||||
|                 ctx.stroke(); | ||||
|                 break; | ||||
|             case "text": | ||||
|                 if (act.s && act.s.TextColor) | ||||
|                     ctx.fillStyle = exports.css_color(act.s.TextColor); | ||||
|                 if (act.s.Font.Angle != 0) { | ||||
|                     ctx.translate(act.p[0], act.p[1]); | ||||
|                     ctx.rotate(-act.s.Font.Angle * Math.PI / 180); | ||||
|                     ctx.fillText(act.v, 0, 0); | ||||
|                     ctx.translate(-act.p[0], -act.p[1]); | ||||
|                 } | ||||
|                 else | ||||
|                     ctx.fillText(act.v, act.p[0], act.p[1]); | ||||
|                 break; | ||||
|             case "cpy": | ||||
|                 { | ||||
|                     // TODO: base on ROP
 | ||||
|                     var idata = ctx.getImageData(act.src[0][0], act.src[1][0], act.src[0][1], act.src[1][1]); | ||||
|                     ctx.putImageData(idata, act.dst[0], act.dst[1]); | ||||
|                 } | ||||
|                 break; | ||||
|             case "str": { | ||||
|                 if (act.data && act.data.BitCount == 24 && act.data.ImageData) { | ||||
|                     var _o = new Uint8ClampedArray(act.data.Width * act.data.Height * 4); | ||||
|                     for (var i = 0; i < act.data.Width * act.data.Height; ++i) { | ||||
|                         var j = (i % act.data.Width) + act.data.Width * (act.data.Height - 1 - Math.floor(i / act.data.Width)); | ||||
|                         _o[4 * i] = act.data.ImageData[3 * j + 2]; | ||||
|                         _o[4 * i + 1] = act.data.ImageData[3 * j + 1]; | ||||
|                         _o[4 * i + 2] = act.data.ImageData[3 * j]; | ||||
|                         _o[4 * i + 3] = 255; | ||||
|                     } | ||||
|                     var idata = new ImageData(_o, act.data.Width, act.data.Height); | ||||
|                     ctx.putImageData(idata, act.dst[0][0], act.dst[1][0]); | ||||
|                 } | ||||
|                 // TODO: ROP et al
 | ||||
|             } | ||||
|         } | ||||
|         ctx.restore(); | ||||
|     }); | ||||
| }; | ||||
| exports.render_canvas = function (out, image) { | ||||
|     var ctx; | ||||
|     /* find first action with window info */ | ||||
|     out.forEach(function (act) { | ||||
|         if (ctx) | ||||
|             return; | ||||
|         if (!act.s) | ||||
|             return; | ||||
|         if (!act.s.Extent || !act.s.Origin) | ||||
|             return; | ||||
|         image.width = act.s.Extent[0] - act.s.Origin[0]; | ||||
|         image.height = act.s.Extent[1] - act.s.Origin[1]; | ||||
|         ctx = image.getContext('2d'); | ||||
|         ctx.save(); | ||||
|         ctx.fillStyle = 'rgb(255,255,255)'; | ||||
|         ctx.fillRect(0, 0, act.s.Extent[0] - act.s.Origin[0], act.s.Extent[1] - act.s.Origin[1]); | ||||
|         ctx.restore(); | ||||
|     }); | ||||
|     if (!ctx) | ||||
|         ctx = image.getContext('2d'); | ||||
|     exports.render_actions_to_context(out, ctx); | ||||
| }; | ||||
| exports.draw_canvas = function (data, image) { | ||||
|     if (data instanceof ArrayBuffer) | ||||
|         return exports.draw_canvas(new Uint8Array(data), image); | ||||
|     util_1.prep_blob(data, 0); | ||||
|     var out = wmf_1.get_actions_prepped_bytes(data); | ||||
|     return exports.render_canvas(out, image); | ||||
| }; | ||||
| //# sourceMappingURL=canvas.js.map
 | ||||
							
								
								
									
										1
									
								
								js/canvas.js.map
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								js/canvas.js.map
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										21
									
								
								js/index.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										21
									
								
								js/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| "use strict"; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| var util_1 = require("./util"); | ||||
| var wmf_1 = require("./wmf"); | ||||
| var canvas_1 = require("./canvas"); | ||||
| exports.draw_canvas = canvas_1.draw_canvas; | ||||
| exports.render_canvas = canvas_1.render_canvas; | ||||
| exports.get_actions = function (data) { | ||||
|     if (data instanceof ArrayBuffer) | ||||
|         return exports.get_actions(new Uint8Array(data)); | ||||
|     util_1.prep_blob(data, 0); | ||||
|     return wmf_1.get_actions_prepped_bytes(data); | ||||
| }; | ||||
| exports.image_size = function (data) { | ||||
|     if (data instanceof ArrayBuffer) | ||||
|         return exports.image_size(new Uint8Array(data)); | ||||
|     util_1.prep_blob(data, 0); | ||||
|     return wmf_1.image_size_prepped_bytes(data); | ||||
| }; | ||||
| //# sourceMappingURL=index.js.map
 | ||||
							
								
								
									
										1
									
								
								js/index.js.map
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								js/index.js.map
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,iEAAiE;AACjE,+BAAiD;AACjD,6BAAmF;AAEnF,mCAAsD;AAA7C,+BAAA,WAAW,CAAA;AAAE,iCAAA,aAAa,CAAA;AAEtB,QAAA,WAAW,GAAG,UAAC,IAAuC;IAClE,IAAG,IAAI,YAAY,WAAW;QAAE,OAAO,mBAAW,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,gBAAS,CAAE,IAAY,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,+BAAyB,CAAC,IAAoB,CAAC,CAAC;AACxD,CAAC,CAAA;AAEY,QAAA,UAAU,GAAG,UAAC,IAAuC;IACjE,IAAG,IAAI,YAAY,WAAW;QAAE,OAAO,kBAAU,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,gBAAS,CAAE,IAAY,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,8BAAwB,CAAC,IAAoB,CAAC,CAAC;AACvD,CAAC,CAAA"} | ||||
							
								
								
									
										395
									
								
								js/util.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										395
									
								
								js/util.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,395 @@ | ||||
| "use strict"; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| // ---
 | ||||
| var has_buf = !!(typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && process.versions.node); | ||||
| exports.has_buf = has_buf; | ||||
| var Buffer_from; | ||||
| exports.Buffer_from = Buffer_from; | ||||
| if (typeof Buffer !== 'undefined') { | ||||
|     var nbfs = !Buffer.from; | ||||
|     if (!nbfs) | ||||
|         try { | ||||
|             Buffer.from("foo", "utf8"); | ||||
|         } | ||||
|         catch (e) { | ||||
|             nbfs = true; | ||||
|         } | ||||
|     exports.Buffer_from = Buffer_from = nbfs ? (function (buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); }) : Buffer.from.bind(Buffer); | ||||
|     if (!Buffer.alloc) | ||||
|         Buffer.alloc = function (n) { return new Buffer(n); }; | ||||
|     if (!Buffer.allocUnsafe) | ||||
|         Buffer.allocUnsafe = function (n) { return new Buffer(n); }; | ||||
| } | ||||
| exports.new_raw_buf = function (len) { return has_buf ? Buffer.alloc(len) : new Array(len); }; | ||||
| exports.new_unsafe_buf = function (len) { return has_buf ? Buffer.allocUnsafe(len) : new Array(len); }; | ||||
| exports._chr = function (c) { return String.fromCharCode(c); }; | ||||
| exports.chr0 = /\u0000/g; // eslint-disable-line no-control-regex
 | ||||
| exports.chr1 = /[\u0001-\u0006]/g; // eslint-disable-line no-control-regex
 | ||||
| // ---
 | ||||
| var read_double_le = function (b, idx) { | ||||
|     var s = 1 - 2 * (b[idx + 7] >>> 7); | ||||
|     var e = ((b[idx + 7] & 0x7f) << 4) + ((b[idx + 6] >>> 4) & 0x0f); | ||||
|     var m = (b[idx + 6] & 0x0f); | ||||
|     for (var i = 5; i >= 0; --i) | ||||
|         m = m * 256 + b[idx + i]; | ||||
|     if (e == 0x7ff) | ||||
|         return m == 0 ? (s * Infinity) : NaN; | ||||
|     if (e == 0) | ||||
|         e = -1022; | ||||
|     else { | ||||
|         e -= 1023; | ||||
|         m += Math.pow(2, 52); | ||||
|     } | ||||
|     return s * Math.pow(2, e - 52) * m; | ||||
| }; | ||||
| var write_double_le = function (b, v, idx) { | ||||
|     var bs = ((((v < 0) || (1 / v == -Infinity)) ? 1 : 0) << 7); | ||||
|     var e = 0, m = 0; | ||||
|     var av = bs ? (-v) : v; | ||||
|     if (!isFinite(av)) { | ||||
|         e = 0x7ff; | ||||
|         m = isNaN(v) ? 0x6969 : 0; | ||||
|     } | ||||
|     else if (av == 0) | ||||
|         e = m = 0; | ||||
|     else { | ||||
|         e = Math.floor(Math.log(av) / Math.LN2); | ||||
|         m = av * Math.pow(2, 52 - e); | ||||
|         if ((e <= -1023) && (!isFinite(m) || (m < Math.pow(2, 52)))) { | ||||
|             e = -1022; | ||||
|         } | ||||
|         else { | ||||
|             m -= Math.pow(2, 52); | ||||
|             e += 1023; | ||||
|         } | ||||
|     } | ||||
|     for (var i = 0; i <= 5; ++i, m /= 256) | ||||
|         b[idx + i] = m & 0xff; | ||||
|     b[idx + 6] = ((e & 0x0f) << 4) | (m & 0xf); | ||||
|     b[idx + 7] = (e >> 4) | bs; | ||||
| }; | ||||
| var __toBuffer = function (bufs /*:Array<Array<RawBytes> >*/) { | ||||
|     var x = []; | ||||
|     for (var i = 0; i < bufs[0].length; ++i) | ||||
|         if (bufs[0][i]) | ||||
|             for (var j = 0, L = bufs[0][i].length; j < L; j += 10240) | ||||
|                 x.push.apply(x, bufs[0][i].slice(j, j + 10240)); | ||||
|     return x; | ||||
| }; | ||||
| var ___toBuffer = __toBuffer; | ||||
| var __readUInt8 = function (b, idx) { return b[idx]; }; | ||||
| var __readUInt16LE = function (b, idx) { return (b[idx + 1] * (1 << 8)) + b[idx]; }; | ||||
| var __readInt16LE = function (b, idx) { var u = (b[idx + 1] * (1 << 8)) + b[idx]; return (u < 0x8000) ? u : ((0xffff - u + 1) * -1); }; | ||||
| var __readUInt32LE = function (b, idx) { return b[idx + 3] * (1 << 24) + (b[idx + 2] << 16) + (b[idx + 1] << 8) + b[idx]; }; | ||||
| var __readInt32LE = function (b, idx) { return (b[idx + 3] << 24) | (b[idx + 2] << 16) | (b[idx + 1] << 8) | b[idx]; }; | ||||
| var __readInt32BE = function (b, idx) { return (b[idx] << 24) | (b[idx + 1] << 16) | (b[idx + 2] << 8) | b[idx + 3]; }; | ||||
| var __utf16le = function (b, s, e) { | ||||
|     var ss = []; | ||||
|     for (var i = s; i < e; i += 2) | ||||
|         ss.push(String.fromCharCode(__readUInt16LE(b, i))); | ||||
|     return ss.join("").replace(exports.chr0, ''); | ||||
| }; | ||||
| exports.__utf16le = __utf16le; | ||||
| var ___utf16le = __utf16le; | ||||
| var __hexlify = function (b /*:RawBytes|CFBlob*/, s, l) { var ss = []; for (var i = s; i < s + l; ++i) | ||||
|     ss.push(("0" + b[i].toString(16)).slice(-2)); return ss.join(""); }; | ||||
| var ___hexlify = __hexlify; | ||||
| var __utf8 = function (b /*:RawBytes|CFBlob*/, s, e) { var ss = []; for (var i = s; i < e; i++) | ||||
|     ss.push(String.fromCharCode(__readUInt8(b, i))); return ss.join(""); }; | ||||
| var ___utf8 = __utf8; | ||||
| var __lpstr = function (b /*:RawBytes|CFBlob*/, i) { var len = __readUInt32LE(b, i); return len > 0 ? __utf8(b, i + 4, i + 4 + len - 1) : ""; }; | ||||
| var ___lpstr = __lpstr; | ||||
| var __cpstr = function (b /*:RawBytes|CFBlob*/, i) { var len = __readUInt32LE(b, i); return len > 0 ? __utf8(b, i + 4, i + 4 + len - 1) : ""; }; | ||||
| var ___cpstr = __cpstr; | ||||
| var __lpwstr = function (b /*:RawBytes|CFBlob*/, i) { var len = 2 * __readUInt32LE(b, i); return len > 0 ? __utf8(b, i + 4, i + 4 + len - 1) : ""; }; | ||||
| var ___lpwstr = __lpwstr; | ||||
| var __lpp4, ___lpp4; | ||||
| __lpp4 = ___lpp4 = function lpp4_(b /*:RawBytes|CFBlob*/, i) { var len = __readUInt32LE(b, i); return len > 0 ? __utf16le(b, i + 4, i + 4 + len) : ""; }; | ||||
| var ___8lpp4 = function (b /*:RawBytes|CFBlob*/, i) { var len = __readUInt32LE(b, i); return len > 0 ? __utf8(b, i + 4, i + 4 + len) : ""; }; | ||||
| var __8lpp4 = ___8lpp4; | ||||
| var ___double = function (b /*:RawBytes|CFBlob*/, idx) { return read_double_le(b, idx); }; | ||||
| var __double = ___double; | ||||
| if (has_buf) { | ||||
|     exports.__utf16le = __utf16le = function (b /*:RawBytes|CFBlob*/, s, e) { return (!Buffer.isBuffer(b)) ? ___utf16le(b, s, e) : b.toString('utf16le', s, e).replace(exports.chr0, ''); }; | ||||
|     __hexlify = function (b /*:RawBytes|CFBlob*/, s, l) { return Buffer.isBuffer(b) ? b.toString('hex', s, s + l) : ___hexlify(b, s, l); }; | ||||
|     __lpstr = function (b /*:RawBytes|CFBlob*/, i) { if (!Buffer.isBuffer(b)) | ||||
|         return ___lpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8', i + 4, i + 4 + len - 1) : ""; }; | ||||
|     __cpstr = function (b /*:RawBytes|CFBlob*/, i) { if (!Buffer.isBuffer(b)) | ||||
|         return ___cpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8', i + 4, i + 4 + len - 1) : ""; }; | ||||
|     __lpwstr = function (b /*:RawBytes|CFBlob*/, i) { if (!Buffer.isBuffer(b)) | ||||
|         return ___lpwstr(b, i); var len = 2 * b.readUInt32LE(i); return b.toString('utf16le', i + 4, i + 4 + len - 1); }; | ||||
|     __lpp4 = function (b /*:RawBytes|CFBlob*/, i) { if (!Buffer.isBuffer(b)) | ||||
|         return ___lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf16le', i + 4, i + 4 + len); }; | ||||
|     __8lpp4 = function (b /*:RawBytes|CFBlob*/, i) { if (!Buffer.isBuffer(b)) | ||||
|         return ___8lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf8', i + 4, i + 4 + len); }; | ||||
|     __utf8 = function (b /*:RawBytes|CFBlob*/, s, e) { return (Buffer.isBuffer(b)) ? b.toString('utf8', s, e) : ___utf8(b, s, e); }; | ||||
|     __toBuffer = function (bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs); }; | ||||
|     __double = function (b /*:RawBytes|CFBlob*/, i) { return (Buffer.isBuffer(b)) ? b.readDoubleLE(i) : ___double(b, i); }; | ||||
| } | ||||
| function ReadShift(size, t) { | ||||
|     var o = "", oI = 0, oR, w, vv, i, loc; | ||||
|     var oo = []; | ||||
|     switch (t) { | ||||
|         case 'dbcs': | ||||
|             loc = this.l; | ||||
|             if (has_buf && Buffer.isBuffer(this)) | ||||
|                 o = this.slice(this.l, this.l + 2 * size).toString("utf16le"); | ||||
|             else | ||||
|                 for (i = 0; i < size; ++i) { | ||||
|                     o += String.fromCharCode(__readUInt16LE(this, loc)); | ||||
|                     loc += 2; | ||||
|                 } | ||||
|             size *= 2; | ||||
|             break; | ||||
|         case 'utf8': | ||||
|             o = __utf8(this, this.l, this.l + size); | ||||
|             break; | ||||
|         case 'utf16le': | ||||
|             size *= 2; | ||||
|             o = __utf16le(this, this.l, this.l + size); | ||||
|             break; | ||||
|         case 'wstr': | ||||
|             return ReadShift.call(this, size, 'dbcs'); | ||||
|         /* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */ | ||||
|         case 'lpstr-ansi': | ||||
|             o = __lpstr(this, this.l); | ||||
|             size = 4 + __readUInt32LE(this, this.l); | ||||
|             break; | ||||
|         case 'lpstr-cp': | ||||
|             o = __cpstr(this, this.l); | ||||
|             size = 4 + __readUInt32LE(this, this.l); | ||||
|             break; | ||||
|         /* [MS-OLEDS] 2.1.5 LengthPrefixedUnicodeString */ | ||||
|         case 'lpwstr': | ||||
|             o = __lpwstr(this, this.l); | ||||
|             size = 4 + 2 * __readUInt32LE(this, this.l); | ||||
|             break; | ||||
|         /* [MS-OFFCRYPTO] 2.1.2 Length-Prefixed Padded Unicode String (UNICODE-LP-P4) */ | ||||
|         case 'lpp4': | ||||
|             size = 4 + __readUInt32LE(this, this.l); | ||||
|             o = __lpp4(this, this.l); | ||||
|             if (size & 0x02) | ||||
|                 size += 2; | ||||
|             break; | ||||
|         /* [MS-OFFCRYPTO] 2.1.3 Length-Prefixed UTF-8 String (UTF-8-LP-P4) */ | ||||
|         case '8lpp4': | ||||
|             size = 4 + __readUInt32LE(this, this.l); | ||||
|             o = __8lpp4(this, this.l); | ||||
|             if (size & 0x03) | ||||
|                 size += 4 - (size & 0x03); | ||||
|             break; | ||||
|         case 'cstr': | ||||
|             size = 0; | ||||
|             o = ""; | ||||
|             while ((w = __readUInt8(this, this.l + size++)) !== 0) | ||||
|                 oo.push(String.fromCharCode(w)); | ||||
|             o = oo.join(""); | ||||
|             break; | ||||
|         case '_wstr': | ||||
|             size = 0; | ||||
|             o = ""; | ||||
|             while ((w = __readUInt16LE(this, this.l + size)) !== 0) { | ||||
|                 oo.push(String.fromCharCode(w)); | ||||
|                 size += 2; | ||||
|             } | ||||
|             size += 2; | ||||
|             o = oo.join(""); | ||||
|             break; | ||||
|         /* sbcs and dbcs support continue records in the SST way TODO codepages */ | ||||
|         case 'dbcs-cont': | ||||
|             o = ""; | ||||
|             loc = this.l; | ||||
|             for (i = 0; i < size; ++i) { | ||||
|                 if (this.lens && this.lens.indexOf(loc) !== -1) { | ||||
|                     w = __readUInt8(this, loc); | ||||
|                     this.l = loc + 1; | ||||
|                     vv = ReadShift.call(this, size - i, w ? 'dbcs-cont' : 'sbcs-cont'); | ||||
|                     return oo.join("") + vv; | ||||
|                 } | ||||
|                 oo.push(String.fromCharCode(__readUInt16LE(this, loc))); | ||||
|                 loc += 2; | ||||
|             } | ||||
|             o = oo.join(""); | ||||
|             size *= 2; | ||||
|             break; | ||||
|         case 'cpstr': | ||||
|         /* falls through */ | ||||
|         case 'sbcs-cont': | ||||
|             o = ""; | ||||
|             loc = this.l; | ||||
|             for (i = 0; i != size; ++i) { | ||||
|                 if (this.lens && this.lens.indexOf(loc) !== -1) { | ||||
|                     w = __readUInt8(this, loc); | ||||
|                     this.l = loc + 1; | ||||
|                     vv = ReadShift.call(this, size - i, w ? 'dbcs-cont' : 'sbcs-cont'); | ||||
|                     return oo.join("") + vv; | ||||
|                 } | ||||
|                 oo.push(String.fromCharCode(__readUInt8(this, loc))); | ||||
|                 loc += 1; | ||||
|             } | ||||
|             o = oo.join(""); | ||||
|             break; | ||||
|         default: | ||||
|             switch (size) { | ||||
|                 case 1: | ||||
|                     oI = __readUInt8(this, this.l); | ||||
|                     this.l++; | ||||
|                     return oI; | ||||
|                 case 2: | ||||
|                     oI = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l); | ||||
|                     this.l += 2; | ||||
|                     return oI; | ||||
|                 case 4: | ||||
|                 case -4: | ||||
|                     if (t === 'i' || ((this[this.l + 3] & 0x80) === 0)) { | ||||
|                         oI = ((size > 0) ? __readInt32LE : __readInt32BE)(this, this.l); | ||||
|                         this.l += 4; | ||||
|                         return oI; | ||||
|                     } | ||||
|                     else { | ||||
|                         oR = __readUInt32LE(this, this.l); | ||||
|                         this.l += 4; | ||||
|                     } | ||||
|                     return oR; | ||||
|                 case 8: | ||||
|                 case -8: | ||||
|                     if (t === 'f') { | ||||
|                         if (size == 8) | ||||
|                             oR = __double(this, this.l); | ||||
|                         else | ||||
|                             oR = __double([this[this.l + 7], this[this.l + 6], this[this.l + 5], this[this.l + 4], this[this.l + 3], this[this.l + 2], this[this.l + 1], this[this.l + 0]], 0); | ||||
|                         this.l += 8; | ||||
|                         return oR; | ||||
|                     } | ||||
|                     else | ||||
|                         size = 8; | ||||
|                 /* falls through */ | ||||
|                 case 16: | ||||
|                     o = __hexlify(this, this.l, size); | ||||
|                     break; | ||||
|             } | ||||
|     } | ||||
|     this.l += size; | ||||
|     return o; | ||||
| } | ||||
| exports.ReadShift = ReadShift; | ||||
| var __writeUInt32LE = function (b /*:RawBytes|CFBlob*/, val, idx) { b[idx] = (val & 0xFF); b[idx + 1] = ((val >>> 8) & 0xFF); b[idx + 2] = ((val >>> 16) & 0xFF); b[idx + 3] = ((val >>> 24) & 0xFF); }; | ||||
| var __writeInt32LE = function (b /*:RawBytes|CFBlob*/, val, idx) { b[idx] = (val & 0xFF); b[idx + 1] = ((val >> 8) & 0xFF); b[idx + 2] = ((val >> 16) & 0xFF); b[idx + 3] = ((val >> 24) & 0xFF); }; | ||||
| var __writeUInt16LE = function (b /*:RawBytes|CFBlob*/, val, idx) { b[idx] = (val & 0xFF); b[idx + 1] = ((val >>> 8) & 0xFF); }; | ||||
| function WriteShift(t, val, f) { | ||||
|     var size = 0, i = 0; | ||||
|     if (f === 'dbcs') { | ||||
|         if (typeof val !== 'string') | ||||
|             throw new Error("expected string"); | ||||
|         for (i = 0; i != val.length; ++i) | ||||
|             __writeUInt16LE(this, val.charCodeAt(i), this.l + 2 * i); | ||||
|         size = 2 * val.length; | ||||
|     } | ||||
|     else if (f === 'sbcs') { | ||||
|         { | ||||
|             val = val.replace(/[^\x00-\x7F]/g, "_"); // eslint-disable-line no-control-regex
 | ||||
|             for (i = 0; i != val.length; ++i) | ||||
|                 this[this.l + i] = (val.charCodeAt(i) & 0xFF); | ||||
|         } | ||||
|         size = val.length; | ||||
|     } | ||||
|     else if (f === 'hex') { | ||||
|         for (; i < t; ++i) { | ||||
|             this[this.l++] = (parseInt(val.slice(2 * i, 2 * i + 2), 16) || 0); | ||||
|         } | ||||
|         return this; | ||||
|     } | ||||
|     else if (f === 'utf16le') { | ||||
|         /*:: if(typeof val !== "string") throw new Error("unreachable"); */ | ||||
|         var end = Math.min(this.l + t, this.length); | ||||
|         for (i = 0; i < Math.min(val.length, t); ++i) { | ||||
|             var cc = val.charCodeAt(i); | ||||
|             this[this.l++] = (cc & 0xff); | ||||
|             this[this.l++] = (cc >> 8); | ||||
|         } | ||||
|         while (this.l < end) | ||||
|             this[this.l++] = 0; | ||||
|         return this; | ||||
|     } | ||||
|     else if (typeof val === 'number') | ||||
|         switch (t) { | ||||
|             case 1: | ||||
|                 size = 1; | ||||
|                 this[this.l] = val & 0xFF; | ||||
|                 break; | ||||
|             case 2: | ||||
|                 size = 2; | ||||
|                 this[this.l] = val & 0xFF; | ||||
|                 val >>>= 8; | ||||
|                 this[this.l + 1] = val & 0xFF; | ||||
|                 break; | ||||
|             case 3: | ||||
|                 size = 3; | ||||
|                 this[this.l] = val & 0xFF; | ||||
|                 val >>>= 8; | ||||
|                 this[this.l + 1] = val & 0xFF; | ||||
|                 val >>>= 8; | ||||
|                 this[this.l + 2] = val & 0xFF; | ||||
|                 break; | ||||
|             case 4: | ||||
|                 size = 4; | ||||
|                 __writeUInt32LE(this, val, this.l); | ||||
|                 break; | ||||
|             case 8: | ||||
|                 size = 8; | ||||
|                 if (f === 'f') { | ||||
|                     write_double_le(this, val, this.l); | ||||
|                     break; | ||||
|                 } | ||||
|             /* falls through */ | ||||
|             case 16: break; | ||||
|             case -4: | ||||
|                 size = 4; | ||||
|                 __writeInt32LE(this, val, this.l); | ||||
|                 break; | ||||
|         } | ||||
|     this.l += size; | ||||
|     return this; | ||||
| } | ||||
| exports.WriteShift = WriteShift; | ||||
| function CheckField(hexstr, fld) { | ||||
|     var m = __hexlify(this, this.l, hexstr.length >> 1); | ||||
|     if (m !== hexstr) | ||||
|         throw new Error(fld + 'Expected ' + hexstr + ' saw ' + m); | ||||
|     this.l += hexstr.length >> 1; | ||||
| } | ||||
| exports.CheckField = CheckField; | ||||
| var prep_blob = function (blob, pos) { | ||||
|     blob.l = pos; | ||||
|     blob.read_shift = ReadShift; | ||||
|     blob.chk = CheckField; | ||||
|     blob.write_shift = WriteShift; | ||||
| }; | ||||
| exports.prep_blob = prep_blob; | ||||
| var new_buf = function (sz) { | ||||
|     var o = exports.new_raw_buf(sz); | ||||
|     prep_blob(o, 0); | ||||
|     return o; | ||||
| }; | ||||
| exports.new_buf = new_buf; | ||||
| // ---
 | ||||
| var __bconcat = function (bufs /*:Array<RawBytes>*/) { | ||||
|     var is_all_arrays = true; | ||||
|     for (var w = 0; w < bufs.length; ++w) | ||||
|         if (!Array.isArray(bufs[w])) | ||||
|             is_all_arrays = false; | ||||
|     if (is_all_arrays) | ||||
|         return [].concat.apply([], bufs); | ||||
|     var maxlen = 0, i = 0; | ||||
|     for (i = 0; i < bufs.length; ++i) | ||||
|         maxlen += bufs[i].length; | ||||
|     var o = new Uint8Array(maxlen); | ||||
|     for (i = 0, maxlen = 0; i < bufs.length; maxlen += bufs[i].length, ++i) | ||||
|         o.set(bufs[i], maxlen); | ||||
|     return o; | ||||
| }; | ||||
| var bconcat = __bconcat; | ||||
| exports.bconcat = bconcat; | ||||
| if (has_buf) | ||||
|     exports.bconcat = bconcat = function (bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : [].concat.apply([], bufs); }; | ||||
| //# sourceMappingURL=util.js.map
 | ||||
							
								
								
									
										1
									
								
								js/util.js.map
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								js/util.js.map
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										428
									
								
								js/wmf.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										428
									
								
								js/wmf.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,428 @@ | ||||
| "use strict"; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| var util_1 = require("./util"); | ||||
| var Records_1 = require("./Records"); | ||||
| var parse_emf = function (data) { | ||||
|     //try { require("fs").writeFileSync("out.emf", data); } catch(e) {}
 | ||||
| }; | ||||
| /* 2.2.2.9 */ | ||||
| var parse_dib = function (data) { | ||||
|     if (data.length == 0) | ||||
|         return null; | ||||
|     util_1.prep_blob(data, 0); | ||||
|     /* DIBHeaderInfo */ | ||||
|     var HeaderSize = data.read_shift(4); | ||||
|     var Width = 0, Height = 0, Planes = 0, BitCount = 0; | ||||
|     var Compression = 0, ImageSize = 0, XPelsPerMeter = 0, YPelsPerMeter = 0, ColorUsed = 0, ColorImportant = 0; | ||||
|     if (HeaderSize == 0x0C) { | ||||
|         Width = data.read_shift(2); | ||||
|         Height = data.read_shift(2); | ||||
|     } | ||||
|     else { | ||||
|         Width = data.read_shift(4, 'i'); | ||||
|         Height = data.read_shift(4, 'i'); | ||||
|     } | ||||
|     Planes = data.read_shift(2); | ||||
|     BitCount = data.read_shift(2); | ||||
|     var out = { | ||||
|         Width: Width, | ||||
|         Height: Height, | ||||
|         BitCount: BitCount, | ||||
|     }; | ||||
|     if (HeaderSize != 0x0C) { | ||||
|         Compression = data.read_shift(4); | ||||
|         ImageSize = data.read_shift(4); | ||||
|         XPelsPerMeter = data.read_shift(4, 'i'); | ||||
|         YPelsPerMeter = data.read_shift(4, 'i'); | ||||
|         ColorUsed = data.read_shift(4); | ||||
|         ColorImportant = data.read_shift(4); | ||||
|         out["Compression"] = Compression; | ||||
|         if (BitCount == 24 && ImageSize > Height * 3 * Width) | ||||
|             Width = out["Width"] = ImageSize / (Height * 3); | ||||
|     } | ||||
|     /* Colors */ | ||||
|     /* BitmapBuffer */ | ||||
|     if (ImageSize == data.length - data.l) { | ||||
|         out["ImageData"] = data.slice(data.l, data.length); | ||||
|         util_1.prep_blob(out["ImageData"], 0); | ||||
|     } | ||||
|     return out; | ||||
| }; | ||||
| var add_to_objects = function (objects, obj) { | ||||
|     var found = false; | ||||
|     for (var i = 0; i < objects.length; ++i) | ||||
|         if (!objects[i]) { | ||||
|             objects[i] = obj; | ||||
|             found = true; | ||||
|         } | ||||
|     if (!found) | ||||
|         objects.push(obj); | ||||
| }; | ||||
| exports.get_actions_prepped_bytes = function (data) { | ||||
|     var out = []; | ||||
|     /* 2.3.22 META_HEADER */ | ||||
|     // Type (2 bytes) must be 1 or 2
 | ||||
|     var h = data.read_shift(2); | ||||
|     if (h != 1 && h != 2) | ||||
|         throw "Header: Type " + h + " must be 1 or 2"; | ||||
|     // HeaderSize expected to be 9
 | ||||
|     if ((h = data.read_shift(2)) != 9) | ||||
|         throw "Header: HeaderSize " + h + " must be 9"; | ||||
|     // Version (2 bytes) 1 or 3
 | ||||
|     h = data.read_shift(2); | ||||
|     if (h != 0x0100 && h != 0x0300) | ||||
|         throw "Header: Version " + h + " must be 0x0100 or 0x0300"; | ||||
|     // SizeLow
 | ||||
|     // SizeHigh
 | ||||
|     // #Objects
 | ||||
|     // MaxRecord
 | ||||
|     // NumberOfMembers
 | ||||
|     data.l = 18; | ||||
|     var rt = 0; | ||||
|     /* used for EMF */ | ||||
|     var escapecnt = 0; | ||||
|     var CommentRecordCount = 0; | ||||
|     var RemainingBytes = 0; | ||||
|     var EnhancedMetafileDataSize = 0; | ||||
|     var bufs = []; | ||||
|     var objects = []; | ||||
|     var states = []; | ||||
|     var state = {}; | ||||
|     var sidx = -1; | ||||
|     while (data.l < data.length) { | ||||
|         h = data.read_shift(4); | ||||
|         var end = data.l + h * 2 - 4; | ||||
|         rt = data.read_shift(2); | ||||
|         var Record = Records_1.WMFRecords[rt]; | ||||
|         if (rt == 0x0000) | ||||
|             break; // META_EOF
 | ||||
|         switch (rt) { | ||||
|             case 0x0626: | ||||
|                 { // META_ESCAPE
 | ||||
|                     var EscapeFunction = data.read_shift(2); | ||||
|                     var Escape = Records_1.WMFEscapes[EscapeFunction]; | ||||
|                     //console.log("::", Escape);
 | ||||
|                     /* 2.3.6 */ | ||||
|                     switch (EscapeFunction) { | ||||
|                         case 0x000F: | ||||
|                             { // META_ESCAPE_ENHANCED_METAFILE
 | ||||
|                                 var ByteCount = data.read_shift(2); | ||||
|                                 var tmp = data.read_shift(4); | ||||
|                                 if (tmp != 0x43464D57) | ||||
|                                     throw "Escape: Comment ID 0x" + tmp.toString(16) + " != 0x43464D57"; | ||||
|                                 tmp = data.read_shift(4); | ||||
|                                 if (tmp != 0x00000001) | ||||
|                                     throw "Escape: Comment Type 0x" + tmp.toString(16) + " != 0x00000001"; | ||||
|                                 tmp = data.read_shift(4); | ||||
|                                 if (tmp != 0x00010000) | ||||
|                                     throw "Escape: Version 0x" + tmp.toString(16) + " != 0x00010000"; | ||||
|                                 var Checksum = data.read_shift(2); | ||||
|                                 data.l += 4; // Flags
 | ||||
|                                 if (escapecnt == 0) { | ||||
|                                     CommentRecordCount = data.read_shift(4); // total number of records
 | ||||
|                                 } | ||||
|                                 else { | ||||
|                                     var _CommentRecordCount = data.read_shift(4); | ||||
|                                     if (_CommentRecordCount != CommentRecordCount) | ||||
|                                         throw "Escape: CommentRecordCount " + _CommentRecordCount + " != " + CommentRecordCount; | ||||
|                                 } | ||||
|                                 var CurrentRecordSize = data.read_shift(4); // size of this record
 | ||||
|                                 var _RemainingBytes = data.read_shift(4); | ||||
|                                 if (escapecnt > 0 && CurrentRecordSize + _RemainingBytes != RemainingBytes) | ||||
|                                     throw "Escape: " + RemainingBytes + " != " + CurrentRecordSize + " + " + _RemainingBytes; | ||||
|                                 RemainingBytes = _RemainingBytes; | ||||
|                                 var _EnhancedMetafileDataSize = data.read_shift(4); | ||||
|                                 if (escapecnt == 0) { | ||||
|                                     if (_EnhancedMetafileDataSize != CurrentRecordSize + _RemainingBytes) | ||||
|                                         throw "Escape: " + _EnhancedMetafileDataSize + " != " + CurrentRecordSize + " + " + _RemainingBytes; | ||||
|                                     EnhancedMetafileDataSize = _EnhancedMetafileDataSize; | ||||
|                                 } | ||||
|                                 else if (EnhancedMetafileDataSize != _EnhancedMetafileDataSize) | ||||
|                                     throw "Escape: " + EnhancedMetafileDataSize + " != " + _EnhancedMetafileDataSize; | ||||
|                                 if (ByteCount != (end - data.l) + 34) | ||||
|                                     throw "Escape: Sizes " + ByteCount + " != " + (end - data.l) + " + 34"; | ||||
|                                 if (end - data.l != CurrentRecordSize) | ||||
|                                     throw "Escape: CRSize " + CurrentRecordSize + " != " + (end - data.l); | ||||
|                                 bufs.push(data.slice(data.l, end)); | ||||
|                                 ++escapecnt; | ||||
|                                 if (escapecnt == CommentRecordCount) { | ||||
|                                     var prepped = util_1.bconcat(bufs); | ||||
|                                     util_1.prep_blob(prepped, 0); | ||||
|                                     parse_emf(prepped); | ||||
|                                 } | ||||
|                             } | ||||
|                             break; | ||||
|                         default: throw "Escape: Unrecognized META_ESCAPE Type 0x" + EscapeFunction.toString(16); | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             // #region 2.3.1 Bitmap Record Types
 | ||||
|             case 0x0940: | ||||
|                 { // 2.3.1.2 META_DIBBITBLT
 | ||||
|                     var has_bitmap = h != (rt >> 8) + 3; | ||||
|                     var RasterOperation = data.read_shift(4); | ||||
|                     var YSrc = data.read_shift(2, "i"); | ||||
|                     var XSrc = data.read_shift(2, "i"); | ||||
|                     if (!has_bitmap) | ||||
|                         data.l += 2; | ||||
|                     var Height = data.read_shift(2, "i"); | ||||
|                     var Width = data.read_shift(2, "i"); | ||||
|                     var YDest = data.read_shift(2, "i"); | ||||
|                     var XDest = data.read_shift(2, "i"); | ||||
|                     var res = { | ||||
|                         t: "cpy", | ||||
|                         src: [[XSrc, Width], [YSrc, Height]], | ||||
|                         dst: [XDest, YDest], | ||||
|                         rop: RasterOperation, | ||||
|                         s: Object.assign({}, state) | ||||
|                     }; | ||||
|                     if (has_bitmap) { | ||||
|                         var DIB = parse_dib(data.slice(data.l, end)); | ||||
|                         res.data = DIB; | ||||
|                     } | ||||
|                     out.push(res); | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x0B41: | ||||
|                 { // 2.3.1.3 META_DIBSTRETCHBLT
 | ||||
|                     var has_bitmap = h != (rt >> 8) + 3; | ||||
|                     var RasterOperation = data.read_shift(4); | ||||
|                     var SrcHeight = data.read_shift(2, "i"); | ||||
|                     var SrcWidth = data.read_shift(2, "i"); | ||||
|                     var YSrc = data.read_shift(2, "i"); | ||||
|                     var XSrc = data.read_shift(2, "i"); | ||||
|                     if (!has_bitmap) | ||||
|                         data.l += 2; | ||||
|                     var DestHeight = data.read_shift(2, "i"); | ||||
|                     var DestWidth = data.read_shift(2, "i"); | ||||
|                     var YDest = data.read_shift(2, "i"); | ||||
|                     var XDest = data.read_shift(2, "i"); | ||||
|                     var res = { | ||||
|                         t: "str", | ||||
|                         src: [[XSrc, SrcWidth], [YSrc, SrcHeight]], | ||||
|                         dst: [[XDest, DestWidth], [YDest, DestHeight]], | ||||
|                         rop: RasterOperation, | ||||
|                         s: Object.assign({}, state) | ||||
|                     }; | ||||
|                     if (has_bitmap) { | ||||
|                         var DIB = parse_dib(data.slice(data.l, end)); | ||||
|                         res.data = DIB; | ||||
|                     } | ||||
|                     out.push(res); | ||||
|                 } | ||||
|                 break; | ||||
|             // #endregion
 | ||||
|             // #region 2.3.3 Drawing Record Types
 | ||||
|             case 0x0A32: | ||||
|                 { // 2.3.3.5 META_EXTTEXTOUT
 | ||||
|                     var Y = data.read_shift(2); | ||||
|                     var X = data.read_shift(2); | ||||
|                     var StringLength = data.read_shift(2); | ||||
|                     var fwOpts = data.read_shift(2); // 2.1.2.2
 | ||||
|                     if (fwOpts & 0x06) { | ||||
|                         data.l += 8; // Rectangle 2.2.2.18 (for clipping/opaquing)
 | ||||
|                     } | ||||
|                     var str = data.read_shift(StringLength, 'cpstr'); | ||||
|                     if (data.l < end) { /* TODO: Dx */ } | ||||
|                     out.push({ t: "text", v: str, p: [X, Y], s: Object.assign({}, state) }); | ||||
|                     /* TODO!! */ | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x0325: // 2.3.3.14 META_POLYLINE
 | ||||
|             case 0x0324: // 2.3.3.15 META_POLYGON
 | ||||
|                 { | ||||
|                     var nPoints = data.read_shift(2); | ||||
|                     var points = []; | ||||
|                     for (var i = 0; i < nPoints; ++i) | ||||
|                         points.push([data.read_shift(2), data.read_shift(2)]); | ||||
|                     out.push({ t: "poly", p: points, g: rt !== 0x0325, s: state }); | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x0538: | ||||
|                 { // 2.3.3.16 META_POLYPOLYGON
 | ||||
|                     var nPolygons = data.read_shift(2); | ||||
|                     var polys = []; | ||||
|                     var szs = []; | ||||
|                     /* 2.2.2.17 PolyPolygon */ | ||||
|                     for (var i = 0; i < nPolygons; ++i) | ||||
|                         szs[i] = data.read_shift(2); | ||||
|                     for (var i = 0; i < szs.length; ++i) { | ||||
|                         polys[i] = []; | ||||
|                         for (var j = 0; j < szs[i]; ++j) | ||||
|                             polys[i].push([data.read_shift(2), data.read_shift(2)]); | ||||
|                         out.push({ t: "poly", p: polys[i], g: true, s: state }); | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             // #endregion
 | ||||
|             // #region 2.3.4 Object Record Types
 | ||||
|             case 0x02FC: | ||||
|                 { // 2.3.4.1 META_CREATEBRUSHINDIRECT
 | ||||
|                     var obj = {}; | ||||
|                     obj.Brush = { | ||||
|                         Style: data.read_shift(2), | ||||
|                         Color: data.read_shift(4), | ||||
|                         Hatch: data.read_shift(2) | ||||
|                     }; | ||||
|                     add_to_objects(objects, obj); | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x02FB: | ||||
|                 { // 2.3.4.2 META_CREATEFONTINDIRECT
 | ||||
|                     var obj = {}; | ||||
|                     obj.Font = {}; | ||||
|                     /* 2.2.1.2 Font TODO!! */ | ||||
|                     var Height = data.read_shift(2, "i"); | ||||
|                     var Width = data.read_shift(2, "i"); | ||||
|                     var Escapement = data.read_shift(2, "i"); | ||||
|                     var Orientation = data.read_shift(2, "i"); | ||||
|                     var Weight = data.read_shift(2, "i"); | ||||
|                     var Italic = !!data.read_shift(1); | ||||
|                     var Underline = !!data.read_shift(1); | ||||
|                     var StrikeOut = !!data.read_shift(1); | ||||
|                     var CharSet = data.read_shift(1); | ||||
|                     var OutPrecision = data.read_shift(1); | ||||
|                     var ClipPrecision = data.read_shift(1); | ||||
|                     var Quality = data.read_shift(1); | ||||
|                     var PitchAndFamily = data.read_shift(1); | ||||
|                     var Facename = data.read_shift(32, "cstr"); | ||||
|                     obj.Font.Name = Facename; | ||||
|                     obj.Font.Height = Height; | ||||
|                     obj.Font.Weight = Weight; | ||||
|                     obj.Font.Italic = Italic; | ||||
|                     obj.Font.Angle = Escapement / 10; | ||||
|                     add_to_objects(objects, obj); | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x02FA: | ||||
|                 { // 2.3.4.5 META_CREATEPENINDIRECT
 | ||||
|                     var obj = {}; | ||||
|                     obj.Pen = { | ||||
|                         Style: data.read_shift(2), | ||||
|                         Width: data.read_shift(4) & 0xFF, | ||||
|                         Color: data.read_shift(4) | ||||
|                     }; | ||||
|                     add_to_objects(objects, obj); | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x01F0: | ||||
|                 { // 2.3.4.7 META_DELETEOBJECT
 | ||||
|                     var ObjectIndex = data.read_shift(2); | ||||
|                     //console.log("DELETE", ObjectIndex, objects[ObjectIndex]);
 | ||||
|                     objects[ObjectIndex] = null; | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x012C: | ||||
|                 { // 2.3.4.9 META_SELECTCLIPREGION
 | ||||
|                     var Region = data.read_shift(2); | ||||
|                     //console.log("CLIPREGION", Region, objects[Region]);
 | ||||
|                     //Object.assign(state, objects[Region]);
 | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x012D: | ||||
|                 { // 2.3.4.10 META_SELECTOBJECT
 | ||||
|                     var ObjectIndex = data.read_shift(2); | ||||
|                     //console.log("SELECT", ObjectIndex, objects[ObjectIndex]);
 | ||||
|                     Object.assign(state, objects[ObjectIndex]); | ||||
|                     // TODO!!
 | ||||
|                 } | ||||
|                 break; | ||||
|             // #endregion
 | ||||
|             // #region 2.3.5 State Record Types
 | ||||
|             case 0x0416: // 2.3.5.3 META_INTERSECTCLIPRECT
 | ||||
|                 state.ClipRect = [[0, 0], [0, 0]]; | ||||
|                 state.ClipRect[1][1] = data.read_shift(2); | ||||
|                 state.ClipRect[1][0] = data.read_shift(2); | ||||
|                 state.ClipRect[0][1] = data.read_shift(2); | ||||
|                 state.ClipRect[0][0] = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x0127: | ||||
|                 { // 2.3.5.10 META_RESTOREDC
 | ||||
|                     var nSavedDC = data.read_shift(2, 'i'); | ||||
|                     state = states[sidx = (nSavedDC >= 0 ? nSavedDC : sidx + nSavedDC)]; | ||||
|                 } | ||||
|                 break; | ||||
|             case 0x001E: // 2.3.5.11 META_SAVEDC
 | ||||
|                 states.push(state); | ||||
|                 sidx = states.length - 1; | ||||
|                 state = JSON.parse(JSON.stringify(state)); | ||||
|                 break; | ||||
|             case 0x0102: // 2.3.5.15 META_SETBKMODE
 | ||||
|                 state.BkMode = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x0103: // 2.3.5.17 META_SETMAPMODE
 | ||||
|                 state.MapMode = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x0106: // 2.3.5.20 META_SETPOLYFILLMODE
 | ||||
|                 state.PolyFillMode = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x0107: // 2.3.5.23 META_SETSTRETCHBLTMODE
 | ||||
|                 state.StretchMode = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x012E: // 2.3.5.24 META_SETTEXTALIGN
 | ||||
|                 state.TextAlignmentMode = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x0209: // 2.3.5.26 META_SETTEXTCOLOR
 | ||||
|                 state.TextColor = data.read_shift(4); | ||||
|                 break; | ||||
|             case 0x020C: // 2.3.5.30 META_SETWINDOWEXT
 | ||||
|                 state.Extent = [0, 0]; | ||||
|                 state.Extent[1] = data.read_shift(2); | ||||
|                 state.Extent[0] = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x020B: // 2.3.5.31 META_SETWINDOWORG
 | ||||
|                 state.Origin = [0, 0]; | ||||
|                 state.Origin[1] = data.read_shift(2); | ||||
|                 state.Origin[0] = data.read_shift(2); | ||||
|                 break; | ||||
|             // #endregion
 | ||||
|             default: | ||||
|                 if (!Record) | ||||
|                     throw "Record: Unrecognized type 0x" + rt.toString(16); | ||||
|                 console.log(Record); | ||||
|         } | ||||
|         data.l = end; | ||||
|         //if(rt != 0x0626) console.log(Record);
 | ||||
|     } | ||||
|     if (rt !== 0) | ||||
|         throw "Record: Last Record Type " + rt + " is not EOF type"; | ||||
|     return out; | ||||
| }; | ||||
| exports.image_size_prepped_bytes = function (data) { | ||||
|     var origin = [NaN, NaN], extents = [NaN, NaN]; | ||||
|     /* 2.3.22 META_HEADER */ | ||||
|     // Type (2 bytes) must be 1 or 2
 | ||||
|     var h = data.read_shift(2); | ||||
|     if (h != 1 && h != 2) | ||||
|         throw "Header: Type " + h + " must be 1 or 2"; | ||||
|     // HeaderSize expected to be 9
 | ||||
|     if ((h = data.read_shift(2)) != 9) | ||||
|         throw "Header: HeaderSize " + h + " must be 9"; | ||||
|     // Version (2 bytes) 1 or 3
 | ||||
|     h = data.read_shift(2); | ||||
|     if (h != 0x0100 && h != 0x0300) | ||||
|         throw "Header: Version " + h + " must be 0x0100 or 0x0300"; | ||||
|     data.l = 18; | ||||
|     var rt = 0; | ||||
|     while (data.l < data.length) { | ||||
|         h = data.read_shift(4); | ||||
|         var end = data.l + h * 2 - 4; | ||||
|         rt = data.read_shift(2); | ||||
|         if (rt == 0x0000) | ||||
|             break; // META_EOF
 | ||||
|         switch (rt) { | ||||
|             case 0x020C: // 2.3.5.30 META_SETWINDOWEXT
 | ||||
|                 extents[1] = data.read_shift(2); | ||||
|                 extents[0] = data.read_shift(2); | ||||
|                 break; | ||||
|             case 0x020B: // 2.3.5.31 META_SETWINDOWORG
 | ||||
|                 origin[1] = data.read_shift(2); | ||||
|                 origin[0] = data.read_shift(2); | ||||
|                 break; | ||||
|         } | ||||
|         data.l = end; | ||||
|     } | ||||
|     return [extents[0] - origin[0], extents[1] - origin[1]]; | ||||
| }; | ||||
| //# sourceMappingURL=wmf.js.map
 | ||||
							
								
								
									
										1
									
								
								js/wmf.js.map
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								js/wmf.js.map
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										3
									
								
								misc/entry.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										3
									
								
								misc/entry.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| var WMF = require("../js/"); | ||||
| module.exports = WMF; | ||||
							
								
								
									
										37
									
								
								misc/webpack.browser.config.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										37
									
								
								misc/webpack.browser.config.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| const webpack = require('webpack'); | ||||
| const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); | ||||
| 
 | ||||
| const banner = '/*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */'; | ||||
| 
 | ||||
| module.exports = { | ||||
| 	mode: 'development', | ||||
| 	entry: './misc/entry.js', | ||||
| 	devtool: 'source-map', | ||||
| 	output: { | ||||
| 		library: 'WMF', | ||||
| 		libraryTarget: 'var', | ||||
| 		filename: 'wmf.js', | ||||
| 	}, | ||||
| 	node: { process: false }, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.js$/, | ||||
| 				use: ['source-map-loader'], | ||||
| 				enforce: 'pre' | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	optimization: { | ||||
| 		minimizer: [ | ||||
| 			new UglifyJsPlugin({ | ||||
| 				uglifyOptions: { | ||||
| 					output: { beautify: false, preamble: banner } | ||||
| 				} | ||||
| 			}) | ||||
| 		] | ||||
| 	}, | ||||
| 	plugins: [ | ||||
| 		new webpack.BannerPlugin({ banner, raw: true, entryOnly: true }) | ||||
| 	] | ||||
| }; | ||||
							
								
								
									
										39
									
								
								misc/webpack.node.config.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										39
									
								
								misc/webpack.node.config.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| const webpack = require('webpack'); | ||||
| const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); | ||||
| var nodeExternals = require('webpack-node-externals'); | ||||
| 
 | ||||
| const banner = '/*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */'; | ||||
| 
 | ||||
| module.exports = { | ||||
| 	mode: 'development', | ||||
| 	entry: './misc/entry.js', | ||||
| 	devtool: 'source-map', | ||||
| 	output: { | ||||
| 		library: 'WMF', | ||||
| 		libraryTarget: 'commonjs2', | ||||
| 		filename: 'wmf.node.js' | ||||
| 	}, | ||||
| 	target: 'node', | ||||
| 	externals: [nodeExternals()], | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.js$/, | ||||
| 				use: ['source-map-loader'], | ||||
| 				enforce: 'pre' | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	optimization: { | ||||
| 		minimizer: [ | ||||
| 			new UglifyJsPlugin({ | ||||
| 				uglifyOptions: { | ||||
| 					output: { beautify: false, preamble: banner } | ||||
| 				} | ||||
| 			}) | ||||
| 		] | ||||
| 	}, | ||||
| 	plugins: [ | ||||
| 		new webpack.BannerPlugin({ banner, raw: true, entryOnly: true }) | ||||
| 	] | ||||
| }; | ||||
							
								
								
									
										20
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										20
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| { | ||||
| 	"name": "wmf", | ||||
| 	"version": "1.0.0", | ||||
| 	"author": "sheetjs", | ||||
| 	"description": "Windows MetaFile (WMF) parser", | ||||
| 	"keywords": [ "wmf", "image" ], | ||||
| 	"main": "./dist/wmf.node.js", | ||||
| 	"browser": "./js/index.js", | ||||
| 	"dependencies": { | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"source-map-loader": "^0.2.4", | ||||
| 		"uglifyjs-webpack-plugin": "^2.2.0" | ||||
| 	}, | ||||
| 	"homepage": "https://sheetjs.com/", | ||||
| 	"files": ["dist/", "LICENSE", "README.md", "js/"], | ||||
| 	"bugs": { "url": "https://github.com/SheetJS/js-wmf/issues" }, | ||||
| 	"license": "Apache-2.0", | ||||
| 	"engines": { "node": ">=12.0" } | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/Records.ts
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										50
									
								
								src/Records.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| export interface WMFRecord { | ||||
| 	n: string; | ||||
| } | ||||
| 
 | ||||
| export interface WMFEscape { | ||||
| 	n: string; | ||||
| } | ||||
| 
 | ||||
| /* 2.1.1.1 RecordType Enumeration */ | ||||
| export const WMFRecords: {[key: number]: WMFRecord} = { | ||||
| 	0x0000: { n: "META_EOF" }, // 2.3.2.1
 | ||||
| 	0x0626: { n: "META_ESCAPE" }, // 2.3.6.1
 | ||||
| 
 | ||||
| 	0x0940: { n: "META_DIBBITBLT" }, // 2.3.1.2
 | ||||
| 	0x0B41: { n: "META_DIBSTRETCHBLT" }, // 2.3.1.3
 | ||||
| 
 | ||||
| 	0x0A32: { n: "META_EXTTEXTOUT" }, // 2.3.3.5
 | ||||
| 	0x0325: { n: "META_POLYLINE" }, // 2.3.3.14
 | ||||
| 	0x0324: { n: "META_POLYGON" }, // 2.3.3.15
 | ||||
| 	0x0538: { n: "META_POLYPOLYGON" }, // 2.3.3.16
 | ||||
| 
 | ||||
| 	0x02FC: { n: "META_CREATEBRUSHINDIRECT" }, // 2.3.4.1
 | ||||
| 	0x02FB: { n: "META_CREATEFONTINDIRECT" }, // 2.3.4.2
 | ||||
| 	0x02FA: { n: "META_CREATEPENINDIRECT" }, // 2.3.4.5
 | ||||
| 	0x01F0: { n: "META_DELETEOBJECT" }, // 2.3.4.7
 | ||||
| 	0x012C: { n: "META_SELECTCLIPREGION" }, // 2.3.4.9
 | ||||
| 	0x012D: { n: "META_SELECTOBJECT" }, // 2.3.4.10
 | ||||
| 
 | ||||
| 	0x0416: { n: "META_INTERSECTCLIPRECT" }, // 2.3.5.3
 | ||||
| 	0x0035: { n: "META_REALIZEPALETTE" }, // 2.3.5.8
 | ||||
| 	0x0127: { n: "META_RESTOREDC" }, // 2.3.5.10
 | ||||
| 	0x001E: { n: "META_SAVEDC" }, // 2.3.5.11
 | ||||
| 	0x0102: { n: "META_SETBKMODE" }, // 2.3.5.15
 | ||||
| 	0x0103: { n: "META_SETMAPMODE" }, // 2.3.5.17
 | ||||
| 	0x0037: { n: "META_SETPALENTRIES" }, // 2.3.5.19
 | ||||
| 	0x0106: { n: "META_SETPOLYFILLMODE" }, // 2.3.5.20
 | ||||
| 	0x0107: { n: "META_SETSTRETCHBLTMODE" }, // 2.3.5.23
 | ||||
| 	0x012E: { n: "META_SETTEXTALIGN" }, // 2.3.5.24
 | ||||
| 	0x0209: { n: "META_SETTEXTCOLOR" }, // 2.3.5.26
 | ||||
| 	0x020C: { n: "META_SETWINDOWEXT" }, // 2.3.5.30
 | ||||
| 	0x020B: { n: "META_SETWINDOWORG" }, // 2.3.5.31
 | ||||
| 
 | ||||
| 	0xFFFF: { n: "META_SHEETJS" } | ||||
| }; | ||||
| 
 | ||||
| export const WMFEscapes: {[key: number]: WMFEscape} = { | ||||
| 	0x000F: { n: "META_ESCAPE_ENHANCED_METAFILE" } | ||||
| }; | ||||
| 
 | ||||
							
								
								
									
										98
									
								
								src/canvas.ts
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										98
									
								
								src/canvas.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| import { PreppedBytes, prep_blob } from './util'; | ||||
| import { Action, PlaybackDeviceContextState, get_actions_prepped_bytes } from './wmf' | ||||
| 
 | ||||
| export const css_color = (clr: number): string => `#${(clr & 0xFF).toString(16).padStart(2, "0")}${((clr>>8) & 0xFF).toString(16).padStart(2, "0")}${((clr>>16) & 0xFF).toString(16).padStart(2, "0")}` | ||||
| 
 | ||||
| export const set_ctx_state = (ctx: CanvasRenderingContext2D, state: PlaybackDeviceContextState): void => { | ||||
| 	if(!state) return; | ||||
| 	let font = ""; | ||||
| 	if(state.Font) { | ||||
| 		if(state.Font.Italic) font += " italic"; | ||||
| 		if(state.Font.Weight) font += ` ${state.Font.Weight == 700 ? "bold" : state.Font.Weight == 400 ? "" : state.Font.Weight}`; | ||||
| 		if(state.Font.Height < 0) font += ` ${-state.Font.Height}px`; | ||||
| 		else if(state.Font.Height > 0) font += ` ${state.Font.Height}px`; | ||||
| 		let name = state.Font.Name || ""; | ||||
| 		if(name == "System") name = "Calibri"; // TODO: default sys font is Segoe UI
 | ||||
| 		if(name) font += ` '${name}', sans-serif`; | ||||
| 		ctx.font = font.trim(); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // TODO: DIB BIT ORDER?
 | ||||
| export const render_actions_to_context = (out: Action[], ctx: CanvasRenderingContext2D) => { | ||||
| 	out.forEach(act => { | ||||
| 		ctx.save(); | ||||
| 		set_ctx_state(ctx, act.s); | ||||
| 		switch(act.t) { | ||||
| 			case "poly": | ||||
| 				ctx.beginPath(); | ||||
| 				ctx.moveTo(act.p[0][0], act.p[0][1]); | ||||
| 				act.p.slice(1).forEach(([x,y]) => { | ||||
| 					ctx.lineTo(x, y); | ||||
| 				}); | ||||
| 				if(act.g) ctx.closePath(); | ||||
| 				ctx.stroke(); | ||||
| 				break; | ||||
| 			case "text": | ||||
| 				if(act.s && act.s.TextColor) ctx.fillStyle = css_color(act.s.TextColor); | ||||
| 				if(act.s.Font.Angle != 0) { | ||||
| 					ctx.translate(act.p[0], act.p[1]); | ||||
| 					ctx.rotate(-act.s.Font.Angle * Math.PI / 180); | ||||
| 					ctx.fillText(act.v, 0, 0); | ||||
| 					ctx.translate(-act.p[0], -act.p[1]); | ||||
| 				} | ||||
| 				else ctx.fillText(act.v, act.p[0], act.p[1]); | ||||
| 				break; | ||||
| 			case "cpy": { | ||||
| 				// TODO: base on ROP
 | ||||
| 				const idata = ctx.getImageData(act.src[0][0], act.src[1][0], act.src[0][1], act.src[1][1]); | ||||
| 				ctx.putImageData(idata, act.dst[0], act.dst[1]); | ||||
| 			} break; | ||||
| 			case "str": { | ||||
| 				if(act.data && act.data.BitCount == 24 && act.data.ImageData) { | ||||
| 					const _o = new Uint8ClampedArray(act.data.Width * act.data.Height * 4); | ||||
| 					for(let i = 0; i < act.data.Width * act.data.Height; ++i) { | ||||
| 						const j = (i % act.data.Width) + act.data.Width * (act.data.Height - 1 - Math.floor(i / act.data.Width)); | ||||
| 						_o[4*i] = act.data.ImageData[3*j+2]; | ||||
| 						_o[4*i+1] = act.data.ImageData[3*j+1]; | ||||
| 						_o[4*i+2] = act.data.ImageData[3*j]; | ||||
| 						_o[4*i+3] = 255; | ||||
| 					} | ||||
| 					const idata = new ImageData(_o, act.data.Width, act.data.Height); | ||||
| 					ctx.putImageData(idata, act.dst[0][0], act.dst[1][0]); | ||||
| 				} | ||||
| 				// TODO: ROP et al
 | ||||
| 			} | ||||
| 		} | ||||
| 		ctx.restore(); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| export const render_canvas = (out: Action[], image: HTMLCanvasElement): void => { | ||||
| 	let ctx: CanvasRenderingContext2D; | ||||
| 
 | ||||
| 	/* find first action with window info */ | ||||
| 	out.forEach(act => { | ||||
| 		if(ctx) return; | ||||
| 		if(!act.s) return; | ||||
| 		if(!act.s.Extent || !act.s.Origin) return; | ||||
| 		image.width = act.s.Extent[0] - act.s.Origin[0]; | ||||
| 		image.height = act.s.Extent[1] - act.s.Origin[1]; | ||||
| 		ctx = image.getContext('2d'); | ||||
| 		ctx.save(); | ||||
| 		ctx.fillStyle = 'rgb(255,255,255)'; | ||||
| 		ctx.fillRect(0, 0, act.s.Extent[0] - act.s.Origin[0], act.s.Extent[1] - act.s.Origin[1]) | ||||
| 		ctx.restore(); | ||||
| 	}); | ||||
| 
 | ||||
| 	if(!ctx) ctx = image.getContext('2d'); | ||||
| 	render_actions_to_context(out, ctx); | ||||
| } | ||||
| 
 | ||||
| export const draw_canvas = (data: Buffer | Uint8Array | ArrayBuffer, image: HTMLCanvasElement): void => { | ||||
| 	if(data instanceof ArrayBuffer) return draw_canvas(new Uint8Array(data), image); | ||||
| 	prep_blob((data as any), 0); | ||||
| 	const out: Action[] = get_actions_prepped_bytes(data as PreppedBytes); | ||||
| 	return render_canvas(out, image); | ||||
| }; | ||||
							
								
								
									
										17
									
								
								src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										17
									
								
								src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| import { PreppedBytes, prep_blob } from './util'; | ||||
| import { Action, get_actions_prepped_bytes, image_size_prepped_bytes } from './wmf' | ||||
| 
 | ||||
| export { draw_canvas, render_canvas } from './canvas'; | ||||
| 
 | ||||
| export const get_actions = (data: Buffer | Uint8Array | ArrayBuffer): Action[] => { | ||||
| 	if(data instanceof ArrayBuffer) return get_actions(new Uint8Array(data)); | ||||
| 	prep_blob((data as any), 0); | ||||
| 	return get_actions_prepped_bytes(data as PreppedBytes); | ||||
| } | ||||
| 
 | ||||
| export const image_size = (data: Buffer | Uint8Array | ArrayBuffer): [number, number] => { | ||||
| 	if(data instanceof ArrayBuffer) return image_size(new Uint8Array(data)); | ||||
| 	prep_blob((data as any), 0); | ||||
| 	return image_size_prepped_bytes(data as PreppedBytes); | ||||
| } | ||||
							
								
								
									
										297
									
								
								src/util.ts
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										297
									
								
								src/util.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,297 @@ | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| export type RawBytes = Buffer | number[]; | ||||
| 
 | ||||
| export type PreppedBytes = RawBytes & { | ||||
|   l: number; | ||||
|   read_shift(size: 1): number; | ||||
|   read_shift(size: 2): number; | ||||
|   read_shift(size: 2, t: "i"): number; | ||||
|   read_shift(size: 4): number; | ||||
|   read_shift(size: 4, t: "i"): number; | ||||
|   read_shift(size: 8, t: "f"): number; | ||||
|   read_shift(size: number, t: "cstr"): string; | ||||
|   read_shift(size: number, t: "cpstr"): string; | ||||
|   read_shift(size: number, t: "_wstr"): string; | ||||
|   read_shift(size: number, t?: string): number|string; | ||||
|   chk(hexstr: string, fld: string): void; | ||||
|   write_shift(t: number, val: string|number, f?: string): void; | ||||
| }; | ||||
| 
 | ||||
| // ---
 | ||||
| 
 | ||||
| const has_buf = !!(typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && process.versions.node); | ||||
| 
 | ||||
| let Buffer_from: typeof Buffer.from; | ||||
| 
 | ||||
| if(typeof Buffer !== 'undefined') { | ||||
|   let nbfs = !Buffer.from; | ||||
| 	if(!nbfs) try { | ||||
|     Buffer.from("foo", "utf8"); | ||||
|   } catch(e) { nbfs = true; } | ||||
| 	Buffer_from = nbfs ? ((buf, enc?: string): Buffer => (enc) ? new Buffer(buf, (enc as BufferEncoding)) : new Buffer(buf)) : Buffer.from.bind(Buffer); | ||||
| 	if(!Buffer.alloc) Buffer.alloc = function(n: number): Buffer { return new Buffer(n); }; | ||||
| 	if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n: number): Buffer { return new Buffer(n); }; | ||||
| } | ||||
| 
 | ||||
| export { Buffer_from, has_buf }; | ||||
| 
 | ||||
| export const new_raw_buf = (len: number): Buffer|number[] => has_buf ? Buffer.alloc(len) : new Array(len); | ||||
| 
 | ||||
| export const new_unsafe_buf = (len: number): Buffer|number[] => has_buf ? Buffer.allocUnsafe(len) : new Array(len); | ||||
| 
 | ||||
| export const _chr = (c: number): string => String.fromCharCode(c); | ||||
| 
 | ||||
| export const chr0 = /\u0000/g; // eslint-disable-line no-control-regex
 | ||||
| export const chr1 = /[\u0001-\u0006]/g; // eslint-disable-line no-control-regex
 | ||||
| 
 | ||||
| // ---
 | ||||
| 
 | ||||
| const read_double_le = (b: RawBytes, idx: number): number => { | ||||
| 	const s = 1 - 2 * (b[idx + 7] >>> 7); | ||||
| 	let e = ((b[idx + 7] & 0x7f) << 4) + ((b[idx + 6] >>> 4) & 0x0f); | ||||
| 	let m = (b[idx+6]&0x0f); | ||||
| 	for(let i = 5; i >= 0; --i) m = m * 256 + b[idx + i]; | ||||
| 	if(e == 0x7ff) return m == 0 ? (s * Infinity) : NaN; | ||||
| 	if(e == 0) e = -1022; | ||||
| 	else { e -= 1023; m += Math.pow(2,52); } | ||||
| 	return s * Math.pow(2, e - 52) * m; | ||||
| }; | ||||
| 
 | ||||
| const write_double_le = (b: RawBytes, v: number, idx: number): void => { | ||||
| 	const bs = ((((v < 0) || (1/v == -Infinity)) ? 1 : 0) << 7); | ||||
| 	let e = 0, m = 0; | ||||
| 	const av = bs ? (-v) : v; | ||||
| 	if(!isFinite(av)) { e = 0x7ff; m = isNaN(v) ? 0x6969 : 0; } | ||||
| 	else if(av == 0) e = m = 0; | ||||
| 	else { | ||||
| 		e = Math.floor(Math.log(av) / Math.LN2); | ||||
| 		m = av * Math.pow(2, 52 - e); | ||||
| 		if((e <= -1023) && (!isFinite(m) || (m < Math.pow(2,52)))) { e = -1022; } | ||||
| 		else { m -= Math.pow(2,52); e+=1023; } | ||||
| 	} | ||||
| 	for(let i = 0; i <= 5; ++i, m/=256) b[idx + i] = m & 0xff; | ||||
| 	b[idx + 6] = ((e & 0x0f) << 4) | (m & 0xf); | ||||
| 	b[idx + 7] = (e >> 4) | bs; | ||||
| }; | ||||
| 
 | ||||
| let __toBuffer = (bufs/*:Array<Array<RawBytes> >*/): RawBytes => { | ||||
| 	const x: number[] =[]; | ||||
| 	for(let i=0; i<bufs[0].length; ++i) if(bufs[0][i]) | ||||
| 		for(let j=0, L=bufs[0][i].length; j<L; j+=10240) x.push(...bufs[0][i].slice(j,j+10240)); | ||||
| 	return x; | ||||
| }; | ||||
| const ___toBuffer = __toBuffer; | ||||
| 
 | ||||
| const __readUInt8 = (b: RawBytes, idx: number): number => b[idx]; | ||||
| const __readUInt16LE = (b: RawBytes, idx: number): number => (b[idx+1]*(1<<8))+b[idx]; | ||||
| const __readInt16LE = (b: RawBytes, idx: number): number => { const u = (b[idx+1]*(1<<8))+b[idx]; return (u < 0x8000) ? u : ((0xffff - u + 1) * -1); }; | ||||
| const __readUInt32LE = (b: RawBytes, idx: number): number => b[idx+3]*(1<<24)+(b[idx+2]<<16)+(b[idx+1]<<8)+b[idx]; | ||||
| const __readInt32LE = (b: RawBytes, idx: number): number => (b[idx+3]<<24)|(b[idx+2]<<16)|(b[idx+1]<<8)|b[idx]; | ||||
| const __readInt32BE = (b: RawBytes, idx: number): number => (b[idx]<<24)|(b[idx+1]<<16)|(b[idx+2]<<8)|b[idx+3]; | ||||
| 
 | ||||
| let __utf16le = (b: RawBytes, s: number, e: number): string => { | ||||
| 	const ss: string[] = []; | ||||
| 	for(let i=s; i<e; i+=2) ss.push(String.fromCharCode(__readUInt16LE(b,i))); | ||||
| 	return ss.join("").replace(chr0,''); | ||||
| }; | ||||
| const ___utf16le = __utf16le; | ||||
| let __hexlify = function(b/*:RawBytes|CFBlob*/,s: number,l: number): string { const ss: string[] = []; for(let i=s; i<s+l; ++i) ss.push(("0" + b[i].toString(16)).slice(-2)); return ss.join(""); }; | ||||
| const ___hexlify = __hexlify; | ||||
| let __utf8 = function(b/*:RawBytes|CFBlob*/,s: number,e: number): string { const ss=[]; for(let i=s; i<e; i++) ss.push(String.fromCharCode(__readUInt8(b,i))); return ss.join(""); }; | ||||
| const ___utf8 = __utf8; | ||||
| let __lpstr = function(b/*:RawBytes|CFBlob*/,i: number): string { const len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";}; | ||||
| const ___lpstr = __lpstr; | ||||
| let __cpstr = function(b/*:RawBytes|CFBlob*/,i: number): string { const len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";}; | ||||
| const ___cpstr = __cpstr; | ||||
| let __lpwstr = function(b/*:RawBytes|CFBlob*/,i: number): string { const len = 2*__readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";}; | ||||
| const ___lpwstr = __lpwstr; | ||||
| let __lpp4, ___lpp4; | ||||
| __lpp4 = ___lpp4 = function lpp4_(b/*:RawBytes|CFBlob*/,i: number): string { const len = __readUInt32LE(b,i); return len > 0 ? __utf16le(b, i+4,i+4+len) : "";}; | ||||
| const ___8lpp4 = function(b/*:RawBytes|CFBlob*/,i: number): string { const len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len) : "";}; | ||||
| let __8lpp4 = ___8lpp4; | ||||
| const ___double = (b/*:RawBytes|CFBlob*/, idx: number): number => read_double_le(b, idx); | ||||
| let __double = ___double; | ||||
| 
 | ||||
| if(has_buf) { | ||||
| 	__utf16le = (b/*:RawBytes|CFBlob*/,s: number,e: number): string => (!Buffer.isBuffer(b)) ?  ___utf16le(b,s,e) : b.toString('utf16le',s,e).replace(chr0,''); | ||||
| 	__hexlify = (b/*:RawBytes|CFBlob*/,s: number,l: number): string => Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); | ||||
| 	__lpstr = (b/*:RawBytes|CFBlob*/, i: number): string => { if(!Buffer.isBuffer(b)) return ___lpstr(b, i); const len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";}; | ||||
| 	__cpstr = (b/*:RawBytes|CFBlob*/, i: number): string => { if(!Buffer.isBuffer(b)) return ___cpstr(b, i); const len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";}; | ||||
| 	__lpwstr = (b/*:RawBytes|CFBlob*/, i: number): string => { if(!Buffer.isBuffer(b)) return ___lpwstr(b, i); const len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);}; | ||||
| 	__lpp4 = (b/*:RawBytes|CFBlob*/, i: number): string => { if(!Buffer.isBuffer(b)) return ___lpp4(b, i); const len = b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len);}; | ||||
| 	__8lpp4 = (b/*:RawBytes|CFBlob*/, i: number): string => { if(!Buffer.isBuffer(b)) return ___8lpp4(b, i); const len = b.readUInt32LE(i); return b.toString('utf8',i+4,i+4+len);}; | ||||
| 	__utf8 = (b/*:RawBytes|CFBlob*/, s: number, e: number): string => (Buffer.isBuffer(b)) ? b.toString('utf8',s,e) : ___utf8(b,s,e); | ||||
| 	__toBuffer = (bufs): RawBytes => (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs); | ||||
| 	__double = (b/*:RawBytes|CFBlob*/, i: number): number => (Buffer.isBuffer(b)) ? b.readDoubleLE(i) : ___double(b,i); | ||||
| } | ||||
| 
 | ||||
| function ReadShift(size: 1): number; | ||||
| function ReadShift(size: 2): number; | ||||
| function ReadShift(size: 2, t: "i"): number; | ||||
| function ReadShift(size: 4): number; | ||||
| function ReadShift(size: 4, t: "i"): number; | ||||
| function ReadShift(size: 8, t: "f"): number; | ||||
| function ReadShift(size: number, t: "cstr"): string; | ||||
| function ReadShift(size: number, t: "cpstr"): string; | ||||
| function ReadShift(size: number, t: "_wstr"): string; | ||||
| function ReadShift(size: number, t?: string): number|string { | ||||
| 	let o="", oI = 0, oR, w, vv, i, loc; | ||||
| 	const oo = []; | ||||
| 	switch(t) { | ||||
| 		case 'dbcs': | ||||
| 			loc = this.l; | ||||
| 			if(has_buf && Buffer.isBuffer(this)) o = this.slice(this.l, this.l+2*size).toString("utf16le"); | ||||
| 			else for(i = 0; i < size; ++i) { o+=String.fromCharCode(__readUInt16LE(this, loc)); loc+=2; } | ||||
| 			size *= 2; | ||||
| 			break; | ||||
| 
 | ||||
| 		case 'utf8': o = __utf8(this, this.l, this.l + size); break; | ||||
| 		case 'utf16le': size *= 2; o = __utf16le(this, this.l, this.l + size); break; | ||||
| 
 | ||||
| 		case 'wstr': | ||||
| 			return ReadShift.call(this, size, 'dbcs'); | ||||
| 
 | ||||
| 		/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */ | ||||
| 		case 'lpstr-ansi': o = __lpstr(this, this.l); size = 4 + __readUInt32LE(this, this.l); break; | ||||
| 		case 'lpstr-cp': o = __cpstr(this, this.l); size = 4 + __readUInt32LE(this, this.l); break; | ||||
| 		/* [MS-OLEDS] 2.1.5 LengthPrefixedUnicodeString */ | ||||
| 		case 'lpwstr': o = __lpwstr(this, this.l); size = 4 + 2 * __readUInt32LE(this, this.l); break; | ||||
| 		/* [MS-OFFCRYPTO] 2.1.2 Length-Prefixed Padded Unicode String (UNICODE-LP-P4) */ | ||||
| 		case 'lpp4': size = 4 +  __readUInt32LE(this, this.l); o = __lpp4(this, this.l); if(size & 0x02) size += 2; break; | ||||
| 		/* [MS-OFFCRYPTO] 2.1.3 Length-Prefixed UTF-8 String (UTF-8-LP-P4) */ | ||||
| 		case '8lpp4': size = 4 +  __readUInt32LE(this, this.l); o = __8lpp4(this, this.l); if(size & 0x03) size += 4 - (size & 0x03); break; | ||||
| 
 | ||||
| 		case 'cstr': size = 0; o = ""; | ||||
| 			while((w=__readUInt8(this, this.l + size++))!==0) oo.push(String.fromCharCode(w)); | ||||
| 			o = oo.join(""); break; | ||||
| 		case '_wstr': size = 0; o = ""; | ||||
| 			while((w=__readUInt16LE(this,this.l +size))!==0){oo.push(String.fromCharCode(w));size+=2;} | ||||
| 			size+=2; o = oo.join(""); break; | ||||
| 
 | ||||
| 		/* sbcs and dbcs support continue records in the SST way TODO codepages */ | ||||
| 		case 'dbcs-cont': o = ""; loc = this.l; | ||||
| 			for(i = 0; i < size; ++i) { | ||||
| 				if(this.lens && this.lens.indexOf(loc) !== -1) { | ||||
| 					w = __readUInt8(this, loc); | ||||
| 					this.l = loc + 1; | ||||
| 					vv = ReadShift.call(this, size-i, w ? 'dbcs-cont' : 'sbcs-cont'); | ||||
| 					return oo.join("") + vv; | ||||
| 				} | ||||
| 				oo.push(String.fromCharCode(__readUInt16LE(this, loc))); | ||||
| 				loc+=2; | ||||
| 			} o = oo.join(""); size *= 2; break; | ||||
| 
 | ||||
| 		case 'cpstr': | ||||
| 		/* falls through */ | ||||
| 		case 'sbcs-cont': o = ""; loc = this.l; | ||||
| 			for(i = 0; i != size; ++i) { | ||||
| 				if(this.lens && this.lens.indexOf(loc) !== -1) { | ||||
| 					w = __readUInt8(this, loc); | ||||
| 					this.l = loc + 1; | ||||
| 					vv = ReadShift.call(this, size-i, w ? 'dbcs-cont' : 'sbcs-cont'); | ||||
| 					return oo.join("") + vv; | ||||
| 				} | ||||
| 				oo.push(String.fromCharCode(__readUInt8(this, loc))); | ||||
| 				loc+=1; | ||||
| 			} o = oo.join(""); break; | ||||
| 
 | ||||
| 		default: | ||||
| 	switch(size) { | ||||
| 		case 1: oI = __readUInt8(this, this.l); this.l++; return oI; | ||||
| 		case 2: oI = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l); this.l += 2; return oI; | ||||
| 		case 4: case -4: | ||||
| 			if(t === 'i' || ((this[this.l+3] & 0x80)===0)) { oI = ((size > 0) ? __readInt32LE : __readInt32BE)(this, this.l); this.l += 4; return oI; } | ||||
| 			else { oR = __readUInt32LE(this, this.l); this.l += 4; } return oR; | ||||
| 		case 8: case -8: | ||||
| 			if(t === 'f') { | ||||
| 				if(size == 8) oR = __double(this, this.l); | ||||
| 				else oR = __double([this[this.l+7],this[this.l+6],this[this.l+5],this[this.l+4],this[this.l+3],this[this.l+2],this[this.l+1],this[this.l+0]], 0); | ||||
| 				this.l += 8; return oR; | ||||
| 			} else size = 8; | ||||
| 		/* falls through */ | ||||
| 		case 16: o = __hexlify(this, this.l, size); break; | ||||
| 	}} | ||||
| 	this.l+=size; return o; | ||||
| } | ||||
| 
 | ||||
| const __writeUInt32LE = (b/*:RawBytes|CFBlob*/, val: number, idx: number): void => { b[idx] = (val & 0xFF); b[idx+1] = ((val >>> 8) & 0xFF); b[idx+2] = ((val >>> 16) & 0xFF); b[idx+3] = ((val >>> 24) & 0xFF); }; | ||||
| const __writeInt32LE  = (b/*:RawBytes|CFBlob*/, val: number, idx: number): void => { b[idx] = (val & 0xFF); b[idx+1] = ((val >> 8) & 0xFF); b[idx+2] = ((val >> 16) & 0xFF); b[idx+3] = ((val >> 24) & 0xFF); }; | ||||
| const __writeUInt16LE = (b/*:RawBytes|CFBlob*/, val: number, idx: number): void => { b[idx] = (val & 0xFF); b[idx+1] = ((val >>> 8) & 0xFF); }; | ||||
| 
 | ||||
| function WriteShift(t: number, val: string|number, f?: string): void { | ||||
| 	let size = 0, i = 0; | ||||
| 	if(f === 'dbcs') { | ||||
| 		if(typeof val !== 'string') throw new Error("expected string"); | ||||
| 		for(i = 0; i != val.length; ++i) __writeUInt16LE(this, val.charCodeAt(i), this.l + 2 * i); | ||||
| 		size = 2 * val.length; | ||||
| 	} else if(f === 'sbcs') { | ||||
| 		{ | ||||
| 			val = (val as string).replace(/[^\x00-\x7F]/g, "_"); // eslint-disable-line no-control-regex
 | ||||
| 			for(i = 0; i != val.length; ++i) this[this.l + i] = (val.charCodeAt(i) & 0xFF); | ||||
| 		} | ||||
| 		size = val.length; | ||||
| 	} else if(f === 'hex') { | ||||
| 		for(; i < t; ++i) { | ||||
| 			this[this.l++] = (parseInt((val as string).slice(2*i, 2*i+2), 16)||0); | ||||
| 		} return this; | ||||
| 	} else if(f === 'utf16le') { | ||||
| 			/*:: if(typeof val !== "string") throw new Error("unreachable"); */ | ||||
| 			const end: number = Math.min(this.l + t, this.length); | ||||
| 			for(i = 0; i < Math.min((val as string).length, t); ++i) { | ||||
| 				const cc = (val as string).charCodeAt(i); | ||||
| 				this[this.l++] = (cc & 0xff); | ||||
| 				this[this.l++] = (cc >> 8); | ||||
| 			} | ||||
| 			while(this.l < end) this[this.l++] = 0; | ||||
| 			return this; | ||||
| 	} else if(typeof val === 'number') switch(t) { | ||||
| 		case  1: size = 1; this[this.l] = val&0xFF; break; | ||||
| 		case  2: size = 2; this[this.l] = val&0xFF; val >>>= 8; this[this.l+1] = val&0xFF; break; | ||||
| 		case  3: size = 3; this[this.l] = val&0xFF; val >>>= 8; this[this.l+1] = val&0xFF; val >>>= 8; this[this.l+2] = val&0xFF; break; | ||||
| 		case  4: size = 4; __writeUInt32LE(this, val, this.l); break; | ||||
| 		case  8: size = 8; if(f === 'f') { write_double_le(this, val, this.l); break; } | ||||
| 		/* falls through */ | ||||
| 		case 16: break; | ||||
| 		case -4: size = 4; __writeInt32LE(this, val, this.l); break; | ||||
| 	} | ||||
| 	this.l += size; return this; | ||||
| } | ||||
| 
 | ||||
| function CheckField(hexstr: string, fld: string): void { | ||||
| 	const m = __hexlify(this,this.l,hexstr.length>>1); | ||||
| 	if(m !== hexstr) throw new Error(fld + 'Expected ' + hexstr + ' saw ' + m); | ||||
| 	this.l += hexstr.length>>1; | ||||
| } | ||||
| 
 | ||||
| const prep_blob = (blob: PreppedBytes, pos: number): void => { | ||||
| 	blob.l = pos; | ||||
| 	blob.read_shift = ReadShift; | ||||
| 	blob.chk = CheckField; | ||||
| 	blob.write_shift = WriteShift; | ||||
| }; | ||||
| 
 | ||||
| const new_buf = (sz: number): PreppedBytes => { | ||||
| 	const o = (new_raw_buf(sz) as PreppedBytes); | ||||
| 	prep_blob(o, 0); | ||||
| 	return o; | ||||
| }; | ||||
| 
 | ||||
| export { ReadShift, WriteShift, CheckField, prep_blob, new_buf, __utf16le }; | ||||
| 
 | ||||
| // ---
 | ||||
| 
 | ||||
| const __bconcat = function(bufs/*:Array<RawBytes>*/): Buffer | Uint8Array | number[] { | ||||
|   let is_all_arrays = true; | ||||
|   for(let w = 0; w < bufs.length; ++w) if(!Array.isArray(bufs[w])) is_all_arrays = false; | ||||
| 	if(is_all_arrays) return [].concat(...bufs); | ||||
| 	let maxlen = 0, i = 0; | ||||
| 	for(i = 0; i < bufs.length; ++i) maxlen += bufs[i].length; | ||||
| 	const o = new Uint8Array(maxlen); | ||||
| 	for(i = 0, maxlen = 0; i < bufs.length; maxlen += bufs[i].length, ++i) o.set(bufs[i], maxlen); | ||||
| 	return o; | ||||
| }; | ||||
| let bconcat = __bconcat; | ||||
| 
 | ||||
| if(has_buf) bconcat = (bufs): Buffer | Uint8Array | number[] => Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : [].concat(...bufs); | ||||
| 
 | ||||
| export { bconcat }; | ||||
							
								
								
									
										533
									
								
								src/wmf.ts
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										533
									
								
								src/wmf.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,533 @@ | ||||
| /*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */ | ||||
| import { PreppedBytes, RawBytes, bconcat, prep_blob } from './util'; | ||||
| import { WMFRecords, WMFEscapes } from './Records'; | ||||
| 
 | ||||
| export interface Brush { | ||||
| 	/** Style (MS-WMF 2.1.1.4) */ | ||||
| 	Style?: Number; | ||||
| 	/** Brush color RGB */ | ||||
| 	Color?: number; | ||||
| 	/** Hatch Type (2.1.1.12 if brush is hatched) */ | ||||
| 	Hatch?: number; | ||||
| } | ||||
| 
 | ||||
| export interface Pen { | ||||
| 	Style?: number; | ||||
| 	Width?: number; | ||||
| 	Color?: number; | ||||
| } | ||||
| 
 | ||||
| export interface Font { | ||||
| 	Name?: string; | ||||
| 	Height?: number; | ||||
| 	Italic?: boolean; | ||||
| 	Weight?: number; | ||||
| 	Angle?: number; | ||||
| } | ||||
| 
 | ||||
| export interface PlaybackDeviceContextState { | ||||
| 	/** Mapping mode (MS-WMF 2.1.1.16) */ | ||||
| 	MapMode?: number; | ||||
| 	/** Output window origin (X, Y) */ | ||||
| 	Origin?: [number, number]; | ||||
| 	/** Output window extents (X, Y) */ | ||||
| 	Extent?: [number, number]; | ||||
| 	/** Background Mix Mode (MS-WMF 2.1.1.20) */ | ||||
| 	BkMode?: number; | ||||
| 	/** Polygon fill mode (MS-WMF 2.1.1.25) */ | ||||
| 	PolyFillMode?: number; | ||||
| 	/** Bitmap stretching mode (MS-WMF 2.1.1.30) */ | ||||
| 	StretchMode?: number; | ||||
| 	/** Text alignment mode (MS-WMF 2.1.2.3 / 2.1.2.4) */ | ||||
| 	TextAlignmentMode?: number; | ||||
| 	/** Text foreground color RGB */ | ||||
| 	TextColor?: number; | ||||
| 	/** Brush */ | ||||
| 	Brush?: Brush; | ||||
| 	/** Font */ | ||||
| 	Font?: Font; | ||||
| 	/** Pen */ | ||||
| 	Pen?: Pen; | ||||
| 	/** Clipping Region (x,y) LT (x,y) RB */ | ||||
| 	ClipRect?: [[number, number], [number, number]]; | ||||
| } | ||||
| 
 | ||||
| /** [x, y] */ | ||||
| export type Point = [ number, number ]; | ||||
| 
 | ||||
| export interface ActionCommon { | ||||
| 	/** State */ | ||||
| 	s?: PlaybackDeviceContextState; | ||||
| } | ||||
| 
 | ||||
| /** Draw Text */ | ||||
| export interface ActionText extends ActionCommon { | ||||
| 	/** Action Type */ | ||||
| 	t: "text"; | ||||
| 
 | ||||
| 	/** Text */ | ||||
| 	v: string; | ||||
| 
 | ||||
| 	/** Origin */ | ||||
| 	p?: Point; | ||||
| } | ||||
| 
 | ||||
| /** Draw Polygon (shape with stroke/fill) / Polyline (stroke only) */ | ||||
| export interface ActionPoly extends ActionCommon { | ||||
| 	/** Action Type */ | ||||
| 	t: "poly"; | ||||
| 
 | ||||
| 	/** Points */ | ||||
| 	p: Point[]; | ||||
| 
 | ||||
| 	/** Polygon (true) or Polyline (false) */ | ||||
| 	g: boolean; | ||||
| } | ||||
| 
 | ||||
| export interface ActionRaster { | ||||
| 	/** Raster Operaton 2.1.1.31 */ | ||||
| 	rop?: number; | ||||
| } | ||||
| 
 | ||||
| export interface ActionCpy extends ActionCommon, ActionRaster { | ||||
| 	t: "cpy"; | ||||
| 
 | ||||
| 	/** Source [[X, W], [Y, H]] */ | ||||
| 	src: [[number, number], [number, number]]; | ||||
| 
 | ||||
| 	dst: Point; | ||||
| 
 | ||||
| 	data?: any; | ||||
| } | ||||
| 
 | ||||
| export interface ActionStr extends ActionCommon, ActionRaster { | ||||
| 	t: "str"; | ||||
| 
 | ||||
| 	/** Source [[X, W], [Y, H]] */ | ||||
| 	src: [[number, number], [number, number]]; | ||||
| 
 | ||||
| 	/** Dest [[X, W], [Y, H]] */ | ||||
| 	dst: [[number, number], [number, number]]; | ||||
| 
 | ||||
| 	data?: any; | ||||
| } | ||||
| 
 | ||||
| export type Action = ActionText | ActionPoly | ActionCpy | ActionStr; | ||||
| 
 | ||||
| const parse_emf = (data: PreppedBytes): void => { | ||||
| 	//try { require("fs").writeFileSync("out.emf", data); } catch(e) {}
 | ||||
| } | ||||
| 
 | ||||
| /* 2.2.2.9 */ | ||||
| const parse_dib = (data: PreppedBytes) => { | ||||
| 	if(data.length == 0) return null; | ||||
| 	prep_blob(data, 0); | ||||
| 
 | ||||
| 	/* DIBHeaderInfo */ | ||||
| 	const HeaderSize = data.read_shift(4); | ||||
| 	let Width = 0, Height = 0, Planes = 0, BitCount = 0; | ||||
| 	let Compression = 0, ImageSize = 0, XPelsPerMeter = 0, YPelsPerMeter = 0, ColorUsed = 0, ColorImportant = 0; | ||||
| 	if(HeaderSize == 0x0C) { | ||||
| 		Width = data.read_shift(2); | ||||
| 		Height = data.read_shift(2); | ||||
| 	} else { | ||||
| 		Width = data.read_shift(4, 'i'); | ||||
| 		Height = data.read_shift(4, 'i'); | ||||
| 	} | ||||
| 	Planes = data.read_shift(2); | ||||
| 	BitCount = data.read_shift(2); | ||||
| 
 | ||||
| 	const out: object = { | ||||
| 		Width, | ||||
| 		Height, | ||||
| 		BitCount, | ||||
| 	}; | ||||
| 
 | ||||
| 	if(HeaderSize != 0x0C) { | ||||
| 		Compression = data.read_shift(4); | ||||
| 		ImageSize = data.read_shift(4); | ||||
| 		XPelsPerMeter = data.read_shift(4, 'i'); | ||||
| 		YPelsPerMeter = data.read_shift(4, 'i'); | ||||
| 		ColorUsed = data.read_shift(4); | ||||
| 		ColorImportant = data.read_shift(4); | ||||
| 		out["Compression"] = Compression; | ||||
| 		if(BitCount == 24 && ImageSize > Height * 3 * Width) Width = out["Width"] = ImageSize / (Height * 3); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Colors */ | ||||
| 	/* BitmapBuffer */ | ||||
| 	if(ImageSize == data.length - data.l) { | ||||
| 		out["ImageData"] = data.slice(data.l, data.length); | ||||
| 		prep_blob(out["ImageData"], 0); | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| const add_to_objects = (objects: PlaybackDeviceContextState[], obj: PlaybackDeviceContextState): void => { | ||||
| 	let found = false; | ||||
| 	for(var i = 0; i < objects.length; ++i) if(!objects[i]) { objects[i] = obj; found = true; } | ||||
| 	if(!found) objects.push(obj); | ||||
| } | ||||
| 
 | ||||
| export const get_actions_prepped_bytes = (data: PreppedBytes): Action[] => { | ||||
| 	const out: Action[] = []; | ||||
| 
 | ||||
| 	/* 2.3.22 META_HEADER */ | ||||
| 	// Type (2 bytes) must be 1 or 2
 | ||||
| 	let h = data.read_shift(2); | ||||
| 	if(h != 1 && h != 2) throw `Header: Type ${h} must be 1 or 2`; | ||||
| 	// HeaderSize expected to be 9
 | ||||
| 	if((h = data.read_shift(2)) != 9) throw `Header: HeaderSize ${h} must be 9`; | ||||
| 	// Version (2 bytes) 1 or 3
 | ||||
| 	h = data.read_shift(2); | ||||
| 	if(h != 0x0100 && h != 0x0300) throw `Header: Version ${h} must be 0x0100 or 0x0300`; | ||||
| 	// SizeLow
 | ||||
| 	// SizeHigh
 | ||||
| 	// #Objects
 | ||||
| 	// MaxRecord
 | ||||
| 	// NumberOfMembers
 | ||||
| 	data.l = 18; | ||||
| 
 | ||||
| 	let rt = 0; | ||||
| 
 | ||||
| 	/* used for EMF */ | ||||
| 	let escapecnt = 0; | ||||
| 	let CommentRecordCount = 0; | ||||
| 	let RemainingBytes = 0; | ||||
| 	let EnhancedMetafileDataSize = 0; | ||||
| 	let bufs: RawBytes[] = []; | ||||
| 
 | ||||
| 	let objects: PlaybackDeviceContextState[] = []; | ||||
| 	let states: PlaybackDeviceContextState[] = []; | ||||
| 	let state: PlaybackDeviceContextState = {}; | ||||
| 	let sidx = -1; | ||||
| 
 | ||||
| 	while(data.l < data.length) { | ||||
| 		h = data.read_shift(4); | ||||
| 		const end = data.l + h*2 - 4; | ||||
| 
 | ||||
| 		rt = data.read_shift(2); | ||||
| 		let Record = WMFRecords[rt]; | ||||
| 		if(rt == 0x0000) break; // META_EOF
 | ||||
| 
 | ||||
| 		switch(rt) { | ||||
| 			case 0x0626: { // META_ESCAPE
 | ||||
| 				const EscapeFunction = data.read_shift(2); | ||||
| 				const Escape = WMFEscapes[EscapeFunction]; | ||||
| 				//console.log("::", Escape);
 | ||||
| 				/* 2.3.6 */ | ||||
| 				switch(EscapeFunction) { | ||||
| 					case 0x000F: { // META_ESCAPE_ENHANCED_METAFILE
 | ||||
| 						const ByteCount = data.read_shift(2); | ||||
| 						let tmp = data.read_shift(4); | ||||
| 						if(tmp != 0x43464D57) throw `Escape: Comment ID 0x${tmp.toString(16)} != 0x43464D57`; | ||||
| 						tmp = data.read_shift(4); | ||||
| 						if(tmp != 0x00000001) throw `Escape: Comment Type 0x${tmp.toString(16)} != 0x00000001`; | ||||
| 						tmp = data.read_shift(4); | ||||
| 						if(tmp != 0x00010000) throw `Escape: Version 0x${tmp.toString(16)} != 0x00010000`; | ||||
| 
 | ||||
| 						const Checksum = data.read_shift(2); | ||||
| 
 | ||||
| 						data.l += 4; // Flags
 | ||||
| 						if(escapecnt == 0) { | ||||
| 							CommentRecordCount = data.read_shift(4); // total number of records
 | ||||
| 						} else { | ||||
| 							const _CommentRecordCount = data.read_shift(4); | ||||
| 							if(_CommentRecordCount != CommentRecordCount) throw `Escape: CommentRecordCount ${_CommentRecordCount} != ${CommentRecordCount}`; | ||||
| 						} | ||||
| 						const CurrentRecordSize = data.read_shift(4); // size of this record
 | ||||
| 						const _RemainingBytes = data.read_shift(4); | ||||
| 						if(escapecnt > 0 && CurrentRecordSize + _RemainingBytes != RemainingBytes) throw `Escape: ${RemainingBytes} != ${CurrentRecordSize} + ${_RemainingBytes}`; | ||||
| 						RemainingBytes = _RemainingBytes; | ||||
| 						const _EnhancedMetafileDataSize = data.read_shift(4); | ||||
| 						if(escapecnt == 0) { | ||||
| 							if(_EnhancedMetafileDataSize != CurrentRecordSize + _RemainingBytes) throw `Escape: ${_EnhancedMetafileDataSize} != ${CurrentRecordSize} + ${_RemainingBytes}`; | ||||
| 							EnhancedMetafileDataSize = _EnhancedMetafileDataSize; | ||||
| 						} else if(EnhancedMetafileDataSize != _EnhancedMetafileDataSize) throw `Escape: ${EnhancedMetafileDataSize} != ${_EnhancedMetafileDataSize}`; | ||||
| 
 | ||||
| 						if(ByteCount != (end - data.l) + 34) throw `Escape: Sizes ${ByteCount} != ${end - data.l} + 34` | ||||
| 						if(end - data.l != CurrentRecordSize) throw `Escape: CRSize ${CurrentRecordSize} != ${end - data.l}`; | ||||
| 						bufs.push(data.slice(data.l, end)); | ||||
| 						++escapecnt; | ||||
| 						if(escapecnt == CommentRecordCount) { | ||||
| 							const prepped: PreppedBytes = bconcat(bufs) as PreppedBytes; | ||||
| 							prep_blob(prepped, 0); | ||||
| 							parse_emf(prepped); | ||||
| 						} | ||||
| 					} break; | ||||
| 					default: throw `Escape: Unrecognized META_ESCAPE Type 0x${EscapeFunction.toString(16)}`; | ||||
| 				} | ||||
| 			} break; | ||||
| 
 | ||||
| 			// #region 2.3.1 Bitmap Record Types
 | ||||
| 
 | ||||
| 			case 0x0940: { // 2.3.1.2 META_DIBBITBLT
 | ||||
| 				const has_bitmap = h != (rt>>8)+3; | ||||
| 				const RasterOperation = data.read_shift(4); | ||||
| 				const YSrc = data.read_shift(2, "i"); | ||||
| 				const XSrc = data.read_shift(2, "i"); | ||||
| 				if(!has_bitmap) data.l += 2; | ||||
| 				const Height = data.read_shift(2, "i"); | ||||
| 				const Width = data.read_shift(2, "i"); | ||||
| 				const YDest = data.read_shift(2, "i"); | ||||
| 				const XDest = data.read_shift(2, "i"); | ||||
| 				const res: ActionCpy = { | ||||
| 					t: "cpy", | ||||
| 					src: [[XSrc, Width], [YSrc, Height]], | ||||
| 					dst: [XDest, YDest], | ||||
| 					rop: RasterOperation, | ||||
| 					s: Object.assign({}, state) | ||||
| 				}; | ||||
| 				if(has_bitmap) { | ||||
| 					const DIB = parse_dib(data.slice(data.l, end) as PreppedBytes); | ||||
| 					res.data = DIB; | ||||
| 				} | ||||
| 				out.push(res); | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x0B41: { // 2.3.1.3 META_DIBSTRETCHBLT
 | ||||
| 				const has_bitmap = h != (rt>>8)+3; | ||||
| 				const RasterOperation = data.read_shift(4); | ||||
| 				const SrcHeight = data.read_shift(2, "i"); | ||||
| 				const SrcWidth = data.read_shift(2, "i"); | ||||
| 				const YSrc = data.read_shift(2, "i"); | ||||
| 				const XSrc = data.read_shift(2, "i"); | ||||
| 				if(!has_bitmap) data.l += 2; | ||||
| 				const DestHeight = data.read_shift(2, "i"); | ||||
| 				const DestWidth = data.read_shift(2, "i"); | ||||
| 				const YDest = data.read_shift(2, "i"); | ||||
| 				const XDest = data.read_shift(2, "i"); | ||||
| 				const res: ActionStr = { | ||||
| 					t: "str", | ||||
| 					src: [[XSrc, SrcWidth], [YSrc, SrcHeight]], | ||||
| 					dst: [[XDest, DestWidth], [YDest, DestHeight]], | ||||
| 					rop: RasterOperation, | ||||
| 					s: Object.assign({}, state) | ||||
| 				}; | ||||
| 				if(has_bitmap) { | ||||
| 					const DIB = parse_dib(data.slice(data.l, end) as PreppedBytes); | ||||
| 					res.data = DIB; | ||||
| 				} | ||||
| 				out.push(res); | ||||
| 			} break; | ||||
| 
 | ||||
| 			// #endregion
 | ||||
| 
 | ||||
| 			// #region 2.3.3 Drawing Record Types
 | ||||
| 
 | ||||
| 			case 0x0A32: { // 2.3.3.5 META_EXTTEXTOUT
 | ||||
| 				const Y = data.read_shift(2); | ||||
| 				const X = data.read_shift(2); | ||||
| 				const StringLength = data.read_shift(2); | ||||
| 				const fwOpts = data.read_shift(2); // 2.1.2.2
 | ||||
| 				if(fwOpts & 0x06) { | ||||
| 					data.l += 8; // Rectangle 2.2.2.18 (for clipping/opaquing)
 | ||||
| 				} | ||||
| 				const str = data.read_shift(StringLength, 'cpstr'); | ||||
| 				if(data.l < end){/* TODO: Dx */} | ||||
| 				out.push({t: "text", v: str, p: [X, Y], s: Object.assign({}, state)}); | ||||
| 				/* TODO!! */ | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x0325: // 2.3.3.14 META_POLYLINE
 | ||||
| 			case 0x0324: // 2.3.3.15 META_POLYGON
 | ||||
| 			{ | ||||
| 				const nPoints = data.read_shift(2); | ||||
| 				const points: Array<Point> = []; | ||||
| 				for(let i = 0; i < nPoints; ++i) points.push([data.read_shift(2), data.read_shift(2)]) | ||||
| 				out.push({t: "poly", p: points, g: rt !== 0x0325, s: state}); | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x0538: { // 2.3.3.16 META_POLYPOLYGON
 | ||||
| 				const nPolygons = data.read_shift(2); | ||||
| 				const polys: Array<Array<Point> > = []; | ||||
| 				const szs: number[] = []; | ||||
| 				/* 2.2.2.17 PolyPolygon */ | ||||
| 				for(let i = 0; i < nPolygons; ++i) szs[i] = data.read_shift(2); | ||||
| 				for(let i = 0; i < szs.length; ++i) { | ||||
| 					polys[i] = []; | ||||
| 					for(let j = 0; j < szs[i]; ++j) polys[i].push([data.read_shift(2), data.read_shift(2)]) | ||||
| 					out.push({t: "poly", p: polys[i], g: true, s: state}); | ||||
| 				} | ||||
| 			} break; | ||||
| 
 | ||||
| 			// #endregion
 | ||||
| 
 | ||||
| 			// #region 2.3.4 Object Record Types
 | ||||
| 
 | ||||
| 			case 0x02FC: { // 2.3.4.1 META_CREATEBRUSHINDIRECT
 | ||||
| 				const obj: PlaybackDeviceContextState = {}; | ||||
| 				obj.Brush = { | ||||
| 					Style: data.read_shift(2), | ||||
| 					Color: data.read_shift(4), | ||||
| 					Hatch: data.read_shift(2) | ||||
| 				}; | ||||
| 				add_to_objects(objects, obj); | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x02FB: { // 2.3.4.2 META_CREATEFONTINDIRECT
 | ||||
| 				const obj: PlaybackDeviceContextState = {}; | ||||
| 				obj.Font = {}; | ||||
| 				/* 2.2.1.2 Font TODO!! */ | ||||
| 				const Height = data.read_shift(2, "i"); | ||||
| 				const Width = data.read_shift(2, "i"); | ||||
| 				const Escapement = data.read_shift(2, "i"); | ||||
| 				const Orientation = data.read_shift(2, "i"); | ||||
| 				const Weight = data.read_shift(2, "i"); | ||||
| 				const Italic = !!data.read_shift(1); | ||||
| 				const Underline = !!data.read_shift(1); | ||||
| 				const StrikeOut = !!data.read_shift(1); | ||||
| 				const CharSet = data.read_shift(1); | ||||
| 				const OutPrecision = data.read_shift(1); | ||||
| 				const ClipPrecision = data.read_shift(1); | ||||
| 				const Quality = data.read_shift(1); | ||||
| 				const PitchAndFamily = data.read_shift(1); | ||||
| 				const Facename = data.read_shift(32, "cstr"); | ||||
| 				obj.Font.Name = Facename; | ||||
| 				obj.Font.Height = Height; | ||||
| 				obj.Font.Weight = Weight; | ||||
| 				obj.Font.Italic = Italic; | ||||
| 				obj.Font.Angle = Escapement / 10; | ||||
| 				add_to_objects(objects, obj); | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x02FA: { // 2.3.4.5 META_CREATEPENINDIRECT
 | ||||
| 				const obj: PlaybackDeviceContextState = {}; | ||||
| 				obj.Pen = { | ||||
| 					Style: data.read_shift(2), | ||||
| 					Width: data.read_shift(4) & 0xFF, | ||||
| 					Color: data.read_shift(4) | ||||
| 				}; | ||||
| 				add_to_objects(objects, obj); | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x01F0: { // 2.3.4.7 META_DELETEOBJECT
 | ||||
| 				const ObjectIndex = data.read_shift(2); | ||||
| 				//console.log("DELETE", ObjectIndex, objects[ObjectIndex]);
 | ||||
| 				objects[ObjectIndex] = null; | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x012C: { // 2.3.4.9 META_SELECTCLIPREGION
 | ||||
| 				const Region = data.read_shift(2); | ||||
| 				//console.log("CLIPREGION", Region, objects[Region]);
 | ||||
| 				//Object.assign(state, objects[Region]);
 | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x012D: { // 2.3.4.10 META_SELECTOBJECT
 | ||||
| 				const ObjectIndex = data.read_shift(2); | ||||
| 				//console.log("SELECT", ObjectIndex, objects[ObjectIndex]);
 | ||||
| 				Object.assign(state, objects[ObjectIndex]); | ||||
| 				// TODO!!
 | ||||
| 			} break; | ||||
| 
 | ||||
| 			// #endregion
 | ||||
| 
 | ||||
| 			// #region 2.3.5 State Record Types
 | ||||
| 
 | ||||
| 			case 0x0416: // 2.3.5.3 META_INTERSECTCLIPRECT
 | ||||
| 				state.ClipRect = [[0,0],[0,0]]; | ||||
| 				state.ClipRect[1][1] = data.read_shift(2); | ||||
| 				state.ClipRect[1][0] = data.read_shift(2); | ||||
| 				state.ClipRect[0][1] = data.read_shift(2); | ||||
| 				state.ClipRect[0][0] = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x0127: { // 2.3.5.10 META_RESTOREDC
 | ||||
| 				const nSavedDC = data.read_shift(2, 'i'); | ||||
| 				state = states[sidx = (nSavedDC >= 0 ? nSavedDC : sidx + nSavedDC)]; | ||||
| 			} break; | ||||
| 
 | ||||
| 			case 0x001E: // 2.3.5.11 META_SAVEDC
 | ||||
| 				states.push(state); | ||||
| 				sidx = states.length - 1; | ||||
| 				state = JSON.parse(JSON.stringify(state)); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x0102: // 2.3.5.15 META_SETBKMODE
 | ||||
| 				state.BkMode = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x0103: // 2.3.5.17 META_SETMAPMODE
 | ||||
| 				state.MapMode = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x0106: // 2.3.5.20 META_SETPOLYFILLMODE
 | ||||
| 				state.PolyFillMode = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x0107: // 2.3.5.23 META_SETSTRETCHBLTMODE
 | ||||
| 				state.StretchMode = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x012E: // 2.3.5.24 META_SETTEXTALIGN
 | ||||
| 				state.TextAlignmentMode = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x0209: // 2.3.5.26 META_SETTEXTCOLOR
 | ||||
| 				state.TextColor = data.read_shift(4); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x020C: // 2.3.5.30 META_SETWINDOWEXT
 | ||||
| 				state.Extent = [0, 0]; | ||||
| 				state.Extent[1] = data.read_shift(2); | ||||
| 				state.Extent[0] = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x020B: // 2.3.5.31 META_SETWINDOWORG
 | ||||
| 				state.Origin = [0, 0]; | ||||
| 				state.Origin[1] = data.read_shift(2); | ||||
| 				state.Origin[0] = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			// #endregion
 | ||||
| 
 | ||||
| 			default: | ||||
| 				if(!Record) throw `Record: Unrecognized type 0x${rt.toString(16)}`; | ||||
| 				console.log(Record); | ||||
| 		} | ||||
| 		data.l = end; | ||||
| 		//if(rt != 0x0626) console.log(Record);
 | ||||
| 	} | ||||
| 	if(rt !== 0) throw `Record: Last Record Type ${rt} is not EOF type`; | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| export const image_size_prepped_bytes = (data: PreppedBytes): [number, number] => { | ||||
| 	const origin: Point = [NaN, NaN], extents: Point = [NaN, NaN]; | ||||
| 
 | ||||
| 	/* 2.3.22 META_HEADER */ | ||||
| 	// Type (2 bytes) must be 1 or 2
 | ||||
| 	let h = data.read_shift(2); | ||||
| 	if(h != 1 && h != 2) throw `Header: Type ${h} must be 1 or 2`; | ||||
| 	// HeaderSize expected to be 9
 | ||||
| 	if((h = data.read_shift(2)) != 9) throw `Header: HeaderSize ${h} must be 9`; | ||||
| 	// Version (2 bytes) 1 or 3
 | ||||
| 	h = data.read_shift(2); | ||||
| 	if(h != 0x0100 && h != 0x0300) throw `Header: Version ${h} must be 0x0100 or 0x0300`; | ||||
| 	data.l = 18; | ||||
| 
 | ||||
| 	let rt = 0; | ||||
| 
 | ||||
| 	while(data.l < data.length) { | ||||
| 		h = data.read_shift(4); | ||||
| 		const end = data.l + h*2 - 4; | ||||
| 
 | ||||
| 		rt = data.read_shift(2); | ||||
| 		if(rt == 0x0000) break; // META_EOF
 | ||||
| 
 | ||||
| 		switch(rt) { | ||||
| 			case 0x020C: // 2.3.5.30 META_SETWINDOWEXT
 | ||||
| 				extents[1] = data.read_shift(2); | ||||
| 				extents[0] = data.read_shift(2); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 0x020B: // 2.3.5.31 META_SETWINDOWORG
 | ||||
| 				origin[1] = data.read_shift(2); | ||||
| 				origin[0] = data.read_shift(2); | ||||
| 				break; | ||||
| 		} | ||||
| 		data.l = end; | ||||
| 	} | ||||
| 
 | ||||
| 	return [extents[0] - origin[0], extents[1] - origin[1]]; | ||||
| }; | ||||
							
								
								
									
										2
									
								
								test/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								test/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| .cache | ||||
| dist | ||||
							
								
								
									
										17
									
								
								test/fetch.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										17
									
								
								test/fetch.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| <html><body> | ||||
| 	<style> | ||||
| 		* { | ||||
| 			padding: 0; | ||||
| 			margin: 0; | ||||
| 		} | ||||
| 	</style> | ||||
| 	<canvas id="canvas"></canvas> | ||||
| 	<script src="wmf.js"></script> | ||||
| 	<script> | ||||
| 		(async() => { | ||||
| 			const res = await fetch("static/image1.wmf"); | ||||
| 			const ab = await res.arrayBuffer(); | ||||
| 			WMF.draw_canvas(ab, document.getElementById("canvas")); | ||||
| 		})(); | ||||
| 	</script> | ||||
| </body></html> | ||||
							
								
								
									
										21
									
								
								test/filereader.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										21
									
								
								test/filereader.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| <html><body> | ||||
| 	<style> | ||||
| 		* { | ||||
| 			padding: 0; | ||||
| 			margin: 0; | ||||
| 		} | ||||
|   </style> | ||||
|   <input type="file" id="file"><label for="file">Click here to select a file</label><br/> | ||||
| 	<canvas id="canvas"></canvas> | ||||
| 	<script src="wmf.js"></script> | ||||
| 	<script> | ||||
|     document.getElementById("file").addEventListener("change", (e) => { | ||||
|       const files = e.target.files, f = files[0]; | ||||
|       const reader = new FileReader(); | ||||
|       reader.onload = (evt) => { | ||||
|         WMF.draw_canvas(evt.target.result, document.getElementById("canvas")); | ||||
|       }; | ||||
|       reader.readAsArrayBuffer(f); | ||||
|     }, false); | ||||
| 	</script> | ||||
| </body></html> | ||||
							
								
								
									
										20
									
								
								test/node-canvas.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										20
									
								
								test/node-canvas.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| const fs = require("fs"); | ||||
| const { createCanvas, createImageData } = require("canvas"); | ||||
| const WMF = require("../"); | ||||
| 
 | ||||
| /* WMF uses ImageData -- make it visible to the library */ | ||||
| global.ImageData = createImageData; | ||||
| 
 | ||||
| /* read data */ | ||||
| const data = fs.readFileSync(process.argv[2] || "./static/image1.wmf"); | ||||
| 
 | ||||
| /* create canvas */ | ||||
| const size = WMF.image_size(data); | ||||
| const canvas = createCanvas(size[0], size[1]); | ||||
| 
 | ||||
| /* do it! */ | ||||
| WMF.draw_canvas(data, canvas); | ||||
| 
 | ||||
| /* export to file */ | ||||
| const res = canvas.toBuffer("image/png"); | ||||
| fs.writeFileSync("out.png", res); | ||||
							
								
								
									
										17
									
								
								test/parcel.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										17
									
								
								test/parcel.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| <html><body> | ||||
| 	<style> | ||||
| 		* { | ||||
| 			padding: 0; | ||||
| 			margin: 0; | ||||
| 		} | ||||
| 	</style> | ||||
| 	<canvas id="canvas"></canvas> | ||||
| 	<script> | ||||
| 		const urlparams = new URLSearchParams(window.location.search); | ||||
| 		//window.MODE = "DIRECT"; // draw to an on-screen canvas | ||||
| 		//window.MODE = "INDIRECT"; // draw to an orphan canvas and copy | ||||
| 		//window.MODE = "OFFSCREEN"; // draw to an OffscreenCanvas and copy | ||||
| 		window.MODE = urlparams.get("mode") || "OFFSCREEN"; | ||||
| 	</script> | ||||
| 	<script src="./parcel.js"></script> | ||||
| </body></html> | ||||
							
								
								
									
										38
									
								
								test/parcel.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										38
									
								
								test/parcel.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| import * as WMF from '../'; | ||||
| import * as fs from 'fs'; | ||||
| 
 | ||||
| Buffer; // this line is required by Parcel to pull in the buffer shim
 | ||||
| 
 | ||||
| const data = fs.readFileSync("static/image1.wmf"); | ||||
| const domelt/*: HTMLCanvasElement*/ = document.getElementById("canvas")/* as HTMLCanvasElement*/; | ||||
| 
 | ||||
| const mode = window["MODE"] || "OFFSCREEN"; | ||||
| console.log(mode); | ||||
| switch(mode) { | ||||
|   case "DIRECT": { | ||||
|     /* draw_canvas automatically resizes the canvas */ | ||||
|     WMF.draw_canvas(data, domelt); | ||||
|   } break; | ||||
|   case "INDIRECT": { | ||||
|     const newelt = document.createElement("canvas"); | ||||
|     WMF.draw_canvas(data, newelt); | ||||
|     /* the copy_canvas helper performs a resize of the new canvas */ | ||||
|     copy_canvas(domelt, newelt); | ||||
|   } break; | ||||
|   case "OFFSCREEN": { | ||||
|     /* OffscreenCanvas requires the size beforehand */ | ||||
|     const size = WMF.image_size(data); | ||||
|     const newelt = new OffscreenCanvas(size[0], size[1]); | ||||
|     WMF.draw_canvas(data, newelt); | ||||
|     copy_canvas(domelt, newelt); | ||||
|   } break; | ||||
| } | ||||
| 
 | ||||
| function copy_canvas(dst, src) { | ||||
|   dst.height = src.height; | ||||
|   dst.width = src.width; | ||||
|   const ctxdst = dst.getContext('2d'); | ||||
|   const ctxsrc = src.getContext('2d'); | ||||
|   const imdata = ctxsrc.getImageData(0, 0, domelt.width, domelt.height); | ||||
|   ctxdst.putImageData(imdata, 0, 0); | ||||
| } | ||||
							
								
								
									
										1
									
								
								test/wmf.js
									
									
									
									
									
										Symbolic link
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								test/wmf.js
									
									
									
									
									
										Symbolic link
									
								
							| @ -0,0 +1 @@ | ||||
| ../dist/wmf.js | ||||
							
								
								
									
										10
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| { | ||||
| 	"compilerOptions": { | ||||
| 		"target": "es5", | ||||
| 		"moduleResolution": "node", | ||||
| 		"outDir": "./js", | ||||
| 		"lib": [ "es5", "DOM" ], | ||||
| 		"sourceMap": true, | ||||
| 		"noImplicitReturns": true | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user