UNPKG

18.6 kBMarkdownView Raw
1# SVG Tiler
2**SVG Tiler** is a tool for drawing diagrams on a grid using text or
3spreadsheets, and then substituting SVG symbols to make a big SVG figure,
4and optionally convert it to PDF.
5
6To use SVG Tiler, you combine two types of files
7(possibly multiple of each type):
8
91. A **mapping file** specifies how to map symbol names (strings) to
10 SVG content (either embedded in the same file or in separate files).
11 Mapping files can be specified in a simple ASCII format, or
12 as a dynamic mapping defined by JavaScript or CoffeeScript code.
13
142. A **drawing file** specifies a grid of symbol names (strings) which,
15 combined with one or more mapping files to define the SVG associated
16 with each symbol, compile to a single (tiled) SVG.
17 Drawing files can be specified as ASCII art (where each symbol is
18 limited to a single character), space-separated ASCII art
19 (where symbols are separated by whitespace), or standard CSV/TSV
20 formats such as those exported by Google Sheets or Excel.
21
22These input files are listed on the `svgtiler` command line,
23with mapping files typically before drawing files.
24File types and formats are distinguished automatically by their extension.
25For example:
26
27```
28svgtiler map1.txt map2.coffee drawing.asc drawings.xls
29```
30
31will generate `drawing.svg` using the mappings in `map1.txt` and `map2.coffee`,
32and will generate `drawings_<sheet>.svg` for each unhidden sheet in
33`drawings.xlsx`.
34
35## Mapping Files: .txt, .js, .coffee, .jsx, .cjsx
36
37In the **.txt format** for mapping files, each line consists of a symbol name
38(either having no spaces, or consisting entirely of a single space),
39followed by whitespace, followed by either a block of SVG code
40(such as `<symbol viewBox="...">...</symbol>`) or a filename containing
41such a block. For example, here is a mapping of `O` to black squares
42and both ` ` (space) and empty string to blank squares, all dimensioned
4350 &times; 50:
44
45```
46O <symbol viewBox="0 0 50 50"><rect width="50" height="50"/></symbol>
47 <symbol viewBox="0 0 50 50"></symbol>
48 <symbol viewBox="0 0 50 50"></symbol>
49```
50
51Here is a mapping of the same symbols to external files:
52
53```
54O O.svg
55 blank.svg
56 blank.svg
57```
58
59In the **.js / .coffee / .jsx / .cjsx formats**, the file consists of
60JavaScript / CoffeeScript code, the last line of which should evaluate to either
61
621. an *object* whose keys are symbol names, or
632. a *function* in one argument, a symbol name (string).
64 (This feature allows you to parse symbol names how you want; or check an
65 object for a matching key but use a default value otherwise; etc.).
66
67The object or function should map a symbol name to either
68
691. a string of SVG code (detected by the presence of a `<` character),
702. [Preact](https://preactjs.com/) (React-style) Virtual DOM elements, via
71 [JSX](https://reactjs.org/docs/introducing-jsx.html) syntax
72 (or its [CoffeeScript analog](https://coffeescript.org/#jsx))
73 or via `preact.h` calls
74 (see [the polyomino example](examples/polyomino)),
753. a filename with `.svg` extension containing SVG code,
764. a filename with `.png`, `.jpg`, `.jpeg`, or `.gif` extension
77 containing an image, or
785. a function returning one of the above.
79
80In the last case, the function is called *for each occurrence of the symbol*,
81and has `this` bound to a manufactured `Context` object, giving you access to
82the following properties:
83
84* `this.key` is the symbol name, or `null` if the `Context` is out of bounds
85 of the drawing.
86* `this.includes(substring)` computes whether `this.key` contains the given
87 `substring` (as would `this.key.includes(substring)` in ECMAScript 2015).
88* `this.i` is the row number of the cell of this symbol occurrence (starting
89 at 0).
90* `this.j` is the column number of the cell of this symbol occurrence
91 (starting at 0).
92* `this.neighbor(dj, di)` returns a new `Context` for row `i + di` and
93 column `j + dj`. (Note the reversal of coordinates, so that the order
94 passed to `neighbor` corresponds to *x* then *y* coordinate.)
95 If there is no symbol at that position, you will still get a `Context`
96 whose `key` value is `null` and whose `includes()` always returns `false`.
97* In particular, it's really useful to call e.g.
98 `this.neighbor(1, 0).includes('-')` to check for adjacent symbols that
99 change how this symbol should be rendered.
100* `this.row(di = 0)` returns an array of `Context` objects, one for each
101 symbol in row `i + di` (in particular, including `this` if `di` is the
102 default of `0`). For example, you can use the `some` or `every` methods
103 on this array to do bulk tests on the row.
104* `this.column(dj = 0)` returns an array of `Context` objects, one for each
105 symbol in column `j + dj`.
106* `this.filename` is the name of the drawing file (e.g. `"input.xlsx"`).
107* `this.subname` is the name of the sheet within the spreadsheet drawing input,
108 or `undefined` if the input format does allow multiple sheets.
109
110Like regular NodeJS modules, .js and .coffee files can access `__dirname`
111and `__filename`, e.g., to use paths relative to the mapping file.
112They can also use `require('./filename')` to import local modules relative to
113the mapping file, which is useful for sharing code among mapping files.
114
115## Drawing Files: .asc, .ssv, .csv, .tsv, .xlsx, .xls, .ods
116
117The **.asc format** for drawing files represents traditional ASCII art:
118each non-newline character represents a one-character symbol name.
119For example, here is a simple 5 &times; 5 ASCII drawing using symbols
120`O` and ` `&nbsp;(space):
121
122```
123 OOO
124O O O
125OOOOO
126O O
127 OOO
128```
129
130.asc files can include Unicode characters encoded in UTF8.
131In this case, a single "character" is defined as a full "Unicode grapheme"
132(according to [UAX #29](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules), via the [grapheme-splitter library](https://github.com/orling/grapheme-splitter)),
133such as 👍🏽.
134See [an example with Unicode](examples/unicode).
135
136The **.ssv, .csv, and .tsv formats** use
137[delimiter-separated values (DSV)](https://en.wikipedia.org/wiki/Delimiter-separated_values)
138to specify an array of symbol names. In particular,
139[.csv (comma-separated)](https://en.wikipedia.org/wiki/Comma-separated_values)
140and
141[.tsv (tab-separated)](https://en.wikipedia.org/wiki/Tab-separated_values)
142formats are exactly those exported by spreadsheet software such as
143Google Drive or Excel, enabling you to draw in that software.
144The .ssv format is similar, but where the delimiter between symbol names
145is arbitrary whitespace.
146(Contrast this behavior with .csv which treats every comma as a delimiter.)
147This format is nice to work with in a text editor, allowing you to line up
148the columns by padding symbol names with extra spaces.
149All three formats support quoting according to the usual DSV rules:
150any symbol name (in particular, if it has a delimiter or double quote in it)
151can be put in double quotes, and double quotes can be produced in the
152symbol name by putting `""` (two double quotes) within the quoted string.
153Thus, the one-character symbol name `"` would be represented by `""""`.
154
155The **.xlsx, .xlsm, .xlsb, .xls** (Microsoft Excel),
156**.ods, .fods** (OpenDocument), **.dif** (Data Interchange Format),
157**.prn** (Lotus), and **.dbf** (dBASE/FoxPro) formats support data straight
158from spreadsheet software. This format is special in that it supports
159multiple sheets in one file. In this case, the output SVG files have
160filenames distinguished by an underscore followed by the sheet name.
161By default, **hidden** sheets are ignored, making it easy to "deprecate" old
162drafts, but if you prefer, you can process hidden sheets via `--hidden`.
163
164## Layout Algorithm
165
166Given one or more mapping files and a drawing file, SVG Tiler follows a fairly
167simple layout algorithm to place the SVG expansions of the symbols into a
168single SVG output. Each symbol has a bounding box, either specified by
169the `viewBox` of the root element, or
170[automatically computed](#automatic-symbol-wrapping).
171The algorithm places symbols in a single row to align their top edges,
172with no horizontal space between them.
173The algorithm places rows to align their left edges so that the rows' bounding
174boxes touch, with the bottom of one row's bounding box equalling the top of
175the next row's bounding box.
176
177This layout algorithm works well if each row has a uniform height and each
178column has a uniform width, even if different rows have different heights
179and different columns have different widths. But it probably isn't what you
180want if symbols have wildly differing widths or heights, so you should set
181your `viewBox`es accordingly.
182
183Each unique symbol gets defined just once (via SVG's `<symbol>`) and
184then instantiated (via SVG's `<use>`) many times,
185resulting in relatively small and efficient SVG outputs.
186
187## z-index: Stacking Order of Symbols
188
189Often it is helpful to render some tile symbols on top of others.
190Although [SVG](https://www.w3.org/TR/SVG2/) does not support a `z-index`
191property, there was
192[a proposal](https://www.w3.org/TR/2016/CR-SVG2-20160915/render.html#ZIndexProperty)
193which SVG Tiler supports *at the `<symbol>` level*, emulated by
194re-ordering tile rendering order to simulate the specified z order.
195For example, `<symbol viewBox="0 0 10 10" z-index="2">...</symbol>`
196will be rendered on top of (later than) all symbols without a
197`z-index="..."` specification (which default to a z-index of 0).
198You can use a `z-index="..."` property or an HTML-style
199`style="z-index: ..."` property.
200
201## Overflow and Bounding Box
202
203To allow a tile `<symbol>` to draw outside its own `viewBox`, you need to set
204`overflow="visible"` or `style="overflow: visible"`. (This is
205[a standard SVG feature](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/overflow).)
206In this case, `viewBox` represents the size of the element in the grid layout,
207but allows the element's actual bounding box to be something else.
208To correctly set the bounding box of the overall SVG drawing, SVG Tiler
209defines an additional symbol attribute called `overflowBox`, which is like
210`viewBox` but for specifying the actual bounding box of the content
211(when they differ &mdash; `overflowBox` defaults to the value of `viewBox`).
212The `viewBox` of the overall SVG is set to the minimum rectangle
213containing all symbols' `overflowBox`s.
214
215For example, `<symbol viewBox="0 0 10 10" overflowBox="-5 -5 20 20" overflow="visible">...</symbol>`
216defines a symbol that gets laid out as if it occupies the [0, 10] &times;
217[0, 10] square, but the symbol can draw outside that square, and the overall
218drawing bounding box will be set as if the symbol occupies the
219[&minus;5, 15] &times; [&minus;5, 15] square.
220
221Even zero-width and zero-height symbols will get rendered when
222`overflow="visible"` is specified. This can be useful for drawing grid
223outlines without affecting the overall grid layout, for example.
224(SVG defines that [symbols are invisible if they have zero width or
225height](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox),
226so SVG Tiler automatically works around this by using slightly positive
227widths and heights in the output `viewBox`.)
228
229## Autosizing Symbols
230
231As a special non-SVG feature, symbols can specify `width="auto"` and/or
232`height="auto"` to make their instantiated width and/or height match their
233column and/or row, respectively.
234In this way, multiple uses of the same symbol can appear as different sizes.
235See the [auto sizing example](examples/auto).
236
237If you want to nonuniformly scale the tile, you may want to also adjust
238the symbol's [`preserveAspectRatio`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio) property.
239
240## Unrecognized Symbols
241
242Any undefined symbol displays as a red-on-yellow diamond question mark
243(like the [Unicode replacement character](https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character)),
244with automatic width and height, so that it is easy to spot.
245See the [auto sizing example](examples/auto).
246
247## Automatic `<symbol>` Wrapping
248
249In limited cases, you can avoid wrapping your symbol definitions in
250`<symbol>` tags, or avoid specifying the `viewBox` of the `<symbol>` tag.
251In this case, SVG Tiler attempts to set the `viewBox` to the bounding box of
252the SVG elements in the symbol.
253For example, the SVG `<rect x="-5" y="-5" width="10" height="10"/>`
254will automatically get wrapped by `<symbol viewBox="-5 -5 10 10">`.
255However, the current computation has many limitations (see the code for
256details), so it is recommended to specify your own `viewBox`, especially to
257control the layout bounding box which may different from the contents'
258bounding box.
259
260## Converting SVG to PDF/PNG
261
262SVG Tiler can automatically convert all exported SVG files into PDF and/or PNG,
263if you have [Inkscape](https://inkscape.org/) installed, via the `-p`/`--pdf`
264and/or `-P` or `--png` command-line options.
265For example: `svgtiler -p map.coffee drawings.xls`
266will generate both `drawings_sheet.svg` and `drawings_sheet.pdf`.
267PNG conversion is intended for pixel art; see the
268[Tetris example](examples/tetris/).
269
270You can speed up multiple Inkscape conversions process on a multithreaded CPU
271via the `-j`/`--jobs` command-line option.
272For example, `svgtiler -j 4 -p map.coffee drawings.xls`
273will run up to four Inkscape jobs at once.
274
275## LaTeX Text
276
277Using the `-t` command-line option, you can extract all `<text>` from the SVG
278into a LaTeX overlay file so that your text gets rendered by LaTeX during
279inclusion.
280
281For example: `svgtiler -p -t map.coffee drawings.xls`
282will create `drawings_sheet.svg`, `drawings_sheet.pdf`, and
283`drawings_sheet.svg_tex`. The first two files omit the text, while the third
284file is the one to include in LaTeX, via `\input{drawings_sheet.svg_tex}`.
285The same `.svg_tex` file will include graphics defined by `.pdf`
286(created with `-p`) or `.png` (created with `-P`).
287
288You can control the scale of the graphics component by defining
289`\svgwidth`, `\svgheight`, or `\svgscale` before `\input`ting the `.svg_tex`.
290(If more than one is specified, the first in the list takes priority.)
291For example:
292* `\def\svgwidth{\linewidth}` causes the figure to span the full width
293* `\def\svgheight{5in}` makes the figure 5 inches tall
294* `\def\svgscale{0.5}` makes the figure 50% of its natural size
295 (where the SVG coordinates' unit translates to 1px = 0.75bp)
296
297If the figure files are in a different directory from your root `.tex` file,
298you need to help the `.svg_tex` file find its auxiliary `.pdf`/`.png` file
299via one of the following options (any one will do):
300* `\usepackage{currfile}` to enable finding the figure's directory.
301* `\usepackage{import}` and `\import{path/to/file/}{filename.svg_tex}`
302* instead of `\import{filename.svg_tex}`.
303* `\graphicspath{{path/to/file/}}` (note extra braces and trailing slash).
304
305## Examples
306
307Games
308* [Tetris](examples/tetris)
309* [The Witness](examples/witness)
310* [Tilt](examples/tilt)
311
312Demos
313* [Polyomino outline drawing and JSX](examples/polyomino)
314* [Auto width/height](examples/auto)
315* [Unicode](examples/unicode)
316
317## Installation
318
319After [installing Node](https://nodejs.org/en/download/),
320you can install this tool via
321
322```sh
323npm install -g svgtiler
324```
325
326## Command-Line Usage
327
328The command-line arguments consist mostly of mapping and/or drawing files.
329The files and other arguments are processed *in order*, so for example a
330drawing can use all mapping files specified *before* it on the command line.
331If the same symbol is defined by multiple mapping files, later mappings take
332precedence (overwriting previous mappings).
333
334Here is the output of `svgtiler --help`:
335
336```
337Usage: svgtiler (...options and filenames...)
338
339Optional arguments:
340 --help Show this help message and exit.
341 -m / --margin Don't delete blank extreme rows/columns
342 --hidden Process hidden sheets within spreadsheet files
343 --tw TILE_WIDTH / --tile-width TILE_WIDTH
344 Force all symbol tiles to have specified width
345 --th TILE_HEIGHT / --tile-height TILE_HEIGHT
346 Force all symbol tiles to have specified height
347 -p / --pdf Convert output SVG files to PDF via Inkscape
348 -P / --png Convert output SVG files to PNG via Inkscape
349 -t / --tex Move <text> from SVG to accompanying LaTeX file.tex
350 --no-sanitize Don't sanitize PDF output by blanking out /CreationDate
351 -j N / --jobs N Run up to N Inkscape jobs in parallel
352
353Filename arguments: (mappings before drawings!)
354
355 *.txt ASCII mapping file
356 Each line is <symbol-name><space><raw SVG or filename.svg>
357 *.js JavaScript mapping file
358 Object mapping symbol names to SYMBOL e.g. dot: 'dot.svg'
359 *.coffee CoffeeScript mapping file
360 Object mapping symbol names to SYMBOL e.g. dot: 'dot.svg'
361 *.asc ASCII drawing (one character per symbol)
362 *.ssv Space-delimiter drawing (one word per symbol)
363 *.csv Comma-separated drawing (spreadsheet export)
364 *.tsv Tab-separated drawing (spreadsheet export)
365 *.xlsx Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
366 *.xlsm Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
367 *.xlsb Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
368 *.xls Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
369 *.ods Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
370 *.fods Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
371 *.dif Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
372 *.prn Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
373 *.dbf Spreadsheet drawing(s) (Excel/OpenDocument/Lotus/dBASE)
374
375SYMBOL specifiers: (omit the quotes in anything except .js and .coffee files)
376
377 'filename.svg': load SVG from specifies file
378 'filename.png': include PNG image from specified file
379 'filename.jpg': include JPEG image from specified file
380 '<svg>...</svg>': raw SVG
381 -> ...@key...: function computing SVG, with `this` bound to Context with
382 `key` (symbol name), `i` and `j` (y and x coordinates),
383 `filename` (drawing filename), `subname` (subsheet name),
384 and supporting `neighbor`/`includes`/`row`/`column` methods
385```
386
387## About
388
389This take on SVG Tiler was written by Erik Demaine, in discussions with
390Jeffrey Bosboom and others, with the intent of subsuming his
391[original SVG Tiler](https://github.com/jbosboom/svg-tiler).
392In particular, the .txt mapping format and .asc drawing format here
393are nearly identical to the formats supported by the original.