rsyntaxtree 1.13.1 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +90 -19
- data/README.md +48 -0
- data/lib/rsyntaxtree/element.rb +11 -2
- data/lib/rsyntaxtree/markup_parser.rb +8 -1
- data/lib/rsyntaxtree/notation_core.md +95 -42
- data/lib/rsyntaxtree/string_parser.rb +50 -1
- data/lib/rsyntaxtree/svg_graph.rb +92 -30
- data/lib/rsyntaxtree/utils.rb +19 -11
- data/lib/rsyntaxtree/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 965fe8dae50ad6fe7907850239f395fda6a5186654a81d36b89400ebe52108f2
|
|
4
|
+
data.tar.gz: 4a183744f4d05b63efcbd1e9aedb6a4bf060654fde9dfc62abaeb20e6a38ac8a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee4fca1e926ad6886aaca041468f61dc0a596eeffa107c4ee3a82576f2ac7ac53eddcdc0ee586fc5f4408fc809188dba1279eb0ff8083892ef1b598aea830ca1
|
|
7
|
+
data.tar.gz: 19173b80e3c2376a9f25d6ea72604bc5774194af7eccfc2b1158dfdf75939ed5d700f654b8122ecf3df99242036686666266ed829710fbe22d94bbea965ae91b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,77 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [1.
|
|
3
|
+
## [1.14.0] - 2026-08-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- The notation has a map. The reference the gem ships (`--notation`) is one
|
|
7
|
+
page: the characters that already mean something, then every feature at a
|
|
8
|
+
line each with the notation beside it, then the options. Both manuals open
|
|
9
|
+
with the same list, each row pointing at the section that explains it. A test
|
|
10
|
+
reads the feature set out of the grammar itself and draws every sample, so a
|
|
11
|
+
feature the grammar gains with no row in the documents fails the build, and a
|
|
12
|
+
sample that stops meaning what it says fails with it.
|
|
13
|
+
- The changelog is on the documentation site, at /changelog, built from
|
|
14
|
+
CHANGELOG.md and linked from the manuals and the README.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Every release in the changelog is dated to the day, from the git tags. Two of
|
|
18
|
+
the old month-only dates were wrong.
|
|
19
|
+
- What the trap section warns about is now, by definition, what the tool
|
|
20
|
+
accepts and draws as something other than what was meant. A mistake the tool
|
|
21
|
+
refuses is the error message's job, and its card has come down; the warning
|
|
22
|
+
about parentheses, which the Penn Treebank conversion had made false, tells
|
|
23
|
+
the truth again.
|
|
24
|
+
- The arrow markers' dimensions are rounded; the defs of every SVG carried
|
|
25
|
+
float dust in an attribute.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Turning `derivation` on no longer deletes a column from a node that has no
|
|
29
|
+
daughters. A rule name is written after a column break and names the step
|
|
30
|
+
that produced a node from its daughters, so a node with none names no rule —
|
|
31
|
+
but the label is read before the tree is built, and the name was taken out
|
|
32
|
+
anyway and then had no rule to be drawn beside. `[A\tfoo]` drew "A foo" with
|
|
33
|
+
the option off and "A" with it on. The label is put back once the tree is
|
|
34
|
+
known.
|
|
35
|
+
- A mistake inside a node's label keeps the name of what is wrong with it. The
|
|
36
|
+
first raw space splits a token into a label and its children, and when the
|
|
37
|
+
label would not parse, every cause but one was relabelled "a raw space split
|
|
38
|
+
this" — so the message named an unknown colour while the code and the hint
|
|
39
|
+
talked about spaces, and a caller acting on the code was sent to fix what was
|
|
40
|
+
not wrong. Which story is right is now asked of the parser rather than
|
|
41
|
+
guessed: the token is put back together with its spaces written as the
|
|
42
|
+
notation writes them, and if it reads, the space was the cause.
|
|
43
|
+
- A hex colour is three digits or six, which is what every message about colour
|
|
44
|
+
here already said. The grammar asked for three to six, so four and five
|
|
45
|
+
parsed, passed the validator — which does not look at a value beginning with
|
|
46
|
+
`#` — and reached librsvg, which cannot read them and draws the label black
|
|
47
|
+
without reporting it.
|
|
48
|
+
|
|
49
|
+
## [1.13.2] - 2026-08-27
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
- A movement path is drawn as one stroke with its corners eased, in place of
|
|
53
|
+
three lines meeting at right angles. The dash of a non-directional path now
|
|
54
|
+
runs round each turn instead of restarting at it, and the arrowhead is the end
|
|
55
|
+
of the stroke rather than a marker on whichever line finished last. The radius
|
|
56
|
+
is held down to half of each run it joins, so a short leg cannot be swallowed
|
|
57
|
+
by its own turn, and to the length of an arrowhead where there is one, so the
|
|
58
|
+
head is never drawn onto the curve. Three gallery figures carry paths and
|
|
59
|
+
change with it.
|
|
60
|
+
- Digits are set in the text face again on machines that have a colour emoji
|
|
61
|
+
font. The font chains named emoji families by name, and asking fontconfig for
|
|
62
|
+
one is asking for the generic `emoji` family, whose preference list fontconfig
|
|
63
|
+
then prepends to the whole pattern with a strong binding — so the emoji face
|
|
64
|
+
arrived ahead of the Noto text faces rather than behind them. A colour emoji
|
|
65
|
+
font carries the ASCII digits, for keycap sequences, so on an ordinary Linux
|
|
66
|
+
desktop `[N 20]` came out in the emoji face at more than twice the width.
|
|
67
|
+
Measurement and drawing read the same chain, so the figure held together; it
|
|
68
|
+
was simply not the figure the same input gives elsewhere. No emoji family is
|
|
69
|
+
named now: a codepoint no named family covers reaches fontconfig's own
|
|
70
|
+
fallback, which is the path this was duplicating. Emoji draw as before, and
|
|
71
|
+
the gallery is unchanged to the pixel — every raster figure is byte-identical
|
|
72
|
+
and the SVGs differ only in the family list they carry.
|
|
73
|
+
|
|
74
|
+
## [1.13.1] - 2026-08-27
|
|
4
75
|
|
|
5
76
|
### Fixed
|
|
6
77
|
- `^` at the head of a leaf draws the triangle it asks for. The mark may be
|
|
@@ -19,7 +90,7 @@
|
|
|
19
90
|
every input, because the form still read a `format` select the page no longer
|
|
20
91
|
had. A value that is actually wrong is still rejected.
|
|
21
92
|
|
|
22
|
-
## [1.13.0] - 2026-08
|
|
93
|
+
## [1.13.0] - 2026-08-24
|
|
23
94
|
|
|
24
95
|
### Added
|
|
25
96
|
- Derivations. A derivation puts the words first and the result last, and
|
|
@@ -38,7 +109,7 @@
|
|
|
38
109
|
have when the root is at the top, so every line ran from above the daughter,
|
|
39
110
|
through its label and the mother's, to below the mother.
|
|
40
111
|
|
|
41
|
-
## [1.12.0] - 2026-08
|
|
112
|
+
## [1.12.0] - 2026-08-22
|
|
42
113
|
|
|
43
114
|
Everything needed to write this notation is now available as text, built from
|
|
44
115
|
the files the tool itself reads, and a refusal says where that text is.
|
|
@@ -68,7 +139,7 @@ the files the tool itself reads, and a refusal says where that text is.
|
|
|
68
139
|
meant waiting on a pipe that never closes when stdin is not a terminal,
|
|
69
140
|
which is how a script reaches them.
|
|
70
141
|
|
|
71
|
-
## [1.11.0] - 2026-08
|
|
142
|
+
## [1.11.0] - 2026-08-21
|
|
72
143
|
|
|
73
144
|
Every input the tool accepts now draws, and every input it refuses says why.
|
|
74
145
|
Line thickness follows the type size, so figures drawn at any font size keep
|
|
@@ -127,7 +198,7 @@ fifth lighter than before.
|
|
|
127
198
|
read as a path to the current directory.
|
|
128
199
|
- Overline was documented as missing from PNG output. It has been there.
|
|
129
200
|
|
|
130
|
-
## [1.10.0] - 2026-08
|
|
201
|
+
## [1.10.0] - 2026-08-19
|
|
131
202
|
|
|
132
203
|
A transitional release ahead of 2.0, which drops JPG/GIF output and the
|
|
133
204
|
RMagick dependency.
|
|
@@ -150,7 +221,7 @@ RMagick dependency.
|
|
|
150
221
|
file here carries `frozen_string_literal`, which made the working form the
|
|
151
222
|
unobvious one.
|
|
152
223
|
|
|
153
|
-
## [1.9.0] - 2026-08
|
|
224
|
+
## [1.9.0] - 2026-08-19
|
|
154
225
|
|
|
155
226
|
### Added
|
|
156
227
|
- `hyphen: literal`, which trades the two readings of a hyphen: a bare one is
|
|
@@ -218,7 +289,7 @@ RMagick dependency.
|
|
|
218
289
|
the arrow was drawn small to fit; now the pair is spread and the arrow keeps
|
|
219
290
|
its size. Only pairs that carry a link move, and only when they need to.
|
|
220
291
|
|
|
221
|
-
## [1.8.2] - 2026-08
|
|
292
|
+
## [1.8.2] - 2026-08-17
|
|
222
293
|
|
|
223
294
|
### Changed
|
|
224
295
|
- `tidy: high` now compresses as far as its name promises. The level-balance
|
|
@@ -246,7 +317,7 @@ RMagick dependency.
|
|
|
246
317
|
resolved nowhere on macOS, Debian/Ubuntu or Alpine; the chain fell through
|
|
247
318
|
to `Noto Sans Mono`, which it now names directly.
|
|
248
319
|
|
|
249
|
-
## [1.8.1] - 2026-08
|
|
320
|
+
## [1.8.1] - 2026-08-15
|
|
250
321
|
|
|
251
322
|
### Fixed
|
|
252
323
|
- Arabic rendered as isolated, unjoined letterforms in some environments.
|
|
@@ -275,7 +346,7 @@ RMagick dependency.
|
|
|
275
346
|
- Documentation records how to override any of the named families with a
|
|
276
347
|
fontconfig alias, for users who prefer their own script fonts.
|
|
277
348
|
|
|
278
|
-
## [1.8.0] - 2026-08
|
|
349
|
+
## [1.8.0] - 2026-08-15
|
|
279
350
|
|
|
280
351
|
### Added
|
|
281
352
|
- `tidy` layout scale, one option covering every layout mode from the most
|
|
@@ -324,7 +395,7 @@ RMagick dependency.
|
|
|
324
395
|
when the caller passes only a partial parameter set (e.g. `tidy: off`
|
|
325
396
|
rendered as `symmetric` in the web UI).
|
|
326
397
|
|
|
327
|
-
## [1.7.0] - 2026-08
|
|
398
|
+
## [1.7.0] - 2026-08-13
|
|
328
399
|
|
|
329
400
|
### Changed
|
|
330
401
|
- Text measurement now uses Pango, the same engine (and the same fontconfig
|
|
@@ -344,7 +415,7 @@ RMagick dependency.
|
|
|
344
415
|
- Runtime dependency on the `pango` gem (ruby-gnome). No new system
|
|
345
416
|
requirements: librsvg already depends on Pango.
|
|
346
417
|
|
|
347
|
-
## [1.6.3] - 2026-07
|
|
418
|
+
## [1.6.3] - 2026-07-19
|
|
348
419
|
|
|
349
420
|
### Fixed
|
|
350
421
|
- Packaged files no longer carry owner-only permissions. `gem build` preserves
|
|
@@ -356,14 +427,14 @@ RMagick dependency.
|
|
|
356
427
|
tree had picked up on data files (images, fonts, Markdown, CSS, library
|
|
357
428
|
sources).
|
|
358
429
|
|
|
359
|
-
## [1.6.2] - 2026-06
|
|
430
|
+
## [1.6.2] - 2026-06-26
|
|
360
431
|
|
|
361
432
|
### Fixed
|
|
362
433
|
- Region shade no longer touches the image edge when its padded bounds reach
|
|
363
434
|
past the tree's natural extent (e.g. a deep enclosed/multi-line node): the
|
|
364
435
|
canvas now grows with a margin around the shaded plane.
|
|
365
436
|
|
|
366
|
-
## [1.6.1] - 2026-06
|
|
437
|
+
## [1.6.1] - 2026-06-26
|
|
367
438
|
|
|
368
439
|
### Improved
|
|
369
440
|
- Region shade rendering wraps the subtree more cleanly: wider, balanced
|
|
@@ -377,7 +448,7 @@ RMagick dependency.
|
|
|
377
448
|
- Example 065: nested (overlapping) region shades, shown as progressively
|
|
378
449
|
darker gray.
|
|
379
450
|
|
|
380
|
-
## [1.6.0] - 2026-06
|
|
451
|
+
## [1.6.0] - 2026-06-25
|
|
381
452
|
|
|
382
453
|
### Added
|
|
383
454
|
- Region shade (`%` prefix): paints a semi-transparent plane behind the whole
|
|
@@ -405,7 +476,7 @@ RMagick dependency.
|
|
|
405
476
|
- Region shade on the root/topmost node no longer clipped by the canvas: the
|
|
406
477
|
SVG viewBox now grows to include region planes that extend past the tree.
|
|
407
478
|
|
|
408
|
-
## [1.5.0] - 2026-04
|
|
479
|
+
## [1.5.0] - 2026-04-06
|
|
409
480
|
|
|
410
481
|
### Added
|
|
411
482
|
- Left-to-right tree layout (`-d ltr` / `--direction ltr`)
|
|
@@ -420,7 +491,7 @@ RMagick dependency.
|
|
|
420
491
|
### Improved
|
|
421
492
|
- TTB path bulge proportional to endpoint distance (reduced excess)
|
|
422
493
|
|
|
423
|
-
## [1.4.0] - 2026-01
|
|
494
|
+
## [1.4.0] - 2026-01-06
|
|
424
495
|
|
|
425
496
|
### Added
|
|
426
497
|
- LSIF (Linguistic Structure Interchange Format) JSON output (`-f lsif`)
|
|
@@ -436,15 +507,15 @@ RMagick dependency.
|
|
|
436
507
|
- Added examples for per-node coloring (054, 055, 056)
|
|
437
508
|
- Added example 057: Subscript and superscript demo
|
|
438
509
|
|
|
439
|
-
## [1.3.2] -
|
|
510
|
+
## [1.3.2] - 2025-03-03
|
|
440
511
|
|
|
441
512
|
- Garbage collection friendly implementation
|
|
442
513
|
|
|
443
|
-
## [1.3.1] - 2024
|
|
514
|
+
## [1.3.1] - 2024-10-24
|
|
444
515
|
|
|
445
516
|
- Bug fixes and improvements
|
|
446
517
|
|
|
447
|
-
## [1.3.0] - 2024
|
|
518
|
+
## [1.3.0] - 2024-08-27
|
|
448
519
|
|
|
449
520
|
- TikZ/forest LaTeX output support
|
|
450
521
|
|
data/README.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**RSyntaxTree** is a graphical syntax tree generator for linguistic research.
|
|
4
4
|
|
|
5
|
+
[](https://github.com/yohasebe/rsyntaxtree/actions/workflows/test.yml)
|
|
6
|
+
[](https://rubygems.org/gems/rsyntaxtree)
|
|
7
|
+
|
|
5
8
|
<p>
|
|
6
9
|
<a href="https://yohasebe.com/rsyntaxtree"><strong>Web App</strong></a> ·
|
|
7
10
|
<a href="https://yohasebe.github.io/rsyntaxtree/examples"><strong>Example Gallery</strong></a> ·
|
|
@@ -203,6 +206,50 @@ fontsize: 18
|
|
|
203
206
|
|
|
204
207
|
CLI options override config file settings.
|
|
205
208
|
|
|
209
|
+
## Using RSyntaxTree with an AI Model
|
|
210
|
+
|
|
211
|
+
The notation is small, but several characters in it already mean something, so
|
|
212
|
+
a model writing it does better with the reference in front of it than without.
|
|
213
|
+
|
|
214
|
+
**If the model only writes the notation** and you draw it yourself — in the
|
|
215
|
+
[web interface](https://yohasebe.com/rsyntaxtree) or on your own machine —
|
|
216
|
+
nothing needs installing. Give the model one of these to read:
|
|
217
|
+
|
|
218
|
+
- [The brief reference](https://yohasebe.github.io/rsyntaxtree/llms.txt), about
|
|
219
|
+
a page, leading with the characters that already mean something.
|
|
220
|
+
- [Everything in one file](https://yohasebe.github.io/rsyntaxtree/llms-full.txt):
|
|
221
|
+
the reference, the manual and all the gallery examples, 78 KB.
|
|
222
|
+
|
|
223
|
+
**If the model can run a shell**, it can draw and then look at what it drew,
|
|
224
|
+
which is the part that lets it correct itself:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
docker run --rm -v "$PWD:/work" -w /work ghcr.io/yohasebe/rsyntaxtree \
|
|
228
|
+
rsyntaxtree --notation # the reference, on stdout
|
|
229
|
+
docker run --rm -v "$PWD:/work" -w /work ghcr.io/yohasebe/rsyntaxtree \
|
|
230
|
+
rsyntaxtree -f png -o . "[S [NP a tree] [VP appears]]"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Then read `syntree.png` back. `--examples` prints every gallery example with
|
|
234
|
+
the settings it was drawn at, and `--validate` reports what is wrong with an
|
|
235
|
+
input as JSON — including whether rewriting it is worth trying — without
|
|
236
|
+
drawing anything:
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{ "schema": "rsyntaxtree.error/1", "ok": false,
|
|
240
|
+
"errors": [ { "code": "unbalanced_brackets",
|
|
241
|
+
"message": "Error: open and close brackets do not match",
|
|
242
|
+
"hint": "Count the brackets: every '[' needs one ']'.",
|
|
243
|
+
"retryable": true } ],
|
|
244
|
+
"reference": "rsyntaxtree --notation, or https://yohasebe.github.io/rsyntaxtree/llms-full.txt" }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The exit code carries the verdict too, so a script can branch on it without
|
|
248
|
+
reading the JSON. The Docker image is used above because this tool draws
|
|
249
|
+
through Pango and librsvg, and a figure depends on which versions of those —
|
|
250
|
+
and which fonts — are installed; the image fixes all of them. Installed as a
|
|
251
|
+
gem it works the same, once those libraries are on the machine.
|
|
252
|
+
|
|
206
253
|
## Documentation
|
|
207
254
|
|
|
208
255
|
For detailed documentation on syntax and markup:
|
|
@@ -210,6 +257,7 @@ For detailed documentation on syntax and markup:
|
|
|
210
257
|
- [Documentation in English](https://yohasebe.github.io/rsyntaxtree/documentation)
|
|
211
258
|
- [日本語ドキュメント](https://yohasebe.github.io/rsyntaxtree/documentation_ja)
|
|
212
259
|
- [Example Gallery](https://yohasebe.github.io/rsyntaxtree/examples)
|
|
260
|
+
- [Changelog](https://yohasebe.github.io/rsyntaxtree/changelog)
|
|
213
261
|
|
|
214
262
|
## How to Cite
|
|
215
263
|
|
data/lib/rsyntaxtree/element.rb
CHANGED
|
@@ -13,7 +13,7 @@ require_relative "color_names"
|
|
|
13
13
|
|
|
14
14
|
module RSyntaxTree
|
|
15
15
|
class Element
|
|
16
|
-
attr_accessor :rule_name, :id, :parent, :type, :level, :width, :height, :content, :content_width, :text_width, :content_height, :horizontal_indent, :vertical_indent, :triangle, :enclosure, :children, :font, :fontsize, :contains_phrase, :path, :color, :raw_content, :region, :region_color
|
|
16
|
+
attr_accessor :rule_name, :label_with_rule_name, :id, :parent, :type, :level, :width, :height, :content, :content_width, :text_width, :content_height, :horizontal_indent, :vertical_indent, :triangle, :enclosure, :children, :font, :fontsize, :contains_phrase, :path, :color, :raw_content, :region, :region_color
|
|
17
17
|
|
|
18
18
|
# names_a_rule says the content is a mother's label rather than a leaf's
|
|
19
19
|
# text. Only a mother has a step under it for a name to sit beside, and a
|
|
@@ -42,7 +42,11 @@ module RSyntaxTree
|
|
|
42
42
|
@fontset = fontset
|
|
43
43
|
@fontsize = fontsize
|
|
44
44
|
# In a derivation the label may carry the name of the rule that produced
|
|
45
|
-
# it, written after a column break: `S/NP\t
|
|
45
|
+
# it, written after a column break: `S/NP\t>B`. The name is taken out
|
|
46
|
+
# before the label is read as markup, so it is drawn exactly as written —
|
|
47
|
+
# `>` and `<` need no escaping there, and escaping them puts the backslash
|
|
48
|
+
# in the figure. (This example carried one, which is the opposite of what
|
|
49
|
+
# the gallery's derivations do.) The name belongs beside
|
|
46
50
|
# the rule rather than beside the result, so it comes out of the label
|
|
47
51
|
# here, before the label is measured, and BaseGraph draws it at the end
|
|
48
52
|
# of the rule.
|
|
@@ -51,6 +55,11 @@ module RSyntaxTree
|
|
|
51
55
|
name = at && content[(at + 2)..].to_s.strip
|
|
52
56
|
if name && !name.empty?
|
|
53
57
|
@rule_name = name
|
|
58
|
+
# Kept whole, because whether this node has daughters — and so
|
|
59
|
+
# whether it names a rule at all — is not known until the tree
|
|
60
|
+
# is built. StringParser puts the label back if it turns out
|
|
61
|
+
# there was no step for the name to belong to.
|
|
62
|
+
@label_with_rule_name = content
|
|
54
63
|
content = content[0...at]
|
|
55
64
|
end
|
|
56
65
|
end
|
|
@@ -15,7 +15,14 @@ class MarkupParser < Parslet::Parser
|
|
|
15
15
|
|
|
16
16
|
# Color specification: @colorname: or @#hexcode:
|
|
17
17
|
rule(:color_name) { match('[a-zA-Z]').repeat(1) }
|
|
18
|
-
|
|
18
|
+
# Three digits or six, which is what every message about colour in this
|
|
19
|
+
# codebase already says. The rule used to be written as "three to six", so
|
|
20
|
+
# four and five passed the parser, passed the validator (which does not look
|
|
21
|
+
# at a value beginning with '#' at all), and went into the SVG — where
|
|
22
|
+
# librsvg cannot read them and draws the label black, with nothing reported.
|
|
23
|
+
# Six first: on a six-digit value the three-digit branch matches and then
|
|
24
|
+
# the ':' is not there to be found.
|
|
25
|
+
rule(:color_hex) { str('#') >> (match('[0-9a-fA-F]').repeat(6, 6) | match('[0-9a-fA-F]').repeat(3, 3)) }
|
|
19
26
|
rule(:color_spec) { str('@') >> (color_hex | color_name).as(:color_value) >> str(':') }
|
|
20
27
|
|
|
21
28
|
# Region shade: '%' marks the node so that the whole subtree it governs
|
|
@@ -1,52 +1,105 @@
|
|
|
1
|
-
RSyntaxTree notation,
|
|
1
|
+
RSyntaxTree notation, on one page.
|
|
2
2
|
|
|
3
3
|
A tree is labeled brackets: [S [NP the cat] [VP sat]]. The label follows the
|
|
4
|
-
opening bracket; children follow the label.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
4
|
+
opening bracket; children follow the label. Parentheses are read as Penn
|
|
5
|
+
Treebank notation and converted: (S (NP the cat) (VP sat)) draws the same tree.
|
|
6
|
+
|
|
7
|
+
## What already means something
|
|
8
|
+
|
|
9
|
+
The characters that bite, first. A mistake the tool can see is refused with a
|
|
10
|
+
hint; everything below is taken silently, and the figure that draws is not the
|
|
11
|
+
figure that was meant.
|
|
12
|
+
|
|
13
|
+
- `<>` is one space and `<3>` is three, everywhere — never angle brackets.
|
|
14
|
+
Where linguistics wants the brackets themselves, write ⟨ and ⟩ (U+27E8/9):
|
|
15
|
+
SPR\t⟨<>NP<>⟩, 'hand⟨SUBJ,OBJ⟩'. A label written <NP> is refused with this
|
|
16
|
+
advice; a stray <3> simply draws three spaces.
|
|
17
|
+
- A pair of hyphens underlines what stands between them: well-made-word draws
|
|
18
|
+
"made" underlined. (A lone hyphen is refused with a hint.) Write `\-` for a
|
|
19
|
+
hyphen — V\-bar, HEAD\-DTR — or set the hyphen option to literal.
|
|
20
|
+
- A raw space is safe in a one-line label and unreliable in a label that has
|
|
21
|
+
`\n` or `\t` in it: there it can split a value that carries markup, and it
|
|
22
|
+
breaks a nested #( ... #) matrix. Inside any label with columns, write a
|
|
23
|
+
space as `<>`: 'a<>toy', not 'a toy'.
|
|
24
|
+
- A straight apostrophe is typeset as the curly ’ (U+2019).
|
|
25
|
+
- A backslash takes the character after it, whatever it is: \q draws q, and
|
|
26
|
+
C:\path draws C:path. A backslash itself is \\.
|
|
27
|
+
- The prefixes of a label compose in one order, and only this one:
|
|
28
|
+
`^` → `#`/`##`/`###` → `%` → `@color:`. So ^#%@red:NP is read whole, while
|
|
29
|
+
@red:%NP leaves the % as a literal character and silently drops the shade.
|
|
30
|
+
- A rule name (for derivations) is written after the label's last `\t`, as in
|
|
31
|
+
[S/NP\t>B ...], and needs the derivation option ON — with it off the name is
|
|
32
|
+
drawn as a column. The name is taken out before markup runs, so `>` and `<`
|
|
33
|
+
need no escaping there and `\>` would put the backslash in the figure. A
|
|
34
|
+
label that also carries `\n` keeps all its columns instead.
|
|
35
|
+
|
|
36
|
+
## What there is
|
|
37
|
+
|
|
38
|
+
Structure:
|
|
39
|
+
|
|
40
|
+
| what | how it is written |
|
|
41
|
+
|----------------------------------------|-------------------|
|
|
42
|
+
| a node with children | [S [NP the cat] [VP sat]] |
|
|
43
|
+
| a phrase under one leaf (triangle) | [NP a toy] — one word, forced: [NP ^cats] |
|
|
44
|
+
| an invisible joint, to align leaves | a label of only <>, as in [X [<> [Y y]] [Z z]] |
|
|
45
|
+
| a movement path (dashed) | [S [NP a+1] [VP [V b] [NP c+1]]] |
|
|
46
|
+
| the same, with an arrowhead | write +>1 on the end the arrow points at |
|
|
47
|
+
| an extra straight connector | +-1 on two nodes; +->1 for an arrowhead |
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
Inside a label:
|
|
50
|
+
|
|
51
|
+
| what | how it is written |
|
|
52
|
+
|----------------------------------------|-------------------|
|
|
53
|
+
| italic, bold, both | *x*, **x**, ***x*** |
|
|
54
|
+
| subscript, superscript | x_i_, x__2__ |
|
|
55
|
+
| small capitals | H___EAD___ |
|
|
56
|
+
| overline, underline, strikethrough | =x=, -x-, ~x~ |
|
|
57
|
+
| a line break; a blank line | a\nb (or a\ b); \n\n |
|
|
58
|
+
| columns, aligned down the label | HEAD\t*verb*\nSPR\t⟨<>⟩ |
|
|
59
|
+
| a horizontal rule across the label | a line of --- (or === for a double rule) |
|
|
60
|
+
| boxed, circled text | |1|, {2} — more than one character draws a capsule |
|
|
61
|
+
| empty and hatched boxes and circles | ||, {}, |/|, {/} |
|
|
62
|
+
| bar and arrows as symbols | --, ->, <-, <-> — bold with *...*, as in *->* |
|
|
63
|
+
|
|
64
|
+
Around a label:
|
|
65
|
+
|
|
66
|
+
| what | how it is written |
|
|
67
|
+
|----------------------------------------|-------------------|
|
|
68
|
+
| square brackets, rectangle, bold | #NP, ##NP, ###NP |
|
|
69
|
+
| a feature matrix as a value | #(HEAD\tnoun#), nested to any depth |
|
|
70
|
+
| a whole label that is one matrix | [#(CAT\tS#) [#(CAT\tNP#) Kim] [#(CAT\tVP#) sleeps]] |
|
|
71
|
+
| a region shade behind the subtree | %NP, coloured: %@blue:NP |
|
|
72
|
+
| a colour | @red:NP, @#3af:NP, @#33aaff:NP (3 or 6 hex digits) |
|
|
73
|
+
|
|
74
|
+
An attribute-value matrix is columns plus an enclosure, and a value can be
|
|
75
|
+
another matrix — nested with #( ... #), never with square brackets, which are
|
|
76
|
+
read as tree structure:
|
|
44
77
|
|
|
45
78
|
[#PRED\t'hand⟨SUBJ,OBJ⟩'\
|
|
46
79
|
TENSE\tpast\
|
|
47
80
|
SUBJ\t#(PRED\t'David'#)
|
|
48
81
|
]
|
|
49
82
|
|
|
50
|
-
|
|
83
|
+
Options (the command line spells them --like-this):
|
|
84
|
+
|
|
85
|
+
| option | what it decides |
|
|
86
|
+
|------------|-----------------|
|
|
87
|
+
| format | png, svg, pdf, gif, jpg, tikz, lsif |
|
|
88
|
+
| fontstyle | sans, serif, mono, cjk |
|
|
89
|
+
| fontsize | 6–26 |
|
|
90
|
+
| color | modern, traditional, off, gray |
|
|
91
|
+
| linewidth | every line's weight, 0.5–3.0, as a ratio of the font size |
|
|
92
|
+
| leafstyle | auto, bar, nothing — what joins a node to its leaf |
|
|
93
|
+
| direction | ttb, ltr, btt |
|
|
94
|
+
| mirror | flip the finished layout, for RTL scripts |
|
|
95
|
+
| tidy | off, symmetric, low, medium, high — how tightly subtrees pack |
|
|
96
|
+
| hspacing | horizontal spacing factor, 0.5–3.0 (tidy_spacing is its old name) |
|
|
97
|
+
| vheight | vertical spacing factor, 0.5–5.0 |
|
|
98
|
+
| derivation | one rule across the daughters, categorial-grammar style |
|
|
99
|
+
| hyphen | markup (hyphens underline) or literal (hyphens are hyphens) |
|
|
100
|
+
|
|
101
|
+
The full manual explains each of these with figures, and every example in the
|
|
102
|
+
gallery is written out beside the figure it draws:
|
|
51
103
|
|
|
52
|
-
|
|
104
|
+
https://yohasebe.github.io/rsyntaxtree/documentation
|
|
105
|
+
https://yohasebe.github.io/rsyntaxtree/examples
|
|
@@ -96,9 +96,44 @@ module RSyntaxTree
|
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
+
# Whether the raw space that split this token is what stopped it parsing.
|
|
100
|
+
# Asked of the parser rather than reasoned about: the whole token, with its
|
|
101
|
+
# spaces written as `<>`, either reads as one label or it does not.
|
|
102
|
+
def space_is_the_cause?(token, parent)
|
|
103
|
+
Element.new(-1, parent, token.gsub(" ", WHITESPACE_BLOCK), @level,
|
|
104
|
+
@fontset, @fontsize, @global, true)
|
|
105
|
+
true
|
|
106
|
+
rescue StandardError
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
99
110
|
def parse
|
|
100
111
|
make_tree(0);
|
|
101
112
|
@elist.set_hierarchy
|
|
113
|
+
restore_rule_names_without_a_rule
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# A rule name names the step that produced a node from its daughters. A node
|
|
117
|
+
# with no daughters is the product of no step, so what looked like a name is
|
|
118
|
+
# a column of the label like any other, and it goes back.
|
|
119
|
+
#
|
|
120
|
+
# Whether a node will have daughters is not known where the label is read —
|
|
121
|
+
# they arrive as later tokens — so the label is read first and put right
|
|
122
|
+
# here, once the tree is built. Left alone, turning the option on deleted a
|
|
123
|
+
# column: `[A\tfoo]` drew "A foo" with derivation off and "A" with it on,
|
|
124
|
+
# and said nothing about the difference.
|
|
125
|
+
def restore_rule_names_without_a_rule
|
|
126
|
+
@elist.elements.each_with_index do |e, i|
|
|
127
|
+
next if e.rule_name.nil? || e.rule_name.empty?
|
|
128
|
+
next unless e.children.empty?
|
|
129
|
+
next if e.label_with_rule_name.nil?
|
|
130
|
+
|
|
131
|
+
restored = Element.new(e.id, e.parent, e.label_with_rule_name,
|
|
132
|
+
e.level, @fontset, @fontsize, @global)
|
|
133
|
+
restored.children = e.children
|
|
134
|
+
restored.type = e.type
|
|
135
|
+
@elist.elements[i] = restored
|
|
136
|
+
end
|
|
102
137
|
end
|
|
103
138
|
|
|
104
139
|
def get_elementlist
|
|
@@ -215,7 +250,21 @@ module RSyntaxTree
|
|
|
215
250
|
# parse, the likeliest story is that the space belongs
|
|
216
251
|
# inside the label and cut a construct in two — say so,
|
|
217
252
|
# unless a more specific cause is already known.
|
|
218
|
-
|
|
253
|
+
#
|
|
254
|
+
# Which story is right is not a thing to guess at: ask
|
|
255
|
+
# whether the space is the one that breaks it. Put the whole
|
|
256
|
+
# token back together with the spaces written as the notation
|
|
257
|
+
# writes them, and try again. If it parses, the space was
|
|
258
|
+
# cutting a construct in two and that is what to say. If it
|
|
259
|
+
# fails the same way, the space is a red herring and the cause
|
|
260
|
+
# already named is the one to keep.
|
|
261
|
+
#
|
|
262
|
+
# It used to keep :bare_hyphen and relabel every other cause,
|
|
263
|
+
# so one error said two things: the message naming an unknown
|
|
264
|
+
# colour while the code and the hint talked about spaces. A
|
|
265
|
+
# caller acting on the code — which is what these are for —
|
|
266
|
+
# was sent to fix what was not wrong.
|
|
267
|
+
raise e unless space_is_the_cause?(token_r.join, parent)
|
|
219
268
|
|
|
220
269
|
raise RSTError.new(e.message,
|
|
221
270
|
code: :label_split,
|
|
@@ -92,8 +92,11 @@ module RSyntaxTree
|
|
|
92
92
|
|
|
93
93
|
extra_lines = @extra_lines.join("\n")
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
# Rounded like the stroke widths are: the arithmetic leaves float dust
|
|
96
|
+
# (13.600000000000001) in an attribute of every figure, and the SVG is a
|
|
97
|
+
# deliverable people open in editors.
|
|
98
|
+
as2 = (@global[:h_gap_between_nodes] * 1.0).round(3)
|
|
99
|
+
as4 = (as2 * 3).round(3)
|
|
97
100
|
# The hatch density follows the type size the way the enclosure it
|
|
98
101
|
# fills does: the cell is 10/32 of the font size, the line 4/32 —
|
|
99
102
|
# the ratios the absolute 10 and 4 had at the default size.
|
|
@@ -108,6 +111,9 @@ module RSyntaxTree
|
|
|
108
111
|
<marker id="arrow" markerUnits="userSpaceOnUse" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="#{as2}" markerHeight="#{as2}" orient="auto">
|
|
109
112
|
<path d="M 0 0 L 10 5 L 0 10" fill="#{@col_extra}"/>
|
|
110
113
|
</marker>
|
|
114
|
+
<marker id="arrowStart" markerUnits="userSpaceOnUse" viewBox="0 0 10 10" refX="0" refY="5" markerWidth="#{as2}" markerHeight="#{as2}" orient="auto">
|
|
115
|
+
<path d="M 10 0 L 0 5 L 10 10" fill="#{@col_extra}"/>
|
|
116
|
+
</marker>
|
|
111
117
|
<marker id="arrowBackward" markerUnits="userSpaceOnUse" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="#{as2}" markerHeight="#{as2}" orient="auto">
|
|
112
118
|
<path d="M 0 0 L 10 5 L 0 10 z" fill="#{@col_extra}"/>
|
|
113
119
|
</marker>
|
|
@@ -259,9 +265,83 @@ module RSyntaxTree
|
|
|
259
265
|
b
|
|
260
266
|
end
|
|
261
267
|
|
|
268
|
+
# A point the given distance from `from` along the line towards `to`.
|
|
269
|
+
def along(from, to, distance)
|
|
270
|
+
dx = to[0] - from[0]
|
|
271
|
+
dy = to[1] - from[1]
|
|
272
|
+
length = Math.sqrt((dx * dx) + (dy * dy))
|
|
273
|
+
return from.dup if length.zero?
|
|
274
|
+
|
|
275
|
+
[from[0] + (dx / length * distance), from[1] + (dy / length * distance)]
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# The `d` of a polyline whose corners are eased into quarter turns.
|
|
279
|
+
#
|
|
280
|
+
# The radius is clamped to half of each of the two runs meeting at a corner,
|
|
281
|
+
# so a short leg can never be swallowed by its own turn and two adjacent
|
|
282
|
+
# corners can never overlap — which is the whole of the arithmetic that
|
|
283
|
+
# rounding a right angle needs, and why it can be a constant everywhere
|
|
284
|
+
# else.
|
|
285
|
+
def rounded_polyline_d(points, radius)
|
|
286
|
+
return +"" if points.size < 2
|
|
287
|
+
|
|
288
|
+
at = ->(p) { "#{p[0].round(3)},#{p[1].round(3)}" }
|
|
289
|
+
d = +"M#{at.call(points[0])}"
|
|
290
|
+
points.each_cons(3) do |before, corner, after|
|
|
291
|
+
r = [radius,
|
|
292
|
+
Math.sqrt(((corner[0] - before[0])**2) + ((corner[1] - before[1])**2)) / 2.0,
|
|
293
|
+
Math.sqrt(((after[0] - corner[0])**2) + ((after[1] - corner[1])**2)) / 2.0].min
|
|
294
|
+
if r < 0.01
|
|
295
|
+
d << " L#{at.call(corner)}"
|
|
296
|
+
else
|
|
297
|
+
d << " L#{at.call(along(corner, before, r))}" \
|
|
298
|
+
" Q#{at.call(corner)} #{at.call(along(corner, after, r))}"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
d << " L#{at.call(points.last)}"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# One stroke rather than three lines meeting at right angles. Drawn this way
|
|
305
|
+
# the dash pattern runs round each turn instead of restarting at it, and an
|
|
306
|
+
# arrowhead is the end of the stroke rather than a marker on whichever line
|
|
307
|
+
# happens to finish last.
|
|
308
|
+
def generate_path(points, col, dashed: false, radius: 0, start_arrow: false, end_arrow: false)
|
|
309
|
+
# An arrowhead is drawn back along the run it ends, so the corner before
|
|
310
|
+
# it has to leave that run its full length. Without this the shorter of
|
|
311
|
+
# the two end runs — which is only as long as the path's bulge — lost half
|
|
312
|
+
# of itself to the turn and the head came out sitting on the curve.
|
|
313
|
+
arrow_length = @global[:h_gap_between_nodes]
|
|
314
|
+
leg = ->(a, b) { Math.sqrt(((a[0] - b[0])**2) + ((a[1] - b[1])**2)) }
|
|
315
|
+
radius = [radius, leg.call(points[0], points[1]) - arrow_length].min if start_arrow
|
|
316
|
+
radius = [radius, leg.call(points[-2], points[-1]) - arrow_length].min if end_arrow
|
|
317
|
+
radius = [radius, 0].max
|
|
318
|
+
|
|
319
|
+
dash = @fontsize / 4.0
|
|
320
|
+
dasharray = dashed ? "stroke-dasharray='#{dash} #{dash}' " : ""
|
|
321
|
+
markers = +""
|
|
322
|
+
markers << "marker-start='url(#arrowStart)' " if start_arrow
|
|
323
|
+
markers << "marker-end='url(#arrow)' " if end_arrow
|
|
324
|
+
|
|
325
|
+
# A round cap is half the stroke's width of ink past the point the stroke
|
|
326
|
+
# ends at, and an arrowhead has its tip at exactly that point — so the
|
|
327
|
+
# line came through the tip: 13% ink on the pixel beyond the head of a
|
|
328
|
+
# path, and 81% beyond its tail, where the marker's apex sits on the
|
|
329
|
+
# anchor rather than behind it. Squared off, the stroke stops where the
|
|
330
|
+
# head begins. Only where there is a head: a path without one is the
|
|
331
|
+
# dashed form, and every dash of it wants its round ends.
|
|
332
|
+
linecap = start_arrow || end_arrow ? "butt" : "round"
|
|
333
|
+
|
|
334
|
+
"<path d='#{rounded_polyline_d(points, radius)}' style='fill: none; stroke: #{col}; " \
|
|
335
|
+
"stroke-width:#{@global[:stroke_normal]}; stroke-linecap:#{linecap}; stroke-linejoin:round;' " \
|
|
336
|
+
"#{dasharray}#{markers}/>"
|
|
337
|
+
end
|
|
338
|
+
|
|
262
339
|
def draw_a_path(s_x, s_y, t_x, t_y, target_arrow = :none)
|
|
263
340
|
spacing = @global[:h_gap_between_nodes] * 1.25
|
|
264
341
|
min_bulge = @global[:height_connector_to_text]
|
|
342
|
+
# Small enough that the turn reads as an eased corner rather than as an
|
|
343
|
+
# arc, and taken from a layout measure so it holds at every font size.
|
|
344
|
+
corner_radius = min_bulge / 2.0
|
|
265
345
|
|
|
266
346
|
# Centered offset for multiple lines at the same endpoint
|
|
267
347
|
s_key = "#{s_x.round}"
|
|
@@ -288,20 +368,11 @@ module RSyntaxTree
|
|
|
288
368
|
new_s_y = s_y + s_offset
|
|
289
369
|
new_t_y = t_y + t_offset
|
|
290
370
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
when :double
|
|
297
|
-
@extra_lines << generate_line(new_x, new_s_y, s_x, new_s_y, @col_path, dashed, true)
|
|
298
|
-
@extra_lines << generate_line(new_x, new_s_y, new_x, new_t_y, @col_path, dashed)
|
|
299
|
-
@extra_lines << generate_line(new_x, new_t_y, t_x, new_t_y, @col_path, dashed, true)
|
|
300
|
-
else
|
|
301
|
-
@extra_lines << generate_line(s_x, new_s_y, new_x, new_s_y, @col_path, dashed)
|
|
302
|
-
@extra_lines << generate_line(new_x, new_s_y, new_x, new_t_y, @col_path, dashed)
|
|
303
|
-
@extra_lines << generate_line(new_x, new_t_y, t_x, new_t_y, @col_path, dashed)
|
|
304
|
-
end
|
|
371
|
+
@extra_lines << generate_path([[s_x, new_s_y], [new_x, new_s_y],
|
|
372
|
+
[new_x, new_t_y], [t_x, new_t_y]],
|
|
373
|
+
@col_path, dashed: dashed, radius: corner_radius,
|
|
374
|
+
start_arrow: target_arrow == :double,
|
|
375
|
+
end_arrow: target_arrow != :none)
|
|
305
376
|
@width = new_x if new_x > @width
|
|
306
377
|
else
|
|
307
378
|
# TTB: route BELOW the tree (U shape)
|
|
@@ -315,20 +386,11 @@ module RSyntaxTree
|
|
|
315
386
|
new_s_x = s_x - s_offset
|
|
316
387
|
new_t_x = t_x - t_offset
|
|
317
388
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
when :double
|
|
324
|
-
@extra_lines << generate_line(new_s_x, new_y, new_s_x, s_y, @col_path, dashed, true)
|
|
325
|
-
@extra_lines << generate_line(new_s_x, new_y, new_t_x, new_y, @col_path, dashed)
|
|
326
|
-
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path, dashed, true)
|
|
327
|
-
else
|
|
328
|
-
@extra_lines << generate_line(new_s_x, s_y, new_s_x, new_y, @col_path, dashed)
|
|
329
|
-
@extra_lines << generate_line(new_s_x, new_y, new_t_x, new_y, @col_path, dashed)
|
|
330
|
-
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path, dashed)
|
|
331
|
-
end
|
|
389
|
+
@extra_lines << generate_path([[new_s_x, s_y], [new_s_x, new_y],
|
|
390
|
+
[new_t_x, new_y], [new_t_x, t_y]],
|
|
391
|
+
@col_path, dashed: dashed, radius: corner_radius,
|
|
392
|
+
start_arrow: target_arrow == :double,
|
|
393
|
+
end_arrow: target_arrow != :none)
|
|
332
394
|
@height = new_y if new_y > @height
|
|
333
395
|
end
|
|
334
396
|
end
|
data/lib/rsyntaxtree/utils.rb
CHANGED
|
@@ -28,14 +28,22 @@ SCRIPT_FAMILIES_SANS = ["Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans Devan
|
|
|
28
28
|
# Arabic behind it for systems that ship one but not the other.
|
|
29
29
|
SCRIPT_FAMILIES_SERIF = ["Noto Naskh Arabic", "Noto Sans Arabic", "Noto Serif Hebrew", "Noto Serif Devanagari", "Noto Serif Thai", "Noto Serif Khmer"].freeze
|
|
30
30
|
|
|
31
|
-
#
|
|
32
|
-
# or COLR tables, which Pango happily measures but librsvg does not draw, so a
|
|
33
|
-
# colour font in this list would give a figure whose emoji are blank or blobbed.
|
|
31
|
+
# No emoji family is named in the chains below, and naming one was a mistake.
|
|
34
32
|
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
|
|
33
|
+
# Asking for an emoji face by name is asking fontconfig for it, and fontconfig
|
|
34
|
+
# aliases the name to the generic `emoji` family, whose preference list it then
|
|
35
|
+
# prepends to the whole pattern with a strong binding. The requested face
|
|
36
|
+
# arrives at the head of the chain rather than at the tail, ahead of the Noto
|
|
37
|
+
# text faces — and a colour emoji font carries ASCII digits, for the keycap
|
|
38
|
+
# sequences. So on any machine with one installed, which is every ordinary
|
|
39
|
+
# Linux desktop, `[N 20]` was set in the emoji face at more than twice the
|
|
40
|
+
# width. Measurement and drawing read the same chain, so the figure held
|
|
41
|
+
# together; it was simply not the figure the same input gives anywhere else.
|
|
42
|
+
#
|
|
43
|
+
# Emoji still draw. A codepoint no named family covers reaches fontconfig's own
|
|
44
|
+
# fallback, which finds the emoji font — that is the path this list was
|
|
45
|
+
# duplicating, and it was already the path taken wherever these families were
|
|
46
|
+
# not installed.
|
|
39
47
|
|
|
40
48
|
# Mathematical alphanumerics (U+1D400–) label heads such as the little v of vP.
|
|
41
49
|
# Neither Noto Sans nor Noto Serif covers the block, so without these entries
|
|
@@ -49,15 +57,15 @@ MATH_FAMILIES_SANS = ["Noto Sans Math"].freeze
|
|
|
49
57
|
MATH_FAMILIES_SERIF = ["DejaVu Serif", "Noto Sans Math"].freeze
|
|
50
58
|
|
|
51
59
|
FONT_FAMILIES = {
|
|
52
|
-
sans: (["Noto Sans", "Noto Sans JP", "Noto Sans CJK JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS +
|
|
53
|
-
serif: (["Noto Serif", "Noto Serif JP", "Noto Serif CJK JP"] + SCRIPT_FAMILIES_SERIF + MATH_FAMILIES_SERIF +
|
|
60
|
+
sans: (["Noto Sans", "Noto Sans JP", "Noto Sans CJK JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS + ["sans-serif"]).freeze,
|
|
61
|
+
serif: (["Noto Serif", "Noto Serif JP", "Noto Serif CJK JP"] + SCRIPT_FAMILIES_SERIF + MATH_FAMILIES_SERIF + ["serif"]).freeze,
|
|
54
62
|
# Noto ships no monospaced faces for these scripts, so the mono style borrows
|
|
55
63
|
# the proportional ones rather than dropping to the generic fallback.
|
|
56
|
-
mono: (["Noto Sans Mono", "Noto Sans JP", "Noto Sans Mono CJK JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS +
|
|
64
|
+
mono: (["Noto Sans Mono", "Noto Sans JP", "Noto Sans Mono CJK JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS + ["monospace"]).freeze,
|
|
57
65
|
# The cjk style puts a full-coverage CJK family first, for text that mixes
|
|
58
66
|
# Han, Hangul and kana. Latin falls back to the same Noto faces the sans
|
|
59
67
|
# style uses. (Before 1.8.0 this was WQY Zen Hei.)
|
|
60
|
-
cjk: (["Noto Sans CJK JP", "Noto Sans", "Noto Sans JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS +
|
|
68
|
+
cjk: (["Noto Sans CJK JP", "Noto Sans", "Noto Sans JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS + ["sans-serif"]).freeze
|
|
61
69
|
}.freeze
|
|
62
70
|
|
|
63
71
|
module FontFamily
|
data/lib/rsyntaxtree/version.rb
CHANGED