rsyntaxtree 1.8.0 → 1.8.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27e1c2eb4e8e276a63ab92e99b32bd3ef607451df95088f379ff093f6a206001
4
- data.tar.gz: 5147922ca1269a629761d7b93f364f9ae9d22fc41d63ce28fdd4c75223cccc52
3
+ metadata.gz: 232efc991d608eb2af9ff19d6816d9f22f9bc4a1006506276507c1b8705f34ec
4
+ data.tar.gz: da8e1ab08b470d76d47809b89ae4fd5b7bf0ef7519847ef3bc7e2955814f5521
5
5
  SHA512:
6
- metadata.gz: 36edfb6922b94b9e8c0821e72f9154b58ad58fc43bd7f55ae913807534f39acd9dbe8becd00e9f14c30c999bb90cc15b6d7ba9cf38e75a6e1feaeca9c090edb9
7
- data.tar.gz: 58f60c25f6c8ab2566ff1755c74c353769ea80a970e020f3e2c656bb2b6ee7d8e75fe82831b5d10c3788f8539d601a6817a257c5f57006d492ca11526c30e77b
6
+ metadata.gz: 5af80d2af48070f3733c21f19cb543da6a2f443b4e4e2fb4fa2f5a0760584cedcfac5e3b86067623f5f24ba63cfe3267e69692f5ddbc44b3c9fdf35f1de34ace
7
+ data.tar.gz: 3d391279ae3b669f87f6c62cfa6b2f44cb607a6f5c9d0adbfd37e147cd9a9d2a8c4f998a3e508c423886a4adfe56610e5041c838ffcd40d47f958e96b45d9d49
data/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.2] - 2026-08
4
+
5
+ ### Changed
6
+ - `tidy: high` now compresses as far as its name promises. The level-balance
7
+ floor added in 1.8.0, which keeps a pair of siblings from being tucked
8
+ narrower than the level below it, applied to `medium` and `high` alike and
9
+ bound first in nearly every tree: across the 75 gallery examples the two
10
+ modes produced identical figures 70 times. The floor now applies to `medium`
11
+ only, leaving `high` free to trade even branch angles for width — 29 of the
12
+ 75 examples now differ, by up to 19%. `off`, `low` and `medium` are
13
+ unchanged; `high`, like `symmetric` at the other end of the scale, is for
14
+ figures that ask for it.
15
+ - Mathematical alphanumerics (U+1D400–, such as the little *v* of *v*P) are
16
+ named in the family chains. Neither Noto Sans nor Noto Serif covers the
17
+ block, so the glyphs came from whatever the machine offered: Noto Sans Math
18
+ on Alpine, DejaVu Serif on Debian/Ubuntu, STIX Two Math on macOS. The serif
19
+ style now asks for a serif source first, so a serif tree no longer shows a
20
+ sans *v*. The Docker images install `font-dejavu` for it.
21
+
22
+ ### Fixed
23
+ - The CLI printed parse errors to stdout and exited 0, so a script generating
24
+ figures in bulk could not tell a rejected input from a drawn one. Errors go
25
+ to stderr and the exit status is 1.
26
+ - Dropped `Noto Sans Mono SemiCondensed` from the mono chain. It is a width
27
+ style of the variable Noto Sans Mono rather than a family of its own, and
28
+ resolved nowhere on macOS, Debian/Ubuntu or Alpine; the chain fell through
29
+ to `Noto Sans Mono`, which it now names directly.
30
+
31
+ ## [1.8.1] - 2026-08
32
+
33
+ ### Fixed
34
+ - Arabic rendered as isolated, unjoined letterforms in some environments.
35
+ Scripts outside Latin and CJK were left to the system's generic font
36
+ fallback, which is not the same font on every machine: on Alpine the Arabic
37
+ block was claimed by Noto Sans Math, which has the glyphs but no joining
38
+ rules, while on Debian/Ubuntu the same text fell to DejaVu Sans. The family
39
+ chains now name the scripts the gallery covers — Arabic (`Noto Sans Arabic`,
40
+ with `Noto Naskh Arabic` for the serif style), Hebrew, Devanagari, Thai and
41
+ Khmer — so machines that have those fonts installed produce the same shapes.
42
+ Mathematical alphanumerics (U+1D400–) are not named yet and still vary by
43
+ environment. Gallery example 067 (Arabic) was affected and has been
44
+ regenerated.
45
+ - Emoji were measured with one font and drawn with another where a colour
46
+ emoji font was installed: Pango selects `Noto Color Emoji`, but the
47
+ librsvg/Cairo pipeline does not rasterise its bitmap glyphs, so the drawing
48
+ fell back to whatever outline font happened to cover the codepoint. The
49
+ project's Docker images now install the monochrome Noto Emoji and leave the
50
+ colour build out.
51
+
52
+ ### Changed
53
+ - The Docker images install the Noto packages for Arabic (including Naskh),
54
+ Hebrew, Devanagari, Thai and Khmer, and carry a fontconfig rule that removes
55
+ the Arabic ranges from Noto Sans Math while keeping its mathematical
56
+ alphanumerics (used for the little *v* of *v*P).
57
+ - Documentation records how to override any of the named families with a
58
+ fontconfig alias, for users who prefer their own script fonts.
59
+
3
60
  ## [1.8.0] - 2026-08
