rsyntaxtree 1.7.0 → 1.8.1
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/.github/workflows/test.yml +35 -0
- data/CHANGELOG.md +76 -0
- data/CITATION.cff +2 -2
- data/Dockerfile +21 -4
- data/README.md +21 -2
- data/bin/rsyntaxtree +29 -20
- data/lib/rsyntaxtree/base_graph.rb +381 -2
- data/lib/rsyntaxtree/element.rb +14 -0
- data/lib/rsyntaxtree/lsif_graph.rb +12 -2
- data/lib/rsyntaxtree/string_parser.rb +1 -4
- data/lib/rsyntaxtree/svg_graph.rb +12 -9
- data/lib/rsyntaxtree/utils.rb +25 -4
- data/lib/rsyntaxtree/version.rb +1 -1
- data/lib/rsyntaxtree.rb +53 -50
- data/rsyntaxtree.gemspec +6 -2
- metadata +3 -266
- data/dev/generate_examples.rb +0 -96
- data/dev/region_shade_proposal.md +0 -66
- data/docs/.gitignore +0 -5
- data/docs/404.html +0 -25
- data/docs/Gemfile +0 -33
- data/docs/_config.yml +0 -77
- data/docs/_examples/000.md +0 -28
- data/docs/_examples/001.md +0 -26
- data/docs/_examples/002.md +0 -35
- data/docs/_examples/003.md +0 -29
- data/docs/_examples/004.md +0 -29
- data/docs/_examples/005.md +0 -26
- data/docs/_examples/006.md +0 -29
- data/docs/_examples/007.md +0 -30
- data/docs/_examples/008.md +0 -32
- data/docs/_examples/009.md +0 -28
- data/docs/_examples/010.md +0 -52
- data/docs/_examples/011.md +0 -41
- data/docs/_examples/012.md +0 -44
- data/docs/_examples/013.md +0 -66
- data/docs/_examples/014.md +0 -51
- data/docs/_examples/015.md +0 -34
- data/docs/_examples/016.md +0 -86
- data/docs/_examples/017.md +0 -26
- data/docs/_examples/018.md +0 -32
- data/docs/_examples/019.md +0 -63
- data/docs/_examples/020.md +0 -43
- data/docs/_examples/021.md +0 -46
- data/docs/_examples/022.md +0 -61
- data/docs/_examples/023.md +0 -26
- data/docs/_examples/024.md +0 -26
- data/docs/_examples/025.md +0 -90
- data/docs/_examples/026.md +0 -30
- data/docs/_examples/027.md +0 -57
- data/docs/_examples/028.md +0 -23
- data/docs/_examples/029.md +0 -59
- data/docs/_examples/030.md +0 -36
- data/docs/_examples/031.md +0 -23
- data/docs/_examples/032.md +0 -30
- data/docs/_examples/033.md +0 -27
- data/docs/_examples/034.md +0 -29
- data/docs/_examples/035.md +0 -37
- data/docs/_examples/036.md +0 -20
- data/docs/_examples/037.md +0 -21
- data/docs/_examples/038.md +0 -33
- data/docs/_examples/039.md +0 -17
- data/docs/_examples/040.md +0 -63
- data/docs/_examples/041.md +0 -35
- data/docs/_examples/042.md +0 -22
- data/docs/_examples/043.md +0 -40
- data/docs/_examples/044.md +0 -16
- data/docs/_examples/045.md +0 -48
- data/docs/_examples/046.md +0 -440
- data/docs/_examples/047.md +0 -21
- data/docs/_examples/048.md +0 -71
- data/docs/_examples/049.md +0 -49
- data/docs/_examples/050.md +0 -96
- data/docs/_examples/051.md +0 -95
- data/docs/_examples/052.md +0 -58
- data/docs/_examples/053.md +0 -34
- data/docs/_examples/054.md +0 -22
- data/docs/_examples/055.md +0 -24
- data/docs/_examples/056.md +0 -25
- data/docs/_examples/057.md +0 -34
- data/docs/_examples/058.md +0 -47
- data/docs/_examples/059.md +0 -54
- data/docs/_examples/060.md +0 -72
- data/docs/_examples/061.md +0 -97
- data/docs/_examples/062.md +0 -96
- data/docs/_examples/063.md +0 -53
- data/docs/_examples/064.md +0 -25
- data/docs/_examples/065.md +0 -22
- data/docs/_includes/box_and_circle_table.html +0 -78
- data/docs/_includes/escape_char_table.html +0 -29
- data/docs/_includes/social_media_links.html +0 -13
- data/docs/_layouts/default.html +0 -43
- data/docs/assets/css/style.scss +0 -31
- data/docs/assets/img/000.png +0 -0
- data/docs/assets/img/001.png +0 -0
- data/docs/assets/img/002.png +0 -0
- data/docs/assets/img/003.png +0 -0
- data/docs/assets/img/004.png +0 -0
- data/docs/assets/img/005.png +0 -0
- data/docs/assets/img/006.png +0 -0
- data/docs/assets/img/007.png +0 -0
- data/docs/assets/img/008.png +0 -0
- data/docs/assets/img/009.png +0 -0
- data/docs/assets/img/010.png +0 -0
- data/docs/assets/img/011.png +0 -0
- data/docs/assets/img/012.png +0 -0
- data/docs/assets/img/013.png +0 -0
- data/docs/assets/img/014.png +0 -0
- data/docs/assets/img/015.png +0 -0
- data/docs/assets/img/016.png +0 -0
- data/docs/assets/img/017.png +0 -0
- data/docs/assets/img/018.png +0 -0
- data/docs/assets/img/019.png +0 -0
- data/docs/assets/img/020.png +0 -0
- data/docs/assets/img/021.png +0 -0
- data/docs/assets/img/022.png +0 -0
- data/docs/assets/img/023.png +0 -0
- data/docs/assets/img/024.png +0 -0
- data/docs/assets/img/025.png +0 -0
- data/docs/assets/img/026.png +0 -0
- data/docs/assets/img/027.png +0 -0
- data/docs/assets/img/028.png +0 -0
- data/docs/assets/img/029.png +0 -0
- data/docs/assets/img/030.png +0 -0
- data/docs/assets/img/031.png +0 -0
- data/docs/assets/img/032.png +0 -0
- data/docs/assets/img/033.png +0 -0
- data/docs/assets/img/034.png +0 -0
- data/docs/assets/img/035.png +0 -0
- data/docs/assets/img/036.png +0 -0
- data/docs/assets/img/037.png +0 -0
- data/docs/assets/img/038.png +0 -0
- data/docs/assets/img/039.png +0 -0
- data/docs/assets/img/040.png +0 -0
- data/docs/assets/img/041.png +0 -0
- data/docs/assets/img/042.png +0 -0
- data/docs/assets/img/043.png +0 -0
- data/docs/assets/img/044.png +0 -0
- data/docs/assets/img/045.png +0 -0
- data/docs/assets/img/046.png +0 -0
- data/docs/assets/img/047.png +0 -0
- data/docs/assets/img/048.png +0 -0
- data/docs/assets/img/049.png +0 -0
- data/docs/assets/img/050.png +0 -0
- data/docs/assets/img/051.png +0 -0
- data/docs/assets/img/052.png +0 -0
- data/docs/assets/img/053.png +0 -0
- data/docs/assets/img/054.png +0 -0
- data/docs/assets/img/055.png +0 -0
- data/docs/assets/img/056.png +0 -0
- data/docs/assets/img/057.png +0 -0
- data/docs/assets/img/058.png +0 -0
- data/docs/assets/img/059.png +0 -0
- data/docs/assets/img/060.png +0 -0
- data/docs/assets/img/061.png +0 -0
- data/docs/assets/img/062.png +0 -0
- data/docs/assets/img/063.png +0 -0
- data/docs/assets/img/064.png +0 -0
- data/docs/assets/img/065.png +0 -0
- data/docs/assets/img/elements/arrow_both.png +0 -0
- data/docs/assets/img/elements/arrow_both_bold.png +0 -0
- data/docs/assets/img/elements/arrow_left.png +0 -0
- data/docs/assets/img/elements/arrow_left_bold.png +0 -0
- data/docs/assets/img/elements/arrow_right.png +0 -0
- data/docs/assets/img/elements/arrow_right_bold.png +0 -0
- data/docs/assets/img/elements/circle.png +0 -0
- data/docs/assets/img/elements/circle_abc.png +0 -0
- data/docs/assets/img/elements/circle_bold.png +0 -0
- data/docs/assets/img/elements/circle_hatched.png +0 -0
- data/docs/assets/img/elements/circle_one.png +0 -0
- data/docs/assets/img/elements/connector.png +0 -0
- data/docs/assets/img/elements/connector_bold.png +0 -0
- data/docs/assets/img/elements/square.png +0 -0
- data/docs/assets/img/elements/square_abc.png +0 -0
- data/docs/assets/img/elements/square_bold.png +0 -0
- data/docs/assets/img/elements/square_hatched.png +0 -0
- data/docs/assets/img/elements/square_one.png +0 -0
- data/docs/assets/img/favicon/apple-touch-icon.png +0 -0
- data/docs/assets/img/favicon/favicon-16x16.png +0 -0
- data/docs/assets/img/favicon/favicon-32x32.png +0 -0
- data/docs/assets/img/favicon/favicon.ico +0 -0
- data/docs/assets/svg/000.svg +0 -74
- data/docs/assets/svg/001.svg +0 -48
- data/docs/assets/svg/002.svg +0 -74
- data/docs/assets/svg/003.svg +0 -63
- data/docs/assets/svg/004.svg +0 -77
- data/docs/assets/svg/005.svg +0 -50
- data/docs/assets/svg/006.svg +0 -56
- data/docs/assets/svg/007.svg +0 -64
- data/docs/assets/svg/008.svg +0 -72
- data/docs/assets/svg/009.svg +0 -70
- data/docs/assets/svg/010.svg +0 -94
- data/docs/assets/svg/011.svg +0 -58
- data/docs/assets/svg/012.svg +0 -64
- data/docs/assets/svg/013.svg +0 -212
- data/docs/assets/svg/014.svg +0 -190
- data/docs/assets/svg/015.svg +0 -69
- data/docs/assets/svg/016.svg +0 -810
- data/docs/assets/svg/017.svg +0 -86
- data/docs/assets/svg/018.svg +0 -75
- data/docs/assets/svg/019.svg +0 -235
- data/docs/assets/svg/020.svg +0 -110
- data/docs/assets/svg/021.svg +0 -95
- data/docs/assets/svg/022.svg +0 -172
- data/docs/assets/svg/023.svg +0 -125
- data/docs/assets/svg/024.svg +0 -56
- data/docs/assets/svg/025.svg +0 -179
- data/docs/assets/svg/026.svg +0 -50
- data/docs/assets/svg/027.svg +0 -101
- data/docs/assets/svg/028.svg +0 -51
- data/docs/assets/svg/029.svg +0 -122
- data/docs/assets/svg/030.svg +0 -76
- data/docs/assets/svg/031.svg +0 -42
- data/docs/assets/svg/032.svg +0 -85
- data/docs/assets/svg/033.svg +0 -81
- data/docs/assets/svg/034.svg +0 -88
- data/docs/assets/svg/035.svg +0 -77
- data/docs/assets/svg/036.svg +0 -47
- data/docs/assets/svg/037.svg +0 -68
- data/docs/assets/svg/038.svg +0 -82
- data/docs/assets/svg/039.svg +0 -30
- data/docs/assets/svg/040.svg +0 -120
- data/docs/assets/svg/041.svg +0 -93
- data/docs/assets/svg/042.svg +0 -42
- data/docs/assets/svg/043.svg +0 -304
- data/docs/assets/svg/044.svg +0 -32
- data/docs/assets/svg/045.svg +0 -107
- data/docs/assets/svg/046.svg +0 -990
- data/docs/assets/svg/047.svg +0 -62
- data/docs/assets/svg/048.svg +0 -134
- data/docs/assets/svg/049.svg +0 -97
- data/docs/assets/svg/050.svg +0 -193
- data/docs/assets/svg/051.svg +0 -179
- data/docs/assets/svg/052.svg +0 -96
- data/docs/assets/svg/053.svg +0 -81
- data/docs/assets/svg/054.svg +0 -51
- data/docs/assets/svg/055.svg +0 -54
- data/docs/assets/svg/056.svg +0 -71
- data/docs/assets/svg/057.svg +0 -91
- data/docs/assets/svg/058.svg +0 -95
- data/docs/assets/svg/059.svg +0 -111
- data/docs/assets/svg/060.svg +0 -134
- data/docs/assets/svg/061.svg +0 -193
- data/docs/assets/svg/062.svg +0 -179
- data/docs/assets/svg/063.svg +0 -94
- data/docs/assets/svg/064.svg +0 -63
- data/docs/assets/svg/065.svg +0 -54
- data/docs/documentation.md +0 -303
- data/docs/documentation_ja.md +0 -313
- data/docs/examples.html +0 -76
- data/docs/index.html +0 -2
- data/fonts/NotoSans-Bold.ttf +0 -0
- data/fonts/NotoSans-BoldItalic.ttf +0 -0
- data/fonts/NotoSans-Italic.ttf +0 -0
- data/fonts/NotoSans-Regular.ttf +0 -0
- data/fonts/NotoSansJP-Bold.otf +0 -0
- data/fonts/NotoSansJP-Regular.otf +0 -0
- data/fonts/NotoSansMath-Regular.ttf +0 -0
- data/fonts/NotoSansMono_SemiCondensed-Bold.ttf +0 -0
- data/fonts/NotoSansMono_SemiCondensed-Regular.ttf +0 -0
- data/fonts/NotoSerif-Bold.ttf +0 -0
- data/fonts/NotoSerif-BoldItalic.ttf +0 -0
- data/fonts/NotoSerif-Italic.ttf +0 -0
- data/fonts/NotoSerif-Regular.ttf +0 -0
- data/fonts/NotoSerifJP-Bold.otf +0 -0
- data/fonts/NotoSerifJP-Regular.otf +0 -0
- data/fonts/OpenMoji-black-glyf.ttf +0 -0
- data/fonts/wqy-zenhei.ttf +0 -0
- data/img/rsyntaxtree-web-screenshot.png +0 -0
- data/img/rsyntaxtree.png +0 -0
- data/img/sample.png +0 -0
- data/img/sample.svg +0 -38
- data/test/cli_test.rb +0 -262
- data/test/example_verify_test.rb +0 -79
- data/test/format_converter_test.rb +0 -129
- data/test/lsif_test.rb +0 -304
- data/test/markup_parser_test.rb +0 -260
- data/test/node_styling_test.rb +0 -377
- data/test/overlap_test.rb +0 -122
- data/test/tikz_test.rb +0 -147
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b9513e8d667af90de0a12e427eae11d0089f6a969ed1a47714ca30401001821
|
|
4
|
+
data.tar.gz: '0458b65b7f9ad1f495d344963b41b801b7c26562b0256abe48885756a1481bef'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1738b00df2a2dbe5cafba5d52b19c44149472912c2859c8183384582770be0b54c0c4e96688fe4c0a808969c96096504956b7ce7dffc76bd3c682ee28124cf6
|
|
7
|
+
data.tar.gz: a8363b3b3e6b11f345603f020e36766275f740ab0f80af4ecb4be3f9160dd7860d4a26c4cdd2788a8b02e27d4c99d1147bd9843200cdd41cf48f87b6ce373e34
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
ruby-version: ["3.2", "3.4"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install system dependencies
|
|
20
|
+
run: |
|
|
21
|
+
sudo apt-get update
|
|
22
|
+
sudo apt-get install -y \
|
|
23
|
+
libpango1.0-dev librsvg2-dev libmagickwand-dev \
|
|
24
|
+
libgirepository1.0-dev gobject-introspection \
|
|
25
|
+
fonts-noto-core fonts-noto-cjk
|
|
26
|
+
sudo fc-cache -f
|
|
27
|
+
|
|
28
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
29
|
+
uses: ruby/setup-ruby@v1
|
|
30
|
+
with:
|
|
31
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
32
|
+
bundler-cache: true
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.8.1] - 2026-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Arabic rendered as isolated, unjoined letterforms in some environments.
|
|
7
|
+
Scripts outside Latin and CJK were left to the system's generic font
|
|
8
|
+
fallback, which is not the same font on every machine: on Alpine the Arabic
|
|
9
|
+
block was claimed by Noto Sans Math, which has the glyphs but no joining
|
|
10
|
+
rules, while on Debian the same text fell to DejaVu Sans. The family chains
|
|
11
|
+
now name the scripts the gallery covers — Arabic (`Noto Sans Arabic`, with
|
|
12
|
+
`Noto Naskh Arabic` for the serif style), Hebrew, Devanagari, Thai and
|
|
13
|
+
Khmer — so the same input renders the same way everywhere. Gallery example
|
|
14
|
+
067 (Arabic) was affected and has been regenerated.
|
|
15
|
+
- Emoji were measured with one font and drawn with another where a colour
|
|
16
|
+
emoji font was installed: Pango selects `Noto Color Emoji`, but the
|
|
17
|
+
librsvg/Cairo pipeline does not rasterise its bitmap glyphs, so the drawing
|
|
18
|
+
fell back to whatever outline font happened to cover the codepoint. The
|
|
19
|
+
project's Docker images now install the monochrome Noto Emoji and leave the
|
|
20
|
+
colour build out.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- The Docker images install the Noto packages for Arabic (including Naskh),
|
|
24
|
+
Hebrew, Devanagari, Thai and Khmer, and carry a fontconfig rule that removes
|
|
25
|
+
the Arabic ranges from Noto Sans Math while keeping its mathematical
|
|
26
|
+
alphanumerics (used for the little *v* of *v*P).
|
|
27
|
+
- Documentation records how to override any of the named families with a
|
|
28
|
+
fontconfig alias, for users who prefer their own script fonts.
|
|
29
|
+
|
|
30
|
+
## [1.8.0] - 2026-08
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- `tidy` layout scale, one option covering every layout mode from the most
|
|
34
|
+
spacious to the most dense: `symmetric` (radical symmetrization) | `off` |
|
|
35
|
+
`low` (contour packing, strict leaf positions) | `medium` (packing with
|
|
36
|
+
cross-row tucking that never lets two leaves swap their left-right order) |
|
|
37
|
+
`high` (free tucking; leaf order kept per row only). Connector heights
|
|
38
|
+
adjust automatically: a small height budget (5% of the tree's height) is
|
|
39
|
+
spent on the levels whose branches spread widest, evening out branch
|
|
40
|
+
angles. Tidy never produces overlapping labels (collisions roll back).
|
|
41
|
+
- `mirror` option: flips the finished layout horizontally for the
|
|
42
|
+
right-to-left tree convention of Arabic/Hebrew syntax (composes with
|
|
43
|
+
`direction`).
|
|
44
|
+
- `hspacing` option: scales every horizontal gap in every layout mode — the
|
|
45
|
+
horizontal counterpart of `vheight`/connector height.
|
|
46
|
+
- Pass-through empty nodes: a node labeled only `<>` renders as an invisible
|
|
47
|
+
joint with the connector running continuously through it, so a `<>` chain
|
|
48
|
+
aligns a shallow leaf with deeper leaves without a broken line.
|
|
49
|
+
- Full CJK coverage in every font style: the family chains fall back to
|
|
50
|
+
Noto Sans/Serif/Mono CJK, so Hangul and simplified/traditional Han render
|
|
51
|
+
in all styles (the standalone Noto JP faces carry no Hangul).
|
|
52
|
+
- Example gallery: Multilingual category (the same UD-PUD sentence in nine
|
|
53
|
+
languages, constituency derived mechanically from the dependency
|
|
54
|
+
annotation; the Arabic example demonstrates `mirror`), a Morphology
|
|
55
|
+
category, and per-figure tidy settings across the whole gallery.
|
|
56
|
+
- CI on GitHub Actions (Ruby 3.2/3.4).
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
- LSIF output records the layout settings a reader cannot recover from the
|
|
60
|
+
coordinates: `geometry.mirror`, and `tidy`, `mirror`, `direction` and
|
|
61
|
+
`horizontal_spacing` under `meta.source.params`.
|
|
62
|
+
- The standalone `symmetrize` option and `-y` flag are deprecated aliases of
|
|
63
|
+
`tidy: symmetric`; `tidy_spacing` is a deprecated alias of `hspacing`.
|
|
64
|
+
- `--direction` now owns the `-d` short flag (it had been auto-assigned to
|
|
65
|
+
`--hide-default-connectors`, so the documented `-d ltr` silently did
|
|
66
|
+
nothing). Every published short flag is now declared explicitly rather
|
|
67
|
+
than derived from the option set. One undocumented auto-assignment moved
|
|
68
|
+
as a result: `-e` was `--direction` in 1.7.0 and is `--version` here.
|
|
69
|
+
- The gem no longer bundles font files (38MB that were never opened at
|
|
70
|
+
runtime since the Pango migration): fonts resolve by family name through
|
|
71
|
+
fontconfig. See README for the system font packages. The dead `--font`
|
|
72
|
+
CLI option (its value was never read) is gone.
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
- Boolean options left at their defaults are no longer misread as enabled
|
|
76
|
+
when the caller passes only a partial parameter set (e.g. `tidy: off`
|
|
77
|
+
rendered as `symmetric` in the web UI).
|
|
78
|
+
|
|
3
79
|
## [1.7.0] - 2026-08
|
|
4
80
|
|
|
5
81
|
### Changed
|
data/CITATION.cff
CHANGED
|
@@ -11,8 +11,8 @@ repository-code: "https://github.com/yohasebe/rsyntaxtree"
|
|
|
11
11
|
# Concept DOI: always resolves to the latest archived version on Zenodo
|
|
12
12
|
doi: "10.5281/zenodo.21916150"
|
|
13
13
|
license: MIT
|
|
14
|
-
version: 1.
|
|
15
|
-
date-released:
|
|
14
|
+
version: 1.8.1
|
|
15
|
+
date-released: 2026-08-15
|
|
16
16
|
keywords:
|
|
17
17
|
- linguistics
|
|
18
18
|
- syntax tree
|
data/Dockerfile
CHANGED
|
@@ -7,16 +7,33 @@ RUN apk update && \
|
|
|
7
7
|
apk add --no-cache linux-headers libxml2-dev make gcc libc-dev bash && \
|
|
8
8
|
apk add --no-cache librsvg librsvg-dev pango-dev imagemagick imagemagick-dev xz-dev libbz2 && \
|
|
9
9
|
apk add --no-cache gobject-introspection gobject-introspection-dev && \
|
|
10
|
-
apk add --no-cache -t .build-packages --no-cache build-base curl-dev wget gcompat
|
|
10
|
+
apk add --no-cache -t .build-packages --no-cache build-base curl-dev wget gcompat && \
|
|
11
|
+
apk add --no-cache font-noto font-noto-cjk font-noto-cjk-extra \
|
|
12
|
+
font-noto-arabic font-noto-naskh-arabic font-noto-hebrew \
|
|
13
|
+
font-noto-devanagari font-noto-thai font-noto-khmer
|
|
14
|
+
|
|
15
|
+
# Noto Sans Math (pulled in by font-noto) claims the Arabic block but carries no
|
|
16
|
+
# joining rules, and fontconfig ranks it above Noto Sans Arabic — Arabic then
|
|
17
|
+
# renders as isolated letterforms. Keep the font, since trees use its
|
|
18
|
+
# mathematical alphanumerics (the little v of vP), but drop Arabic from it.
|
|
19
|
+
COPY dev/fontconfig/99-noto-math-no-arabic.conf /etc/fonts/conf.d/99-noto-math-no-arabic.conf
|
|
20
|
+
|
|
21
|
+
# Emoji: the monochrome Noto Emoji, not Alpine's font-noto-emoji (which is the
|
|
22
|
+
# colour NotoColorEmoji). Cairo does not rasterise its bitmap glyphs in this
|
|
23
|
+
# pipeline, so emoji would silently fall back to whatever outline font happens
|
|
24
|
+
# to cover them. Pinned to a google/fonts commit so the gallery stays stable.
|
|
25
|
+
ARG NOTO_EMOJI_SHA=b979dba422e445492b0eb9951ac52ee0b4d648c3
|
|
26
|
+
ARG NOTO_EMOJI_SHA256=de6c18832938afc99caf132b39d6a30a19bac7f2e812e28db2535b4608d27551
|
|
27
|
+
RUN mkdir -p /usr/share/fonts/noto-emoji && \
|
|
28
|
+
wget -q -O /usr/share/fonts/noto-emoji/NotoEmoji-Regular.ttf \
|
|
29
|
+
"https://raw.githubusercontent.com/google/fonts/${NOTO_EMOJI_SHA}/ofl/notoemoji/NotoEmoji%5Bwght%5D.ttf" && \
|
|
30
|
+
echo "${NOTO_EMOJI_SHA256} /usr/share/fonts/noto-emoji/NotoEmoji-Regular.ttf" | sha256sum -c -
|
|
11
31
|
|
|
12
32
|
|
|
13
33
|
ADD Gemfile $WORKSPACE
|
|
14
34
|
ADD rsyntaxtree.gemspec $WORKSPACE
|
|
15
35
|
RUN bundle install -j4
|
|
16
36
|
|
|
17
|
-
ADD fonts $WORKSPACE
|
|
18
|
-
RUN mkdir -p /usr/share/fonts/yh
|
|
19
|
-
COPY ./fonts/* /usr/share/fonts/yh/
|
|
20
37
|
RUN fc-cache -fv
|
|
21
38
|
|
|
22
39
|
ADD . $WORKSPACE
|
data/README.md
CHANGED
|
@@ -110,6 +110,23 @@ See [RSyntaxTree Example Gallery](https://yohasebe.github.io/rsyntaxtree/example
|
|
|
110
110
|
|
|
111
111
|
<img src='https://github.com/yohasebe/rsyntaxtree/blob/master/img/sample.png?raw=true' width='600' />
|
|
112
112
|
|
|
113
|
+
## System Fonts
|
|
114
|
+
|
|
115
|
+
RSyntaxTree resolves fonts by family name through fontconfig (measurement via Pango, rendering via librsvg), so the fonts must be installed on the system that generates the images. Recommended:
|
|
116
|
+
|
|
117
|
+
- Debian/Ubuntu: `apt install fonts-noto-core fonts-noto-cjk fonts-noto-color-emoji`
|
|
118
|
+
- Alpine: `apk add font-noto font-noto-cjk font-noto-cjk-extra font-noto-emoji` (the `-extra` package carries Noto Serif CJK)
|
|
119
|
+
- macOS: install [Noto Sans/Serif](https://fonts.google.com/noto) (and Noto Sans/Serif JP for CJK); otherwise the system falls back to available fonts
|
|
120
|
+
|
|
121
|
+
Any script supported by an installed font renders correctly — the family chains fall back from Noto Sans/Serif to the JP and CJK variants, so Japanese, Chinese (simplified and traditional) and Korean are covered by the Noto CJK package. As of v1.8.0 the gem no longer bundles font files (they were not used at runtime).
|
|
122
|
+
|
|
123
|
+
Scripts outside Latin and CJK need their own Noto package, or they render as tofu boxes. Since v1.8.0 the gem names these families explicitly (`Noto Sans Arabic`, `Noto Naskh Arabic`, `Noto Sans/Serif Hebrew`, `Devanagari`, `Thai`, `Khmer`) instead of leaving them to the system fallback, so install them to get the intended shapes:
|
|
124
|
+
|
|
125
|
+
- Debian/Ubuntu: `apt install fonts-noto-core` already covers them; on minimal images add `fonts-noto` for the full set
|
|
126
|
+
- Alpine: `apk add font-noto-arabic font-noto-naskh-arabic font-noto-hebrew font-noto-devanagari font-noto-thai font-noto-khmer`
|
|
127
|
+
|
|
128
|
+
Emoji need the **monochrome** [Noto Emoji](https://fonts.google.com/noto/specimen/Noto+Emoji); colour emoji fonts (`NotoColorEmoji`, shipped by Alpine's `font-noto-emoji` and Debian's `fonts-noto-color-emoji`) are measured but not drawn by the librsvg/Cairo pipeline, so emoji then fall back to whatever outline font happens to cover them.
|
|
129
|
+
|
|
113
130
|
## Installation
|
|
114
131
|
|
|
115
132
|
```bash
|
|
@@ -156,9 +173,11 @@ Usage:
|
|
|
156
173
|
| `-n, --fontstyle` | Font style: sans, serif, cjk, mono | `sans` |
|
|
157
174
|
| `-s, --fontsize` | Font size: 8-26 | `16` |
|
|
158
175
|
| `-c, --color` | Color mode: modern, traditional, off | `modern` |
|
|
159
|
-
| `-y, --symmetrize` | Symmetrical tree: on, off | `off` |
|
|
160
176
|
| `-p, --polyline` | Polyline connectors: on, off | `off` |
|
|
161
177
|
| `-d, --direction` | Tree layout direction: ttb, ltr | `ttb` |
|
|
178
|
+
| `--tidy` | Layout scale: off, symmetric, low, medium, high | `off` |
|
|
179
|
+
| `--hspacing` | Horizontal spacing factor, all layout modes (0.5-3.0) | `1.0` |
|
|
180
|
+
| `-m, --mirror` | Flip the tree horizontally (RTL convention): on, off | `off` |
|
|
162
181
|
|
|
163
182
|
Run `rsyntaxtree -h` for the full list of options.
|
|
164
183
|
|
|
@@ -201,7 +220,7 @@ If you use RSyntaxTree in your research, please cite it. You can use the "Cite t
|
|
|
201
220
|
title = {RSyntaxTree: A graphical syntax tree image generator},
|
|
202
221
|
url = {https://yohasebe.com/rsyntaxtree},
|
|
203
222
|
doi = {10.5281/zenodo.21916150},
|
|
204
|
-
version = {1.
|
|
223
|
+
version = {1.8.0},
|
|
205
224
|
year = {2026}
|
|
206
225
|
}
|
|
207
226
|
```
|
data/bin/rsyntaxtree
CHANGED
|
@@ -15,7 +15,6 @@ CONFIG_VALIDATORS = {
|
|
|
15
15
|
format: ->(v) { /\A(png|jpg|gif|pdf|svg|lsif)\z/ =~ v.to_s ? nil : "must be png, jpg, gif, pdf, svg, or lsif" },
|
|
16
16
|
leafstyle: ->(v) { /\A(auto|triangle|bar|nothing)\z/ =~ v.to_s ? nil : "must be auto, triangle, bar, or nothing" },
|
|
17
17
|
fontstyle: ->(v) { /\A(sans|serif|cjk|mono)\z/ =~ v.to_s ? nil : "must be sans, serif, cjk, or mono" },
|
|
18
|
-
font: ->(v) { v.nil? || File.exist?(v) ? nil : "must be path to an existing ttf font" },
|
|
19
18
|
fontsize: ->(v) { v.is_a?(Integer) && v >= 8 && v <= 26 ? nil : "must be in the range of 8-26" },
|
|
20
19
|
linewidth: ->(v) { v.is_a?(Integer) && v >= 1 && v <= 5 ? nil : "must be in the range of 1-5" },
|
|
21
20
|
vheight: ->(v) { v.is_a?(Numeric) && v >= 0.5 && v <= 5.0 ? nil : "must be in the range of 0.5-5.0" },
|
|
@@ -24,6 +23,10 @@ CONFIG_VALIDATORS = {
|
|
|
24
23
|
transparent: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
25
24
|
polyline: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
26
25
|
hide_default_connectors: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
26
|
+
mirror: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
27
|
+
tidy: ->(v) { /\A(off|symmetric|low|medium|high|on|compact|true|false)\z/ =~ v.to_s ? nil : "must be off, symmetric, low, medium, or high" },
|
|
28
|
+
hspacing: ->(v) { v.is_a?(Numeric) && v >= 0.5 && v <= 3.0 ? nil : "must be in the range of 0.5-3.0" },
|
|
29
|
+
tidy_spacing: ->(v) { v.is_a?(Numeric) && v >= 0.5 && v <= 3.0 ? nil : "must be in the range of 0.5-3.0" },
|
|
27
30
|
direction: ->(v) { /\A(ttb|ltr)\z/ =~ v.to_s ? nil : "must be ttb or ltr" }
|
|
28
31
|
}.freeze
|
|
29
32
|
|
|
@@ -84,7 +87,7 @@ unless loaded[:errors].empty?
|
|
|
84
87
|
end
|
|
85
88
|
|
|
86
89
|
opts = Optimist.options do
|
|
87
|
-
version "RSyntaxTree #{RSyntaxTree::VERSION} (c)
|
|
90
|
+
version "RSyntaxTree #{RSyntaxTree::VERSION} (c) 2026 Yoichiro Hasebe"
|
|
88
91
|
banner <<~BANNER
|
|
89
92
|
RSyntaxTree, (linguistic) syntax tree generator written in Ruby.
|
|
90
93
|
|
|
@@ -96,21 +99,24 @@ opts = Optimist.options do
|
|
|
96
99
|
where [options] are:
|
|
97
100
|
BANNER
|
|
98
101
|
|
|
99
|
-
opt :outdir, "Output directory", default: "./"
|
|
100
|
-
opt :outfilename, "Output file base name", default: "syntree"
|
|
101
|
-
opt :format, "Output format: png, jpg, gif, pdf, svg, or lsif", default: "png"
|
|
102
|
-
opt :leafstyle, "visual style of tree leaves: auto, triangle, bar, or nothing", default: "auto"
|
|
103
|
-
opt :fontstyle, "Font style
|
|
104
|
-
opt :
|
|
105
|
-
opt :
|
|
106
|
-
opt :
|
|
107
|
-
opt :
|
|
108
|
-
opt :
|
|
109
|
-
opt :
|
|
110
|
-
opt :
|
|
111
|
-
opt :
|
|
112
|
-
opt :
|
|
113
|
-
opt :
|
|
102
|
+
opt :outdir, "Output directory", default: "./", short: :o
|
|
103
|
+
opt :outfilename, "Output file base name", default: "syntree", short: :u
|
|
104
|
+
opt :format, "Output format: png, jpg, gif, pdf, svg, or lsif", default: "png", short: :f
|
|
105
|
+
opt :leafstyle, "visual style of tree leaves: auto, triangle, bar, or nothing", default: "auto", short: :l
|
|
106
|
+
opt :fontstyle, "Font style: sans, serif, cjk, mono", default: "sans", short: :n
|
|
107
|
+
opt :fontsize, "Size: 8-26", default: 16, short: :s
|
|
108
|
+
opt :linewidth, "Size: 1-5", default: 1, short: :i
|
|
109
|
+
opt :vheight, "Connector Height: 0.5-5.0", default: 2.0, short: :v
|
|
110
|
+
opt :color, "Color text and bars: modern, traditional, or off", default: "modern", short: :c
|
|
111
|
+
opt :symmetrize, "DEPRECATED alias of --tidy symmetric: on or off", default: "off", short: :y
|
|
112
|
+
opt :transparent, "Make background transparent: on or off", default: "off", short: :r
|
|
113
|
+
opt :polyline, "draw polyline connectors: on or off", default: "off", short: :p
|
|
114
|
+
opt :hide_default_connectors, "make default connectors transparent: on or off", default: "off", short: :none
|
|
115
|
+
opt :mirror, "flip the tree horizontally (RTL linguistics convention: first word at the right edge): on or off", default: "off"
|
|
116
|
+
opt :tidy, "tidy tree layout: off, symmetric (radical symmetrization), low (packed, strict leaf positions), medium (packed; leaves may overlap but never swap order), or high (packed; leaf order kept per row only)", default: "off", short: :none
|
|
117
|
+
opt :hspacing, "Horizontal spacing factor (counterpart of vheight): 0.5-3.0", default: 1.0, short: :none
|
|
118
|
+
opt :tidy_spacing, "DEPRECATED alias of --hspacing: 0.5-3.0", default: 1.0, short: :none
|
|
119
|
+
opt :direction, "Tree layout direction: ttb (top-to-bottom) or ltr (left-to-right)", default: "ttb", short: :d
|
|
114
120
|
opt :help, "This is a custom help message", short: :h
|
|
115
121
|
end
|
|
116
122
|
|
|
@@ -119,7 +125,6 @@ Optimist.die :direction, "must be ttb or ltr" unless /\A(ttb|ltr)\z/ =~ opts[:di
|
|
|
119
125
|
Optimist.die :format, "must be png, pdf, svg, or lsif" unless /\A(png|jpg|gif|pdf|svg|lsif)\z/ =~ opts[:format]
|
|
120
126
|
Optimist.die :leafstyle, "must be auto, triangle, bar, or nothing" unless /\A(auto|triangle|bar|nothing)\z/ =~ opts[:leafstyle]
|
|
121
127
|
Optimist.die :fontstyle, "must be sans, serif, cjk, or mono" unless /\A(sans|serif|cjk|mono)\z/ =~ opts[:fontstyle]
|
|
122
|
-
Optimist.die :font, "must be path to an existing ttf font" if opts[:font] && !File.exist?(opts[:font])
|
|
123
128
|
Optimist.die :fontsize, "must be in the range of 8-26" unless opts[:fontsize] >= 8 && opts[:fontsize] <= 26
|
|
124
129
|
Optimist.die :linewidth, "must be in the range of 1-5" unless opts[:linewidth] >= 1 && opts[:linewidth] <= 5
|
|
125
130
|
Optimist.die :color, "must be either modern, traditional, or off" unless /\A(modern|traditional|on|off|true|false)\z/ =~ opts[:color]
|
|
@@ -128,6 +133,10 @@ Optimist.die :vheight, "must be in the range of 0.5-5.0" if opts[:vheight] < 0.5
|
|
|
128
133
|
Optimist.die :transparent, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:transparent]
|
|
129
134
|
Optimist.die :polyline, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:polyline]
|
|
130
135
|
Optimist.die :hide_default_connectors, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:hide_default_connectors]
|
|
136
|
+
Optimist.die :mirror, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:mirror]
|
|
137
|
+
Optimist.die :tidy, "must be off, symmetric, low, medium, or high" unless /\A(off|symmetric|low|medium|high|on|compact|true|false)\z/ =~ opts[:tidy]
|
|
138
|
+
Optimist.die :hspacing, "must be in the range of 0.5-3.0" if opts[:hspacing] < 0.5 || opts[:hspacing] > 3.0
|
|
139
|
+
Optimist.die :tidy_spacing, "must be in the range of 0.5-3.0" if opts[:tidy_spacing] < 0.5 || opts[:tidy_spacing] > 3.0
|
|
131
140
|
|
|
132
141
|
# Merge config file options with CLI options (CLI takes precedence)
|
|
133
142
|
string_opts = {}
|
|
@@ -140,8 +149,8 @@ end
|
|
|
140
149
|
# Then, apply CLI options (overrides config file)
|
|
141
150
|
opts.each do |key, value|
|
|
142
151
|
key_sym = key.to_sym
|
|
143
|
-
# Skip internal optimist keys
|
|
144
|
-
next if key.to_s.start_with?('_')
|
|
152
|
+
# Skip internal optimist keys
|
|
153
|
+
next if key.to_s.start_with?('_')
|
|
145
154
|
# Only override if explicitly given on CLI or not in config
|
|
146
155
|
if opts[:"#{key}_given"] || !file_config.key?(key_sym)
|
|
147
156
|
string_opts[key_sym] = value
|