1 | # SVG Tiler
|
2 | **SVG Tiler** is a tool for drawing diagrams on a grid using text or
|
3 | spreadsheets, and then substituting SVG symbols to make a big SVG figure,
|
4 | and optionally convert it to PDF.
|
5 |
|
6 | To use SVG Tiler, you combine two types of files
|
7 | (possibly multiple of each type):
|
8 |
|
9 | 1. 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 |
|
14 | 2. 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 |
|
22 | These input files are listed on the `svgtiler` command line,
|
23 | with mapping files typically before drawing files.
|
24 | File types and formats are distinguished automatically by their extension.
|
25 | For example:
|
26 |
|
27 | ```
|
28 | svgtiler map1.txt map2.coffee drawing.asc drawings.xls
|
29 | ```
|
30 |
|
31 | will generate `drawing.svg` using the mappings in `map1.txt` and `map2.coffee`,
|
32 | and will generate `drawings_<sheet>.svg` for each unhidden sheet in
|
33 | `drawings.xlsx`.
|
34 |
|
35 | ## Mapping Files: .txt, .js, .coffee, .jsx, .cjsx
|
36 |
|
37 | In 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),
|
39 | followed by whitespace, followed by either a block of SVG code
|
40 | (such as `<symbol viewBox="...">...</symbol>`) or a filename containing
|
41 | such a block. For example, here is a mapping of `O` to black squares
|
42 | and both ` ` (space) and empty string to blank squares, all dimensioned
|
43 | 50 × 50:
|
44 |
|
45 | ```
|
46 | O <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 |
|
51 | Here is a mapping of the same symbols to external files:
|
52 |
|
53 | ```
|
54 | O O.svg
|
55 | blank.svg
|
56 | blank.svg
|
57 | ```
|
58 |
|
59 | In the **.js / .coffee / .jsx / .cjsx formats**, the file consists of
|
60 | JavaScript / CoffeeScript code, the last line of which should evaluate to either
|
61 |
|
62 | 1. an *object* whose keys are symbol names, or
|
63 | 2. 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 |
|
67 | The object or function should map a symbol name to either
|
68 |
|
69 | 1. a string of SVG code (detected by the presence of a `<` character),
|
70 | 2. [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)),
|
75 | 3. a filename with `.svg` extension containing SVG code,
|
76 | 4. a filename with `.png`, `.jpg`, `.jpeg`, or `.gif` extension
|
77 | containing an image, or
|
78 | 5. a function returning one of the above.
|
79 |
|
80 | In the last case, the function is called *for each occurrence of the symbol*,
|
81 | and has `this` bound to a manufactured `Context` object, giving you access to
|
82 | the 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 |
|
110 | Like regular NodeJS modules, .js and .coffee files can access `__dirname`
|
111 | and `__filename`, e.g., to use paths relative to the mapping file.
|
112 | They can also use `require('./filename')` to import local modules relative to
|
113 | the mapping file, which is useful for sharing code among mapping files.
|
114 |
|
115 | ## Drawing Files: .asc, .ssv, .csv, .tsv, .xlsx, .xls, .ods
|
116 |
|
117 | The **.asc format** for drawing files represents traditional ASCII art:
|
118 | each non-newline character represents a one-character symbol name.
|
119 | For example, here is a simple 5 × 5 ASCII drawing using symbols
|
120 | `O` and ` ` (space):
|
121 |
|
122 | ```
|
123 | OOO
|
124 | O O O
|
125 | OOOOO
|
126 | O O
|
127 | OOO
|
128 | ```
|
129 |
|
130 | .asc files can include Unicode characters encoded in UTF8.
|
131 | In 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)),
|
133 | such as 👍🏽.
|
134 | See [an example with Unicode](examples/unicode).
|
135 |
|
136 | The **.ssv, .csv, and .tsv formats** use
|
137 | [delimiter-separated values (DSV)](https://en.wikipedia.org/wiki/Delimiter-separated_values)
|
138 | to specify an array of symbol names. In particular,
|
139 | [.csv (comma-separated)](https://en.wikipedia.org/wiki/Comma-separated_values)
|
140 | and
|
141 | [.tsv (tab-separated)](https://en.wikipedia.org/wiki/Tab-separated_values)
|
142 | formats are exactly those exported by spreadsheet software such as
|
143 | Google Drive or Excel, enabling you to draw in that software.
|
144 | The .ssv format is similar, but where the delimiter between symbol names
|
145 | is arbitrary whitespace.
|
146 | (Contrast this behavior with .csv which treats every comma as a delimiter.)
|
147 | This format is nice to work with in a text editor, allowing you to line up
|
148 | the columns by padding symbol names with extra spaces.
|
149 | All three formats support quoting according to the usual DSV rules:
|
150 | any symbol name (in particular, if it has a delimiter or double quote in it)
|
151 | can be put in double quotes, and double quotes can be produced in the
|
152 | symbol name by putting `""` (two double quotes) within the quoted string.
|
153 | Thus, the one-character symbol name `"` would be represented by `""""`.
|
154 |
|
155 | The **.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
|
158 | from spreadsheet software. This format is special in that it supports
|
159 | multiple sheets in one file. In this case, the output SVG files have
|
160 | filenames distinguished by an underscore followed by the sheet name.
|
161 | By default, **hidden** sheets are ignored, making it easy to "deprecate" old
|
162 | drafts, but if you prefer, you can process hidden sheets via `--hidden`.
|
163 |
|
164 | ## Layout Algorithm
|
165 |
|
166 | Given one or more mapping files and a drawing file, SVG Tiler follows a fairly
|
167 | simple layout algorithm to place the SVG expansions of the symbols into a
|
168 | single SVG output. Each symbol has a bounding box, either specified by
|
169 | the `viewBox` of the root element, or
|
170 | [automatically computed](#automatic-symbol-wrapping).
|
171 | The algorithm places symbols in a single row to align their top edges,
|
172 | with no horizontal space between them.
|
173 | The algorithm places rows to align their left edges so that the rows' bounding
|
174 | boxes touch, with the bottom of one row's bounding box equalling the top of
|
175 | the next row's bounding box.
|
176 |
|
177 | This layout algorithm works well if each row has a uniform height and each
|
178 | column has a uniform width, even if different rows have different heights
|
179 | and different columns have different widths. But it probably isn't what you
|
180 | want if symbols have wildly differing widths or heights, so you should set
|
181 | your `viewBox`es accordingly.
|
182 |
|
183 | Each unique symbol gets defined just once (via SVG's `<symbol>`) and
|
184 | then instantiated (via SVG's `<use>`) many times,
|
185 | resulting in relatively small and efficient SVG outputs.
|
186 |
|
187 | ## z-index: Stacking Order of Symbols
|
188 |
|
189 | Often it is helpful to render some tile symbols on top of others.
|
190 | Although [SVG](https://www.w3.org/TR/SVG2/) does not support a `z-index`
|
191 | property, there was
|
192 | [a proposal](https://www.w3.org/TR/2016/CR-SVG2-20160915/render.html#ZIndexProperty)
|
193 | which SVG Tiler supports *at the `<symbol>` level*, emulated by
|
194 | re-ordering tile rendering order to simulate the specified z order.
|
195 | For example, `<symbol viewBox="0 0 10 10" z-index="2">...</symbol>`
|
196 | will be rendered on top of (later than) all symbols without a
|
197 | `z-index="..."` specification (which default to a z-index of 0).
|
198 | You can use a `z-index="..."` property or an HTML-style
|
199 | `style="z-index: ..."` property.
|
200 |
|
201 | ## Overflow and Bounding Box
|
202 |
|
203 | To 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).)
|
206 | In this case, `viewBox` represents the size of the element in the grid layout,
|
207 | but allows the element's actual bounding box to be something else.
|
208 | To correctly set the bounding box of the overall SVG drawing, SVG Tiler
|
209 | defines 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 — `overflowBox` defaults to the value of `viewBox`).
|
212 | The `viewBox` of the overall SVG is set to the minimum rectangle
|
213 | containing all symbols' `overflowBox`s.
|
214 |
|
215 | For example, `<symbol viewBox="0 0 10 10" overflowBox="-5 -5 20 20" overflow="visible">...</symbol>`
|
216 | defines a symbol that gets laid out as if it occupies the [0, 10] ×
|
217 | [0, 10] square, but the symbol can draw outside that square, and the overall
|
218 | drawing bounding box will be set as if the symbol occupies the
|
219 | [−5, 15] × [−5, 15] square.
|
220 |
|
221 | Even zero-width and zero-height symbols will get rendered when
|
222 | `overflow="visible"` is specified. This can be useful for drawing grid
|
223 | outlines without affecting the overall grid layout, for example.
|
224 | (SVG defines that [symbols are invisible if they have zero width or
|
225 | height](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox),
|
226 | so SVG Tiler automatically works around this by using slightly positive
|
227 | widths and heights in the output `viewBox`.)
|
228 |
|
229 | ## Autosizing Symbols
|
230 |
|
231 | As 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
|
233 | column and/or row, respectively.
|
234 | In this way, multiple uses of the same symbol can appear as different sizes.
|
235 | See the [auto sizing example](examples/auto).
|
236 |
|
237 | If you want to nonuniformly scale the tile, you may want to also adjust
|
238 | the symbol's [`preserveAspectRatio`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio) property.
|
239 |
|
240 | ## Unrecognized Symbols
|
241 |
|
242 | Any 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)),
|
244 | with automatic width and height, so that it is easy to spot.
|
245 | See the [auto sizing example](examples/auto).
|
246 |
|
247 | ## Automatic `<symbol>` Wrapping
|
248 |
|
249 | In limited cases, you can avoid wrapping your symbol definitions in
|
250 | `<symbol>` tags, or avoid specifying the `viewBox` of the `<symbol>` tag.
|
251 | In this case, SVG Tiler attempts to set the `viewBox` to the bounding box of
|
252 | the SVG elements in the symbol.
|
253 | For example, the SVG `<rect x="-5" y="-5" width="10" height="10"/>`
|
254 | will automatically get wrapped by `<symbol viewBox="-5 -5 10 10">`.
|
255 | However, the current computation has many limitations (see the code for
|
256 | details), so it is recommended to specify your own `viewBox`, especially to
|
257 | control the layout bounding box which may different from the contents'
|
258 | bounding box.
|
259 |
|
260 | ## Converting SVG to PDF/PNG
|
261 |
|
262 | SVG Tiler can automatically convert all exported SVG files into PDF and/or PNG,
|
263 | if you have [Inkscape](https://inkscape.org/) installed, via the `-p`/`--pdf`
|
264 | and/or `-P` or `--png` command-line options.
|
265 | For example: `svgtiler -p map.coffee drawings.xls`
|
266 | will generate both `drawings_sheet.svg` and `drawings_sheet.pdf`.
|
267 | PNG conversion is intended for pixel art; see the
|
268 | [Tetris example](examples/tetris/).
|
269 |
|
270 | You can speed up multiple Inkscape conversions process on a multithreaded CPU
|
271 | via the `-j`/`--jobs` command-line option.
|
272 | For example, `svgtiler -j 4 -p map.coffee drawings.xls`
|
273 | will run up to four Inkscape jobs at once.
|
274 |
|
275 | ## LaTeX Text
|
276 |
|
277 | Using the `-t` command-line option, you can extract all `<text>` from the SVG
|
278 | into a LaTeX overlay file so that your text gets rendered by LaTeX during
|
279 | inclusion.
|
280 |
|
281 | For example: `svgtiler -p -t map.coffee drawings.xls`
|
282 | will create `drawings_sheet.svg`, `drawings_sheet.pdf`, and
|
283 | `drawings_sheet.svg_tex`. The first two files omit the text, while the third
|
284 | file is the one to include in LaTeX, via `\input{drawings_sheet.svg_tex}`.
|
285 | The same `.svg_tex` file will include graphics defined by `.pdf`
|
286 | (created with `-p`) or `.png` (created with `-P`).
|
287 |
|
288 | You 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.)
|
291 | For 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 |
|
297 | If the figure files are in a different directory from your root `.tex` file,
|
298 | you need to help the `.svg_tex` file find its auxiliary `.pdf`/`.png` file
|
299 | via 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 |
|
307 | Games
|
308 | * [Tetris](examples/tetris)
|
309 | * [The Witness](examples/witness)
|
310 | * [Tilt](examples/tilt)
|
311 |
|
312 | Demos
|
313 | * [Polyomino outline drawing and JSX](examples/polyomino)
|
314 | * [Auto width/height](examples/auto)
|
315 | * [Unicode](examples/unicode)
|
316 |
|
317 | ## Installation
|
318 |
|
319 | After [installing Node](https://nodejs.org/en/download/),
|
320 | you can install this tool via
|
321 |
|
322 | ```sh
|
323 | npm install -g svgtiler
|
324 | ```
|
325 |
|
326 | ## Command-Line Usage
|
327 |
|
328 | The command-line arguments consist mostly of mapping and/or drawing files.
|
329 | The files and other arguments are processed *in order*, so for example a
|
330 | drawing can use all mapping files specified *before* it on the command line.
|
331 | If the same symbol is defined by multiple mapping files, later mappings take
|
332 | precedence (overwriting previous mappings).
|
333 |
|
334 | Here is the output of `svgtiler --help`:
|
335 |
|
336 | ```
|
337 | Usage: svgtiler (...options and filenames...)
|
338 |
|
339 | Optional 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 |
|
353 | Filename 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 |
|
375 | SYMBOL 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 |
|
389 | This take on SVG Tiler was written by Erik Demaine, in discussions with
|
390 | Jeffrey Bosboom and others, with the intent of subsuming his
|
391 | [original SVG Tiler](https://github.com/jbosboom/svg-tiler).
|
392 | In particular, the .txt mapping format and .asc drawing format here
|
393 | are nearly identical to the formats supported by the original.
|