idml 0.10.13 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2a7b73c56645a87269780fc0cab9f998f5a6bf0d167b0ce9433dba9a8019cd
4
- data.tar.gz: e4bb1522273937a4c2cf5631de1927f4583f63d16f2b351f893921407d89dc88
3
+ metadata.gz: 578a2a0fbd712f1931eaff74db316971441be4a967afd67d47ab55ecdc9679d1
4
+ data.tar.gz: e93c08f8694da2e0522a787ff0f58b4f607952f7024a0dd0d54bab42599ac8e9
5
5
  SHA512:
6
- metadata.gz: 83756750d549a02ff7080686509ac45acce77405db6c9d3ba0ee6dae3643622b8eb89198d28fead77047dac50bdcf9491bdab05b7e03d02e18560f1021fa5360
7
- data.tar.gz: 8a9fc59030029b4342019b205f9fe7da46ae6296729d641c49c199391cd5765ffea195dfca6cde46ff0df6518f496dcf9a1b66dc5dad0b808503230be8057107
6
+ metadata.gz: 260e3fdbcd2339d2e461aae7f2992b311120a0b15544d48b65c39d915dee9fd0219fcb3ee6d9e07ce40eaeb9bf68846fa72bdf2475d9ce12d1982cdd1503afd5
7
+ data.tar.gz: 6d760f50d28216bbe1a620aa4ba2b43028ab3051384e0fee559764892f8495b04f5623b2a3ba14698f89cf6bf1e3de9f0426be158e3e7faf0d8d7a3b639e289e
@@ -0,0 +1,27 @@
1
+ # TODO PDF 152: RTL paragraph alignment
2
+
3
+ ## Status: COMPLETE — implemented 2026-08-31
4
+
5
+ ## Problem
6
+
7
+ ParagraphDirection (LeftToRightDirection /
8
+ RightToLeftDirection) was parsed but unused: RTL paragraphs
9
+ aligned left/right by LTR convention, so Arabic/Hebrew text sat
10
+ on the wrong edge.
11
+
12
+ ## Solution
13
+
14
+ `StyleResolver::Paragraph` carries `paragraph_direction` (PSR or
15
+ paragraph style); `TextEngine::Justifier` gains a `direction:`
16
+ argument that mirrors the visual alignment under
17
+ RightToLeftDirection — left↔right, start↔end; center and
18
+ justified unchanged. Both justify call sites (frame renderer and
19
+ footnote layout) pass the paragraph's direction.
20
+
21
+ ## Files
22
+
23
+ - `lib/idml/text_engine/justifier.rb`
24
+ - `lib/idml/render/style_resolver.rb`
25
+ - `lib/idml/render/renderers/text_frame_renderer.rb`
26
+ - `lib/idml/render/footnote.rb`
27
+ - `spec/idml/text_engine/text_engine_spec.rb`
@@ -0,0 +1,31 @@
1
+ # TODO PDF 153: Keep options in vertical writing
2
+
3
+ ## Status: COMPLETE (approximation) — implemented 2026-08-31
4
+
5
+ ## Problem
6
+
7
+ Keep options (TODOs 123/127/133) applied only to the horizontal
8
+ engine; vertical paragraphs split across frames regardless of
9
+ KeepAllLinesTogether / KeepWithNext.
10
+
11
+ ## Solution
12
+
13
+ `VerticalEmit.vertical_render` consults a keep check BEFORE
14
+ placing each paragraph (deferral after placement cannot un-emit):
15
+
16
+ - **KeepAllLinesTogether** defers the paragraph wholly to the
17
+ next frame when its column need exceeds the remaining columns.
18
+ - **KeepWithNext** also defers when the follower's forced break
19
+ or column need would strand it.
20
+ - Never defers the frame's first paragraph — the same progress
21
+ guarantee as KeepPolicy (shared: `KeepPolicy.paragraph_break?`).
22
+
23
+ Approximation: column need = runs count (one column per run),
24
+ which under-estimates runs spanning several columns — deferral
25
+ errs toward placing. KeepFirstLines/KeepLastLines windows remain
26
+ horizontal-only (they are line-window concepts).
27
+
28
+ ## Files
29
+
30
+ - `lib/idml/render/renderers/vertical_emit.rb`
31
+ - `spec/idml/render/vertical_render_spec.rb`
@@ -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-08-28
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;
@@ -14,8 +14,9 @@ this list reflects the current state.
14
14
  wrap after explicit hyphens (TODO 139).
15
15
  - **Justification**: word/letter spacing caps apply (TODO 119),
16
16
  MaximumGlyphScaling stretches (TODO 129) and MinimumGlyphScaling
17
- compresses overlong lines (TODO 135); ToBinding / RTL binding
18
- alignment is not applied.
17
+ compresses overlong lines (TODO 135); RTL paragraphs mirror
18
+ left/right alignment (TODO 152); ToBinding / kashida-based
19
+ Arabic justification is not applied.
19
20
  - **Keep options**: KeepAllLinesTogether (TODO 123), KeepWithNext
