idml 0.9.8 → 0.9.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd4031c47fca99598c64ed757ac446d3e1f2d2870603eb31af338132dae01288
|
|
4
|
+
data.tar.gz: 520a5ded7c74cfc50d0f893921486930c295b113f7e0d3c7af4dfb576186498c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bad5a41e54b9e173241e08ba9e7006e6f621230ff782e89e4821d6b26749ee11b30fe8a38dae5185ee1cb41e4497ec20f32267584852cac43c9a37e41f488990
|
|
7
|
+
data.tar.gz: a62b908ae372636a3dec1722265ee44b24ab847510b348dd0029a3d04b388e41c6ad708f53c11ba21d7077817cfa831d700626ee73cc0380e46bfcb02e7ea58d
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# TODO PDF 127: KeepWithNext
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE — implemented 2026-08-20
|
|
4
|
+
|
|
5
|
+
## What was done
|
|
6
|
+
|
|
7
|
+
- `StyleResolver::Paragraph` carries `keep_with_next` from the PSR.
|
|
8
|
+
- A paragraph with KeepWithNext defers to the next frame when the
|
|
9
|
+
FOLLOWING paragraph is forced there (StartParagraph break) or
|
|
10
|
+
when the follower's first line would not fit after this
|
|
11
|
+
paragraph (pre-measured via TextEngine::Measurement plus the
|
|
12
|
+
follower's leading). Combined with TODO 123's KeepAllLinesTogether,
|
|
13
|
+
both keep flavors share one deferral predicate in
|
|
14
|
+
`consume_paragraphs`.
|
|
15
|
+
|
|
16
|
+
## Known limitations
|
|
17
|
+
|
|
18
|
+
- The numeric strength of KeepWithNext (1–5, lines to keep) is
|
|
19
|
+
treated as boolean.
|
|
20
|
+
- Not applied in vertical writing mode.
|
|
@@ -1,35 +1,51 @@
|
|
|
1
1
|
# TODO PDF 61: Known limitations and future work
|
|
2
2
|
|
|
3
|
-
## Status: DOCUMENTED
|
|
3
|
+
## Status: DOCUMENTED — refreshed 2026-08-20
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
not embed FontFile2 binary data in the PDF. Text renders correctly
|
|
9
|
-
on systems with the font installed but may not render on other
|
|
10
|
-
systems. Requires pdfrb font subsetting support (TODO 52).
|
|
5
|
+
Previously listed limitations (font embedding, tagged PDF, per-run
|
|
6
|
+
styling, dead code) were resolved by TODOs 52/53/55/67/76 long ago;
|
|
7
|
+
this list reflects the current state.
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
JPEG at a macOS-specific path. Tests that check image embedding
|
|
14
|
-
are skipped on CI. A self-contained fixture with an embedded image
|
|
15
|
-
would fix this.
|
|
16
|
-
|
|
17
|
-
3. **Text engine integration**: TextFrameRenderer uses the text engine
|
|
18
|
-
for line breaking when FontMetrics is available (Helvetica as .ttf).
|
|
19
|
-
On macOS, Helvetica is a .ttc collection, so the fallback simple
|
|
20
|
-
renderer is used (no line breaking). Per-run styling (different
|
|
21
|
-
fonts/sizes in one frame) is simplified to use the first run's style.
|
|
9
|
+
## Known limitations
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
### Text layout
|
|
12
|
+
- **Hyphenation** is not implemented (Hyphenation* attributes are
|
|
13
|
+
parsed but unused); a hyphenation dictionary would be required.
|
|
14
|
+
- **Justification**: word/letter spacing caps apply (TODO 119);
|
|
15
|
+
glyph scaling (Min/Max/DesiredGlyphScaling) is not applied;
|
|
16
|
+
ToBinding / RTL binding alignment maps to left.
|
|
17
|
+
- **Keep options**: whole-paragraph KeepAllLinesTogether and
|
|
18
|
+
KeepWithNext(first-line) are honored; partial keep windows
|
|
19
|
+
(KeepFirstLines / KeepLastLines) are not.
|
|
20
|
+
- **StartParagraph** breaks act at frame/column granularity — no
|
|
21
|
+
odd/even page parity.
|
|
22
|
+
- **Text wrap** (TODO 114): BoundingBox mode only; Shape contour
|
|
23
|
+
following and Inverse mode are not implemented.
|
|
24
|
+
|
|
25
|
+
### CJK
|
|
26
|
+
- **Mojikumi**: CJK/Latin auto script spacing is applied (TODO
|
|
27
|
+
125); full class-based punctuation compression tables are not.
|
|
28
|
+
- **Vertical mode**: Latin rotation is per glyph (not run-grouped);
|
|
29
|
+
ruby placement is beside-column; keep options do not apply.
|
|
30
|
+
|
|
31
|
+
### Structural features
|
|
32
|
+
- **Endnotes** (TODO 117): reference markers render; endnote TEXT
|
|
33
|
+
rendering is gated on a real fixture (the EndnoteTextRange
|
|
34
|
+
reference chain is opaque without one).
|
|
35
|
+
- **Footnotes** number per story; overflow balancing (NoSplitting)
|
|
36
|
+
is not modeled; the simple-render fallback shows markers without
|
|
37
|
+
footnote text.
|
|
38
|
+
- **Anchored objects** render at stored geometry; AnchorType
|
|
39
|
+
text-reflow is not simulated.
|
|
40
|
+
- **Tables** do not span frames (no repeated header rows); diagonal
|
|
41
|
+
cell strokes are modeled but not drawn.
|
|
42
|
+
- **Inline anchored objects / footnotes inside table cells** are
|
|
43
|
+
not handled.
|
|
27
44
|
|
|
28
45
|
## Future enhancements
|
|
29
46
|
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- Per-run text styling with cursor tracking
|
|
47
|
+
- Hyphenation dictionary integration
|
|
48
|
+
- Shape-mode text wrap contours
|
|
49
|
+
- Frame-spanning tables with header repeats
|
|
50
|
+
- Class-based mojikumi tables
|
|
35
51
|
- Performance benchmark for 100+ page documents
|
|
@@ -629,11 +629,13 @@ module Idml
|
|
|
629
629
|
cursor_y, bottom_limit, char_cursor,
|
|
630
630
|
footnotes)
|
|
631
631
|
pending = 0
|
|
632
|
-
state.paragraphs.
|
|
632
|
+
state.paragraphs.each_with_index do |paragraph, index|
|
|
633
633
|
break if cursor_y < bottom_limit
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
634
|
+
|
|
635
|
+
next_paragraph = state.paragraphs[index + 1]
|
|
636
|
+
break if paragraph_deferred?(paragraph, next_paragraph,
|
|
637
|
+
layout_frame, font, cursor_y,
|
|
638
|
+
bottom_limit, pending.positive?)
|
|
637
639
|
|
|
638
640
|
remaining_runs, cursor_y, char_cursor = render_runs_for_paragraph(
|
|
639
641
|
canvas, paragraph, paragraph.runs, context,
|
|
@@ -969,11 +971,25 @@ module Idml
|
|
|
969
971
|
# frame: a StartParagraph forced break, or KeepAllLines-
|
|
970
972
|
# Together with insufficient remaining space (never for the
|
|
971
973
|
# frame's first paragraph, so progress is always made).
|
|
972
|
-
def self.paragraph_deferred?(paragraph,
|
|
973
|
-
|
|
974
|
+
def self.paragraph_deferred?(paragraph, next_paragraph,
|
|
975
|
+
layout_frame, font, cursor_y,
|
|
976
|
+
bottom_limit, placed_any)
|
|
974
977
|
return true if paragraph_break?(paragraph) && placed_any
|
|
975
|
-
return false unless paragraph.keep_all_lines_together
|
|
976
978
|
return false unless placed_any
|
|
979
|
+
return true if keep_with_next_break?(paragraph, next_paragraph,
|
|
980
|
+
layout_frame, font,
|
|
981
|
+
cursor_y, bottom_limit)
|
|
982
|
+
|
|
983
|
+
keep_all_lines_break?(paragraph, layout_frame, font,
|
|
984
|
+
cursor_y, bottom_limit)
|
|
985
|
+
end
|
|
986
|
+
private_class_method :paragraph_deferred?
|
|
987
|
+
|
|
988
|
+
# KeepAllLinesTogether: defer when the paragraph cannot
|
|
989
|
+
# fully fit the remaining space.
|
|
990
|
+
def self.keep_all_lines_break?(paragraph, layout_frame, font,
|
|
991
|
+
cursor_y, bottom_limit)
|
|
992
|
+
return false unless paragraph.keep_all_lines_together
|
|
977
993
|
|
|
978
994
|
wrap_width = TextEngine::VerticalLayout.wrap_width(
|
|
979
995
|
layout_frame, paragraph.right_indent || 0
|
|
@@ -983,7 +999,40 @@ module Idml
|
|
|
983
999
|
)
|
|
984
1000
|
(cursor_y - height) < bottom_limit
|
|
985
1001
|
end
|
|
986
|
-
private_class_method :
|
|
1002
|
+
private_class_method :keep_all_lines_break?
|
|
1003
|
+
|
|
1004
|
+
# KeepWithNext: this paragraph defers when the next
|
|
1005
|
+
# paragraph is forced to the next frame, or when the next
|
|
1006
|
+
# paragraph's first line would not fit after this one.
|
|
1007
|
+
def self.keep_with_next_break?(paragraph, next_paragraph,
|
|
1008
|
+
layout_frame, font, cursor_y,
|
|
1009
|
+
bottom_limit)
|
|
1010
|
+
return false unless keepable_pair?(paragraph, next_paragraph)
|
|
1011
|
+
return true if paragraph_break?(next_paragraph)
|
|
1012
|
+
|
|
1013
|
+
wrap_width = TextEngine::VerticalLayout.wrap_width(
|
|
1014
|
+
layout_frame, paragraph.right_indent || 0
|
|
1015
|
+
)
|
|
1016
|
+
height = TextEngine::Measurement.paragraph_height(
|
|
1017
|
+
paragraph, font, wrap_width
|
|
1018
|
+
)
|
|
1019
|
+
(cursor_y - height - follower_leading(next_paragraph)) <
|
|
1020
|
+
bottom_limit
|
|
1021
|
+
end
|
|
1022
|
+
private_class_method :keep_with_next_break?
|
|
1023
|
+
|
|
1024
|
+
def self.keepable_pair?(paragraph, next_paragraph)
|
|
1025
|
+
!paragraph.keep_with_next.nil? && !next_paragraph.nil?
|
|
1026
|
+
end
|
|
1027
|
+
private_class_method :keepable_pair?
|
|
1028
|
+
|
|
1029
|
+
def self.follower_leading(next_paragraph)
|
|
1030
|
+
size = next_paragraph.runs.first&.point_size || DEFAULT_SIZE
|
|
1031
|
+
TextEngine::VerticalLayout.leading_for(
|
|
1032
|
+
next_paragraph.auto_leading, size
|
|
1033
|
+
)
|
|
1034
|
+
end
|
|
1035
|
+
private_class_method :follower_leading
|
|
987
1036
|
|
|
988
1037
|
# True when the paragraph requests a forced break to the
|
|
989
1038
|
# next frame/column (StartParagraph). All break flavors act
|
|
@@ -72,8 +72,11 @@ module Idml
|
|
|
72
72
|
# NextColumn / NextFrame / NextOddPage / NextEvenPage).
|
|
73
73
|
:start_paragraph,
|
|
74
74
|
# Widow/orphan control: KeepAllLinesTogether pushes the whole
|
|
75
|
-
# paragraph to the next frame when it cannot fully fit
|
|
75
|
+
# paragraph to the next frame when it cannot fully fit;
|
|
76
|
+
# KeepWithNext binds this paragraph to the next paragraph's
|
|
77
|
+
# first line.
|
|
76
78
|
:keep_all_lines_together,
|
|
79
|
+
:keep_with_next,
|
|
77
80
|
:bullets_and_numbering_list_type,
|
|
78
81
|
:bullet_character_value,
|
|
79
82
|
:bullets_text_after,
|
|
@@ -207,6 +210,8 @@ module Idml
|
|
|
207
210
|
keep_all_lines_together: resolve_attr(
|
|
208
211
|
style_lookup, psr, :keep_all_lines_together
|
|
209
212
|
),
|
|
213
|
+
keep_with_next: resolve_attr(style_lookup, psr,
|
|
214
|
+
:keep_with_next),
|
|
210
215
|
drop_cap_lines: resolve_attr(style_lookup, psr, :drop_cap_lines),
|
|
211
216
|
drop_cap_characters: resolve_attr(style_lookup, psr,
|
|
212
217
|
:drop_cap_characters),
|
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.9.
|
|
4
|
+
version: 0.9.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
@@ -185,6 +185,7 @@ files:
|
|
|
185
185
|
- TODO.pdf/124-tatechuyoko-vertical.md
|
|
186
186
|
- TODO.pdf/125-script-spacing.md
|
|
187
187
|
- TODO.pdf/126-part-cache.md
|
|
188
|
+
- TODO.pdf/127-keep-with-next.md
|
|
188
189
|
- TODO.pdf/13-cjk-text-layout.md
|
|
189
190
|
- TODO.pdf/14-page-item-element-models.md
|
|
190
191
|
- TODO.pdf/15-wire-spread-children.md
|