arrolio 0.1.6 → 0.1.7

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: 057f22d28911ebd5fb250d0a3ee3aae065a493c2c8aee0a5ff329a138da38674
4
- data.tar.gz: 8477b14736601c87ee24c690aa867ef175de94568ef866b48d260948c3d98116
3
+ metadata.gz: 6d2fb34e355fe255920ca92e339c7c2529db69b9668130d2f5c9f36319f2aa13
4
+ data.tar.gz: a591ae1001ae525c96d19a7321eee6840a105e3d9e6404a5b2a2454e68bd7b46
5
5
  SHA512:
6
- metadata.gz: 2aef88dd6df44e8aef2be218f34f53de0e332ecfb94bc2ef5aa8a87f0221fab634ac05ad0a900997fc1ebb870529626a464eeaa302c7f3e19d1b05664bfd7e90
7
- data.tar.gz: b984b3dae46396305d96e5c6977a24992c4ddae317be8a4682fafed9de46a35a7e2442dbc418147c77afb943ba7981c6b8be28b562b85723fe5624b3242b6c7c
6
+ metadata.gz: 3587a3bf6d2bebc2d66b4f990bccc365f8508a08ee75d191636c737a5d4c6eb31ff1cd8069175e010a40a38f17d2b98a8c788da3bb164dbb182aa632eb85fd99
7
+ data.tar.gz: deed9c49dc8e31bc26854c8dcb92b4f46214318da53a067786bdc1ac86329e6403d53bce132826a314b7e6876d28c51bd45ce6089ad8eb1d07eb61d0fb92e0b2
@@ -99,20 +99,15 @@ module Arrolio
99
99
  header, body = partition_rows
100
100
  header_count = header.length
101
101
 
102
- head_groups = []
103
- tail_groups = []
104
102
  # The head part also carries the caption and the repeated
105
103
  # header rows — they consume the same budget as body rows.
106
- used = caption_height(width) + heights[0, header_count].sum
107
- body_row_groups.each do |group|
108
- group_h = heights[group.first, group.length].sum
109
- if used + group_h <= remaining_height
110
- head_groups << group
111
- used += group_h
112
- else
113
- tail_groups << group
114
- end
115
- end
104
+ budget = remaining_height - caption_height(width) -
105
+ heights[0, header_count].sum
106
+ result = Table::SplitPolicy.new(groups: body_row_groups,
107
+ group_heights: heights,
108
+ budget: budget).call
109
+ head_groups = result.head_groups
110
+ tail_groups = result.tail_groups
116
111
 
117
112
  head_rows = head_groups.flat_map { |g| body[g.first - header_count, g.length] }
118
113
  tail_rows = tail_groups.flat_map { |g| body[g.first - header_count, g.length] }
@@ -51,7 +51,11 @@ module Arrolio
51
51
  def split_by_page(text)
52
52
  return [text] unless text.include?("\f")
53
53
 
54
- text.split("\f", -1)
54
+ pages = text.split("\f", -1)
55
+ # pdftotext ends output with a trailing form-feed for some
56
+ # writers; the empty string after it is not a page.
57
+ pages.pop if pages.last.to_s.strip.empty? && pages.length > 1
58
+ pages
55
59
  end
56
60
  end
57
61
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arrolio
4
+ module Table
5
+ # Pure table-split decision: which welded row groups fit the
6
+ # page budget. The budget is the remaining page height minus
7
+ # what travels with every part (the caption and the repeated
8
+ # header rows). Groups are greedily packed while they fit; an
9
+ # empty head means whole-move (the engine moves the entire
10
+ # table to the next page).
11
+ class SplitPolicy
12
+ Result = Struct.new(:head_groups, :tail_groups, keyword_init: true) do
13
+ def whole_move?
14
+ head_groups.empty?
15
+ end
16
+ end
17
+
18
+ # +groups+ Array of Arrays of row indexes (welded by rowspan).
19
+ # +group_heights+ parallel Array of Float heights per group.
20
+ # +budget+ Float height available for body groups.
21
+ def initialize(groups:, group_heights:, budget:)
22
+ @groups = groups
23
+ @group_heights = group_heights
24
+ @budget = budget.to_f
25
+ end
26
+
27
+ # Rows split IN ORDER: once a group does not fit, every
28
+ # later group goes to the tail (a fitting later group must
29
+ # never jump ahead of an overflowing earlier one - the
30
+ # original greedy loop allowed exactly that reordering).
31
+ def call
32
+ head = []
33
+ tail = []
34
+ used = 0.0
35
+ overflowing = false
36
+ @groups.each do |group|
37
+ height = group_height_for(group)
38
+ overflowing ||= used + height > @budget
39
+ (overflowing ? tail : head) << group
40
+ used += height unless overflowing
41
+ end
42
+ Result.new(head_groups: head, tail_groups: tail)
43
+ end
44
+
45
+ private
46
+
47
+ def group_height_for(group)
48
+ @group_heights[group.first, group.length].sum
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/arrolio/table.rb CHANGED
@@ -6,5 +6,6 @@ module Arrolio
6
6
  module Table
7
7
  autoload :AutoLayout, 'arrolio/table/auto_layout'
8
8
  autoload :Grid, 'arrolio/table/grid'
9
+ autoload :SplitPolicy, 'arrolio/table/split_policy'
9
10
  end
10
11
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arrolio
4
- VERSION = '0.1.6'
4
+ VERSION = '0.1.7'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arrolio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-31 00:00:00.000000000 Z
11
+ date: 2026-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fontisan
@@ -432,6 +432,7 @@ files:
432
432
  - lib/arrolio/table.rb
433
433
  - lib/arrolio/table/auto_layout.rb
434
434
  - lib/arrolio/table/grid.rb
435
+ - lib/arrolio/table/split_policy.rb
435
436
  - lib/arrolio/text_direction.rb
436
437
  - lib/arrolio/text_layout.rb
437
438
  - lib/arrolio/text_layout/break_opportunity.rb