20
21
  (TODO 127), and the KeepFirstLines / KeepLastLines windows
21
22
  (TODO 133, line-count approximation) are honored.
@@ -26,11 +27,10 @@ this list reflects the current state.
26
27
  odd/even page parity.
27
28
  - **Text wrap**: BoundingBoxTextWrap, Contour, and Inverse modes
28
29
  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.
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.
34
34
 
35
35
  ### CJK
36
36
  - **Mojikumi**: script spacing (TODO 125, now actually wired),
@@ -39,8 +39,8 @@ this list reflects the current state.
39
39
  Desired applies per class pair) are applied; Minimum/Maximum
40
40
  aki bounds and CompressionPriority are parsed but unused.
41
41
  - **Vertical mode**: Latin runs rotate as one group per segment
42
- (TODO 142); ruby placement is beside-column; keep options do
43
- not apply.
42
+ (TODO 142); ruby placement is beside-column; keep options apply
43
+ with a column-count approximation (TODOs 153-154).
44
44
 
45
45
  ### Structural features
46
46
  - **Endnotes** (TODO 117): reference markers render and endnote TEXT
@@ -60,7 +60,5 @@ this list reflects the current state.
60
60
  ## Future enhancements
61
61
 
62
62
  - Hyphenation dictionary integration
63
- - NextColumn wrap via column-jump chaining (multi-column frames)
64
63
  - Minimum/Maximum mojikumi aki bounds + CompressionPriority
65
- - ToBinding / RTL binding alignment
66
- - Vertical-mode keep options
64
+ - ToBinding / kashida Arabic justification
@@ -138,6 +138,7 @@ module Idml
138
138
  TextEngine::Justifier.justify(
139
139
  line: line, frame_width: wrap_width,
140
140
  alignment: paragraph.alignment || :left,
141
+ direction: paragraph.paragraph_direction,
141
142
  last_line: last_line?(paragraph, run, index, lines),
142
143
  limits: limits
143
144
  )
@@ -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
@@ -853,7 +889,8 @@ module Idml
853
889
  line: line, frame_width: wrap_width,
854
890
  alignment: paragraph.alignment || :left,
855
891
  last_line: paragraph_last && index == lines.length - 1,
856
- limits: limits
892
+ limits: limits,
893
+ direction: paragraph.paragraph_direction
857
894
  )
858
895
  end
859
896
  end
@@ -24,10 +24,20 @@ module Idml
24
24
  layout_frame = layout_frame(context.item, box)
25
25
  left_limit = layout_frame.x + (layout_frame.inset_left || 0)
26
26
  resume_vertical_paragraph(state)
27
- next_column = state.column_offset || 0
27
+ entry_offset = state.column_offset || 0
28
+ next_column = entry_offset
28
29
  pending = 0
29
30
 
30
- state.paragraphs.each do |paragraph|
31
+ state.paragraphs.each_with_index do |paragraph, index|
32
+ if vertical_keep_deferred?(paragraph, state.paragraphs[index + 1],
33
+ layout_frame, font, left_limit,
34
+ next_column, pending, entry_offset)
35
+ state.current_paragraph = nil
36
+ state.runs_remaining = []
37
+ state.column_offset = 0
38
+ break
39
+ end
40
+
31
41
  remaining_runs, next_column = vertical_paragraph(
32
42
  canvas, paragraph, context, layout_frame, font,
33
43
  left_limit, next_column
@@ -45,6 +55,87 @@ module Idml
45
55
  state.paragraphs = state.paragraphs[pending..]
46
56
  end
47
57
 
58
+ # Keep options in vertical mode (TODO 153, column-count
59
+ # approximation — one column per run under-estimates runs
60
+ # that span several columns, so deferral errs toward
61
+ # placing): KeepAllLinesTogether defers the paragraph
62
+ # wholly to the next frame; KeepWithNext also defers when
63
+ # the follower (or its forced break) would strand. Never
64
+ # defers the frame's first paragraph — the progress
65
+ # guarantee, shared with KeepPolicy.
66
+ def vertical_keep_deferred?(paragraph, next_paragraph,
67
+ layout_frame, _font, left_limit,
68
+ next_column, pending, entry_offset)
69
+ placed = pending.positive? || entry_offset.positive?
70
+ return false unless placed
71
+ return false unless keep_flagged?(paragraph)
72
+
73
+ needed = [paragraph.runs.length, 1].max
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)
78
+
79
+ with_next_deferred?(paragraph, next_paragraph, layout_frame,
80
+ left_limit, next_column, needed)
81
+ end
82
+
83
+ def keep_flagged?(paragraph)
84
+ paragraph.keep_all_lines_together ||
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
108
+ end
109
+
110
+ def with_next_deferred?(paragraph, next_paragraph, layout_frame,
111
+ left_limit, next_column, needed)
112
+ return false if paragraph.keep_with_next.nil?
113
+
114
+ follower_needed = [next_paragraph&.runs&.length || 1, 1].max
115
+ follower_break = KeepPolicy.paragraph_break?(next_paragraph)
116
+ room = vertical_room(layout_frame, paragraph, left_limit,
117
+ next_column)
118
+ follower_break || needed + follower_needed > room
119
+ end
120
+
121
+ def vertical_room(layout_frame, paragraph, left_limit,
122
+ next_column)
123
+ leading = TextEngine::VerticalLayout.leading_for(
124
+ paragraph.auto_leading,
125
+ paragraph.runs.first&.point_size ||
126
+ TextFrameRenderer::DEFAULT_SIZE,
127
+ )
128
+ vertical_available_columns(layout_frame, leading, left_limit,
129
+ next_column)
130
+ end
131
+
132
+ def vertical_available_columns(layout_frame, leading, left_limit,
133
+ next_column)
134
+ right = layout_frame.x + layout_frame.width -
135
+ (layout_frame.inset_right || 0)
136
+ (((right - left_limit) / leading).floor - next_column).to_i
137
+ end
138
+
48
139
  # Folds a chain-resumed paragraph (runs_remaining) back to