4
61
 
5
62
  ### Added
data/CITATION.cff CHANGED
@@ -11,7 +11,7 @@ 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.8.0
14
+ version: 1.8.2
15
15
  date-released: 2026-08-15
16
16
  keywords:
17
17
  - linguistics
data/Dockerfile CHANGED
@@ -8,8 +8,27 @@ RUN apk update && \
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
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 font-noto-emoji \
12
- font-noto-arabic font-noto-devanagari font-noto-thai
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
+ font-dejavu
15
+
16
+ # Noto Sans Math (pulled in by font-noto) claims the Arabic block but carries no
17
+ # joining rules, and fontconfig ranks it above Noto Sans Arabic — Arabic then
18
+ # renders as isolated letterforms. Keep the font, since trees use its
19
+ # mathematical alphanumerics (the little v of vP), but drop Arabic from it.
20
+ COPY dev/fontconfig/99-noto-math-no-arabic.conf /etc/fonts/conf.d/99-noto-math-no-arabic.conf
21
+
22
+ # Emoji: the monochrome Noto Emoji, not Alpine's font-noto-emoji (which is the
23
+ # colour NotoColorEmoji). Cairo does not rasterise its bitmap glyphs in this
24
+ # pipeline, so emoji would silently fall back to whatever outline font happens
25
+ # to cover them. Pinned to a google/fonts commit so the gallery stays stable.
26
+ ARG NOTO_EMOJI_SHA=b979dba422e445492b0eb9951ac52ee0b4d648c3
27
+ ARG NOTO_EMOJI_SHA256=de6c18832938afc99caf132b39d6a30a19bac7f2e812e28db2535b4608d27551
28
+ RUN mkdir -p /usr/share/fonts/noto-emoji && \
29
+ wget -q -O /usr/share/fonts/noto-emoji/NotoEmoji-Regular.ttf \
30
+ "https://raw.githubusercontent.com/google/fonts/${NOTO_EMOJI_SHA}/ofl/notoemoji/NotoEmoji%5Bwght%5D.ttf" && \
31
+ echo "${NOTO_EMOJI_SHA256} /usr/share/fonts/noto-emoji/NotoEmoji-Regular.ttf" | sha256sum -c -
13
32
 
14
33
 
15
34
  ADD Gemfile $WORKSPACE
