UNPKG

74 kBJavaScriptView Raw
1#!/usr/bin/env node
2(function() {
3;
4var ASCIIDrawing, ASCIIMapping, CSSStyle, CSVDrawing, CoffeeMapping, Context, DOMParser, DSVDrawing, Drawing, Drawings, DynamicSymbol, Input, JSMapping, Mapping, Mappings, SSVDrawing, SVGNS, SVGTilerException, StaticSymbol, Style, Styles, StylusStyle, Symbol, TSVDrawing, XLINKNS, XLSXDrawings, XMLSerializer, allBlank, attributeOrStyle, blankCells, bufferSize, contentType, convertSVG, domImplementation, domRecurse, escapeId, extensionMap, extensionOf, extractOverflowBox, extractZIndex, fs, getHref, graphemeSplitter, help, implicitFinalReturn, inkscapeVersion, isAuto, main, metadata, parseBox, parseNum, path, postprocess, prettyXML, renderPreact, sanitize, splitIntoLines, svgBBox, svgtiler, unrecognizedSymbol, whitespace, xmldom, zeroSizeReplacement,
5 indexOf = [].indexOf,
6 hasProp = {}.hasOwnProperty;
7
8if (typeof window === "undefined" || window === null) {
9 path = require('path');
10 fs = require('fs');
11 xmldom = require('@xmldom/xmldom');
12 DOMParser = xmldom.DOMParser;
13 domImplementation = new xmldom.DOMImplementation();
14 XMLSerializer = xmldom.XMLSerializer;
15 prettyXML = require('prettify-xml');
16 graphemeSplitter = new require('grapheme-splitter')();
17 metadata = require('../package.json');
18 require('coffeescript/register');
19} else {
20 DOMParser = window.DOMParser; // escape CoffeeScript scope
21 domImplementation = document.implementation;
22 XMLSerializer = window.XMLSerializer; // escape CoffeeScript scope
23 path = {
24 extname: function(x) {
25 return /\.[^\/]+$/.exec(x)[0];
26 },
27 dirname: function(x) {
28 return /[^]*\/|/.exec(x)[0];
29 }
30 };
31 graphemeSplitter = {
32 splitGraphemes: function(x) {
33 return x.split('');
34 }
35 };
36 metadata = {
37 version: '(web)'
38 };
39}
40
41SVGNS = 'http://www.w3.org/2000/svg';
42
43XLINKNS = 'http://www.w3.org/1999/xlink';
44
45splitIntoLines = function(data) {
46 return data.replace('\r\n', '\n').replace('\r', '\n').split('\n');
47};
48
49whitespace = /[\s\uFEFF\xA0]+/; //# based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim
50
51extensionOf = function(filename) {
52 return path.extname(filename).toLowerCase();
53};
54
55SVGTilerException = class SVGTilerException {
56 constructor(message) {
57 this.message = message;
58 }
59
60 toString() {
61 return `svgtiler: ${this.message}`;
62 }
63
64};
65
66parseBox = function(box) {
67 if (!box) {
68 return null;
69 }
70 box = box.split(/\s*[\s,]\s*/).map(parseNum);
71 if (indexOf.call(box, null) >= 0) {
72 return null;
73 }
74 return box;
75};
76
77extractOverflowBox = function(xml) {
78 var box;
79 //# Parse and return root overflowBox attribute.
80 //# Also remove it if present, so output is valid SVG.
81 box = xml.documentElement.getAttribute('overflowBox');
82 xml.documentElement.removeAttribute('overflowBox');
83 return parseBox(box);
84};
85
86parseNum = function(x) {
87 var parsed;
88 parsed = parseFloat(x);
89 if (isNaN(parsed)) {
90 return null;
91 } else {
92 return parsed;
93 }
94};
95
96svgBBox = function(xml) {
97 var recurse, viewBox;
98 //# xxx Many unsupported features!
99 //# - transformations
100 //# - used symbols/defs
101 //# - paths
102 //# - text
103 //# - line widths which extend bounding box
104 if (xml.documentElement.hasAttribute('viewBox')) {
105 return parseBox(xml.documentElement.getAttribute('viewBox'));
106 } else {
107 recurse = function(node) {
108 var child, coord, cx, cy, point, points, r, ref, ref1, ref10, ref11, ref12, ref13, ref14, ref15, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, rx, ry, viewBox, viewBoxes, x1, x2, xmax, xmin, xs, y1, y2, ymax, ymin, ys;
109 if (node.nodeType !== node.ELEMENT_NODE || ((ref = node.nodeName) === 'defs' || ref === 'use')) {
110 return null;
111 }
112 // Ignore <symbol>s except the root <symbol> that we're bounding
113 if (node.nodeName === 'symbol' && node !== xml.documentElement) {
114 return null;
115 }
116 switch (node.tagName) {
117 case 'rect':
118 case 'image':
119 //# For <image>, should autodetect image size (#42)
120 return [(ref1 = parseNum(node.getAttribute('x'))) != null ? ref1 : 0, (ref2 = parseNum(node.getAttribute('y'))) != null ? ref2 : 0, (ref3 = parseNum(node.getAttribute('width'))) != null ? ref3 : 0, (ref4 = parseNum(node.getAttribute('height'))) != null ? ref4 : 0];
121 case 'circle':
122 cx = (ref5 = parseNum(node.getAttribute('cx'))) != null ? ref5 : 0;
123 cy = (ref6 = parseNum(node.getAttribute('cy'))) != null ? ref6 : 0;
124 r = (ref7 = parseNum(node.getAttribute('r'))) != null ? ref7 : 0;
125 return [cx - r, cy - r, 2 * r, 2 * r];
126 case 'ellipse':
127 cx = (ref8 = parseNum(node.getAttribute('cx'))) != null ? ref8 : 0;
128 cy = (ref9 = parseNum(node.getAttribute('cy'))) != null ? ref9 : 0;
129 rx = (ref10 = parseNum(node.getAttribute('rx'))) != null ? ref10 : 0;
130 ry = (ref11 = parseNum(node.getAttribute('ry'))) != null ? ref11 : 0;
131 return [cx - rx, cy - ry, 2 * rx, 2 * ry];
132 case 'line':
133 x1 = (ref12 = parseNum(node.getAttribute('x1'))) != null ? ref12 : 0;
134 y1 = (ref13 = parseNum(node.getAttribute('y1'))) != null ? ref13 : 0;
135 x2 = (ref14 = parseNum(node.getAttribute('x2'))) != null ? ref14 : 0;
136 y2 = (ref15 = parseNum(node.getAttribute('y2'))) != null ? ref15 : 0;
137 xmin = Math.min(x1, x2);
138 ymin = Math.min(y1, y2);
139 return [xmin, ymin, Math.max(x1, x2) - xmin, Math.max(y1, y2) - ymin];
140 case 'polyline':
141 case 'polygon':
142 points = (function() {
143 var k, len, ref16, results;
144 ref16 = node.getAttribute('points').trim().split(/\s+/);
145 results = [];
146 for (k = 0, len = ref16.length; k < len; k++) {
147 point = ref16[k];
148 results.push((function() {
149 var l, len1, ref17, results1;
150 ref17 = point.split(/,/);
151 results1 = [];
152 for (l = 0, len1 = ref17.length; l < len1; l++) {
153 coord = ref17[l];
154 results1.push(parseFloat(coord));
155 }
156 return results1;
157 })());
158 }
159 return results;
160 })();
161 xs = (function() {
162 var k, len, results;
163 results = [];
164 for (k = 0, len = points.length; k < len; k++) {
165 point = points[k];
166 results.push(point[0]);
167 }
168 return results;
169 })();
170 ys = (function() {
171 var k, len, results;
172 results = [];
173 for (k = 0, len = points.length; k < len; k++) {
174 point = points[k];
175 results.push(point[1]);
176 }
177 return results;
178 })();
179 xmin = Math.min(...xs);
180 ymin = Math.min(...ys);
181 if (isNaN(xmin) || isNaN(ymin)) { // invalid points attribute; don't render
182 return null;
183 } else {
184 return [xmin, ymin, Math.max(...xs) - xmin, Math.max(...ys) - ymin];
185 }
186 break;
187 default:
188 viewBoxes = (function() {
189 var k, len, ref16, results;
190 ref16 = node.childNodes;
191 results = [];
192 for (k = 0, len = ref16.length; k < len; k++) {
193 child = ref16[k];
194 results.push(recurse(child));
195 }
196 return results;
197 })();
198 viewBoxes = (function() {
199 var k, len, results;
200 results = [];
201 for (k = 0, len = viewBoxes.length; k < len; k++) {
202 viewBox = viewBoxes[k];
203 if (viewBox != null) {
204 results.push(viewBox);
205 }
206 }
207 return results;
208 })();
209 xmin = Math.min(...((function() {
210 var k, len, results;
211 results = [];
212 for (k = 0, len = viewBoxes.length; k < len; k++) {
213 viewBox = viewBoxes[k];
214 results.push(viewBox[0]);
215 }
216 return results;
217 })()));
218 ymin = Math.min(...((function() {
219 var k, len, results;
220 results = [];
221 for (k = 0, len = viewBoxes.length; k < len; k++) {
222 viewBox = viewBoxes[k];
223 results.push(viewBox[1]);
224 }
225 return results;
226 })()));
227 xmax = Math.max(...((function() {
228 var k, len, results;
229 results = [];
230 for (k = 0, len = viewBoxes.length; k < len; k++) {
231 viewBox = viewBoxes[k];
232 results.push(viewBox[0] + viewBox[2]);
233 }
234 return results;
235 })()));
236 ymax = Math.max(...((function() {
237 var k, len, results;
238 results = [];
239 for (k = 0, len = viewBoxes.length; k < len; k++) {
240 viewBox = viewBoxes[k];
241 results.push(viewBox[1] + viewBox[3]);
242 }
243 return results;
244 })()));
245 return [xmin, ymin, xmax - xmin, ymax - ymin];
246 }
247 };
248 viewBox = recurse(xml.documentElement);
249 if ((viewBox == null) || indexOf.call(viewBox, 2e308) >= 0 || indexOf.call(viewBox, -2e308) >= 0) {
250 return null;
251 } else {
252 return viewBox;
253 }
254 }
255};
256
257isAuto = function(xml, prop) {
258 return xml.documentElement.hasAttribute(prop) && /^\s*auto\s*$/i.test(xml.documentElement.getAttribute(prop));
259};
260
261attributeOrStyle = function(node, attr, styleKey = attr) {
262 var match, style, value;
263 if (value = node.getAttribute(attr)) {
264 return value.trim();
265 } else {
266 style = node.getAttribute('style');
267 if (style) {
268 match = RegExp(`(?:^|;)\\s*${styleKey}\\s*:\\s*([^;\\s][^;]*)`, "i").exec(style);
269 return match != null ? match[1] : void 0;
270 }
271 }
272};
273
274getHref = function(node) {
275 var href, k, key, len, ref;
276 ref = ['xlink:href', 'href'];
277 for (k = 0, len = ref.length; k < len; k++) {
278 key = ref[k];
279 if (href = node.getAttribute(key)) {
280 return {
281 key: key,
282 href: href
283 };
284 }
285 }
286 return {
287 key: null,
288 href: null
289 };
290};
291
292extractZIndex = function(node) {
293 var z;
294 //# Check whether DOM node has a specified z-index, defaulting to zero.
295 //# Also remove z-index attribute, so output is valid SVG.
296 //# Note that z-index must be an integer.
297 //# 1. https://www.w3.org/Graphics/SVG/WG/wiki/Proposals/z-index suggests
298 //# a z-index="..." attribute. Check for this first.
299 //# 2. Look for style="z-index:..." as in HTML.
300 z = parseInt(attributeOrStyle(node, 'z-index'));
301 node.removeAttribute('z-index');
302 if (isNaN(z)) {
303 return 0;
304 } else {
305 return z;
306 }
307};
308
309domRecurse = function(node, callback) {
310 var child, nextChild;
311 /*
312 Recurse through DOM starting at `node`, calling `callback(node, parent)`
313 on every recursive node except `node` itself.
314 `callback()` should return a true value if you want to recurse into
315 the specified node's children (typically, when there isn't a match).
316 */
317 if (!node.hasChildNodes()) {
318 return;
319 }
320 child = node.lastChild;
321 while (child != null) {
322 nextChild = child.previousSibling;
323 if (callback(child, node)) {
324 domRecurse(child, callback);
325 }
326 child = nextChild;
327 }
328 return null;
329};
330
331contentType = {
332 '.png': 'image/png',
333 '.jpg': 'image/jpeg',
334 '.jpeg': 'image/jpeg',
335 '.gif': 'image/gif',
336 '.svg': 'image/svg+xml'
337};
338
339renderPreact = function(data) {
340 if (typeof data === 'object' && (data.type != null) && (data.props != null)) {
341 data = require('preact-render-to-string')(data);
342 }
343 return data;
344};
345
346Symbol = (function() {
347 class Symbol {
348 static parse(key, data, dirname) {
349 var extension, filename;
350 if (data == null) {
351 throw new SVGTilerException(`Attempt to create symbol '${key}' without data`);
352 } else if (typeof data === 'function') {
353 return new DynamicSymbol(key, data, dirname);
354 } else if (data.function != null) {
355 return new DynamicSymbol(key, data.function, dirname);
356 } else {
357 //# Render Preact virtual dom nodes (e.g. from JSX notation) into strings.
358 //# Serialization + parsing shouldn't be necessary, but this lets us
359 //# deal with one parsed format (xmldom).
360 data = renderPreact(data);
361 return new StaticSymbol(key, (function() {
362 if (typeof data === 'string') {
363 if (data.trim() === '') { //# Blank SVG treated as 0x0 symbol
364 return {
365 svg: '<symbol viewBox="0 0 0 0"/>'
366 };
367 } else if (data.indexOf('<') < 0) { //# No <'s -> interpret as filename
368 if (dirname != null) {
369 filename = path.join(dirname, data);
370 } else {
371 filename = data;
372 }
373 extension = extensionOf(data);
374 //# <image> tag documentation: "Conforming SVG viewers need to
375 //# support at least PNG, JPEG and SVG format files."
376 //# [https://svgwg.org/svg2-draft/embedded.html#ImageElement]
377 switch (extension) {
378 case '.png':
379 case '.jpg':
380 case '.jpeg':
381 case '.gif':
382 return {
383 dirname: dirname,
384 svg: `<image ${Drawing.hrefAttr()}="${encodeURI(data)}"/>`
385 };
386 case '.svg':
387 return {
388 dirname: path.dirname(filename),
389 filename: filename,
390 svg: fs.readFileSync(filename, {
391 encoding: this.svgEncoding
392 })
393 };
394 default:
395 throw new SVGTilerException(`Unrecognized extension in filename '${data}' for symbol '${key}'`);
396 }
397 } else {
398 return {
399 dirname: dirname,
400 svg: data
401 };
402 }
403 } else {
404 return data;
405 }
406 }).call(this));
407 }
408 }
409
410 includes(substring) {
411 return this.key.indexOf(substring) >= 0;
412 }
413
414 //# ECMA6: @key.includes substring
415 match(regex) {
416 return this.key.match(regex);
417 }
418
419 };
420
421 Symbol.svgEncoding = 'utf8';
422
423 Symbol.forceWidth = null; //# default: no size forcing
424
425 Symbol.forceHeight = null; //# default: no size forcing
426
427 Symbol.texText = false;
428
429 // Set default overflow behavior to visible unless --no-overflow specified;
430 // use overflow:hidden to restore normal SVG behavior of keeping each tile
431 // within its bounding box.
432 Symbol.overflowDefault = 'visible';
433
434 return Symbol;
435
436}).call(this);
437
438escapeId = function(key) {
439 /*
440 According to XML spec [https://www.w3.org/TR/xml/#id],
441 id/href follows the XML name spec: [https://www.w3.org/TR/xml/#NT-Name]
442 NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
443 NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
444 Name ::= NameStartChar (NameChar)*
445 In addition, colons in IDs fail when embedding an SVG via <img>.
446 We use encodeURIComponent which escapes everything except
447 A-Z a-z 0-9 - _ . ! ~ * ' ( )
448 [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent]
449 into %-encoded symbols, plus we encode _ . ! ~ * ' ( ) and - 0-9 (start only).
450 But % (and %-encoded) symbols are not supported, so we replace '%' with '_',
451 an allowed character that we escape.
452 In the special case of a blank key, we use the special _blank which cannot
453 be generated by the escaping process.
454 */
455 return (encodeURIComponent(key).replace(/[_\.!~*'()]|^[\-0-9]/g, function(c) {
456 return `%${c.charCodeAt(0).toString(16).toUpperCase()}`;
457 }).replace(/%/g, '_')) || '_blank';
458};
459
460zeroSizeReplacement = 1;
461
462StaticSymbol = (function() {
463 class StaticSymbol extends Symbol {
464 constructor(key1, options) {
465 var attribute, child, doc, k, key, l, len, len1, node, overflow, ref, ref1, ref2, ref3, symbol, value, warnings;
466 super();
467 this.key = key1;
468 for (key in options) {
469 if (!hasProp.call(options, key)) continue;
470 value = options[key];
471 this[key] = value;
472 }
473 //# Force SVG namespace when parsing, so nodes have correct namespaceURI.
474 //# (This is especially important on the browser, so the results can be
475 //# reparented into an HTML Document.)
476 this.svg = this.svg.replace(/^\s*<(?:[^<>'"\/]|'[^']*'|"[^"]*")*\s*(\/?\s*>)/, function(match, end) {
477 if (indexOf.call(match, 'xmlns') < 0) {
478 match = match.slice(0, match.length - end.length) + ` xmlns='${SVGNS}'` + match.slice(match.length - end.length);
479 }
480 return match;
481 });
482 this.xml = new DOMParser({
483 locator: { //# needed when specifying errorHandler
484 line: 1,
485 col: 1
486 },
487 errorHandler: (level, msg, indent = ' ') => {
488 msg = msg.replace(/^\[xmldom [^\[\]]*\]\t/, '');
489 msg = msg.replace(/@#\[line:(\d+),col:(\d+)\]$/, (match, line, col) => {
490 var lines;
491 lines = this.svg.split('\n');
492 return (line > 1 ? indent + lines[line - 2] + '\n' : '') + indent + lines[line - 1] + '\n' + indent + ' '.repeat(col - 1) + '^^^' + (line < lines.length ? '\n' + indent + lines[line] : '');
493 });
494 return console.error(`SVG parse ${level} in symbol '${this.key}': ${msg}`);
495 }
496 }).parseFromString(this.svg, 'image/svg+xml');
497 // Remove from the symbol any top-level xmlns=SVGNS possibly added above:
498 // we will have such a tag in the top-level <svg>.
499 this.xml.documentElement.removeAttribute('xmlns');
500 //# Wrap XML in <symbol> if not already.
501 symbol = this.xml.createElementNS(SVGNS, 'symbol');
502 symbol.setAttribute('id', this.id = escapeId(this.key));
503 // Avoid a layer of indirection for <symbol>/<svg> at top level
504 if (((ref = this.xml.documentElement.nodeName) === 'symbol' || ref === 'svg') && (this.xml.documentElement.nextSibling == null)) {
505 ref1 = this.xml.documentElement.attributes;
506 for (k = 0, len = ref1.length; k < len; k++) {
507 attribute = ref1[k];
508 if (!(((ref2 = attribute.name) === 'version' || ref2 === 'id') || attribute.name.slice(0, 5) === 'xmlns')) {
509 symbol.setAttribute(attribute.name, attribute.value);
510 }
511 }
512 doc = this.xml.documentElement;
513 this.xml.removeChild(this.xml.documentElement);
514 } else {
515 doc = this.xml;
516 }
517 ref3 = (function() {
518 var len1, n, ref3, results;
519 ref3 = doc.childNodes;
520 results = [];
521 for (n = 0, len1 = ref3.length; n < len1; n++) {
522 node = ref3[n];
523 results.push(node);
524 }
525 return results;
526 })();
527 for (l = 0, len1 = ref3.length; l < len1; l++) {
528 child = ref3[l];
529 symbol.appendChild(child);
530 }
531 this.xml.appendChild(symbol);
532 //# <image> processing
533 domRecurse(this.xml.documentElement, (node) => {
534 /*
535 Fix image-rendering: if unspecified, or if specified as "optimizeSpeed"
536 or "pixelated", attempt to render pixels as pixels, as needed for
537 old-school graphics. SVG 1.1 and Inkscape define
538 image-rendering="optimizeSpeed" for this. Chrome doesn't support this,
539 but supports a CSS3 (or SVG) specification of
540 "image-rendering:pixelated". Combining these seems to work everywhere.
541 */
542 var e, filedata, filename, height, href, ref4, rendering, size, style, type, width;
543 if (node.nodeName === 'image') {
544 rendering = attributeOrStyle(node, 'image-rendering');
545 if ((rendering == null) || (rendering === 'optimizeSpeed' || rendering === 'pixelated')) {
546 node.setAttribute('image-rendering', 'optimizeSpeed');
547 style = (ref4 = node.getAttribute('style')) != null ? ref4 : '';
548 style = style.replace(/(^|;)\s*image-rendering\s*:\s*\w+\s*($|;)/, function(m, before, after) {
549 return before || after || '';
550 });
551 if (style) {
552 style += ';';
553 }
554 node.setAttribute('style', style + 'image-rendering:pixelated');
555 }
556 //# Read file for width/height detection and/or inlining
557 ({href, key} = getHref(node));
558 filename = href;
559 if ((this.dirname != null) && filename) {
560 filename = path.join(this.dirname, filename);
561 }
562 if ((filename != null) && !/^data:|file:|[a-z]+:\/\//.test(filename)) { // skip URLs
563 filedata = null;
564 try {
565 if (typeof window === "undefined" || window === null) {
566 filedata = fs.readFileSync(filename);
567 }
568 } catch (error1) {
569 e = error1;
570 console.warn(`Failed to read image '${filename}': ${e}`);
571 }
572 //# Fill in width and height
573 size = null;
574 if (typeof window === "undefined" || window === null) {
575 try {
576 size = require('image-size')(filedata != null ? filedata : filename);
577 } catch (error1) {
578 e = error1;
579 console.warn(`Failed to detect size of image '${filename}': ${e}`);
580 }
581 }
582 if (size != null) {
583 //# If one of width and height is set, scale to match.
584 if (!isNaN(width = parseFloat(node.getAttribute('width')))) {
585 node.setAttribute('height', size.height * (width / size.width));
586 } else if (!isNaN(height = parseFloat(node.getAttribute('height')))) {
587 node.setAttribute('width', size.width * (height / size.height));
588 } else {
589 //# If neither width nor height are set, set both.
590 node.setAttribute('width', size.width);
591 node.setAttribute('height', size.height);
592 }
593 }
594 //# Inline
595 if ((filedata != null) && Drawing.inlineImages) {
596 type = contentType[extensionOf(filename)];
597 if (type != null) {
598 node.setAttribute("data-filename", filename);
599 if (size != null) {
600 node.setAttribute("data-width", size.width);
601 node.setAttribute("data-height", size.height);
602 }
603 node.setAttribute(key, `data:${type};base64,${filedata.toString('base64')}`);
604 }
605 }
606 }
607 return false;
608 } else {
609 return true;
610 }
611 });
612 //# Compute viewBox attribute if absent.
613 this.viewBox = svgBBox(this.xml);
614 //# Overflow behavior
615 overflow = attributeOrStyle(this.xml.documentElement, 'overflow');
616 if ((this.constructor.overflowDefault != null) && (overflow == null)) {
617 this.xml.documentElement.setAttribute('overflow', overflow = this.constructor.overflowDefault);
618 }
619 this.overflowVisible = (overflow != null) && /^\s*(visible|scroll)\b/.test(overflow);
620 this.width = this.height = null;
621 if (this.viewBox != null) {
622 this.width = this.viewBox[2];
623 this.height = this.viewBox[3];
624 /*
625 SVG's viewBox has a special rule that "A value of zero [in <width>
626 or <height>] disables rendering of the element." Avoid this.
627 [https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute]
628 */
629 if (this.overflowVisible) {
630 if (this.width === 0) {
631 this.viewBox[2] = zeroSizeReplacement;
632 }
633 if (this.height === 0) {
634 this.viewBox[3] = zeroSizeReplacement;
635 }
636 }
637 //# Reset viewBox attribute in case either absent (and computed via
638 //# svgBBox) or changed to avoid zeroes.
639 this.xml.documentElement.setAttribute('viewBox', this.viewBox.join(' '));
640 }
641 this.overflowBox = extractOverflowBox(this.xml);
642 if (Symbol.forceWidth != null) {
643 this.width = Symbol.forceWidth;
644 }
645 if (Symbol.forceHeight != null) {
646 this.height = Symbol.forceHeight;
647 }
648 warnings = [];
649 if (this.width == null) {
650 warnings.push('width');
651 this.width = 0;
652 }
653 if (this.height == null) {
654 warnings.push('height');
655 this.height = 0;
656 }
657 if (warnings.length > 0) {
658 console.warn(`Failed to detect ${warnings.join(' and ')} of SVG for symbol '${this.key}'`);
659 }
660 //# Detect special `width="auto"` and/or `height="auto"` fields for future
661 //# processing, and remove them to ensure valid SVG.
662 this.autoWidth = isAuto(this.xml, 'width');
663 this.autoHeight = isAuto(this.xml, 'height');
664 if (this.autoWidth) {
665 this.xml.documentElement.removeAttribute('width');
666 }
667 if (this.autoHeight) {
668 this.xml.documentElement.removeAttribute('height');
669 }
670 this.zIndex = extractZIndex(this.xml.documentElement);
671 //# Optionally extract <text> nodes for LaTeX output
672 if (Symbol.texText) {
673 this.text = [];
674 domRecurse(this.xml.documentElement, (node, parent) => {
675 if (node.nodeName === 'text') {
676 this.text.push(node);
677 parent.removeChild(node);
678 return false; // don't recurse into <text>'s children
679 } else {
680 return true;
681 }
682 });
683 }
684 }
685
686 setId(id) {
687 this.id = id; // for <use>
688 return this.xml.documentElement.setAttribute('id', id);
689 }
690
691 use() {
692 return this;
693 }
694
695 };
696
697 StaticSymbol.prototype.usesContext = false;
698
699 return StaticSymbol;
700
701}).call(this);
702
703DynamicSymbol = (function() {
704 class DynamicSymbol extends Symbol {
705 constructor(key1, func1, dirname1) {
706 super();
707 this.key = key1;
708 this.func = func1;
709 this.dirname = dirname1;
710 this.versions = {};
711 this.nversions = 0;
712 this.constructor.all.push(this);
713 }
714
715 static resetAll() {
716 var k, len, ref, results, symbol;
717 ref = this.all;
718 //# Resets all DynamicSymbol's versions to 0.
719 //# Use before starting a new SVG document.
720 results = [];
721 for (k = 0, len = ref.length; k < len; k++) {
722 symbol = ref[k];
723 symbol.versions = {};
724 results.push(symbol.nversions = 0);
725 }
726 return results;
727 }
728
729 use(context) {
730 var result, string, version;
731 result = this.func.call(context);
732 if (result == null) {
733 throw new Error(`Function for symbol ${this.key} returned ${result}`);
734 }
735 //# Render Preact virtual dom elements (e.g. from JSX notation) to SVG now,
736 //# for better duplicate detection.
737 result = renderPreact(result);
738 //# We use JSON serialization to detect duplicate symbols. This enables
739 //# return values like {filename: ...}, in addition to raw SVG strings.
740 string = JSON.stringify(result);
741 if (!(string in this.versions)) {
742 version = this.nversions++;
743 this.versions[string] = Symbol.parse(`${this.key}_v${version}`, result, this.dirname);
744 this.versions[string].setId(`${escapeId(this.key)}_v${version}`);
745 }
746 return this.versions[string];
747 }
748
749 };
750
751 DynamicSymbol.all = [];
752
753 DynamicSymbol.prototype.usesContext = true;
754
755 return DynamicSymbol;
756
757}).call(this);
758
759//# Symbol to fall back to when encountering an unrecognized symbol.
760//# Path from https://commons.wikimedia.org/wiki/File:Replacement_character.svg
761//# by Amit6, released into the public domain.
762unrecognizedSymbol = new StaticSymbol('_unrecognized', {
763 svg: `<symbol viewBox="0 0 200 200" preserveAspectRatio="none" width="auto" height="auto">
764 <rect width="200" height="200" fill="yellow"/>
765 <path stroke="none" fill="red" d="M 200,100 100,200 0,100 100,0 200,100 z M 135.64709,74.70585 q 0,-13.52935 -10.00006,-22.52943 -9.99999,-8.99999 -24.35289,-8.99999 -17.29415,0 -30.117661,5.29409 L 69.05879,69.52938 q 9.764731,-6.23528 21.52944,-6.23528 8.82356,0 14.58824,4.82351 5.76469,4.82351 5.76469,12.70589 0,8.5883 -9.94117,21.70588 -9.94117,13.11766 -9.94117,26.76473 l 17.88236,0 q 0,-6.3529 6.9412,-14.9412 11.76471,-14.58816 12.82351,-16.35289 6.9412,-11.05887 6.9412,-23.29417 z m -22.00003,92.11771 0,-24.70585 -27.29412,0 0,24.70585 27.29412,0 z"/>
766</symbol>`
767});
768
769unrecognizedSymbol.setId('_unrecognized'); // cannot be output of escapeId()
770
771Input = (function() {
772 class Input {
773 static parseFile(filename, filedata) {
774 var input;
775 //# Generic method to parse file once we're already in the right class.
776 input = new (this)();
777 input.filename = filename;
778 if (filedata == null) {
779 filedata = fs.readFileSync(filename, {
780 encoding: this.encoding
781 });
782 }
783 input.parse(filedata);
784 return input;
785 }
786
787 static recognize(filename, filedata) {
788 var extension;
789 //# Recognize type of file and call corresponding class's `parseFile`.
790 extension = extensionOf(filename);
791 if (extension in extensionMap) {
792 return extensionMap[extension].parseFile(filename, filedata);
793 } else {
794 throw new SVGTilerException(`Unrecognized extension in filename ${filename}`);
795 }
796 }
797
798 };
799
800 Input.encoding = 'utf8';
801
802 return Input;
803
804}).call(this);
805
806Style = class Style extends Input {
807 load(css) {
808 this.css = css;
809 }
810
811};
812
813CSSStyle = (function() {
814 class CSSStyle extends Style {
815 parse(filedata) {
816 return this.load(filedata);
817 }
818
819 };
820
821 CSSStyle.title = "CSS style file";
822
823 return CSSStyle;
824
825}).call(this);
826
827StylusStyle = (function() {
828 class StylusStyle extends Style {
829 parse(filedata) {
830 var styl;
831 styl = require('stylus')(filedata, {
832 filename: this.filename
833 });
834 return this.load(styl.render());
835 }
836
837 };
838
839 StylusStyle.title = "Stylus style file (https://stylus-lang.com/)";
840
841 return StylusStyle;
842
843}).call(this);
844
845Styles = class Styles {
846 constructor(styles1 = []) {
847 this.styles = styles1;
848 }
849
850 push(map) {
851 return this.styles.push(map);
852 }
853
854};
855
856Mapping = class Mapping extends Input {
857 load(data) {
858 this.map = {};
859 if (typeof data === 'function') {
860 return this.function = data;
861 } else {
862 return this.merge(data);
863 }
864 }
865
866 merge(data) {
867 var dirname, key, results, value;
868 if (this.filename != null) {
869 dirname = path.dirname(this.filename);
870 }
871 results = [];
872 for (key in data) {
873 if (!hasProp.call(data, key)) continue;
874 value = data[key];
875 if (!(value instanceof Symbol)) {
876 value = Symbol.parse(key, value, dirname);
877 }
878 results.push(this.map[key] = value);
879 }
880 return results;
881 }
882
883 lookup(key) {
884 var dirname, value;
885 if (this.filename != null) {
886 dirname = path.dirname(this.filename);
887 }
888 key = key.toString(); //# Sometimes get a number, e.g., from XLSX
889 if (key in this.map) {
890 return this.map[key];
891 } else if (this.function != null) {
892 //# Cache return value of function so that only one Symbol generated
893 //# for each key. It still may be a DynamicSymbol, which will allow
894 //# it to make multiple versions, but keep track of which are the same.
895 value = this.function(key);
896 if (value != null) {
897 return this.map[key] = Symbol.parse(key, value, dirname);
898 } else {
899 return value;
900 }
901 } else {
902 return void 0;
903 }
904 }
905
906};
907
908ASCIIMapping = (function() {
909 class ASCIIMapping extends Mapping {
910 parse(data) {
911 var k, key, len, line, map, ref, separator;
912 map = {};
913 ref = splitIntoLines(data);
914 for (k = 0, len = ref.length; k < len; k++) {
915 line = ref[k];
916 separator = whitespace.exec(line);
917 if (separator == null) {
918 continue;
919 }
920 if (separator.index === 0) {
921 if (separator[0].length === 1) {
922 //# Single whitespace character at beginning defines blank character
923 key = '';
924 } else {
925 //# Multiple whitespace at beginning defines first whitespace character
926 key = line[0];
927 }
928 } else {
929 key = line.slice(0, separator.index);
930 }
931 map[key] = line.slice(separator.index + separator[0].length);
932 }
933 return this.load(map);
934 }
935
936 };
937
938 ASCIIMapping.title = "ASCII mapping file";
939
940 ASCIIMapping.help = "Each line is <symbol-name><space><raw SVG or filename.svg>";
941
942 return ASCIIMapping;
943
944}).call(this);
945
946//# Babel plugin to add implicit return to last line of program, to simulate
947//# the effect of `eval`.
948implicitFinalReturn = function({types}) {
949 return {
950 visitor: {
951 Program: function(path) {
952 var body, last, returnLast;
953 body = path.get('body');
954 if (!body.length) {
955 return;
956 }
957 last = body[body.length - 1];
958 if (last.node.type === 'ExpressionStatement') {
959 returnLast = types.returnStatement(last.node.expression);
960 returnLast.leadingComments = last.node.leadingComments;
961 returnLast.innerComments = last.node.innerComments;
962 returnLast.trailingComments = last.node.trailingComments;
963 last.replaceWith(returnLast);
964 }
965 return void 0;
966 }
967 }
968 };
969};
970
971JSMapping = (function() {
972 class JSMapping extends Mapping {
973 parse(data) {
974 var _dirname, _filename, _require, code, func;
975 ({code} = require('@babel/core').transform(data, {
976 filename: this.filename,
977 plugins: [
978 [
979 require.resolve('@babel/plugin-transform-react-jsx'),
980 {
981 useBuiltIns: true,
982 pragma: 'preact.h',
983 pragmaFrag: 'preact.Fragment',
984 throwIfNamespace: false
985 }
986 ],
987 implicitFinalReturn
988 ],
989 sourceMaps: 'inline',
990 retainLines: true
991 }));
992 //code = "#{code}\n//# sourceURL=#{@filename}"
993 //# Mimick NodeJS module's __filename and __dirname variables
994 //# [https://nodejs.org/api/modules.html#modules_the_module_scope]
995 _filename = path.resolve(this.filename);
996 _dirname = path.dirname(_filename);
997 //# Redirect require() to use paths relative to the mapping file.
998 _require = function(module) {
999 if (module.startsWith('.')) {
1000 return require(path.resolve(_dirname, module));
1001 } else {
1002 return require(module);
1003 }
1004 };
1005 //# Use `new Function` instead of `eval` for improved performance and to
1006 //# restrict to passed arguments + global scope. On NodeJS, we could
1007 //# instead use `vm.runInThisContext` (like `CoffeeScript.eval` does).
1008 //@load eval code
1009 func = new Function('__filename', '__dirname', 'require', 'svgtiler', 'preact', code);
1010 return this.load(func(_filename, _dirname, _require, svgtiler, (0 <= code.indexOf('preact') ? require('preact') : void 0)));
1011 }
1012
1013 };
1014
1015 JSMapping.title = "JavaScript mapping file (including JSX notation)";
1016
1017 JSMapping.help = "Object mapping symbol names to SYMBOL e.g. {dot: 'dot.svg'}";
1018
1019 return JSMapping;
1020
1021}).call(this);
1022
1023CoffeeMapping = (function() {
1024 class CoffeeMapping extends JSMapping {
1025 parse(data) {
1026 return super.parse(require('coffeescript').compile(data, {
1027 bare: true,
1028 filename: this.filename,
1029 sourceFiles: [this.filename],
1030 inlineMap: true
1031 }));
1032 }
1033
1034 };
1035
1036 CoffeeMapping.title = "CoffeeScript mapping file (including JSX notation)";
1037
1038 CoffeeMapping.help = "Object mapping symbol names to SYMBOL e.g. dot: 'dot.svg'";
1039
1040 return CoffeeMapping;
1041
1042}).call(this);
1043
1044//catch err
1045// throw err
1046// if err.stack? and err.stack.startsWith "#{@filename}:"
1047// sourceMap = require('coffeescript').compile(data,
1048// bare: true
1049// filename: @filename
1050// sourceFiles: [@filename]
1051// sourceMap: true
1052// ).sourceMap
1053// err.stack = err.stack.replace /:([0-9]*)/, (m, line) ->
1054// ## sourceMap starts line numbers at 0, but we want to work from 1
1055// for col in sourceMap?.lines[line-1]?.columns ? [] when col?.sourceLine?
1056// unless sourceLine? and sourceLine < col.sourceLine
1057// sourceLine = col.sourceLine
1058// line = sourceLine + 1
1059// ":#{line}"
1060// throw err
1061Mappings = class Mappings {
1062 constructor(maps = []) {
1063 this.maps = maps;
1064 }
1065
1066 push(map) {
1067 return this.maps.push(map);
1068 }
1069
1070 lookup(key) {
1071 var i, k, ref, value;
1072 if (!this.maps.length) {
1073 return;
1074 }
1075 for (i = k = ref = this.maps.length - 1; (ref <= 0 ? k <= 0 : k >= 0); i = ref <= 0 ? ++k : --k) {
1076 value = this.maps[i].lookup(key);
1077 if (value != null) {
1078 return value;
1079 }
1080 }
1081 return void 0;
1082 }
1083
1084};
1085
1086blankCells = {
1087 '': true,
1088 ' ': true //# for ASCII art in particular
1089};
1090
1091allBlank = function(list) {
1092 var k, len, x;
1093 for (k = 0, len = list.length; k < len; k++) {
1094 x = list[k];
1095 if ((x != null) && !(x in blankCells)) {
1096 return false;
1097 }
1098 }
1099 return true;
1100};
1101
1102Drawing = (function() {
1103 class Drawing extends Input {
1104 static hrefAttr() {
1105 if (this.useHref) {
1106 return 'href';
1107 } else {
1108 return 'xlink:href';
1109 }
1110 }
1111
1112 load(data) {
1113 var cell, j, k, l, len, len1, row;
1114 //# Turn strings into arrays
1115 data = (function() {
1116 var k, len, results;
1117 results = [];
1118 for (k = 0, len = data.length; k < len; k++) {
1119 row = data[k];
1120 results.push((function() {
1121 var l, len1, results1;
1122 results1 = [];
1123 for (l = 0, len1 = row.length; l < len1; l++) {
1124 cell = row[l];
1125 results1.push(cell);
1126 }
1127 return results1;
1128 })());
1129 }
1130 return results;
1131 })();
1132 if (!Drawing.keepMargins) {
1133 //# Top margin
1134 while (data.length > 0 && allBlank(data[0])) {
1135 data.shift();
1136 }
1137 //# Bottom margin
1138 while (data.length > 0 && allBlank(data[data.length - 1])) {
1139 data.pop();
1140 }
1141 if (data.length > 0) {
1142 //# Left margin
1143 while (allBlank((function() {
1144 var l, len1, results;
1145 results = [];
1146 for (l = 0, len1 = data.length; l < len1; l++) {
1147 row = data[l];
1148 results.push(row[0]);
1149 }
1150 return results;
1151 })())) {
1152 for (k = 0, len = data.length; k < len; k++) {
1153 row = data[k];
1154 row.shift();
1155 }
1156 }
1157 //# Right margin
1158 j = Math.max(...((function() {
1159 var l, len1, results;
1160 results = [];
1161 for (l = 0, len1 = data.length; l < len1; l++) {
1162 row = data[l];
1163 results.push(row.length);
1164 }
1165 return results;
1166 })()));
1167 while (j >= 0 && allBlank((function() {
1168 var len2, n, results;
1169 results = [];
1170 for (n = 0, len2 = data.length; n < len2; n++) {
1171 row = data[n];
1172 results.push(row[j]);
1173 }
1174 return results;
1175 })())) {
1176 for (l = 0, len1 = data.length; l < len1; l++) {
1177 row = data[l];
1178 if (j < row.length) {
1179 row.pop();
1180 }
1181 }
1182 j--;
1183 }
1184 }
1185 }
1186 return this.data = data;
1187 }
1188
1189 writeSVG(mappings, styles, filename) {
1190 //# Default filename is the input filename with extension replaced by .svg
1191 if (filename == null) {
1192 filename = path.parse(this.filename);
1193 if (filename.ext === '.svg') {
1194 filename.base += '.svg';
1195 } else {
1196 filename.base = filename.base.slice(0, -filename.ext.length) + '.svg';
1197 }
1198 filename = path.format(filename);
1199 }
1200 console.log('->', filename);
1201 fs.writeFileSync(filename, this.renderSVG(mappings, styles));
1202 return filename;
1203 }
1204
1205 renderSVGDOM(mappings, styles) {
1206 var cell, colWidths, coordsRow, doc, dx, dy, i, inlineImageVersions, inlineImages, j, k, key, l, lastSymbol, len, len1, len2, len3, len4, len5, level, levelOrder, levels, missing, n, name, node, o, p, q, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, row, row2, rowHeight, scaleX, scaleY, style, styleTag, svg, symbol, symbolsByKey, use, viewBox, x, y;
1207 /*
1208 Main rendering engine, returning an xmldom object for the whole document.
1209 Also saves the table of symbols in `@symbols`, the corresponding
1210 coordinates in `@coords`, and overall `@weight` and `@height`,
1211 for use by `renderTeX`.
1212 */
1213 DynamicSymbol.resetAll();
1214 doc = domImplementation.createDocument(SVGNS, 'svg');
1215 svg = doc.documentElement;
1216 if (!Drawing.useHref) {
1217 svg.setAttribute('xmlns:xlink', XLINKNS);
1218 }
1219 svg.setAttribute('version', '1.1');
1220 ref1 = (ref = styles != null ? styles.styles : void 0) != null ? ref : [];
1221 //svg.appendChild defs = doc.createElementNS SVGNS, 'defs'
1222 //# <style> tags for CSS
1223 for (k = 0, len = ref1.length; k < len; k++) {
1224 style = ref1[k];
1225 svg.appendChild(styleTag = doc.createElementNS(SVGNS, 'style'));
1226 styleTag.textContent = style.css;
1227 }
1228 //# Look up all symbols in the drawing.
1229 missing = {};
1230 this.symbols = (function() {
1231 var l, len1, ref2, results;
1232 ref2 = this.data;
1233 results = [];
1234 for (l = 0, len1 = ref2.length; l < len1; l++) {
1235 row = ref2[l];
1236 results.push((function() {
1237 var len2, n, results1;
1238 results1 = [];
1239 for (n = 0, len2 = row.length; n < len2; n++) {
1240 cell = row[n];
1241 symbol = mappings.lookup(cell);
1242 if (symbol != null) {
1243 results1.push(lastSymbol = symbol);
1244 } else {
1245 missing[cell] = true;
1246 results1.push(unrecognizedSymbol);
1247 }
1248 }
1249 return results1;
1250 })());
1251 }
1252 return results;
1253 }).call(this);
1254 missing = (function() {
1255 var results;
1256 results = [];
1257 for (key in missing) {
1258 if (!hasProp.call(missing, key)) continue;
1259 results.push(`'${key}'`);
1260 }
1261 return results;
1262 })();
1263 if (missing.length) {
1264 console.warn("Failed to recognize symbols:", missing.join(', '));
1265 }
1266 //# Instantiate (.use) all (dynamic) symbols in the drawing.
1267 symbolsByKey = {};
1268 this.symbols = (function() {
1269 var l, len1, ref2, results;
1270 ref2 = this.symbols;
1271 results = [];
1272 for (i = l = 0, len1 = ref2.length; l < len1; i = ++l) {
1273 row = ref2[i];
1274 results.push((function() {
1275 var len2, n, results1;
1276 results1 = [];
1277 for (j = n = 0, len2 = row.length; n < len2; j = ++n) {
1278 symbol = row[j];
1279 if (symbol.usesContext) {
1280 symbol = symbol.use(new Context(this, i, j));
1281 } else {
1282 symbol = symbol.use();
1283 }
1284 if (!(symbol.key in symbolsByKey)) {
1285 symbolsByKey[symbol.key] = symbol;
1286 } else if (symbolsByKey[symbol.key] === !symbol) {
1287 console.warn(`Multiple symbols with key ${symbol.key}`);
1288 }
1289 results1.push(symbol);
1290 }
1291 return results1;
1292 }).call(this));
1293 }
1294 return results;
1295 }).call(this);
1296//# Include all used symbols in SVG
1297 for (key in symbolsByKey) {
1298 symbol = symbolsByKey[key];
1299 if (symbol == null) {
1300 continue;
1301 }
1302 svg.appendChild(symbol.xml.documentElement.cloneNode(true));
1303 }
1304 //# Factor out duplicate inline <image>s into separate <symbol>s.
1305 inlineImages = {};
1306 inlineImageVersions = {};
1307 domRecurse(svg, function(node, parent) {
1308 var attr, attributes, filename, height, href, l, len1, ref2, ref3, use, version, width;
1309 if (node.nodeName !== 'image') {
1310 return true;
1311 }
1312 ({href} = getHref(node));
1313 if (!(href != null ? href.startsWith('data:') : void 0)) {
1314 return true;
1315 }
1316 // data-filename gets set to the original filename when inlining,
1317 // which we use for key labels so isn't needed as an exposed attribute.
1318 // Ditto for width and height of image.
1319 filename = (ref2 = node.getAttribute('data-filename')) != null ? ref2 : '';
1320 node.removeAttribute('data-filename');
1321 width = node.getAttribute('data-width');
1322 node.removeAttribute('data-width');
1323 height = node.getAttribute('data-height');
1324 node.removeAttribute('data-height');
1325 // Transfer x/y/width/height to <use> element, for more re-usability.
1326 parent.replaceChild((use = doc.createElementNS(SVGNS, 'use')), node);
1327 ref3 = ['x', 'y', 'width', 'height'];
1328 for (l = 0, len1 = ref3.length; l < len1; l++) {
1329 attr = ref3[l];
1330 if (node.hasAttribute(attr)) {
1331 use.setAttribute(attr, node.getAttribute(attr));
1332 }
1333 node.removeAttribute(attr);
1334 }
1335 // Memoize versions
1336 attributes = (function() {
1337 var len2, n, ref4, results;
1338 ref4 = node.attributes;
1339 results = [];
1340 for (n = 0, len2 = ref4.length; n < len2; n++) {
1341 attr = ref4[n];
1342 results.push(`${attr.name}=${attr.value}`);
1343 }
1344 return results;
1345 })();
1346 attributes.sort();
1347 attributes = attributes.join(' ');
1348 if (!(attributes in inlineImages)) {
1349 if (inlineImageVersions[filename] == null) {
1350 inlineImageVersions[filename] = 0;
1351 }
1352 version = inlineImageVersions[filename]++;
1353 inlineImages[attributes] = `_image_${escapeId(filename)}_v${version}`;
1354 svg.appendChild(symbol = doc.createElementNS(SVGNS, 'symbol'));
1355 symbol.setAttribute('id', inlineImages[attributes]);
1356 // If we don't have width/height set from data-width/height fields,
1357 // we take the first used width/height as the defining height.
1358 node.setAttribute('width', width || use.getAttribute('width'));
1359 node.setAttribute('height', height || use.getAttribute('height'));
1360 symbol.setAttribute('viewBox', `0 0 ${width} ${height}`);
1361 symbol.appendChild(node);
1362 }
1363 use.setAttribute(Drawing.hrefAttr(), '#' + inlineImages[attributes]);
1364 return false;
1365 });
1366 //# Lay out the symbols in the drawing via SVG <use>.
1367 viewBox = [
1368 0,
1369 0,
1370 0,
1371 0 //# initially x-min, y-min, x-max, y-max
1372 ];
1373 levels = {};
1374 y = 0;
1375 colWidths = {};
1376 this.coords = [];
1377 ref2 = this.symbols;
1378 for (i = l = 0, len1 = ref2.length; l < len1; i = ++l) {
1379 row = ref2[i];
1380 this.coords.push(coordsRow = []);
1381 rowHeight = 0;
1382 for (n = 0, len2 = row.length; n < len2; n++) {
1383 symbol = row[n];
1384 if (!symbol.autoHeight) {
1385 if (symbol.height > rowHeight) {
1386 rowHeight = symbol.height;
1387 }
1388 }
1389 }
1390 x = 0;
1391 for (j = o = 0, len3 = row.length; o < len3; j = ++o) {
1392 symbol = row[j];
1393 coordsRow.push({x, y});
1394 if (symbol == null) {
1395 continue;
1396 }
1397 if (levels[name = symbol.zIndex] == null) {
1398 levels[name] = [];
1399 }
1400 levels[symbol.zIndex].push(use = doc.createElementNS(SVGNS, 'use'));
1401 use.setAttribute(Drawing.hrefAttr(), '#' + symbol.id);
1402 use.setAttribute('x', x);
1403 use.setAttribute('y', y);
1404 scaleX = scaleY = 1;
1405 if (symbol.autoWidth) {
1406 if (colWidths[j] == null) {
1407 colWidths[j] = Math.max(0, ...((function() {
1408 var len4, p, ref3, results;
1409 ref3 = this.symbols;
1410 results = [];
1411 for (p = 0, len4 = ref3.length; p < len4; p++) {
1412 row2 = ref3[p];
1413 if ((row2[j] != null) && !row2[j].autoWidth) {
1414 results.push(row2[j].width);
1415 }
1416 }
1417 return results;
1418 }).call(this)));
1419 }
1420 if (symbol.width !== 0) {
1421 scaleX = colWidths[j] / symbol.width;
1422 }
1423 if (!symbol.autoHeight) {
1424 scaleY = scaleX;
1425 }
1426 }
1427 if (symbol.autoHeight) {
1428 if (symbol.height !== 0) {
1429 scaleY = rowHeight / symbol.height;
1430 }
1431 if (!symbol.autoWidth) {
1432 scaleX = scaleY;
1433 }
1434 }
1435 //# Scaling of symbol is relative to viewBox, so use that to define
1436 //# width and height attributes:
1437 use.setAttribute('width', ((ref3 = (ref4 = symbol.viewBox) != null ? ref4[2] : void 0) != null ? ref3 : symbol.width) * scaleX);
1438 use.setAttribute('height', ((ref5 = (ref6 = symbol.viewBox) != null ? ref6[3] : void 0) != null ? ref5 : symbol.height) * scaleY);
1439 if (symbol.overflowBox != null) {
1440 dx = (symbol.overflowBox[0] - symbol.viewBox[0]) * scaleX;
1441 dy = (symbol.overflowBox[1] - symbol.viewBox[1]) * scaleY;
1442 viewBox[0] = Math.min(viewBox[0], x + dx);
1443 viewBox[1] = Math.min(viewBox[1], y + dy);
1444 viewBox[2] = Math.max(viewBox[2], x + dx + symbol.overflowBox[2] * scaleX);
1445 viewBox[3] = Math.max(viewBox[3], y + dy + symbol.overflowBox[3] * scaleY);
1446 }
1447 x += symbol.width * scaleX;
1448 viewBox[2] = Math.max(viewBox[2], x);
1449 }
1450 y += rowHeight;
1451 viewBox[3] = Math.max(viewBox[3], y);
1452 }
1453 //# Change from x-min, y-min, x-max, y-max to x-min, y-min, width, height
1454 viewBox[2] = viewBox[2] - viewBox[0];
1455 viewBox[3] = viewBox[3] - viewBox[1];
1456 //# Sort by level
1457 levelOrder = ((function() {
1458 var results;
1459 results = [];
1460 for (level in levels) {
1461 results.push(level);
1462 }
1463 return results;
1464 })()).sort(function(x, y) {
1465 return x - y;
1466 });
1467 for (p = 0, len4 = levelOrder.length; p < len4; p++) {
1468 level = levelOrder[p];
1469 ref7 = levels[level];
1470 for (q = 0, len5 = ref7.length; q < len5; q++) {
1471 node = ref7[q];
1472 svg.appendChild(node);
1473 }
1474 }
1475 svg.setAttribute('viewBox', viewBox.join(' '));
1476 svg.setAttribute('width', this.width = viewBox[2]);
1477 svg.setAttribute('height', this.height = viewBox[3]);
1478 //svg.setAttribute 'preserveAspectRatio', 'xMinYMin meet'
1479 return doc;
1480 }
1481
1482 renderSVG(mappings, styles) {
1483 var out;
1484 out = new XMLSerializer().serializeToString(this.renderSVGDOM(mappings, styles));
1485 //# Parsing xlink:href in user's SVG fragments, and then serializing,
1486 //# can lead to these null namespace definitions. Remove.
1487 out = out.replace(/\sxmlns:xlink=""/g, '');
1488 if (prettyXML != null) {
1489 out = prettyXML(out, {
1490 newline: '\n' //# force consistent line endings, not require('os').EOL
1491 });
1492 }
1493 return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1494<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
1495` + out;
1496 }
1497
1498 renderTeX(filename) {
1499 var anchor, basename, child, content, i, j, k, l, len, len1, len2, lines, n, ref, ref1, ref2, ref3, row, symbol, text, tx, ty, wrap, x, y;
1500 //# Must be called *after* `renderSVG` (or `renderSVGDOM`)
1501 filename = path.parse(filename);
1502 basename = filename.base.slice(0, -filename.ext.length);
1503 //# LaTeX based loosely on Inkscape's PDF/EPS/PS + LaTeX output extension.
1504 //# See http://tug.ctan.org/tex-archive/info/svg-inkscape/
1505 lines = [
1506 `%% Creator: svgtiler ${metadata.version}, https://github.com/edemaine/svgtiler
1507%% This LaTeX file includes and overlays text on top of companion file
1508%% ${basename}.pdf/.png
1509%%
1510%% Instead of \\includegraphics, include this figure via
1511%% \\input{${filename.base}}
1512%% You can scale the image by first defining \\svg{width,height,scale}:
1513%% \\def\\svgwidth{\\linewidth} % full width
1514%% or
1515%% \\def\\svgheight{5in}
1516%% or
1517%% \\def\\svgscale{0.5} % 50%
1518%% (If multiple are specified, the first in the list above takes priority.)
1519%%
1520%% If this file resides in another directory from the root .tex file,
1521%% you need to help it find its auxiliary .pdf/.png file via one of the
1522%% following options (any one will do):
1523%% 1. \\usepackage{currfile} so that this file can find its own directory.
1524%% 2. \\usepackage{import} and \\import{path/to/file/}{${filename.base}}
1525%% instead of \\import{${filename.base}}
1526%% 3. \\graphicspath{{path/to/file/}} % note extra braces and trailing slash
1527%%
1528\\begingroup
1529 \\providecommand\\color[2][]{%
1530 \\errmessage{You should load package 'color.sty' to render color in svgtiler text.}%
1531 \\renewcommand\\color[2][]{}%
1532 }%
1533 \\ifx\\currfiledir\\undefined
1534 \\def\\currfiledir{}%
1535 \\fi
1536 \\ifx\\svgwidth\\undefined
1537 \\ifx\\svgheight\\undefined
1538 \\unitlength=0.75bp\\relax % 1px (SVG unit) = 0.75bp (SVG pts)
1539 \\ifx\\svgscale\\undefined\\else
1540 \\ifx\\real\\undefined % in case calc.sty not loaded
1541 \\unitlength=\\svgscale \\unitlength
1542 \\else
1543 \\setlength{\\unitlength}{\\unitlength * \\real{\\svgscale}}%
1544 \\fi
1545 \\fi
1546 \\else
1547 \\unitlength=\\svgheight
1548 \\unitlength=${1 / this.height}\\unitlength % divide by image height
1549 \\fi
1550 \\else
1551 \\unitlength=\\svgwidth
1552 \\unitlength=${1 / this.width}\\unitlength % divide by image width
1553 \\fi
1554 \\def\\clap#1{\\hbox to 0pt{\\hss#1\\hss}}%
1555 \\begin{picture}(${this.width},${this.height})%
1556 \\put(0,0){\\includegraphics[width=${this.width}\\unitlength]{\\currfiledir ${basename}}}%`
1557 ];
1558 ref = this.symbols;
1559 for (i = k = 0, len = ref.length; k < len; i = ++k) {
1560 row = ref[i];
1561 for (j = l = 0, len1 = row.length; l < len1; j = ++l) {
1562 symbol = row[j];
1563 ({x, y} = this.coords[i][j]);
1564 ref1 = symbol.text;
1565 for (n = 0, len2 = ref1.length; n < len2; n++) {
1566 text = ref1[n];
1567 tx = (ref2 = parseNum(text.getAttribute('x'))) != null ? ref2 : 0;
1568 ty = (ref3 = parseNum(text.getAttribute('y'))) != null ? ref3 : 0;
1569 content = ((function() {
1570 var len3, o, ref4, results;
1571 ref4 = text.childNodes;
1572 // TEXT_NODE
1573 results = [];
1574 for (o = 0, len3 = ref4.length; o < len3; o++) {
1575 child = ref4[o];
1576 if (child.nodeType === 3) {
1577 results.push(child.data);
1578 }
1579 }
1580 return results;
1581 })()).join('');
1582 anchor = attributeOrStyle(text, 'text-anchor');
1583 if (/^middle\b/.test(anchor)) {
1584 wrap = '\\clap{';
1585 } else if (/^end\b/.test(anchor)) {
1586 wrap = '\\rlap{'; //if /^start\b/.test anchor # default
1587 } else {
1588 wrap = '\\llap{';
1589 }
1590 // "@height -" is to flip between y down (SVG) and y up (picture)
1591 lines.push(` \\put(${x + tx},${this.height - (y + ty)}){\\color{${attributeOrStyle(text, 'fill') || 'black'}}${wrap}${content}${wrap && '}'}}%`);
1592 }
1593 }
1594 }
1595 lines.push(` \\end{picture}%
1596\\endgroup`, ''); // trailing newline
1597 return lines.join('\n');
1598 }
1599
1600 writeTeX(filename) {
1601 /*
1602 Must be called *after* `writeSVG`.
1603 Default filename is the input filename with extension replaced by .svg_tex
1604 (analogous to .pdf_tex from Inkscape's --export-latex feature, but noting
1605 that the text is extracted from the SVG not the PDF, and that this file
1606 works with both .pdf and .png auxiliary files).
1607 */
1608 if (filename == null) {
1609 filename = path.parse(this.filename);
1610 if (filename.ext === '.svg_tex') {
1611 filename.base += '.svg_tex';
1612 } else {
1613 filename.base = filename.base.slice(0, -filename.ext.length) + '.svg_tex';
1614 }
1615 filename = path.format(filename);
1616 }
1617 console.log(' &', filename);
1618 fs.writeFileSync(filename, this.renderTeX(filename));
1619 return filename;
1620 }
1621
1622 };
1623
1624 Drawing.inlineImages = typeof window === "undefined" || window === null;
1625
1626 return Drawing;
1627
1628}).call(this);
1629
1630ASCIIDrawing = (function() {
1631 class ASCIIDrawing extends Drawing {
1632 parse(data) {
1633 var line;
1634 return this.load((function() {
1635 var k, len, ref, results;
1636 ref = splitIntoLines(data);
1637 results = [];
1638 for (k = 0, len = ref.length; k < len; k++) {
1639 line = ref[k];
1640 results.push(graphemeSplitter.splitGraphemes(line));
1641 }
1642 return results;
1643 })());
1644 }
1645
1646 };
1647
1648 ASCIIDrawing.title = "ASCII drawing (one character per symbol)";
1649
1650 return ASCIIDrawing;
1651
1652}).call(this);
1653
1654DSVDrawing = class DSVDrawing extends Drawing {
1655 parse(data) {
1656 var ref;
1657 //# Remove trailing newline / final blank line.
1658 if (data.slice(-2) === '\r\n') {
1659 data = data.slice(0, -2);
1660 } else if ((ref = data.slice(-1)) === '\r' || ref === '\n') {
1661 data = data.slice(0, -1);
1662 }
1663 //# CSV parser.
1664 return this.load(require('csv-parse/lib/sync')(data, {
1665 delimiter: this.constructor.delimiter,
1666 relax_column_count: true
1667 }));
1668 }
1669
1670};
1671
1672SSVDrawing = (function() {
1673 class SSVDrawing extends DSVDrawing {
1674 parse(data) {
1675 //# Coallesce non-newline whitespace into single space
1676 return super.parse(data.replace(/[ \t\f\v]+/g, ' '));
1677 }
1678
1679 };
1680
1681 SSVDrawing.title = "Space-delimiter drawing (one word per symbol)";
1682
1683 SSVDrawing.delimiter = ' ';
1684
1685 return SSVDrawing;
1686
1687}).call(this);
1688
1689CSVDrawing = (function() {
1690 class CSVDrawing extends DSVDrawing {};
1691
1692 CSVDrawing.title = "Comma-separated drawing (spreadsheet export)";
1693
1694 CSVDrawing.delimiter = ',';
1695
1696 return CSVDrawing;
1697
1698}).call(this);
1699
1700TSVDrawing = (function() {
1701 class TSVDrawing extends DSVDrawing {};
1702
1703 TSVDrawing.title = "Tab-separated drawing (spreadsheet export)";
1704
1705 TSVDrawing.delimiter = '\t';
1706
1707 return TSVDrawing;
1708
1709}).call(this);
1710
1711Drawings = (function() {
1712 class Drawings extends Input {
1713 load(datas) {
1714 var data, drawing;
1715 return this.drawings = (function() {
1716 var k, len, results;
1717 results = [];
1718 for (k = 0, len = datas.length; k < len; k++) {
1719 data = datas[k];
1720 drawing = new Drawing();
1721 drawing.filename = this.filename;
1722 drawing.subname = data.subname;
1723 drawing.load(data);
1724 results.push(drawing);
1725 }
1726 return results;
1727 }).call(this);
1728 }
1729
1730 subfilename(extension, drawing) {
1731 var filename2;
1732 filename2 = path.parse(this.filename);
1733 filename2.base = filename2.base.slice(0, -filename2.ext.length);
1734 if (this.drawings.length > 1) {
1735 filename2.base += this.constructor.filenameSeparator + drawing.subname;
1736 }
1737 filename2.base += extension;
1738 return path.format(filename2);
1739 }
1740
1741 writeSVG(mappings, styles, filename) {
1742 var drawing, k, len, ref, results;
1743 ref = this.drawings;
1744 results = [];
1745 for (k = 0, len = ref.length; k < len; k++) {
1746 drawing = ref[k];
1747 results.push(drawing.writeSVG(mappings, styles, this.subfilename('.svg', drawing)));
1748 }
1749 return results;
1750 }
1751
1752 writeTeX(filename) {
1753 var drawing, k, len, ref, results;
1754 ref = this.drawings;
1755 results = [];
1756 for (k = 0, len = ref.length; k < len; k++) {
1757 drawing = ref[k];
1758 results.push(drawing.writeTeX(this.subfilename('.svg_tex', drawing)));
1759 }
1760 return results;
1761 }
1762
1763 };
1764
1765 Drawings.filenameSeparator = '_';
1766
1767 return Drawings;
1768
1769}).call(this);
1770
1771XLSXDrawings = (function() {
1772 class XLSXDrawings extends Drawings {
1773 parse(data) {
1774 var rows, sheet, sheetInfo, subname, workbook, xlsx;
1775 xlsx = require('xlsx');
1776 workbook = xlsx.read(data, {
1777 type: 'binary'
1778 });
1779 //# https://www.npmjs.com/package/xlsx#common-spreadsheet-format
1780 return this.load((function() {
1781 var k, len, ref, results;
1782 ref = workbook.Workbook.Sheets;
1783 results = [];
1784 for (k = 0, len = ref.length; k < len; k++) {
1785 sheetInfo = ref[k];
1786 subname = sheetInfo.name;
1787 sheet = workbook.Sheets[subname];
1788 //# 0 = Visible, 1 = Hidden, 2 = Very Hidden
1789 //# https://sheetjs.gitbooks.io/docs/#sheet-visibility
1790 if (sheetInfo.Hidden && !Drawings.keepHidden) {
1791 continue;
1792 }
1793 if (subname.length === 31) {
1794 console.warn(`Warning: Sheet '${subname}' has length exactly 31, which may be caused by Google Sheets export truncation`);
1795 }
1796 rows = xlsx.utils.sheet_to_json(sheet, {
1797 header: 1,
1798 defval: ''
1799 });
1800 rows.subname = subname;
1801 results.push(rows);
1802 }
1803 return results;
1804 })());
1805 }
1806
1807 };
1808
1809 XLSXDrawings.encoding = 'binary';
1810
1811 XLSXDrawings.title = "Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)";
1812
1813 return XLSXDrawings;
1814
1815}).call(this);
1816
1817Context = class Context {
1818 constructor(drawing1, i1, j1) {
1819 var ref, ref1;
1820 this.drawing = drawing1;
1821 this.i = i1;
1822 this.j = j1;
1823 this.symbols = this.drawing.symbols;
1824 this.filename = this.drawing.filename;
1825 this.subname = this.drawing.subname;
1826 this.symbol = (ref = this.symbols[this.i]) != null ? ref[this.j] : void 0;
1827 this.key = (ref1 = this.symbol) != null ? ref1.key : void 0;
1828 }
1829
1830 neighbor(dj, di) {
1831 return new Context(this.drawing, this.i + di, this.j + dj);
1832 }
1833
1834 includes(...args) {
1835 return (this.symbol != null) && this.symbol.includes(...args);
1836 }
1837
1838 row(di = 0) {
1839 var i, j, k, len, ref, ref1, results, symbol;
1840 i = this.i + di;
1841 ref1 = (ref = this.symbols[i]) != null ? ref : [];
1842 results = [];
1843 for (j = k = 0, len = ref1.length; k < len; j = ++k) {
1844 symbol = ref1[j];
1845 results.push(new Context(this.drawing, i, j));
1846 }
1847 return results;
1848 }
1849
1850 column(dj = 0) {
1851 var i, j, k, len, ref, results, row;
1852 j = this.j + dj;
1853 ref = this.symbols;
1854 results = [];
1855 for (i = k = 0, len = ref.length; k < len; i = ++k) {
1856 row = ref[i];
1857 results.push(new Context(this.drawing, i, j));
1858 }
1859 return results;
1860 }
1861
1862};
1863
1864extensionMap = {
1865 // Mappings
1866 '.txt': ASCIIMapping,
1867 '.js': JSMapping,
1868 '.jsx': JSMapping,
1869 '.coffee': CoffeeMapping,
1870 '.cjsx': CoffeeMapping,
1871 // Drawings
1872 '.asc': ASCIIDrawing,
1873 '.ssv': SSVDrawing,
1874 '.csv': CSVDrawing,
1875 '.tsv': TSVDrawing,
1876 //# Parsable by xlsx package:
1877 '.xlsx': XLSXDrawings, //# Excel 2007+ XML Format
1878 '.xlsm': XLSXDrawings, //# Excel 2007+ Macro XML Format
1879 '.xlsb': XLSXDrawings, //# Excel 2007+ Binary Format
1880 '.xls': XLSXDrawings, //# Excel 2.0 or 2003-2004 (SpreadsheetML)
1881 '.ods': XLSXDrawings, //# OpenDocument Spreadsheet
1882 '.fods': XLSXDrawings, //# Flat OpenDocument Spreadsheet
1883 '.dif': XLSXDrawings, //# Data Interchange Format (DIF)
1884 '.prn': XLSXDrawings, //# Lotus Formatted Text
1885 '.dbf': XLSXDrawings, //# dBASE II/III/IV / Visual FoxPro
1886 // Styles
1887 '.css': CSSStyle,
1888 '.styl': StylusStyle
1889};
1890
1891sanitize = true;
1892
1893bufferSize = 16 * 1024;
1894
1895postprocess = function(format, filename) {
1896 var buffer, e, file, fileSize, match, position, readSize, string;
1897 if (!sanitize) {
1898 return;
1899 }
1900 try {
1901 switch (format) {
1902 case 'pdf':
1903 //# Blank out /CreationDate in PDF for easier version control.
1904 //# Replace these commands with spaces to avoid in-file pointer errors.
1905 buffer = Buffer.alloc(bufferSize);
1906 fileSize = fs.statSync(filename).size;
1907 position = Math.max(0, fileSize - bufferSize);
1908 file = fs.openSync(filename, 'r+');
1909 readSize = fs.readSync(file, buffer, 0, bufferSize, position);
1910 string = buffer.toString('binary'); //# must use single-byte encoding!
1911 match = /\/CreationDate\s*\((?:[^()\\]|\\[^])*\)/.exec(string);
1912 if (match != null) {
1913 fs.writeSync(file, ' '.repeat(match[0].length), position + match.index);
1914 }
1915 return fs.closeSync(file);
1916 }
1917 } catch (error1) {
1918 e = error1;
1919 return console.log(`Failed to postprocess '${filename}': ${e}`);
1920 }
1921};
1922
1923inkscapeVersion = null;
1924
1925convertSVG = function(format, svg, sync) {
1926 var args, child_process, filename, output, preprocess, result;
1927 child_process = require('child_process');
1928 if (inkscapeVersion == null) {
1929 result = child_process.spawnSync('inkscape', ['--version']);
1930 if (result.error) {
1931 console.log(`inkscape --version failed: ${result.error.message}`);
1932 } else if (result.status || result.signal) {
1933 console.log(`inkscape --version failed: ${result.stderr.toString()}`);
1934 } else {
1935 inkscapeVersion = result.stdout.toString().replace(/^Inkscape\s*/, '');
1936 }
1937 }
1938 filename = path.parse(svg);
1939 if (filename.ext === `.${format}`) {
1940 filename.base += `.${format}`;
1941 } else {
1942 filename.base = `${filename.base.slice(0, -filename.ext.length)}.${format}`;
1943 }
1944 output = path.format(filename);
1945 //# Workaround relative paths not working in MacOS distribution of Inkscape
1946 //# [https://bugs.launchpad.net/inkscape/+bug/181639]
1947 if (process.platform === 'darwin') {
1948 preprocess = path.resolve;
1949 } else {
1950 preprocess = function(x) {
1951 return x;
1952 };
1953 }
1954 if (inkscapeVersion.startsWith('0')) {
1955 args = ["-z", `--file=${preprocess(svg)}`, `--export-${format}=${preprocess(output)}`];
1956 } else {
1957 args = [
1958 "--export-overwrite",
1959 //"--export-type=#{format}"
1960 `--export-filename=${preprocess(output)}`,
1961 preprocess(svg)
1962 ];
1963 }
1964 if (sync) {
1965 //# In sychronous mode, we let inkscape directly output its error messages,
1966 //# and add warnings about any failures that occur.
1967 console.log('=>', output);
1968 result = child_process.spawnSync('inkscape', args, {
1969 stdio: 'inherit'
1970 });
1971 if (result.error) {
1972 return console.log(result.error.message);
1973 } else if (result.status || result.signal) {
1974 return console.log(`:-( ${output} FAILED`);
1975 } else {
1976 return postprocess(format, output);
1977 }
1978 } else {
1979 //# In asychronous mode, we capture inkscape's outputs, and print them only
1980 //# when the process has finished, along with which file failed, to avoid
1981 //# mixing up messages from parallel executions.
1982 return function(resolve) {
1983 var inkscape, out;
1984 console.log('=>', output);
1985 inkscape = child_process.spawn('inkscape', args);
1986 out = '';
1987 inkscape.stdout.on('data', function(buf) {
1988 return out += buf;
1989 });
1990 inkscape.stderr.on('data', function(buf) {
1991 return out += buf;
1992 });
1993 inkscape.on('error', function(error) {
1994 return console.log(error.message);
1995 });
1996 return inkscape.on('exit', function(status, signal) {
1997 if (status || signal) {
1998 console.log(`:-( ${output} FAILED:`);
1999 console.log(out);
2000 } else {
2001 postprocess(format, output);
2002 }
2003 return resolve();
2004 });
2005 };
2006 }
2007};
2008
2009help = function() {
2010 var extension, klass;
2011 console.log(`svgtiler ${metadata.version}
2012Usage: ${process.argv[1]} (...options and filenames...)
2013Documentation: https://github.com/edemaine/svgtiler
2014
2015Optional arguments:
2016 --help Show this help message and exit.
2017 -m / --margin Don't delete blank extreme rows/columns
2018 --hidden Process hidden sheets within spreadsheet files
2019 --tw TILE_WIDTH / --tile-width TILE_WIDTH
2020 Force all symbol tiles to have specified width
2021 --th TILE_HEIGHT / --tile-height TILE_HEIGHT
2022 Force all symbol tiles to have specified height
2023 -p / --pdf Convert output SVG files to PDF via Inkscape
2024 -P / --png Convert output SVG files to PNG via Inkscape
2025 -t / --tex Move <text> from SVG to accompanying LaTeX file.tex
2026 --no-inline Don't inline <image>s into output SVG
2027 --no-overflow Don't default <symbol> overflow to "visible"
2028 --no-sanitize Don't sanitize PDF output by blanking out /CreationDate
2029 -j N / --jobs N Run up to N Inkscape jobs in parallel
2030
2031Filename arguments: (mappings before drawings!)
2032`);
2033 for (extension in extensionMap) {
2034 klass = extensionMap[extension];
2035 if (extension.length < 10) {
2036 extension += ' '.repeat(10 - extension.length);
2037 }
2038 console.log(` *${extension} ${klass.title}`);
2039 if (klass.help != null) {
2040 console.log(` ${klass.help}`);
2041 }
2042 }
2043 console.log(`
2044SYMBOL specifiers: (omit the quotes in anything except .js and .coffee files)
2045
2046 'filename.svg': load SVG from specified file
2047 'filename.png': include PNG image from specified file
2048 'filename.jpg': include JPEG image from specified file
2049 '<svg>...</svg>': raw SVG
2050 -> ...@key...: function computing SVG, with \`this\` bound to Context with
2051 \`key\` (symbol name), \`i\` and \`j\` (y and x coordinates),
2052 \`filename\` (drawing filename), \`subname\` (subsheet name),
2053 and supporting \`neighbor\`/\`includes\`/\`row\`/\`column\` methods`);
2054 //object with one or more attributes
2055 return process.exit();
2056};
2057
2058main = function() {
2059 var arg, args, filename, filenames, files, format, formats, i, input, jobs, k, l, len, len1, len2, mappings, n, skip, styles, sync;
2060 mappings = new Mappings();
2061 styles = new Styles();
2062 args = process.argv.slice(2);
2063 files = skip = 0;
2064 formats = [];
2065 jobs = [];
2066 sync = true;
2067 for (i = k = 0, len = args.length; k < len; i = ++k) {
2068 arg = args[i];
2069 if (skip) {
2070 skip--;
2071 continue;
2072 }
2073 switch (arg) {
2074 case '-h':
2075 case '--help':
2076 help();
2077 break;
2078 case '-m':
2079 case '--margin':
2080 Drawing.keepMargins = true;
2081 break;
2082 case '--hidden':
2083 Drawings.keepHidden = true;
2084 break;
2085 case '--tw':
2086 case '--tile-width':
2087 skip = 1;
2088 arg = parseFloat(args[i + 1]);
2089 if (arg) {
2090 Symbol.forceWidth = arg;
2091 } else {
2092 console.warn(`Invalid argument to --tile-width: ${args[i + 1]}`);
2093 }
2094 break;
2095 case '--th':
2096 case '--tile-height':
2097 skip = 1;
2098 arg = parseFloat(args[i + 1]);
2099 if (arg) {
2100 Symbol.forceHeight = arg;
2101 } else {
2102 console.warn(`Invalid argument to --tile-height: ${args[i + 1]}`);
2103 }
2104 break;
2105 case '-p':
2106 case '--pdf':
2107 formats.push('pdf');
2108 break;
2109 case '-P':
2110 case '--png':
2111 formats.push('png');
2112 break;
2113 case '-t':
2114 case '--tex':
2115 Symbol.texText = true;
2116 break;
2117 case '--no-sanitize':
2118 sanitize = false;
2119 break;
2120 case '--no-overflow':
2121 Symbol.overflowDefault = null; // no default
2122 break;
2123 case '--no-inline':
2124 Drawing.inlineImages = false;
2125 break;
2126 case '-j':
2127 case '--jobs':
2128 skip = 1;
2129 arg = parseInt(args[i + 1]);
2130 if (arg) {
2131 jobs = new require('async-limiter')({
2132 concurrency: arg
2133 });
2134 sync = false;
2135 } else {
2136 console.warn(`Invalid argument to --jobs: ${args[i + 1]}`);
2137 }
2138 break;
2139 default:
2140 files++;
2141 console.log('*', arg);
2142 input = Input.recognize(arg);
2143 if (input instanceof Mapping) {
2144 mappings.push(input);
2145 } else if (input instanceof Style) {
2146 styles.push(input);
2147 } else if (input instanceof Drawing || input instanceof Drawings) {
2148 filenames = input.writeSVG(mappings, styles);
2149 if (Symbol.texText) {
2150 input.writeTeX();
2151 }
2152 for (l = 0, len1 = formats.length; l < len1; l++) {
2153 format = formats[l];
2154 if (typeof filenames === 'string') {
2155 jobs.push(convertSVG(format, filenames, sync));
2156 } else {
2157 for (n = 0, len2 = filenames.length; n < len2; n++) {
2158 filename = filenames[n];
2159 jobs.push(convertSVG(format, filename, sync));
2160 }
2161 }
2162 }
2163 }
2164 }
2165 }
2166 if (!files) {
2167 console.log('Not enough filename arguments');
2168 return help();
2169 }
2170};
2171
2172svgtiler = {
2173 Symbol,
2174 StaticSymbol,
2175 DynamicSymbol,
2176 unrecognizedSymbol,
2177 Mapping,
2178 ASCIIMapping,
2179 JSMapping,
2180 CoffeeMapping,
2181 Drawing,
2182 ASCIIDrawing,
2183 DSVDrawing,
2184 SSVDrawing,
2185 CSVDrawing,
2186 TSVDrawing,
2187 Drawings,
2188 XLSXDrawings,
2189 Style,
2190 CSSStyle,
2191 StylusStyle,
2192 extensionMap,
2193 Input,
2194 Mappings,
2195 Context,
2196 SVGTilerException,
2197 SVGNS,
2198 XLINKNS,
2199 escapeId,
2200 main,
2201 convertSVG,
2202 version: metadata.version
2203};
2204
2205if (typeof module !== "undefined" && module !== null) {
2206 module.exports = svgtiler;
2207}
2208
2209if (typeof window !== "undefined" && window !== null) {
2210 window.svgtiler = svgtiler;
2211}
2212
2213if (typeof window === "undefined" || window === null) {
2214 if ((typeof require !== "undefined" && require !== null ? require.main : void 0) === module) {
2215 main();
2216 }
2217}
2218
2219}).call(this);