49
140
  # the head of the paragraph list so the vertical path sees a
50
141
  # uniform sequence. The consumed column offset persists in
@@ -70,6 +70,9 @@ module Idml
70
70
  :maximum_letter_spacing,
71
71
  :maximum_glyph_scaling,
72
72
  :minimum_glyph_scaling,
73
+ # ParagraphDirection (LeftToRightDirection /
74
+ # RightToLeftDirection) — mirrors alignment under RTL.
75
+ :paragraph_direction,
73
76
  # Forced paragraph break (StartParagraph: NextPage /
74
77
  # NextColumn / NextFrame / NextOddPage / NextEvenPage).
75
78
  :start_paragraph,
@@ -197,6 +200,9 @@ module Idml
197
200
  paragraph = Paragraph.new(
198
201
  runs: runs,
199
202
  alignment: runs.first.alignment,
203
+ paragraph_direction: resolve_attr(
204
+ style_lookup, psr, :paragraph_direction
205
+ ),
200
206
  space_before: resolve_attr(style_lookup, psr, :space_before),
201
207
  space_after: resolve_attr(style_lookup, psr, :space_after),
202
208
  first_line_indent: resolve_attr(style_lookup, psr,
@@ -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 and the renderer
104
- # skips the run below the object's bottom edge.
105
- return [frame_right - frame_x, 0.0] if contour.jump
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 box_contour?(contour) && contour.jump
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: JUMP_MODES.include?(pref&.text_wrap_mode)
221
+ jump: mode == "JumpObjectTextWrap",
222
+ next_column: mode == "NextColumnTextWrap"
193
223
  )
194
224
  end
195
225
  private_class_method :box_contour
@@ -21,13 +21,13 @@ module Idml
21
21
  DEFAULT_MIN_GLYPH_SCALING = 100.0
22
22
 
23
23
  def self.justify(line:, frame_width:, alignment: :left,
24
- last_line: false, limits: nil)
25
- new(frame_width, alignment, last_line, limits).justify(line)
24
+ last_line: false, limits: nil, direction: nil)
25
+ new(frame_width, alignment, last_line, limits, direction).justify(line)
26
26
  end
27
27
 
28
- def initialize(frame_width, alignment, last_line, limits)
28
+ def initialize(frame_width, alignment, last_line, limits, direction)
29
29
  @frame_width = frame_width
30
- @alignment = alignment
30
+ @alignment = mirrored_alignment(alignment, direction)
31
31
  @last_line = last_line
32
32
  @limits = limits
33
33
  end
@@ -44,7 +44,18 @@ module Idml
44
44
  line
45
45
  end
46
46
 
47
- private
47
+ RTL_ALIGNMENT_MIRROR = {
48
+ left: :right, right: :left,
49
+ start: :end, end: :start
50
+ }.freeze
51
+
52
+ # RightToLeftDirection mirrors the visual alignment: what is
53
+ # "left" in an LTR paragraph sits right in an RTL one.
54
+ def mirrored_alignment(alignment, direction)
55
+ return alignment unless direction == "RightToLeftDirection"
56
+
57
+ RTL_ALIGNMENT_MIRROR[alignment] || alignment
58
+ end
48
59
 
49
60
  # The paragraph's last line stays ragged under full
50
61
  # justification.
data/lib/idml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Idml
4
- VERSION = "0.10.13"
4
+ VERSION = "0.10.15"
5
5
  end
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.13
4
+ version: 0.10.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
@@ -216,6 +216,10 @@ files:
216
216
  - TODO.pdf/15-wire-spread-children.md
217
217
  - TODO.pdf/150-architecture-round-three.md
218
218
  - TODO.pdf/151-schema-conformance.md
219
+ - TODO.pdf/152-rtl-alignment.md
220
+ - TODO.pdf/153-vertical-keeps.md
221
+ - TODO.pdf/154-vertical-keep-windows.md
222
+ - TODO.pdf/155-next-column-jump.md
219
223
  - TODO.pdf/16-typed-model-pipeline.md
220
224
  - TODO.pdf/17-shape-rendering.md
221
225
  - TODO.pdf/18-text-rendering.md