data/README.md CHANGED
@@ -112,13 +112,19 @@ See [RSyntaxTree Example Gallery](https://yohasebe.github.io/rsyntaxtree/example
112
112
 
113
113
  ## System Fonts
114
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:
115
+ RSyntaxTree resolves fonts by family name through fontconfig (measurement via Pango, rendering via librsvg), so the fonts must be installed on the machine that generates the images. As of v1.8.0 the gem no longer bundles font files; they were not used at runtime.
116
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
117
+ - Debian/Ubuntu: `apt install fonts-noto-core fonts-noto-cjk`
118
+ - Alpine: `apk add font-noto font-noto-cjk font-noto-cjk-extra` (the `-extra` package carries Noto Serif CJK)
119
+ - macOS: `brew install --cask font-noto-sans font-noto-serif font-noto-sans-mono font-noto-sans-jp font-noto-serif-jp font-noto-sans-cjk font-noto-serif-cjk font-noto-sans-mono-cjk-jp` (macOS itself ships Noto only for a few rare scripts)
120
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).
121
+ Arabic, Hebrew, Devanagari, Thai and Khmer need their own packages, and emoji need the **monochrome** [Noto Emoji](https://fonts.google.com/noto/specimen/Noto+Emoji) colour emoji fonts are not drawn by this pipeline:
122
+
123
+ - Debian/Ubuntu: `fonts-noto-core` covers the scripts; on minimal images add `fonts-noto` for the full set
124
+ - Alpine: `apk add font-noto-arabic font-noto-naskh-arabic font-noto-hebrew font-noto-devanagari font-noto-thai font-noto-khmer`
125
+ - macOS: the same families are available as `font-noto-*` casks
126
+
127
+ See [Fonts](https://yohasebe.github.io/rsyntaxtree/documentation#install-fonts-for-svg) in the documentation for which family is used for what, how to substitute your own, and the macOS caveats.
122
128
 
123
129
  ## Installation
124
130
 
data/bin/rsyntaxtree CHANGED
@@ -173,8 +173,8 @@ data = RSyntaxTree::FormatConverter.to_bracket(data) if data
173
173
  begin
174
174
  RSyntaxTree::RSGenerator.check_data(data)
175
175
  rescue RSTError => e
176
- puts e
177
- exit
176
+ warn e
177
+ exit 1
178
178
  end
179
179
 
180
180
  begin
@@ -188,10 +188,10 @@ begin
188
188
  output = rsg.send(draw_method)
189
189
  File.binwrite(filepath, output)
190
190
  rescue RSTError => e
191
- puts e
192
- exit
191
+ warn e
192
+ exit 1
193
193
  rescue StandardError => e
194
- p e
195
- puts "Error: something unexpected occurred"
196
- exit
194
+ warn "Error: something unexpected occurred"
195
+ warn "#{e.class}: #{e.message}"
196
+ exit 1
197
197
  end
@@ -376,7 +376,8 @@ module RSyntaxTree
376
376
  # being compressed narrower than the level right below it (an inversion
377
377
  # that reads as a needle-thin top over flat lower branches); branch
378
378
  # angles then stay comparable between adjacent levels. 1.0 = a pair is
379
- # never tighter than the widest pair among its own children.
379
+ # never tighter than the widest pair among its own children. Applied in
380
+ # the :ordered nest mode only; see tidy_compress.
380
381
  TIDY_LEVEL_BALANCE = 1.0
381
382
 
382
383
  # Base multiplier for the minimum clearance between adjacent subtrees.
@@ -554,16 +555,21 @@ module RSyntaxTree
554
555
  end
555
556
  end
556
557
 
557
- # Level-balance floor (nest mode only): keep the pair at least a
558
+ # Level-balance floor (:ordered only): keep the pair at least a
558
559
  # fraction of the widest child-level spread found inside its own
559
560
  # two subtrees. Nesting can tuck an upper pair far narrower than
560
561
  # the level right below it — an inversion that reads as a
561
- # needle-thin top over flat lower branches. Without nesting the
562
- # leaf-span guard already prevents such inversions, and applying
563
- # the floor there would only forfeit legitimate compression.
562
+ # needle-thin top over flat lower branches.
563
+ #
564
+ # :none does not need it, since the leaf-span guard already
565
+ # prevents such inversions. :free deliberately goes without: it is
566
+ # the end of the scale where density outranks even branch angles,
567
+ # and with the floor in place it compressed no further than
568
+ # :ordered on all but a handful of trees.
569
+ #
564
570
  # Compression is a negative delta (the right subtree moves left),
565
571
  # so the floor bounds delta from below.
566
- if @tidy_nest != :none && delta.negative?
572
+ if @tidy_nest == :ordered && delta.negative?
567
573
  dist = (right_child.horizontal_indent + right_child.content_width / 2.0) -
568
574
  (left_child.horizontal_indent + left_child.content_width / 2.0)
569
575
  below = [left_child, right_child].map { |c| child_spread(c) }.compact.max
@@ -15,14 +15,50 @@ require 'pango'
15
15
  # helpers format it for SVG (quoted) or for Pango::FontDescription#family
16
16
  # (unquoted). Keeping a single source guarantees that measurement and
17
17
  # rendering resolve through the same fallback chain.
18
+ #
19
+ # Scripts beyond Latin and CJK are named explicitly rather than left to the
20
+ # generic fallback, because the generic fallback is not the same font on every
21
+ # machine: on Alpine the Arabic block was picked up by Noto Sans Math, which
22
+ # has the glyphs but no joining rules and so rendered Arabic as isolated
23
+ # letters, while on Debian the same text fell to DejaVu Sans. A family is
24
+ # listed here when (i) the gallery has an example in that script, or (ii) a
25
+ # concrete environment-dependent failure has been reported for it. Anything
26
+ # else stays with the generic fallback — an unbounded list is unmaintainable.
27
+ SCRIPT_FAMILIES_SANS = ["Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans Devanagari", "Noto Sans Thai", "Noto Sans Khmer"].freeze
28
+ # Noto has no serif Arabic; Naskh is its serif-like counterpart, with Noto Sans
29
+ # Arabic behind it for systems that ship one but not the other.
30
+ SCRIPT_FAMILIES_SERIF = ["Noto Naskh Arabic", "Noto Sans Arabic", "Noto Serif Hebrew", "Noto Serif Devanagari", "Noto Serif Thai", "Noto Serif Khmer"].freeze
31
+
32
+ # Monochrome emoji faces only. Colour emoji fonts carry their glyphs as bitmap
33
+ # or COLR tables, which Pango happily measures but librsvg does not draw, so a
34
+ # colour font in this list would give a figure whose emoji are blank or blobbed.
35
+ #
36
+ # This holds only where Pango resolves through fontconfig. On macOS it goes
37
+ # through CoreText, which answers every emoji codepoint with Apple Color Emoji
38
+ # whatever the chain asks for; emoji figures have to be generated elsewhere.
39
+ EMOJI_FAMILIES = ["OpenMoji", "OpenMoji Color", "OpenMoji Black", "Noto Emoji"].freeze
40
+
41
+ # Mathematical alphanumerics (U+1D400–) label heads such as the little v of vP.
42
+ # Neither Noto Sans nor Noto Serif covers the block, so without these entries
43
+ # the glyphs came from whatever the machine happened to offer — Noto Sans Math
44
+ # on Alpine, DejaVu Serif on Debian/Ubuntu, STIX Two Math on macOS. Noto Sans
45
+ # Math is the only Noto face that covers the block and it is a sans design, so
46
+ # the serif style asks for a serif source first. Listed after the script
47
+ # families: Noto Sans Math also claims the Arabic block without joining rules,
48
+ # and must never be reached before Noto Sans Arabic.
49
+ MATH_FAMILIES_SANS = ["Noto Sans Math"].freeze
50
+ MATH_FAMILIES_SERIF = ["DejaVu Serif", "Noto Sans Math"].freeze
51
+
18
52
  FONT_FAMILIES = {
19
- sans: ["Noto Sans", "Noto Sans JP", "Noto Sans CJK JP", "OpenMoji", "OpenMoji Color", "OpenMoji Black", "Noto Emoji", "sans-serif"].freeze,
20
- serif: ["Noto Serif", "Noto Serif JP", "Noto Serif CJK JP", "OpenMoji", "OpenMoji Color", "OpenMoji Black", "Noto Emoji", "serif"].freeze,
21
- mono: ["Noto Sans Mono SemiCondensed", "Noto Sans Mono", "Noto Sans JP", "Noto Sans Mono CJK JP", "OpenMoji", "OpenMoji Color", "OpenMoji Black", "Noto Emoji", "monospace"].freeze,
53
+ sans: (["Noto Sans", "Noto Sans JP", "Noto Sans CJK JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS + EMOJI_FAMILIES + ["sans-serif"]).freeze,
54
+ serif: (["Noto Serif", "Noto Serif JP", "Noto Serif CJK JP"] + SCRIPT_FAMILIES_SERIF + MATH_FAMILIES_SERIF + EMOJI_FAMILIES + ["serif"]).freeze,
55
+ # Noto ships no monospaced faces for these scripts, so the mono style borrows
56
+ # the proportional ones rather than dropping to the generic fallback.
57
+ mono: (["Noto Sans Mono", "Noto Sans JP", "Noto Sans Mono CJK JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS + EMOJI_FAMILIES + ["monospace"]).freeze,
22
58
  # The cjk style puts a full-coverage CJK family first, for text that mixes
23
59
  # Han, Hangul and kana. Latin falls back to the same Noto faces the sans
24
60
  # style uses. (Before 1.8.0 this was WQY Zen Hei.)
25
- cjk: ["Noto Sans CJK JP", "Noto Sans", "Noto Sans JP", "OpenMoji", "OpenMoji Color", "OpenMoji Black", "Noto Emoji", "sans-serif"].freeze
61
+ cjk: (["Noto Sans CJK JP", "Noto Sans", "Noto Sans JP"] + SCRIPT_FAMILIES_SANS + MATH_FAMILIES_SANS + EMOJI_FAMILIES + ["sans-serif"]).freeze
26
62
  }.freeze
27
63
 
28
64
  module FontFamily
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSyntaxTree
4
- VERSION = "1.8.0"
4
+ VERSION = "1.8.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsyntaxtree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoichiro Hasebe