idml 0.10.8 → 0.10.9
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/TODO.pdf/145-apply-mojikumi-aki.md +37 -0
- data/TODO.pdf/146-next-column-wrap.md +25 -0
- data/TODO.pdf/147-side-aware-shapes.md +27 -0
- data/TODO.pdf/61-known-limitations.md +14 -14
- data/lib/idml/render/renderers/text_frame_renderer.rb +16 -0
- data/lib/idml/render/text_wrap_resolver.rb +49 -28
- data/lib/idml/render/wrap_contour.rb +13 -3
- data/lib/idml/text_engine/cjk_layout.rb +79 -6
- data/lib/idml/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c3ebdf83496770ecda7b37cc6f6cdc2388874785ccad23ac57aa396f76aef03b
|
|
4
|
+
data.tar.gz: d7304a209e09d4de3ee6d9f7556d18053f838ee3fef9a4f787f06693aa42ee32
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd4dc75a85966131506edf7f3e01df91127fbf26525bb02edcf783f0c07580380be0d8bd38562c11a477602731e2efff8de78061580b471f1389f3bb87a07119
|
|
7
|
+
data.tar.gz: 74286965e709455a356789cc6f7e4c93f75d414a1132301ee8e347a85d114065bd1988921c180a522c646824d79f2049bdfacf7db609e9981b270d3a88b71fbb
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# TODO PDF 145: Apply mojikumi aki to layout
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE — implemented 2026-08-28
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Two gaps: (a) `apply_script_spacing` (TODO 125) was implemented
|
|
8
|
+
but never wired into the render pipeline — CJK/Latin auto spacing
|
|
9
|
+
never actually applied; (b) the named mojikumi sets modeled in
|
|
10
|
+
TODO 144 were parsed but unused.
|
|
11
|
+
|
|
12
|
+
## Solution
|
|
13
|
+
|
|
14
|
+
- `CjkLayout.apply_script_spacing` now takes the document's aki
|
|
15
|
+
overrides: when the designmap declares a `MojikumiTable`, each
|
|
16
|
+
adjacent glyph pair whose classes match an
|
|
17
|
+
`OverrideMojikumiAki` entry (Target/Side class ×
|
|
18
|
+
SideIsAfterTarget) gets that entry's Desired spacing (em ×
|
|
19
|
+
point size). Unlisted pairs keep the default eighth-em at
|
|
20
|
+
CJK/Latin script boundaries.
|
|
21
|
+
- New `CjkLayout.mojikumi_class` classifies the seven InDesign
|
|
22
|
+
mojikumi classes (ideograph, opening bracket, closing bracket,
|
|
23
|
+
comma/period, middle dot, digit — incl. fullwidth, Latin —
|
|
24
|
+
incl. fullwidth).
|
|
25
|
+
- `TextFrameRenderer#layout_run` applies the spacing between
|
|
26
|
+
shaping and line breaking, using
|
|
27
|
+
`mojikumi_aki_overrides(context)` (first named set from the
|
|
28
|
+
designmap; empty when absent).
|
|
29
|
+
|
|
30
|
+
The result: documents declaring custom mojikumi now render their
|
|
31
|
+
declared inter-class spacing.
|
|
32
|
+
|
|
33
|
+
## Files
|
|
34
|
+
|
|
35
|
+
- `lib/idml/text_engine/cjk_layout.rb`
|
|
36
|
+
- `lib/idml/render/renderers/text_frame_renderer.rb`
|
|
37
|
+
- `spec/idml/text_engine/cjk_layout_spec.rb`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# TODO PDF 146: NextColumn wrap behavior
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE (approximation) — implemented 2026-08-28
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
NextColumnTextWrap approximated as the bounding box (text narrowed
|
|
8
|
+
beside the object). InDesign moves the text to the next COLUMN.
|
|
9
|
+
|
|
10
|
+
## Solution
|
|
11
|
+
|
|
12
|
+
NextColumnTextWrap now joins JumpObjectTextWrap in the jump path:
|
|
13
|
+
the object blocks the full frame width and the run skips below
|
|
14
|
+
the object's bottom edge. In a single-column frame this matches
|
|
15
|
+
InDesign's visible outcome (the "next column" is below).
|
|
16
|
+
|
|
17
|
+
Remaining approximation (documented in TODO.pdf/61): in a
|
|
18
|
+
MULTI-column frame, true NextColumn jumps to the next column in
|
|
19
|
+
the same frame — that needs column-jump chain integration, which
|
|
20
|
+
stays a future enhancement.
|
|
21
|
+
|
|
22
|
+
## Files
|
|
23
|
+
|
|
24
|
+
- `lib/idml/render/text_wrap_resolver.rb`
|
|
25
|
+
- `spec/idml/render/text_wrap_resolver_spec.rb`
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# TODO PDF 147: Side-aware Contour shapes
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE — implemented 2026-08-28
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
TextWrapSide applied only to BoundingBoxTextWrap contours;
|
|
8
|
+
Contour-mode shapes narrowed by polygon overlap regardless of
|
|
9
|
+
side — text smeared over shapes at frame edges instead of
|
|
10
|
+
flowing around the chosen side.
|
|
11
|
+
|
|
12
|
+
## Solution
|
|
13
|
+
|
|
14
|
+
`WrapContour::Shape` carries `side`; `wrap_adjustment`'s shape
|
|
15
|
+
branch dispatches through the shared interval-based side logic
|
|
16
|
+
using the polygon's bounding box: LeftSide keeps text left of the
|
|
17
|
+
shape, RightSide shifts lines past its right edge, LargestArea
|
|
18
|
+
picks the roomier side; BothSides (and spine variants) keep the
|
|
19
|
+
exact polygon-overlap narrowing. Inverse shapes keep their
|
|
20
|
+
flip-to-inside semantics. The side dispatch is now one shared
|
|
21
|
+
`interval_side_adjustment` for boxes and shapes alike.
|
|
22
|
+
|
|
23
|
+
## Files
|
|
24
|
+
|
|
25
|
+
- `lib/idml/render/wrap_contour.rb`
|
|
26
|
+
- `lib/idml/render/text_wrap_resolver.rb`
|
|
27
|
+
- `spec/idml/render/text_wrap_resolver_spec.rb`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TODO PDF 61: Known limitations and future work
|
|
2
2
|
|
|
3
|
-
## Status: DOCUMENTED — refreshed 2026-08-
|
|
3
|
+
## Status: DOCUMENTED — refreshed 2026-08-28
|
|
4
4
|
|
|
5
5
|
Previously listed limitations (font embedding, tagged PDF, per-run
|
|
6
6
|
styling, dead code) were resolved by TODOs 52/53/55/67/76 long ago;
|
|
@@ -25,18 +25,19 @@ this list reflects the current state.
|
|
|
25
25
|
- **StartParagraph** breaks act at frame/column granularity — no
|
|
26
26
|
odd/even page parity.
|
|
27
27
|
- **Text wrap**: BoundingBoxTextWrap, Contour, and Inverse modes
|
|
28
|
-
render (TODOs 130-131) with TextWrapSide side-awareness
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
render (TODOs 130-131) with TextWrapSide side-awareness for both
|
|
29
|
+
box contours and Contour shapes (TODOs 138/147); JumpObject and
|
|
30
|
+
NextColumn move text below the object (TODOs 143/146) — NextColumn
|
|
31
|
+
in a MULTI-column frame should jump to the next column instead
|
|
32
|
+
(chain integration, future); spine variants approximate as
|
|
33
|
+
BothSides.
|
|
33
34
|
|
|
34
35
|
### CJK
|
|
35
|
-
- **Mojikumi**: script spacing (TODO 125
|
|
36
|
-
(TODO 132),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
- **Mojikumi**: script spacing (TODO 125, now actually wired),
|
|
37
|
+
line-end compression (TODO 132), class-based pair compression,
|
|
38
|
+
and named-set aki overrides (TODO 145: OverrideMojikumiAki
|
|
39
|
+
Desired applies per class pair) are applied; Minimum/Maximum
|
|
40
|
+
aki bounds and CompressionPriority are parsed but unused.
|
|
40
41
|
- **Vertical mode**: Latin runs rotate as one group per segment
|
|
41
42
|
(TODO 142); ruby placement is beside-column; keep options do
|
|
42
43
|
not apply.
|
|
@@ -59,8 +60,7 @@ this list reflects the current state.
|
|
|
59
60
|
## Future enhancements
|
|
60
61
|
|
|
61
62
|
- Hyphenation dictionary integration
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
- NextColumn wrap via column-jump chaining
|
|
63
|
+
- NextColumn wrap via column-jump chaining (multi-column frames)
|
|
64
|
+
- Minimum/Maximum mojikumi aki bounds + CompressionPriority
|
|
65
65
|
- ToBinding / RTL binding alignment
|
|
66
66
|
- Vertical-mode keep options
|
|
@@ -1145,6 +1145,9 @@ module Idml
|
|
|
1145
1145
|
glyphs = TextEngine::Shaper.shape(
|
|
1146
1146
|
text: run.text, font: font, size: size,
|
|
1147
1147
|
)
|
|
1148
|
+
glyphs = TextEngine::CjkLayout.apply_script_spacing(
|
|
1149
|
+
glyphs, size, mojikumi_aki_overrides(context)
|
|
1150
|
+
)
|
|
1148
1151
|
lines = TextEngine::LineBreaker.break(
|
|
1149
1152
|
glyphs: glyphs, frame_width: wrap_width,
|
|
1150
1153
|
)
|
|
@@ -1169,6 +1172,19 @@ module Idml
|
|
|
1169
1172
|
end
|
|
1170
1173
|
private_class_method :layout_run
|
|
1171
1174
|
|
|
1175
|
+
# Aki overrides from the document's first named mojikumi
|
|
1176
|
+
# set, when the designmap declares one (TODO 145). Empty
|
|
1177
|
+
# otherwise — apply_script_spacing then uses the default
|
|
1178
|
+
# CJK/Latin script spacing.
|
|
1179
|
+
def self.mojikumi_aki_overrides(context)
|
|
1180
|
+
package = context.package
|
|
1181
|
+
return [] unless package&.has_part?("designmap.xml")
|
|
1182
|
+
|
|
1183
|
+
table = package.designmap.mojikumi_table.first
|
|
1184
|
+
table&.aki_overrides || []
|
|
1185
|
+
end
|
|
1186
|
+
private_class_method :mojikumi_aki_overrides
|
|
1187
|
+
|
|
1172
1188
|
# True when the paragraph should move wholly to the next
|
|
1173
1189
|
# frame: a StartParagraph forced break, or KeepAllLines-
|
|
1174
1190
|
# Together with insufficient remaining space (never for the
|
|
@@ -63,10 +63,23 @@ module Idml
|
|
|
63
63
|
width = WrapContour.overlap_width(contour, line_y,
|
|
64
64
|
line_height, frame_x,
|
|
65
65
|
frame_right)
|
|
66
|
-
|
|
66
|
+
if contour.inverse
|
|
67
|
+
reduction = [reduction,
|
|
68
|
+
(frame_right - frame_x) - width].max
|
|
69
|
+
next
|
|
70
|
+
end
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
# Side-aware shapes (TODO 147): the side dispatch uses
|
|
73
|
+
# the polygon's bounding box; narrowing itself keeps the
|
|
74
|
+
# exact polygon overlap for BothSides.
|
|
75
|
+
bbox_left, _bottom, bbox_right, = WrapContour.bounding_box(
|
|
76
|
+
contour,
|
|
77
|
+
)
|
|
78
|
+
r, s = interval_side_adjustment(contour.side, bbox_left,
|
|
79
|
+
bbox_right, width,
|
|
80
|
+
frame_x, frame_right)
|
|
81
|
+
reduction = [reduction, r].max
|
|
82
|
+
shift = [shift, s].max
|
|
70
83
|
end
|
|
71
84
|
[reduction, shift]
|
|
72
85
|
end
|
|
@@ -86,12 +99,14 @@ module Idml
|
|
|
86
99
|
|
|
87
100
|
return [0.0, 0.0] if overlap.zero?
|
|
88
101
|
|
|
89
|
-
# JumpObject: text never flows beside the
|
|
90
|
-
# frame width is blocked and the renderer
|
|
91
|
-
# below the object's bottom edge.
|
|
102
|
+
# JumpObject / NextColumn: text never flows beside the
|
|
103
|
+
# object — the full frame width is blocked and the renderer
|
|
104
|
+
# skips the run below the object's bottom edge.
|
|
92
105
|
return [frame_right - frame_x, 0.0] if contour.jump
|
|
93
106
|
|
|
94
|
-
|
|
107
|
+
interval_side_adjustment(contour.side, contour.x,
|
|
108
|
+
contour.x + contour.width, overlap,
|
|
109
|
+
frame_x, frame_right)
|
|
95
110
|
end
|
|
96
111
|
private :box_adjustment
|
|
97
112
|
|
|
@@ -114,30 +129,35 @@ module Idml
|
|
|
114
129
|
end
|
|
115
130
|
private :box_contour?
|
|
116
131
|
|
|
117
|
-
|
|
118
|
-
|
|
132
|
+
# Side dispatch over an object's horizontal extent
|
|
133
|
+
# [obj_left, obj_right] (TODO 138/147 — shared by box
|
|
134
|
+
# contours and Contour shapes' bounding boxes): LeftSide
|
|
135
|
+
# keeps text left of the object (reduce to its left edge, no
|
|
136
|
+
# shift); RightSide moves text past its right edge (reduce +
|
|
137
|
+
# shift); LargestArea picks the roomier side; BothSides and
|
|
138
|
+
# the spine variants reduce by `overlap` only.
|
|
139
|
+
def interval_side_adjustment(side, obj_left, obj_right, overlap,
|
|
140
|
+
frame_x, frame_right)
|
|
141
|
+
case side
|
|
119
142
|
when "LeftSide"
|
|
120
|
-
[frame_right -
|
|
143
|
+
[frame_right - obj_left, 0.0]
|
|
121
144
|
when "RightSide"
|
|
122
|
-
shift =
|
|
145
|
+
shift = obj_right - frame_x
|
|
123
146
|
[shift, shift]
|
|
124
147
|
when "LargestArea"
|
|
125
|
-
|
|
148
|
+
left_free = obj_left - frame_x
|
|
149
|
+
right_free = frame_right - obj_right
|
|
150
|
+
if left_free >= right_free
|
|
151
|
+
[frame_right - obj_left, 0.0]
|
|
152
|
+
else
|
|
153
|
+
shift = obj_right - frame_x
|
|
154
|
+
[shift, shift]
|
|
155
|
+
end
|
|
126
156
|
else
|
|
127
157
|
[overlap, 0.0]
|
|
128
158
|
end
|
|
129
159
|
end
|
|
130
|
-
private :
|
|
131
|
-
|
|
132
|
-
def largest_side_adjustment(contour, frame_x, frame_right)
|
|
133
|
-
left_free = contour.x - frame_x
|
|
134
|
-
right_free = frame_right - (contour.x + contour.width)
|
|
135
|
-
return [frame_right - contour.x, 0.0] if left_free >= right_free
|
|
136
|
-
|
|
137
|
-
shift = contour.x + contour.width - frame_x
|
|
138
|
-
[shift, shift]
|
|
139
|
-
end
|
|
140
|
-
private :largest_side_adjustment
|
|
160
|
+
private :interval_side_adjustment
|
|
141
161
|
|
|
142
162
|
# Returns the total horizontal overlap of all contours with a
|
|
143
163
|
# text line at the given y range within the given x range.
|
|
@@ -164,10 +184,11 @@ module Idml
|
|
|
164
184
|
# The wrap shape for an item: a bounding-box rectangle
|
|
165
185
|
# (BoundingBoxTextWrap — the legacy "BoundingBox" spelling is
|
|
166
186
|
# also accepted from early synthetic fixtures) or a flattened
|
|
167
|
-
# PathGeometry polygon (Contour mode). JumpObject
|
|
168
|
-
# NextColumn
|
|
169
|
-
#
|
|
170
|
-
#
|
|
187
|
+
# PathGeometry polygon (Contour mode). JumpObject moves text
|
|
188
|
+
# below the object; NextColumn approximates as the same
|
|
189
|
+
# jump-below in a single-column frame (true column jumping
|
|
190
|
+
# needs chain integration, documented in TODO.pdf/61).
|
|
191
|
+
JUMP_MODES = %w[JumpObjectTextWrap NextColumnTextWrap].freeze
|
|
171
192
|
def self.contour_for(item, page_height)
|
|
172
193
|
pref = wrap_preference(item)
|
|
173
194
|
return nil unless pref
|
|
@@ -190,7 +211,7 @@ module Idml
|
|
|
190
211
|
x: rect[:x], y: rect[:y],
|
|
191
212
|
width: rect[:width], height: rect[:height],
|
|
192
213
|
side: pref&.text_wrap_side,
|
|
193
|
-
jump: pref&.text_wrap_mode
|
|
214
|
+
jump: JUMP_MODES.include?(pref&.text_wrap_mode)
|
|
194
215
|
)
|
|
195
216
|
end
|
|
196
217
|
private_class_method :box_contour
|
|
@@ -13,8 +13,9 @@ module Idml
|
|
|
13
13
|
|
|
14
14
|
# A wrap shape: flattened polygons (one per subpath) plus the
|
|
15
15
|
# TextWrapOffset bounds. `inverse` flips the wrap region:
|
|
16
|
-
# text may only flow INSIDE the shape.
|
|
17
|
-
|
|
16
|
+
# text may only flow INSIDE the shape. `side` carries
|
|
17
|
+
# TextWrapSide for side-aware narrowing (TODO 147).
|
|
18
|
+
Shape = Struct.new(:polygons, :offset, :inverse, :side,
|
|
18
19
|
keyword_init: true)
|
|
19
20
|
|
|
20
21
|
# Builds the wrap shape for an item's PathGeometry and wrap
|
|
@@ -24,7 +25,16 @@ module Idml
|
|
|
24
25
|
return nil if polygons.empty?
|
|
25
26
|
|
|
26
27
|
Shape.new(polygons: polygons, offset: wrap_offset(pref),
|
|
27
|
-
inverse: pref.inverse)
|
|
28
|
+
inverse: pref.inverse, side: pref.text_wrap_side)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The shape's bounding box: [left, bottom, right, top] in
|
|
32
|
+
# PDF coordinates, flattened across subpaths.
|
|
33
|
+
def self.bounding_box(shape)
|
|
34
|
+
points = shape.polygons.flatten(1)
|
|
35
|
+
xs = points.map(&:first)
|
|
36
|
+
ys = points.map(&:last)
|
|
37
|
+
[xs.min, ys.min, xs.max, ys.max]
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
def self.polygons_for(item, page_height)
|
|
@@ -10,22 +10,95 @@ module Idml
|
|
|
10
10
|
# Mojikumi subset: automatic inter-script spacing — an eighth
|
|
11
11
|
# em is inserted between adjacent CJK and ASCII alphanumeric
|
|
12
12
|
# glyphs (widenning the leading glyph of each pair), matching
|
|
13
|
-
# InDesign's default CJK/Latin auto spacing.
|
|
13
|
+
# InDesign's default CJK/Latin auto spacing. A named
|
|
14
|
+
# mojikumi set's OverrideMojikumiAki entries take precedence
|
|
15
|
+
# when given: the Desired value (em units) applies to each
|
|
16
|
+
# matching class pair (TODO 145).
|
|
14
17
|
SCRIPT_SPACING_EM = 0.125
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
# Mojikumi character classes (InDesign mojikumi table rows).
|
|
20
|
+
CLASS_IDEOGRAPH = 1
|
|
21
|
+
CLASS_OPENING = 2
|
|
22
|
+
CLASS_CLOSING = 3
|
|
23
|
+
CLASS_COMMA_PERIOD = 4
|
|
24
|
+
CLASS_MIDDLE_DOT = 5
|
|
25
|
+
CLASS_DIGIT = 6
|
|
26
|
+
CLASS_LATIN = 7
|
|
27
|
+
|
|
28
|
+
COMMA_PERIOD_CODEPOINTS = [
|
|
29
|
+
0x3001, # 、 ideographic comma
|
|
30
|
+
0x3002, # 。 ideographic full stop
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
CLOSING_CODEPOINTS = [
|
|
34
|
+
0x3009, # 〉
|
|
35
|
+
0x300B, # 》
|
|
36
|
+
0x300D, # 」
|
|
37
|
+
0x300F, # 』
|
|
38
|
+
0x3011, # 】
|
|
39
|
+
0x3015, # 〕
|
|
40
|
+
0xFF09, # )fullwidth right paren
|
|
41
|
+
0xFF3D, # ]
|
|
42
|
+
0xFF5D, # }
|
|
43
|
+
].freeze
|
|
44
|
+
|
|
45
|
+
def apply_script_spacing(glyphs, size, aki_overrides = [])
|
|
18
46
|
(0...(glyphs.length - 1)).each do |index|
|
|
19
47
|
leading = glyphs[index]
|
|
20
48
|
following = glyphs[index + 1]
|
|
21
|
-
|
|
22
|
-
|
|
49
|
+
em = aki_em(leading.codepoint, following.codepoint,
|
|
50
|
+
aki_overrides)
|
|
51
|
+
next unless em&.positive?
|
|
23
52
|
|
|
24
|
-
leading.width +=
|
|
53
|
+
leading.width += em * size
|
|
25
54
|
end
|
|
26
55
|
glyphs
|
|
27
56
|
end
|
|
28
57
|
|
|
58
|
+
# Spacing (em units) between an adjacent pair: the named
|
|
59
|
+
# set's Desired aki for a matching class-pair override, else
|
|
60
|
+
# the default eighth-em at CJK/Latin script boundaries, else
|
|
61
|
+
# none.
|
|
62
|
+
def aki_em(left, right, aki_overrides)
|
|
63
|
+
override = aki_overrides.find do |entry|
|
|
64
|
+
entry.side_is_after_target &&
|
|
65
|
+
entry.target_mojikumi_class == mojikumi_class(left) &&
|
|
66
|
+
entry.side_mojikumi_class == mojikumi_class(right)
|
|
67
|
+
end
|
|
68
|
+
return override.desired if override&.desired
|
|
69
|
+
|
|
70
|
+
return SCRIPT_SPACING_EM if script_boundary?(left, right)
|
|
71
|
+
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The mojikumi class of a codepoint, or nil outside the
|
|
76
|
+
# CJK punctuation / script classes. Ideographs (the default
|
|
77
|
+
# CJK class) win only when no special class matches.
|
|
78
|
+
def mojikumi_class(codepoint)
|
|
79
|
+
return CLASS_OPENING if OPENING_CODEPOINTS.include?(codepoint)
|
|
80
|
+
return CLASS_COMMA_PERIOD if COMMA_PERIOD_CODEPOINTS.include?(codepoint)
|
|
81
|
+
return CLASS_CLOSING if CLOSING_CODEPOINTS.include?(codepoint)
|
|
82
|
+
return CLASS_MIDDLE_DOT if MIDDLE_CODEPOINTS.include?(codepoint)
|
|
83
|
+
return CLASS_DIGIT if digit_class?(codepoint)
|
|
84
|
+
return CLASS_LATIN if latin_class?(codepoint)
|
|
85
|
+
return CLASS_IDEOGRAPH if cjk?(codepoint)
|
|
86
|
+
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def digit_class?(codepoint)
|
|
91
|
+
(0x30..0x39).cover?(codepoint) ||
|
|
92
|
+
(0xFF10..0xFF19).cover?(codepoint)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def latin_class?(codepoint)
|
|
96
|
+
(0x41..0x5A).cover?(codepoint) ||
|
|
97
|
+
(0x61..0x7A).cover?(codepoint) ||
|
|
98
|
+
(0xFF21..0xFF3A).cover?(codepoint) ||
|
|
99
|
+
(0xFF41..0xFF5A).cover?(codepoint)
|
|
100
|
+
end
|
|
101
|
+
|
|
29
102
|
# True when exactly one of the pair is CJK and the other is
|
|
30
103
|
# an ASCII letter or digit.
|
|
31
104
|
def script_boundary?(left, right)
|
data/lib/idml/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: idml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.10.
|
|
4
|
+
version: 0.10.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bigdecimal
|
|
@@ -208,6 +208,9 @@ files:
|
|
|
208
208
|
- TODO.pdf/142-vertical-latin-run-groups.md
|
|
209
209
|
- TODO.pdf/143-jump-object-below.md
|
|
210
210
|
- TODO.pdf/144-mojikumi-table-model.md
|
|
211
|
+
- TODO.pdf/145-apply-mojikumi-aki.md
|
|
212
|
+
- TODO.pdf/146-next-column-wrap.md
|
|
213
|
+
- TODO.pdf/147-side-aware-shapes.md
|
|
211
214
|
- TODO.pdf/15-wire-spread-children.md
|
|
212
215
|
- TODO.pdf/16-typed-model-pipeline.md
|
|
213
216
|
- TODO.pdf/17-shape-rendering.md
|