idml 0.10.14 → 0.10.15
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/154-vertical-keep-windows.md +21 -0
- data/TODO.pdf/155-next-column-jump.md +29 -0
- data/TODO.pdf/61-known-limitations.md +7 -11
- data/lib/idml/render/renderers/text_frame_renderer.rb +36 -0
- data/lib/idml/render/renderers/vertical_emit.rb +27 -4
- data/lib/idml/render/text_wrap_resolver.rb +36 -6
- data/lib/idml/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 578a2a0fbd712f1931eaff74db316971441be4a967afd67d47ab55ecdc9679d1
|
|
4
|
+
data.tar.gz: e93c08f8694da2e0522a787ff0f58b4f607952f7024a0dd0d54bab42599ac8e9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 260e3fdbcd2339d2e461aae7f2992b311120a0b15544d48b65c39d915dee9fd0219fcb3ee6d9e07ce40eaeb9bf68846fa72bdf2475d9ce12d1982cdd1503afd5
|
|
7
|
+
data.tar.gz: 6d760f50d28216bbe1a620aa4ba2b43028ab3051384e0fee559764892f8495b04f5623b2a3ba14698f89cf6bf1e3de9f0426be158e3e7faf0d8d7a3b639e289e
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# TODO PDF 154: Vertical keep windows
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE (approximation) — implemented 2026-09-01
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
KeepFirstLines / KeepLastLines windows (TODO 133) applied only to
|
|
8
|
+
the horizontal engine; vertical paragraphs ignored them.
|
|
9
|
+
|
|
10
|
+
## Solution
|
|
11
|
+
|
|
12
|
+
`VerticalEmit`'s pre-placement keep check (TODO 153) also honors
|
|
13
|
+
the windows on the same column-count approximation: a splitting
|
|
14
|
+
paragraph (needed columns > available) defers when fewer than
|
|
15
|
+
KeepFirstLines columns fit, or when the stranded tail would hold
|
|
16
|
+
fewer than KeepLastLines columns.
|
|
17
|
+
|
|
18
|
+
## Files
|
|
19
|
+
|
|
20
|
+
- `lib/idml/render/renderers/vertical_emit.rb`
|
|
21
|
+
- `spec/idml/render/vertical_render_spec.rb`
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# TODO PDF 155: NextColumn column jumping in multi-column frames
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE — implemented 2026-09-01
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
NextColumnTextWrap approximated as JumpObject-below (TODO 146):
|
|
8
|
+
in a multi-column frame, InDesign moves the text to the NEXT
|
|
9
|
+
COLUMN, not below the object.
|
|
10
|
+
|
|
11
|
+
## Solution
|
|
12
|
+
|
|
13
|
+
The wrap contour carries the mode: `jump` (JumpObjectTextWrap)
|
|
14
|
+
vs `next_column` (NextColumnTextWrap). Both block the full column
|
|
15
|
+
width; the renderer chooses the response —
|
|
16
|
+
`TextFrameRenderer#next_column_jump?` abandons the column when a
|
|
17
|
+
NextColumn contour blocks the run's band in a multi-column frame:
|
|
18
|
+
the cursor drops to the bottom limit mid-run, the unplaced runs
|
|
19
|
+
return to the chain state, and the next column (or frame)
|
|
20
|
+
resumes them. Single-column frames and JumpObject keep the
|
|
21
|
+
skip-below path (`jump_contour_bottom` reports the resume point
|
|
22
|
+
for both flavors).
|
|
23
|
+
|
|
24
|
+
## Files
|
|
25
|
+
|
|
26
|
+
- `lib/idml/render/text_wrap_resolver.rb`
|
|
27
|
+
- `lib/idml/render/renderers/text_frame_renderer.rb`
|
|
28
|
+
- `spec/idml/render/text_wrap_resolver_spec.rb`
|
|
29
|
+
- `spec/idml/render/jump_object_render_spec.rb` (end-to-end)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TODO PDF 61: Known limitations and future work
|
|
2
2
|
|
|
3
|
-
## Status: DOCUMENTED — refreshed 2026-
|
|
3
|
+
## Status: DOCUMENTED — refreshed 2026-09-01
|
|
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;
|
|
@@ -27,11 +27,10 @@ this list reflects the current state.
|
|
|
27
27
|
odd/even page parity.
|
|
28
28
|
- **Text wrap**: BoundingBoxTextWrap, Contour, and Inverse modes
|
|
29
29
|
render (TODOs 130-131) with TextWrapSide side-awareness for both
|
|
30
|
-
box contours and Contour shapes (TODOs 138/147); JumpObject
|
|
31
|
-
|
|
32
|
-
in
|
|
33
|
-
(
|
|
34
|
-
BothSides.
|
|
30
|
+
box contours and Contour shapes (TODOs 138/147); JumpObject moves
|
|
31
|
+
text below the object (TODO 143); NextColumn jumps to the next
|
|
32
|
+
column in multi-column frames and below the object otherwise
|
|
33
|
+
(TODOs 146/155); spine variants approximate as BothSides.
|
|
35
34
|
|
|
36
35
|
### CJK
|
|
37
36
|
- **Mojikumi**: script spacing (TODO 125, now actually wired),
|
|
@@ -40,9 +39,8 @@ this list reflects the current state.
|
|
|
40
39
|
Desired applies per class pair) are applied; Minimum/Maximum
|
|
41
40
|
aki bounds and CompressionPriority are parsed but unused.
|
|
42
41
|
- **Vertical mode**: Latin runs rotate as one group per segment
|
|
43
|
-
(TODO 142); ruby placement is beside-column;
|
|
44
|
-
|
|
45
|
-
keep windows are horizontal-only.
|
|
42
|
+
(TODO 142); ruby placement is beside-column; keep options apply
|
|
43
|
+
with a column-count approximation (TODOs 153-154).
|
|
46
44
|
|
|
47
45
|
### Structural features
|
|
48
46
|
- **Endnotes** (TODO 117): reference markers render and endnote TEXT
|
|
@@ -62,7 +60,5 @@ this list reflects the current state.
|
|
|
62
60
|
## Future enhancements
|
|
63
61
|
|
|
64
62
|
- Hyphenation dictionary integration
|
|
65
|
-
- NextColumn wrap via column-jump chaining (multi-column frames)
|
|
66
63
|
- Minimum/Maximum mojikumi aki bounds + CompressionPriority
|
|
67
64
|
- ToBinding / kashida Arabic justification
|
|
68
|
-
- Vertical keep windows (KeepFirstLines / KeepLastLines)
|
|
@@ -543,6 +543,11 @@ module Idml
|
|
|
543
543
|
context, layout_frame, cursor_y, run
|
|
544
544
|
)
|
|
545
545
|
run_wrap -= wrap_reduction
|
|
546
|
+
if next_column_jump?(context, layout_frame, cursor_y, run,
|
|
547
|
+
run_wrap)
|
|
548
|
+
cursor_y = bottom_limit
|
|
549
|
+
break
|
|
550
|
+
end
|
|
546
551
|
cursor_y, run_wrap, wrap_shift = skip_below_jump_object(
|
|
547
552
|
context, layout_frame, cursor_y, run, run_wrap, wrap_shift,
|
|
548
553
|
wrap_reduction, bottom_limit
|
|
@@ -612,6 +617,37 @@ module Idml
|
|
|
612
617
|
end
|
|
613
618
|
private_class_method :render_footnote_entries
|
|
614
619
|
|
|
620
|
+
# NextColumnTextWrap in a multi-column frame (TODO 155):
|
|
621
|
+
# abandon the column — the unplaced runs return to the
|
|
622
|
+
# chain state and the next column resumes them. Falls
|
|
623
|
+
# through to skip-below everywhere else (single-column
|
|
624
|
+
# frames included).
|
|
625
|
+
def self.next_column_jump?(context, layout_frame, cursor_y, run,
|
|
626
|
+
run_wrap)
|
|
627
|
+
size = run.point_size || DEFAULT_SIZE
|
|
628
|
+
return false unless run_wrap < size
|
|
629
|
+
return false unless multi_column_frame?(context)
|
|
630
|
+
|
|
631
|
+
blocked_in_band?(context, layout_frame, cursor_y, size)
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
def self.multi_column_frame?(context)
|
|
635
|
+
text_column_count(context).to_i > 1
|
|
636
|
+
end
|
|
637
|
+
private_class_method :multi_column_frame?
|
|
638
|
+
|
|
639
|
+
def self.blocked_in_band?(context, layout_frame, cursor_y, size)
|
|
640
|
+
resolver = context.text_wrap_resolver
|
|
641
|
+
return false unless resolver
|
|
642
|
+
|
|
643
|
+
frame_x = layout_frame.x + (layout_frame.inset_left || 0)
|
|
644
|
+
frame_right = layout_frame.x + layout_frame.width -
|
|
645
|
+
(layout_frame.inset_right || 0)
|
|
646
|
+
resolver.next_column_block?(cursor_y, size, frame_x, frame_right)
|
|
647
|
+
end
|
|
648
|
+
private_class_method :blocked_in_band?
|
|
649
|
+
private_class_method :next_column_jump?
|
|
650
|
+
|
|
615
651
|
# JumpObjectTextWrap blocks the full column width: when the
|
|
616
652
|
# run's band is fully blocked, move the cursor below the
|
|
617
653
|
# object's bottom edge and re-measure once. InDesign's
|
|
@@ -71,9 +71,10 @@ module Idml
|
|
|
71
71
|
return false unless keep_flagged?(paragraph)
|
|
72
72
|
|
|
73
73
|
needed = [paragraph.runs.length, 1].max
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
room = vertical_room(layout_frame, paragraph, left_limit,
|
|
75
|
+
next_column)
|
|
76
|
+
return true if keep_all_deferred?(paragraph, needed, room)
|
|
77
|
+
return true if window_deferred?(paragraph, needed, room)
|
|
77
78
|
|
|
78
79
|
with_next_deferred?(paragraph, next_paragraph, layout_frame,
|
|
79
80
|
left_limit, next_column, needed)
|
|
@@ -81,7 +82,29 @@ module Idml
|
|
|
81
82
|
|
|
82
83
|
def keep_flagged?(paragraph)
|
|
83
84
|
paragraph.keep_all_lines_together ||
|
|
84
|
-
!paragraph.keep_with_next.nil?
|
|
85
|
+
!paragraph.keep_with_next.nil? ||
|
|
86
|
+
paragraph.keep_first_lines ||
|
|
87
|
+
paragraph.keep_last_lines
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def keep_all_deferred?(paragraph, needed, room)
|
|
91
|
+
paragraph.keep_all_lines_together && needed > room
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Keep windows on the same column-count approximation:
|
|
95
|
+
# defer a splitting paragraph when fewer than
|
|
96
|
+
# KeepFirstLines columns fit, or when the stranded tail
|
|
97
|
+
# would hold fewer than KeepLastLines columns.
|
|
98
|
+
def window_deferred?(paragraph, needed, room)
|
|
99
|
+
return false unless needed > room
|
|
100
|
+
|
|
101
|
+
if paragraph.keep_first_lines &&
|
|
102
|
+
room < paragraph.keep_first_lines
|
|
103
|
+
return true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
paragraph.keep_last_lines &&
|
|
107
|
+
(needed - room) < paragraph.keep_last_lines
|
|
85
108
|
end
|
|
86
109
|
|
|
87
110
|
def with_next_deferred?(paragraph, next_paragraph, layout_frame,
|
|
@@ -18,7 +18,7 @@ module Idml
|
|
|
18
18
|
class TextWrapResolver
|
|
19
19
|
# A wrap contour: a PDF-coordinate rectangle the text avoids.
|
|
20
20
|
Contour = Struct.new(
|
|
21
|
-
:x, :y, :width, :height, :inverse, :side, :jump,
|
|
21
|
+
:x, :y, :width, :height, :inverse, :side, :jump, :next_column,
|
|
22
22
|
keyword_init: true
|
|
23
23
|
)
|
|
24
24
|
|
|
@@ -100,9 +100,10 @@ module Idml
|
|
|
100
100
|
return [0.0, 0.0] if overlap.zero?
|
|
101
101
|
|
|
102
102
|
# JumpObject / NextColumn: text never flows beside the
|
|
103
|
-
# object — the full frame width is blocked
|
|
104
|
-
# skips
|
|
105
|
-
|
|
103
|
+
# object — the full frame width is blocked. The renderer
|
|
104
|
+
# either skips below (JumpObject) or jumps column
|
|
105
|
+
# (NextColumn in multi-column frames).
|
|
106
|
+
return [frame_right - frame_x, 0.0] if blocks_full_width?(contour)
|
|
106
107
|
|
|
107
108
|
interval_side_adjustment(contour.side, contour.x,
|
|
108
109
|
contour.x + contour.width, overlap,
|
|
@@ -115,7 +116,7 @@ module Idml
|
|
|
115
116
|
# jump contour blocks the band.
|
|
116
117
|
def jump_contour_bottom(line_y, line_height, frame_x, frame_right)
|
|
117
118
|
bottoms = @contours.filter_map do |contour|
|
|
118
|
-
next unless
|
|
119
|
+
next unless skip_contour?(contour)
|
|
119
120
|
next unless y_overlap?(contour, line_y, line_height) &&
|
|
120
121
|
x_overlap?(contour, frame_x, frame_right)
|
|
121
122
|
|
|
@@ -124,6 +125,25 @@ module Idml
|
|
|
124
125
|
bottoms.min
|
|
125
126
|
end
|
|
126
127
|
|
|
128
|
+
# Contours whose block resolves as skip-below (both jump
|
|
129
|
+
# flavors report a resume point; the renderer chooses).
|
|
130
|
+
def skip_contour?(contour)
|
|
131
|
+
box_contour?(contour) &&
|
|
132
|
+
(contour.jump || contour.next_column)
|
|
133
|
+
end
|
|
134
|
+
private :skip_contour?
|
|
135
|
+
|
|
136
|
+
# True when a NextColumn contour blocks the given band: in a
|
|
137
|
+
# multi-column frame the renderer abandons the column and
|
|
138
|
+
# the chain resumes in the next one.
|
|
139
|
+
def next_column_block?(line_y, line_height, frame_x, frame_right)
|
|
140
|
+
@contours.any? do |contour|
|
|
141
|
+
box_contour?(contour) && contour.next_column &&
|
|
142
|
+
y_overlap?(contour, line_y, line_height) &&
|
|
143
|
+
x_overlap?(contour, frame_x, frame_right)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
127
147
|
def box_contour?(contour)
|
|
128
148
|
!contour.is_a?(WrapContour::Shape)
|
|
129
149
|
end
|
|
@@ -136,6 +156,14 @@ module Idml
|
|
|
136
156
|
# shift); RightSide moves text past its right edge (reduce +
|
|
137
157
|
# shift); LargestArea picks the roomier side; BothSides and
|
|
138
158
|
# the spine variants reduce by `overlap` only.
|
|
159
|
+
# JumpObject / NextColumn both forbid text beside the
|
|
160
|
+
# object; the renderer decides between skip-below and
|
|
161
|
+
# column jump.
|
|
162
|
+
def blocks_full_width?(contour)
|
|
163
|
+
contour.jump || contour.next_column
|
|
164
|
+
end
|
|
165
|
+
private :blocks_full_width?
|
|
166
|
+
|
|
139
167
|
def interval_side_adjustment(side, obj_left, obj_right, overlap,
|
|
140
168
|
frame_x, frame_right)
|
|
141
169
|
case side
|
|
@@ -185,11 +213,13 @@ module Idml
|
|
|
185
213
|
|
|
186
214
|
rect = Geometry.placement_rect(bounds, item.item_transform,
|
|
187
215
|
page_height)
|
|
216
|
+
mode = pref&.text_wrap_mode
|
|
188
217
|
Contour.new(
|
|
189
218
|
x: rect[:x], y: rect[:y],
|
|
190
219
|
width: rect[:width], height: rect[:height],
|
|
191
220
|
side: pref&.text_wrap_side,
|
|
192
|
-
jump:
|
|
221
|
+
jump: mode == "JumpObjectTextWrap",
|
|
222
|
+
next_column: mode == "NextColumnTextWrap"
|
|
193
223
|
)
|
|
194
224
|
end
|
|
195
225
|
private_class_method :box_contour
|
data/lib/idml/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
@@ -218,6 +218,8 @@ files:
|
|
|
218
218
|
- TODO.pdf/151-schema-conformance.md
|
|
219
219
|
- TODO.pdf/152-rtl-alignment.md
|
|
220
220
|
- TODO.pdf/153-vertical-keeps.md
|
|
221
|
+
- TODO.pdf/154-vertical-keep-windows.md
|
|
222
|
+
- TODO.pdf/155-next-column-jump.md
|
|
221
223
|
- TODO.pdf/16-typed-model-pipeline.md
|
|
222
224
|
- TODO.pdf/17-shape-rendering.md
|
|
223
225
|
- TODO.pdf/18-text-rendering.md
|